# OpenCore EFI for the VM: OSX-KVM's proven ESP (OpenCore + Lilu/VirtualSMC/... kexts) # with a config.plist rewritten for this image's SMBIOS identity. # Output: # $out/EFI/ BOOT/BOOTx64.efi + OC/ — copied into the image's ESP # $out/boot.img raw GPT disk with that ESP, used to boot the installer # $out/vmix.json model, serial, mlb, uuid, mac (also copied to EFI/vmix/) # Serial + MLB come from macserial when not given (random per build); everything # derived from `seed` (MAC, UUID) is deterministic so the NixOS module knows it. { pkgs, lib, upstream, macserial, qemu, ... }: { name ? "opencore", model ? "MacPro7,1", mac, uuid, serial ? null, mlb ? null, bootArgs ? "keepsyms=1", resolution ? "1024x768", showPicker ? true, pickerTimeout ? 2, extraConfig ? {}, memSize ? 8192, # RAM described in SMBIOS (MacPro7,1 wants 4 DIMMs) }: let ocImage = pkgs.fetchurl { inherit (upstream.opencore.image) url sha256; name = "OSX-KVM-OpenCore.qcow2"; }; in pkgs.runCommand "${name}-esp" { nativeBuildInputs = with pkgs; [ _7zz python3 mtools dosfstools gptfdisk jq macserial ]; passthru = { inherit model mac uuid; }; } '' echo "=== vmix: extracting OSX-KVM OpenCore ESP ===" 7zz x -y -oparts ${ocImage} 0.primary.img >/dev/null 7zz x -y -oesp parts/0.primary.img >/dev/null [ -f esp/EFI/OC/config.plist ] || { echo "config.plist not found in OpenCore image"; exit 1; } SERIAL="${toString serial}"; MLB="${toString mlb}" if [ -z "$SERIAL" ] || [ -z "$MLB" ]; then echo "=== vmix: generating serial/MLB for ${model} with macserial ===" LINE=$(macserial --num 1 --model "${model}" 2>/dev/null | grep '|' | tail -1) [ -n "$LINE" ] || { echo "macserial produced nothing for ${model}"; exit 1; } [ -z "$SERIAL" ] && SERIAL=$(echo "$LINE" | awk -F' *\\| *' '{print $1}') [ -z "$MLB" ] && MLB=$(echo "$LINE" | awk -F' *\\| *' '{print $2}') fi echo "model=${model} serial=$SERIAL mlb=$MLB uuid=${uuid} mac=${mac}" mkdir -p $out/EFI/vmix cp -r esp/EFI/BOOT esp/EFI/OC $out/EFI/ chmod -R u+w $out/EFI # hide OpenCore's own launcher from its picker (otherwise it is the default entry and loops) echo -n Disabled > $out/EFI/BOOT/.contentVisibility python3 ${./oc-config.py} --base esp/EFI/OC/config.plist --esp esp --out $out/EFI/OC/config.plist \ --model "${model}" --serial "$SERIAL" --mlb "$MLB" --uuid "${uuid}" --mac "${mac}" \ --nic-path "${qemu.nicDevicePath}" --boot-args "${bootArgs}" --resolution "${resolution}" \ --show-picker "${lib.boolToString showPicker}" --timeout ${toString pickerTimeout} \ --extra-json ${lib.escapeShellArg (builtins.toJSON extraConfig)} --memory-mb ${toString memSize} ocvalidate $out/EFI/OC/config.plist || echo "vmix: ocvalidate reported issues (OpenCore version may differ from validator), continuing" jq -n --arg model "${model}" --arg serial "$SERIAL" --arg mlb "$MLB" --arg uuid "${uuid}" --arg mac "${mac}" \ '{model:$model, serial:$serial, mlb:$mlb, uuid:$uuid, mac:$mac}' > $out/vmix.json cp $out/vmix.json $out/EFI/vmix/vmix.json echo "=== vmix: building OpenCore boot disk ===" # 64 MiB raw GPT disk, ESP from sector 2048 to the end (minus backup GPT) 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 $out/EFI :: mdir -i $out/boot.img@@1M ::EFI/OC >/dev/null ''