Replace the screenshot/OCR/keystroke driving of Apple's Recovery with a "PE": BaseSystem.dmg (a journaled HFS+ volume, writable from Linux) with one LaunchDaemon added (makeRecoveryPE) that runs /Volumes/VMIX/run.sh as root at boot, records the status and powers off. launchd loads it alongside its signed cache (verified on Tahoe 26.6.2); same idea as AutoNBI/Imagr NetBoot images. - makeImage: the PE runs vmix-install.sh (erase, installer app, SharedSupport pkgdmg, startosinstall). Progress is read from the serial console (boot-args serial=3 -v, VMIX-* markers) and screenshots (brightness only). Fully offline; prepare now takes ~5 min instead of ~10. - customizeImage: boots the PE with the image attached and runs the template offline against the mounted System/Data volumes; OpenCore ScanPolicy restricted to HFS+/SATA so only the PE can boot. One PE boot ~30 s. The installed macOS is never booted for customization, so nothing depends on launchd/BTM approval or a first-boot agent (removed). - templates rewritten for offline use: generalize creates the user with dscl -f (admin, home, auto-login kcpassword, Setup Assistant suppression, hostname, locale, timezone, keyboard type, container resize); remote-access, no-updates, performance edit the target's plists. - makeBootDisk: build-time OpenCore variant (serial console, ScanPolicy). - vm-driver.py rewritten: passive observation only (serial markers, kernel boots, panics, brightness), disk+serial-aware hang watchdog, reboot-death reset, halt/loginwindow detection. No OCR/tesseract. - OpenCore: four SMBIOS DIMMs for MacPro7,1 (no "Memory Modules Misconfigured" warning). - tools/soak.sh: repeatability harness. Verified on daku: base install 23 min end to end; basic + generalize in three ~30 s PE boots; the result auto-logs into the desktop with the created user. Root cause of the "first-boot hang" (from the serial log): the guest's restart path panics (IOPlatformHaltRestartAction -> AppleSMC, SMCWDT smcWriteKey kSMCBadCommand, nested panic) because the pinned OSX-KVM Lilu disables itself on macOS 26, so VirtualSMC never loads. Handled by the driver (reset within 60 s); kext update to follow. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XsESshRCoBoUVWV9qKURUF
29 lines
1.4 KiB
Nix
29 lines
1.4 KiB
Nix
# OpenCore boot disk for build-time boots, derived from an image's ESP
|
|
# (makeOpenCore output) with build-only settings: extra boot-args (serial
|
|
# console, verbose) and optionally an OpenCore ScanPolicy so that only the PE
|
|
# (an HFS+ volume on SATA) is bootable — the build never lands on the wrong OS.
|
|
{ pkgs, lib, ... }:
|
|
{ esp, bootArgs ? null, scanPolicy ? null, name ? "boot" }:
|
|
pkgs.runCommand "${name}-bootdisk" {
|
|
nativeBuildInputs = with pkgs; [ python3 mtools dosfstools gptfdisk ];
|
|
} ''
|
|
cp -r ${esp}/EFI EFI
|
|
chmod -R u+w EFI
|
|
python3 - <<'PY'
|
|
import plistlib
|
|
p = 'EFI/OC/config.plist'
|
|
cfg = plistlib.load(open(p, 'rb'))
|
|
nv = cfg['NVRAM']['Add']['7C436110-AB2A-4BBB-A880-FE41995C9F82']
|
|
${lib.optionalString (bootArgs != null) ''nv['boot-args'] = ${builtins.toJSON bootArgs}''}
|
|
${lib.optionalString (scanPolicy != null) ''cfg['Misc']['Security']['ScanPolicy'] = ${toString scanPolicy}''}
|
|
plistlib.dump(cfg, open(p, 'wb'))
|
|
print('boot-args:', nv['boot-args'], 'ScanPolicy:', cfg['Misc']['Security']['ScanPolicy'])
|
|
PY
|
|
mkdir -p $out
|
|
truncate -s 64M $out/boot.img
|
|
sgdisk -n 1:2048:0 -t 1:EF00 -c 1:EFI $out/boot.img >/dev/null
|
|
SECTORS=$(( 64*1024*1024/512 - 2048 - 34 ))
|
|
mkfs.vfat -F 32 -n OPENCORE --offset 2048 $out/boot.img $(( SECTORS / 2 )) >/dev/null
|
|
mcopy -i $out/boot.img@@1M -s EFI ::
|
|
mdir -i $out/boot.img@@1M ::EFI/OC >/dev/null
|
|
''
|