Skip to content

nubia RedMagic 9 Pro / 9 Pro+ / 9S Pro / 9S Pro+ (NX769J) ​

Asultop/UniXNU-NX769J

nubia RedMagic 9 Pro / 9 Pro+ / 9S Pro / 9S Pro+ (NX769J) on Qualcomm SM8650, targeting iOS 16.5 (build 20F66, Darwin 22.5.0, XNU 8796) with an iPhone X identity. The board shares the SM8550 SoC register map and owns its CPU topology, UFS root policy, Goodix touch input and Adreno 750 GPU work. This record states the implementation coverage of each area and the evidence each milestone requires.

源记录: porting.toml, 2a265afdbd95d45e4a90664157e793c3bfee3755.

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

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

配置 ​

host-verified

组件: ArmDTPlatform, SoftwareAES, XnuPrelink.

硬件与依赖 ​

  • SM8650 register map and CPU topology from mainline sm8650.dtsi.
  • iOS 16.5 build 20F66 OS profile with XNU 8796.101.5.
  • UniXNU-sm8550-common revision 23b2049fcd9e30356161def84231334fb55ecca9 as the SoC source.
  • Board identity from the NX769J Android 14 vendor firmware and board configuration.

配置 ​

  • board.name = nx769j; board.soc = SM8650; board.soc_source = sm8550; board.os_profile = ios-16.5-20F66; schema_version = 2.
  • components.include lists the complete selection; arrays replace the inherited ARM64, SoC and OS arrays.
  • kernel.defconfig = nx769j_defconfig; config_fragments apply arch, soc, os in order and the device defconfig last.
  • device_tree.source = device:dts/nx769j.dts with include_dirs = [soc:dts].
  • Board submodules are not used; every driver comes from the component catalog at its pinned revision.

操作步骤 ​

  • Read https://unixnu.com/porting/device-repository and https://unixnu.com/porting/records.
  • Run ./unixnu defconfig nx769j_defconfig -D ../UniXNU-NX769J from the UniXNU checkout.
  • Run ./unixnu fetch -D ../UniXNU-NX769J and commit the resulting unixnu.lock.
  • Run ./unixnu status --json -D ../UniXNU-NX769J and review the resolved profiles, components, source pins and patch order, then ./unixnu tree -D ../UniXNU-NX769J.

