Files
meeting-assistant/docs/macos-native-diagnostic.md
T

14 KiB
Raw Blame History

macOS 14 TCG prerequisite diagnostic

This manual candidate uses the existing Ubuntu/x64 Docker runner. Before downloading Apple Recovery it tests actual AVX/AVX2 instruction execution in the pinned QEMU binary, then probes a fresh macOS 14+ Recovery guest. It does not install macOS, erase a disk, provision .NET/CLT or run Meeting Assistant tests. Readiness is only a prerequisite for full native CI.

Profile and evidence

The existing Intel Celeron 1037U has neither AVX nor AVX2. KVM run 4187 at 720a431 reached macOS 13.6/x86_64/root and visible whole writable 64-GiB media. Diskutil timed out after 122 seconds; the sampler produced no report after 61 seconds. The screen remained at the Apple boot progress bar. CPU throttling, memory-limit/OOM events and container swap were zero; memory peaked at 2.67 GB. Host paging occurred. No unsupported-instruction crash or particular IPC wait is proved.

CryptexFixup 1.0.5 selects the installed/updated Rosetta Cryptex and patches APFS hash checking; it does not replace the running Recovery cache or emulate instructions. macOS 13 is outside the .NET 10 supported-OS policy. This candidate therefore uses macOS 14 and software CPU emulation without Cryptex. It changes the compatibility profile, not one isolated causal variable; actual success must be measured.

Earlier TCG run 4159 observed guest AVX2 with the upstream-selected Skylake model. Runs 4161/4163 measured slow native startup and reached the 40-minute host limit before readiness. They predated the UDIF CRC repair at 94a70b2, reuse of successful sw_vers output and capturing the large Recovery hash only once. They do not qualify this candidate. Host/workflow limits for the read-only mode are 90/95 minutes; a readiness pass does not establish that full installation/build/tests fit the pipeline.

Run 4188 with Haswell recorded a boot loop; first-reset run 4189 retained repeated supervisor instruction-fetch pagefaults at RIP/CR2 0x24b0 before native readiness. Run 4190 at 3aaab45 restored Skylake-Client-v4 and the upstream TCG -spec-ctrl mask. The actual AVX/AVX2 ROM passed (exit 33; AVX2-disabled control exit 0), and macOS 14's Darwin 23.6.0 kernel identified the Skylake CPU. It retained one kernel handoff, a running VM and later userspace execution without the earlier reset, but reached the 20-minute diagnostic deadline without the readiness hook. These observations do not identify Haswell as the original cause or qualify native tests.

Run 4190 used synchronous kernel serial output and QEMU interrupt/register tracing to preserve the failure context. Its kernel explicitly warned that synchronous output impacts performance. The current full candidate uses normal upstream boot arguments and only the existing iothread QEMU argument; it retains actual CPU/staging receipts independently of Docker log rotation. Its existing fresh-readiness gate precedes every installation permit. This allows one bounded full qualification to test boot performance and, only after readiness, installation/build/tests without duplicating the guest startup. No remote full result has qualified this candidate yet.

Full run 4191 at 9735db1 reached native Recovery: x86_64/root, Darwin 23.6.0 and successful launchd service queries. Both sw_vers attempts were stopped by the existing 45-second watchdog at about 50 seconds. Other successful commands took 24–47 seconds, and small log-copy batches took 85–181 seconds. Thus this run proves broad native startup latency and a probe-imposed abort, without proving a permanent sw_vers hang. Disk enumeration, installation and application tests were not reached. The next candidate obtains the version from the current guest's SystemVersion plist to reduce process launches; it does not claim that sw_vers has become functional.

Run 4192 at 6122be2 captured the actual 603-byte guest file and strictly parsed version 14.6.1. Its host reply was published successfully, but the guest's reply-existence check timed out before services or disk enumeration. Guest request/timeout UTC timestamps were not retained, so late delivery and 9p visibility cannot be distinguished. The current candidate removes this version reply: the guest publishes its raw file and a provisional version candidate; the host's full XML validation and exact result binding remain mandatory before any installation permit. The later installation-permit transport is still unqualified.

