QEMU provides good support for system-level RISC-V emulation. A full FreeBSD system can be run using its virt platform.

To run FreeBSD/RISC-V, QEMU version 5.0 or later is required. This can be installed via emulators/qemu50 or emulators/qemu-devel.

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 METALOG

Note: 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 $EFIDIR

Create 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.img

riscv.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 \
    -device virtio-blk-device,drive=hd0

The 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 \
    -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).

riscv/QEMU (last edited 2021-06-08 13:41:42 by MitchellHorne)