Skip to content

Instantly share code, notes, and snippets.

@nrjdalal
Last active February 1, 2024 05:31
Show Gist options
  • Save nrjdalal/e70249bb5d2e9d844cc203fd11f74c55 to your computer and use it in GitHub Desktop.
Save nrjdalal/e70249bb5d2e9d844cc203fd11f74c55 to your computer and use it in GitHub Desktop.
Create Virtual Machines using QEMU on Silicon based Apple Macs

Install QEMU on Silicon based Apple Macs (June 2021)

Option 1 - Automatically

zsh -c "$(curl -fsSL https://raw.githubusercontent.com/nrjdalal/silicon-virtualizer/master/install-qemu.sh)"

Option 2 - Manually

  • Install Xcode command line tools

xcode-select --install
  • Install Homebrew arm64

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> /Users/$(logname)/.zprofile
eval "$(/opt/homebrew/bin/brew shellenv)"
  • Install necessary packages for building

brew install libffi gettext glib pkg-config autoconf automake pixman ninja
  • Clone qemu

git clone https://github.com/qemu/qemu
  • Change directory to qemu repository

cd qemu
  • Checkout to commit dated June 03, 2021 v6.0.0

git checkout 3c93dfa42c394fdd55684f2fbf24cf2f39b97d47
  • Apply patch series v8 by Alexander Graf

curl https://patchwork.kernel.org/series/485309/mbox/ | git am
  • Building qemu installer

mkdir build && cd build
../configure --target-list=aarch64-softmmu
make -j8
  • Install qemu

sudo make install

Create Ubuntu Server using QEMU on Silicon based Apple Macs

First run, close manually after installation aka first reboot.

curl https://cdimage.ubuntu.com/releases/20.04/release/ubuntu-20.04.4-live-server-arm64.iso -o ubuntu-lts.iso

qemu-img create -f qcow2 virtual-disk.qcow2 8G

cp $(dirname $(which qemu-img))/../share/qemu/edk2-aarch64-code.fd .

dd if=/dev/zero conv=sync bs=1m count=64 of=ovmf_vars.fd

qemu-system-aarch64 \
  -machine virt,accel=hvf,highmem=off \
  -cpu cortex-a72 -smp 4 -m 4G \
  -device virtio-gpu-pci \
  -device virtio-keyboard-pci \
  -drive "format=raw,file=edk2-aarch64-code.fd,if=pflash,readonly=on" \
  -drive "format=raw,file=ovmf_vars.fd,if=pflash" \
  -drive "format=qcow2,file=virtual-disk.qcow2" \
  -cdrom ubuntu-lts.iso

Second run, enjoy your Ubuntu Server.

qemu-system-aarch64 \
  -machine virt,accel=hvf,highmem=off \
  -cpu cortex-a72 -smp 4 -m 4G \
  -device virtio-gpu-pci \
  -device virtio-keyboard-pci \
  -drive "format=raw,file=edk2-aarch64-code.fd,if=pflash,readonly=on" \
  -drive "format=raw,file=ovmf_vars.fd,if=pflash" \
  -drive "format=qcow2,file=virtual-disk.qcow2"

Bonus, connect via SSH and serve port 80 at localhost.

  1. Start server using -
qemu-system-aarch64 \
  -machine virt,accel=hvf,highmem=off \
  -cpu cortex-a72 -smp 4 -m 4G \
  -device virtio-gpu-pci \
  -device virtio-keyboard-pci \
  -drive "format=raw,file=edk2-aarch64-code.fd,if=pflash,readonly=on" \
  -drive "format=raw,file=ovmf_vars.fd,if=pflash" \
  -drive "format=qcow2,file=virtual-disk.qcow2" \
  -nic hostfwd=tcp:127.0.0.1:9922-0.0.0.0:22,hostfwd=tcp:127.0.0.1:9980-0.0.0.0:80 &
  1. Login via SSH using -
ssh <username>@127.0.0.1 -p 9922
  1. Visit http content at -
open http://127.0.0.1:9980
@corwin-of-amber
Copy link

I managed to get around the problem by editing build.ninja and adding arch --arm64e at the beginning of every command (cc, c++, clang). This is so weird that it was x86 by default. Now I've built it and it runs!

