Files
Snapintosh/docs/APPLE-ENCODINGS.md
Acelogic 1510d8c6ce Snapintosh public release
An EL2 monitor/hypervisor and toolchain that boots macOS 27 on the ASUS A16
(Snapdragon X2 Elite), with an Adreno Metal plug-in and IOGPU kext, and
air2ir3, an AIR to Mesa ir3 shader compiler.

MIT, with GPL-2.0 components listed in LICENSE and THIRD_PARTY_NOTICES.md.
No Apple, Qualcomm, ASUS or Microsoft binaries are included.
2026-10-21 21:26:36 -04:00

5.8 KiB

Apple-only instructions and system registers

The X2 Elite's Oryon cores implement ARMv9. Apple silicon implements ARMv9 plus instructions in encodings Arm leaves unallocated, and a large space of IMPLEMENTATION DEFINED system registers. On this machine every Apple-only instruction raises an exception with EC 0 ("unknown reason"), which looks the same as a corrupt word; a handler has to look at the instruction. This document lists what the stock macOS 27 kernelcache (26A428, j700ap) uses and how the Snapintosh monitor handles each one.

Counts come from a linear scan of __TEXT_EXEC, attributing each address to its fileset entry by that entry's own segment ranges.

Inventory

Encoding What it is In kernel In kexts Handling
0x00201420 genter: enter the guarded world (SPTM/TXM call) 174 0 intercepted and emulated
0xd4e00000 alternate call encoding, paired 1:1 with genter 174 0 same path
0x00201400 gexit 0 0 nothing to do: only the monitor executes it
0xe7ffdeff XNU's TRAP_DEBUGGER 1 0 must reach XNU's own vector, never emulated
0xe7ffdefe TRAP_DEBUGGER, ARM32-ancestor spelling 1 0 same as above
0x002010x0, 0x0020122x AMX instructions 175 0 never executed (see below)
0x00200820 Apple compression instruction - - avoided with XNU's -vm_compressor_wk_sw boot argument

genter, gexit and AMX all live in the 0x00201xxx block. That is Apple's reserved region, so a scan for Apple instructions should sweep the block rather than a list of constants.

The monitor calls

XNU reaches SPTM and TXM with genter, and every call stub also carries the alternate 0xd4e00000. A byte in __DATA_CONST, copied from a feature bit in the SPTM boot structure, selects which one executes. The monitor builds that structure, so XNU stays on genter; both are intercepted anyway. lldb names 0xd4e00000 tenter; llvm-mc, llvm-objdump and otool decline it.

How a call reaches EL2 (stage-2 execute-never call pages, a guarded vector in native-call mode, or the opt-in HVC replacement) is described in BOOT-CHAIN.md, section 4, and the HVC patch in KERNEL-PATCHES.md.

The debugger traps

0xe7ffdeff is not an instruction to emulate. It is XNU's TRAP_DEBUGGER, defined in osfmk/kern/debug.c as a deliberately undefined word. XNU executes it from DebuggerTrapWithState() after it has decided to panic. handle_uncategorized() in osfmk/arm64/sleh.c recognises it with (instr & ~1) == ARM_GDB_INSTR, so both spellings match, and advances ELR itself. The surrounding code zeroes db_op_return before trapping and reads it afterwards, so emulating or skipping the word would be wrong. The monitor delivers it to XNU's vector and reads the panic record back after a run.

0xe7ffdefe is the whole body of a three-instruction function (bti c, the trap, ret) in the KDP code and needs the same treatment.

AMX

All 175 AMX words are in com.apple.kernel, starting in the thread context-switch path. Emulating them would cost a trap per instruction in the hottest code in the kernel.

They are never reached. XNU panics if AMXIDR_EL1 advertises no known AMX version, so the monitor reports version 1. But the save/restore path is guarded by two Apple IMPDEF registers: one is read and must be non-zero, and the other has an enable bit (bit 63) that XNU sets on first use. Both trap through HCR_EL2.TIDCP, the monitor answers reads with zero and drops writes, so the enable never sticks and the guard never opens. XNU's AMX support is lazy per thread (state allocated on first use, "AMX exception" handlers), so a machine where AMX is never enabled never executes those words.

This is a consequence of a good default rather than a guarantee: one call site is gated by software flags, and the argument is that nothing sets them because nothing enables AMX. User-space numeric code that tries AMX would arrive as an EC 0. If that is ever needed, the right shape is to emulate at the exception boundary XNU already provides (shadow state per thread, trap on first use, one save or restore per switch), not per instruction.

System registers

Apple's IMPDEF registers (op0 = 3, CRn = 15 and similar) trap to EL2 through HCR_EL2.TIDCP. The monitor:

  • models the ones that carry state XNU depends on: the fast-IPI request and status registers, the deadline timer aliases, the SPRR permission registers, GXF_STATUS_EL1, AMXIDR_EL1;
  • answers every other read with zero and drops the write, and logs it.

"A feature XNU is told it does not have is a feature it does not then use." The zero answers are cheap but frequent; an opt-in kernel patch (SNAP_KERNEL_SYSREG_ZERO) rewrites the reads of the 18 registers the monitor always answers with zero into MOVZ Xt, #0 and their writes into NOP. See KERNEL-PATCHES.md.

Method notes

  • Capstone 5.0.6 mis-reports SME2 ldr zt0 / str zt0 as undecodable on this kernelcache. LLVM with the full ARMv9.6/SME2.2/SVE2.2 feature set is the reliable decoder.
  • Undecodable is not the same as Apple. Restricted to real function bodies (from LC_FUNCTION_STARTS), most undecodable words are data in text: ASCII, jump-table deltas, permutation tables and hash constants.
  • A word appearing exactly once proves nothing; hundreds of words are singletons. What identified 0xe7ffdeff was the symbol table around it and XNU's own comparison against it.

The rule

Cost is trap frequency times cost per trap, not the number of encodings. Trap at the coarsest boundary the guest already defines: genter is a call, so trap the call; AMX is a lazy per-thread state machine, so control the enable, not the register moves; the debugger trap is XNU's own, so do not intercept it.