Support arm64 hosts in the Windows 11 launcher

Pick qemu-system-aarch64, the AArch64 UEFI firmware and a per-architecture
QEMU config (NVMe disk, virtio-net, ramfb, USB installation media) on arm64,
render the answer file for the target architecture, and move the q35-only
disk and NIC devices out of the shared config. Update vm-images to match
arm64 Windows ISO names.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
guofangandClaude Sonnet 5.5 committed 2026-10-17 14:23:44 +08:00
1 parent 5feed51c61
commit c13ebc7d11
6 files changed
+124 -49

No files matched your search

+63 -30
View File
@@ -67,7 +67,19 @@ rdp_port = env("WIN11_VM_RDP_PORT") or "3389"
ssh_port = env("WIN11_VM_SSH_PORT") or "2222"
qemu_config_template = Path(
env("WIN11_VM_QEMU_CONFIG") or script_dir / "qemu" / "windows11.ini")
qemu_arch_config = script_dir / "qemu" / "windows11-amd64.ini"
def normalize_arch(arch):
if arch in ("arm64", "aarch64"):
return "arm64"
if arch in ("x86_64", "amd64"):
return "amd64"
die(f"unsupported architecture: {arch}")
host_arch = normalize_arch(env("VM_IMAGE_ARCH") or platform.machine())
qemu_binary = "qemu-system-aarch64" if host_arch == "arm64" else "qemu-system-x86_64"
qemu_arch_config = script_dir / "qemu" / f"windows11-{host_arch}.ini"
def pick_firmware(override, *candidates):
@@ -79,20 +91,36 @@ def pick_firmware(override, *candidates):
return Path(candidates[0])
uefi_code = pick_firmware(
env("WIN11_VM_UEFI_CODE"),
"/usr/share/OVMF/OVMF_CODE_4M.fd",
"/usr/share/edk2/ovmf/OVMF_CODE.fd",
"/opt/homebrew/share/qemu/edk2-x86_64-code.fd",
"/usr/local/share/qemu/edk2-x86_64-code.fd",
)
uefi_vars_template = pick_firmware(
env("WIN11_VM_UEFI_VARS"),
"/usr/share/OVMF/OVMF_VARS_4M.fd",
"/usr/share/edk2/ovmf/OVMF_VARS.fd",
"/opt/homebrew/share/qemu/edk2-i386-vars.fd",
"/usr/local/share/qemu/edk2-i386-vars.fd",
)
if host_arch == "arm64":
uefi_code = pick_firmware(
env("WIN11_VM_UEFI_CODE"),
"/usr/share/AAVMF/AAVMF_CODE.fd",
"/usr/share/edk2/aarch64/QEMU_EFI.fd",
"/opt/homebrew/share/qemu/edk2-aarch64-code.fd",
"/usr/local/share/qemu/edk2-aarch64-code.fd",
)
uefi_vars_template = pick_firmware(
env("WIN11_VM_UEFI_VARS"),
"/usr/share/AAVMF/AAVMF_VARS.fd",
"/usr/share/edk2/aarch64/vars-template-pflash.raw",
"/opt/homebrew/share/qemu/edk2-arm-vars.fd",
"/usr/local/share/qemu/edk2-arm-vars.fd",
)
else:
uefi_code = pick_firmware(
env("WIN11_VM_UEFI_CODE"),
"/usr/share/OVMF/OVMF_CODE_4M.fd",
"/usr/share/edk2/ovmf/OVMF_CODE.fd",
"/opt/homebrew/share/qemu/edk2-x86_64-code.fd",
"/usr/local/share/qemu/edk2-x86_64-code.fd",
)
uefi_vars_template = pick_firmware(
env("WIN11_VM_UEFI_VARS"),
"/usr/share/OVMF/OVMF_VARS_4M.fd",
"/usr/share/edk2/ovmf/OVMF_VARS.fd",
"/opt/homebrew/share/qemu/edk2-i386-vars.fd",
"/usr/local/share/qemu/edk2-i386-vars.fd",
)
daemonize = False
install_from_iso = False
download_only = False
@@ -108,8 +136,8 @@ If the system disk (DIR/disk.qcow2, see --state-dir) is missing, create it from
the Windows 11 QCOW2 base image.
The VM hardware (accelerator, memory, CPUs, drives, devices) is defined in
qemu/windows11.ini, which is rendered to qemu.ini in the state directory and
loaded with QEMU's -readconfig, followed by qemu/windows11-amd64.ini. Later QEMU
arguments override them, e.g. -- -m 4G -smp 4.
loaded with QEMU's -readconfig, followed by qemu/windows11-<arch>.ini (amd64 or arm64, chosen from the host
architecture or VM_IMAGE_ARCH). Later QEMU arguments override them, e.g. -- -m 4G -smp 4.
Use --install-from-iso to perform an automatic ISO installation instead.
If it exists, start Windows normally without attaching installation media.
@@ -226,8 +254,8 @@ if not disk_image.is_file():
base_image = Path(env("WIN11_VM_BASE_IMAGE")
or capture([str(image_downloader), "--print-path", "windows11"]))
if shutil.which("qemu-system-x86_64") is None:
die("qemu-system-x86_64 is not installed.")
if shutil.which(qemu_binary) is None:
die(f"{qemu_binary} is not installed.")
if installing:
if shutil.which("qemu-img") is None:
die("qemu-img is not installed.")
@@ -259,7 +287,7 @@ if installing_from_iso:
return source.exists() and source.stat().st_mtime > seed_iso.stat().st_mtime
if not seed_iso.is_file() or any(newer_than_seed(s) for s in seed_sources):
run([str(script_dir / "seediso" / "windows11" / "build-seed.sh"), str(seed_iso)])
run([str(script_dir / "seediso" / "windows11" / "build-seed.sh"), str(seed_iso), host_arch])
if pid_file.is_file():
old_pid = pid_file.read_text().strip()
@@ -336,14 +364,19 @@ qemu_args = [
# "-display", "none", "-spice", "port=5900,addr=127.0.0.1,disable-ticketing=on,agent-mouse=off", "-device", "virtio-vga",
]
if installing_from_iso:
qemu_args += [
"-drive", f"file={install_iso},if=none,id=install_media,media=cdrom,readonly=on",
"-device", "ide-cd,drive=install_media,bus=ide.1,bootindex=2",
"-drive", f"file={seed_iso},if=none,id=answer_media,media=cdrom,readonly=on",
"-device", "ide-cd,drive=answer_media,bus=ide.2,bootindex=3",
"-drive", f"file={tools_iso},if=none,id=tools_media,media=cdrom,readonly=on",
"-device", "ide-cd,drive=tools_media,bus=ide.3,bootindex=4",
]
# q35 has IDE for the installation media; "virt" does not, so use USB.
for media_id, media_file, boot_index, ide_bus in (
("install_media", install_iso, 2, "ide.1"),
("answer_media", seed_iso, 3, "ide.2"),
("tools_media", tools_iso, 4, "ide.3")):
device = (f"ide-cd,drive={media_id},bus={ide_bus},bootindex={boot_index}"
if host_arch == "amd64"
else f"usb-storage,drive={media_id},bootindex={boot_index}")
qemu_args += [
"-drive", f"file={media_file},if=none,id={media_id},media=cdrom,readonly=on",
"-device", device,
]
if installing_from_iso:
print("Starting Windows 11 automatic installation.")
elif installing:
@@ -357,7 +390,7 @@ print("Account: guofang / password; " + "; ".join(
if daemonize:
log_file.write_text("")
qemu_args += ["-serial", f"file:{log_file}", "-daemonize", "-pidfile", str(pid_file)]
run(["qemu-system-x86_64", *qemu_args, *extra_args])
run([qemu_binary, *qemu_args, *extra_args])
print(f"VM started (PID {pid_file.read_text().strip()}); console log: {log_file}")
else:
os.execvp("qemu-system-x86_64", ["qemu-system-x86_64", *qemu_args, *extra_args])
os.execvp(qemu_binary, [qemu_binary, *qemu_args, *extra_args])