I tried running the Debian 10.7.0 installer, and I had to add these flags:

  -object rng-random,id=rng0,filename=/dev/urandom -device virtio-rng-pci,rng=rng0 \
  -device qemu-xhci -device usb-kbd \

Now it hangs after Booting Linux Kernel... 🤷

@mateuszdrab
Copy link

I end up launching QEMU via UTM and I must admit that whilst x64 OS works reasonably well, x86 is just slow as hell.

@cattyhouse
Copy link

this is the only guide that works! thank you very much!

@brainard52
Copy link

brainard52 commented Jun 21, 2021

I did something slightly different for my scripts. I also made some Windows scripts:

new_ubuntu.sh:

#!/bin/bash

efi_firm="$(dirname $(which qemu-img))/../share/qemu/edk2-aarch64-code.fd"

dd if=/dev/zero conv=sync bs=1m count=64 of=ubuntu_ovmf_vars.fd

qemu-img create -f qcow2 ubuntu.qcow2 512G

echo "starting"

qemu-system-aarch64 \
  -machine virt,accel=hvf,highmem=off \
  -cpu cortex-a72 -smp 4 -m 4G \
  -device qemu-xhci,id=usb-bus \
  -device usb-tablet,bus=usb-bus.0 \
  -device usb-mouse,bus=usb-bus.0 \
  -device usb-kbd,bus=usb-bus.0 \
  -device virtio-gpu-pci \
  -display default,show-cursor=on \
  -nic user,model=virtio \
  -drive format=raw,file=$efi_firm,if=pflash,readonly=on \
  -drive format=raw,file=ubuntu_ovmf_vars.fd,if=pflash \
  -device nvme,drive=drive0,serial=drive0,bootindex=0 \
  -drive if=none,media=disk,id=drive0,format=qcow2,file=ubuntu.qcow2 \
  -boot d \
  -device usb-storage,drive=drive2,removable=true,bootindex=2 \
  -drive if=none,media=cdrom,id=drive2,file=disk_images/ubuntu-21.04-live-server-arm64.iso

start_ubuntu.sh:

#!/bin/bash

efi_firm="$(dirname $(which qemu-img))/../share/qemu/edk2-aarch64-code.fd"

echo "starting"

qemu-system-aarch64 \
  -machine virt,accel=hvf,highmem=off \
  -cpu cortex-a72 -smp 4 -m 4G \
  -device qemu-xhci,id=usb-bus \
  -device usb-tablet,bus=usb-bus.0 \
  -device usb-mouse,bus=usb-bus.0 \
  -device usb-kbd,bus=usb-bus.0 \
  -device virtio-gpu-pci \
  -display default,show-cursor=on \
  -nic user,model=virtio \
  -drive format=raw,file=$efi_firm,if=pflash,readonly=on \
  -drive format=raw,file=ubuntu_ovmf_vars.fd,if=pflash \
  -device nvme,drive=drive0,serial=drive0,bootindex=0 \
  -drive if=none,media=disk,id=drive0,format=qcow2,file=ubuntu.qcow2 \

new_windows.sh:

#!/bin/bash

windows10vhdx="disk_images/Windows10_InsiderPreview_Client_ARM64_en-us_21354.VHDX windows.qcow2"
virtio-win-iso="disk_images/virtio-win-0.1.190.iso"

efi_firm="$(dirname $(which qemu-img))/../share/qemu/edk2-aarch64-code.fd"

dd if=/dev/zero conv=sync bs=1m count=64 of=windows_ovmf_vars.fd

qemu-img convert -O qcow2 "$windows10vhdx" windows.qcow2

echo "starting"

qemu-system-aarch64 \
  -machine virt,accel=hvf,highmem=off \
  -cpu cortex-a72 -smp 4 -m 4G \
  -device ramfb \
  -device qemu-xhci,id=usb-bus \
  -device usb-tablet,bus=usb-bus.0 \
  -device usb-mouse,bus=usb-bus.0 \
  -device usb-kbd,bus=usb-bus.0 \
  -nic user,model=virtio \
  -drive format=raw,file="$efi_firm",if=pflash,readonly=on \
  -drive format=raw,file=windows_ovmf_vars.fd,if=pflash \
  -drive if=none,media=disk,id=drive0,format=qcow2,file=windows.qcow2 \
  -device nvme,drive=drive0,serial=drive0,bootindex=0 \
  -device usb-storage,drive=drive2,removable=true,bootindex=2 \
  -drive if=none,media=cdrom,id=drive2,file="$virtio-win-iso"

