QEMU provides good support for system-level RISC-V emulation. A full FreeBSD system can be run using its virt platform.
For best results, use the active version of QEMU provided by: emulators/qemu.
Also required is the OpenSBI bootloader, which can be installed from the sysutils/opensbi port or package.
Generate a root filesystem
If you have built FreeBSD by hand and would like to generate a root filesystem and bootable image, you can follow these steps. If you downloaded a bootable FreeBSD image, skip to #Boot FreeBSD.
Create root partition
# Install the system to a staging directory
setenv ROOTFSDIR $HOME/rootfsstage
make TARGET=riscv DESTDIR=$ROOTFSDIR NO_ROOT=yes installworld
make TARGET=riscv DESTDIR=$ROOTFSDIR NO_ROOT=yes installkernel
make TARGET=riscv DESTDIR=$ROOTFSDIR NO_ROOT=yes distribution
# Make any desired tweaks to the system, e.g. /etc/fstab
echo "# Device Mountpoint FStype Options Dump Pass#" > $ROOTFSDIR/etc/fstab
echo "/dev/gpt/rootfs / ufs rw,noatime 1 1" >> $ROOTFSDIR/etc/fstab
echo "/dev/gpt/swapfs none swap sw 0 0" >> $ROOTFSDIR/etc/fstab
echo "hostname=qemu" > $ROOTFSDIR/etc/rc.conf
# Add be sure to add them to METALOG
echo "./etc/fstab type=file uname=root gname=wheel mode=0644" >> $ROOTFSDIR/METALOG
echo "./etc/rc.conf type=file uname=root gname=wheel mode=0644" >> $ROOTFSDIR/METALOG
# Create a UFS root filesystem from that directory
cd $ROOTFSDIR
makefs -B little \
-o label=rootfs -o version=2 -o softupdates=1 \
-D -s 6g \
rootfs.ufs METALOGNote: specify -s or -f to ensure the filesystem is created with free inodes
Create EFI partition
# Copy the EFI bootloader
setenv EFIDIR $HOME/efistage
mkdir -p $EFIDIR/EFI/BOOT/
cp $ROOTFSDIR/boot/loader.efi $EFIDIR/EFI/BOOT/BOOTRISCV64.EFI
# Create an EFI partition (msdosfs)
makefs -t msdos \
-o fat_type=32 -o sectors_per_cluster=1 -o volume_label=EFISYS \
-s 50m \
efi.part $EFIDIRCreate Image File
Now, to combine our root and EFI partitions into a final image file:
mkimg -s gpt -f raw \ # GPT partition scheme
-p efi:=efi.part \
-p freebsd-swap/swapfs::2G \ # Swap partition (optional)
-p freebsd-ufs/rootfs:=rootfs.ufs \
-o riscv.imgriscv.img is the final output. You may clean up all other temporary objects as you choose.
Boot FreeBSD
To boot using u-boot, install sysutils/u-boot-qemu-riscv64.
qemu-system-riscv64 -machine virt -smp 2 -m 2048 -nographic \
-bios /usr/local/share/opensbi/lp64/generic/firmware/fw_jump.elf \
-kernel /usr/local/share/u-boot/u-boot-qemu-riscv64/u-boot.bin \
-drive file=/path/to/riscv.img,format=raw,id=hd0,if=none \
-device virtio-blk-device,drive=hd0The FreeBSD kernel can also be booted directly, skipping the u-boot and loader(8) stages. This can be useful for quicker turnaround during kernel development, but is in general less flexible compare to u-boot.
qemu-system-riscv64 -machine virt -smp 2 -m 2048 -nographic \
-bios /path/to/opensbi/fw_jump.elf \
-kernel /path/to/freebsd/kernel \
-drive file=/path/to/riscv.img,format=raw,id=hd0,if=none \
-device virtio-blk-device,drive=hd0
Networking
There are two main networking backends offered by QEMU, user and tap. User-mode networking requires almost no configuration, but tap based networking offers better performance, and allows the guest to obtain its own IP address on the network.
Configure tap(4) (Optional)
If using tap(4) for networking (as opposed to user-mode networking), you will need to load the (tun)tap module(s) if not already loaded.
If FreeBSD >= 13:
sudo kldload if_tuntap
Else:
sudo kldload if_tun if_tap
Configure the network with tap(4):
ifconfig tap0 create up ifconfig bridge0 create up ifconfig bridge0 addm em0 addm tap0 # em0 might depend on your ethernet device
User Networking
Add the following arguments to the QEMU commandline:
-netdev user,id=net0,ipv6=off,hostfwd=tcp::8022-:22 -device virtio-net-device,netdev=net0
This will forward port 8022 on the host to port 22 in the guest, meaning you can connect via ssh(1):
$ ssh -p 8022 root@localhost
Tap Networking
Add the following arguments to the QEMU commandline.
# ifname points to your tap device of choice -netdev tap,ifname=tap0,script=no,id=net0 -device virtio-net-device,netdev=net0
Tips & Tricks
Commandline arguments
You can specify kernel commandline arguments via QEMU's -append option. For example:
-append "-v vfs.root.mountfrom=/dev/vtbd1p3"
The -v argument enables verbose boot messages, while the variable assignment instructs the system to attempt to mount root from /dev/vtbd1p3. These will be ignored if booting with loader(8).