Skip to content

Instantly share code, notes, and snippets.

@avoidik
Last active October 3, 2024 11:35
Show Gist options
  • Save avoidik/dbc7786365ad7f475a5166ab42d2f2f7 to your computer and use it in GitHub Desktop.
Save avoidik/dbc7786365ad7f475a5166ab42d2f2f7 to your computer and use it in GitHub Desktop.
Run Windows 11 IoT ARM on Mac M1 or similar

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).

$ brew install qemu coreutils samba

Download A64FRE ISO from archive.org, and rename it to windows.iso for simplicity.

$ 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

$ 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

$ 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)

$ qemu-img create -f qcow2 disk.qcow2 40G

Download virtio ISO with required drivers

$ curl -fsSLO https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/latest-virtio/virtio-win.iso

Start VM

$ 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:

$ 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:

> 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)

> REG ADD HKLM\SYSTEM\CurrentControlSet\Services\LanmanWorkstation\Parameters /v AllowInsecureGuestAuth /t REG_DWORD /d 1 /f

Disable security questions for local accounts

> REG ADD HKLM\SOFTWARE\Policies\Microsoft\Windows\System /v NoLocalPasswordResetQuestions /t REG_DWORD /d 1 /f

Classic context menu

> REG ADD HKCU\Software\Classes\CLSID\{86ca1aa0-34aa-4e8b-a509-50c905bae2a2}\InprocServer32 /f /ve

Disable services

> 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

> 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.

$ brew install swtpm

Make sure VM is turned off, then start swtpm as daemon (it will be running in the background)

$ 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

$ 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

$ kill -INT $(pidof swtpm)
@avoidik
Copy link
Author

avoidik commented Jun 8, 2024

Disable AppCompat

@echo off
REG ADD HKLM\Software\Policies\Microsoft\Windows\AppCompat /v DisableInventory /t REG_DWORD /d 1 /f
REG ADD HKLM\Software\Policies\Microsoft\Windows\AppCompat /v DisableUAR /t REG_DWORD /d 1 /f
REG ADD HKLM\Software\Policies\Microsoft\Windows\AppCompat /v DisablePCA /t REG_DWORD /d 1 /f
REG ADD HKLM\Software\Policies\Microsoft\Windows\AppCompat /v DisableEngine /t REG_DWORD /d 1 /f
REG ADD HKLM\Software\Policies\Microsoft\Windows\AppCompat /v SbEnable /t REG_DWORD /d 0 /f
REG ADD HKLM\Software\Policies\Microsoft\Windows\AppCompat /v AITEnable /t REG_DWORD /d 0 /f
REG ADD HKLM\Software\Policies\Microsoft\Windows\AppCompat /v DisablePropPage /t REG_DWORD /d 1 /f
REG ADD HKLM\Software\Policies\Microsoft\Windows\AppCompat /v VDMDisallowed /t REG_DWORD /d 1 /f
REG ADD HKLM\Software\Policies\Microsoft\Windows\AppCompat /v DisablePcaUI /t REG_DWORD /d 1 /f

Tweak Edge Browser

