cs_native4k_prepare reads the whole signed range of a file with 16K
page hashes before its signature is published. For the 3 GB dyld
shared cache that happens in launchd at every boot, and the cached
16 KiB reads filled the page cache: on the TB128FU free memory went
from 3.4 GB to 170 MB in the 42 s before the first daemon started, and
those daemons then started under memory pressure.
Read 1 MiB per vn_rdwr and use IO_NOCACHE for files above 32 MiB
(executables are faulted in right after their signature is added and
stay cached). Export the time spent in the reads and in the hashing
and the bytes read (cs_native4k_read_ticks, cs_native4k_hash_ticks,
cs_native4k_bytes) so a board can show what the boot pays.
TB128FU: free memory stays at 3.3 GB through the stall and the first
frame moved from 96 s to 86 s. The counters show 3141 MiB in 53 files,
28.9 s in the reads and 11.0 s hashing: the stall itself is bound by
the storage driver, so larger reads alone do not shorten it.
native_gpu_addon_parents starts its mkdir walk after
"/private/var/ngpu/", so it creates the directories below ngpu but
never ngpu. A Data seed that never had add-gpu-plugin-data.py applied
has no ngpu, every file of the payload's addon then fails with ENOENT
and the overlay mounts fail too, although make-gpu-addon.py says the
kernel creates missing directories. Start the walk at the slash after
ngpu so ngpu is created first (EEXIST when it is there).
Seen on the TB128FU (skip-setup seed without the plugin data):
files=9 written=0 failed=9 error=2, mounts 1.2/1.2; with the fix
written=9 and both mounts 5.0, the Metal plugin loads in backboardd.
The workflow is a build check. Kernels are linked against Apple's
kernelcache, payloads carry the platform seed and Apple content, and
userdata holds Apple's disk images, so none of them may be uploaded to a
public repository.
- Remove the kernel, payload and userdata uploads and the
upload_userdata input.
- Upload only *.log files of the build, on success and failure, for 7
days, after replacing the chip ID, the trust cache and Data seed URLs
and signed URLs with ***, and skipping binary files.
- Write a step summary with the result of each build step.
- Pin the runner to macos-26 and select Xcode by input (default 26.6)
instead of the moving macos-latest label.
- Pass the workflow token to the kernel build so the DriverKit header
listing is not rate limited.
- Install lld (no longer part of the llvm formula; the payload links
the panic screen with ld.lld) and keep Homebrew LLVM off PATH, as on
the development machines.
- Key the concurrency group on ref, device repository, device ref and
defconfig, and stop cancelling running builds.
- Check the inputs, the defconfig, the Xcode path and the platform seed
secret before installing or building anything.
- Read the trust cache and Data seed URLs from the event payload and
mask them, so they do not appear in public logs.
- Drop the explicit fetch; the build step fetches the references it
needs and no longer rewrites the device unixnu.lock.
- Let seed-data-grow.py find its submodule and build directory.
- Default to the kernel step and offer kernel plus payload without the
userdata image.
- Upload the logs on failure, name artifacts per board and attempt,
leave the skip-setup seed copy out of the payload artifact and store
userdata uncompressed.
- Pin the actions to commits of their Node 24 releases and run steps
with bash -eo pipefail.
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 logo's wordmark already reads UniXNU; as in the portrait header, the landscape header draws the title only without the logo or when it says something else, and its provenance line reads version and source.
A service that names a process in NativeSandboxGrantProcess now also lets that process set its properties when the sandbox profile would refuse it, as the grant already does for opening its user client.
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.