Run 4193 at 9164fe4 successfully derived the same current-file version in both guest and host. Its single diskutil list physical process was stopped at 124 seconds by the 120-second watchdog without output. The observer saw one runnable row and 3.18 seconds of accumulated CPU time, without a stack or proven IPC endpoint. ioreg was also stopped before producing output. The post-disk service gates, permit, installation and tests were not reached. A prior modified Recovery14 reference image has byte-identical SystemVersion contents but a different image hash; it contains StorageKit with the com.apple.storagekitd and com.apple.storagekitd.dm MachServices, not diskmanagementd. The next observation targets that actual service family and the own diskutil stack; this reference does not establish the live service state of run 4193.

Entry points and dependencies

Orchestration remains the .NET 10 file-based app tools/ci/MacOsNativeDiagnostic.cs. Existing Bash/Python boot integration is necessary before a guest SDK exists. NASM assembles the CPU probe in the disposable image build, without host/runner installation. No new runner, device, capability, secret or service is used.

dotnet run --file tools/ci/MacOsNativeDiagnostic.cs -- --validate
dotnet run --file tools/ci/MacOsNativeDiagnostic.cs -- --validate --source /path/to/clean/pinned/dockur-clone --output /path/to/fresh/validation
dotnet run --file tools/ci/MacOsNativeDiagnostic.cs -- --run --output artifacts/native-macos
dotnet run --file tools/ci/MacOsNativeDiagnostic.cs -- --cleanup --output artifacts/native-macos

The native diagnostic workflow is manual only. Temporary diagnostic branches are excluded from ordinary push jobs to avoid repeating unchanged Wine/portable jobs. Remove this routing when integrating qualified CI into the actual PR.

Before Apple downloads

The existing daemon must be Linux/x64 with two CPUs and 6 GiB memory; the runner must have 5 GiB available memory and the Docker filesystem 8 GiB free. These checks do not reconfigure resources. Dockur commit 16a5b470cdd601bae8b05b02d748d7edfb36c12e, both imported QEMU image digests and original source seams remain pinned.

Actual Skylake-Client-v4 CPU flags under TCG use enforce=on to reject unsupported requests. The CPU preflight uses that same composed flag list and QEMU binary before Recovery download/boot. tools/ci/macos-tcg-cpu-preflight.asm enables long mode/YMM state, executes AVX and AVX2 integer arithmetic, and checks an Int32 from the upper 128-bit lane. Only the correct result reaches QEMU debug-exit code 33. No disks/network attach; failure/timeout fails preflight. This tests that instruction chain, not the complete ISA or macOS.

The locally assembled NASM 2.16.03 ROM is 65,536 bytes, SHA256 c32746122cc68f3ed642aa46c21b677f803c58f0d4ff665841723fcc5625f549. Assembly/static review does not prove remote execution.

Native gates, bounds and cleanup

The original Apple recoveryosd runs under its existing job/PID beside the read-only probe. Exact known macOS 13/14 plist layouts and same-length replacements retain their allowlist. The patcher validates UDIF boundaries, updates changed mish/koly CRCs and reads back the image. Four raw/zlib positive and twelve rejection fixtures use an independent C# CRC32 reader. Apple chunklist authentication applies to the input, not the deliberately modified image.

Native readiness requires x86_64, UID 0, macOS 14+, successful launchd service queries and exactly one writable whole 64-GiB disk. The guest's existing Bash runtime reads its own /System/Library/CoreServices/SystemVersion.plist with a 4-KiB bound and mandatory EOF. Apple documents this path as the system-version source. Bash extracts only a provisional numeric version from the same bytes it publishes; subsequent guest probes remain read-only. The existing C# controller parses the full captured XML with external resolution disabled and requires a flat string-valued dictionary with exactly one valid direct ProductVersion string and macOS 14+. Missing, binary, oversized, ambiguous or malformed content fails. Before either readiness success or an installation permit, the result's version must exactly match this current-file evidence. No configured VERSION or host OS value serves as proof. The actual native diskutil query remains mandatory.

The raw file, exact source path, length, SHA256 and parsing receipt are retained and bound to the current token. This version evidence travels only from guest to host and requires no reply. The guest uses its existing Bash before any SDK exists; authoritative XML logic stays in C#/.NET. A Bash candidate alone cannot authorize installation or qualify readiness. This method establishes the current guest version, not successful execution of sw_vers.

