Watch
1
0
Fork
You've already forked SouveraineOS
0
SouveraineOS/saf/device/evidence/secure_load_tz_frontier.md
Fimeg bde961c6f2 saf: one spine — device, state, and work under the index
PAF becomes saf/device (history kept), STATE.md dissolves
into saf/state.md with the dated era archived, the substrate
SAF moves up from souveraine, and every agreement points at
saf/INDEX.md and nowhere else. one map, nothing to remember
2026-08-18 09:47:30 -04:00

18 KiB
Raw Permalink Blame History

PAF · Secure-load / TrustZone frontier

🔴 SUPERSEDED 2026-06-29. Secure-load is RESOLVED for the modem — the MSS loads clean (MBA boots, MPSS loads, remoteproc running, zero 22 on the modem path) via DELTA-1 (c2923cb49 MPSS assign-ordering). This doc is kept as the historical 22 / fastrpc_mem investigation record. The 22 that still floods the logs is the SLPI's SDSP heap, not the modem's — see slpi.md. The current frontier is the firmware-internal OFFLINE/52 park, not secure-load. (Body below is the 2026-06-26 investigation as-written.)

2026-06-26 (OPUS session) — mem-protect SKIP broke the boot on hardware; reverted. LOS does mem-protect differently, not skip it.

Platform is now clean and reproducible (this part is solid, keep it):

  • ONE canonical branch blueline (was split across mobian-sdm845-7.1 + blueline-slpi-build, neither complete — consolidated). It = Mobian mobian-sdm845-7.1 base (mainline 7.1.1 tag + full 179-patch sdm845 device series: camera/imx sensors, q6voice, haptics, fuel-gauge, panels)
    • our 6 fixes. Other branches renamed reference/mobian-7.1.1-base, archive/superseded-*.
  • Guard against drift: tools/verify-fixes.sh (13 checks, asserts SOURCE CONTENT not commit messages; covers our 6 fixes AND the inherited Mobian migrations). Run before every build.
  • Build: pmbootstrap build --src=references/linux-7.1.1-sdm845-CANONICAL linux-postmarketos-qcom-sdm845 (host make is NOT the path — build.md:7). Deploy: on-device apk add --allow-untrustedboot-deploy auto-flashes boot_a. uname -r = 7.1.1-sdm845. Rollback image: build-output/boot_a-rollback-20260626-1605.img.gz.
  • Found + fixed the silent killer behind every prior "flash and test": the phone's kernel and its /lib/modules were ABI-mismatched — qcom_q6v5_mss gave Exec format error, the modem driver never loaded, so the modem was dead regardless of any fix. Matched build → driver loads, modem remoteproc 4080000.remoteproc now registers.

The mem-protect skip (need_mem_protection = false for sdm845) is REFUTED — likely roll back:

  • It removed the -22 from the modem path as designed — but the modem now fails EARLIER: Booting .../mba.mbn → PBL returned unexpected status 0xEF110000 → can't start rproc: -22. It never reaches "MBA booted".
  • The r2 baseline (below) had the modem boot MBA then XPU-crash-loop at 52. So the skip REGRESSED the boot. qcom_scm_assign_mem (q6v5_xfer_mem_ownership, qcom_q6v5_mss.c:463/489) transfers the MBA region HLOS→MSS_MSA so the modem PBL can read MBA; skipping starves PBL. The assign is load-bearing. The real gate is WHY TrustZone returns -22 (EINVAL), not the call's existence.

The LOS claim in HANDOFF_20260626_OPUS.md was unverified inference — treat as RETRACTED:

  • It blended mainline (need_mem_protection) with downstream (subsys_vmid / qcom,mem-protect-id) concepts into one story.
  • LOS Pixel 3 runs the downstream msm-google 4.9 kernel; its modem loader is subsys-pil-msa, NOT mainline qcom_q6v5_mss.c. We have NOT pulled that source (Tier 1 still open) — so what LOS does was never read off real code.
  • Hardware contradicts the inference: if LOS did no mem-setup and MBA self-unlocked XPU, the skip would have worked. It made things worse.

PRINCIPLE (Casey, 2026-06-26): LineageOS is the ONLY known-working modem reference on this device. Other sdm845 devices (op6 / enchilada / beryllium / shift) are IRRELEVANT to the secure-load / XPU question — do not cite them as modem-bringup evidence. The downstream Pixel-3 stack is the authority.

Current phone state: booted on blueline 7.1.1 (the skip build). Modem PBL-fails every boot; SLPI crash-loops ~10s (the -22 spam every 10s is the SDSP/SLPI hyp-assign, NOT the modem).

OPTIONS — left for Casey to choose (not picking one):

  1. Revert the skip → restore the MBA-boots-then-XPU-52 baseline on the clean 7.1.1 platform, then work secure-load Tier 0/1 from a known state.
  2. Pull msm-google downstream kernel → diff subsys-pil-msa mem-setup + hyp-assign vs mainline. Answers "what does LOS actually do" with real code (Tier 1).
  3. Downstream-kernel hybrid (Droidian / Halium pattern) — this doc's honest realistic 12 week path to working telephony.
  4. Chase the TZ -22 directly — why does mainline's qcom_scm_assign_mem get EINVAL on blueline (VMID / mem-protect-id / TZ expectation)? Open-ended.

2026-06-26 — TRIGGER RESOLVED: this doc is the plan. Hard secure-memory evidence captured.

The trigger condition below ("still 52, modem boots then refuses RF → the gate is the secure-load/TZ layer → this doc is the plan") is now MET, with a concrete error string instead of inference. On the rebuilt r2 LLVM kernel (slot A, live ssh), dmesg shows the modem crash-looping on a hardware secure-memory protection fault:

qcom-q6v5-mss 4080000.remoteproc: fatal error: modem_xpu_int.c:456:[modem_sec_xpu_I]XPU violation
(also crash #1: "SFR Init: wdog or kernel error suspected")

and EVERY modem + slpi restart logs the shared TZ rejection:

qcom_scm firmware:scm: Assign memory protection call failed -22   (qcom_scm_assign_mem → -EINVAL)
SDSP fastrpc hyp-assign rejected by TZ (-22)

XPU = eXtreme Protection Unit, the hw memory guard TZ programs. modem_sec_xpu XPU violation = the modem firmware touched a region the XPU forbids → its secure memory regions aren't assigned right for mainline → crash → q6v5 recovers → repeat → never RF-inits → 52. The fastrpc/SLPI assign -22 is the same mechanism (TZ refusing mainline's hyp-assign); fastrpc tolerates it, the modem can't. The gate is concretely the secure-memory-assign / XPU region setup — pull the assign_mem -22 thread. (See modem.md TOP LEAD #11, slpi.md.)

CAVEAT (don't overclaim): this XPU crash-loop was captured on 7.1.1 r2. The 6.16.7 / 7.1rc1 captures recorded the modem "up + QMI registers" (lead #10) — QMI still answers here too, in the up-windows between crashes, so a single-sample running looked fine before. OPEN: re-check whether 6.16.7 also XPU-crash-loops (i.e. always-present, just not noticed) or whether 7.1.1 worsened it — diff journalctl -p err across both kernels before assuming XPU is identical on the older tree.

Original status (pre-2026-06-26): alternative, not primary. The primary lever was Opus's reserved-memory geometry fix (modem_dt_rf_analysis_20260622.md candidate #1: revert the sdm845-google-common.dtsi override so MPSS = 0x7800000 / MBA = 0x96500000, matching both working devices). That swing did not clear the 52; the secure-load/TZ layer (this doc) is now primary.

It is written assuming the reader has modem.md, modem_dt_rf_analysis_20260622.md, tools/modem-online-provisioner/RESULTS.md, and android-capture-20260622/EFS_FINDINGS.md in head.

Trigger condition (the decision point)

Flash the reserved-memory DT fix to boot_a, cold-boot pmOS, one --dms-set-operating-mode=online:

  • online works → geometry was the gate. Stay mainline; push the fix upstream (sdm845-mainline / pmaports), bisect the google-common override. This doc is not needed.
  • modem fails to load (remoteproc Boot failed) → the firmware was signed for the old geometry; the override existed for a reason. Revert; geometry is load-bearing-correct as-is. Come here.
  • still 52 (modem boots clean, refuses RF) → the gate is not the DT geometry. It is in the secure-load / TrustZone / subsystem-coordination layer. This doc is the plan.

The gate, precisely (today's hard proof — do not re-derive)

Source: tools/modem-online-provisioner/RESULTS.md (raw QMI over AF_QIPCRTR, 2026-06-22).

  • Modem is in op-mode 3 = OFFLINE (deepest non-persistent off state). Not LPM, not transient.
  • Accepts every read, non-RF writes (DMS/NAS indication registration), and DMS Set Operating Mode = RESET (0x002E mode 4) → OK.
  • Refuses every RF-requiring transition: online → 52 DeviceNotReady; low_power → 60 InvalidTransition; NAS Set System Selection Preference (0x0033) → 25 DeviceUnsupported; DMS ATEL UI-ready (0x0064) → 58 (arg-rejected, message id confirmed correct).
  • After the accepted RESET, firmware re-initialises straight back to OFFLINE and refuses again, with zero sys-info/serving-system indications in an 8 s listen. The firmware is inert on RF by decision, not attempting and failing.

Conclusion (raw-level, not inference): no AP-side QMI message or ordering unlocks RF. The choreography-replay path (qcril_online_sequence.md) is closed: the modem rejects the pre-online writes themselves before we reach the online flip. QCRIL only succeeds on Android because the firmware has already self-completed RF-init there; on pmOS it never does.

Why this points at secure-load / TZ (the RESET argument)

A QMI RESET re-initialises the modem firmware without re-doing the cold-boot setup — PIL image auth, the TZ/SCM state, the bootloader (ABL/XBL) modem init, and sibling-subsystem coordination are all established once at cold boot and persist through a QMI reset. The RESET reproducing OFFLINE tells us the gate lives in that persistent cold-boot environment, not in a one-shot race or a userspace omission. That environment is exactly four things, in suspect order:

  1. DT reserved-memory geometry — Opus's test (may already settle it).
  2. Sibling subsystem / Protection-Domain coordination — the modem RF-init can gate on a PD registered over glink/pd-mapper by ADSP/CDSP/SLPI. pmOS DT has no SLPI region at all (absent in the modem_dt_rf_analysis table; Android + enchilada both carry slpi@0x96700000). Never confirmed up on pmOS. Mainline-fixable if so — not TZ.
  3. PIL / SCM secondary auth — image auth clearly succeeded (modem boots), but a non-fatal secondary secure step (a hash-table segment, a secure-regulator vote, a TUST handshake) may not have completed, leaving the firmware in a degraded RF-locked mode.
  4. Bootloader / verified-boot state — pmOS boots a hand-built --header_version 0 image with a custom cmdline. If ABL sees an unverified/inconsistent chain, it can skip modem secure-env init that stock Android gets.

Ranked investigation — cheapest discriminator first

Tier 0 — free, same boot (run the instant reserved-memory fails, before any build)

  1. Mine dmesg for the secure/PD layer. Image auth succeeded, so look for non-fatal warnings: dmesg | grep -iE 'pas|scm|auth|hash|mba|qcom_q6v5|remoteproc|glink|pdr|pil'. A non-zero qcom_scm_pas_* return, a PDR restart-notification timeout, or a glink intent-failure is a citable pointer into #2/#3.
  2. Confirm sibling DSPs are up and their PDs registered. for r in /sys/class/remoteproc/remoteproc*; do printf '%s: %s %s\n' "$r" "$(cat $r/name 2>/dev/null)" "$(cat $r/state 2>/dev/null)"; done and qrtr-lookup | grep -iE 'adsp|slpi|cdsp|sns|domain'. If SLPI/ADSP is offline/absent and the modem waits on its PD, that is the gate and it is mainline-fixable (firmware path + remoteproc node + a pd-mapper entry), not TZ. This is the highest-value cheap check — the whole elimination tracked remoteproc2 (modem) closely and never verified the siblings.
  3. Timing/missed-response. strace -f -p $(pidof pd-mapper) and rmtfs during a QMI RESET. If the modem's RF-init window closes before pd-mapper/rmtfs answer a request, you'll see the missed response. (Likely already excluded — services are confirmed up post-boot — but cheap to confirm.)
  4. LDO13 rail state — the one rail NOT on the eliminated list. Unlike the refuted #9 LDO set (ldo4/6/8/9/10/11/15/16/18/23/27, which were absent from pmOS), ldo13 IS defined in dt-work/blueline-pmos.dts:5466, and on SDM845 LDO13 feeds RFFE (per OpenStick prior art — the l13: voltage operation not allowed line is benign on MSM8917 but not automatically so here). Static dumps show a state delta: pmOS ldo13 … 0 0 0 … vs Android pm8998_l13 … 0 1 0 …, and there is no l13/voltage operation not allowed line in the captured pmOS dmesg. Live check at the moment of online: for r in /sys/class/regulator/regulator.*/; do n=$(cat $r/name 2>/dev/null); [ "$n" = ldo13 ] && echo "$n state=$(cat $r/state) @ $(cat $r/microvolts)uV"; done — is it on @ 1.8 V while the modem is booted-but-refusing-RF? Then retry online and watch whether L13 flips. Likely falls to the same "modem votes RF via RPMh, not the AP framework" refutation as #9 — but it's the one rail never explicitly probed, so confirm live before closing. Plus dmesg | grep -iE 'l13|voltage operation not allowed' on the same boot.

Tier 1 — diff the downstream kernel (the real working reference for secure-load)

Enchilada proves the mainline stack works on SDM845; it does not prove the secure-load code paths work on blueline's exact modem. The authority for those is Google's downstream Pixel-3 kernel (msm-google, 4.4-era — confirm the exact branch against boot_a's kernel version on android.googlesource.com/kernel/msm-google before cloning). Pull it and diff, against mainline, exactly three things:

  • drivers/remoteproc/qcom_q6v5_mss.c — the PAS auth + mba mem-setup + reset sequence.
  • drivers/soc/qcom/{scm,pdr_interface,smem} — the secure-channel and PD-restart path.
  • modem reserved-memory + qcom,smem-states + power-domains in the DT.

The reserved-memory hit is the first output of this method; the SCM/auth/PDR delta is the next. This is the strongest "honest swing" if reserved-memory alone fails — methodical, sourced, no RE of opaque blobs. Artifact we have but haven't used this way: android-reference/vendor-stack-20260621/ (vendor RIL + MBN tree). Artifact we still need: the downstream kernel source tree.

Tier 2 — instrument the firmware's own RF-init (the only inside view)

Every capture so far is userspace (QCRIL/radio logs). Nobody has captured the MPSS firmware's own RF-init trace — the ordered set of things it reads and waits for. On rooted Android (slot A), QCSuper continuous diag over /dev/diag (local — confirmed working; USB-diag is HAL-locked on this production Pixel, per pmos_fix_plan.md) captures the F3/log stream during a cold boot. An open F3 decoder (linux-msm / sdm845-mainline ecosystem; QCSuper has partial decode) turns it into the firmware's RF-init step list. Diff which of those steps can't complete on pmOS. This is the one instrument that can name the gate from inside the firmware; everything on pmOS only observes the refusal. Read-only capture — do not write EFS.

Tier 3 — TZ / SCM RE (the hard pole, deprioritize)

If Tiers 02 point at a specific SCM call or secure state: disassemble the QSEE trustlets or diff XBL/ABL's modem init. Long, uncertain, opaque. Keep as the escalation, not the plan.

The strategic fork (be honest about the 12 week telephony goal)

Mainline-native secure-load RE (Tier 23) is realistically not a 12 week path to working telephony/SMS/data — it's open-ended research. If Tier 0 + reserved-memory don't clear 52, the fastest route to "telephony on Linux like I have on rooted Android" is to use the stack that already works on this silicon:

  • Downstream-kernel hybrid (Droidian / Halium / pmOS-downstream pattern): boot the downstream msm-google kernel under a Linux userspace, where the modem firmware gets the exact cold-boot environment it expects so RF-init completes, then drive it with the vendor qcrild already pulled in android-reference/. Trades mainline purity for a known-good outcome. This is the realistic 12 week path.

  • WRONG TURN — do not burn time on this: "run vendor QCRIL on the mainline kernel via libhybris" will not fix it. QCRIL does not trigger RF-init (the firmware self-arms on Android; QCRIL only queries/confirms — proven by today's raw results). The gate is the cold-boot environment, so only changing that environment (DT → subsystems → TZ → bootloader) or booting the downstream kernel helps. Vendor-RIL-on-mainline addresses a different (already-solved) layer.

Net: reserved-memory fix + Tier 0 (free) → if clear, stay mainline; if not, decide mainline-secure-load-research vs downstream-kernel-hybrid on evidence, not on momentum.

Artifacts

  • Have: android-reference/vendor-stack-20260621/ (libril-qc-hal-qmi.so + 174-file MBN tree + selection DB); tools/modem-online-provisioner/{qrtr_raw.py,qrtr_seq.py} (send any QMI PDU over AF_QIPCRTR — validated against DMS 0x002D); QCSuper efs-shell + diag over /dev/diag on rooted slot A; slotb-backup-20260621/pmos-7.1-boot.img recovery; 6.16.7 known-good kernel; EFS clones.
  • Need (not yet pulled): downstream msm-google kernel source (Tier 1 ground truth); an open Hexagon F3/diag decoder (Tier 2); possibly a devcoredump/minidump parser if SSR ever yields one (today it did not — modem doesn't crash, it deliberates).

Guardrails (secure-load-specific)

  • pmOS-side secure-load investigation is read-only: dmesg, /sys/class/remoteproc/*, /sys/kernel/debug/{remoteproc,msm_subsys}/*, qrtr-lookup. Safe.
  • diag/F3 capture is on rooted slot A /dev/diag — read-only capture. Never write EFS or NV from these probes (shared unslotted modemst1/2 — a write is seen by both slots).
  • Never flash/patch TZ, trustlets, XBL, or ABL — brick territory. references/edl-pixel exists as last-resort recovery but assume no EDL safety net; behave as if a bad flash is permanent.
  • Only ever touch boot_a / pmOS. Never slot B, system_a, the shared EFS, or userdata.
  • Recovery image: slotb-backup-20260621/pmos-7.1-boot.img. Document every slot switch + flash.
  • One online attempt per boot (a failed attempt poisons the modem until a power cycle).
  • Standard hard constraints apply: pmOS is Alpine/ash (no [[ ]]/arrays); rmtfs -P -s (never -r alone); never add pmic@4/pmic@5 SPMI nodes; sudo pw 147147.

Not yet referenced from START-HERE.md or PAF/README.md (left for the Opus handoff to wire, to avoid colliding with in-flight edits). If reserved-memory fails, point START-HERE's "actual frontier" at this doc and demote the reserved-memory lever to the trigger-condition note above.