I’ve had not recently tried to compile QEMU. Compiling it on macOS is also new to me. So here are some helpful tips. I’m using QEMU 10.2.1 and macOS Tahoe 26.3.1.
Update Homebrew
Firstly, if (like most people), you moved your homebrew installation at some stage from your old intel mac, then you will need to move it over to native ARM64. Why? Because the version of python you run will be the homebrew one, and if you have the old version of macOS it runs under Rosetta. As Python detects the processor running, your configuration for QEMU will then have x86_64 in, yet an ARM binary will be built. This will prevent you from running HVF (acceleration). This took me a little to work out. It seems updating homebrew to use ARM requires wiping it and reinstalling it. To do that, refer to someone who actually knows what they are doing with Homebrew, or failing that, here’s what I did:
Check which version of Homebrew you have installed (adapt to run any executable brew has installed)
|
1 2 3 |
$ brew install “python@3.14” $ file `which python3.14` /opt/homebrew/bin/python3.14: Mach–O 64–bit executable arm64 |
That should say arm64 like the above. If it does, great – skip the rest of this section. If it’s showing an intel installation, then carry on.
Dump the current installation to a file called Brewfile:
|
1 |
brew bundle dump |
Remove your current homebrew installation:
|
1 |
Remove any of the directories it complains about that are solely brew‘s reponsibility.
Install a new version of brew:
|
1 |
Check this now says arm64:
|
1 2 3 |
$ brew install “python@3.14” $ file `which python3.14` /opt/homebrew/bin/python3.14: Mach–O 64–bit executable arm64 |
That should now say arm64 like the above. If not, something has gone wrong!
Install all the brew goodness you previously had (look through Brewfile).
Quit and restart any other terminal windows you have open.
Install Homebrew pre-requisites
You’ll need the following:
|
1 |
$ brew install brew “python@3.14” “cdrtools” “glib” “imagemagick” “libslirp” “ninja” “pkgconf” “sphinx-doc” “toml11” |
Get the QEMU source
|
1 2 3 4 |
$ cd [somewhere appropriate] $ git clone https://gitlab.com/qemu-project/qemu.git $ cd qemu $ git checkout v10.2.1 |
Build with an appropriate configuration
|
1 2 3 |
$ ./configure —target–list=aarch64–softmmu —enable–hvf —enable–docs $ make –j12 qemu–system–aarch64 $ make –j12 |
Why the explicity make -j12 qemu-system-aarch64? Because make alone builds an unsigned binary, and an unsigned binary won’t run HVF.
You should now have a build/qemu-system-aarch64.
Test you can display something useful with the Cocoa UI (it will be a blank screen):
|
1 |
$ ./build/qemu–system–aarch64 –display cocoa –M virt,accel=hvf |
The main difficulties I found were the magic incantations to get HVF to work. That command will error if HVF isn’t built in right.
Get an Ubuntu cloud image to run
I’m going to assume you are going to put the cloud image in the directory above.
|
1 2 |
$ (cd .. ; curl –O http://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-arm64.img) $ chmod 444 ../noble–server–cloudimg–arm64.img |
I’ve made the image read-only, in case you forget -snapshot on the CLI to QEMU and thereby corrupt it.
Now we need to make a tiny ISO containing cloudinit parameters so we can set up ssh keys etc. (insert your keys as appropriate), and a password for the ubuntu user set to an imaginative password.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
$ mkdir ../cloud–init $ cd ../cloud–init $ mkdir my–cloud–config $ cat > my–cloud–config/meta–data <<EOF instance–id: new–fresh–boot–instance EOF $ cat > my–cloud–config/user–data <<EOF #cloud-config password: password chpasswd: expire: False ssh_pwauth: True ssh_authorized_keys: – ssh–ed25519 AAAAC3N...HoiJ your.email@example.com – ssh–rsa AAAAB...9xXoU= your.email@example.com EOF $ hdiutil makehybrid –o cidata.iso –joliet –iso –default–volume–name cidata my–cloud–config/ $ cd ../qemu |
Launch your cloud image
This runs the hypervisor attaching the cloud image but using -snapshot so changes are written to a temporary file and deleted when Qemu exits.
|
1 2 3 4 5 6 7 8 9 10 11 |
$ exec ./build/qemu–system–aarch64 \ –cpu host –M virt,accel=hvf –m 2048 \ –bios ./build/qemu–bundle/usr/local/share/qemu/edk2–aarch64–code.fd \ –display cocoa \ –device ramfb \ –device qemu–xhci –device usb–kbd –device usb–tablet \ –snapshot \ –netdev id=net00,type=user,hostfwd=tcp::2222–:22 \ –device virtio–net–pci,netdev=net00 \ –drive if=virtio,format=qcow2,file=../noble–server–cloudimg–arm64.img \ –drive if=virtio,format=raw,file=../cloud–init/cidata.iso |
You should see it booting. To log in via ssh, you want something like:
|
1 |
ssh –o StrictHostKeyChecking=no –p2222 ubuntu@127.0.0.1 |
In the above, note:
|
1 2 3 |
–display cocoa \ –device ramfb \ –device qemu–xhci –device usb–kbd –device usb–tablet \ |
which make Qemu use the Cocoa UI, a RAM frame-buffer, and keybord and mouse drivers. Once you have it working, you don’t need this, and can instead use:
|
1 |
–nodisplay |
if you like.
Leave a Reply