A changed patch series re-clones the XNU work tree, which lacks the public
headers newer xnu omits (TrustCache among them), and archives built before
the kernel failed to compile against it.
Patches 0009 to 0016 touch files the earlier ones do, so the source is
checked against the tree each prefix of the patches leaves (a scratch
index per prefix) instead of a concatenated diff. The runtime id hashes
the final tree.
Show the OS cryptex where iPadOS looks for it, under /private/preboot,
and let a board option skip the launch constraints that refused App
cryptex daemons and owner-trusted binaries. The App cryptex trust module
joins the owner module of the trust bundle, and the lookup trace also
records lsd.
MGGetProductType() looks /product/product-id up in libMobileGestalt's
product table; without it the product type was 0, and the audio server
loaded no routing database ("Unknown product ID (0)"). The profile now
sets the J120 product id, puts J120AP first in the root compatible and
adds /product/audio. The ADT generator lets a profile prefix the root
compatible and declare child nodes.
The userdata step read each IPSW disk image through macOS hdiutil, so it
stopped on Linux with "hdiutil not found on PATH". It only needs the raw
disk bytes, so on hosts other than macOS it now decodes them with
scripts/unixnu/udif.py.
udif.py reads the koly trailer and the blkx (mish) tables of a UDIF
image and yields the disk in sector order: raw chunks are copied,
zero-fill and ignored chunks read as zeros, and zlib, bzip2 and LZFSE
chunks are decompressed (LZFSE with the lzfse tool the Linux toolchain
installs). It refuses gaps, unsupported chunk types and chunks that do
not decode to their size. A file without a koly trailer is a raw image
and is copied: the SystemOS cryptex is one, while the OS image is UDIF
with LZFSE chunks. test_udif.py checks this with synthetic images.
The window layout, hashes, SHNGROOT manifest and report are unchanged,
and macOS keeps hdiutil. For iPadOS 17.7.11 (21H461) the image built on
Linux and the one built on macOS from the same disk images are byte
for byte identical (9382658048 bytes, sha256 fd43ee7035ce185c...), as
are the window hashes (OS e5634d04..., SystemOS b4908cde...).
The lzfse lookup that ipsw.py had for the kernelcache moves to util.py,
which both use. The Linux notice now names --skip-setup and
seed-data-grow as the steps that need macOS.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
XNU reads the arm-io ranges parent address as the SoC base and skips
platform setup when it is zero; device registers are offsets from that
base. Derive the window from the exported registers (16 MiB aligned)
and emit child registers relative to it.
Introduce a generic Tools submenu plus a menu-plugin protocol so device
tools can be discovered at runtime instead of being hardcoded in UniXNU.
Structure:
- UniXNUPkg/Include/Protocol/UniXNUMenuPlugin.h: plugin protocol with a
Run callback, a content rect and a DrawStatus footer callback.
- UniXNUPkg/Library/UniXNULib: discover installed plugins, render a
Back + plugin list, and run the selected plugin between the header and
footer while keeping the upper UI visible.
- UniXNUPkg/Driver/AdvancedLogDxe: first plugin; exports the Advanced
Logger log as OneSend QR frames with 5 volume-key speed gears and a
5-segment level bar, drawn with a single Blt.
- UniXNUPkg.dec: adds the menu plugin GUID and PcdUniXNUDeviceModel so
exported log files are named with the device model.
- scripts/unixnu/payload.py, built-in config and docs: `tools = true`
appends a Tools entry and documents the `tools` action.
The exported QR frames are read with OneSend (扫传):
https://onesend.01mvp.com
Scope: loader and boot logic are unchanged. This commit only adds the
menu/UI plugin path and the Advanced Logger tool; device wiring lives in
the platform checkout and is not part of this change.
On a Linux host the driver sets the environment of the
scripts/linux/setup-toolchain.py toolchain: the xcrun shim answers SDK
queries with the build tree's fakeroot, clang targets plain arm64 for
iOS 17, and CCC_OVERRIDE_OPTIONS turns off two warnings upstream clang
has and Apple clang does not (the archive builders use -Werror and
replay xnu's compile records, so only the clang driver sees them).
- build-xnu-4k-baseline.sh runs scripts/linux/prepare-xnu.py: source
rewrites the xnu sources that assume Apple's tools (SHA/AES assembler
macros, makesyscalls.sh under mawk, kextsymboltool and /dev/null) and
installs TargetConditionals.h into the fakeroot; libs builds
libcc_kext.a from compiler-rt's cc_kext profile sources. xnu's make
also gets the host compiler, mig and the machine sizes a Mac supplies.
ditto becomes cp.
- build-firehose.sh compiles libfirehose_kernel from its xcconfig.
- build-corecrypto-14.py reads project.pbxproj with scripts/pbxproj.py
(same JSON as plutil for all 13,112 objects).
- The kernelcache is decoded with lzfse.
The macOS path is unchanged apart from ditto. Built from the same
sources and configuration on Linux and with Xcode 27.1, the kernels have
the same global symbols, linked objects, archive symbols, 1,265
kalloc_type signatures, and prelink bindings and kexts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
scripts/linux/setup-toolchain.py, run once as root, builds into
/opt/unixnu-linux (or PREFIX, or $UNIXNU_TOOLCHAIN):
- clang from llvm-project 58c46bae2118 with llvm-xnu-builtin-type.patch,
which adds __builtin_xnu_type_signature, __builtin_xnu_type_summary,
__builtin_xnu_types_compatible and xnu_usage_semantics; kalloc.h uses
the builtins with no fallback;
- cctools-port 1030.6.3-ld64-956.6 (its master, ld64-609, corrupts the
kernel link), with libdispatch for ld64;
- mig and migcom from bootstrap_cmds-138;
- iig-port, with iig-port-darwin-target.patch;
- xnu-host-cc, the host compiler for xnu's SETUP tools;
- lzfse for the kernelcache.
It installs the Command Line Tools shims (clang, clang++, cc, install,
gm4) under the prefix and xcrun, plutil and codesign in /usr/bin, where
xnu's makefiles call them. libmacho.a and libstuff.a go to /usr/lib and
libdispatch to /usr/local. Each step is skipped when its output exists.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The bridge build applies the padded-row alias patch and passes a runtime id derived from the translator pin and its patches, so primed shaders survive until the translator changes. Kernel argument values may now contain a slash.
prepare-xnu-source.py lists Apple's xnu tree through the GitHub API without credentials. Hosted runners share the anonymous per-address quota and the bootstrap pass stops with HTTP 403 rate limit exceeded. Send GITHUB_TOKEN or GH_TOKEN, when set, to api.github.com and raw.githubusercontent.com only.
The payload's unixnu.toml replaces the built-in menu, which held the only Restart and Power off entries; the generated configuration now ends with both.
board.toml [payload.install] adds a second boot menu entry with the same
kernel and Data seed behind a confirmation; each confirmation choice
appends its boot argument (the sheng board uses it for the Data reset).
The payload ran patch-xnu-entry-probe.py only for panic_screen, always
with --no-reset. [payload] entry_probe_reset_delay now keeps the reset
(--reset-delay), and entry_probe_reset_after, _in, _occurrence and
_before move it after a given call (--reset-after, --in, --occurrence,
--reset-before), with or without the panic screen. Before the kernel
can draw, a reboot after the delay is the only sign that it got that
far. Without the new keys the call is unchanged.
[payload] skip_setup_args (optional) is passed to skip_setup_tool after
the two paths, and the edited seed is cached by the hash of the base and
of those arguments.
seed-data-skip-setup.py --no-auto-dim also sets SBDisableAutoDim,
SBDontDimOrLockOnAC and SBDisableIdleSleep in com.apple.springboard, as
UniXNU-qemu's build-os-root.py does: a board without buttons or a working
idle timer otherwise turns its display off about 30 seconds after
SpringBoard starts and puts the system to sleep.
Turnip asked a dedicated allocation of every image made for external
memory, so an image over a no-copy buffer's host pages could not be
bound inside the buffer's import and each such texture fell back to a
shadow image copied in and out per pass. Ask it only for handle types
that carry per-image metadata, as the host-allocation format query
already reports.
offline-owner-systemos-mount.patch mounted the SystemOS cryptex volume
by the sheng image's UUID. XNU_OWNER_CACHE_VOLUME_UUID
(CONFIG_XNU_OWNER_CACHE_VOLUME_UUID) now names it; the build writes it
into owner-data-volume.h next to the Data volume UUID and rebuilds
kern_exec.o when it changes. Without it the sheng value stays the
default.
build-native-metal-bridge.py --manifest-only writes
build/native-metal-bridge/Cargo.toml exactly as the build does (patched
translator source, fixtures) and stops before cargo, so the crates can
be fetched once (cargo fetch --manifest-path ...) and later builds run
offline.
NativeMetal now makes textures on two-plane IOSurfaces, which widget
snapshots are drawn into, and reports why a surface texture was
refused. The bridge build applies the runtime patch that accepts the
two-plane RGB surface format.
make-gpu-addon.py --prime-list FILE adds the OS image's .metallib paths
as addons/NativeAdrenoMetal.primelist. The plugin build also links
build/prime-list.txt into the executable's __DATA,__primelist section,
since the render server's sandbox may refuse to read the file.
The deletable system apps (Weather, Notes, Calendar and the rest) ship in
the OS volume's /private/var/staged_system_apps; a restore copies them
into Data, where the first-boot data migration installs them. The owner
Data mount hides that directory, so the apps and their widgets were
missing. With boot argument sheng-staged-apps=1 the kernel copies it once,
before launchd starts, through the vnode the Data mount covers, owned by
mobile (installcoordinationd moves the bundles as mobile). The walk is
iterative and every file is synced; /private/var/db/.sheng-staged-apps
marks a complete copy.
The payload passes --persistent-data to the ADT tool for boards that set
[payload.adt] persistent_data.
PosterBoard renders the iPad7,1 variant of wallpaper 7570, a layered
animation whose two HEIC images ImageIO cannot decode without a hardware
HEVC decoder, so the wallpaper stayed black.
scripts/make-wallpaper-overlay.py converts both images to 2732 pixel PNG
and points a copy of main.caml at them. make-gpu-addon.py --wallpaper
packs that directory into the addon. The kernel now creates missing
directories below /private/var/ngpu while it writes the addon, and
mounts /private/var/ngpu/wallpaper as a read-only union over the
animation's directory.
The text fixup loop landed inside the ipad_identity condition, between
the if and its body, which leaves payload.py unparsable. Move it after
the --ipad-identity append.
A board lists its fixups of signed file text in board.toml [payload]
text_fixups. The payload passes them to the ADT tool, which stores them
NUL-separated in /chosen/text-fixups, so they take no space on the
1000-byte command line. The kernel reads that table before the
text-fixup0..7 boot arguments and now holds up to 32 fixups.
`unixnu build payload --gpu-addon FILE` carries a file made by
scripts/make-gpu-addon.py next to the kernel. The loader hands it over as
/chosen/memory-map/GpuAddon, and before launchd starts the kernel writes
every file whose content or mode differs into /private/var/ngpu of the
mounted Data volume, then mounts the addon views. The Adreno Metal plugin
then no longer has to be part of the Data seed, so the seed's persistent
store survives a plugin rebuild. Every file is zlib compressed to keep
the payload FAT at 192 MiB. Updates DarwinBoot and NativeRamBlock for the
addon handoff.
Parse the P6 header explicitly, keep every raster byte, and reject
non-positive dimensions, maxval other than 255, and mismatched raster
lengths. Add scripts/test-qemu-ppm.py.
The defconfig grammar accepts signed decimal digit sequences, but
int(raw, 0) rejects nonzero decimal values with leading zeroes, such
as 08, 010 and -08. Select base 16 only for the existing 0x form and
base 10 otherwise, preserving signed hexadecimal values.
Add 16 numeric regression cases to the build driver's existing
self-test.
seed-data-grow.py resizes the APFS container of a packed seed with
diskutil, without mounting a volume, and packs it again. The size is
given in GiB or taken from the userdata partition of a tablet in
fastboot, minus the root image, a guard, the log reserve and one GiB.
The tree's .cache/firmware links to .cache/firmware of the device repository, where scripts/extract-files.py puts them, so every tree sees the same files and none is copied into a repository.