Skip to content

Hypervisor Support for M4+ SoCs#622

Closed
ADevWithAnIdea wants to merge 34 commits into
AsahiLinux:mainfrom
ADevWithAnIdea:PR_sptm_hypervisor
Closed

Hypervisor Support for M4+ SoCs#622
ADevWithAnIdea wants to merge 34 commits into
AsahiLinux:mainfrom
ADevWithAnIdea:PR_sptm_hypervisor

Conversation

@ADevWithAnIdea

Copy link
Copy Markdown

This patch series adds partial support for booting macOS under hypervisor on
A18 Pro (and presumably M4). It is not sufficient by itself; it requires either
a virtualized SPTM companion or SPTM emulator. To this end, we are making
significant progress documenting SPTM and our emulator to allow a clean room
implementation.

Exclaves are not supported and may come in a future patch series. Suspend is
not supported and may require significant work.

One change is required on the base system before installing m1n1: during the
configuration of macOS before m1n1 is installed, the command:

VOL=your volume here
defaults write "$VOL/Library/Preferences/com.apple.coreanimation" CAEnableMedina -bool false

must be run to disable the WindowServer exclave integration. If the directory
does not already exist, it should be created with root:wheel ownership and
0644 permissions.

There is a small amount of known nondeterminism encountered during development:

  • SPMI timeouts at various points during boot
  • SEP timeouts with a message about lo 0x17600
  • AMCC flake involving the first page of physical memory
  • Crashes when XNU attempts to start secondaries

While testing this hypervisor for upstreaming, we did not hit any of these
issues. We speculate that at least the first two are due to timing related
side effects that are mostly resolved now, however, they are documented here in
case they are found in wider usage of this hypervisor.

There are two known issues with this PR:

The hv_t8140.c file is horrific and we spent almost two weeks trying to get
rid of it but ultimately were unable to; the current file is the result of many
hours of trial and error and finding the only thing that worked.

The commpage changes currently require hardcoded addresses from the
kernelcache to force the ARM fallback paths. Some values in the commpage are
hardcoded directly in the instructions of the kernelcache with no gate or
other way to disable them. Another option would be to patch the commpage once
it is already published in memory, but this comes with its own set of
tradeoffs. Notably, it has to be located, and guest introspection would be
required to identify when it's been published and will no longer be modified
by XNU. This change is not meant to be immediately upstreamable, but rather
to start a dialog to identify a viable upstreamable path.

Tested on a MacBook Neo running 26.6 Beta 4 using a tainted (not published)
SPTM emulator. NOT tested on any M1-M3 devices to check if any changes
regressed.

Depends on #604 for detecting apple_sysregs_unlocked from the proxyclient

