# How to run Windows on Mac We're going to run Windows 11 IoT (ARM) on Mac M1 (ARM) using the native binary translation, thanks to up-to-date version of qemu which has native hardware support. ## Prerequisites Install qemu and required tools (coreutils - we need truncate/gtruncate, dd/gdd CLI tools, samba - to share files between host and guest VM). ```terminal $ brew install qemu coreutils samba ``` Download A64FRE ISO from [archive.org](https://archive.org/details/Windows11LTSC), and rename it to `windows.iso` for simplicity. ```terminal $ sha256sum 26100.1.240331-1435.ge_release_CLIENT_ENTERPRISES_OEM_A64FRE_en-us.iso 743fdddf1c774cd97ec96b99b12577869cd320d24d7ca97c1c9dc44ad172f9ae 26100.1.240331-1435.ge_release_CLIENT_ENTERPRISES_OEM_A64FRE_en-us.iso ``` Or, download an evaluation copy from: https://www.microsoft.com/en-us/evalcenter/evaluate-windows-11-iot-enterprise-ltsc ## Prepare UEFI Prepare UEFI firmware image ```terminal $ gtruncate -s 64m efi.img $ gdd if=/opt/homebrew/opt/qemu/share/qemu/edk2-aarch64-code.fd of=efi.img conv=notrunc ``` Prepare UEFI variables image ```terminal $ gtruncate -s 64m vars.img $ gdd if=/opt/homebrew/opt/qemu/share/qemu/edk2-arm-vars.fd of=vars.img conv=notrunc ``` ## Install Windows Prepare system disk image (put as much space as you need) ```terminal $ qemu-img create -f qcow2 disk.qcow2 40G ``` Download virtio ISO with required drivers ```terminal $ curl -fsSLO https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/latest-virtio/virtio-win.iso ``` Start VM ```terminal $ qemu-system-aarch64 \ -m 4G -smp 4 -cpu host -M virt -accel hvf \ -drive if=pflash,format=raw,file=efi.img,readonly=on \ -drive if=pflash,format=raw,file=vars.img \ -device ramfb \ -device qemu-xhci \ -device usb-kbd \ -device usb-tablet \ -netdev user,id=net,ipv6=off,smb="$HOME" \ -device virtio-net-pci,netdev=net \ -device usb-storage,drive=install \ -drive if=none,id=install,format=raw,media=cdrom,readonly=on,file=windows.iso \ -device usb-storage,drive=virtio-drivers \ -drive if=none,id=virtio-drivers,format=raw,media=cdrom,readonly=on,file=virtio-win.iso \ -device virtio-blk,drive=system \ -drive if=none,id=system,format=qcow2,file=disk.qcow2 ``` Press any key when prompted at boot. Keep sane defaults, skip product key if you don't have one. Select `Windows 11 IoT Enterprise LTSC` edition, so that we skip TPM2 check. If no hard disk drives were identified by the installer, just click `Load driver` and side-load an appropriate driver from the mounted ISO drive (e.g., click `Browse`, nagivate to CD-ROM named `virtio-win`, select `viostor\w11\ARM64` folder). When presented with the network connection dialog, click SHIFT+F10, type `oobe\bypassnro.cmd` and hit enter. Wait for reboot to be completed. Next time on the same screen press `no internet connection` button. When presented with the password selection prompt leave it empty to bypass security questions. Once installation is finished install network controller driver from the same CD-ROM drive (press WIN+R, run `devmgmt.msc`, find `Ethernet controller`, install driver, nagivate to CD-ROM named `virtio-win` and select `NetKVM\w11\ARM64` folder). Install guest-tools from https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/latest-virtio/virtio-win-guest-tools.exe I was not able to find a valid driver for unknown `ACPI\LNRO0005` device. Keep it as is. This is the point where you may want to stop, because we are done with the installation. ## virtio-gpu It should work out of the box, we can omit most of the properties and switch to virtio-gpu as follows: ```terminal $ qemu-system-aarch64 \ -m 4G -smp 4 -cpu host -M virt -accel hvf \ -drive if=pflash,format=raw,file=efi.img,readonly=on \ -drive if=pflash,format=raw,file=vars.img \ -device virtio-gpu-pci \ -display default \ -device qemu-xhci \ -device usb-kbd \ -device usb-tablet \ -netdev user,id=net,ipv6=off,smb="$HOME" \ -device virtio-net-pci,netdev=net \ -device virtio-blk,drive=system \ -drive if=none,id=system,format=qcow2,file=disk.qcow2 ``` If not, try to boot with `-device ramfb` again, and install required drivers from the CD-ROM manually: ```terminal > e: > cd cert > certutil -addstore Root Virtio_Win_Red_Hat_CA.cer > certutil -addstore -f TrustedPublisher Virtio_Win_Red_Hat_CA.cer > cd ..\viogpudo\w11\ARM64 > pnputil /add-driver viogpudo.inf /install ``` Now try to boot with `-device virtio-gpu-pci` again. ## Minor tweaks Allow guest access to Samba share (it is accessible over `\\10.0.2.4\qemu` in Windows VM) ```terminal > REG ADD HKLM\SYSTEM\CurrentControlSet\Services\LanmanWorkstation\Parameters /v AllowInsecureGuestAuth /t REG_DWORD /d 1 /f ``` Disable security questions for local accounts ```terminal > REG ADD HKLM\SOFTWARE\Policies\Microsoft\Windows\System /v NoLocalPasswordResetQuestions /t REG_DWORD /d 1 /f ``` Classic context menu ```terminal > REG ADD HKCU\Software\Classes\CLSID\{86ca1aa0-34aa-4e8b-a509-50c905bae2a2}\InprocServer32 /f /ve ``` Disable services ```terminal > sc stop DiagTrack > sc config DiagTrack start=disabled > sc stop Spooler > sc config Spooler start=disabled > sc stop WSearch > sc config WSearch start=disabled ``` Disable Recall and AI related features ```terminal > REG ADD HKLM\SOFTWARE\Policies\Microsoft\Windows\WindowsAI /v DisableAIDataAnalysis /t REG_DWORD /d 1 /f > REG ADD HKCU\SOFTWARE\Policies\Microsoft\Windows\WindowsAI /v DisableAIDataAnalysis /t REG_DWORD /d 1 /f > REG ADD HKLM\Software\Policies\Microsoft\Windows\WindowsCopilot /v TurnOffWindowsCopilot /t REG_DWORD /d 1 /f > REG ADD HKCU\Software\Policies\Microsoft\Windows\WindowsCopilot /v TurnOffWindowsCopilot /t REG_DWORD /d 1 /f ``` ## TPM2 If you are looking for the TPM2 software emulation. ```terminal $ brew install swtpm ``` Make sure VM is turned off, then start swtpm as daemon (it will be running in the background) ```terminal $ swtpm_setup --create-config-files skip-if-exist $ swtpm_setup --tpmstate "$(pwd)/tpm" --tpm2 --create-ek-cert --create-platform-cert $ swtpm socket --tpmstate dir="$(pwd)/tpm" --ctrl type=unixio,path="$(pwd)/tpm/swtpm-sock" --tpm2 --daemon ``` Add these command-line parameters ```terminal $ qemu-system-aarch64 \ -chardev socket,id=chrtpm,path=./tpm/swtpm-sock \ -tpmdev emulator,id=tpm0,chardev=chrtpm \ -device tpm-tis-device,tpmdev=tpm0 \ ... ``` To terminate TPM2 daemon ```terminal $ kill -INT $(pidof swtpm) ```