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
18 KiB
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 (c2923cb49MPSS 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 — seeslpi.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 acrossmobian-sdm845-7.1+blueline-slpi-build, neither complete — consolidated). It = Mobianmobian-sdm845-7.1base (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-*.
- our 6 fixes. Other branches renamed
- 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(hostmakeis NOT the path — build.md:7). Deploy: on-deviceapk add --allow-untrusted→boot-deployauto-flashesboot_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/moduleswere ABI-mismatched —qcom_q6v5_mssgaveExec format error, the modem driver never loaded, so the modem was dead regardless of any fix. Matched build → driver loads, modem remoteproc4080000.remoteprocnow registers.
The mem-protect skip (need_mem_protection = false for sdm845) is REFUTED — likely roll back:
- It removed the
-22from 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 mainlineqcom_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):
- 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.
- Pull
msm-googledownstream kernel → diffsubsys-pil-msamem-setup + hyp-assign vs mainline. Answers "what does LOS actually do" with real code (Tier 1). - Downstream-kernel hybrid (Droidian / Halium pattern) — this doc's honest realistic 1–2 week path to working telephony.
- Chase the TZ
-22directly — why does mainline'sqcom_scm_assign_memget 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-commonoverride. 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:
- DT reserved-memory geometry — Opus's test (may already settle it).
- 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_analysistable; Android + enchilada both carryslpi@0x96700000). Never confirmed up on pmOS. Mainline-fixable if so — not TZ. - 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.
- Bootloader / verified-boot state — pmOS boots a hand-built
--header_version 0image 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)
- 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-zeroqcom_scm_pas_*return, a PDR restart-notification timeout, or a glink intent-failure is a citable pointer into #2/#3. - 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)"; doneandqrtr-lookup | grep -iE 'adsp|slpi|cdsp|sns|domain'. If SLPI/ADSP isoffline/absent and the modem waits on its PD, that is the gate and it is mainline-fixable (firmware path + remoteproc node + apd-mapperentry), not TZ. This is the highest-value cheap check — the whole elimination trackedremoteproc2(modem) closely and never verified the siblings. - 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.) - 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),
ldo13IS defined indt-work/blueline-pmos.dts:5466, and on SDM845 LDO13 feeds RFFE (per OpenStick prior art — thel13: voltage operation not allowedline is benign on MSM8917 but not automatically so here). Static dumps show a state delta: pmOSldo13 … 0 0 0 …vs Androidpm8998_l13 … 0 1 0 …, and there is nol13/voltage operation not allowedline in the captured pmOS dmesg. Live check at the moment ofonline: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 iton@ 1.8 V while the modem is booted-but-refusing-RF? Then retryonlineand 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. Plusdmesg | 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-domainsin 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 0–2 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 1–2 week telephony goal)
Mainline-native secure-load RE (Tier 2–3) is realistically not a 1–2 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-googlekernel 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 vendorqcrildalready pulled inandroid-reference/. Trades mainline purity for a known-good outcome. This is the realistic 1–2 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/diagon rooted slot A;slotb-backup-20260621/pmos-7.1-boot.imgrecovery; 6.16.7 known-good kernel; EFS clones. - Need (not yet pulled): downstream
msm-googlekernel 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 unslottedmodemst1/2— a write is seen by both slots). - Never flash/patch TZ, trustlets, XBL, or ABL — brick territory.
references/edl-pixelexists 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
onlineattempt 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-ralone); never addpmic@4/pmic@5SPMI nodes; sudo pw147147.
On link-in
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.