SkillAgentSearch skills...

Zelda.RISCV.Emulator

A System Level RISCV32 Emulator Over x86_64: capable of booting RISCV Linux

Install / Use

/learn @chillancezen/Zelda.RISCV.Emulator
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

Zelda.RISCV.Emulator

What's this?

It's a system level emulator which utilizes dynamic binary translation to translate RISCV32 instructions and emulate RISCV32 ISA. the following diagram shows how the emulator is organized.

        +------------------------------------+
        |---------------------------------------+
        || hart 0                               |
        ||       +------------+--------------+  |       (raising load/store exception)
        ||       | Exception  | Interrupt    |  | <-----------------------------------+
  +----------->  +------------+--------------+  |                                     |
d |     ||       |        TRAP handling      |  |                                     |
e |     ||       +---------------------------+  |                                     |
l |     ||                                      |         +----------------------------------------------+
i |     ||                                      |         |   Memory Management Unit  |                  |
v |     ||    +------+---------------+          |         |                           +    +-----------+ |
e |     ||    | CSRs | Registers| PC |          |         |    +---------+                 |           | |
r |     ||    +------+---------------+          |  +----> |    |  iTLB   | +-------------+ | main mem  | |
  |     ||                                      |  |      |    +---------+      +--------+ |           | |
e |     ||   +--------------------------------+ |  |      |                     |          +-----------+ |
x |     ||   | dynamic binary translation     | |  |      |                     |                        |
t |     ||   | +------------------------------+ |  |      |    +---------+      |          +-----------+ |
e |     ||   | |   translation cache          | |  | +--> |    |  dTLB   | +----+--------+ |           | |
r |     ||   | +------------------------------+ |  | |    |    +---------+   Page Walker   |  io mem   | |
n |     ||   | |   x86_64 translation         | |  | |    |                                |           | |
a |     ||   +-+---+--------+-----------------+ |  | |    |                                +-----------+ |
l |     ++         |        |                   |  | |    |                                              |
  |      +--------------------------------------+  | |    +----------------------------------------------+
i |                |        |                      | |
n |                |        +----------------------+ |
t |                |         (mmu instruction load)  |
e |                |                                 |
r |                +---------------------------------+
r |                       (mmu data load/store)
u |
p |
t |    +-----------------------------------------+
  +----+  Platform-level Interrupt Controller    |
       |                                         |
       +-----------------------------------------+

Try it out

I built a binary executable plus bootloader and Linux image. you can download(https://github.com/chillancezen/Zelda.RISCV.Emulator/releases) and decompress it, then run by:

#./vmx test.vm.ini

How to build?

Host env: a x86_64 Linux host + gcc 4.8.x

there are lots of things to build in order to run a riscv Linux. here is the guide page: https://risc-v-getting-started-guide.readthedocs.io/en/latest/linux-qemu.html

  • riscv32 cross-compile toolchain: riscv32-unknown-linux-gnu-
  • riscv-Linux, make sure a newer Linux kernel source is built from, Linux 5.4.0 is chosen in my testbed.
  • SBI software: Berkely Bootloader(aka. BBL) is my choice.
  • initramfs cpio built with busybox.
  • build the emulator by simplily running CC=gcc-4.8 make

How to run ?

Now you should be able to find an executable:vmx under directory vmm. that's the virtual machine monitor. in order to run a guest, we have to define a configuration file to instruct vmm how to load and run a guest.

A typical config file is given in test.vm.ini


[image]
name = test.guest
; The BBL+Linux path
kernel = /root/workspace/riscv-pk-master/build/bbl.img

; our ram starts at 0x80000000, so we load the kernel image right here.
kernel_load_base = 0x80000000

; the boot argument delivered to Linux kernel
; earlycon or console must be specified here
bootarg = console=uart8250,mmio,0x10000000

; init ramdisk arguments.
initrd = /root/workspace/busybox-1.31.1/initrd.cpio
initrd_load_base = 0x84000000

[rom]
rom_image = bootrom/bootrom.rv32.img
rom_start = 0x1000
rom_size = 0x1000000


[cpu]
; number of cpus, always required.
nr_cpus = 1
; the cpu index as the primary cpu to boot: optional, default:0
boot_cpu = 0
; the program counter will be set to the value when the hart is reset or poweron
; also the rom image will be loaded to the address
pc_on_reset = 0x4000
; the Core Local Interrupt Controller which generates and delivers machine
; and supervisor timer interrupts to HLIC
clint_base = 0x02000000
clint_size = 0x00010000
; the capacity of TLB, MUST BE POWER OF 2, and 16K is quite enough.
itlb_size = 0x4000
dtlb_size = 0x4000

[mem]
main_memory_start = 0x80000000
main_memory_size_in_mega = 1024

[misc]
; dump the device tree blob, optional
dump_dtb = ./zelda.dtb

[debug]
; the log verbosity is an integer.
;LOG_TRACE = 0, LOG_DEBUG = 1, LOG_INFO = 2, LOG_WARN = 3, LOG_ERROR = 4,
;LOG_FATAL = 5, LOG_UART = 6
verbosity = 2
; initial breakpoints, optional.
;breakpoints = c011de8c

then run the virtual machine:

$./vmm/vmx ./test.vm.ini

[UART16550] bbl loader
[UART16550]               vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
[UART16550]                   vvvvvvvvvvvvvvvvvvvvvvvvvvvv
[UART16550] rrrrrrrrrrrrr       vvvvvvvvvvvvvvvvvvvvvvvvvv
[UART16550] rrrrrrrrrrrrrrrr      vvvvvvvvvvvvvvvvvvvvvvvv
[UART16550] rrrrrrrrrrrrrrrrrr    vvvvvvvvvvvvvvvvvvvvvvvv
[UART16550] rrrrrrrrrrrrrrrrrr    vvvvvvvvvvvvvvvvvvvvvvvv
[UART16550] rrrrrrrrrrrrrrrrrr    vvvvvvvvvvvvvvvvvvvvvvvv
[UART16550] rrrrrrrrrrrrrrrr      vvvvvvvvvvvvvvvvvvvvvv
[UART16550] rrrrrrrrrrrrr       vvvvvvvvvvvvvvvvvvvvvv
[UART16550] rr                vvvvvvvvvvvvvvvvvvvvvv
[UART16550] rr            vvvvvvvvvvvvvvvvvvvvvvvv      rr
[UART16550] rrrr      vvvvvvvvvvvvvvvvvvvvvvvvvv      rrrr
[UART16550] rrrrrr      vvvvvvvvvvvvvvvvvvvvvv      rrrrrr
[UART16550] rrrrrrrr      vvvvvvvvvvvvvvvvvv      rrrrrrrr
[UART16550] rrrrrrrrrr      vvvvvvvvvvvvvv      rrrrrrrrrr
[UART16550] rrrrrrrrrrrr      vvvvvvvvvv      rrrrrrrrrrrr
[UART16550] rrrrrrrrrrrrrr      vvvvvv      rrrrrrrrrrrrrr
[UART16550] rrrrrrrrrrrrrrrr      vv      rrrrrrrrrrrrrrrr
[UART16550] rrrrrrrrrrrrrrrrrr          rrrrrrrrrrrrrrrrrr
[UART16550] rrrrrrrrrrrrrrrrrrrr      rrrrrrrrrrrrrrrrrrrr
[UART16550] rrrrrrrrrrrrrrrrrrrrrr  rrrrrrrrrrrrrrrrrrrrrr
[UART16550]
[UART16550]        INSTRUCTION SETS WANT TO BE FREE
[UART16550] [    0.000000] OF: fdt: Ignoring memory range 0x80000000 - 0x80400000
[UART16550] [    0.000000] Linux version 5.4.0 (root@my-container-host) (gcc version 9.2.0 (GCC)) #49 SMP Thu Feb 20 00:06:15 EST 2020
[UART16550] [    0.000000] earlycon: uart8250 at MMIO 0x0000000010000000 (options '')
[UART16550] [    0.000000] printk: bootconsole [uart8250] enabled
[UART16550] [    0.000000] initrd not found or empty - disabling initrd
[UART16550] [    0.000000] Zone ranges:
[UART16550] [    0.000000]   Normal   [mem 0x0000000080400000-0x00000000bfffffff]
[UART16550] [    0.000000] Movable zone start for each node
[UART16550] [    0.000000] Early memory node ranges
[UART16550] [    0.000000]   node   0: [mem 0x0000000080400000-0x00000000bfffffff]
[UART16550] [    0.000000] Initmem setup node 0 [mem 0x0000000080400000-0x00000000bfffffff]
[UART16550] [    0.000000] elf_hwcap is 0x1101
[UART16550] [    0.000000] percpu: Embedded 12 pages/cpu s18572 r8192 d22388 u49152
[UART16550] [    0.000000] Built 1 zonelists, mobility grouping on.  Total pages: 258570
[UART16550] [    0.000000] Kernel command line: console=uart8250,mmio,0x10000000
[UART16550] [    0.000000] Dentry cache hash table entries: 131072 (order: 7, 524288 bytes, linear)
[UART16550] [    0.000000] Inode-cache hash table entries: 65536 (order: 6, 262144 bytes, linear)
[UART16550] [    0.000000] Sorting __ex_table...
[UART16550] [    0.000000] mem auto-init: stack:off, heap alloc:off, heap free:off
[UART16550] [    0.000000] Memory: 1027168K/1044480K available (4827K kernel code, 167K rwdata, 869K rodata, 194K init, 215K bss, 17312K reserved, 0K cma-reserved)
[UART16550] [    0.000000] SLUB: HWalign=64, Order=0-3, MinObjects=0, CPUs=1, Nodes=1
[UART16550] [    0.000000] rcu: Hierarchical RCU implementation.
[UART16550] [    0.000000] rcu:     RCU event tracing is enabled.
[UART16550] [    0.000000] rcu:     RCU restricting CPUs from NR_CPUS=8 to nr_cpu_ids=1.
[UART16550] [    0.000000] rcu: RCU calculated value of scheduler-enlistment delay is 25 jiffies.
[UART16550] [    0.000000] rcu: Adjusting geometry for rcu_fanout_leaf=16, nr_cpu_ids=1
[UART16550] [    0.000000] NR_IRQS: 0, nr_irqs: 0, preallocated irqs: 0
[UART16550] [    0.000000] riscv_timer_init_dt: Registering clocksource cpuid [0] hartid [0]
[UART16550] [    0.000000] clocksource: riscv_clocksource: mask: 0xffffffffffffffff max_cycles: 0x24e6a1710, max_idle_ns: 440795202120 ns
[UART16550] [    0.422538] sched_clock: 64 bits at 10MHz, resolution 100ns, wraps every 4398046511100ns
[UART16550] [    4.252737] Console: colour dummy device 80x25
[UART16550] [    6.235356] Calibrating delay loop (skipped), value calculated using timer frequen
View on GitHub
GitHub Stars29
CategoryDevelopment
Updated19h ago
Forks9

Languages

C

Security Score

95/100

Audited on Apr 3, 2026

No findings