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.

20230724

Some Hints For Splitting Commits

Some Hints For Splitting Commits

Sometimes in a code review, the commentators suggest that commits be broken up into smaller pieces. Here are a few of the collected tricks I have learned over the years. They are presented as 'editing the current tip of the tree' for simplicity. However, you can apply them to rebasing as well using the 'edit' action. The last section offers a strategy when you need to recombine the commits.

Splitting up

Sometimes you have a commit that you need to split up. This section will show you how to do that. Briefly, you will reset the HEAD of the tree to remove the commit from the current branch (the original hash remains in the repo), and then add and commit it piecemeal.

Before we get started on doing the change, there’s two steps to do:

  • Run git status to ensure the tree is clean.
    If the tree is unclean, then you can lose changes in the following steps (or have them accidentally merged in).

  • Run git show to show the commit you are working on.
    This is especially important when using these steps when doing a git rebase -i with and edit step.
    It also helps to know what the whole change looks like.

Now, to 'undo' the change from the repository, make sure the changes are what we think, then redo the changes.

  • Run git reset HEAD^ to 'undo' the change.
    This will set the pseudo-tag ORIG_HEAD to the value of HEAD before this command.

  • Run git diff and ensure that the diff matches the diff part of the git show you did earlier.

  • Run git add -ip to interactively select the subset of the change you want.
    There are instructions for how to edit the context diff at the end of the diff, so I won’t go over them here.

  • Run git commit -c ORIG_HEAD to commit the change.
    This commits the change and copies the commit message fro the pseudo-tag ORIG_HEAD.
    More often than not, when you are splitting changes, you will want a subset of the original message, or to edit it somehow.

  • Run git diff to see what’s left to commit.
    Repeat the last two steps until there’s no more changes left.
    git commit does not move ORIG_HEAD so you will start with the right commit message each time.

If you are really operating on the last change in your patch series, you are now done: the patch is split up. If you are doing this inside a git rebase -i with an edit action for the step, do not forget to now run git rebase --continue to complete the process. I usually just edit one at a time to keep things simple (I often forget to do the git rebase --continue if I have too many things to edit).

Rearranging the commits.

Sometimes, you have 2 commits that should be 2 different commits, or some similar rearrangement. Each of the new commits have parts of the original commits. What I like to do here is take the original two commits and break them down into the basic parts using the previous section. So I break the first commit down into the commit into 2 new commits. I repeat the process for the second commit. There are now 4 commits in my branch. I usually keep the original commit messages intact and do not edit them at this stage. I then use git rebase -i to recombine them using the 'squash' action. This gives me the new commits with copies of the original two commits' commit messages so I can edit each one down appropriately. I find that doing this in multiple steps makes it easier to keep track of everything. This allows easy backing out half way through if you realize you have done something in error. Doing only one or a few things at a time makes it easier to do that. git reflog can also help if you made a mistake several steps ago.