Important: Apple has stopped signing 14.7.7 (23H723), which is the primary build used for this testing.
The next best build to use is 14.8.3 (23J220), as it has a KDK with a matching build number. However, you must use the same macOS version and KDK version.
Warning: If you use 14.8.3, the binary patches (offsets) listed in Section 3, Step 8 and the symbol offsets in Section 4 will likely require modification. I suspect the core kernel functions haven't changed much, but this has not been tested.
Requirements:
- Target Device: A Mac with t6030 (or similar) architecture.
- Host Device: Can be a Mac or another OS (Linux), provided it can run Python and serial tools.
Perform these steps on the target Mac.
- Open Disk Utility.
- From the top bar, select View -> Show All Devices.
- Select your internal disk (usually named "Apple SSD...").
- Add a new partition (not volume) to this disk. Approximately 40GB is sufficient. Format it as Mac OS Extended (Journaled).
- Select the primary APFS container for the internal disk.
- Add a new volume (not partition) to this container.
- Download Mist: https://github.com/ninxsoft/Mist.
- In Mist, select Installers and locate 14.7.7 (23H723). Click the disk icon to download and install it to the partition created in Step 4.
- Shut down the target. Hold the power button until "Loading startup options" appears, then select the 14.7.7 Installer.
- In the installer, select the new volume created in Step 6 as the destination.
- Once installed, boot into the new volume and complete the setup assistant.
- Install Xcode Command Line Tools and a Rust toolchain on the 14.7.7 volume.
- Install the KDK for 23H723 (if you are using 14.8.3 it must be 23J220) on the 14.7.7 volume (available from Apple Developer Downloads).
- Clone the m1n1 repository: https://github.com/AsahiLinux/m1n1.
- Modify
src/chickens_everest.c. Add the following code insideinit_t6030_everest, immediately belowUNUSED(rev);:(Note: This performs register operations that the kernel attempts to do later; we must do them here so we can NOP them out in the kernel).msr(s3_1_c15_c1_5, 0x3uL); msr(s3_4_c15_c14_6, 0x3uL); reg_mask(S3_4_C15_C12_1, 0x0, 0x1e);
- Build m1n1 and copy
build/m1n1.binto/Users/Sharedon the target. - Open Terminal and change directory to the kernel folder:
cd /Library/Developer/KDKs/KDK_14.7.7_23H723.kdk/System/Library/Kernels - Run the following command to create a custom kernel collection:
kmutil create -z -n boot -a arm64e -B ~/dev.kc.macho -V development \ -k kernel.development.t6030 \ -r ../Extensions \ -x $(kmutil inspect -V release --no-header | grep -v "SEPHiber" | awk '{print " -b "$1; }')
- Transfer the following files to the Host machine (e.g., via AirDrop):
- The resulting kernel collection:
~/dev.kc.macho.development - The kernel DWARF file:
./kernel.development.t6030.dSYM/Contents/Resources/DWARF/kernel.development.t6030
- The resulting kernel collection:
- Reboot the target into 1TR (One True Recovery) by holding the power button during boot.
- Select the 14.7.7 volume options.
- Open Terminal in Recovery and run:
bputil -nkcas csrutil disable
- Configure the custom boot object (replace
[YourVolumeName]with your volume name):kmutil configure-boot \ -c /Volumes/[YourVolumeName]/Users/Shared/m1n1.bin \ --raw \ --entry-point 2048 \ --lowest-virtual-address 0 \ -v /Volumes/[YourVolumeName]
- Reboot the target normally. It should boot into m1n1 and display "Waiting for proxy...".
- Shut down the target by holding the power button for 10 seconds.
Perform these steps on the host machine.
- Install Xcode Command Line Tools and a Rust toolchain.
- Clone the m1n1 repository: https://github.com/AsahiLinux/m1n1.
- Apply the same patch to
src/chickens_everest.cas described in Section 1, Step 15. - Apply this patch: https://patch-diff.githubusercontent.com/raw/AsahiLinux/m1n1/pull/515.diff
- Build m1n1.
- Connect the Host and Target via USB-C (do not use the USB-C port closest to the magsafe on the target).
- Boot the Target. It should start m1n1 and await the proxy.
- On the Host: Identify the USB serial devices:
You should see two devices like
ls /dev/cu.*/dev/cu.usbmodemXXXXXX. One ends in1(the m1n1 interface) and the other in3(the serial log interface). - Export the m1n1 device variable:
export M1N1DEVICE=/dev/cu.usbmodem[...1] - In the host's m1n1 folder, chainload and run the guest. Replace
[DWARF_PATH]and[KC_PATH]with the locations of the files you copied in Section 1, Step 19:python3 proxyclient/tools/chainload.py -r build/m1n1.bin && \ python3 proxyclient/tools/run_guest.py -S -s [DWARF_PATH] [KC_PATH] -- "debug=0x14e serial=3 -enable-kprintf-spam wdt=-1 clpc=0 keepsyms=1"
- You will drop into a Python REPL prompt.
- Open a new terminal window on the host to monitor logs:
picocom -b 115200 /dev/cu.usbmodem[...3]
- Back in the REPL prompt: Press
^D(Ctrl+D) to continue execution. - Watch the log output. As soon as you see a line resembling:
Rapidly press[cpu6] Pass: msr [REG], [VAL] = [VAL] (OK) ([REG_NAME])^C(Ctrl+C) in the REPL window to interrupt. - You should now be in the Hypervisor shell. Verify you stopped at the correct point.
- Correct: The last log line resembles the format above.
- Incorrect: If the last log is
[cpu6] Pass: msr s3_0_c15_c2_4, x13 = 1e004000 (OK) (s3_0_c15_c2_4), you have gone too far.
- Apply the in-memory binary patches by running the following Python commands in the REPL:
Note: Ask if these offsets are confusing or fail on build 14.8.3.
init_base = hv.resolve_symbol('com.apple.kernel:_arm_init') config_base = hv.resolve_symbol('com.apple.kernel:_configure_misc_apple_regs') recount_base = hv.resolve_symbol("com.apple.kernel:_recount_transition") # NOP instructions write32(hv_translate(init_base + 0x7D0), 0xd503201f) write32(hv_translate(config_base + 0xc8), 0xd503201f) write32(hv_translate(config_base + 0xc8 + 0x4), 0xd503201f) write32(hv_translate(config_base + 0xc8 + 0x8), 0xd503201f) write32(hv_translate(recount_base) + 0x34, 0xd503201f)
- Type
hv.contand press Enter.
The boot process is non-deterministic. It may hang immediately, reach a mostly full progress bar, or start various userspace daemons before failing. There is usually no visual panic indication on the device; check the picocom terminal for logs.
I suspect AppleT6030PMGR is a primary culprit. When testing with minimal kexts and adding them back one by one, this driver appeared to trigger memory corruption, leading to random panics in other drivers. However, AppleT6030PMGR is also required to push the boot process forward; without it, the boot hangs earlier. It is possible that the absence of this driver simply prevents the boot from reaching the stage where the corruption from a different driver occurs. I strongly believe that it is in one of the T6030 specific kexts, at least, where the root cause lies, as m1n1 works fine on M1 & M2 devices.
Below is a list of issues observed over 13 recorded boot attempts. For each issue (each time it happened) I captured the full panic log and all serial output during bootup and can provide it if you want to compare or are interested. Note that for all of them, attempting to continue once XNU trapped our debugger, resulted in a later SERROR (different from the SERROR with no panic described in #1 below).
A severe hardware/memory bus error. The hypervisor catches this and drops to a shell. There is no standard panic log. The last visible logs are always the same and involve launchd:
[System Event] ... [Event: xpcroleaccountd] Doing boot task
[System Event] ... [Event: init_featureflags] Doing boot task
Backtrace:
_kernel_bootstrap_thread_bsd_init_vfs_mountrootcom.apple.filesystems.apfs(Offset 0xae614)com.apple.filesystems.apfs(Offset 0xab1a4)- ...followed by offsets 0x53ac0, 0x378bc, 0x132698, 0x10b8c, 0x10fe4, 0x10e14
Error: copyio.c:94 Assertion failed: __builtin_arm_rsr("pan") != 0
The Kernel expects Privileged Access Never (PAN) to be enabled, but it is 0. I've checked and it seems m1n1 handles PAN correctly, so this suggests kernel state corruption. This may be triggered by an early userspace daemon causing a fatal exception it shouldn't, leading the kernel to attempt a backtrace before killing the process when the kernel is in an unstable or too early state. Or, it could be handelling this exception is expected for the kernel at this stage, but the kernel's internal state is corrupted leading to the faulty check.
Backtrace:
_handle_breakpoint_exception_triage_thread->_thread_exception_returnuser_take_ast->ast_taken_user->_bsd_ast_postsig_locked->_exit_with_reason->_proc_prepareexit_backtrace_user_copyinOR_copy_validate
Error: Invalid spinlock [address]: <0x0...0> @locks_arm.c:343
Triggered by tccd (Transparency, Consent, and Control daemon) during a syscall.
Backtrace:
- [within
tccd] _unix_syscall_sys_ulock_wait->_sys_ulock_wait2->_ull_get
Happens on IOConfigThread during service matching.
Backtrace:
IOConfigThreadIOService::doServiceMatch->probeCandidates->startCandidate- [within
AppleS5L8940XI2C] IOFilterInterruptEventSourceIOInterruptEventSource::registerInterruptHandler
Error: ApplePPM: ... "readPTDEntry returned 0xe00002c7" @ApplePPMPTDManager.cpp:289
(Note: 0xe00002c7 is kIOReturnUnsupported)
Backtrace:
IOInterruptEventSource::checkForWork- [within
ApplePMGR] - [within
ApplePassthroughPPM] _Assert
Error: ApplePMGR: ... REQUIRE failed: die < _target->_dieCount" @ApplePMGR.cpp:14454
Backtrace:
IOConfigThread->IOService::doServiceMatch->probeCandidates->startCandidate- [within
AppleS8000DWI] - [within
ApplePMGR] _panic
Error: PAC failure from kernel with IA key while authing x16...
Backtrace:
fleh_dispatch64_common->_fleh_irq->_sleh_irq- [within
AppleInterruptControllerV3] (Actual PAC failure occurs here) fleh_dispatch64_common->_fleh_synchronous->_sleh_synchronus_handle_pac_fail->_sleh_synchronous_sp1->_Assert
Error: [data.kalloc.64]: element modified after free...
Backtrace:
IOService::pmDriverCallout->IOService::driverSetPowerStateEv- [within
AppleSEPManager] IOCommandGate::runAction-> [withinAppleSEPManager]IODTNVRAM::getProperty->copyProperty->copyPropertySymbol->copyPropertyWithGUIDAndNamekeyWithGuidAndCString->__ZN8OSSymbol11withCStringEPKc_kalloc_etc->zalloc_item->zalloc_log->_Assert
I strongly believe all of these are caused by some underlying memory or state corruption caused by a kext, as it's completely random which one will occur each boot, and they all seem to be related to memory/state issues.