Skip to content

QEMU testing ​

QEMU tests UniXNU, the injection paths, DarwinBoot and the kernel entry without a device.

Requirements ​

ToolPurpose
qemu-system-aarch64AArch64 virtual machine
AAVMF or EDK2 AArch64 firmwareUEFI environment
dtcDevice tree compilation
mtools or pyfatfsFAT image for the block slice test

The QEMU runner looks for the firmware in the common Homebrew and distribution paths, or takes --firmware.

Stage an ESP ​

sh
python3 package/Scripts/stage-esp.py -o out/esp \
  package/UniXNUPkg/Test/Qemu/esp \
  --put out/<board>/package/Build/Out/UniXNU.efi=EFI/BOOT/BOOTAA64.EFI \
  --put out/<board>/package/Build/Out/HelloPayload.efi=EFI/UniXNU/Payloads/HelloPayload.efi \
  --put out/<board>/package/Build/Out/SampleDriver.efi=EFI/UniXNU/Kits/SampleDriver.kit/SampleDriver.efi

stage-esp.py copies the tree, compiles .dts to .dtb and .dtso to .dtbo, and places each --put file at its destination.

Run ​

sh
python3 package/Scripts/qemu-run.py out/esp \
  --log out/qemu.log --screenshot out/menu.png \
  --expect 'UNIXNU: menu: shown' \
  --expect 'UNIXNU-HELLO: RESULT PASS'
OptionMeaning
--firmware FILEAAVMF code image
--log FILESerial log
--screenshot FILEPNG captured when the menu appears
--keys LISTQEMU key codes sent after the menu
--expect REGEXPattern that must match the serial log
--reject REGEXPattern that must not match
--no-gopNo display device, text console only
--disk FILEExtra read-only raw disk
--timeout SECONDSRun timeout
--cpus NVirtual CPU count
--finish-on-expectStop two seconds after all expectations match
--menu-marker TEXTLog marker that triggers the screenshot and keys

The runner exposes the ESP read-only through QEMU vvfat, captures the serial log, and talks to QEMU over QMP for screenshots and keys.

Smoke test ​

sh
JOBS=8 bash package/Scripts/smoke.sh

The script builds the package and runs five scenarios:

ScenarioChecks
Default entryDevice tree patches, overlay, kit driver, initrd media, entry variable
KeyboardDown and Enter select the second entry
DegradedNo GOP and no configuration, built-in entries, text console
Block sliceConfiguration on a FAT partition head, tail published as a block device
Kext stagingRevision 2 handoff, revision 1 refusal, invalid bundles, duplicates

Logs and screenshots go to package/Build/Smoke/.

XNU entry fixture ​

The QEMU entry generator builds an Apple-format device tree for the kernel:

sh
python3 scripts/qemu-entry/make-dt.py \
  --armvirt --armcpu --smp 2 \
  --owned-memory-size 0x20000000 \
  --output build/qemu-test.adt
OptionMeaning
--armvirtDescribe QEMU virt devices
--armcpuAdd the GICv3 distributor and redistributor
--smp NCPU count and PSCI
--owned-memory-sizeContiguous DarwinBoot window
--data-ramdiskScratch Data placeholder
--trust-cacheTrust cache placeholder
--nvram-proxyVolatile CHRP NVRAM proxy
--root-match-volume-uuidRoot volume match
--osenvironmentmain, recoveryos or ramdisk
--qemu-vmm-presentReport the VMM in /defaults
--crypto-hash-methodImage4 hash method

The generator describes QEMU and uses QEMU values only.

HVF ​

On Apple Silicon macOS, QEMU can use HVF:

sh
python3 scripts/qemu-entry/make-dt.py --armvirt --armcpu --qemu-vmm-present \
  --qemu-accel hvf --output build/qemu-hvf.adt

The generator reads the host counter frequency for the CPU timebase. The bound fixture is used to observe the actual TCR_EL1, SCTLR_EL1 and TTBR registers at the kernel stop point.

Stop point ​

sh
python3 scripts/qemu-entry/gdb-stop.py --help

The helper stops QEMU at a symbol and records registers, memory and the last checkpoint. The output is a JSON report.

Next steps ​

Released under the BSD-2-Clause-Patent License.