The commits are divided into roughly three sections (the first commit just merges yuka's branch):

  1. Commits 2-9 are mostly standalone commits that can be evaluated and merged individually
  2. Commits 10-16 add the basic hypervisor plumbing including commpage fixups
  3. Commits 17-26 all deal with locked apple sysregs in raw boot mode

yuyuyureka and others added 30 commits June 25, 2026 21:01
Signed-off-by: Yureka <yuka@yuka.dev>
Signed-off-by: Yureka <yuka@yuka.dev>
Signed-off-by: Yureka <yuka@yuka.dev>
Signed-off-by: Yureka <yuka@yuka.dev>
Signed-off-by: Yureka <yuka@yuka.dev>
RVBAR (cpu_impl_reg+0) isn't writable on newer chips, so we can't set it
to the guest entry, and there is no way the guest can choose a different
secondary entry later on.
Instead, return the guest entry on reads to make m1n1 happy, and use the
initial guest entry as secondary entry.

Signed-off-by: Yureka <yuka@yuka.dev>
This fixes use with the new pmgr on A18 Pro / M4 Pro.

Signed-off-by: Yureka <yuka@yuka.dev>
Writing a locked RVBAR causes an SError, so skip the write.

Signed-off-by: Cody Ho <codyho@stanford.edu>
T8140 also needs DAPF programmed for more DARTs. Other SoCs gate on the
ADT, so this will not break existing chips.

Signed-off-by: Cody Ho <codyho@stanford.edu>
Some platforms do not expose usable MCC TZ window registers to m1n1, but
iBoot still publishes the protected memory ranges under
/chosen/carveout-memory-map. Factor the unmap/bookkeeping logic into a
helper and, if the MCC TZ register walk yields no regions, unmap
region-id-4 and region-id-2 from the ADT carveout map instead.

Existing SoCs keep the register-derived path; the ADT fallback only runs
when no MCC TZ carveouts were recorded.

Signed-off-by: Cody Ho <codyho@stanford.edu>
Add the T8140 AMCC register discovery path. T8140 publishes the AMCC
register index and topology through ADT properties and
/chosen/lock-regs/amcc rather than matching the older MCC register
layouts.

The existing cache enable path controls MCC/system-level-cache
registers, not CPU caches. Mark older supported layouts as having that
cache-control interface, and let T8140 skip it because this boot path
does not have usable PLANE_CACHE_ENABLE/STATUS programming for the
guarded AMCC state.

T8140 also leaves tz unset so protected carveouts are taken from the ADT
fallback added by the previous commit.

Signed-off-by: Cody Ho <codyho@stanford.edu>
m1n1 maps RAM Normal-WB, which allocates AMCC SLC directory tags. Two
regions are read non-coherently after handoff and then hit those stale
tags, raising AMCC UNEXP_RT_HIT_DIR / RO_RGN_ACCESS_VIO:

  - The iBoot handoff carveout (/chosen/iboot-handoff, or carveout
    region-id-1): keep m1n1's aliases uncached over that sub-range so no
    new WB state is added for the iBoot-RO-locked page.
  - The boot framebuffer (/vram): XNU's non-coherent framebuffer writes
    hit stale SLC tags -> PEH panic. Remap it Normal-NC (which also cleans
    the lines), avoiding the panic without disabling the SLC.

Signed-off-by: Cody Ho <codyho@stanford.edu>
Add find_msgbuf_va() and dump_msgbuf() to recover the XNU kernel message
buffer when the serial/panic path is unavailable:

Signed-off-by: Cody Ho <codyho@stanford.edu>
Signed-off-by: Cody Ho <codyho@stanford.edu>
CNTVOFF_EL2 is per physical CPU. Apply the current stolen-time offset
before entering the boot CPU and secondary CPUs, as well as when
returning from an exception, so newly entered guest CPUs do not run with
a stale virtual counter offset until their first trap.

Signed-off-by: Cody Ho <codyho@stanford.edu>
On SPTM-enabled SoCs XNU boots with the mmu already enabled and expect
seeded state (previous SoCs would enable the mmu and create the state
themselves). Leaving stale m1n1 EL2 state causes the secondaries to
fault; program them with the running guest EL1 state instead.

Signed-off-by: Cody Ho <codyho@stanford.edu>
Allow callers to override the primary and secondary guest entry ABI
while preserving the existing defaults: primary guests still receive the
bootargs pointer, and secondaries still use the discovered RVBAR/entry
path unless explicit override state is set.

Signed-off-by: Cody Ho <codyho@stanford.edu>
Passing through MMIO acceses to these pages results in EL2 SErrors that
we have not figured out how to avoid.  This file selectively guards MMIO
to prevent the SErrors: reads pass through, writes are selectively
forwarded or blackholed by writer-PC range and page index. The exact
policy (which writes to forward vs drop) was arrived at over nearly two
weeks of trial-and-error.

We are not happy with this file, as it is a very ugly hack, however we
cannot figure out how to get rid of it.

Signed-off-by: Cody Ho <codyho@stanford.edu>
For SPTM enabled devices, booting macOS requires some boot args. Set
these by default to avoid users needing to specify them manually.

Signed-off-by: Cody Ho <codyho@stanford.edu>
The hypervisor doesn't yet support exclaves, so remove the stale adt
nodes to not confuse XNU.

Signed-off-by: Cody Ho <codyho@stanford.edu>
M4-class CPUs and above support SME, but XNU's native boot path normally
runs with the privilege to program EL2 SME controls directly. Under m1n1
the macOS guest runs at EL1, so those architectural EL2 registers are
not writable by the guest even though the hardware feature exists.

Add an M4 feature flag, expose it through the proxy ABI, and have EL2
seed CPTR_EL2/FGT/SMPRIMAP_EL2 on the boot CPU and secondaries. Guest
loaders can use the same SoC capability bit when deciding whether XNU's
direct EL2 SME-control writes need to be neutralized.

Signed-off-by: Cody Ho <codyho@stanford.edu>
Signed-off-by: Cody Ho <codyho@stanford.edu>
On M4-class (guarded) SoCs the Apple impdef system registers are GL2-only
and fault when executed from EL2, so the HV must intercept the guest's EL1
accesses and soft-model/shadow them (the KERNEL_CNTV timer and the
SYSREG_SHADOW bank). Set HCR_EL2.TIDCP so those accesses trap to EL2.

Gate it on !apple_sysregs_unlocked: on unlocked SoCs the guest accesses these
registers directly at EL1, and trapping them would regress existing (M1-M3)
virtualization.
Alongside SYSREG_MAP and SYSREG_PASS, add SYSREG_SHADOW(sr, store):
return the last value the guest wrote (or a seeded reset default on
first read) and swallow the write, dropping the register hardware side
effect.

This is for guarded Apple impdef registers that fault at EL2 but whose
value XNU reads back (RMW or context save/restore)

Signed-off-by: Cody Ho <codyho@stanford.edu>
XNU on Apple Silicon schedules off Apple's impdef KERNEL_CNTV_* timer, not
the architectural timer that m1n1 current virtualizes; its CTL/TVAL
registers are write-locked at EL2 on M4-class SoCs, so they can't be passed
through to hardware.

Soft-model it: trap KERNEL_CNTV_CTL/TVAL, keep the deadline and ctl in a
per-CPU shadow, read "now" from the counter (KERNEL_CNTVCTSS aliases the
already-passed-through CNTVCT_ALIAS), and assert a virtual FIQ from
hv_update_fiq() once the soft deadline elapses.

Delivery currently rides the periodic HV tick; arming the tick to the exact
deadline is a latency refinement.

Signed-off-by: Cody Ho <codyho@stanford.edu>
Signed-off-by: Cody Ho <codyho@stanford.edu>
XNU round-trips several Apple impdef registers through RMW and context
save/restore paths, but m1n1 does not consume their state. On guarded
SoCs those accesses trap to EL2, so keep per-CPU shadows to preserve
guest read-after-write behavior.

Signed-off-by: Cody Ho <codyho@stanford.edu>
XNU still probes and context-switches Apple AMX state on M4-class SoCs
even though userspace uses SME there. The AMX control/state registers
fault at EL2 on guarded systems, so preserve guest read-after-write
behavior with per-CPU shadows while keeping the existing real-register
path on unlocked SoCs.

Return a valid AMXIDR value on guarded systems so XNU accepts the AMX
version probe during boot.

Signed-off-by: Cody Ho <codyho@stanford.edu>
Cody Ho added 4 commits July 10, 2026 03:51
Signed-off-by: Cody Ho <codyho@stanford.edu>
SPRR registers are locked on M4+ SoCs. Seed and soft-cache SPRR_PERM_EL0
with the sane default values.

Signed-off-by: Cody Ho <codyho@stanford.edu>
XNU's AppleT8140CLPC (and presumably other SoCs) path accesses
Apple-private sampler registers such as S3_1_C15_C0_3 which are locked
on M4+ SoCs.

Handle the previously proven CLPC sampler family in C: keep
read-after-write coherent state for control registers and provide
monotonic guest-visible counters for CLPC perf/accumulator reads. This
avoids the Python/EL2 replay fallback without claiming to drive physical
CLPC hardware.

Signed-off-by: Cody Ho <codyho@stanford.edu>
ACTLR_EL2 is locked on M4+ SoCs so skip the write.

Signed-off-by: Cody Ho <codyho@stanford.edu>
@AsahiLinux AsahiLinux locked and limited conversation to collaborators Jul 10, 2026
@davide125 davide125 closed this Jul 10, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants