Skip to content

Xiaomi Mi 8 SE (HuajiFruit) ​

HuajiFruit/UniXNU-sirius

Xiaomi Mi 8 SE running the UniXNU source kernel through its own U-Boot UEFI: entry, MMU, timer, PSCI, platform expert and the boot-progress console work on hardware; IOKit is blocked on AppleARMCPU claiming /cpus.

源记录: porting.toml, e02fa174224ff2fcb0211d2b45a4232c4e92a58f.

本页按设备记录自动生成。

部分状态
配置implemented
固件与启动implemented
CPU 与中断implemented
内存与 DMAimplemented
存储与根卷device-verified
显示implemented
输入planned
GPUplanned
网络planned
音频planned
电源planned
镜像组装implemented
诊断implemented
boot-chainimplemented

配置 ​

implemented

组件: ArmDTPlatform, DarwinBoot.

硬件与依赖 ​

  • SoC: SDM710 (Kryo 360, 2x Cortex-A75 + 6x Cortex-A55), GIC-500 GICv3, 19.2 MHz Qualcomm XO counter, PSCI over SMC.
  • DRAM: 6 GiB model, 2 GiB at 0x80000000 plus 0xfd6c0000 at 0x100000000, 0x17d6c0000 usable.
  • Target OS: the unchanged iPad7,1 iPadOS 17.7.11 (21H461) restore image, the same kernelcache as fajita and sheng.
  • Hardware sources: mainline sdm670.dtsi, the vendor kernel tree and measurements on the phone.

配置 ​

  • board.toml is format 2 (schema_version = 2) and selects soc_source = sdm710 and os_profile = ipados-17.7.11-21H461.
  • dts/sirius.dts describes memory, the eight cores and their affinities, and includes the SoC DTSI for the GIC, PSCI and counter.
  • configs/sirius_defconfig keeps the first-console kernel configuration and turns off the profile settings the image does not use yet.
  • The prelink roots, slide, early security initializer and Apple device-tree identity come from the OS profile; the board adds sleep-disabled, the product model and the button set.

操作步骤 ​

  • From a UniXNU checkout: ./unixnu defconfig sirius_defconfig -D ../UniXNU-sirius.
  • Then ./unixnu fetch, ./unixnu status --json and ./unixnu tree; the output is out/sirius/ipados-17.7.11-21H461/sirius/.
  • Compile the hardware tree through the payload step (prototype: dtc -I dts -O dtb -i <soc>/dts dts/sirius.dts).

