Watch
1
0
Fork
You've already forked SouveraineOS
0
SouveraineOS/saf/device/evidence/tier1_los_vs_mainline_diff.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

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_setupSKIPPED 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

  1. 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.

  2. DELTA 4 — SCM buffer allocation (tzmem vs kzalloc). If qcom_tzmem allocates from a region TZ can't read, every SCM call fails. Would affect ALL assign calls, not just modem.

  3. DELTA 3 — Metadata reserved region. Mainline uses a fixed mdata_mem at alloc-ranges 0xa0000000. 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.

  4. DELTA 8 — No qcom,mem-protect-id in mainline DT. LOS reads VMID 0xF from DT. Mainline hardcodes need_mem_protection=true and uses QCOM_SCM_VMID_MSS_MSA (also 0xF). 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.