在 QEMU 安装 Windows on ARM

前言

所有的操作都需要管理员身份,执行前请二选一执行以下操作:

  • 按下 WIN + X 以打开“高级菜单”,选择“终端管理员”,“Windows PowerShell (管理员)”或“命令提示符(管理员)”
  • 按下 WIN + QWIN + S 以打开“Windows 搜索”,输入 wt.exe(终端)、cmd.exe(命令提示符)或 powershell.exe(Windows PowerShell),选择最佳匹配结果并点击“以管理员身份运行”

<> 里的内容是需要自行修改的内容,修改时需要将 <...> 以内的东西修改掉
/Index:<序号>,修改为 /Index:1

转载于:

更新日志

  • 2023/1/2 更新
    建议在构建镜像的时候用 https://uup.rg-adguard 的镜像,可以移除安装检查,可以有效避免 TPM 等硬件问题
    或者自己手动修改镜像,抑或是本地安装完成后再挂载到 QEMU

  • 2024/5/5 更新

    下面的操作中,你可以将 Dism++ 文件夹放到 virtio-win.iso 内,在安装完磁盘驱动的时候(或提前注入后),按 Shift + F10 呼出 CMD,然后打开 Dism++ARM64.exe
    随后使用 DiskPartDism++ 分别创建分区与安装系统,安装完后使用 Dism++ 的“驱动管理”,选择 virtio-win.iso 的根目录即可自动安装驱动
    不知道如何使用 DiskPart 可以参阅 章节中 #操作磁盘 的部分

  • 2024/7/28 更新

    在 QEMU 2024 以后的版本中(目前截止到 qemu-w64-setup-20240720.exe)均有 ramfb 显示不正常问题,但 2023 最后一版的 QEMU 还能正常显示,下载链接:https://qemu.weilnetz.de/w64/2023/qemu-w64-setup-20231224.exe

  • 2025/2/2 更新

    重写了该文章
    文章已从旧文章(https://goo-aw233.github.io/zh-CN/tutorial/VM/QEMU/WindowsARMonQEMU)迁移至此

  • 2025/11/03 更新

    提供 edk2-git 固件构建的 kraxel 在 Jenkins AutoBuilder 上已停止于 2022 年 7 月 19 日,https://www.kraxel.org/repos/jenkins/edk2 已不可访问,详见:https://www.kraxel.org/blog/2022/07/edk2-firmware-packaging

准备工具

  • 下文中,“存放文件夹”均指 E:\VMs\WoA11,后文不再阐述
  • “存放文件夹”创建时最好不带有非 ANSI 字符与空格(如中文)

推荐使用

推荐使用我提供的工具,但是不包含系统镜像,大部分工具已不可下载,我还可以提供现有的备份以供使用
OneDrive

Windows ARM 镜像

QEMU

安装 QEMU 时需要记住安装路径,安装完成后需要添加到 PATH 环境变量

QEMU 安装路径

UEFI 固件

Fedora 与 QEMU 提供的 edk2 固件无法正常使用,故使用 kraxel 的 edk2-git 固件构建(存档)
Linaro Releases
OneDrive
下载完成后打开压缩包,定位到路径:edk2.git-aarch64-0-20220719.209.gf0064ac3af.EOL.no.nore.updates.noarch.rpm\edk2.git-aarch64-0-20220719.209.gf0064ac3af.EOL.no.nore.updates.noarch.cpio\.\usr\share\edk2.git\aarch64,将里面的 QEMU_EFI.fdvars-template-pflash.raw 解压出来到“存放文件夹”

VirtIO 驱动光盘

最新版下载链接
将下载好的文件,放入到“存放文件夹”

配置 QEMU 环境变量

按下 WIN + R 以打开“运行”,输入 SystemPropertiesAdvanced.exe 并按下 Enter 以回车
在弹出的“高级系统设置”程序里,点击“环境变量”

高级系统设置

在“系统变量”里找到名为 Path 的项并双击,点击旁边的“新建”,最后在矩形框中输入 QEMU 的安装目录

环境变量

最后,全部点击“确定”即可

创建硬盘文件

输入如下命令来创建硬盘文件

1
qemu-img.exe create -f <硬盘格式> "<存放路径\文件名.硬盘格式>" <容量大小><单位>

比如我存放在“存放文件夹”,文件名为 OS,硬盘格式为 qcow2,容量大小为 80 GB
那么我的命令如下(需要区分大小写,而且路径和文件名最好不要有空格,用下划线 _ 来代替):

  • 硬盘格式可选的后缀为:rawhost_deviceqcowcowvdivmdkvpccloopimg
  • 硬盘容量可选的单位为:KMGTPE
1
qemu-img.exe create -f qcow2 "E:\VMs\WoA11\OS.qcow2" 80G

若没有别的错误则创建成功

创建硬盘文件

创建 VHD(X) 而不是使用以上硬盘格式

使用 VHDX 或 VHD 的好处是可以直接挂载到文件资源管理器,简单地操作里面的文件,性能更好,也可以挂载后直接使用 Dism++ 应用系统,安装完成后再启动到虚拟机
管理虚拟硬盘 (VHD) | Microsoft Learn

由于硬盘文件较脆弱,切勿强制关机,否则会造成硬盘文件损坏,特别是 VHD(X) 文件

编写启动脚本

在“存放文件夹”下,创建一个文本文档,并将其重命名为 Start.cmd

至此,“存放文件夹”下的文件都有:

E:\VMs\WoA11
├─OS.qcow2
├─QEMU_EFI.fd
├─Start.cmd
├─vars-template-pflash.raw
├─virtio-win-0.1.285.iso
└─zh-cn_windows_11_consumer_editions_version_26h1_arm64_dvd_900d64ce.iso

文件解释
  • OS.qcow2:硬盘文件
  • QEMU_EFI.fd:UEFI 固件代码镜像(作为 “只读” pflash 设备挂载,用于虚拟机启动
  • Start.cmd:快捷启动脚本
  • vars-template-pflash.raw:UEFI 变量存储镜像(模板/可写 pflash 设备,用于保持启动项、固件变量持久化)
  • virtio-win-0.1.285.iso:VirtIO 驱动镜像
  • zh-cn_windows_11_consumer_editions_version_26h1_arm64_dvd_900d64ce.iso:系统镜像

“存放文件夹”下的文件

编辑 Start.cmd,并输入如下内容

你可以在最顶端插入 chcp 65001,这样 CMD 内提示的中文报错就不会乱码,但是会导致 QEMU 变成英文

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
qemu-system-aarch64.exe ^
-M virt,gic-version=3 ^
-cpu <CPU 型号> ^
-smp <逻辑总数>,sockets=<插槽数>,cores=<核心数>,threads=<线程数> ^
-m <运行内存> ^
-accel tcg,thread=multi ^
-device qemu-xhci ^
-device usb-kbd ^
-device usb-tablet ^
-drive file="<存放文件夹\硬盘文件名.qcow2>",if=virtio ^
-nic user,model=virtio ^
-drive file="<存放文件夹\系统镜像文件名.iso>",media=cdrom,if=none,id=cdrom0 ^
-device usb-storage,drive=cdrom0 ^
-drive file="<存放文件夹\virtio-win 名称.iso>",media=cdrom,if=none,id=cdrom1 ^
-device usb-storage,drive=cdrom1 ^
-bios "<存放文件夹\QQEMU_EFI.fd>" ^
-device ramfb ^
-drive file="<存放文件夹\vars-template-pflash.raw>",if=pflash,index=1,format=raw
重要提示,必看内容
  1. 特别注意:自 Windows 11 24H2 采用 Rust 重写 Windows 内核文件后,所模拟的 SoC 需要为 ARMv8.1 及以上(即支持新的原子操作指令),否则将无法启动;不支持新的原子操作指令的 SoC 最高可启动的 Windows 版本为:Windows 11 Build 25163

    Windows 11 build 25163 is the last one than can boot on the Raspberry Pi 4 and older.
    Recent insider builds no longer work as they make extensive use of the new atomic instructions introduced in ARMv8.1.[3]

关于硬件的选择
  1. QEMU 查询可用的硬件命令
    • 查询支持模拟的主板:qemu-system-aarch64.exe -M help
    • 查询支持模拟的 CPU:qemu-system-aarch64.exe -cpu help
  2. 对于 Windows ARM64,可用的 CPU 型号:
    • Cortex 系列:cortex-a76cortex-a72cortex-a57cortex-a53(最成熟、测试最充分的模型)
    • max(向虚拟机暴露 QEMU 当前版本支持的所有 AArch64 指令集特性),使用 max 时推荐添加 ,pauth=off 以禁用指针验证,在纯软件模拟 (TCG) 下,这会导致 CPU 为了验证每一个跳转指令而产生海量的计算开销,经常导致内核初始化超时而卡死
    • Neoverse 系列:neoverse-n1neoverse-v1(专为服务器设计的架构)
  3. 若要开启嵌套虚拟化,请将 -M virt 修改为 -M virt,virtualization=true(不推荐开启,避免增加性能消耗)
  4. gic-version=3 开启 ITS 能显著提高 I/O 稳定性
  5. -smp 表示任务管理器可以显示多少个框框,sockets 表示有多少 CPU 插槽(推荐只设置为 1 以只设置 1 个 CPU 数,以避免 SKU 检查与性能损失),cores 表示 CPU 核心数量,threads 表示支持的线程数(推荐只设置为 1 以关闭超线程,减少性能损耗)
    • 计算公式为:总核心数 (smp) = sockets * cores * threads
  6. -accel tcg,thread=multi 为 QEMU 2.9 及之后的版本提供多核翻译支持
  7. 若想要免驱的网卡,请使用 -netdev user,id=net0 ^-device e1000e,netdev=net0 ^,而不是 -nic user,model=virtio ^

比如我的 CPU 型号为 max
CPU 逻辑总数为 4,插槽数为 1,核心数为 4,线程数为 1,运行内存大小为 6144 MB
那么我的命令如下:(需要区分大小写,而且路径和文件名最好不要有空格,用下划线 _ 来代替)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
qemu-system-aarch64.exe ^
-M virt,gic-version=3 ^
-cpu max,pauth=off ^
-smp 4,sockets=1,cores=4,threads=1 ^
-m 4096 ^
-accel tcg,thread=multi ^
-device qemu-xhci ^
-device usb-kbd ^
-device usb-tablet ^
-drive file="E:\VMs\WoA11\OS.qcow2",if=virtio ^
-nic user,model=virtio ^
-drive file="E:\VMs\WoA11\zh-cn_windows_11_consumer_editions_version_26h1_arm64_dvd_900d64ce.iso",media=cdrom,if=none,id=cdrom0 ^
-device usb-storage,drive=cdrom0 ^
-drive file="E:\VMs\WoA11\virtio-win-0.1.285.iso",media=cdrom,if=none,id=cdrom1 ^
-device usb-storage,drive=cdrom1 ^
-bios "E:\VMs\WoA11\QEMU_EFI.fd" ^
-device ramfb ^
-drive file="E:\VMs\WoA11\vars-template-pflash.raw",if=pflash,index=1,format=raw

保存后,双击 Start.cmd 以启动虚拟机

若正在使用大小核的 CPU,推荐将 qemu-system-aarch64.exe 的“处理器相关性”设置为全大核

修改分辨率

在启动阶段,按下 ESC 以进入 UEFI 固件设置

启动选项

进入固件设置后,选择 Device Manager

Device Manager

选择 OVMF Platform Configuration

OVMF Platform Configuration

Change Preferred Resolution for Next Boot 中,将分辨率修改为 1024x768

Change Preferred Resolution for Next Boot

完成后,按下 F10 以保存设置,按下 Y 确认保存,随后按下两次 ESC 以退出固件设置,选择到最下方的 Reset 以重启虚拟机

安装系统

推荐开启“视图” > “鼠标经过时捕获”
若开启了“捕获输入”,请按 Ctrl + Alt + G 以脱离虚拟机

结果漫长的等待后就可以进入系统了

Windows 安装程序

如果遇到 Synchronous Exception at ... 的问题,请替换 QEMU_EFI.fdvars-template-pflash.raw 文件,并重新配置 BIOS;或者是重新尝试使用其他任意键以继续
Synchronous Exception

Windows 安装程序

等待几分钟,虚拟机会自动进入到 Windows 安装程序

在选择磁盘的时候,点击“加载驱动程序”

加载驱动程序

点击“浏览”

浏览驱动

选择 VirtIO-Win 光驱,找到名为 viostor 的文件夹,选择里面 w11 文件夹下的 ARM64 文件夹(如果安装的是 Windows 10 则选择 w10,以此类推)

驱动路径

选择驱动程序,点击“安装”,这样就可以正常加载磁盘了

安装驱动

随后,即可开始安装 Windows

安装 Windows

部署系统

系统会经历两次重启,我在 NVMe 上用了约一个小时才从 Windows 安装程序进入到 OOBE

第一次重启

若在“准备就绪”阶段等待时间过久,或提示安装错误等问题,可用以下方式强制进入 OOBE:
按下 Shift + F10 来打开 CMD,输入以下命令并按下 Enter 以回车

1
oobe\msoobe.exe

“请稍等…”阶段等待几分钟,当微软输入法工具栏出现之后,再等待几分钟即可进入到 OOBE

OOBE

由于我们暂时没有安装网卡驱动,所以需要跳过联网,详情请参阅:跳过Windows 激活联网要求

若期间 OOBE 仍要求联网,可以按下 Shift + F10 打开 CMD,输入 explorer 并回车,并按照安装网卡驱动的方式安装网卡驱动后继续


若无法跳过账户登录或只能登录到工作账户

若发现只能登录到工作账户,请按下 Ctrl + Shift + F3 以跳过 OOBE
在“计算机管理”里启用 Administrator 或新建其他管理员账户,随后以管理员身份运行 CMD 并输入命令:

1
xcopy "%SystemRoot%\System32\oobe\audit.exe" "%Public%\Desktop\Backup" /X
1
xcopy "%SystemRoot%\System32\svchost.exe" "%SystemRoot%\System32\oobe\audit.exe" /X

完成后重启虚拟机即可


完成 OOBE 后,就会开始准备桌面与用户配置
至此,系统就安装完成了

正在准备桌面

桌面

安装网卡驱动

右键“开始”菜单,选择“终端(管理员)”、“Windows PowerShell(管理员)”或“命令提示符(管理员)”
输入以下命令以开启“测试模式”,并重启

1
bcdedit /set testsigning on

关于加载已进行测试签名的驱动程序,请参阅:允许加载已进行测试签名的驱动程序 - Windows drivers | Microsoft Learn

重启之后,在文件资源管理器里打开 VirtIO-Win 光驱,定位到 \NetKVM\w11\ARM64(如果是 Windows 10 就选择 w10 文件夹,以此类推)
右键类型为“安装信息”的 netkvm 文件,选择“安装”

安装 NetKVM

使用体验

无比慢,特别慢,非常慢,巨慢,受到一些限制,x64 的还不能很好地转译到 ARM64

在系统中

任务管理器

后记

  1. 性能与显示
    因为没有显卡驱动以及其他的驱动,在 QEMU 模拟 Windows ARM 性能会大打折扣,整个过程会非常漫长,建议将文件存放在 SSD 内
    如果你会添加驱动的话,可以修改 install.wiminstall.esdboot.wim,向内添加了 VirtIO 驱动之后再添加如下参数到 Start.cmd 以支持 virtio-gpu 输出
1
2
-device virtio-gpu-pci,virgl=on ^
-display gtk,gl=on ^
  1. 光驱管理
    如果你后面不需要光驱了可以把如下内容删除
1
2
3
4
-drive file="<存放文件夹\系统镜像文件名.iso>",media=cdrom,if=none,id=cdrom ^
-device usb-storage,drive=cdrom ^
-drive file="<存放文件夹\virtio-win 名称.iso>",media=cdrom,if=none,id=cdrom1 ^
-device usb-storage,drive=cdrom1 ^
  1. 驱动管理
    文章内仅模拟了所需的硬件及网卡,其他的硬件需要自行查询
    镜像内其他驱动的大致意思如下:[4]

    • NetKVM: Virtio 网络驱动
    • viostor: Virtio 块驱动
    • vioscsi: Virtio SCSI 驱动
    • viorng: Virtio RNG 驱动
    • vioser: Virtio 串口驱动
    • Balloon: Virtio 内存气球驱动
    • qxl: 用于 Windows 7 及之前版本的 QXL 显卡驱动(virtio-win-0.1.103-1 和之后版本会创建)
    • qxldod: 用于 Windows 8 及之后版本的 QXL 显卡驱动(virtio-win-0.1.103-2 和之后版本会创建)
    • pvpanic: QEMU pvpanic 设备驱动(virtio-win-0.1.103-2 和之后版本会创建)
    • guest-agent: QEMU Guest Agent 32bit 和 64bit 安装包
    • qemupciserial: QEMU PCI 串口设备驱动
    • vfd: 用于 Windows XP 下的 VFD 软驱镜像
  2. 优化
    尝试 BetaWiki 给出的解决方案以关闭打印机、磁盘整理与休眠

1
2
3
4
5
6
sc.exe stop "Spooler"
sc.exe config "Spooler" start= disabled
sc.exe stop "WSearch"
sc.exe config "WSearch" start= disabled
schtasks.exe /Delete /TN "\Microsoft\Windows\Defrag\ScheduledDefrag" /F
powercfg.exe -h off
  1. 传输文件
    在物理机内添加共享文件夹,权限改成 Everyone 并允许读取写入(如果你怕会被其他人截取则在映射网络驱动器时需要输入物理机的系统账户以登录到共享文件夹),然后在虚拟机内映射网络驱动器即可

  2. PE 或 Setup 加载驱动
    在 PE 或 Setup 下,可以使用 drvload 命令来加载驱动

更多关于 Devload 的命令,请参阅 Drvload 命令行选项 | Microsoft Learn

1
drvload "path\to\fileName.inf"

示例如下(可使用 treedir 命令来列出文件夹内容)

1
drvload "E:\viostor\w11\ARM64\viostor.inf"

参考文献

  1. BetaWorld Wiki(由此改编)
  2. 封面:BetaWorld Wiki
  3. FAQ | Windows on R
  4. Windows virtio 驱动_51CTO博客_virtio驱动