@echo off
REG ADD "HKLM\Software\Policies\Microsoft\Edge" /v "HideFirstRunExperience" /t REG_DWORD /d "1" /f
REG ADD "HKLM\Software\Policies\Microsoft\Edge" /v "ImportOnEachLaunch" /t REG_DWORD /d "0" /f
REG ADD "HKLM\Software\Policies\Microsoft\Edge" /v "EdgeCollectionsEnabled" /t REG_DWORD /d "0" /f
REG ADD "HKLM\Software\Policies\Microsoft\Edge" /v "HubsSidebarEnabled" /t REG_DWORD /d "0" /f
REG ADD "HKLM\Software\Policies\Microsoft\Edge" /v "EdgeShoppingAssistantEnabled" /t REG_DWORD /d "0" /f
REG ADD "HKLM\Software\Policies\Microsoft\Edge" /v "NewTabPageHideDefaultTopSites" /t REG_DWORD /d "1" /f
REG ADD "HKLM\Software\Policies\Microsoft\Edge" /v "BrowserSignin" /t REG_DWORD /d "0" /f
REG ADD "HKLM\Software\Policies\Microsoft\Edge" /v "ImplicitSignInEnabled" /t REG_DWORD /d "0" /f
REG ADD "HKLM\Software\Policies\Microsoft\Edge" /v "SyncDisabled" /t REG_DWORD /d "1" /f
REG ADD "HKLM\Software\Policies\Microsoft\Edge" /v "MicrosoftEdgeInsiderPromotionEnabled" /t REG_DWORD /d "0" /f
REG ADD "HKLM\Software\Policies\Microsoft\Edge" /v "DefaultBrowserSettingEnabled" /t REG_DWORD /d "0" /f
REG ADD "HKLM\Software\Policies\Microsoft\Edge" /v "StandaloneHubsSidebarEnabled" /t REG_DWORD /d "0" /f
REG ADD "HKLM\Software\Policies\Microsoft\Edge" /v "UserFeedbackAllowed" /t REG_DWORD /d "0" /f
REG ADD "HKLM\Software\Policies\Microsoft\Edge" /v "SearchbarAllowed" /t REG_DWORD /d "0" /f
REG ADD "HKLM\Software\Policies\Microsoft\Edge" /v "SearchbarIsEnabledOnStartup" /t REG_DWORD /d "0" /f
REG ADD "HKLM\Software\Policies\Microsoft\Edge" /v "BrowserGuestModeEnabled" /t REG_DWORD /d "0" /f
REG ADD "HKLM\Software\Policies\Microsoft\Edge" /v "StartupBoostEnabled" /t REG_DWORD /d "0" /f
REG ADD "HKLM\Software\Policies\Microsoft\Edge" /v "NewTabPageQuickLinksEnabled" /t REG_DWORD /d "0" /f
REG ADD "HKLM\Software\Policies\Microsoft\Edge" /v "VisualSearchEnabled" /t REG_DWORD /d "0" /f

@avoidik
Copy link
Author

avoidik commented Jun 14, 2024

To run older Windows

Create system disk

$ qemu-img create -f qcow2 winxp.img 40G

Setup phase

$ qemu-system-x86_64 \
    -m 1G \
    -smp 4,sockets=1,cores=2,threads=2 \
    -device VGA,vgamem_mb=64 \
    -cpu core2duo \
    -accel tcg,thread=multi \
    -machine pc \
    -netdev user,id=lan,ipv6=off \
    -device rtl8139,netdev=lan \
    -rtc base=localtime \
    -monitor stdio \
    -boot once=d \
    -drive file=winxp.img,if=ide,index=0,format=qcow2,media=disk,discard=unmap,detect-zeroes=unmap \
    -drive file=winflp.iso,if=ide,index=1,format=raw,media=cdrom,readonly=on

It is possible to use qemu-system-i386 instead, in this case we have to disable the Long-Mode/IA-32e cpu feature by providing -cpu core2duo,lm=off or compatible cpu model such as coreduo

Normal operational phase

$ qemu-system-x86_64 \
    -m 1G \
    -smp 4,sockets=1,cores=2,threads=2 \
    -device VGA,vgamem_mb=64 \
    -cpu core2duo \
    -accel tcg,thread=multi \
    -machine pc \
    -netdev user,id=lan,ipv6=off,hostfwd=tcp::3389-:3389 \
    -device rtl8139,netdev=lan \
    -rtc base=localtime \
    -monitor stdio \
    -drive file=winxp.img,if=ide,index=0,format=qcow2,media=disk,discard=unmap,detect-zeroes=unmap

You may want to try to set a better machine type, for example -machine q35

@avoidik
Copy link
Author

avoidik commented Jun 14, 2024

$ qemu-system-i386 \
    -m 1G \
    -device VGA,vgamem_mb=64 \
    -cpu coreduo \
    -accel tcg \
    -machine pc \
    -netdev user,id=lan,ipv6=off,hostfwd=tcp::3389-:3389 \
    -device rtl8139,netdev=lan \
    -rtc base=localtime \
    -monitor stdio \
    -drive file=winxp.img,if=ide,index=0,format=qcow2,media=disk,discard=unmap,detect-zeroes=unmap

@Desronist
Copy link

Hello,
I got "Display Output is not active" error when I tried virtio-gpu option.

@avoidik
Copy link
Author

avoidik commented Jun 25, 2024

@Desronist it may take some time to initialize the video output stream after boot, if not it is an indicator that something wrong with the video driver, try to boot with the ramfb again, make sure to install the driver, certutil may fail to import the self-signed certificate, just import it manually

@Desronist
Copy link

@avoidik As you said manually importing certs solved my problem. Thank you so much dude!

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