PacmanPatcher patches XNU to enable two features in EL0:
- Access to the Apple performance counter PMC registers (specifically
PMC0/S3_2_c15_c0_0). - Ability to use data cache maintenance instructions (eg. the ability to use
dc civac).
After patching your kernel, you'll be able to read the performance counters and flush the cache from EL0.
Latest tested XNU: xnu-11417.121.6 / macOS 15.5 RC1 / 24F74
Tested configurations:
| SoC | System | Host OS | Working? |
|---|---|---|---|
| T8132 | M4 Mac Mini | 15.1 (24B2083) | ✅ |
| T6000 | M1 MacBook Pro | 15.5 RC (24F74) | ✅ |
| T8101 | M1 Mac Mini | 12.4 (21F79) | ✅ |
- Download the KDK matching your Mac from Apple.
- Use
sw_versto get your version (eg.24F74).
- Use
- Get your SOC with
uname -v. It's the last part of the XNU version string.- Example:
root:xnu-8020.121.3~4/RELEASE_ARM64_T8101's SOC isT8101.
- Example:
- Grab a copy of
/Library/Developer/KDKs/YOUR_KDK.kdk/System/Library/Kernels/kernel.development.YOUR_SOC.- This is the kernel we'll be patching.
- In this folder,
make ./patch [kernel]- Note that the kernel will be patched in-place.
- You can ignore most
"Warning: Found fewer hits than expected"warnings, as not all patch sets apply to all SoCs (meaning some of the patch sets will have 0 matches in your kernel). - I would test the patches first and debug using the warnings later if things don't work on the first try.
- Make a kernel collection with:
kmutil create -a arm64e -z -v -s none -n boot -k YOUR_KERNEL -B patched.kc -V development -x $(kmutil inspect -V release --no-header | awk '{print " -b "$1; }')
- Shut down your Mac.
- Boot it up while holding down the power button until you see the options menu.
- This is called "one true recovery" or 1TR.
- Select options, then go to Utilities -> Terminal.
- If using FileVault, unlock your disk with
diskutil apfs unlockVolume diskXsY, where X and Y are replaced with the identifiers of your disk (probablydisk3s5)- You can do this via the GUI as well by going to Utilities -> Startup Security Utility and clicking the unlock button.
- Tell iBoot to load your kernel collection with:
kmutil configure-boot -v /Volumes/YOUR_VOLUME -c /Volumes/YOUR_VOLUME/PATH/TO/KERNELCACHE/patched.kc.development
- You should be able to restart your Mac and boot it like normal.
- Run
uname -v, you should see something likeroot:xnu-8020.121.3~4/PACMANPATCH_ARM64_T... - Use
./test_pmcand./test_flushto confirm the patches are working.
- Boot into 1TR again (turn on while holding the power button, then "Options").
- Go to Utilities -> Startup Security Utility and select "Full Security".
- Reboot.
Note: If your kernel fails to boot, you'll end up in a recovery environment that looks a lot like 1TR but isn't. You won't be able to change anything from here- don't panic! The only way we can change the startup security policy is in 1TR mode, which can only be entered by shutting the Mac down and then restarting while physically holding the power button.
Just shut the Mac down and then start it up while holding the power button, and try again.
- If your Mac hangs / crashes when trying to boot the patched kernel, try setting the
-unsafe_kernel_textboot argument (sudo nvram boot-args="-unsafe_kernel_text").- You'll need to set this from a booted macOS, it won't work in recovery mode.
- If your Mac is boot looping with the patched kernel, first uninstall it (see "reverting to normal" above), then try this.
- You may need to enable boot args with
bputil -ain 1TR. csrutil disablein 1TR turns off SIP which can be useful here too.- Adding a
-vboot arg will give you a verbose boot log which can be helpful for debugging. - Any kernel extensions you have installed will be compiled into the newly built kernelcache. So, if you have any kexts you will be updating soon (eg. if you are running PacmanKit) make sure to leave those kexts out of the kernelcache build invocation.
These patches might cause system instability, security issues, or crashes. Additionally, they intentionally make your system less secure by enabling userspace access to the high resolution timers. Proceed at your own risk!
See patches for explanations of what these patches are doing at the binary level. Here is a high-level description of what we're doing.
Access to the performance counters is allowed when:
PMCR0_EL1has bit 30 (PMCR0_USEREN_EN) set to1.
Cache maintenance instructions (XNU calls these "DC MVA ops") are allowed when:
SCTLR.UCI(bit 26) is1(XNU default).SCTLR.DZE(bit 14) is1(XNU default).- On P-Cores pre-H16,
HID4has bit 11 set to0(mask with~0x800). - On E-Cores pre-H16,
EHID4has bit 11 set to0(mask with~0x800). - On H16 cores,
ACFG_EL1has bit 3 set to0(mask with~0x08).
| Register Name | Encoding | Use |
|---|---|---|
EHID4 |
S3_0_C15_C4_1 |
DC MVA Ops on E-Cores |
HID4 |
S3_0_C15_C4_0 |
DC MVA Ops on P-Cores |
ACFG_EL1 |
S3_4_C15_C12_0 |
DC MVA Ops for H16 |
PMCR0 |
S3_1_C15_C0_0 |
Controls PMC Regs |
PMC0 |
S3_2_C15_C0_0 |
Cycle Counter |
PMC1 |
S3_2_C15_C1_0 |
Instruction Counter |
References for PMCs:
- "PMC[0-1] are the 48/64-bit fixed counters --
S3_2_C15_C0_0is cycles andS3_2_C15_C1_0is instructions" from: monotonic_arm64.c enable_counterinkpc.cPMCR0_USEREN_ENABLE_MASK
References for *HID/ ACFG:
- Reverse engineering kernel support libraries (see DC_OPS.md)
ENABLE_DC_MVA_OPSmacro in XNU