# To enable networking after installing:
# 1. run `bcdedit.exe -set TESTSIGNING ON`
# 2. Open device manager and find the unknown ethernet device.
# 3. Choose to update and search for the driver on the computer.
# 4. Navigate to and select D:\NetKVM\w10\ARM64

You will need to download the Windows VHDX from here and the latest virtio-win iso from here

Be sure to update windows10vhdx and virtio-win-iso to have the proper paths to the VHDX and iso respectively. I created a directory within my vm directory called "disk_images" and put them there for organization's sake.

start_windows.sh:

#!/bin/bash

efi_firm="$(dirname $(which qemu-img))/../share/qemu/edk2-aarch64-code.fd"

echo "starting"

qemu-system-aarch64 \
  -machine virt,accel=hvf,highmem=off \
  -cpu cortex-a72 -smp 4 -m 4G \
  -device ramfb \
  -device qemu-xhci,id=usb-bus \
  -device usb-tablet,bus=usb-bus.0 \
  -device usb-mouse,bus=usb-bus.0 \
  -device usb-kbd,bus=usb-bus.0 \
  -nic user,model=virtio \
  -drive format=raw,file="$efi_firm",if=pflash,readonly=on \
  -drive format=raw,file=windows_ovmf_vars.fd,if=pflash \
  -drive if=none,media=disk,id=drive0,format=qcow2,file=windows.qcow2 \
  -device nvme,drive=drive0,serial=drive0,bootindex=0 \

If you want to attach a second drive to the machine, just add it by copying the last two lines and modify the drive id and boot index in each.

For example:

-drive if=none,media=disk,id=drive1,format=qcow2,file=foo.qcow2 \
-device nvme,drive=drive1,serial=drive1,bootindex=1 \

Something you could do with this is create an extra drive that can be used for multiple different machines. If you do that, however, be sure to only run one machine at any given time because I'm sure it would cause severe corruption if both were to write to the drive at the same time.

Another thing to be aware of:
Unfortunately ARM64 Windows doesn't have virtio-gpu drivers yet (as far as I can tell, and I've searched hard for them) so we're stuck with ramfb. I'm not entirely sure, but I think this means everything is rendered in software. Additionally, my machine appears to be restricted to three different resolutions: 640x480, 800x600, and 1024x768. You can set this by hitting escape on the "TianoCore" screen before the windows loading dot circle thing.

@meshonline
Copy link

meshonline commented Jul 11, 2021

So strange!

I am building qemu on Mac Mini Silicon M1 according to the above instructions, it successfully compiled, but the utility only supports the 'tcg' accelerator.

./qemu-system-aarch64 -accel help
Accelerators supported in QEMU binary:
tcg

I examined the configuration output, the compiler detected that the CPU is x86_64:

  Compilation
                         host CPU: x86_64
                  host endianness: little
  Targets and accelerators
                      KVM support: NO
                      HAX support: NO
                      HVF support: NO
                     WHPX support: NO
                     NVMM support: NO
                      Xen support: NO
                      TCG support: YES
                      TCG backend: native (x86_64)
                TCG debug enabled: NO
                      target list: aarch64-softmmu
                  default devices: YES
         out of process emulation: NO

Any hints?

@meshonline
Copy link

meshonline commented Jul 11, 2021

Never mind, I figured it out.

I had Anaconda3 installed on my machine, it uses Python 3.8, which can't detect the CPU correctly.

After I removed Anaconda3 and switched to Python 3.9, everything works.

@kiranchavala
Copy link

Getting invalid hcf error on m1 mac even after following the

zsh -c "$(curl -fsSL https://raw.githubusercontent.com/nrjdalal/silicon-virtualizer/master/install-qemu.sh)"

~ ❯ qemu-system-aarch64 \ 10:47:49 AM
-machine virt,accel=hvf,highmem=off
-cpu cortex-a72 -smp 4 -m 4G
-device virtio-gpu-pci
-device virtio-keyboard-pci
-drive "format=raw,file=edk2-aarch64-code.fd,if=pflash,readonly=on"
-drive "format=raw,file=ovmf_vars.fd,if=pflash"
-drive "format=qcow2,file=virtual-disk.qcow2"
-cdrom ubuntu-lts.iso
qemu-system-aarch64: invalid accelerator hvf