开发经验 ​

  • [kernel] archives build build-sirius-gpu.py into libShengPlatformPower.a and kernel_args pass sirius-gpu=1 for the Adreno 616 phase-1 probe (see the gpu area).
  • [kernel] archives build build-sirius-touch.py into libShengPlatformPower.a and kernel_args pass sirius-touch=1 for the FTS521 touch probe (see the input area).
  • [adt.chosen.u32] sets ephemeral-storage = 1 so datamigrator skips the first-boot migration: kernel ca828cc / payload 95bcb3a showed the migration bar unfinished at t=2177 s with the CPU 87% idle and the plugin wrapper killed by signal 9; the staged system apps are not installed while it is set (see the diagnostics area).
  • kernel_args pass validation_disables=0x10 so user stackshots are refused (see the diagnostics area).
  • [payload] text_fixups repeats the OS profile's four fixups (a board list replaces the profile's) and adds the CoreUI sharedCIContext fixup for iconservicesagent (see the display area); kernel_args pass sirius-spawn-log=1 for the SPAWN console lines (see the diagnostics area).
  • board.toml gained build-sirius-emmc.py in the archive list next to the USB console entry.
  • board.toml selects AppleKeyStoreState in [components] include and passes offline-key-events=1 and sheng-aks=1 for the AppleKeyStore stand-in (see the power area).
  • kernel_args pass sirius-fb-cached=1 for the GopFramebuffer board fork's cacheable source reads (see the display area).
  • .gitignore ignores tmp/: downloaded kernels and payload images for flashing go there, not on the Desktop.
  • kernel_args pass sirius-cpufreq=1 and [kernel] archives build build-sirius-cpufreq.py into libShengPlatformPower.a (see the power area).
  • kernel_args pass sirius-fb-wc=1 for the GopFramebuffer board fork (drivers/GopFramebuffer): the scanout is mapped write-combined instead of uncached (see the display area).
  • A selected component with a kernel build recipe needs a component: entry in [kernel] archives, or unixnu stops with 'archives needs component entries'; that recipe runs the tree's build-arm-driver.py, which is this repository's copy, so it has to know the component's name. The recipe's archive is not linked: the kernel force-loads only the archives build-xnu-4k-baseline.sh names.
  • board.toml lists build-sirius-emmc.py alongside the other archives, because the SDCC1 probe is compiled the same way as the USB console and joins the same force-loaded archive.
  • Format 2 replaced the hand-written ADT generator of the legacy port (scripts/make-sirius-adt.py) with a board DTS: the payload step compiles the tree and derives kernel.macho.adt from it.
  • The arm-io base is derived from the exported nodes, not written by hand: the GIC at 0x17a00000 makes the window start at 0x17000000, which XNU reads as the SoC base.
  • The legacy port set ipad_identity = true; in format 2 the same identity is the OS profile's [adt] tables, so the board only states its deviations.
  • configs/sirius_defconfig keeps the ARM64 profile's CONFIG_XNU_NATIVE_PAN=y: Kryo 360 implements PAN and SCTLR_EL1 leaves SPAN clear, so every exception entry sets PSTATE.PAN, and only the PAN build clears it around copyin/copyout (see the diagnostics area).
  • CONFIG_XNU_NATIVE_CS_HASH_CACHE=y is back on: iPadOS signs code in 16 KiB pages, and this 4 KiB kernel accepts the dyld shared cache's signature only through the native 4K hash cache.
  • The SystemOS cryptex mount (OWNER_CACHE_MOUNT, OWNER_CACHE_STARTUP, OWNER_UNION_SYSTEMOS) builds in the bring-up mode BRINGUP_OWNER_CACHE, as the uke port does, and its volume is authorized by the boot argument sirius-allow-unverified=1, not by an owner root policy.
  • sirius_emmc_hs=3 lets the eMMC driver climb to 25, 50 and 100 MHz, each step proven against blocks read at 400 kHz; the card settles at 50 MHz (HS_TIMING 1).
  • The Data volume: OWNER_DATA_MOUNT with SIRIUS_DATA_SEED (NativeEmmc serves the seed instead of RamBlock) and CONFIG_XNU_OWNER_DATA_VOLUME_UUID = EB6CBBA0-ECB9-4556-A483-4B60D54D4306, the volume of the seed that .github/workflows/data-seed.yml built; [payload.adt] owned_memory_size gives the kernel a 1 GiB window to hold the seed. On the phone the window lands below 4 GiB (seed at 0xa3cc1000), the seed is published as the Data window and the kernel mounts its volume (disk3s1, OwnerData) at /private/var before launchd.
  • With the Data volume mounted, launchd's mount -P 1 still exits 66: apfs logs 'DT_get_fstab_entries: failed to get filesystem entry at: /filesystems/fstab', and the uke port traced the fatal error to the missing /chosen/boot-manifest-hash. [adt.chosen.bytes] boot-manifest-hash now carries uke's explicit bring-up boot-set identity (SHA-384 of 'UkeBringup.BootSet.v1\0'), as asteroids does. On the phone mount-phase-1 and mount-phase-2 then pass and launchd reports 'Early boot complete. Continuing system boot.'
  • Past early boot the 32 MiB Data volume runs out of space (ENOSPC) and watchdogd traps (brk #1), which panics the boot. kernel_args now pass sheng-disable-watchdogd=1 and sheng-lite=1 to package/BootWatchdogPolicy.c (already linked and called after the Data mount), which disables watchdogd and the network/iCloud/photo daemons through launchd's disabled.plist, and the seed workflow grows the container to size_mib (256 MiB, held in RAM inside the 1 GiB window); the volume UUID stays across the grow. Not yet booted.
  • Each seed workflow run formats a new volume, so the 256 MiB seed (run 40621149780, owner-data.cdr 48a10f78...) has UUID E06A21C1-6686-49B8-B242-ED82B00F4AF8, which the defconfig now names; with the seed inside, the payload files take 306 MiB, so [payload] size_mib is 512 (the vendor partition holds 3 GiB).
  • Persistent Data configuration: kernel_args now passes sirius-data-write=1 so NativeEmmc publishes the persistent Data container behind the root image in userdata read-write; [payload] size_mib is reset to 256 MiB because the Data container no longer travels in the vendor payload volume; .github/workflows/data-seed.yml default container size is updated to 8192 MiB.
  • Display configuration: board.toml adds GopFramebuffer to [components] include, replaces build-sirius-probes.py with build-native-legacy-display.py in [kernel] archives, adds offline-legacy-register=1, offline-legacy-gop=1, offline-legacy-unit-scale=2, offline-legacy-pm=1, offline-legacy-validate=1, offline-legacy-transfer=1, offline-legacy-cpu-present=1, offline-legacy-phase=4, offline-mobile-graphics-client=1, offline-mobile-graphics-load=1, offline-apfs-load=1, sheng-vsync=60 to kernel_args, and sets artwork-display-gamut=sRGB in [adt.product.string]; configs/sirius_defconfig enables CONFIG_XNU_NATIVE_LEGACY_DISPLAY=y, clears CONFIG_XNU_EXTRA_LD_ARCHIVES, and updates CONFIG_XNU_OWNER_DATA_VOLUME_UUID to match the persistent Data volume.

完成条件 ​

  • status --json resolves the ARM64, sdm710, iPadOS and device layers, prints a non-empty patch order and shows the device package as kits/sirius.
  • The compiled hardware tree carries one contiguous memory range equal to payload.dram_size, eight enabled CPUs and a PSCI method.

当前范围 ​

  • The SoC profile is new: only the GIC, PSCI, counter and the timer INTID are shared, everything else is still board-local.
  • The Data seed holds files from the IPSW, so the workflow uploads it encrypted (ARTIFACT_PASSPHRASE) and the payload step takes it from a local path; a new seed has a new volume UUID that the defconfig must follow.
  • The 1 GiB window must start below 4 GiB for the eMMC's 32-bit ADMA2; whether this unit's memory map offers one such run is not yet observed.

下一步 ​

  • Run status --json and tree against this repository and commit the resulting unixnu.lock.
  • Rebuild the payload from dts/sirius.dts and compare the generated ADT with the legacy one before the next hardware boot.

证据 ​

固件与启动 ​

implemented

组件: DarwinBoot.

硬件与依赖 ​

  • The device runs sirius-distro (mainline Linux) and its boot partition carries U-Boot 2026.07 with EFI_LOADER, the UEFI boot manager, GOP through vidconsole and EFI block volumes over FAT.
  • UniXNU.efi is built on the host by ./unixnu build package; DarwinBoot.efi is built from drivers/DarwinBoot.
  • The payload volume goes to the unused vendor partition mmcblk0p80 (3 GiB) and is selected by a marker file.

配置 ​

  • board.toml has no [firmware] table: there is no Mu-Silicium firmware and no boot image on this port.
  • [payload] loader = "payload" makes the generated unixnu.toml load Payloads/DarwinBoot.efi as a [[driver]] instead of expecting it in the firmware volume.
  • scripts/make-uboot-payload.py adds \EFI\BOOT\BOOTAA64.EFI (UniXNU.efi) and the DarwinBoot.efi driver to the payload FAT volume.

操作步骤 ​

  • fastboot flash boot the U-Boot image, fastboot flash vendor the finished payload volume, then select the UniXNU menu entry in U-Boot.
  • U-Boot boots UniXNU when /EFI/UniXNU/BOOTUNIXNU exists on the vendor partition and boots Linux otherwise.

开发经验 ​

  • DarwinBoot (board fork) appends the free conventional-memory runs of at least 64 MiB it sees before placing the window to the kernel command line as db-runs=<start>+<bytes>,... (hex, up to eight); the kernel prints them, so the real layout can size owned_memory_size without the NO WINDOW screen.
  • The eMMC work adds no firmware component: the payload still boots through U-Boot with the same marker file.
  • U-Boot's UEFI services are enough for UniXNU, DarwinBoot and XNU: no Mu-Silicium platform port is needed for this board.
  • DarwinBoot's GIC-500 pre-configuration (the iBoot role) is what lets the kernel pass lockdown and start kexts on this SoC.
  • The pre-configuration must not enable the EL1 virtual timer PPI early: on SDM710 that is INTID 19 and it fires before ArmCpu installs its handler.

完成条件 ​

  • U-Boot starts UniXNU from the FAT volume, the menu lists the xnu entry, and DarwinBoot hands control to the kernel with the device tree, framebuffer and command line.

当前范围 ​

  • The boot chain is U-Boot specific: it is not reproducible with the Mu-Silicium flow of the other ports and is not covered by ./unixnu firmware or ./unixnu bootimg.

下一步 ​

  • Keep the U-Boot side (sirius-distro) pinned by revision in the handoff document; it is the firmware for this port.

证据 ​

CPU 与中断 ​

implemented

组件: ArmCpu.

硬件与依赖 ​

  • Eight Kryo 360 cores at Affinity1 0x000..0x700, boot CPU cpu0; cpu0-5 Cortex-A55, cpu6-7 Cortex-A75.
  • GIC-500 distributor 0x17a00000/0x10000, redistributor 0x17a60000/0x100000, stride 0x20000.
  • The EL1 virtual timer is GIC PPI 3 / INTID 19 (Apple uses PPI 11 / INTID 27); the SoC counter runs at 19.2 MHz.

配置 ​

  • dts/sirius.dts lists all eight cores in boot order with their compatible strings; the ADT carries the same order.
  • The kernel command line selects the board behaviour: sheng-smp=1, cpus=8, armcpu-self-ipi=1, armcpu-timer-intid=19.
  • drivers/ArmCpu/ArmCpu.cpp is the board copy of the component: it adds the non-Apple timer INTID, the /cpus probe counters and the sheng-smp claim path.

操作步骤 ​

  • Build the kernel, then read the boot-progress TOPO line to confirm the topology and the probe decision.

开发经验 ​

  • The eMMC bring-up does not touch the CPU selection or the ArmCpu driver; board.toml only gained an archive entry.
  • The boot-progress TOPO row confirms the kernel topology on hardware: boot=0 max=7 nums=01234567.
  • With sheng-smp=1 the native ArmCpu claims every /cpus nub; without it the kernelcache AppleARMCPU takes the nubs and panics with "CPU configuration in device tree is invalid" because it requires /cpus/max_cpus.
  • AppleARMCPU still wins the nubs at the current stage, which is the open IOKit blocker.

完成条件 ​

  • Every enabled CPU reaches its entry checkpoint and timer/IPI handling works on each enabled CPU with the board INTID.

当前范围 ​

  • IOKit attach of the native ArmCpu is not yet proven; the last trusted snapshot shows AppleARMCPU holding /cpus.

下一步 ​

  • Stop pre-enabling the timer PPI in DarwinBoot, re-read the probe row, and confirm ArmCpu installs its IRQ handler before the timer fires.

证据 ​

内存与 DMA ​

implemented

硬件与依赖 ​

  • Usable DRAM 0x17d6c0000 from 0x80000000 (6 GiB model: 2 GiB then 0xfd6c0000 at 0x100000000).
  • The framebuffer handed over by U-Boot sits at 0x9c000000 (1080x2240 splash).

配置 ​

  • The DTS memory node is a single contiguous range and board.toml repeats the size as [payload] dram_size; the driver refuses a mismatch.
  • The ADT receives dram-base, dram-size and the CHRP NVRAM proxy from the payload step.

操作步骤 ​

  • Read the MEM row of the boot-progress panel and compare it with the DTS range.

开发经验 ​

  • DarwinBoot's free runs on this unit (kernel 130bf97, payload 216ef03): 0x80000000+0x5700000, 0x97c00000+0x4400000 and 0x9f800000+0xae9e7000 (2.73 GiB, crossing 4 GiB at 0x100000000); the 1 GiB window sits at 0x9f87c000. A window up to about 2.7 GiB fits that run, with the part above 0x100000000 out of the eMMC's DMA reach.
  • A window larger than 1 GiB is bounded by the eMMC's 32-bit ADMA2: the kernel image's static descriptor and single-block buffers and the run bounce buffer (IOMalloc) must stay below 4 GiB, and sirius_emmc_read_blocks refuses a page above it; DarwinBoot takes the first free run that fits. The heartbeat now prints the window (NEMMC window base/size) and DarwinBoot's free runs (NEMMC db-runs) so the next size is chosen from the measured layout.
  • The eMMC probe maps two register windows (GCC and SDCC1) and reads only; the payload memory map is unchanged.
  • The single contiguous range is what DarwinBoot's window allocation and the ADT handoff require; the two physical banks are described as one range on purpose.

完成条件 ​

  • The kernel memory map accounts for every handoff region, and no DMA engine or framebuffer range overlaps the kernel window.

当前范围 ​

  • DMA and SMMU behaviour is untested: there is no storage or GPU driver yet.

下一步 ​

  • Capture the kernel's memory map row after the next IOKit break-through and reconcile it with the DTS.

证据 ​

存储与根卷 ​

device-verified

硬件与依赖 ​

  • The reference unit is eMMC, not UFS: the UFS transport and its sheng integration points do not apply to this board.
  • The original kernelcache is the only source of IOStorageFamily's addresses; its kext images carry no symbol table, so they are derived from the dispatch table itself.

配置 ​

  • board.toml lists build-sirius-emmc.py (the SDCC1 probe) and build-native-emmc.py (the IOKit block driver) in [kernel] archives; CONFIG_XNU_NATIVE_UFS stays off.
  • [userdata] carries the 512-byte geometry and the OS/SystemOS windows read from the device's own GPT.

操作步骤 ​

  • Build the kernel with that archive list: the driver's storage imports are bound at the prelink slide and archived with it.
  • Read the evidence off the boot-progress console: the probe prints the GPT rows, the driver logs the block count it publishes.

开发经验 ​

  • Read rate from the NEMMC alive blocks counter (kernel f222029): flat 30 MiB/s from 55 to 150 s (the card's limit at 50 MHz 8-bit SDR, mostly the shared-cache verification), 13-23 MiB/s to about 330 s, then under 2 MiB/s while the Apple logo stays up: the first five minutes are storage-bound, the rest is not. Linux runs this card in HS400; DDR52 (controller clock twice the card clock, BUS_WIDTH 6, UHS DDR50, no DLL or tuning below 100 MHz per sdhci-msm) is the next rung that needs no tuning.
  • The run bounce buffer comes from IOBufferMemoryDescriptor::inTaskWithPhysicalMask (contiguous, mask 0xfffff000) instead of IOMalloc, so it stays below 4 GiB where the 32-bit ADMA2 reaches when the window grows past 0x100000000.
  • NativeEmmc's heartbeat also carries the display copy counters every fourth beat (NEMMC fb copies, copy last/mean/max us, read last us, cache source/scanout/wimg) and every sixteenth beat the clock of the core it runs on (NEMMC cpu mpidr, NEMMC cpu MHz), the cpufreq-hw state of both clusters, the memory layout (NEMMC window base/size, NEMMC db-runs) and the CPU load (NEMMC load user/sys/idle %), because past about 220 s the console has no other source for them; the storage path is unchanged. realhost is declared locally there: kern/host.h exposes it only to MACH_KERNEL_PRIVATE code and the IOKit recipe does not define that.
  • The data path works: ADMA2 reads return EFI PART from LBA 1, the GPT header parses (entries_lba=2, count=84, size=128) and 81 partitions with geometry are listed; boot 1179648..1310719, vendor 11010048..17301503, userdata 17301504..122142686.
  • Five conditions had to hold at once: the RCG2 encoding (CFG=0x2017, M=1, N=0xfc, D=0xfb), 1.8 V I/O (the board's vqmmc), the 32-bit interrupt/error map at 0x30/0x32/0x36, the ADMA descriptor address at 0x58/0x5c rather than 0x00, and cache maintenance (flush_dcache64) because the engine reads memory and not the CPU cache.
  • SMMU is not the blocker: the boot chain leaves it disabled (scr0=0x00230406, bit0=0) and writing S2CR hung the SoC twice, so the driver only reads SMMU state.
  • IOStorageFamily is a kext of the original kernelcache, so the archive form of the driver left IOBlockStorageDevice undefined at the kernel link (ld: 'a missing vtable usually means the first non-inline virtual member function has no definition').
  • The prelink slides every component by a constant: extents[].va = original + slide for each segment and bindings[] carries new_site = site + slide, so the addresses the driver needs are the kernelcache's plus the slide.
  • The dispatch table is identifiable without a live snapshot: of the 190 pinned slot names 176 are kernel exports, exactly one __DATA_CONST table matches all of them, the other 14 slots point at the kext's own BRK traps, the single constructor that stores table+16 forwards to the actual IOService constructor and the base destructor resolves to IOService's.
  • Eleven imports are bound (dispatch table, metaclass, protected constructor, base destructor and seven methods); when the prelinked kernel exists the derivation also checks it holds the same constructor at the slid address, so a wrong slide cannot pass.
  • The SHNGROOT manifest at userdata LBA 17301504 describes two windows (OS 17303552+11870275, SystemOS 29175808+6451200); publishing them as their own IOBlockStorageDevice makes APFS mount disk0, disk1s1 and disk2s1 (the OS volume DawnUpdate21H461.J120J121J207J208OS) and bsd_init completes.
  • A thread_call has a single pending slot: thread_call_enter1 neither queues a second call nor preserves the first parameter, so passing requests there silently dropped completions and any caller waiting on such a request hung with the kernel otherwise idle. Requests now sit on a software queue that one drain services.
  • Completing a block request from another thread deadlocks this stack the same way the drop did (reads and blocks frozen at the same point, no error, the drain asleep), so doAsyncReadWrite completes inline by default.
  • The recurring 'Taking non-sleepable RW lock with preemption enabled' panic is the trust cache runtime lock, not a pmap lock: lock 0xfffffff007b3b548 resolves to _trust_cache_rt_lock and the first caller to _query_trust_cache+0x70. trust_cache_runtime_init() is not compiled into this build, so the lock word reads all zeros (lck_rw_can_sleep == false) and lck_rw_lock_shared panics at lock_rw.c:1400; the driver's heartbeat now mirrors the panic text into the USB ring, which is how the addresses were read.
  • With the runtime guarded to answer KERN_NOT_FOUND, AMFI refuses every image and the kernel kills launchd with namespace 9 (OS_REASON_EXEC) code 8 (EXEC_EXIT_REASON_SECURITY_POLICY); recording and clearing that answer with sirius-skip-cs=1 makes exec_activate_image return 0, so execve of /sbin/launchd completes.
  • The card clock can be raised from the identification rate with the same table U-Boot programs: the RCG divider field holds (div * 2 - 1) and DUAL_EDGE is set when n != m, so the 400 kHz entry (CXO, div 12, m 1, n 4) is CFG 0x2017 and 25 MHz (GPLL0_EVEN, div 6, m 1, n 2) is CFG 0x260B; 25 MHz stays inside default speed, 50 MHz would need the CMD6 high-speed switch.
  • With the original trust-cache bundle in the payload and the runtime initialised at startup, AMFI answers the exec policy itself: the boot prints execve activate 0 without the sirius-skip-cs bypass line, so the Apple-signed launchd is trusted rather than skipped into.
  • The 25 MHz switch reads the GPT signature back at the new rate on device (the probe prints EMMC clock25=ok), so the RCG encoding above is confirmed rather than assumed.
  • Throughput at the identification clock is about 550 blocks/s (0x3FC to 0x84A in two seconds), the expected ceiling for 400 kHz on a four-bit bus, so the 400 kHz setting is a speed problem and not the cause of the stalls.
  • The init exec returns 0 and then nothing asks the driver for another block: reads stop at 0x10D (269 requests, 0x98F blocks) while the heartbeat keeps counting, so the wait is past the storage stack. The caller side is now instrumented (sirius-post-exec-trace.patch): the exec thread-switch state, every new user thread start at thread_bootstrap_return, the first pid 1 syscalls with their returns, each bsd_ast for pid 1 and the first user faults after the exec, all written to the same USB ring and summarised by the heartbeat.
  • The caller-side probe closes that question: the pid 1 thread stays in load_init_program_at_path, inside copyout() of the launchd path, and takes 62.6 million faults in about a minute. The stall is a fault loop in the exec path, not a storage wait.
  • That fault loop was PAN (see the diagnostics area). With CONFIG_XNU_NATIVE_PAN=y and the driver no longer clearing PAN, all eight CPUs pass the copyout, the exec returns 0 and launchd issues its first syscalls, every one of them returning: 74, 372, 294, 20, 427, 381, 5, 90, 4 and 202. Syscall 294 (shared_region_check_np) returns ENOMEM with 'vm_shared_region_start_address() failed', i.e. dyld finds no shared cache mapped; afterwards the launchd thread waits and the read count stays at 0x113.
  • The shared cache is in the SystemOS cryptex. Building the ARM64 series' owner SystemOS mount (OWNER_CACHE_MOUNT, OWNER_CACHE_STARTUP, OWNER_UNION_SYSTEMOS, with BRINGUP_OWNER_CACHE as the uke port has it) and answering its two block hooks from NativeEmmc (sirius-allow-unverified=1) mounts disk4s1 (SystemCryptex, 8846FE78-2910-4297-AF97-D59BB6F8FC16) as a union over / before launchd starts; dyld then opens the cache, fstats it and preads its header.
  • Registering the cache's code signature (fcntl, syscall 92) then returned 85 (EBADEXEC): iPadOS signs code in 16 KiB pages, and this 4 KiB kernel only accepts that through native-4k-signature-hash-cache.patch, which the first-console configuration had off. With CONFIG_XNU_NATIVE_CS_HASH_CACHE=y the call returns 0 after the kernel reads the whole cache once, and dyld maps it (197 and 73 return 0).
  • The block driver ran at the 400 kHz identification clock all along: sirius_emmc_prepare() reprogrammed it after the probe's 25 MHz switch, and every 512-byte block was its own CMD17 with five ring lines. On this 8-bit bus that is about 770 blocks/s, and the 2.81 GiB cache took two hours. Multi-block reads (CMD18 with Auto-CMD12, 256 blocks per command through an ADMA2 table) and a clock ladder proven against blocks read at 400 kHz bring it to 50 MHz: EXT_CSD reports rev 8, width 2 (8-bit), HS_TIMING 1 and DEVICE_TYPE 0x57, runs pass with EIS 0, and the whole cache is read in about 104 s (27 MiB/s on average, 30 MiB/s peak).
  • Past the shared cache launchd runs its boot tasks: mount (pid 3) exits with status 66 (EX_NOINPUT) and the boot panics with 'mount[3] exited'. There is no Data volume yet, which is the mount it needs.
  • NativeEmmc serves the Data seed itself instead of RamBlock: the payload's /chosen/memory-map/RAMDisk becomes a read-write RAM window named Data, and the driver answers the two RamBlock entry points kern_exec.c calls once its seed path is compiled in, native_ram_block_volume_seed() (the Data volume on that window) and native_ram_block_gpu_addon() (none on this board; the first link failed on it as an undefined symbol although its declaration is weak). IOMaximumBlockCountRead is 2048 blocks now that transfers run in 256-block commands.
  • Once userland read in parallel, APFS reported data hash mismatches on the sealed SystemOS volume (disk6s1). The block driver drains requests on their callers' threads through one shared bounce buffer, and released the transport lock between the read into it and the copy out of it, so a second reader could refill it first and the first request received the other one's blocks. The copy now happens under the lock (the request's memory is wired for the I/O, so it cannot fault back into the driver). Not yet booted.
  • native_ram_block_volume_seed() takes the one APFS volume on the RAM window instead of looking the kernel's UUID up, since a Data seed can only be there and every seed build has a new UUID; a differing UUID is reported, not refused, so a new seed no longer needs a new kernel.
  • With the copy under the lock, a 256 MiB seed, sheng-disable-watchdogd=1 and sheng-lite=1, the boot runs without a panic for the whole capture (heartbeat at 336 s): no data hash mismatch is reported, the watchdog policy disables watchdogd, early boot completes at 170 s and only liveactivitiesd crashes. The Data volume then fills about a minute after early boot; containermanagerd (first-boot data containers), launchd, analyticsd and APFS itself hit ENOSPC.
  • Persistent Data write path and fences: package/sirius_emmc.c implements block writes via CMD24/CMD25 with Auto-CMD12 over ADMA2, polls CMD13 until the card exits programming state with error checking, and issues CMD6 FLUSH_CACHE when EXT_CSD CACHE_CTRL is active. Writes are strictly fenced twice: once to the single window opened by sirius_emmc_set_write_window(), and globally to the userdata partition boundary minus its first MiB (protecting the SHNGROOT manifest).
  • Persistent Data container window: package/NativeEmmc.cpp implements prepareDataWindow(), locating the first MiB boundary behind the root image (userdata LBA 35627008) and verifying the NXSB APFS superblock before publishing a read-write eMMC block device when sirius-data-write=1. Writes are drained through the software queue keeping the bounce buffer under the transport lock in both directions, doSynchronizeCache flushes the eMMC write cache, and native_ram_block_volume_seed() resolves the Data volume on whichever window exists.
  • scripts/make-data-window-image.py converts the grown 8 GiB APFS Data container into a fastboot sparse image for userdata: a DONT_CARE chunk leaves the root image untouched, RAW chunks write nonzero blocks, and FILL chunks write zero blocks so free space reads as zero on device.
  • Persistent Data volume verified on hardware: with sirius-data-write=1 and the 8 GiB container flashed via data-window.simg behind the root image, NativeEmmc opens the write window at LBA 35627008 (16777216 blocks), mounts disk3s1 (OwnerData, 0C28586B-...), early boot completes at 159.6 s, and APFS commits transactions up to xid 85 with 0 ENOSPC errors across the entire capture.

完成条件 ​

  • The eMMC registers, GPT and the SHNGROOT windows are correct; APFS mounts the OS volume, bsd_init completes, init execs /sbin/launchd and execve returns 0.

当前范围 ​

  • Writes are strictly fenced to the persistent Data window on userdata (LBA 35627008, 8 GiB); the rest of the card remains read-only.
  • The card runs at 50 MHz in high-speed SDR on 8 bits (about 30 MiB/s). It supports DDR52 and HS200 (DEVICE_TYPE 0x57), but DDR needs the host's DDR mode and HS200 needs tuning, and neither is implemented; 100 MHz is only tried when the boot chain already left HS200 timing.
  • The SystemOS volume is authorized by a bring-up boot argument (sirius-allow-unverified=1), not by an owner root policy.
  • The exec policy no longer needs a boot argument: the payload carries the original trust-cache bundle and the kernel initialises its runtime at startup. The sirius-skip-cs bypass is kept in the patch series, unused, in case a future image falls outside the bundle.

下一步 ​

  • Display driver: U-Boot leaves a 1080x2240 splash framebuffer at 0x9c000000; bind GopFramebuffer or NativeLegacyDisplay to get screen output past SpringBoard.
  • Touch driver: Synaptics S3706 adapter from the fajita reference.
  • Keep the trust-cache bundle in step with the OS build when the IPSW changes (its sha256 is pinned in board.toml [payload] trust_cache_sha256).
  • Raise the pid 1 syscall log beyond 24 entries and mirror launchd's stderr writes into the USB ring, so later boot-task failures name their call and message.
  • For more storage speed: DDR52 (8-bit DDR at 52 MHz, no tuning) roughly doubles the rate; HS200 needs the msm tuning sequence.

证据 ​

显示 ​

implemented

组件: GopFramebuffer.

硬件与依赖 ​

  • U-Boot hands over a 1080x2240 splash framebuffer at 0x9c000000 through GOP (vidconsole).
  • The panel is 1080x2244 DSI; there is no backlight or display driver in the kernel yet.
  • GopFramebuffer driver subclasses IOMobileFramebufferLegacy with GOP backend mapped at 0x9c000000.

配置 ​

  • drivers/GopFramebuffer is a board fork of GopFramebuffer at 33e941a878dcf01b98dbd9160d798bd56e7551ea (the whole component: the tree links drivers/<ID> in place of the catalog checkout). Its only change: sirius-fb-wc=1 maps the scanout write-combined instead of uncached in NativeLegacyDisplay.cpp; without the argument it behaves as the catalog revision.
  • CONFIG_XNU_BOOT_PROGRESS=y links the boot-progress layer; boot-progress=2 on the command line draws the kernel console lines.
  • package/Font.h is vendored from Mu-Silicium, so the archive builds without a UEFI checkout.
  • CONFIG_XNU_NATIVE_LEGACY_DISPLAY=y links libNativeLegacyDisplay.late.a built via scripts/build-native-legacy-display.py.
  • board.toml enables offline-legacy-register=1, offline-legacy-gop=1, offline-legacy-unit-scale=2, offline-legacy-pm=1, offline-legacy-phase=4, and artwork-display-gamut=sRGB.

操作步骤 ​

  • Build kernel, payload and volume, boot, and read the panel: stage lines, TOPO, PROBE, MEM and panic text.

开发经验 ​

  • native_legacy_display_late_probe also calls sirius_gpu_start() (package/sirius_gpu.c, sirius-gpu=1; see the gpu area).
  • native_legacy_display_late_probe also calls sirius_touch_start() (package/sirius_touch.c, sirius-touch=1; see the input area).
  • With the CoreUI fixup the panel shows crashes=0 at 335 s (iconservicesagent no longer dies) and the migration progress bar at about half; console: every load of dyld_shared_cache_arm64.11 logs text-fixup +0x34f3b84 (8 bytes) applied.
  • iconservicesagent's crash is CI::GLContext::init+220 calling blr x8 with x8 from the GL dispatch table that GetMacroContext left zeroed (no OpenGL ES driver), reached from +[CUIShapeEffectStack sharedCIContext] = [CIContext contextWithOptions:[CUIShapeEffectStack preferredCIContextOptions]]. CoreImage has no environment switch to a CPU renderer (its CI_* strings checked). board.toml [payload] text_fixups patches that bl (dyld_shared_cache_arm64.11 file offset 0x34f3b84, unslid 0x1a31d7b84) to mov x0, xzr: -[_CUIGraphicVariantVectorGlyph _createGraphicVariantImageAtScale:] then returns the NULL of [nil createCGImage:fromRect:] and -[IFSymbol imageForGraphicSymbolDescriptor:] branches on it (cbz x26), so the symbol draws empty.
  • Kernel afdb4b1 / payload e167d8c (commit 05f5aa8), photo at t=345 s: the panel stays readable above the Apple logo; FB 65 swaps/s, copy mean 9.7 ms, max 15.6 ms; LOAD user 0%, sys 13%, idle 85%; storage idle. The boot is not CPU-, storage- or copy-bound: it waits. An earlier photo at t=3500 s showed the progress bar full and then 0 swaps/s at 99% idle. The bar is iOS's data migration (DataMigrator; its done marker is LastSystemVersion in /var/mobile/Library/Preferences/com.apple.DataMigration.plist, and it kills a plugin after a watchdog timeout): the panel shows com.apple.migrationpluginwrapper ended by signal 9 (exit-reason namespace 2).
  • package/BootProgress.c publishes the live panel's first row (boot_progress_panel_top) and the GopFramebuffer fork copies frames only above it, so the panel stays readable between repaints instead of flashing under 16 frames/s of full-frame copies.
  • With sirius-fb-cached=1 (kernel f222029, payload 952ab40, commit 01d06b4c7826c80fde7c4c2562b153188267b641) the copy drops from 182 ms to 9.3 ms mean over the first 211 frames and 13.7 ms over 9276 frames in about 560 s (about 16 frames/s), and the source page reads VM_WIMG 0x2 (copyback) after the cacheable mapping (docs/evidence/sirius-usb-console-20261028-cached.log). The panel now shows only briefly: each full-frame copy overwrites it and it repaints every few seconds.
  • The source surface is write-combined (VM_WIMG 0x6 on its first page) and the copy stays at 182 ms mean with the write-combined scanout (kernel 130bf97), so the source loads set the cost. sirius-fb-cached=1 maps the source with kIOMapCopybackCache and runs flush_dcache64 (clean and invalidate to PoC) over it before the content scan and the copy; NEMMC fb cache ... wimg shows the page attribute after that mapping.
  • With sirius-fb-wc=1 the screen looked the same and the console cannot show the copy time, so the board fork now times a read-only pass over the source (the content scan of the first 32 frames) and records the cache mode of both mappings and the source page's pmap attribute: a slow read pass with a copy of the same order means the uncached source, not the scanout stores, sets the cost.
  • With the keystore stand-in iOS presented 14954 frames by t=3500 s, every one a full 9450 KiB CPU copy at a mean of 201.7 ms (max 209.6 ms) into the uncached scanout, about 47 MB/s: the copy ran nearly the whole time the Apple logo and progress bar were up (docs/evidence/sirius-panel-20261028-keystore.log).
  • The late display probe calls native_keystore_probe() first, and the late archive carries KeyStoreProbe.o and NativeKeyStoreEvents.late.o, whose static constructor is removed and run by the probe between preModLoad and postModLoad.
  • The panel gained an EMMC row (step, version, capabilities, state, clock, power, clock-branch halts) next to the USB row, so the storage bring-up is visible without a serial link.
  • The USB row (drawn by this panel) labels its six values after what they now carry - step, txev, rxev, chars, echo, poll - because they were first wired to register names and had drifted.
  • The panel is the readable kernel log of this port: it renders the console, the TOPO/PROBE rows and panic() output.
  • Since the panic snapshot patch the panel re-renders at panic time, so the rows are not a stale frame.
  • NativeLegacyDisplay and NativeGOPPresenter compile as a late archive (libNativeLegacyDisplay.late.a) via scripts/build-native-legacy-display.py, stripping static constructors from __DATA,__mod_init_func and globalizing them so the board display probe in LegacyDisplayProbe.cpp runs them safely after prelinked kexts have been admitted.
  • The archive build script filters out C++ language and standard flags (-std=gnu++2b, -fapple-kext, -nostdinc++) when compiling C source units such as LegacyDisplayStubs.c.
  • The audited ABI data for iOS 17.7.11 21H461 is stored in scripts/data/mobile-graphics-legacy-abi.json, allowing build-native-legacy-display.py to bind all ~170 inherited virtual methods in original-bindings.S without needing the raw kernelcache during CI.
  • Audit against the 21H461 kernelcache proved the exact 13 dispatch table / constructor / method targets for IOMobileFramebufferLegacy and its 358-entry vtable with 9 abstract leaves.
  • The GOP framebuffer nub publishes under /arm-io as display-subsystem in phase 4, registering power management and publishing the display service so SpringBoard stops crash-looping.

完成条件 ​

  • A known frame appears with correct geometry, and the kernel console and panic output stay readable on the panel.

当前范围 ​

  • IOMobileFramebufferLegacy scans out nothing of its own: it wraps the U-Boot GOP framebuffer, with no backlight, no modesetting and no DSI panel control.
  • Frames reach the GOP buffer but are black: the on-screen panel counts 8 swaps accepted and 6 full frames (9450 KiB) copied by the CPU, with no GPU offers, so nothing renders content into the surfaces backboardd presents.
  • The CPU copy takes 180-190 ms per full frame, which caps presentation at about 5 frames per second.

下一步 ​

  • Find why the presented frames are black: with no Adreno GPU driver CoreAnimation gets no GPU work done (gpu offers=0); check whether backboardd can fall back to software rendering or needs a GPU, and whether the default_fb_surface miss at 224.9 s matters.

证据 ​

输入 ​

planned

硬件与依赖 ​

  • The board has no Home button; volume keys are reported by the button node of the ADT.
  • 57UU/linux-sirius (feat/sirius-7.2, sdm710-xiaomi-sirius.dts): the touch controller is an ST FTS521 (st,fts521) at I2C 0x49 on i2c9 = QUP1 SE1 at 0xa84000 (GCC_QUPV3_WRAP1_S1_CLK, SPI 354, SDA/SCL gpio6/gpio7 function qup9), interrupt gpio125 active low, reset gpio99, vdd = tp_vddio_vreg (gpio26, regulator-boot-on), avdd = PM660L LDO6 (vreg_l6b_3p3, RPMh); touchscreen-size 1080 x 2244, keys 158/139. Not the Synaptics S3706 the earlier notes assumed.

配置 ​

  • board.toml writes [adt.buttons] with hold, voldown, volup and home-button-type = 2.

操作步骤 ​

  • Follow the input guide once a touch controller is known, then select an implementation.

开发经验 ​

  • Second probe, after a review against Linux (not run yet): the analog rail is voted on through RPMh (cmd-db "ldob6" lookup with zero-padded 8-byte ids, VRM slave id 4 = CMD_DB_HW_VRM; apps RSC drv-2 at 0x179e0000, the first ACTIVE TCS at +0xd00, voltage 3300 mV, mode 7 (PMIC4 LDO HPM), enable, each with a response requested, completion polled on IRQ_STATUS bit 0, command-enable mask written exactly, per rpmh-rsc.c ver 2.7/3.0 offsets), in fts_chip_powercycle() order (reset low, vdd, 1 ms, avdd, 5 + 10 ms, reset high); the chip reset is the gpio toggle (fts_system_reset with a reset gpio), followed by pollForEvent(EVT_ID_CONTROLLER_READY) for up to 1 s before the chip id read. Also: 5 broken string literals from a scripted edit were found by re-reading and fixed; both C files pass clang -fsyntax-only -Wall -Wextra.
  • Kernel 66d1272 (commit 10f440b) / payload 7995ea8, at 77 s (docs/evidence/sirius-touch-20261028-probe1.log): the QUP1 clocks run after the vote (m_ahb, s_ahb, s1 CBCR CLK_OFF clear, vote 0x45b00000), the SE1 RCG already selects XO (cfg 0x00100000), QUP1 hw_ver 0x10010000, SE1 runs I2C firmware (proto 3) with FIFO mode and a 16-word TX FIFO; the chip acks 0x49 (no NACK on any transfer) but every read returns zeros and the reset never reports ready, while gpio125 stays low (events pending). The FIFO packing was LSB-first; i2c-qcom-geni.c packs MSB-first (geni_se_config_packing(..., true, true, true)), so every byte reached the chip bit-reversed. Fixed: TX/RX packing 0x0007f8fe/0x000ffefe, and the RX FIFO is drained after CMD_DONE.
  • package/sirius_touch.c (sirius-touch=1, started from native_legacy_display_late_probe): votes the QUP1 M/S AHB and SE1 clocks (GCC 0x5200c bits 20/21/23), checks their CBCRs before any SE access, sets the SE1 RCG to XO, programs FIFO-mode GENI I2C at 400 kHz (Linux geni_i2c_clk_map_19p2mhz: divider 2, 5/11/22), sets the pins, drives gpio26 high, pulses reset on gpio99, reads the FTM5 chip id (0xfa 0x20000000, expected 0x36 0x39), resets the chip (0x80 to 0x20000024, waits for event 0x03), turns sensing on (0xa0 0x00 0x01) and polls the event FIFO (0x86, 8-byte events: enter 0x13, motion 0x23, leave 0x33). Contacts go out as TOUCH console lines and through native_touch_* (the board side of ShengHidInput, in its 30480 x 20320 units). Not run on hardware yet.
  • The SDCC1 probe does not touch the ADT buttons, so the input row and its hold/voldown/volup mapping are unchanged.
  • No input component is selected; the button node exists so that BSPlatform and MobileGestalt read a coherent product.

完成条件 ​

  • Touches map to the display with stable contacts and the volume keys produce the expected HID reports.

当前范围 ​

  • No touch support and no IOHIDDevice provider.

下一步 ​

  • Identify the touch controller from the vendor DTS and the panel firmware.

证据 ​

按下一步操作收集证据。

GPU ​

planned

硬件与依赖 ​

  • Adreno 616 (A6xx gen1, the A615 family with A615/A618/A619): GPU at 0x05000000 (0x40000) with cx_mem 0x0509e000, GPU SPI 300; GMU at 0x0506a000 (0x30000) with SPI 304 (hfi) and 305 (gmu); GMU PDC at 0x0b280000 and its sequencer at 0x0b480000 (0x10000 each); GPU CC (sdm845-gpucc) at 0x05090000 (0x9000); the GPU's own SMMUv2 at 0x05040000 (0x10000), global SPIs 229/231, context SPIs 364-371, GPU stream 0, GMU stream 5; ZAP region 0x95c15000 (0x2000). Values from mainline sdm670.dtsi; the SDM710 board's own reserved-memory and chip ID are not yet read on hardware.
  • Firmware: 57UU/sirius-distro firmware/gpu carries a615_zap.mbn (14272 bytes, from ellyq/firmware-mainline-pyxis f2e4f88, README: GPU init verified on this phone), a630_sqe.fw and a630_gmu.bin (Debian trixie firmware-qcom-soc 20250410-2). Linux loads the ZAP as qcom/sdm710/pyxis/a615_zap.mbn.
  • On sirius the ZAP region is at 0x97615000 (0x2000), not sdm670.dtsi's 0x95c15000: 57UU/linux-sirius sdm710-xiaomi-sirius.dts (17601ca5198a736f696447a9b6c568e4be97cc9c) deletes gpu_mem and redefines it, enables &gpu (qcom,adreno-615.0, the upstream msm driver) and names the ZAP firmware. That kernel runs the GPU on this phone and is the working reference for the A616 backend.

配置 ​

  • Nothing is configured: the CPU renders the boot-progress panel through the firmware framebuffer.

操作步骤 ​

  • Start from Adreno6xx (RigoLigoRLC/unixnu-adreno6xx, branch unixnu-sm-t733): the Adreno740 service, BO/VM, ring, submission, fences and NativeAdreno ABI v1 with an A6xx power, GMU, SMMU, ZAP and register-init backend, so AdrenoMetal stays usable unchanged.
  • Keep the component's split: A6xx procedures in the component (an A616 generation next to A642L), board windows, interrupts and stream IDs in this repository through native_adreno_a6xx_resources and an /arm-io/adreno ADT node, as sm-t733 does in package/T733GpuResources.cpp.
  • First milestone, in order: CX GDSC and GMU clocks, GMU firmware boot and an HFI reply, GX on, CP start, one bounded submission whose fence completes. Metal and presentation come after.

开发经验 ​

  • Phase 1 of the A616 bring-up is a standalone probe, package/sirius_gpu.c (sirius-gpu=1, from native_legacy_display_late_probe), before the Adreno6xx fork: GCC votes 0x52004 bits 15/16 (GPLL0 and its divided output to the GPU) and the gpu_cfg_ahb (0x71004), gpu_memnoc_gfx (0x7100c), gpu_snoc_dvm_gfx (0x71018) and ddrss_gpu_axi (0x44038) branches, each checked for CLK_OFF; then the GPU CC CX GDSC (0x106c: wait values EN_REST 2, EN_FEW 8, CLK_DIS 8, SW_COLLAPSE cleared, PWR_ON polled in gds_hw_ctrl 0x1540, gdsc.c); then the GMU RCG (0x1120) at 200 MHz (source 6 = GPLL0 main div, divider 1.5, rcg2_shared force-on around the update) and the cx_gmu (0x1098) and cxo (0x109c) branches. It then only reads: GMU registers at 0x05000000 + 4 x dword offset (a6xx_gmu.xml: CM3_SYSRESET 0x1f800, FW_INIT_RESULT 0x1f81c, CORE_FW_VERSION 0x1d3f8, ALWAYS_ON_COUNTER_L 0x1f888 twice 10 ms apart, SPTPRAC 0x1f8d0, RPMH_POWER_STATE 0x1f8ec, HFI_CTRL_STATUS 0x1f980) and the GPU SMMU global sCR0/IDR0/IDR1/IDR2/IDR7. Adreno6xx is not in this UniXNU catalog and its build needs ArmSmmu4K, AdrenoPM4, AdrenoFirmware and generated ABI bindings, so the fork follows once the CX sequence is proven on hardware. Not run on hardware yet.
  • Without a Metal device CoreImage falls back to an OpenGL context and crashes there: iconservicesagent dies with SIGSEGV at PC 0 in CI::GLContext::init() + 220, reached from IconServices -[ISGraphicSymbolResource imageForSize:scale:] through CoreUI +[CUIShapeEffectStack sharedCIContext] while rasterizing an SF Symbol glyph (docs/evidence/sirius-crash-symbols-20261028.log). launchd restarts it; any process that rasterizes symbol glyphs through CoreUI can hit the same path until a GPU driver exists.
  • The catalog's Adreno740 targets the A740 of SM8550, and Adreno6xx's backend is written for the A642L of SM7325 (A660 firmware, QSMMUv500 with TBUs, AOP-hosted PDC, ACD through AOP QMP, Yupik clocks).
  • For the A616 the backend needs: the legacy GMU/HFI protocol (Linux sets gmu->legacy for the A630 family), PDC and RSCC sequences with the PDC in its own MMIO window rather than in AOP, the a615_hwcg clock-gating table and a630 protect list, sdm845 GPU CC clock and GDSC handling, and a plain SMMUv2 (no TBU windows).
  • Linux's A6xx catalog has no separate A616 entry; the A615 entry (chip ID 0x06010500) uses a630_sqe.fw, a630_gmu.bin and a615_zap.mdt with 512 KiB GMEM and ADRENO_QUIRK_4GB_VA, so the chip ID must be read from RBBM on this phone.
  • The S2CR writes that hung the SoC were on the apps SMMU, which the boot chain leaves disabled; the GPU SMMU is a separate instance in the GPU CX power domain and must not be touched before CX is on.
  • On SDM710 touching a branch clock before its source is programmed has stalled the bus and rebooted the phone (sirius_usb_power), so the GPU CC sequence must program sources before enabling branches.

完成条件 ​

  • GPU submissions complete, fences work and a known image renders.

当前范围 ​

  • No GPU bring-up has been attempted on this port.
  • Adreno6xx's power, GMU and SMMU sources carry GPL-2.0-only notices (adapted Qualcomm procedures); the rest of the component is MIT.

下一步 ​

  • Take the three firmware files from sirius-distro firmware/gpu, fork Adreno6xx with an A616 generation following the linux-sirius msm sequence, and add the /arm-io/adreno ADT node and resources glue.

证据 ​

网络 ​

planned

硬件与依赖 ​

  • The Wi-Fi chip of this unit is not identified in the port record yet.

配置 ​

  • No network component is selected.

操作步骤 ​

  • Follow the network guide once the chip and its firmware are known.

开发经验 ​

  • No network work has started; the first console image does not need it.

完成条件 ​

  • Scanning, association and sustained traffic pass with the selected firmware.

当前范围 ​

  • No network support at any layer.

下一步 ​

  • Identify the WCN chip from the vendor DTS and the firmware partition before selecting a driver.

证据 ​

按下一步操作收集证据。

音频 ​

planned

硬件与依赖 ​

  • No audio path is documented for this unit yet.

配置 ​

  • No audio component is selected.

操作步骤 ​

  • Follow the audio guide once the codec and DSP path are known.

开发经验 ​

  • No audio work has started.

完成条件 ​

  • Each declared route carries the selected format through playback or capture.

当前范围 ​

  • No audio support at any layer.

下一步 ​

  • Inventory the codec and the DSP layout from the vendor tree.

证据 ​

按下一步操作收集证据。

电源 ​

planned

组件: AppleKeyStoreState.

硬件与依赖 ​

  • No suspend/resume path exists; the boot-progress panel is the only power-facing behaviour so far.
  • There is no SEP: nothing answers AppleKeyStore, which the unchanged libaks/MobileKeyBag ask for the device and lock state.

配置 ​

  • board.toml writes [adt.defaults] sleep-disabled = 1 so the CPU does not park forever.
  • PSCI is declared in the SoC DTSI and used for reset and secondary-core start.
  • AppleKeyStoreState is selected as sheng and asteroids select it: package/KeyStoreProbe.cpp (sheng's, at 08cde20edc3254ed6f7df8d0f5384fb887786e8f) publishes its service from the display late probe with offline-key-events=1, and sheng-aks=1 lets its user client answer the state queries (no passcode, unlocked). scripts/build-native-legacy-display.py compiles both into libNativeLegacyDisplay.late.a. It stores no key and protects nothing.

操作步骤 ​

  • Follow the power guide to add the PMIC and battery controls once the platform expert is stable.

开发经验 ​

  • The eMMC probe does not vote any RPMH resource; it re-enables the GCC SDCC1 branches that the boot chain had already set.
  • Reset works through PSCI (the stage-3 reset loop proved it).
  • Measured with the PMU cycle counter (kernel 5df62c0, payload 8a04814): the Cortex-A55s run at 1516 MHz and the Cortex-A75s at 300 MHz, the cpufreq-hw source-0 row (GPLL0/2) the boot chain leaves (out/Q-cpuclk captures, NEMMC cpu MHz).
  • With sirius-cpufreq=1 (kernel 130bf97, payload 216ef03, commit fb12af48938b7de333ca29b41195486b95e790ac) the A55 domain (enable 1, 9 rows) moves from row 6 to row 8 and every A55 measures 1708 MHz; the A75 domain reads enable 0 (13 rows, best 2208 MHz), so the firmware never put it under hardware DCVS, the request is skipped as Linux would, and the A75s stay at 300 MHz.
  • With the retry (kernel f222029, payload 952ab40) the A75 domain reads enable 1 after boot and gets row 12 (2208 MHz): NEMMC cpufreq enable/rows/before/request 0001000d0000000c; the A55s stay at 1708 MHz. No heartbeat ran on an A75 in that capture, so the A75 clock is requested but not yet measured (docs/evidence/sirius-usb-console-20261028-cached.log).
  • sirius_cpufreq_retry(), called from the heartbeat every sixteenth beat once the secondary cores run, re-reads each domain's enable bit and makes the request a domain did not get at EARLY_BOOT; NEMMC cpufreq ... enable shows the live bit.
  • Under Linux on this phone (57UU/sirius-distro docs/CPU-POWER-STRESS-TEST.md) the same cpufreq-hw driver, which refuses a domain whose enable bit is 0, holds the A75s at 2208 MHz, so the A75 domain does become enabled; the enable 0 read at EARLY_BOOT is likely taken before cpu6-7 are started (not yet verified).
  • package/sirius_cpufreq.c (sirius-cpufreq=1) reads each cluster's cpufreq-hw table (0x17d43000 cpu0-5, 0x17d45800 cpu6-7; rows at 0x110 + 32 i, PERF_STATE 0x920, current vote 0x704, the Linux qcom-cpufreq-hw layout for sdm670) at EARLY_BOOT and requests the highest non-boost row; the hardware sets voltage and its limits. The register bases are SoC facts that belong in the sdm710 SoC repository once proven.
  • On asteroids SpringBoard never finished booting on persistent Data without the AppleKeyStore stand-in and reached its first picture with it (UniXNU docs/devices/asteroids.md, T20-T29); sirius shows the same state: persistent Data, SpringBoard no longer crashing, a few black frames and then no swaps.

完成条件 ​

  • Declared power controls and battery readings match observations and sleep/resume restores the dependent peripherals.

当前范围 ​

  • No battery reporting, no CPU frequency policy and no PMIC driver.

下一步 ​

  • Measure what makes the Apple-logo stage slow (the live panel's LOAD, SCHED, FB and storage rows, captured without --match so the log runs past the assert text), and find what stored the not_in_kdp assert.
  • Keep sleep-disabled until a resume path exists.

证据 ​

镜像组装 ​

implemented

组件: XnuPrelink.

硬件与依赖 ​

  • The payload FAT volume is the flashed artifact of this port; there is no boot.img.
  • The prelink inputs (kernel path, roots, slide, early security initializer) come from the OS profile; the board does not override them.

配置 ​

  • [payload] sets volume SIRIUSESP, 256 MiB, loader = payload, dram_size and the kernel command line.
  • [payload.adt] keeps only chip_id: the platform seed is supplied per build with --unique-chip-id when the userland stages need it.

操作步骤 ​

  • ./unixnu build kernel, ./unixnu build package, then bash scripts/build-darwinboot.sh (or the CI workflow), ./unixnu build payload, and finally make-uboot-payload.py --output the volume to flash.

开发经验 ​

  • scripts/build-sirius-touch.py compiles package/sirius_touch.c like build-sirius-cpufreq.py and adds it to libShengPlatformPower.a.
  • The eMMC probe ships in the same force-loaded archive as the USB console, so the package list and payload layout are unchanged.
  • scripts/build-arm-driver.py replaces the tree's copy, so a component recipe that names it must be one of its choices: AppleKeyStoreState builds drivers/AppleKeyStoreState/NativeKeyStoreEvents.cpp into build/apple-key-store-state/libAppleKeyStoreState.a.
  • Format 2 generates kernel.macho.adt from the DTS, so the legacy ADT script is retired; make-uboot-payload.py still finishes the volume because U-Boot needs the EFI binaries inside it.
  • scripts/build-xnu-4k-baseline.sh is a copy of the UniXNU script with one board deviation: the boot-progress interlock only requires the source crypto provider.
  • The legacy build applied patches/xnu/*.patch by glob (56 files); format 2 applies only what the series list names, and the ARM64 and OS series assume the two files they omit are present. native-smp-secondary.patch sets MAX_CPUS 8 and Affinity1 core numbering; sheng-platform-power.patch supplies the bsd_init.c block that zzz-boot-progress.patch quotes as context (CONFIG_XNU_SHENG_PMGR is off, so its code compiles to nothing). Both now live in the sdm710 SoC series, which applies before the OS series.
  • The first format 2 kernel build on Linux failed at the last patch, zzz-boot-progress.patch, with a bsd_init.c context mismatch: the missing piece was sheng-platform-power.patch, not the moved native-smp-secondary. With both in the SoC series the whole series applies and the kernel prelinks: kernel.macho 50 MB, __TEXT 0xfffffff007004000, 30 kernelcache components, 69294 bindings.
  • The kernel step also runs on Linux through UniXNU's toolchain installer, which builds a patched LLVM, cctools-port ld64, MIG, iig and lzfse. That installer writes /opt/unixnu-linux, /usr/bin shims and distribution packages, so this port runs it inside a Podman image (containers/xnu-linux/) and leaves the host untouched. The alternative, Allenwdk/xnu-linux-toolchain, uses the system clang with portable-kalloc-type-builtins.patch and cctools-port, but needs a path-layout adapter for the driver's checks.
  • The first flashed build hung on hardware with the entry probe's blue bar and no magenta bar. The cause was a defconfig override, not the kernel sources: CONFIG_XNU_NATIVE_ARCH_TIMER=n. build-xnu-4k-baseline.sh sets NATIVE_ARCH_TIMER=${XNU_NATIVE_ARCH_TIMER:-${XNU_ARMCPU:-0}}, so the legacy build ran with 1; at 0, architectural-timer-entry.patch compiles the BCM2837 local interrupt router write (0x40000040 + cpu*4, Raspberry Pi local peripherals) into start.s. SDM710 has nothing at that address, and the store aborts before the MMU is on. The defconfig now leaves the ARM64 profile value (y) alone and documents why.
  • Diagnosis method worth repeating: the legacy kernel from payload-full.img boots with this repository's new UniXNU.efi, DarwinBoot.efi and DTS-generated ADT (it reaches _sleh_irq and panics on the known early timer PPI), so the EFI side and the ADT are sound and the regression is in the kernel build. Comparing the two kernels' start_first_cpu bodies then showed the extra router write as the only semantic difference.
  • With the timer override removed the Linux-built kernel boots past the MMU on hardware: the boot-progress panel reports TOPO boot=0 max=7 nums=01234567, the console reaches IOKit (PE_init_iokit, IONVRAM initImageFormat) and then the CPU takes an IRQ it cannot dispatch. lr 0xfffffff00741754c is _sleh_irq+0x70, the same NULL per-CPU handler the legacy kernel hit.
  • That IRQ is DarwinBoot's early timer PPI. GIC_SGI_PPI_ENABLE included bit 19, so the EL1 virtual timer fired before ArmCpu::start installed the handler. DarwinBoot now enables SGIs 0..15 only and leaves INTID 19 to ArmCpu (armcpu-timer-intid=19), which is what the handoff's next step asked for.
  • With the timer PPI left to ArmCpu the next stop was AppleARMPlatform's CPU class: it matches cpu0..cpu7 because IODTPlatformExpert compares IONameMatch by prefix ('cpu' matches 'cpu0'), starts first, and panics with 'CPU configuration in device tree is invalid' (its AppleARMCPU.cpp validates max_cpus, cpu-id, interrupts and function-* properties that an Apple device tree carries). The native ArmCpu personality (IOPlatformDevice, probe score 2000) did not win that match even after probe() added 40000, so make-uboot-payload.py rewrites the AppleARMCPU IONameMatch from cpu to xpu in the personality plist inside kernel.macho (equal length, nothing moves).
  • A frozen panel is not proof of a hang: the panel repaints from the timer interrupt, so before ArmCpu enables the timer PPI the last painted second stays on screen while the kernel may still be running. Checkpoints (armcpu-panic=N, panics at entry/prepareGic/registration/controller/initCPU/registerService) are what distinguish the two.
  • ArmCpu::start then failed inside initDistributor: it re-ran the quiesce sequence (GICD_CTLR &= ~3, wait, restore with ARE) over the state DarwinBoot had already programmed, which clears the group enables and leaves the core with no interrupts at all; the boot then sits in IOKit matching with a frozen panel. The fix keeps the firmware state when GICD_CTLR already reports affinity routing plus group 1 and only quiesces the SPIs. After it the panel advances (time=31s, IRQ=1076) and the kernel reaches bsd_init with TOPO boot=0 max=7, PROBE calls=17 acc=8 num=7 reg=700 - eight CPUs online.
  • The next blocker is AMFI: 'AMFI: No PMGR?' @ConfigurationSettings.cpp:235. The unchanged target AMFI finds the PMGR through IOService::serviceMatching("ApplePMGRNub") + waitForMatchingService, then reads the provider at nub+0x88 and calls its readAon/writeAon slots (OR bit 2). The original classes are ApplePMGR (storage 0x4b8a8) and ApplePMGRNub (0x740); UniXNU/scripts/audit-amfi-pmgr-contract.py pins the exact instructions. amfi_get_out_of_my_way=1 does not skip the check. Two routes remain: instantiate the original classes the way UniXNU-sheng does (OSKextLoadKextWithIdentifier + a generated OriginalPMGRABI.h + a published nub), or patch the AMFI block in the prelinked kernel.
  • AMFI is cleared without touching the target kernel: the nub helpers behind AMFI's readAon/writeAon return 0 when the provider stored at nub+0x88 is NULL, so publishing a real but provider-less ApplePMGRNub satisfies the latch (read 0, OR 4, write no-op). package/SiriusPmgr.cpp allocates the class through OSMetaClass::getMetaClassWithName + alloc + init, attaches it to the platform and registers it from the sheng_pmgr_run hook in bsd_init (CONFIG_XNU_SHENG_PMGR=1, sheng-pmgr=2), and its build script writes the archive to build/sheng-platform-power/libShengPlatformPower.a because build-xnu-4k-baseline.sh force-loads exactly that path.
  • With AMFI satisfied the boot passes bsd_init and stops at 'Failed to mount root device' @IOKitBSDInit.cpp:984 with 8 CPUs, IRQ=2145, 61 s of live uptime: the kernel now runs to the point where it needs a storage driver and a root filesystem, which this board does not have yet (no UFS or VirtIO block component, no APFS container on the payload volume).

完成条件 ​

  • The kernel prelinks against the pinned 21H461 kernelcache, the payload contains the kernel, the ADT and the DarwinBoot driver, and the flashed volume boots from U-Boot.

当前范围 ​

  • The kernel step needs macOS and Xcode, or the container image with UniXNU's Linux toolchain; the board script must be re-based on the upstream script when UniXNU changes it.

下一步 ​

  • Run the new payload once and diff the generated ADT against the legacy one before flashing.
  • Boot the prelinked kernel on the phone and record the stage rows; the build side is now reproducible on Linux (container image, see downloads).

证据 ​

诊断 ​

implemented

硬件与依赖 ​

  • The panel and the serial console are the two evidence channels; since the DWC3 CDC console works, the host captures the kernel log directly and photographs are only a fallback.

配置 ​

  • panic_screen = true patches the entry probe and the panic screen into the payload kernel.
  • boot-progress=2 draws the kernel console lines; debug=0x14e keeps the debug output on.

操作步骤 ​

  • Record the last checkpoint printed on the panel and in the U-Boot/XNU log for every boot, with the kernel and payload hashes of that run.

开发经验 ​

  • package/sirius_gpu.c logs each GPU phase-1 step as a GPU console line (see the gpu area).
  • The TOUCH lines of the first probe are docs/evidence/sirius-touch-20261028-probe1.log; the chip-id line now also prints the transfer's M_IRQ status and the bytes read.
  • Kernel ca828cc / payload 38305ff (commit c54ee19, ephemeral-storage = 1): iOS reaches the Setup welcome screen ("hei", status bar, clock, battery icon) at about 358 s without a GPU driver (docs/evidence/sirius-spawn-20261028-ephemeral.log): com.apple.datamigrator now exits 0 after 18 to 35 s (four runs between 150 and 302 s), Setup is exec'd at 225 s, PosterBoard at 208 s; the wallpaper poster extensions crash-loop (KaleidoscopePoster signal 11, AegirPoster signal 6, CollectionsPoster killed by libxpc), crashes=20 at 358 s. Touch does nothing: there is no touch driver yet.
  • package/sirius_touch.c logs each touch bring-up step as a TOUCH console line (see the input area).
  • Kernel ca828cc (commit 144de65) / payload 95bcb3a (commit 1ce0478): no panic. SPAWN timeline (docs/evidence/sirius-spawn-20261028-nostackshot.log): launchd's boot tasks at 139 s, SpringBoard at 142 s, backboardd 148 s, com.apple.datamigrator 151 s, iconservicesagent 154 s (it no longer crashes), com.apple.migrationpluginwrapper 193 s and still running at 244 s; after 200 s only about 20 more execs. The console heartbeat stopped at about 258 s and later windows only trickled the old buffer (1.1 MB in 5 min), but the kernel kept running: the panel at t=2177 s shows crashes=0, the migration bar full, 62 swaps/s, idle 87%, RepackagingWorker killed by RunningBoard (signal 9, namespace 15) and the plugin wrapper by a signal 9. The same console stop came before the 335 s stackshot panic of payload cac4a55, so a stopped heartbeat alone is not a hung kernel.
  • Kernel afdb4b1 / payload cac4a55 (commit 144de65, the CoreUI fixup) panicked at 335 s with "thread wait info count mismatch while capturing snapshots for task ..." (kern_stackshot.c:3451); the console carried no panic text, the heartbeat stopped at beat 0x84. That check is DEVELOPMENT-only and fires when a thread's wait state changes between the count and the capture of a user stackshot, so not every CPU holds still during a stackshot on this port. kernel_args now pass validation_disables=0x10 (KF_STACKSHOT_OVRD): kern_stack_snapshot_internal returns KERN_NOT_SUPPORTED to every stackshot request.
  • package/BootProgress.c prints SPAWN <second>s exec <pid> <name> after an exec passes its signature check and SPAWN <second>s exit <pid> <name> status= ns= code= at every exit when sirius-spawn-log=1: the panel keeps only the last three failures, and the console carried no userspace timeline, so which job the idle boot waits on (and the migration plugins' watchdog interval) was not visible.
  • The boot of kernel 5df62c0 / payload 8a04814 froze at about 244 s: the heartbeat stopped at beat 0x50, after that beat's clock measurement had returned and printed, with no panic text mirrored (out/Q-cpuclk captures). The cause is not known; the earlier boots without the PMU read ran 19 minutes.
  • Crash records on the panel symbolize against the 21H461 arm64 shared cache: unslid address = recorded address - the record's slide (+...), looked up with ipsw dyld a2s; three cache files (.symbols, .34 and .52 .dyldlinkedit) are LZBITMAP-compressed in the SystemOS image and need apfs-fuse to extract. liveactivitiesd's SIGTRAP is a Swift runtime trap in SessionSyncEngine under SessionCore.Daemon.workloop (docs/evidence/sirius-crash-symbols-20261028.log).
  • Every sixteenth beat the heartbeat also prints the CPU load since the previous sample (NEMMC load user/sys/idle %, host_statistics64 HOST_CPU_LOAD_INFO), to tell a CPU-bound late boot from one that waits. The first build of it failed: 'use of undeclared identifier realhost' in NativeEmmc.cpp (kernel run 40695562209).
  • Every sixteenth beat the heartbeat also prints each cluster's cpufreq-hw state (NEMMC cpufreq enable/rows/before/request, row0 and best kHz, vote MHz) and the memory layout (NEMMC window base/size, NEMMC db-runs).
  • NEMMC cpu MHz counts the architectural PMU cycle counter (PMCCNTR_EL0, PMCR_EL0.E set and .D cleared, restored afterwards) over 10 ms of the system counter with interrupts off, and NEMMC cpu mpidr names the core: Affinity1 0x000-0x500 are the Cortex-A55s, 0x600-0x700 the Cortex-A75s. No CPU frequency driver exists, so it shows the clock the boot chain left.
  • The NEMMC fb rows print the copy cost on the console: copies, last/mean/max copy time, the last timed read-only pass over a source frame, and the source map options, scanout map options and the source page's pmap attribute (VM_WIMG_*), packed as source<<40 | scanout<<20 | wimg.
  • Heartbeat lines are re-mirrored too, so the last NEMMC alive line of a window is not the newest: compare the highest beat count per window before concluding the phone rebooted (S-cached: 0x70 then 0xe9, the same boot).
  • Past about 220 s the USB console only re-mirrors fragments of the kernel log buffer: iOS userspace does not log to it, so four 280 s windows after the not_in_kdp text add no new event (docs/evidence/sirius-usb-console-20261028-fbwc.log). Later stages are read from the screen and the panel.
  • The NEMMC heartbeat mirrors boot_progress_panic_text once it is non-empty, not only on a fatal panic: with the stand-in it carried 'pmap.c:3514 Assertion failed: not_in_kdp' at 215.7 s while the kernel kept running and iOS went on to draw its Apple logo, so --match 'NEMMC PANIC TEXT END' can end a capture of a boot that is still alive (docs/evidence/sirius-usb-console-20261028-keystore.log).
  • KeyStoreProbe.cpp writes the AppleKeyStore counters (opens, calls, selector:count) into extension slots 0-1, which the boot page draws and BootLog exports as X0/X1.
  • A register write-and-read-back test is the next diagnostic for the eMMC data phase, because SDHCI_TRANSFER_MODE reads back 0x33 after writing 0x13, which separates a write the vendor layer ignores from a command that is never sent.
  • The eMMC probe reports every step (controller registers, the clock tree, the response registers around CMD0/CMD1 and each failure) into the USB console ring, which is how the card clock and CMD2 findings above were obtained without a photograph.
  • The eMMC probe reports through sirius_usb_log_char() straight into the USB console ring, because kernel console output stops reaching the ring long before bsd_init runs.
  • Stage bands (entry, pre-MMU, panic) and the TOPO/PROBE/MEM rows replaced the LED and UART guessing of earlier bring-up.
  • A panic snapshot forces a panel re-render at panic time; before it, rows could show a stale frame.
  • The USB panel row names its fields after what they hold (step, txev, rxev, chars, echo, poll) instead of the register names they were first wired to, so a photograph can be read without the source.
  • The CDC console hands the console ring to the host once per attach: the pump timing that works (a short inline poll loop that breaks after the ring is drained) delivered the 3813-byte boot log on every attempt, while longer loops and a DTR-gated pump delivered nothing, so that timing is treated as load-bearing until the periodic path runs during bsd_init.
  • The post-exec wait is instrumented on the kernel side rather than through the driver: sirius-post-exec-trace.patch prints the exec thread-switch flags, every user thread start (from thread_bootstrap_return, so one line means the exec'd thread never ran and two mean it did), the first pid 1 syscalls with their returns, every bsd_ast for pid 1 and the first user faults after the exec, and the eMMC heartbeat prints the counters so one capture answers the question.
  • The exec does not stall after activate: load_init_program prints the scratch page (0x1000, the first free page of the init map), then the init thread is inside copyout() and takes 62,673,044 faults in about a minute while it stays TH_RUN, so the wait is a tight fault-retry loop, not a sleep.
  • PAN was the cause after all. The 0 read at driver start was a snapshot: Kryo 360 implements PAN and XNU's SCTLR_EL1_DEFAULT leaves SPAN clear, so the CPU sets PSTATE.PAN on every exception entry to EL1, and with CONFIG_XNU_NATIVE_PAN=n nothing clears it again. A copyout that runs with PAN set takes a permission fault that AT S1E1W (which ignores PAN) and vm_fault both consider resolved, so it retries forever; PAN is not part of the switched context, which is why the loop moved with timing (kernel 91 passed, 93 hung, cpus=1 got past the copyout). With CONFIG_XNU_NATIVE_PAN=y the driver reads PAN as 4194304 (set) and the loop is gone.
  • The mapping is not missing either: the first fault is a level-1 translation fault, but afterwards pmap_find_phys finds the page, AT S1E1W resolves the same address and vm_fault returns KERN_SUCCESS, while the retried store faults again as a level-3 permission fault; the raw leaf PTE the pmap holds is now dumped next to ESR/FAR/PC to see which AP bits it carries.
  • The pid 1 kernel stack, dumped out of band by the heartbeat and symbolized against mach.development.bcm2837.unstripped, shows _bcopyout+0x38 under _load_init_program_at_path+0x6c under _bsdinit_task+0x74, which is the copyout of the launchd path.
  • The loop is not limited to the copyout: with the trust-cache bundle back in the payload the exec completes (execve activate 0, AMFI answers nothing) and the exec'd thread enters userland at pc 0x100924e50, where it takes 92 million instruction aborts in about a minute on three addresses (ESR 0x82000007, level-3 translation faults) without ever issuing a syscall or a single block read; the entry point is right, the code pages are simply not visible to the CPU.
  • Both loops have the same shape: vm_fault opens the page in thread->map's pmap and returns KERN_SUCCESS, while the CPU keeps taking the fault. AT S1E1W and pmap_find_phys disagree about the physical page behind 0x1000, which points at the CPU running with a pmap that is not the thread's after the exec task switch.
  • A payload built without --trust-cache leaves the ring reporting no external trust caches found and AMFI kills launchd with namespace 9 (OS_REASON_EXEC) code 8; the same kernel with the bundle present reaches execve activate 0 without an exec-policy line, so that panic is a build-argument slip, not a regression.
  • The console can be captured on Windows: usbser.sys binds it as a COM port (USB\VID_1209&PID_0001, serial SIRIUS01) and capture-usb-console.py opens it with plain Win32 calls, because the gadget does not answer SET_LINE_CODING and SetCommState (and pyserial) fail with error 121.
  • The ring has no timestamps and is replayed whole on every poke, so capture-usb-console.py --timeline records when each distinct line first arrives; a timed boot shows 51 s from fastboot reboot to the eMMC driver, the shared-cache check until about 168 s and the mount panic at 171.7 s. A hard panic stops the kernel before the heartbeat mirrors its text, so --idle-stop ends a capture after a silence instead.
  • NativeEmmc reports the Data seed as 'NEMMC data seed at/bytes' when it publishes the RAM window and 'NEMMC data volume /dev/...' when the owner Data mount asks for it, or 'NEMMC data volume not found on the seed'.
  • When the seed's volume UUID differs from CONFIG_XNU_OWNER_DATA_VOLUME_UUID the driver logs 'NEMMC data volume is <seed UUID>, the kernel names <configured UUID>' and mounts it anyway.
  • capture-usb-console.py --duration counts from the script's start: counted from the port, which a boot brings up after about a minute, a 280 s capture ran about 5.6 minutes.
  • Persistent Data diagnostics: the driver logs 'EMMC write window lba=... blocks=... cache=...' on window configuration, reports EXT_CSD CACHE_CTRL in the probe, logs fallback to single-block writes if a multi-block write command fails, logs 'NEMMC data window lba=... blocks=...' when the persistent container is discovered, logs 'NEMMC data volume (eMMC) ...' or 'NEMMC data volume (RAM) ...' when resolved, and logs 'NEMMC write failed lba ...' on write errors.
  • With the persistent 8 GiB Data volume, the entire boot capture runs past early boot (159.6 s) up to 280 s with zero ENOSPC lines (vs 187 on the 256 MiB RAM seed); containermanagerd, launchd, and analyticsd write without running out of disk space; APFS transaction flushes advance through xid 85; SpringBoard (pid 118) and liveactivitiesd (pid 128) crash for want of a display/framebuffer driver.
  • Display driver diagnostics: LegacyDisplayProbe.cpp and LegacyDisplayStubs.c interpose on native_legacy_display_probe and native_legacy_display_late_probe, mapping the 1080x2240 GOP framebuffer at 0x9c000000, setting up the display-subsystem platform device under /arm-io, and providing weak stubs for boot progress scanout and DPU mappings.

完成条件 ​

  • Each claimed milestone has a reproducible command, a matching source and image revision and an accessible result.

当前范围 ​

  • The CDC console delivers the console ring as it stood when the host attached; making the whole boot stream live is the remaining work.

下一步 ​

  • Add the boot row and hash triple to every hardware result recorded here, and attach the captured console log rather than a photograph whenever one exists.

证据 ​

boot-chain ​

implemented

硬件与依赖 ​

  • Boot chain: ABL -> U-Boot (boot partition) -> UEFI -> UniXNU.efi on the vendor partition -> DarwinBoot.efi -> XNU.
  • The U-Boot fork github.com/57UU/u-boot branch sirius/v2.0 carries the board support for sdm710-xiaomi-sirius and sirius-build.sh's ENV_FILE option; the UniXNU boot chain itself is environment text, not source.
  • The EDK II package that builds UniXNU.efi, the DXE drivers and DarwinBoot comes from the UniXNU framework's package/ directory.

配置 ​

  • uboot/src is a submodule of that fork at branch sirius/v2.0 (shallow), pinned to a2a847b1a84.
  • uboot/unixnu-sirius.env is the board's stock default environment plus unixnu_boot/unixnu_check, the bootmenu entry they need and the bootcmd chain; uboot/build.sh builds boot-unixnu.img from the submodule and that file.
  • efi/ is a vendored copy of the framework's package/ (UniXNUPkg, Scripts, build.sh) taken at f85b5a6, so the repository can build the whole chain on its own.

操作步骤 ​

  • U-Boot: git submodule update --init --depth 1 uboot/src, then uboot/build.sh (needs CROSS_COMPILE and mkbootimg); fastboot flash boot uboot/boot-unixnu.img.
  • Payload: assemble the kernel, efi/ and the EDK II build output with scripts/make-uboot-payload.py --marker, then fastboot flash vendor.
  • Recovery: flash sirius-distro's build/boot.img, which ignores the marker and boots Linux.

开发经验 ​

  • U-Boot needs no source change for this port: with the environment file above, bootcmd runs unixnu_check first and falls back to the Linux path when /EFI/UniXNU/BOOTUNIXNU is absent on p80.
  • mmc 0:50 is a hexadecimal partition number (0x50 = 80 = the vendor partition); 0:80 is read as 0x80 = 128 and fails with 'Invalid partition 128'.
  • bootflow scan cannot find p80: it stops after the first 30 partitions of each device, so the boot chain uses load/bootefi with the partition spelled out.
  • The UniXNU boot chain used to live only in an unversioned scratch directory (the environment file and boot-unixnu.img), so the boot image could not be rebuilt from any repository; both the file and the recipe are in uboot/ now.
  • U-Boot's fastboot gadget answers the whole getvar dialogue but flashing the vendor partition hangs; the repeated 'request ... was not queued to ep1in-bulk' line is fastboot_tx_write() cancelling the IN request before every queue, a dequeue miss rather than a rejected transfer, and stock 2026.07 never received the modern dwc3 ENDTRANSFER/dequeue rework, so that cancel can strand a TRB the controller still owns. Removing the dequeue is the candidate fix.
  • Writing the BCB command 'uboot-fastboot' before booting UniXNU does bring the next start into U-Boot's own fastboot on the phone, so entering it is solved while flashing from it is not; the PMIC spare ABL actually reads (PON_SOFT_RB_SPARE, 0x88f = 0x04 in U-Boot's PID/offset encoding) still needs CONFIG_CMD_PMIC and a verified pmic device name.
  • U-Boot's fastboot reports slot a, so the host appends _a to every partition name it flashes; fastboot_partition_alias_<name>_a=<name> in the environment maps both spellings onto the real GPT names.

完成条件 ​

  • uboot/build.sh reproduces boot-unixnu.img from a fresh clone; with the marker in the payload U-Boot starts UniXNU.efi, and without it the phone boots Linux.
  • The payload volume holds UniXNU.efi, DarwinBoot.efi, kernel.macho, kernel.macho.trustcache and the marker.

当前范围 ​

  • The EDK II package is a copy, not a submodule: keeping it in step with the framework is a manual diff of package/.
  • The U-Boot submodule is shallow and pinned by commit; rebasing onto a newer sirius/v2.0 is a deliberate update.

下一步 ​

  • Test the f_fastboot dequeue removal end to end: after a power cycle, flash boot from U-Boot's own fastboot and, if that works, arm the BCB before booting UniXNU so a hung run comes back to a usable fastboot without the chord.
  • Build UniXNU.efi from efi/ once and compare it with the framework's output, so the vendored copy is known good rather than assumed.

证据 ​

Released under the BSD-2-Clause-Patent License.