RISC-V simulation with Qemu

https://medium.com/@e1d1/risc-v-simulation-with-qemu-61ea8f2d8f4b

Installation of Qemu

On the Qemu website, you can find a lot of information on how to get it running. But for the specific purpose of this tutorial, a few important extra steps are needed.

In order to use the user mode simulation, your host system must be Linux. I recommend installing from source as described on this site using configure with –target-list= riscv32-linux-user,riscv64-linux-user, riscv32-softmmu,riscv64-softmmu as a configuration. I assume you have a RV toolchain running, if not, here is how.

User-Mode

The simplest way of getting an environment for RV programming is the user mode of Qemu. You cross compile your program as if it were on an RV system running Linux. Then you run your program with Qemu user mode.

To illustrate, I give a short example of compiling and running a C program. I assume you have a running set up for Linux.

echo -e '#include <stdio.h>\nint main(){\n printf("hi\\n");\n return 0;\n}' > hi.c
riscv64-unknown-elf-gcc -o hi hi.c 
qemu-riscv64-static hi

System-Mode

The system mode of Qemu lets you simulate a complete operating system. For example, you can run Linux inside this simulation. You can change (e.b. log in) to this guest system, install all tools you need and run your own RV programs. However, depending on the system chosen, you might need to cross compile your program on your host and transfer your program to the simulated guest system.

To make these steps a bit easier, I will show you how to get a Linux system up for an RV32 using Buildroot and for an RV64 using Debian.

RV32: Linux using Buildroot

This is very easy to achieve. Get Buildroot and build:

git clone https://git.buildroot.net/buildroot
make qemu_riscv32_virt_defconfig
make

Note, you can customize your Buildroot system before make with make menuconfig, for example, to install gdbserver. Then run Qemu with

qemu-system-riscv32 \
   -M virt \
   -bios output/images/fw_jump.elf \
   -kernel output/images/Image \
   -append "root=/dev/vda ro" \
   -drive file=output/images/rootfs.ext2,format=raw,id=hd0 \
   -device virtio-blk-device,drive=hd0 \
   -netdev user,id=net0 -device virtio-net-device,netdev=net0

These options do the following:

  • -M virt: emulated machine is of type virt which is just a general machine type for simulation.
  • -bios output/images/fw_jump.elf: bios image generated by Buildroot.
  • -kernel output/images/Image: kernel image generated by Buildroot.
  • -append "root=/dev/vda ro": command line for the kernel which defines the root filesystem /dev/vda read-only, which is the virtualized hard drive.
  • -drive file=output/images/rootfs.ext2,format=raw,id=hd0 : specifies the drive hd0 from the file generated by Buildroot in raw format.
  • -device virtio-blk-device,drive=hd0: adds the device driver for the drive hd0.
  • -netdev user,id=net0: network user mode with id net0.
  • -device virtio-net-device,netdev=net0: specifies the network driver for net0.

RV64: Linux using Debian

Fortunately, there are a Debian pre-baked images available here. Just download the RV64 image, extract and run with Qemu. In the extracted folder, you can follow the instructions of the readme.txt file.

qemu-system-riscv64 -machine virt -cpu rv64 -m 1G -device virtio-blk-device,drive=hd -drive file=image.qcow2,if=none,id=hd -device virtio-net-device,netdev=net -netdev user,id=net,hostfwd=tcp::2222-:22 -bios /usr/lib/riscv64-linux-gnu/opensbi/generic/fw_jump.elf -kernel /usr/lib/u-boot/qemu-riscv64_smode/uboot.elf -object rng-random,filename=/dev/urandom,id=rng -device virtio-rng-device,rng=rng -nographic -append "root=LABEL=rootfs console=ttyS0"

Many options are explained above. Further options are:

  • -cpu rv64: RV64 processor simulation.
  • -m 1G: use 1GB RAM.
  • -drive file=image.qcow2,if=none,id=hd: defines the image.qcow2 as hard drive with interface none.
  • ... ,hostfwd=tcp::2222-:22: forwards the port 2222 of the host to the port 22 of the guest. This enables ssh login.
  • -object rng-random,filename=/dev/urandom,id=rng: creates the object rng-random with id rng.
  • -device virtio-rng-device,rng=rng: defines the type of id rng.

As you can see, you might need OpenSBI and U-Boot for Qemu. However, Linux distributions like Debian already have packages for this (opensbiu-boot-qemu).

Other options you might find useful are:

  • -device VGA: for getting graphics, e.g. X11.
  • -vnc :1: for remote login with vnc using display :1 of your host.
  • -device virtio-keyboard,serial=virtio-keyboard: for getting a keyboard using X11.
  • -device virtio-mouse-pci: for a mouse with X11.
  • -serial pty: for a serial interface with /dev/pty of your host, the number will be displayed by start.
  • -fsdev local,id=sf,path=/tmp/mnt,security_model=mapped-file for a shared folder using /tmp/mnt/ of your host with id sf.
  • -device virtio-9p-pci,fsdev=sf,mount_tag=sf_tag for defining a mount tag for sharing using mount -t 9p sf_tag /mymount/point.

You can quit Qemu with Ctrl-A X and switch between its monitor and the simulation with Ctrl-A C.

For example, the following command lets you share files from the host (/tmp/host/) to the guest system (mount -t 9p sf_tag /tmp/guest/), forward two ports (22 and 1234) and login via vnc:

qemu-system-riscv64 -device VGA -machine virt   -cpu rv64   -smp 4   -m 4G   -kernel /usr/lib/u-boot/qemu-riscv64_smode/uboot.elf   -device virtio-blk-device,drive=hd   -drive file=image.qcow2,if=none,id=hd   -device virtio-net-device,netdev=net -netdev user,id=net,hostfwd=tcp::2222-:22,hostfwd=tcp::12345-:1234   -object rng-random,filename=/dev/urandom,id=rng   -device virtio-rng-device,rng=rng   -append "root=LABEL=rootfs console=ttyS0" -device virtio-keyboard,serial=virtio-keyboard  -device virtio-mouse-pci -serial pty -fsdev local,id=sf,path=/tmp/host,security_model=mapped-file -device virtio-9p-pci,fsdev=sf,mount_tag=sf_tag -vnc :1

Leave a Reply

Your email address will not be published. Required fields are marked *