Copy link

ghost commented Sep 11, 2021

Wow thank you so much for this guide! :)

@cattyhouse
Copy link

Qemu 6.1 is out and patchwork v12 is here : https://patchwork.kernel.org/series/548227/mbox/

is it worth the update? how do i know which commit to apply the patch on? thanks

@jonathancross
Copy link

@meshonline

After I removed Anaconda3 and switched to Python 3.9, everything works.

Is kvm working?

@meshonline
Copy link

@meshonline

After I removed Anaconda3 and switched to Python 3.9, everything works.

Is kvm working?

No, but hvf works.

@jonathancross
Copy link

Not sure if my question was clear:
I mean inside of the Ubuntu guest... does kvm (nested virtualization) work there?

@meshonline
Copy link

Unfortunately, the nested Ubuntu guest machine does not support any virtualization.

@sokurenko
Copy link

Is 4G upper limit or will it be possible to have more in the future ?

@ericcurtin
Copy link

I use -M virt,highmem=off -m 6G -accel hvf -accel tcg -cpu cortex-a57 among a few other things and seems to work fine for me!

@amitsaha
Copy link

amitsaha commented Mar 6, 2022

Thank you @nrjdalal - after trying multipass and utm, great to have a Linux VM running from the building blocks!

@nrjdalal
Copy link
Author

nrjdalal commented Jun 28, 2022

Qemu 6.1 is out and patchwork v12 is here : https://patchwork.kernel.org/series/548227/mbox/

is it worth the update? how do i know which commit to apply the patch on? thanks

@cattyhouse the updates are always worthy, feel free to update to latest, I'm not currently using virtualization, if it was for me, I'd always go with latest updates, commits you can test at different dates

@KPhans
Copy link

KPhans commented Sep 12, 2022

running option 1 gives me:

ld: symbol(s) not found for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
ninja: build stopped: subcommand failed.

Definitely an M1 problem. I tried using lcurl flag but did not work. Any ideas? @nrjdalal

@ericcurtin
Copy link

@KPhans the version installed via brew works great out of the box now. Here's a wrapper script I use, which helps get the qemu command line args right:

https://gitlab.com/CentOS/automotive/sample-images/-/blob/main/osbuild-manifests/runvm

@KPhans
Copy link

KPhans commented Sep 13, 2022

@KPhans the version installed via brew works great out of the box now. Here's a wrapper script I use, which helps get the qemu command line args right:

https://gitlab.com/CentOS/automotive/sample-images/-/blob/main/osbuild-manifests/runvm

Thanks @ericcurtin ! How exactly do I use this script?

@ericcurtin
Copy link

@KPhans the version installed via brew works great out of the box now. Here's a wrapper script I use, which helps get the qemu command line args right:
https://gitlab.com/CentOS/automotive/sample-images/-/blob/main/osbuild-manifests/runvm

Thanks @ericcurtin ! How exactly do I use this script?

Like so... Create a qcow2 file as a virtual hard disk:

qemu-img create fedora_silverblue.qcow2 512G

Run a vm with an installer .iso file to install your linux vm:

runvm --memory 4G --cdrom Fedora-Silverblue-ostree-aarch64-36-1.5.iso fedora_silverblue.qcow2

On every proceeding run:

runvm --memory 4G fedora_silverblue.qcow2

@cattyhouse
Copy link

cattyhouse commented Sep 16, 2022

since you guys are using bash/zsh, you can always use array to have a better syntax

#!/bin/bash
#~/bin/qemu_ubuntu

options=(
# this is a comment
# enable hvf for the best performance
-machine virt,accel=hvf
# use a newer cpu, you can also use 'host' with qemu 7.0
#-cpu host
-cpu cortex-a72
# let's do 4core and 4GB RAM
-smp 4 -m 4G
# add a cd rom for the first time boot
-cdrom ubuntu-lts.iso
# let's go on, add anything you want
)
qemu-system-aarch64 "${options[@]}"

now move to your terminal and run

qemu_ubuntu

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment