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
11 KiB
PAF Tier 1: LOS pil-q6v5-mss (4.9.337) vs Mainline qcom_q6v5_mss (7.1.1) — Complete Diff
Date: 2026-06-27
Source LOS: references/los-kernel-blueline-4.9/drivers/soc/qcom/{pil-msa.c, peripheral-loader.c, pil-q6v5-mss.c, secure_buffer.c}
Source Mainline: Downloads/mobian-sdm845-7.1/drivers/remoteproc/qcom_q6v5_mss.c + drivers/firmware/qcom/qcom_scm.c
Live Android capture: 4.9.337 cold boot, 89JX0AAJP, 2026-06-27
Live Android ground truth (captured this session)
pil-q6v5-mss 4080000.qcom,mss: modem: loading from 0x8e000000 to 0x97800000 (MPSS 152MB)
pil-q6v5-mss 4080000.qcom,mss: Loading MBA and DP from 0x98500000 to 0x98600000 (MBA 1MB)
pil-q6v5-mss 4080000.qcom,mss: MBA boot done
pil-q6v5-mss 4080000.qcom,mss: modem: Brought out of reset
Zero hyp_assign errors. Zero xpu. Zero -22. The modem boots clean on Android.
Running Android reserved-memory (from /sys/firmware/devicetree/base/reserved-memory/):
| Region | Address | Size |
|---|---|---|
| modem (MPSS) | 0x8e000000 |
0x9800000 (152MB) |
| MBA | 0x98500000 |
0x200000 (2MB) |
| SLPI | 0x98700000 |
0x1400000 (20MB) |
| video | 0x97800000 |
0x500000 (5MB) |
| CDSP | 0x97d00000 |
0x800000 (8MB) |
These match the mainline sdm845-google-common.dtsi override exactly. TZ accepts this layout.
Area 1: Driver boot sequence — MBA load + MPSS load
MBA load
| Step | LOS (pil_mss_reset_load_mba) |
Mainline (q6v5_mba_load) |
|---|---|---|
| 1 | pil_notify_aop("on") |
q6v5_load_state_toggle(true) via QMP — same message |
| 2 | Enable proxy clocks/regulators | Enable proxy clocks/regulators + power-domains |
| 3 | dma_alloc_attrs(mba_mem_dev_fixed, ...) — alloc from reserved pil_mba_mem at 0x98500000 |
memremap(mba_phys) — directly maps reserved mba_region at 0x98500000 |
| 4 | memcpy MBA fw into buffer |
memcpy MBA fw into mapped region |
| 5 | — | q6v5_xfer_mem_ownership(&mpss_perm, false, true, mpss_phys, mpss_size) — HLOS->MSS for 152MB MPSS |
| 6 | pil_assign_mem_to_subsys(mba_dp_phys, mba_dp_size) — HLOS->MSS for MBA only |
q6v5_xfer_mem_ownership(&mba_perm, false, true, mba_phys, mba_size) — HLOS->MSS for MBA |
| 7 | Trigger boot FSM, wait PBL + MBA XPU_UNLOCKED |
Trigger boot FSM, wait PBL + MBA XPU_UNLOCKED |
DELTA 1 (step 5): Mainline pre-assigns the ENTIRE 152MB MPSS region to MSS before MBA boots. LOS does not touch MPSS until after MBA boots and segment loading begins. This is a 152MB hyp-assign call that LOS never makes at this stage.
MPSS load (segment loading + final handoff)
| Step | LOS (pil_boot in peripheral-loader.c) |
Mainline (q6v5_mpss_load) |
|---|---|---|
| 1 | init_image (MBA authenticates metadata) |
q6v5_mpss_init_image (MBA authenticates metadata) |
| 2 | mem_setup (MBA sets up MPSS memory) |
qcom_scm_pas_mem_setup — SKIPPED for SDM845 (need_pas_mem_setup=false) |
| 3 | SSR: pil_assign_mem_to_linux(region) — reclaim from MSS back to HLOS |
SSR: q6v5_xfer_mem_ownership(true, false) — reclaim |
| 4 | pil_assign_mem_to_subsys_and_linux(region) — src={HLOS}, dest={HLOS+MSS}, both RW |
q6v5_xfer_mem_ownership(true, true) — shared HLOS+MSS |
| 5 | Load segments | Load segments |
| 6 | pil_reclaim_mem(region, subsys_vmid) — src={HLOS,MSS}, dest={MSS only} |
q6v5_xfer_mem_ownership(false, true) — MSS only |
| 7 | auth_and_reset |
Wait RMB_MBA_AUTH_COMPLETE |
DELTA 2 (step 3-4): Cold boot ordering difference. On first boot (non-SSR), LOS skips step 3 (comment: "linux already owns it") and goes straight to step 4 (shared). Mainline at step 3 does q6v5_xfer_mem_ownership(&mpss_perm, true, false, ...) — but mpss_perm was already set to BIT(MSS_MSA) by the pre-assign in DELTA 1. So mainline's step 3 is MSS->HLOS (reclaim), then step 4 is HLOS->{HLOS+MSS} (share). LOS goes straight from HLOS->{HLOS+MSS}. The net result should be the same IF the pre-assign succeeded, but if DELTA 1 failed with -22, mpss_perm is still BIT(HLOS) and step 3 would be a no-op (already HLOS-owned), then step 4 works. Except — the pre-assign failure should have already aborted q6v5_mba_load.
Metadata authentication
| Step | LOS (pil_msa_mss_reset_mba_load_auth_mdt) |
Mainline (q6v5_mpss_init_image) |
|---|---|---|
| buffer | dma_alloc_attrs — dynamic |
From mdata_mem reserved region if present, else dma_alloc_attrs |
| assign | pil_assign_mem_to_subsys(mdata, size) — HLOS->MSS |
q6v5_xfer_mem_ownership(false, true, phys, size) — HLOS->MSS |
| auth | Write to RMB, wait META_DATA_AUTH_SUCCESS |
Write to RMB, wait META_DATA_AUTH_SUCCESS |
| reclaim | pil_assign_mem_to_linux(mdata) — MSS->HLOS |
q6v5_xfer_mem_ownership(true, false) — MSS->HLOS |
DELTA 3: Mainline has a dedicated mdata_mem reserved region (alloc-ranges 0xa0000000, 16KB, no-map). LOS uses a dynamic DMA allocation. If TZ doesn't know about this mdata_mem range (it's an alloc-ranges not a fixed reg), the assign could fail.
Area 2: SCM / secure-channel path
The SCM call itself
| LOS | Mainline | |
|---|---|---|
| SCM SVC/CMD | SCM_SVC_MP (0x0C) / MEM_PROT_ASSIGN_ID (0x16) |
QCOM_SCM_SVC_MP (0x0C) / QCOM_SCM_MP_ASSIGN (0x16) |
| Call convention | scm_call2(SCM_SIP_FNID(...), &desc) — 7-arg SIP |
qcom_scm_call(dev, &desc, &res) via scm_smc_call — 7-arg SIP |
| VMID_HLOS | 0x3 |
0x3 |
| VMID_MSS_MSA | 0xF |
0xF |
| Perms | PERM_READ=4, PERM_WRITE=2, PERM_EXEC=1 |
QCOM_SCM_PERM_READ=4, WRITE=2, EXEC=1 |
| Dest struct | {u32 vm, u32 perm, u64 ctx, u32 ctx_size} (20B, pads to 24B on arm64) |
{__le32 vmid, __le32 perm, __le64 ctx, __le32 ctx_size, __le32 unused} (24B) |
| Src struct | mem_prot_info {phys_addr_t addr, u64 size} |
qcom_scm_mem_map_info {__le64 mem_addr, __le64 mem_size} |
| Buffer alloc | kzalloc + virt_to_phys + dmac_flush_range |
qcom_tzmem_alloc from dedicated TZ memory pool |
| Endianness | native (cpu-endian u32/u64) | explicit cpu_to_le32/cpu_to_le64 |
DELTA 4: SCM buffer memory source. LOS uses kzalloc (normal kernel memory) + virt_to_phys for the SCM descriptor buffers. Mainline uses qcom_tzmem_alloc from a special TZ-visible memory pool. If qcom_tzmem is misconfigured or the pool isn't set up correctly on SDM845, TZ can't read the descriptor and returns -22.
DELTA 5: Endianness handling. LOS writes native-endian u32/u64 to the descriptor buffers. Mainline uses cpu_to_le32/cpu_to_le64. On arm64 (little-endian), these are no-ops. Not a functional difference.
DELTA 6: src (mem_to_map) struct. LOS: {phys_addr_t addr, u64 size} — on arm64 this is {u64 addr, u64 size} = 16 bytes. Mainline: {__le64 mem_addr, __le64 mem_size} = 16 bytes. Same wire format.
Area 3: DT — reserved-memory, power-domains, smem-states
Reserved memory
| Region | LOS DTS (sdm845.dtsi) |
Mainline (sdm845-google-common.dtsi) |
Live Android |
|---|---|---|---|
| MPSS | 0x8e000000, 0x7800000 (120MB) |
0x8e000000, 0x9800000 (152MB) |
0x8e000000, 0x9800000 (152MB) |
| MBA | 0x96500000, 0x200000 (2MB) |
0x98500000, 0x200000 (2MB) |
0x98500000, 0x200000 (2MB) |
| SLPI | 0x96700000, 0x1400000 (20MB) |
0x98700000, 0x1400000 (20MB) |
0x98700000, 0x1400000 (20MB) |
| Metadata | (none — uses dma_alloc) | alloc-ranges 0xa0000000, 0x4000 (16KB), no-map |
N/A |
The LOS DTS has different BASE ADDRESSES from what Android actually uses. The google-common override produces the correct values that match the live device.
DELTA 7: LOS DTS says 120MB MPSS, but Android actually runs with 152MB. The google-common override (which LOS applies via board-level .dtsi) matches. Mainline has the same override. Geometry is the same in practice.
DELTA 8: Mainline has a mdata_mem reserved region. LOS does not. Mainline's q6v5_mpss_init_image checks for a metadata child node with memory-region and uses that reserved region for metadata. If absent, falls back to dma_alloc_attrs. LOS always uses DMA. If the mdata_mem alloc-ranges region (0xa0000000) is not TZ-known, the metadata assign would fail. But q6v5_mpss_init_image is called AFTER MBA boots, so this wouldn't cause the MBA-level failure.
MSS DT node differences
| Property | LOS | Mainline |
|---|---|---|
| compatible | qcom,pil-q6v55-mss |
qcom,sdm845-mss-pil |
| reg | 8 regions (qdsp6, halt_q6/modem/nc, rmb, restart, pdc_sync, alt_reset) | 2 regions (qdsp6, rmb) — halts via syscon, reset via reset-controller |
memory-region |
<&pil_modem_mem> on the node itself |
Via mpss child node |
qcom,mem-protect-id |
<0xF> — read by peripheral-loader.c:912 into subsys_vmid |
ABSENT — mainline uses hardcoded need_mem_protection=true in platform descriptor |
| Regulators | vdd_cx, vdd_mx, vdd_mss as direct supplies with TURBO voltage |
power-domains via rpmhpd (cx, mx, mss) |
| AOP | qcom,signal-aop + mboxes = <&qmp_aop 0> |
qcom,qmp = <&aoss_qmp> — same path, different binding |
qcom,pil-self-auth |
present | ABSENT — mainline always self-auths |
qcom,override-acc |
present | ABSENT — mainline handles ACC differently |
qcom,qdsp6v65-1-0 |
present (selects q6v65 reset sequence) | ABSENT — mainline selects via version = MSS_SDM845 |
qcom,mss_pdc_offset |
<8> |
ABSENT — mainline uses pdc_reset reset-controller |
| MBA child | qcom,mba-mem@0 with qcom,pil-mba-mem compatible + memory-region |
mba child with just memory-region |
| Metadata child | (none) | metadata child with memory-region = <&mdata_mem> |
Summary of deltas, ranked by likelihood of causing the -22
-
DELTA 1 — MPSS pre-assign before MBA boot. Mainline assigns 152MB MPSS to MSS before MBA boots. LOS does not. This is a large, early hyp-assign that LOS avoids entirely. If this returns -22, the modem never starts. This is the most likely trigger — it's the first hyp-assign call mainline makes that LOS doesn't, and it's for the biggest region.
-
DELTA 4 — SCM buffer allocation (tzmem vs kzalloc). If
qcom_tzmemallocates from a region TZ can't read, every SCM call fails. Would affect ALL assign calls, not just modem. -
DELTA 3 — Metadata reserved region. Mainline uses a fixed
mdata_memat alloc-ranges0xa0000000. LOS uses dynamic DMA. If TZ doesn't know about this range, metadata auth assign fails. But this is after MBA boot, so it wouldn't cause the PBL-level failure. -
DELTA 8 — No
qcom,mem-protect-idin mainline DT. LOS reads VMID0xFfrom DT. Mainline hardcodesneed_mem_protection=trueand usesQCOM_SCM_VMID_MSS_MSA(also0xF). Functionally equivalent.
The test: Remove the MPSS pre-assign from q6v5_mba_load (lines 1236-1241 in qcom_q6v5_mss.c) — let MBA boot with only the MBA region assigned, matching LOS behavior. The MPSS assign happens later in q6v5_mpss_load anyway. If this clears the -22, DELTA 1 was the gate.