Required commands retain 45 seconds and UID 180 seconds. The single disk query receives a 600-second diagnostic window under the existing 90-minute Recovery deadline. The owned observer captures StorageKit state, thread CPU snapshots and an optional one-second/100-ms sample -mayDie stack of only that live diskutil child. Each observation retains its own 60-second watchdog and two-second TERM/KILL grace. Missing tools, failed or timed-out samples remain explicit missing evidence; thread states alone do not identify an IPC endpoint. Stack output is captured directly from the owned state with a 512-KiB bound, without another native copy command. Observation failure passes no gate. Owned children are stopped on query completion/cancellation; output remains 512 KiB per command and 4 MiB proof. No service is started or restarted by the observer.

The container retains 6 GiB memory/swap, two-CPU limit, 512 MiB shared memory and a 4-GiB/two-vCPU guest. One fresh anonymous /storage volume holds the sparse 64-GiB target. Inspection rejects devices, capabilities, binds, ports, host networking and privileged mode. KVM is disabled with no /dev/kvm mapping; guest networking stays slirp.

Evidence retains run/source/profile identity, CPU preflight, original/patched Recovery identity, container/QEMU state, native proof/result and cleanup. Sparse kernel-handoff lines are retained separately; two handoffs before readiness/installation permission fail early. After permission, normal installer reboots remain allowed. Optional bounded before/during/after pressure snapshots record host/cgroup counters. The /storage/14/setup.dmg hash is captured once after staging; successful evidence survives later capture failure. Screenshots/pressure observations pass no gate.

Both cleanup paths verify exact token/label/ID before removing only the owned container, anonymous volume and image. No pruning, host changes, original checkout changes or Meeting Assistant restart occurs. Artifacts remain seven days. Full CI remains unverified until an installed supported guest builds/signs fresh helpers and passes all 577 tests, including the five native macOS tests, with zero skips.

Prepared full build/test flow

The separate manual .gitea/workflows/macos-native-full.yaml and the prepared required PR job invoke the same full mode:

dotnet run --file tools/ci/MacOsNativeDiagnostic.cs -- --validate --full --source /path/to/clean/pinned/dockur-clone --output /path/to/fresh/full-validation
dotnet run --file tools/ci/MacOsNativeDiagnostic.cs -- --run --full --output artifacts/native-macos-full
dotnet run --file tools/ci/MacOsNativeDiagnostic.cs -- --cleanup --output artifacts/native-macos-full

Full mode repeats CPU preflight and Recovery readiness for its own fresh guest. Before erasing the disposable target, the host verifies the owned container/anonymous volume, raw 64-GiB image, actual QEMU attachment/unique disk serial and state share. A token/source/disk-bound permit authorizes the guest. The guest independently checks whole/writable/size/unique serial before diskutil eraseDisk. It never erases a host disk or reuses an unrelated guest volume.

The installer provisions the owned guest and returns into the prepared firstboot hook. Early firstboot logs and failures enter the state share even before account-package installation or test bootstrap. The installed root must be APFS backed by the exact owned physical store. Apple softwareupdate provisions CLT; a real Swift/SDK smoke build imports the required Apple frameworks. The source archive is bound to clean Git HEAD and SHA256; the pinned macOS x64 .NET SDK 10.0.401 is checked with SHA512. No prebuilt application/helper result counts as this run's evidence.

tools/ci/MacOsNativeGuest.cs restores/builds/tests the source inside the installed guest. Success requires four fresh x86_64 Mach-O helpers, a valid audio-app signature and a fresh source-bound TRX containing exactly 577 distinct passing tests, zero failures/skips and all five named native macOS tests. Archive, SDK, build, signature and TRX receipts are retained. The required PR job follows the existing Wine and portable jobs; all jobs still select ubuntu-latest.

The workflow has 180 minutes; the controller reserves cleanup time with a shared 172-minute total deadline. Recovery, installation, CLT and test caps are 90/80/30/25 minutes under that same total, not additive promises. Actual supported-guest installation/performance and remote test success remain unqualified. The full run's own mandatory fresh-readiness and owned-disk gates prevent installation until that guest passes its prerequisites. CI does not deploy or restart the workstation application.