Apple's built-in QEMU guest agent (AppleQEMUGuestAgent, launched by launchd when a virtio console port org.qemu.guest_agent.0 appears; guest-exec as root) is attached by vmix run --macos and the NixOS module. AppleVirtIO.kext on x86 Tahoe drives virtio-fs, block, console, input, net — verified in QEMU. - customizeImage: `bootScript` — online step through the guest agent (driver mode qga): boot the image, run the script as root with the VMIX volume, shut down through the agent. `as_user` runs commands in the logged-in session. - templates.software: pkg/app (offline in the PE), script/homebrew (online). - templates.profile.settings: widgets, wallpaper (pinned desktoppr — Apple Events need TCC consent that a headless session cannot give), dock apps, autohide, dark mode, hidden files. - generalize: persistHome (fstab LABEL=vmix-home /Users), hideWidgets offline. - formatVolume: formats a blank disk image as APFS by booting the PE (~35 s); idempotent. - NixOS module: macos.guestAgent (/run/vmix/qga-<name>.sock), shares via virtiofsd + vhost-user-fs (Apple automount tag for the first share, others mounted through the agent), macos.homeDisk (created + formatted on first start, virtio-blk), SPICE keeps -vga vmware for macOS. - CLI: vmix run --macos --share DIR --home FILE --qga PATH. - qemu.nix helpers; README section. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XsESshRCoBoUVWV9qKURUF
65 lines
4 KiB
Nix
65 lines
4 KiB
Nix
# QEMU pieces shared by the macOS image builders, the vmix CLI and the NixOS module.
|
|
# Mirrors OSX-KVM's OpenCore-Boot.sh: q35, Skylake-Client CPU spoof (works on AMD
|
|
# hosts too), AppleSMC with the OSK, XHCI keyboard/tablet, AHCI disks, VMware SVGA.
|
|
{ pkgs, lib, ... }:
|
|
rec {
|
|
osk = "ourhardworkbythesewordsguardedpleasedontsteal(c)AppleComputerInc";
|
|
|
|
# OSX-KVM's CPU line for Sequoia/Tahoe; AVX2 capable, Intel vendor for the kernel
|
|
defaultCpu = "Skylake-Client,-hle,-rtm,kvm=on,vendor=GenuineIntel,+invtsc,vmware-cpuid-freq=on,+ssse3,+sse4.2,+popcnt,+avx,+aes,+xsave,+xsaveopt,check";
|
|
|
|
# The NIC is pinned to a fixed PCI slot so OpenCore can mark it built-in
|
|
# (required for en0 / Apple ID, iMessage, App Store).
|
|
nicAddr = "0x12";
|
|
nicDevicePath = "PciRoot(0x0)/Pci(0x12,0x0)";
|
|
|
|
# `-nic user` cannot pin a PCI address, so netdev + device
|
|
netArgs = { mac, netdev ? "user,id=net0", extra ? "" }:
|
|
"-netdev ${netdev} -device virtio-net-pci,netdev=net0,mac=${mac},bus=pcie.0,addr=${nicAddr}${extra}";
|
|
|
|
# Devices macOS needs (no accel, disks, display adapter or display server here).
|
|
# No isa-applesmc: QEMU's stub only answers the OSK keys, and its presence makes
|
|
# VirtualSMC (which carries the OSK itself) step aside, leaving Apple's SMC
|
|
# driver on the stub — whose missing watchdog keys panic the restart path on
|
|
# macOS 26. VirtualSMC alone is the standard Hackintosh setup.
|
|
deviceArgsFor = { appleSmc ? false }:
|
|
''${lib.optionalString appleSmc ''-device isa-applesmc,osk="${osk}" ''}-smbios type=2 -device qemu-xhci,id=xhci -device usb-kbd,bus=xhci.0 -device usb-tablet,bus=xhci.0 -device usb-ehci,id=ehci -device ich9-intel-hda -device hda-duplex -device ich9-ahci,id=sata -global ICH9-LPC.disable_s3=1'';
|
|
deviceArgs = deviceArgsFor { };
|
|
|
|
# macOS has no QXL/virtio-gpu driver; VMware SVGA gives a plain framebuffer
|
|
vgaArgs = "-vga vmware";
|
|
|
|
machineArgs = { cpu ? defaultCpu, smp ? 4, memSize ? 4096, appleSmc ? false }:
|
|
"-accel kvm -machine type=q35 -cpu ${cpu} -smp ${toString smp},sockets=1,cores=${toString smp},threads=1 -m ${toString memSize} ${deviceArgsFor { inherit appleSmc; }} ${vgaArgs}";
|
|
|
|
# SATA disk on a given port. Store files are read-only: callers create qcow2 overlays.
|
|
sataDrive = { id, port, file, format ? "qcow2", extra ? "" }:
|
|
"-drive id=${id},if=none,format=${format},file=${file}${extra} -device ide-hd,bus=sata.${toString port},drive=${id}";
|
|
|
|
# XNU logs to COM1 with boot-args serial=3; the build drivers read this file
|
|
serialArgs = file: "-serial file:${file}";
|
|
|
|
# Apple's own QEMU guest agent (/usr/libexec/AppleQEMUGuestAgent, macOS 13+)
|
|
# attaches to a virtio console port named org.qemu.guest_agent.0 and offers
|
|
# guest-exec (as root), guest-file-* etc. over this unix socket.
|
|
guestAgentArgs = sock:
|
|
"-device virtio-serial-pci,id=vmix-vser -chardev socket,path=${sock},server=on,wait=off,id=vmix-qga -device virtserialport,chardev=vmix-qga,name=org.qemu.guest_agent.0";
|
|
|
|
# virtio-fs (vhost-user, virtiofsd on the host). macOS auto-mounts the tag
|
|
# "com.apple.virtio-fs.automount" at /Volumes/My Shared Files; other tags are
|
|
# mounted with `mount -t virtiofs <tag> <dir>`. Needs a shared memory backend.
|
|
automountTag = "com.apple.virtio-fs.automount";
|
|
memBackendArgs = memSize: "-object memory-backend-memfd,id=vmix-mem,size=${toString memSize}M,share=on -numa node,memdev=vmix-mem";
|
|
virtioFsArgs = { tag, sock, id ? tag }:
|
|
"-chardev socket,id=vmix-vfs-${id},path=${sock} -device vhost-user-fs-pci,chardev=vmix-vfs-${id},tag=${tag}";
|
|
|
|
# virtio-blk data disk (AppleVirtIOBlock), e.g. the persistent home volume
|
|
virtioBlkArgs = { id, file, format ? "qcow2", extra ? "" }:
|
|
"-drive id=${id},if=none,format=${format},file=${file}${extra} -device virtio-blk-pci,drive=${id}";
|
|
|
|
# virtio keyboard/tablet (AppleVirtIOInput), optional alternative to the USB HID pair
|
|
virtioInputArgs = "-device virtio-keyboard-pci -device virtio-tablet-pci";
|
|
|
|
firmwareArgs = varsFile:
|
|
"-drive if=pflash,format=raw,readonly=on,file=${pkgs.OVMF.fd}/FV/OVMF_CODE.fd -drive if=pflash,format=raw,file=${varsFile}";
|
|
}
|