Skip to content

Add Linux host support for firmware prep, and silence two harmless boot-noise sources - #1

Open
cn0xroot wants to merge 1 commit into
jprx:mainfrom
cn0xroot:main
Open

Add Linux host support for firmware prep, and silence two harmless boot-noise sources#1
cn0xroot wants to merge 1 commit into
jprx:mainfrom
cn0xroot:main

Conversation

@cn0xroot

Copy link
Copy Markdown

Summary

  • get_files.sh / fix_perms.sh can now run on a Linux host, not just
    macOS. The macOS path is untouched — every new branch is gated behind
    [[ "$(uname)" == "Darwin" ]], so this is purely additive.
  • Two console log lines that spam constantly (regardless of host OS, since
    this VM never emulates a SEP or a real dyld shared cache) are now
    silenced, without touching any code logic.

See LINUX.md (English) / LINUX.zh-CN.md (Chinese) for the full writeup —
extra host dependencies, exactly what changed and why, and the patch
rationale. CHANGELOG.md has a shorter changelog-style summary.

Motivation

The README's setup steps for downloading/patching firmware
(get_files.sh's patch_ramdisk, and fix_perms.sh) shell out to
macOS-only tools (hdiutil, ditto, codesign) and just print "you need a
Mac" and exit on any other OS. Building qemu-sptm itself already works
fine on Linux (the README already says the build/run machine "can be
different" from the Mac used for firmware prep) — this PR closes the last
gap so the entire pipeline, prep included, works on Linux too.

What changed

  • New dmgutil.sh: shared helpers (dmg_attach, dmg_detach,
    copy_tree) for mounting/unmounting ramdisk.dmg and copying directory
    trees into it. On macOS these are thin, behavior-preserving wrappers over
    hdiutil/ditto. On Linux:
    • Mounting uses the linux-apfs-rw
      out-of-tree kernel module, since firmware/ramdisk.dmg turns out to be a
      raw APFS container image (no UDIF/HFS+ wrapper) — mount -t apfs -o loop,readwrite[,uid=…,gid=…] works directly on it.
    • Copying uses cp -a --remove-destination — the Linux apfs driver's
      experimental write support doesn't implement O_TRUNC, so overwriting
      an existing file has to unlink-then-create rather than
      truncate-in-place, or cp fails with "Operation not supported".
  • get_files.sh / fix_perms.sh: source dmgutil.sh; no longer bail
    out on non-Darwin hosts; use ldid -Cadhoc -S / ldid -h in place of
    codesign -s - / codesign -d -vvv on Linux (textually compatible
    output, so the existing grep -i cdhash= parsing needs no changes);
    chown root:wheel becomes chown 0:0 on Linux (same uid/gid pair — the
    wheel group is gid 0 on macOS, same as Linux's root group).
  • dt_fixup.py: removes the device tree's sep node and sets
    sepfw-load-at-boot=0. Without this, AppleCredentialManager believes a
    SEP is present and retries forever, printing ACMTRM: waitForSEPEndpoint: timed out waiting for AppleSEPManager every ~5 seconds for the entire
    life of the VM.
  • New patch_bootkc.py, wired into get_files.sh's main() right
    after bootkc is downloaded: truncates an unconditionally-printed
    shared_region: ... check_np(...) vm_shared_region_start_address() returned 0x1 debug format string in place, by overwriting its first byte
    with a NUL. This message otherwise reprints on every single process
    launch in the guest. The patch is a single data byte, zero instruction
    changes (printf-family functions never read their vararg list when the
    format string has no % directives), and was verified byte-identical
    against both an iOS (iPhone17,3) and macOS (Mac16,10) bootkc.
  • run.sh: added trm_enabled=0 hidrm_enabled=0 to BOOT_ARGS as a
    secondary, harmless mitigation alongside the device-tree fix for the SEP
    retry noise (kept even though it alone wasn't sufficient to stop it).
  • New .gitignore: excludes generated/downloaded output (firmware/,
    ipsw_db/, sysroot/, sysroot.tar.gz, mnt/, qemu-sptm/build/).

Testing done

On a Linux host (Ubuntu 24.04, kernel 7.0.0-30-generic, x86_64):

  • Built ipsw and ldid from source, and linux-apfs-rw against the
    running kernel's headers.
  • Ran the full get_files.shfix_perms.shrun.sh pipeline
    end-to-end for both an iOS guest (iPhone17,3) and a macOS guest
    (Mac16,10), each booting cleanly to a root shell with commands (id,
    whoami, uname -a, ls) returning correct output.
  • Confirmed neither boot-noise fix introduces new instability across
    multiple repeated boots on both device targets.
  • Did not test on an actual Mac, but every macOS-specific code path is
    identical to what it was before this PR (same hdiutil/ditto/codesign
    invocations, same flags), so no regression is expected there.

Notes for reviewers

  • The two boot-noise patches are host-OS agnostic (they change the guest's
    device tree / kernelcache, not anything host-specific), so they're worth
    taking independently of the Linux-hosting changes if preferred.
  • linux-apfs-rw's write support is explicitly labeled experimental
    upstream. It's been reliable in all testing here (building/rebuilding
    firmware for two different device targets, repeatedly), but flagging it
    since it's the one new moving part with a "use with caution" label from
    its own author.

get_files.sh / fix_perms.sh can now run on Linux, not just macOS: a new
dmgutil.sh wraps DMG mount/copy so the ramdisk-patching and
permission-fixing steps go through the linux-apfs-rw kernel module + ldid
on Linux, while continuing to use hdiutil/ditto/codesign unchanged on
macOS. See LINUX.md / LINUX.zh-CN.md for setup and rationale.

Also patches out two console-spam sources that show up regardless of host
OS, since this VM never emulates a SEP or provides a real dyld shared
cache: dt_fixup.py removes the device tree's 'sep' node (stops
AppleCredentialManager's infinite SEP-wait retry loop), and the new
patch_bootkc.py truncates an unconditional shared_region check_np() debug
string in bootkc (1-byte data patch, no instruction changes).
phakoda added a commit to phakoda/darwin-vm that referenced this pull request Aug 31, 2026
Port the firmware-prep pipeline to Linux using linux-apfs-rw for the APFS recovery ramdisk and ldid for ad-hoc signing/CDHash extraction. Keep the existing macOS hdiutil/ditto/codesign path unchanged.

Based on the Linux-host implementation and end-to-end testing from jprx#1, rebased onto the current main without importing its unrelated guest SEP/kernelcache noise patches.
d0lb33 added a commit to d0lb33/darwin-vm that referenced this pull request Sep 2, 2026
Two results, both from the full four-feature boot that only became
possible today.

The idle is named. Every SpringBoard thread is asleep: three blocked in
ipc_mqueue_receive_results on distinct empty Mach receive queues, the
fourth parked in workq_unpark_continue, with the continuation unslid to
0xfffffff00afb0b48 and matched against osfmk/ipc/ipc_mqueue.h:122 and
osfmk/kern/waitq.h:198. Nothing is spinning or crashed -- they are
waiting for messages that never arrive. Thread addresses, states,
continuations and wait queues are in docs/re/boot-idle.md.

The inbound callback direction now works end to end, which was the named
display gate:

  iomfb: ep 0x37 callback jprx#1 'D575' (0x44353735) in 88 out 76 -> heap+0x40000
  iomfb: IOP -> AP ep 0x37 0x000000b000000302  (class-2 callback 'D575')
  iomfb: ep 0x37 class 2 subkind 1 (callback completion) tag 0 ack 1 status 0x0

The hot-plug-shaped D575 executes and returns status zero, and userspace
opens IOMobileFramebufferUserClient for the first time.

The display hypothesis is now qualified rather than confirmed: DCP is on
the SpringBoard path, but it is not sufficient. A385 returning 1 stops
5,442 polls (down to 3) without preventing the failure, and SpringBoard
still exits with DCP fully active. The RAM snapshot carries no usable
OS_REASON or exception codes, so the exit cause is left unnamed rather
than guessed -- the apparent backtraces decode as
TASK_CRASHINFO_UDATA_PTRS, opaque user-data pointers, not instruction
addresses.

Regression probes preserve both invariants: 11/11 AFK endpoints, and
default-off boots unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant