Zelda.RISCV.Emulator
A System Level RISCV32 Emulator Over x86_64: capable of booting RISCV Linux
Install / Use
/learn @chillancezen/Zelda.RISCV.EmulatorREADME
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 |
| |
+-----------------------------------------+
- RISC-V Emulation From Scratch - Part I: Dynamic Instruction Translation
- RISC-V Emulation From Scratch - Part II: Memory Management Unit
- RISC-V Emulation From Scratch - Part III: Privilege Level and Trap handling
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