开发经验 ​

  • Tables merge in ARM64, SoC, OS, device order and arrays replace inherited arrays, so the device must list every required component and fragment.
  • board.toml sets bootimg.image.header_version to 0 rather than the 4 the NX769J board configuration records. unixnu bootimg verify() reads the header at the v0/v1/v2 offsets and would reject a v4 image, and the tool defaults use 0. The reasoning and the open question about what the NX769J ABL accepts are recorded in the packaging area.
  • soc:configs/sm8550.config sets CONFIG_XNU_NATIVE_ADRENO=y. The device defconfig applies last and sets CONFIG_XNU_NATIVE_ADRENO=n because the SM8550 GPU stack targets Adreno 740 while this board has Adreno 750.
  • There is no UniXNU-sm8650-common. sm8550-common is usable as the SoC source because the register map of every resource this port drives is identical on both parts: GICD 0x17100000/0x10000, GICR 0x17180000/0x200000, UFS host 0x01d84000/0x3000, UFS PHY 0x01d80000/0x2000, GPU 0x03d00000/0x40000, GMU 0x03d6a000/0x35000 and GPU SMMU 0x03da0000/0x40000. The CPU core names and the Adreno generation do differ and are overridden in the board DTS and defconfig. The device confirms the UFS ranges in /proc/iomem and the MPIDR layout in its CPU nodes.
  • board.os_profile selects a directory under UniXNU/profiles/os/, and this port targets ios-16.5-20F66, which does not exist in UniXNU yet. The profile is kept in this repository at os-profiles/ios-16.5-20F66/ and the Build workflow installs it into the UniXNU checkout before resolving. The [ipsw] and [kernel.prelink] tables are inherited from that profile rather than repeated here, so the REQUIRED_IPSW fields are filled in one file.
  • board.toml selects only device:patches/xnu/series. The shared arch, soc and os series still target xnu-10063.141.1 while this port runs xnu-8796, so referencing them would either abort unixnu status with 'patch missing' or fail during application. The rebased copies are carried here and the four-series form goes back once the shared series are rebased.
  • unixnu status resolves the complete ordered patch series, so it needs the source of every component named by a component: entry. The 17.7.11 OS series names XnuPrelink twice and status aborts with 'patch missing' when that source is absent, while defconfig alone succeeds because it only reads the configuration fragments. The resolve build test therefore runs fetch and tree before status, which is the step 4 order in the porting guide.
  • unixnu fetch records component sources in unixnu.lock as well as feed sources when the device repository declares no git submodules. UniXNU-sheng pins its board drivers as submodules and its lock carries feed sources only; this repository carries both, and every component revision in the lock matches its pin in the component catalog.
  • unixnu.lock is machine written: fetch rewrites it with a single header comment and drops any comment added by hand. Keep explanations out of the lock and record them here, so the file stays byte-identical to what fetch produces. The profile's explicit xnu_revision takes priority over the lock, so the xnu entry follows the OS profile.
  • The default install path writes the OS windows into userdata, which is Android's /data and leaves the system images intact but the stock install unusable. The dual-system partition tool instead carves a unixnu partition from the end of userdata, preserving userdata's start, type, GUID and attributes and leaving metadata, super and boot untouched. README.md records the four-step order that ends in adb reboot system, which is the check that the stock system still boots.
  • The dual-system partition tests are documented for Linux, WSL and macOS. On a native Windows host the four RecoveryTests fail because they drive twrp.sh through a POSIX sh and a colon PATH, so they run in the partition-safety build job on ubuntu-latest instead. The seven layout tests that cover GPT entry and GUID preservation, reserved GPT space and F2FS section rounding pass on either host.
  • The board hardware values that no public source provides were read from a physical device over read-only adb and recorded in device-inputs.md: the userdata GPT extents and block size, the splash reservation, the DRAM base and the panel geometry. The handset reports a spoofed Xiaomi identity through its userspace properties while its boot image, kernel and device tree identify NX769J, so the values came from the kernel, the device tree and the raw UFS partition table rather than from getprop.
  • The kernel build runs in CI on ubuntu-latest with the UniXNU Linux toolchain, upstream clang and an open source Mach-O toolchain rather than Xcode. The toolchain is expensive to build and is cached under /opt/unixnu-linux keyed on scripts/linux. The job also asserts that the patch order unixnu resolves matches patches/xnu/series exactly, so a reordered or dropped patch fails the build rather than silently changing what gets compiled.
  • The toolchain cache never persisted and every kernel run rebuilt clang from source, which is the whole 37 minutes. The kernel job is expected to fail while this port is being brought up and actions/cache's combined form only saves on success, so the entry was never written. save-always had no effect and was replaced with explicit restore and save steps, the save running on always(). Restoring the cache then skipped setup-toolchain.py and with it the apt packages the toolchain links against, so --check failed on missing libclang and libbsd; the host dependencies are now installed in their own step regardless of the cache. The installer itself must also run on a cache hit: it places /usr/bin/xcrun and the CLT shims in system paths the cache cannot hold, and scripts/unixnu/kernel.py requires xcrun, so a cache hit skipped the step and the build stopped at 'xcrun missing'. The installer is idempotent and skips each component that already exists, so running it always costs nothing when the cache hits. Installing under the runner's home rather than /opt is separate hygiene: the installer runs as root and the path is expanded before sudo because sudo resets HOME to /root. scripts/unixnu/kernel.py resolves the toolchain through UNIXNU_TOOLCHAIN, so the same variable drives both the install and the build.
  • The kernel step writes its compile errors to logs/kernel-bootstrap.log rather than stdout. Parallel make then interleaves that file so heavily that the diagnostic for the failing compile is garbled past reading, and a failing run was not diagnosable at all. The job uploads out/**/logs as an artifact and repeats the build with -j 1 on failure, so the retry names the actual error. The retry is incremental because the objects from the parallel pass are reused.
  • A change under .github/**, unixnu.lock or os-profiles/** is owned here or in the packaging area, so such a change has to land with a matching finding or evidence entry in the owning area to satisfy the record checker's changed-area rule.
  • payload.adt.root_match_volume_uuid is now filled, so [payload.adt] has no REQUIRED_ placeholder left. It is the APFS volume UUID of the OS window, 97A3EB1A-5218-4936-BC85-9574EC2C8CC9, and not the container UUID c155fa03-68f4-42b0-8117-1915b6aa8e4c that sits in the same superblock: /chosen/root-matching matches IOMedia, and the container UUID belongs to the synthesized whole-disk IOMedia rather than to anything that mounts. scripts/extract-root-volume-uuid.py reads the value out of the userdata image the build writes, so it needs no device and no macOS.
  • The Data seed workflow puts its diagnostics in the job summary rather than only in the log: the runner's free disk before the download, the disk again once the restore image is deleted, and the seed report. Job logs need repository access to read, so a summary plus an ERR trap in every run step is what makes a failed run diagnosable from the run page: the trap prints ::error title=<step>::<command> and the annotations block is public. The 6 GiB restore image is not cached either -- a cache entry that size doubles the disk peak of a step that is already holding the archive and its 4.7 GiB OS member, and the archive is deleted as soon as the member is out. The Data seed workflow ran twice and failed twice before it was right: cache keys reject commas and the product type iPhone10,3 was in the first key, and the seed script then made a 32 MiB container and grew it, which is both an extra round of attach and resize and a size the iOS 16.5 var tree may not fit into. It now creates the container at its target size and refuses up front when the tree would not fit. [payload] tools = true adds a Tools entry to the generated boot menu, which is where the firmware log exporter publishes its QR pages. The generated menu replaces the built-in one and with it its Restart and Power off, so payload.py adds those back; package/unixnu.toml is a hand-written menu and is not the one the payload carries. .github/workflows/data-seed.yml is the macOS runner job that builds the Data seed, and it reads its restore-image pins out of os-profiles/ios-16.5-20F66/profile.toml so the workflow and the profile cannot drift apart.

完成条件 ​

  • All source pins resolve; every selected component has a record; status shows the intended overrides and patch order.

当前范围 ​

  • local.toml can select a different source from the published pin; record the effective source in each verification result.
  • The SoC source is the SM8550 repository. A dedicated UniXNU-sm8650-common would carry the SM8650 CPU topology and Adreno 750 platform operations instead of board-level overrides.

下一步 ​

  • Compare the effective status output whenever a profile, source or component selection changes.
  • Extract the SM8650 CPU topology and platform operations into UniXNU-sm8650-common once the board needs GPU power votes.

证据 ​

固件与启动 ​

host-verified

组件: DarwinBoot.

硬件与依赖 ​

  • uefi-nx769j Mu-Silicium platform package for the Pineapple QRD base board.
  • Stock firmware DTB carried in the device vendor_boot image.
  • UEFI GOP handoff, PSCI SMC interface and the vendor firmware inventory from proprietary-files.txt.
  • Android 14 ABL: boot header version 4, 4096-byte pages, kernel base 0x0.

配置 ​

  • firmware.config = nx769j; firmware.dsc = Platforms/ZTE/nx769jPkg/nx769j.dsc; firmware.fd = Build/nx769jPkg/{target}_CLANGPDB/FV/SILICIUM_UEFI.fd. The vendor directory is ZTE, not Nubia: nubia is ZTE brand and the verified sibling package is Platforms/ZTE/nx729jPkg.
  • firmware.preflight = [scripts/check-nx769j-firmware.py], which checks the Mu-Silicium layout, that Resources/Configs/nx769j.toml agrees with board.toml, that the DSC includes the SM8650 SoC package and that the vendor DXE drivers are present.
  • firmware.submodules = [Common/Mu, Common/Mu_OEM_Sample, Silicium-ACPI, Silicon/Silicium/OpensslPkg/Library/OpensslLib/openssl]; Mu_Basecore is the separate {edk2} checkout, not a submodule.
  • firmware.patches = [Resources/MuPatches/Gic-Spurious.patch], applied to the shared edk2 tree for the build and reverted afterwards.
  • firmware.packages = [{uefi}, {uefi}/Platforms/ZTE, {tree}/package, {tree}/drivers/DarwinBoot, {edk2}, {uefi}/Common/Mu, {uefi}/Common/Mu_OEM_Sample, {uefi}/Silicon/Qualcomm, {uefi}/Silicon/Silicium, {uefi}/Silicium-ACPI, {uefi}/Silicium-ACPI/Platforms/ZTE, {uefi}/Silicium-ACPI/Silicon/Qualcomm].
  • firmware.defines.DEVICE_MODEL = nx769j.
  • The platform DSC includes LanaiPkg/LanaiPkg.dsc.inc with SOC_TYPE = 2. LanaiPkg is Mu-Silicium SM8650 support and SOC_TYPE 2 is SM8650-AB, Snapdragon 8 Gen 3 at 3.30 GHz with 8 cores.
  • [bootimg.image] base = 0x0, pagesize = 4096, header_version = 0. The ABL is a version 4 boot chain, and Resources/Configs/nx769j.toml records 4, but bootimg.py merges DEFAULTS, then the firmware config and then board.toml, so board.toml decides what is packed: bootimg.py verify() only reads v0/v1/v2 headers and a v4 image fails its round-trip check. Extending verify() for v4 is the fix if the ABL rejects the v0 image.

操作步骤 ​

  • Read https://unixnu.com/porting/uefi-firmware and create the uefi-nx769j platform package.
  • Include UniXNU, UniXNUPkg, DarwinBoot and, for a Mu-Silicium initrd, UniXNURamDiskDxe, DiskIoDxe and FAT support in the DSC and FDF.
  • Include the firmware log exporter from https://unixnu.com/porting/log-capture\#firmware-setup so logs can be scanned from the menu during bring-up.
  • Run ./unixnu build firmware -D ../UniXNU-NX769J --uefi ../uefi-nx769j and record the firmware commit, output FD and handoff memory map.
  • Run ./unixnu bootimg with the firmware DTB and inspect the payload report.

开发经验 ​

  • Mu-Silicium names SoC packages by Qualcomm platform codename, not by part number. NX769J is Lanai: the device firmware reports baseband 5.0.c3-00030-LANAI_GEN_PACK and trustzone LANAIAAAAANAZT, and LanaiPkg.dsc.inc declares SM8650-AA through SM8650-AC together with the GIC bases 0x17100000 and 0x17180000 that this board measures. The KailuaPkg that nx729jPkg includes is SM8550 and is wrong for this board.
  • UniXNU/uefi-sheng branch sheng already carries Platforms/ZTE/nx729jPkg, a complete platform package for a nubia RedMagic one SoC generation back whose boot.img reaches fastboot boot OKAY on hardware. Platforms/ZTE/nx769jPkg is the same derivation from the Pineapple QRD base board that nx729jPkg is, so the vendor directory is ZTE rather than Nubia.
  • nx729j.toml sets boot_image.header_version = 4 where sheng and qrd8550 use 3 and 0, which confirms the NX769J ABL setting independently of the board configuration.
  • The UniXNU boot chain modules are not a firmware-side gap: ./unixnu build package builds UniXNU.efi, UniXNUDxe.efi and UniXNURamDiskDxe.efi on Linux with CLANGPDB, which is what proves the DSC integration points this design specifies resolve. What the firmware step still needs is the vendor binary set.
  • The firmware DTB is a separate input to boot-image assembly; XNU consumes the ADT generated from the board DTS.
  • NX769J boots through UEFI: the board configuration sets TARGET_USES_UEFI := true and records BOARD_BOOTIMG_HEADER_VERSION := 4. What the ABL does with the version 0 image unixnu packs is unverified; the sibling RedMagic port packs the same v0 image and fastboot-boots it on hardware, which is the evidence that this is the right shape.
  • The stock bootloader keeps a contiguous-splash region. It is measured at 0xe3940000 with size 0x2b00000 and label cont_splash_region, and board.toml re-adds it to the boot image's device tree through a [[bootimg.dtb_node]] entry, the way sheng declares /reserved-memory/splash_region.
  • The device tree model stays the Qualcomm reference name on this board family. This unit reports 'Qualcomm Technologies, Inc. Pineapple MTP' with compatible qcom,pineapple-mtp and qcom,pineapple, and the NX769J stock DTB overlay in the public dump targets qcom,pineapple-qrd. The board DTS therefore names nubia,nx769j with the qcom,pineapple fallbacks.
  • The firmware step now runs end to end. All 52 vendor modules are extracted from uefi.img and laid out with their INF wrappers under Binaries/nx769j, the firmware DTB is extracted from vendor_boot tree 2, and ./unixnu build firmware --uefi ../uefi-nx769j produces Build/nx769jPkg/DEBUG_CLANGPDB/FV/SILICIUM_UEFI.fd at exactly 3145728 bytes, which is [uefi_fd] size and what firmware.py checks. Mu_Basecore has to be initialised as a submodule before that: it is the {edk2} tree the build runs against and it is not in the four submodules firmware.submodules lists. boot.json records the same fd_sha256 that the build produced, so the firmware packed into boot.img is the firmware that was built.
  • firmware/ changes do not reach the build until they are lifted into the Mu-Silicium checkout: firmware.py drives EDK II against the checkout's copies and never copies from the device repository, so cp -R firmware/. ../uefi-nx769j/ is a required step and a missed one builds stale sources without any warning. The preflight cannot catch it either, because vendor_drivers() reads the platform Include/ from the device repository when the checkout's is absent. Adding the log exporter and rebuilding without lifting produced a byte-identical-looking FD that simply did not contain the module.
  • The firmware log exporter is integrated and is in the firmware volume: AdvancedLogDxe builds as its own FFS, GenFfs names it 8D4E7F50-3A2C-4C9A-9E1B-50C4C8472D41 and FV/FVMAIN.inf lists it, so it rides in the compressed FvMain. board.toml [payload] tools = true adds the Tools entry to the generated menu, which is where it publishes the log. What is not done is step 2 of the log-capture procedure: nothing routes this platform's debug output into the Advanced Logger, so the buffer the exporter reads is whatever LanaiPkg's DebugLib happens to write.

完成条件 ​

  • Firmware image builds, enters UniXNU, publishes the framebuffer and preserves the documented memory reservations.

当前范围 ​

  • Library/MemoryMapLib.c is rebuilt from this board's firmware device tree rather than carried over. Every one of the 36 fixed-address reservations has a descriptor named after its node, and the check over the table reports no overlaps and no uncovered reservation. UEFI's own regions -- FD Reserved, UEFI Stack, SEC Heap, Sched Heap, FV Region, UEFI RESV, Kernel, DXE Heap -- are kept where nx729jPkg puts them, which fits this board's free band 0xa1f80000..0xd7e00000 exactly. Display Reserved moved to the measured splash at 0xE3940000, and DXE Heap now starts at 0xB8000000 because the region nx729j gave to its splash is ordinary RAM here and the device tree reserves nothing in it.
  • The vendor UEFI DXE drivers are laid out and the firmware builds. scripts/extract-uefi-drivers.py recovers all 52 that the platform includes name, from uefi.img, where they sit in gzip-compressed nested firmware volumes under an outer volume holding only SEC and a platform configuration. Twelve carry a UI name different from the INF name the DSC uses -- I2CDxe's module is called plain I2C -- and the script maps them rather than guessing by prefix. The expected set is read from APRIORI.inc, DXE.inc and RAW.inc rather than kept in step with them: APRIORI.inc names one module that DXE.inc has commented out. They cannot be redistributed, so every build machine runs the extraction.
  • The Silicium-ACPI SoC tables are Kailua's, which is SM8550. Silicium-ACPI has no Lanai set, and APIC, GTDT and PPTT describe Kailua's cores where this board has two A520, five A720 and one X4. XNU reads the Apple device tree rather than ACPI, so this does not affect the XNU boot, but the tables are wrong for an ACPI operating system.
  • SOC_TYPE is set to 2 from the board name and the 8-CPU topology. The device does not record the prime core maximum frequency, so 2 against 4 (3.30 against 3.40 GHz) is not measured.

下一步 ​

  • Build a Lanai ACPI table set, or drop the tables from the DSC if XNU is the only target. Silicium-ACPI has none and the Kailua tables describe a different CPU topology.
  • Read the prime core's maximum frequency from the device and settle SOC_TYPE 2 against 4, which is 3.30 against 3.40 GHz.
  • Then reach the UniXNU menu with a published 1116x2480x32 framebuffer, which is the first host-side observation that the firmware runs.

证据 ​

CPU 与中断 ​

implemented

组件: ArmCpu.

硬件与依赖 ​

  • SM8650 CPU topology from mainline sm8650.dtsi: cpu0-1 arm,cortex-a520, cpu2-6 arm,cortex-a720, cpu7 arm,cortex-x4.
  • MPIDR affinities 0x0, 0x100, 0x200, 0x300, 0x400, 0x500, 0x600, 0x700.
  • GICv3 distributor 0x17100000/0x10000, redistributors 0x17180000/0x200000 with #redistributor-regions = 1 and redistributor-stride 0x40000, ITS at 0x17140000/0x40000.
  • 19.2 MHz timer. PSCI uses SMC. GIC PPI partitions group cpu0-1, cpu2-6 and cpu7.

配置 ​

  • dts/nx769j.dts includes sm8550.dtsi and replaces each cpuN compatible with the SM8650 core name, keeping the DTSI MPIDR values.
  • unixnu,cluster-type = E on cpu0-1 and P on cpu2-7 feeds the ADT scheduler grouping.
  • CONFIG_XNU_ARMCPU=y and CONFIG_XNU_NATIVE_ARCH_TIMER=y from the ARM64 common fragment.

操作步骤 ​

  • Compile the board DTS and inspect enabled CPUs, GIC ranges and timer properties in the generated ADT.
  • Record per-CPU entry, timer interrupts and inter-processor interrupt delivery at the checkpoints in https://unixnu.com/porting/bring-up.

开发经验 ​

  • The SM8550 DTSI declares three A510 cores followed by A715, A710 and X3 cores. SM8650 instead has two A520 cores first and a single X4 prime core last, so the compatible strings must be replaced per node while the MPIDR layout is unchanged at 0x100 spacing.
  • The GIC PPI partitions in sm8650.dtsi confirm the grouping independently of the core names: partition 0 is cpu0-1, partition 1 is cpu2-6 and partition 2 is cpu7.
  • CPU affinity values are hardware identifiers; enabled CPU count and MPIDR affinities must be checked together.
  • The device confirms the MPIDR affinities 0x0, 0x100, 0x200, 0x300, 0x400, 0x500, 0x600, 0x700 across its eight cpu@N nodes. Its vendor kernel names the cores qcom,kryo rather than the mainline ARM core names; the board DTS keeps the mainline names that sm8650.dtsi declares and that the UniXNU CPU driver matches on.

完成条件 ​

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

当前范围 ​

  • CPU frequency and idle policy are board inputs and are not implemented; no CpuFreq board service exists yet.
  • Per-CPU entry and interrupt delivery are not yet observed on this board.

下一步 ​

  • Record per-CPU entry and interrupt observations once the firmware reaches the kernel handoff.

证据 ​

内存与 DMA ​

implemented

硬件与依赖 ​

  • ARM64 Qualcomm DRAM base 0x80000000, confirmed on the device by gunyah_hyp_region@80000000 and the lowest System RAM range.
  • Handoff window 0x80000000 bytes at 0x80000000, matching payload.dram_size.
  • Measured contiguous-splash reservation at 0xe3940000 with size 0x2b00000, label cont_splash_region.
  • Apps SMMU at 0x15000000/0x100000 and GPU SMMU at 0x03da0000/0x40000, both qcom,sm8650-smmu-500.
  • UFS host and PHY register ranges shared with SM8550.

配置 ​

  • device_tree.source = device:dts/nx769j.dts; include_dirs = [soc:dts].
  • memory@80000000 declares one contiguous DRAM range; payload.adt.owned_memory_size and payload.dram_size are both 0x80000000.
  • reserved-memory declares splash_region at 0xe3940000 with size 0x2b00000 and no-map, and board.toml re-adds the same node to the boot image tree.

操作步骤 ​

  • Compare firmware RAM ranges, reserved regions, DTS ranges and ADT memory-map entries.
  • Check DMA buffers against controller address width, cache policy and SMMU mapping before submitting work.
  • Record the measured reservations in device-inputs.md with the command that produced each value.

开发经验 ​

  • The memory node describes the contiguous DRAM range handed to XNU, not the installed RAM. This unit reports MemTotal 15705424 kB, about 15.0 GiB usable from a 16 GiB SKU, with System RAM ranges above 4 GiB at 0x880000000, 0x8b0000000, 0x8c0000000 and 0xa00000000. UniXNU takes one contiguous window from that map.
  • The handoff window and payload.dram_size are still 2 GiB at 0x80000000, following sheng and caihong. UniXNU-nx729j, a RedMagic 8 Pro+ port built on the same code base, instead passes the full 16 GiB computed from the device ddr-regions 0x80000000/2G plus 0x880000000/6G plus 0xa00000000/8G, and this unit's measured System RAM shows the same three-region shape. The two parameters are independent, so they need deciding together against the firmware memory map before the first kernel boot rather than assumed from either port.
  • The splash reservation is the one board value that only the device can give: node name splash_region, label cont_splash_region, reg 0xe3940000 with size 0x2b00000. The node name and label match UniXNU-sheng and the size also matches sheng and shennong, so only the address is board specific.
  • Physical register translation and CPU virtual mappings have different purposes. Preserve physical resource ranges in hardware descriptions.
  • The Apps SMMU and GPU SMMU register ranges are identical between SM8550 and SM8650, so the shared SMMU kit is applicable to this board.
  • An exported register below 16 MiB is an error: the common generator reads the arm-io window base as the SoC base and requires it to be non-zero.
  • The kernel memory node is split into System RAM ranges with reservations removed, not the raw DRAM extent. Do not copy it into the board DTS; the board declares the handoff window.

完成条件 ​

  • All handoff regions are accounted for and each enabled DMA engine accesses its allocated range with the documented cache policy.

当前范围 ​

  • The removed regions listed by the firmware are not all enumerated here; only the splash reservation is consumed by the current configuration.
  • The handoff window is a build policy value. Confirm it against the firmware memory map before a kernel boot.

下一步 ​

  • Compare the firmware memory map against the declared handoff window and the splash reservation at the first kernel boot.
  • Record each remaining reserved region that a driver starts consuming.

证据 ​

存储与根卷 ​

implemented

组件: ShengUfs, VirtioMmioBlock, VerifiedRoot.

硬件与依赖 ​

  • UFS host 0x01d84000/0x3000 (qcom,sm8650-ufshc, jedec,ufs-2.0, SPI 265) and UFS PHY 0x01d80000/0x2000 (qcom,sm8650-qmp-ufs-phy).
  • GCC AXI, AHB, UniPro, symbol and PHY auxiliary branches; power domains and firmware link state.
  • Apps SMMU at 0x15000000, UFS stream assignment, context-bank ownership, DMA width and cache maintenance.
  • NX769J is an A/B device with dynamic partitions; userdata is a f2fs volume at /dev/block/bootdevice/by-name/userdata and the super partition is 9126805504 bytes.

配置 ​

  • Select ShengUfs, VirtioMmioBlock and VerifiedRoot; CONFIG_XNU_NATIVE_UFS=y and CONFIG_XNU_NATIVE_VIRTIO_BLOCK=y.
  • dts/nx769j.dts sets &ufs and &ufsphy status = okay.
  • board.toml [userdata] carries the measured extents: first_lba 3302184, last_lba 124002298, lba_bytes 4096, with OS and SystemOS windows.

操作步骤 ​

  • Read https://unixnu.com/porting/ufs with this storage record.
  • Inspect clock enables, power state, HCE/HCS, link state, request-list base and SMMU mappings before changing controller ownership.
  • Validate SCSI capacity and GPT reads, then publish the OS and SystemOS windows within the userdata range.
  • Verify persistent Data writes on the documented Data allocation with a recovery image available.

开发经验 ​

  • UFS host and PHY addresses are identical between SM8550 and SM8650, and the compatible differs only in the SoC name (qcom,sm8550-ufshc versus qcom,sm8650-ufshc). The shared ShengUfs controller access applies to this board; the board owns its userdata policy. The device confirms both ranges in /proc/iomem.
  • An idle AHB branch may report CLK_OFF with hardware gating enabled. Check enable and gating bits together.
  • SMMU bypass still has address-width constraints. Validate every DMA range and context-bank assignment.
  • recovery.fstab mounts /data as f2fs on userdata, so the persistent Data path must handle f2fs rather than the ext4 layout assumed by some board configurations.
  • The measured GPT has userdata as the last occupied partition ending exactly on the last usable sector, with 32 table entries of which 12 are used. Those are the preconditions the dual-system partition tool requires, so a unixnu partition can be carved without altering any other entry.
  • The device publishes five regular UFS LUNs plus the RPMB and boot well-known LUNs (49456, 49476, 49488).
  • The public NX769J board configurations record BOARD_SUPER_PARTITION_SIZE := 9126805504 with a TODO saying the value is hardcoded. The measured super partition is 12.0 GiB; that guess is not the real size.

完成条件 ​

  • Host: register, clock, link and SMMU facts match the board inventory.
  • Reads: SCSI capacity, GPT and known blocks are correct; OS/SystemOS windows stay within userdata.
  • Root: the configured volume mounts and launchd starts. Persistence: writes survive reboot within the allocated Data region.

当前范围 ​

  • payload.adt.root_match_volume_uuid is read out of the built userdata image rather than measured on a device, so it is correct for the image that gets flashed and does not follow a repartitioned or differently imaged device.
  • ShengUfs contains sheng Data and log policy that is board specific and is not reused here.
  • The GPT extents are measured on one unit. Re-read them before writing, since a different SKU or a repartitioned device has different bounds.

下一步 ​

  • Confirm the SCSI capacity reported by the controller matches the measured GPT before the first write.
  • Produce the owner-signed verified-root policy bundle over the root windows of the OS image that gets flashed and replace the generated empty table in scripts/build-verified-root.py with it, so root verification is enabled rather than disabled.

证据 ​

显示 ​

implemented

组件: GopFramebuffer.

硬件与依赖 ​

  • UEFI GOP framebuffer published by uefi-nx769j, its stride, pixel format and handoff.
  • NX769J panel: 1116x2480 at 480 dpi (TARGET_SCREEN_DENSITY := 480).
  • MDSS and DPU at qcom,sm8650-mdss / qcom,sm8650-dpu with two qcom,sm8650-dsi-ctrl and qcom,sm8650-dsi-phy-4nm instances.
  • Backlight controls through the board panel driver.

配置 ​

  • GopFramebuffer is selected, but CONFIG_XNU_NATIVE_LEGACY_DISPLAY and CONFIG_XNU_NATIVE_MOBILE_DISPLAY are off: they reference native_legacy_display_probe and native_mobile_display_provider_probe from bsd_init and IOUserClient, and the late archive that defines them has no builder in this tree. The kernel links headless until the archive exists.
  • Display presentation comes from the firmware framebuffer, so the board does not configure a scanout pipe yet.

操作步骤 ​

  • Verify GOP address, dimensions, stride and pixel layout before enabling presentation.
  • Build the late display archive with the selected OS ABI records, then check display publication and frame presentation.
  • Record frame timing and pixel format at the checkpoints in https://unixnu.com/porting/bring-up.

开发经验 ​

  • The display source and ABI binding are selected independently of the physical panel configuration; the GOP path presents whatever geometry the firmware publishes.
  • The panel geometry is measured on the device: card0-DSI-1 reports 1116x2480x60cmd. That matches the value recorded from the public NX769J board configurations, which also give 480 dpi.
  • SM8650 carries two DSI controllers and a DSI PHY per controller; a native scanout path would bind to those rather than to the SM8550 display nodes.

完成条件 ​

  • A known frame appears with correct geometry and colors, then userland presentation and backlight controls work.

当前范围 ​

  • No presentation result exists yet; the firmware must publish a GOP framebuffer before the display area can be verified.
  • Panel timing, backlight and DSI bring-up are unimplemented. GopFramebuffer presents through the firmware and does not drive the panel.

下一步 ​

  • Capture the GOP geometry from the firmware and record a known frame with correct geometry and colors.

证据 ​

输入 ​

planned

硬件与依赖 ​

  • NX769J touch controller is a Goodix 9916R. The device firmware ships goodix_firmware_9916r.bin and the running kernel registers a goodix_ts input device at goodix_ts/input0.
  • The catalog input stack is ShengNovatekTouch, written for the Novatek NT36532E on sheng, with ShengHidInput publishing IOHIDDevice reports.
  • Buttons register as gpio-keys_nubia with pmic_pwrkey and pmic_resin. Awinic SAR sensing registers as nubia_tgk_aw_sar0_ch0 and nubia_tgk_aw_sar1_ch0, and the fingerprint sensor is goodix_fp.

配置 ​

  • No input component is selected. board.toml components.include deliberately omits ShengHidInput, ShengNovatekTouch and QcomGeniSpi.
  • Touch firmware paths and hashes are recorded in proprietary-files.txt for the future driver.

操作步骤 ​

  • Read https://unixnu.com/porting/input and identify the touch bus, sensor protocol, pins, interrupts and firmware interface.
  • Implement a Goodix 9916R driver against the catalog input interface, or adapt ShengNovatekTouch to the Goodix protocol.
  • Map contacts to the display, confirm stable contact IDs across simultaneous touches and confirm release events.

开发经验 ​

  • The Novatek driver's sensor protocol does not match the Goodix 9916R on this board, so reusing it would require replacing the transport and the report parser rather than a configuration change.
  • ShengHidInput publishes IOHIDDevice reports from a board touch event source, so the HID layer is reusable once a Goodix event source exists.
  • The device confirms the touch controller family: the kernel registers goodix_ts and goodix_fp, and the SAR sensors are Awinic parts behind nubia_tgk_aw_sar* drivers. That matches the goodix_firmware_9916r.bin and aw9620x_* firmware already recorded in proprietary-files.txt.

完成条件 ​

  • Touches map to the display, simultaneous contacts keep stable IDs, release events arrive, and buttons produce expected HID reports.

当前范围 ​

  • No input driver exists for this board. Contact IDs, coordinate mapping and the interrupt wiring are not established, and the device exposes them only through the running Android input stack.
  • Haptics and SAR sensing are out of scope for the first input milestone.

下一步 ​

  • Read the goodix_ts device node from the stock device tree to obtain the bus, address and interrupt, then implement a Goodix 9916R event source behind the catalog HID interface.

证据 ​

GPU ​

planned

硬件与依赖 ​

  • Adreno 750, compatible qcom,adreno-43051401 at 0x03d00000/0x40000 with SPI 300.
  • GMU qcom,adreno-gmu-750.1 at 0x03d6a000/0x35000 with rscc and gmu_pdc ranges, SPI 304 and 305 for hfi and gmu.
  • GPU SMMU qcom,sm8650-smmu-500 at 0x03da0000/0x40000 with stream assignments 0 and 1 on the GPU and 5 on the GMU.
  • ZAP shader firmware and GMU firmware for the Adreno 750 generation.

配置 ​

  • No GPU component is selected. board.toml components.include omits Adreno740, AdrenoMetal, Sm8550Platform, AdrenoPM4, AdrenoFirmware and ArmSmmu4K.
  • CONFIG_XNU_NATIVE_ADRENO=n overrides the y set by soc:configs/sm8550.config.
  • dts/nx769j.dts leaves &gpu, &gmu and &gpu_smmu disabled.

操作步骤 ​

  • Read https://unixnu.com/porting/gpu and https://unixnu.com/porting/drivers.
  • Qualify the catalog GPU stack against Adreno 750, or write a driver for this generation, then select the components and enable the DTS nodes together.
  • Verify submissions, mapping and fence handling, render a known image and present it through the selected Metal client.

开发经验 ​

  • The catalog Adreno740 driver declares hardware Adreno 740 and SM8550 and is verified only on sheng. This board has Adreno 750, whose GMU reports qcom,adreno-gmu-750.1 where SM8550 reports qcom,adreno-gmu-740.1.
  • GPU, GMU and GPU SMMU register ranges are identical between SM8550 and SM8650, so the SMMU and platform kits have matching register inputs; the firmware protocol and the PM4 packet generation are the parts that need qualification for this generation.
  • sm8650.dtsi gives the GPU as qcom,adreno-43051401 while sm8550.dtsi gives qcom,adreno-43050a01; the chip ID is what distinguishes the two parts to the driver.

完成条件 ​

  • GPU submissions complete, mapping/fence handling is correct, a known image renders, and the selected Metal client presents it.

当前范围 ​

  • No GPU driver is qualified for Adreno 750 and no ZAP or GMU firmware is collected.
  • GPU power votes, DCVS and the display scanout hand-off are unimplemented.

下一步 ​

  • Collect the Adreno 750 ZAP and GMU firmware from the device firmware package and qualify the HFI startup sequence against qcom,adreno-gmu-750.1.

证据 ​

按下一步操作收集证据。

网络 ​

planned

硬件与依赖 ​

  • WCN7850 (FastConnect 7800) reached over PCIe, with ath12k firmware and board data.
  • The catalog Ath12kWifi transport declares hardware qcc2072, qcn9274 and wcn7850 build inputs and board dependencies on PCI configuration, BAR, MSI and DMA operations.
  • Ath12kWifi declares os_abi 'nw_abi platform interface', which is not the ipados-17.7.11-21H461 ABI.

配置 ​

  • No network component is selected. board.toml components.include omits Ath12kWifi.

操作步骤 ​

  • Read https://unixnu.com/porting/network and identify the bus, firmware, interrupts, DMA and OS interface.
  • Qualify the ath12k transport against this OS profile's ABI, then select the component.
  • Verify scanning, association, address configuration and sustained receive and transmit traffic.

开发经验 ​

  • Ath12kWifi is the only Wi-Fi transport in the catalog and its hardware list includes wcn7850, which is the chip on this board, but its declared os_abi does not match the selected OS profile. Selecting it would claim an ABI match that the catalog does not state.
  • PCI configuration, BAR, MSI and DMA operations are board dependencies that this board has not implemented.

完成条件 ​

  • Scanning, association, address configuration and sustained receive/transmit traffic pass with the selected firmware.

当前范围 ​

  • No network driver is selected and no ath12k firmware is collected.
  • PCIe enumeration and MSI delivery are unimplemented on this board.

下一步 ​

  • Establish the OS ABI that the ath12k transport requires, then either adapt it to ipados-17.7.11-21H461 or record the gap in the component catalog.

证据 ​

按下一步操作收集证据。

音频 ​

planned

硬件与依赖 ​

  • SM8650 LPASS: qcom,sm8650-adsp-pas, qcom,gpr, qcom,q6apm, qcom,q6prm with lpass rx, tx, wsa and va macros over soundwire v2.0.0.
  • ADSP firmware and its firmware services, loaded through the PAS loader.
  • Board audio routes and the codec configuration shipped in the NX769J vendor firmware.

配置 ​

  • No audio component is selected. board.toml components.include omits the ADSP and audio IPC drivers.

操作步骤 ​

开发经验 ​

  • The ADSP is reached through the PAS loader and the GPR transport, so audio depends on firmware services and IPC routing before any codec work.
  • The audio server keys its routing database and MediaExperience its per-board tables on the board name in the ADT, so the board name must be fixed before audio routes are qualified.

完成条件 ​

  • Each declared route carries the selected audio format through playback or capture and handles start/stop correctly.

当前范围 ​

  • ADSP firmware, IPC transport and codec routing are all unimplemented on this board.

下一步 ​

  • Record the ADSP firmware revision and the IPC transport entry points from the vendor firmware before implementing the transport.

证据 ​

按下一步操作收集证据。

电源 ​

planned

硬件与依赖 ​

  • PMIC regulators over SPMI with RPMh (qcom,rpmh-rsc, qcom,sm8650-rpmhpd, qcom,sm8650-rpmh-clk, qcom,bcm-voter).
  • qcom,aoss-qmp and rpmh-stats for low power state transitions.
  • NX769J battery and charging units, and the Awinic AW22xxx haptics driver.

配置 ​

  • No power component is selected. board.toml components.include omits the PMGR and power drivers.
  • CONFIG_XNU_SHENG_PMGR is not set: the sheng PMGR driver is board specific and SM8650 has no Apple PMGR block.

操作步骤 ​

  • Read the power section of https://unixnu.com/porting/drivers.
  • Record regulator ownership, clock parents and the firmware ownership boundary, then implement the power manager and battery units.
  • Verify declared power controls and battery readings against observations and exercise each claimed sleep and resume transition.

开发经验 ​

  • The SoC exposes RPMh and AOSS rather than an Apple PMGR block. The caihong SM8650 port reports that the AMFI early security init path panics with 'AMFI: No PMGR?' on this SoC family for that reason.
  • SM8550 and SM8650 both expose the PMGR patch surface that UniXNU-sm8550-common patches, which is why the shared SoC source is usable here as well.

完成条件 ​

  • Declared power controls and battery readings match observations; each claimed sleep/resume transition restores its dependent peripherals.

当前范围 ​

  • Regulator ownership, battery units and sleep transitions are unimplemented. No power data is sourced for this board.

下一步 ​

  • Record the PMIC regulator inventory and the firmware ownership boundary from the vendor device tree.

证据 ​

按下一步操作收集证据。

镜像组装 ​

host-verified

组件: XnuPrelink.

硬件与依赖 ​

  • XNU 8796.101.5 at revision aca3beaa3dfbd42498b42c5e5ce20a938e6554e5, the open-source drop closest to Darwin 22.5.0.
  • iOS 16.5 restore image for iPhone10,3, build 20F66. Several profile fields are read out of it and are still marked REQUIRED_IPSW.
  • Android 14 boot image format: header version 4, 4096-byte pages, kernel base 0x0, LZ4 ramdisk.

配置 ​

  • board.os_profile = ios-16.5-20F66; the profile is os-profiles/ios-16.5-20F66/profile.toml and must land in UniXNU/profiles/os/ before a build resolves.
  • kernel.prelink is inherited from the OS profile: tool kits/XnuPrelink/emit_prelink.py, slide -0x1000000, and kernelcache_sha256 and early_security_init are REQUIRED_IPSW.
  • kernel.config_fragments apply arch:configs/common.config, soc:configs/sm8550.config and os:configs/ios.config, then the device defconfig.
  • patch_series resolves arch, soc, os, device in order; the OS series is empty and the device series carries no board patch yet.
  • [bootimg.image] base = 0x0, pagesize = 4096, header_version = 4.
  • package/unixnu.toml defines the payload menu with a single xnu entry.

操作步骤 ​

  • Read https://unixnu.com/xnu/source-and-patches, https://unixnu.com/concepts/prelink and https://unixnu.com/concepts/payload.
  • Run python3 scripts/extract-ipsw-pins.py iPhone10,3,iPhone10,6_16.5_20F66_Restore.ipsw to reproduce every pin in the OS profile, including the ADT identity from the board device tree.
  • Fill payload.text_fixups from the OS image dyld cache once native-text-fixup.patch is rebased onto xnu-8796.
  • Rebase the arch, soc and XnuPrelink patch series onto xnu-8796 before referencing any of them from the OS series, then build kernel, package, firmware and payload in order.
  • After changing boot arguments, rebuild both the payload and the boot image as described in https://unixnu.com/xnu/boot-arguments\#apply-arguments.
  • Run python3 scripts/porting-record.py check --device . from the UniXNU checkout before publishing.

开发经验 ​

  • XNU 8796 is the iOS 16 kernel. The 8796 train has three open-source drops, 8796.101.5, 8796.121.2 and 8796.141.3; 8796.101.5 is the earliest and the closest to Darwin 22.5.0, so it is the baseline for iOS 16.5. The community iOS 16 ports, dipa-studio/ios-sweet and the ios-topaz family, build the same train.
  • osfmk/conf/files.arm64 lists osfmk/arm64/kpc.c as optional kpc nos_arm_asm, but the file is not in the xnu-8796 source drop; only kern/kpc_common.c, kern/kpc_thread.c and x86_64/kpc_x86.c are published. Removing the entry is not enough: cpu.c calls kpc_idle and kpc_idle_exit and kpc_common.c calls the arch hooks, so the link then fails on eight _kpc_* symbols. xnu-8796-arm64-kpc-backend.patch provides the backend instead, reporting no counters and nothing running, which is what kpc_common.c asks before it reads anything.
  • The iig interface generator is newer than this XNU. It emits rpc.kernelContent, reads it back as IORPCMessage * and initialises it with the kernel-resident message, while IORPC in xnu-8796 has only message, reply, sendSize and replySize. Adding IORPCMessage * kernelContent to the struct satisfies the generator; the 8796 dispatch path reads message only and ignores the field.
  • The first kernel build reached the XNU compile and stopped on a single cause repeated four times: clang rejects -enable-trivial-auto-var-init-zero-knowing-it-will-be-removed-from-clang, which makedefs/MakeInc.def adds beside -ftrivial-auto-var-init=zero. That companion flag was removed from clang. linux-clang-auto-var-init.patch drops it and keeps -ftrivial-auto-var-init=zero, which is the supported way to get the same zero-initialisation. The patch is last in the series so it cannot collide with the other MakeInc.def edits.
  • The second build stopped on uio_stackbuf_t, which bsd/sys/uio_internal.h declares as char aligned to union iovecs and then places in arrays sized by UIO_SIZEOF. Upstream clang rejects an array whose element size is not a multiple of its alignment; Apple's clang accepts the idiom. linux-clang-uio-stackbuf-align.patch moves the alignment to the array declarations and leaves the typedef as plain char, so sizeof stays 1 and UIO_SIZEOF still counts bytes. Verified on Ubuntu clang 18.1.3: the element is 1 byte, the buffer keeps its byte count, and the array is 8 byte aligned.
  • zzz-boot-progress.patch inserted its boot_progress_user_abort call between two lines of handle_kernel_abort's parameter list, which is a syntax error wherever that signature appears. Verifying a fuzzy application by comparing the added and removed lines caught nothing, because the change set was identical and only its placement was wrong. Comparing what changed is not enough; a re-anchored hunk needs its placement checked too. The call now goes at the top of the body, matching the other boot_progress hooks.
  • KALLOC_TYPE_IS_COMPATIBLE_PTR takes the pointee type with __typeof__(*(ptr)). In C++ that is ill formed for a void pointer, which the macro explicitly documents as a wildcard, so any C++ translation unit that frees a void pointer fails. __builtin_choose_expr does not help: clang still diagnoses the unselected branch. linux-clang-kalloc-pointee-type.patch derives the pointee type from the pointer type with a template in C++ and keeps the deref in C.
  • The kernelcache pin covers the decompressed arm64 slice, not the whole fat image. scripts/unixnu/ipsw.py takes the IMG4 payload, LZFSE-decodes it, then keeps the single 0x0100000c fat slice and hashes that; hashing the fat image gives a different digest and fails the board pin check. scripts/extract-ipsw-pins.py mirrors that pipeline exactly.
  • The prelink ABI is version bound and was read out of the 16.5 kernelcache rather than copied: AppleMobileFileIntegrity sits at 0xfffffff004e2b640 here against 0xfffffff005ab4ae0 in the 17.7.11 profile, and com.apple.iokit.IOHIDEventDriver, which the 17.7.11 roots list keeps, does not exist in this kernelcache at all.
  • The ADT identity comes from the real board device tree, Firmware/all_flash/DeviceTree.d22ap.im4p inside the restore image, not from libMobileGestalt and not from another profile. That tree gives product-id fd079b298144a7deb4d7c87fef3eb474d8f0e7eb and corrects three values that would otherwise be inherited from the iPadOS profile: artwork-device-subtype is 2436 rather than 2732, compatible-device-fallback is iPhone9,2 rather than iPhone10,6, and kern.maxvnodes is 10000 rather than 66560.
  • The Apple device tree length field carries a flag in its high bit; the real length is the low 31 bits. Reading it unmasked overflows the parser.
  • The rebase is complete and verified: all 59 patches apply strictly, in order, onto xnu-8796.101.5. 54 carried over unchanged, one needed the hunk re-anchored for a build-config file whose surrounding comments drifted, and two hunks were dropped because the include line they rewrite does not exist in this train.
  • Patch order matters and patches depend on earlier ones creating their target code. zone_id_require_aligned is introduced by offline-pgz-alignment-probe and FREEZE_ENABLED_DEFAULT by another patch in the series, so testing a patch in isolation against a clean tree gives the wrong answer. The series was regenerated by replaying it in order.
  • pgz-aligned-validation and published-freezer-condition are dropped entirely. Both patch code that no patch in this train creates and that xnu-8796 does not have: zone_id_require_aligned is only referenced inside the XNU_ZONE_PGZ_ALIGNMENT_PROBE block, which is off by default and has no definition in 8796, and the FREEZE_ENABLED_DEFAULT block does not exist here.
  • zzx-pmap-sync-64bit-pa patch halves split by target: osfmk/arm64/sptm/pmap/pmap.c does not exist in 8796, so that half is dropped, while its osfmk/arm/pmap/pmap.c half applies and is kept.
  • The two prelink patches, security-init and sparse-index, apply to xnu-8796 without modification. XnuPrelink only ships them under xnu-10063 file names, so the copies here are renamed to xnu-8796.101.5-* to name the train they run on.
  • board.toml selects only device:patches/xnu/series. The shared arch, soc and os series still target xnu-10063.141.1; referencing them would abort unixnu status with 'patch missing' or fail during application. Switch back to the four-series form once those are rebased.
  • native-text-fixup.patch is among the rebased set, so payload.text_fixups is the remaining input for that path rather than a separate track.
  • The boot image header version is 0, not the 4 that the NX769J board configuration records. BOARD_BOOTIMG_HEADER_VERSION is the Android build system setting for a stock boot.img; unixnu bootimg verify() unpacks ten u32 words from offset 8 and reads header_version and pagesize at the v0/v1/v2 offsets, so a v4 header fails the round-trip check. The tool defaults use 0. The sibling RedMagic port UniXNU-nx729j hit the same failure, fixed it the same way, and then fastboot-booted the image on hardware. If the NX769J ABL rejects v0 the fix is to extend verify() rather than to set the value here.
  • The caihong SM8650 port omits prelink early_security_init because running AMFI early security init on a SoC without an Apple PMGR panics with 'AMFI: No PMGR?'. NX769J is the same SoC family, so treat the hook as an open risk and record the observed behaviour rather than assuming it runs.
  • The image assembly now runs end to end. build userdata writes userdata-root.img 8357150720 bytes with the OS window from 096-04088-077.dmg at 0x100000 and the SystemOS window from 096-04666-078.dmg at 0x14fa00000; build payload writes payload.img 218103808 bytes carrying the OS trust cache; bootimg packs boot.img 220332032 bytes and round-trips it. The order matters: build userdata before build payload, because scripts/extract-root-volume-uuid.py reads the root volume UUID out of the userdata image. On Linux the image steps need the toolchain bin directory on PATH, since build userdata decodes LZFSE chunks with the lzfse tool that scripts/linux/setup-toolchain.py installs.

完成条件 ​

  • Kernel and prelink succeed, payload contains all declared inputs, and the boot image matches the bootloader format and size limit.

当前范围 ​

  • The kernel compiles and links on xnu-8796 with this series: the build reaches 'built kernel at build/xnu/ios-obj/DEVELOPMENT_ARM64_BCM2837/mach.development.bcm2837'. Nine of the ten archive builders now pass - corecrypto twice, ArmDTPlatform, ArmCpu, SoftwareAES, the SoftwareAES bindings, the libpthread kernel component, ShengUfs and VirtioMmioBlock - and the missing per-object compile records are supplied by scripts/compile-records.py through the $(CCACHE) $(CC) hook that MakeInc.def leaves undefined. The Build workflow sets CCACHE so the same records appear on CI. XNU C rules compile without -o and let clang derive the object name, so the record states the output explicitly when the invocation leaves it implicit: build-mockfs-fixture.py and build-libpthread-kernel.py both substitute -o and otherwise write the object somewhere the consumer does not look. build-mockfs-fixture.py is also dropped from [kernel] archives, because the kernel only force-loads that archive when MOCKFS_MODE is 1 and this board leaves CONFIG_XNU_SOURCE_MOCKFS unset. The pinned libpthread is the iOS 17 drop and is one API ahead of xnu-8796 in exactly one place, proc_get_jit_entitled; patches/libpthread/ adapts it and carries UniXNU's explicit-kernel-address.patch unchanged, since a device patches/libpthread directory shadows the upstream one. The kernel now builds and links: all eleven archives pass and mach.development.bcm2837 is produced with no undefined symbols. The three gaps the link first reported were closed without a device. The eleven IOBlockStorageDevice symbols come from scripts/build-storage-bindings.py, which binds the inherited slots to the original prelinked kext code with absolute symbols taken from scripts/prelink-storage-bindings.json -- measured statically out of the iOS 16.5 kernelcache by scripts/scan-prelink-storage.py, whose output records kernelcache_sha256 e6fa7559... and so proves it read the pinned image. _boot_progress_poll is the weak observer NativeUfs calls only while verifying, and is defined as a no-op. The six verified-root symbols come from scripts/build-verified-root.py, which compiles the real VerifiedRoot crypto rather than stubbing it and generates an empty policy table: verified_root_policy_count is 0, which makes configureVerification return before it reads the key or calls vr_open, so root verification is disabled rather than approximated. A signed policy bundle is the one input still genuinely missing, and it is policy data, not code. Prelink gets through plan() and through the instruction inventory: the one literal pool that stopped it is known to scripts/emit-prelink-nx769j.py, which re-derives its boundary from the image and refuses to run if the pool no longer sits between a function epilogue and the next prologue. What remains is the early security initializer address -- see next_action. The inventory was a safety check rather than a transform, and loosening it would let real code be classified as data and drop a genuine external encoding silently. emit_prelink.py reports instruction_relocation_complete, abi_qualified and loadable as false in every plan it writes, so the artifact it produces is an experimental candidate and not a qualified kernel however these questions are settled.
  • payload.text_fixups is an empty list, and that is the honest state rather than an approval. Entries disable code in a signed file after signature validation -- the worked example ends in c0035fd6, which is `ret` -- and each is a specific byte change in the dyld shared cache inside 096-04088-077.dmg, chosen to bypass whatever turns out to be in the way. None has been determined for this port. They are not derivable mechanically, and an empty list means none needed yet rather than none will be.
  • The four inputs that only a device held are all collected now. The 52 vendor UEFI DXE drivers and the firmware DTB come out of the device flash package with scripts/extract-uefi-drivers.py and scripts/extract-firmware-dtb.py, and both are pinned by hash in proprietary-files.txt; the unique chip id is recorded in device-inputs.md and passed as --unique-chip-id; the root volume UUID comes out of the built userdata image with scripts/extract-root-volume-uuid.py. What assembling the images still needs is a local restore image and a local trust cache, neither of which is redistributable. The payload and the boot image now build: payload.img 218103808 bytes carrying the OS trust cache df0f60fd..., and boot.img 220344320 bytes with the FD and DTB that boot.json records. The payload and boot image are not byte-reproducible: the ADT carries a 64-byte random seed and the FD it packs is not either -- see docs/HOST-VERIFY.md.
  • The Data seed is in the payload: gh run download fetches the nx769j-data-seed artifact and build payload takes it as --data-ramdisk. Each run formats a new volume, so the Data volume UUID changes with it and nothing pins one. The kernel refuses a seed whose store window does not fit the partition, so the container size has to follow the userdata geometry rather than be chosen freely.
  • The rebased copies are duplicated here rather than in the owning ARM64, SoC and component repositories, which this repository does not control. They should move back once those series are rebased.
  • The restore image is not redistributable, so ipsw.url carries the member name and the build takes a local copy through --ipsw or UNIXNU_IPSW.

下一步 ​

  • Measure the AppleMobileFileIntegrity early security initializer and set kernel.prelink.early_security_init to it. The contract is in patches/xnu/xnu-8796.101.5-security-init.patch: OSKext::start reads an _NativePrelinkEarlySecurityInit OSNumber from the kext, adds vm_kernel_slide, checks the result is 4-aligned and inside a read-execute non-writable segment of that module, and calls it as void(void) before MAC policy lockdown. So the value is an unslid function address inside the module's __TEXT_EXEC, which for this kernelcache is 0xfffffff0056fe000..0xfffffff00571b764. Identifying which function is a reversing task on a stripped kext, not a field lookup -- the __PRELINK_INFO dictionary only names the kmod stub. Until it is measured the field stays unset and emit_prelink skips the path; AMFI still starts through its ordinary start routine, but the pre-lockdown call does not happen. Separately, produce the owner-signed verified-root policy bundle over the root windows of the OS image that gets flashed and replace the generated empty table in scripts/build-verified-root.py with it, so root verification is enabled rather than disabled.
  • Set the Data volume UUID the seed run reports in whichever configuration the block driver reads it from, and set the ARTIFACT_PASSPHRASE secret so the seed is uploaded encrypted rather than holding restore-image files in the clear.
  • Derive a payload.text_fixup when a boot gets far enough to fail on one; there is nothing to derive before that.
  • Upstream the rebased series to the ARM64, SoC and XnuPrelink repositories and restore the four-series patch_series form.

证据 ​

诊断 ​

planned

硬件与依赖 ​

  • Firmware log exporter and QR log export from https://unixnu.com/porting/log-capture\#firmware-setup.
  • Serial console over kernel argument serial=3.
  • The NX769J device firmware revisions: baseband 5.0.c3-00030-LANAI_GEN_PACK-1.46586.20 and trustzone TZ.XF.5.0-07512.2-LANAIAAAAANAZT-1.46586.1.

配置 ​

  • kernel_args are '-v serial=3'; payload.panic_screen = true and payload.timeout = 3.
  • scripts/collect-device-inputs.sh gathers the board inputs that no public source provides.

操作步骤 ​

开发经验 ​

  • Each claimed milestone needs a reproducible command, a matching source and image revision, and an accessible result. Store large logs as linked release or CI artifacts.
  • The firmware revisions identify a verification result on this device, so they belong in every device evidence entry once one exists.
  • device-inputs.md records the board inputs measured over read-only adb, with the command behind each value. The handset reports a spoofed Xiaomi identity through its userspace properties while its boot image, kernel and device tree identify NX769J, so identity and hardware values were read from getprop of the bootimage properties, the kernel, the device tree and the raw UFS partition table rather than from the spoofed product properties.
  • scripts/collect-device-inputs.sh is the same collection procedure in runnable form and remains the way to re-measure before writing to a device.
  • The merged kernel configuration can carry either the CONFIG_XNU_ name from the defconfig syntax or the bare XNU_ name the kernel build consumes, and a false value can appear as n, 0 or '# NAME is not set'. scripts/check-merged-config.py accepts all of those forms so the check reports a real value mismatch instead of a format difference.

完成条件 ​

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

当前范围 ​

  • No port verification exists on the device yet: the measurements are hardware facts, not evidence that the port runs. Configuration, firmware and packaging carry host evidence with the command and commit behind it; the rest stay source evidence. Nothing carries device evidence until an image boots.
  • Log capture and recovery commands are not set up. The firmware is built and the serial console is wired through kernel_args serial=3, but nothing has been started on hardware to capture.

下一步 ​

  • Set up QR log export in the uefi-nx769j firmware and capture the firmware log before the first kernel handoff.
  • Re-run the device-inputs.md commands immediately before any write, since the recorded GPT extents describe one unit at one point in time.

证据 ​

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