# 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 `. 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}"; }