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
259 lines
18 KiB
Markdown
259 lines
18 KiB
Markdown
# 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-untrusted` →
|
||
`boot-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
|
||
1–2 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 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-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
|
||
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/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`.
|
||
|
||
## 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.
|