20231222

FreeBSD/armv7 in Qemu

Armv7 Virtualization

Just a quick note for booting an FreeBSD/armv7 virtual machine. I'll document a few bumps that I hit along the way. We'll be booting with qemu's virt armv7 virutal machine. We'll use EDK II as the firmware. 

The Setup: required packages

Before we get started, we need to install a few things. Thankfully, they are all installed with one command:
% sudo pkg install qemu

As of this wrirting, I used qemu 8.1.3, though I've done this with older versions. I suspect that 8.2 will be fine, but I haven't tried it yet.

The Setup: EDK2 prep

% mkdir qemu-armv7
% cd qemu-armv7
% dd if=/dev/zero of=pflash0.img bs=1m count=64
% dd if=/dev/zero of=pflash1.img bs=1m count=64
% dd if=/usr/local/share/qemu/edk2-arm-code.fd of=pflash0.img conv=notrunc
% dd if=/usr/local/share/qemu/edk2-arm-vars.fd of=pflash1.img conv=notrunc

This will create the needed flash images. pflash0.img can be shared among all the VMs you want, but you should have a specific pflash1.img per VM (though if you don't set any specific UEFI environment variables, it could be shared since you won't have multiple writers). There's no easy way to manage the per-VM vars file if you need to have different and do this on a large scale.

The Setup: A quick QEMU script

#!/bin/sh
qemu-system-arm \
  -M virt \
  -m 512 \
  -drive file=pflash0.img,format=raw,if=pflash,readonly=on \
  -drive file=pflash1.img,format=raw,if=pflash \
  -drive file=$1.img,if=virtio,cache=writethrough \
  -nographic

Will run whatever image you want to download. I run the GENERICSD.img that we publish. FreeBSD-13.2-RELEASE-arm-arm7-GENERICSD.img for example. You'll need to specify the image name. The virutal machine for arm, at least, will include the virtio network device (vtnet) if no other network is specified. Handy for scripts, but not always a good thing... 

The bug: vtnet

 The above won't actually work. At least with 13.2 and 14.0 and earlier.  There's a bug in virtio on armv7. The FreeBSD TCP/IP requires that packets be aligned such that they can be accessed via structures that define them. On most architectures, that allow unaligned access, this isn't a problem. However, on armv7, you can't do unalgined accesses in the kernel. Arm fixed this with aarch64, but not the older 32-bit arm. So if you were to try the above, you'd get an alignment fault when the image is getting its IP address and the kernel receives a UDP packet. I've since fixed this bug (maybe wrong, it turns out, but that's another story that will be sorted soon).

So, to run these earlier versions, you need to specify a network device. I've used the old 8139 PCI device, which works in 13.2 and 14.0. Add the following line after the last drive line.
-device rtl8139,netdev=network0 -netdev user,id=network0 \
and that will create a rl device, which the arm GENERICSD image can boot.

Wrap Up

That's about it. The image is what you'd expect. root/root can log you into the console. You can ssh in with freebsd/freebsd and su. If you have other users around, or are doing this on a public-ish network, you'll want to change these passwords right away.

There's several FreeBSD forum entries on this. I culled the script from run / boot freebsd arm 32bit image in qemu, though this matches a lot of other recipes online.