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

392 lines
14 KiB
C

/*
* LOS pil-msa.c / peripheral-loader.c modem boot sequence
* TRANSLATED to mainline qcom_q6v5_mss.c coding style.
*
* Purpose: make the downstream boot ordering readable so the delta
* with mainline is obvious. Not compilable — a reference document.
*
* LEGEND:
* [SAME] — mainline does the same thing
* [DELTA] — mainline does something different (the interesting part)
* [EXTRA] — downstream does this, mainline does not
* [MISS] — mainline does this, downstream does not
*
* Source: los-kernel-blueline-4.9/drivers/soc/qcom/pil-msa.c
* los-kernel-blueline-4.9/drivers/soc/qcom/peripheral-loader.c
* Target: linux-7.1.1-sdm845/drivers/remoteproc/qcom_q6v5_mss.c
*/
/* ------------------------------------------------------------------ */
/* DOWNSTREAM MEMORY ASSIGNMENT HELPERS (translated to mainline API) */
/* ------------------------------------------------------------------ */
/*
* [DELTA] Downstream uses hyp_assign_phys() from secure_buffer.c which
* allocates its argument buffers via kzalloc + dmac_flush_range.
* Mainline uses qcom_scm_assign_mem() which allocates from the
* qcom_tzmem pool. Same SCM call (SVC=0x0C CMD=0x16), different
* buffer provenance. Unlikely to matter but noted.
*/
/* HLOS → MSS exclusively */
static int los_assign_to_subsys(struct q6v5 *qproc,
phys_addr_t addr, size_t size)
{
struct qcom_scm_vmperm dest = {
.vmid = QCOM_SCM_VMID_MSS_MSA, /* 0xF */
.perm = QCOM_SCM_PERM_RW,
};
u64 src = BIT(QCOM_SCM_VMID_HLOS);
return qcom_scm_assign_mem(addr, ALIGN(size, SZ_4K), &src, &dest, 1);
}
/* MSS → HLOS exclusively (panics on failure in LOS — they're serious) */
static int los_assign_to_linux(struct q6v5 *qproc,
phys_addr_t addr, size_t size)
{
struct qcom_scm_vmperm dest = {
.vmid = QCOM_SCM_VMID_HLOS,
.perm = QCOM_SCM_PERM_RWX,
};
u64 src = BIT(QCOM_SCM_VMID_MSS_MSA);
return qcom_scm_assign_mem(addr, ALIGN(size, SZ_4K), &src, &dest, 1);
}
/* HLOS → HLOS+MSS shared */
static int los_assign_shared(struct q6v5 *qproc,
phys_addr_t addr, size_t size)
{
struct qcom_scm_vmperm dest[2] = {
{ .vmid = QCOM_SCM_VMID_HLOS, .perm = QCOM_SCM_PERM_RW },
{ .vmid = QCOM_SCM_VMID_MSS_MSA, .perm = QCOM_SCM_PERM_RW },
};
u64 src = BIT(QCOM_SCM_VMID_HLOS);
return qcom_scm_assign_mem(addr, ALIGN(size, SZ_4K), &src, dest, 2);
}
/* HLOS+MSS → target_vmid exclusively (the final handoff) */
static int los_reclaim_mem(struct q6v5 *qproc,
phys_addr_t addr, size_t size, int target_vmid)
{
struct qcom_scm_vmperm dest = {
.vmid = target_vmid,
.perm = (target_vmid == QCOM_SCM_VMID_HLOS)
? QCOM_SCM_PERM_RWX : QCOM_SCM_PERM_RW,
};
u64 src = BIT(QCOM_SCM_VMID_HLOS) | BIT(QCOM_SCM_VMID_MSS_MSA);
return qcom_scm_assign_mem(addr, ALIGN(size, SZ_4K), &src, &dest, 1);
}
/* ------------------------------------------------------------------ */
/* THE BOOT SEQUENCE (downstream pil_boot + pil_msa_mss_ops_selfauth) */
/* ------------------------------------------------------------------ */
/*
* This is the FULL modem cold-boot path on LOS blueline, translated
* to mainline style. It's called like mainline's q6v5_start().
*
* Downstream splits this across:
* pil_boot() → ops->init_image() → ops->mem_setup() →
* segment load → ops->auth_and_reset()
*
* where init_image = pil_msa_mss_reset_mba_load_auth_mdt()
* mem_setup = pil_mss_mem_setup() [no-op on SDM845]
* auth_and_reset = pil_msa_mba_auth()
*/
static int los_q6v5_start(struct rproc *rproc)
{
struct q6v5 *qproc = rproc->priv;
dma_addr_t mba_phys;
void *mba_virt;
dma_addr_t mdata_phys;
void *mdata_virt;
int ret;
/* ============================================================
* PHASE 1: Load MBA (from pil_mss_reset_load_mba)
* ============================================================ */
/*
* [DELTA] MBA buffer: downstream allocates a DMA buffer from
* general DDR. Mainline uses the static reserved-memory node
* mba@98500000 (2 MB). Both work for MBA load, but TZ may
* treat them differently for hyp-assign. On blueline, the
* mainline approach works (MBA boots, no -22).
*/
mba_virt = dma_alloc_attrs(qproc->dev, SZ_1M, &mba_phys,
GFP_KERNEL, DMA_ATTR_SKIP_ZEROING);
memcpy(mba_virt, mba_fw->data, mba_fw->size);
wmb();
/* Load debug policy if present (same in both) */
/* [SAME] */
/*
* [SAME] Assign MBA buffer to MSS so PBL can read it.
* Downstream: pil_assign_mem_to_subsys(mba_dp_phys, mba_dp_size)
* Mainline: q6v5_xfer_mem_ownership(mba_perm, false, true, mba_phys, mba_size)
*/
if (qproc->need_mem_protection) {
ret = los_assign_to_subsys(qproc, mba_phys, SZ_1M);
if (ret)
goto err_mba;
}
/*
* >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
* [DELTA — THE KEY DIFFERENCE]
*
* Downstream does NOT pre-assign the MPSS region here.
* Mainline (before our fix) did:
* q6v5_xfer_mem_ownership(mpss_perm, false, true,
* mpss_phys, mpss_size);
* This 120-152 MB exclusive HLOS→MSS assignment happens BEFORE
* MBA boots, and blueline TZ may reject it (-22), causing the
* XPU crash-loop.
*
* Our commit c2923cb49 gates this on (version != MSS_SDM845),
* matching downstream. VERIFIED: modem boots clean on 7.1.1.
* <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
*/
/* [SAME] Power up MSS, enable clocks, assert/deassert reset */
ret = pil_mss_power_up(qproc);
ret = pil_mss_enable_clks(qproc);
pil_mss_assert_resets(qproc);
udelay(200);
pil_mss_deassert_resets(qproc);
/* [SAME] Program MBA address into RMB register */
writel(mba_phys, qproc->rmb_base + RMB_MBA_IMAGE);
mb();
/* [SAME] Bring Q6 out of reset */
pil_q6v5_reset(qproc);
/* [SAME] Wait for PBL success, then XPU_UNLOCKED from MBA */
ret = q6v5_rmb_pbl_wait(qproc, 1000);
ret = q6v5_rmb_mba_wait(qproc, RMB_MBA_XPU_UNLOCKED, 1000);
dev_info(qproc->dev, "MBA boot done\n");
/* ============================================================
* PHASE 2: Auth modem metadata (from pil_msa_auth_modem_mdt)
* ============================================================ */
/*
* [DELTA] Metadata buffer: downstream allocates a DMA buffer,
* copies metadata, assigns to MSS, programs RMB, waits for
* META_DATA_AUTH_SUCCESS, then reclaims to Linux.
*
* Mainline (q6v5_mpss_init_image) does essentially the same
* but uses the mpss-metadata reserved-memory region instead
* of a DMA allocation. Functionally equivalent.
*/
mdata_virt = dma_alloc_attrs(qproc->dev, mdata_size, &mdata_phys,
GFP_KERNEL, DMA_ATTR_SKIP_ZEROING);
memcpy(mdata_virt, mdt_metadata, mdata_size);
wmb();
if (qproc->need_mem_protection)
los_assign_to_subsys(qproc, mdata_phys, mdata_size);
writel(0, qproc->rmb_base + RMB_PMI_CODE_LENGTH_REG);
writel(mdata_phys, qproc->rmb_base + RMB_PMI_META_DATA_REG);
writel(CMD_META_DATA_READY, qproc->rmb_base + RMB_MBA_COMMAND_REG);
ret = q6v5_rmb_mba_wait(qproc, RMB_MBA_META_DATA_AUTH_SUCCESS, 10000);
if (qproc->need_mem_protection)
los_assign_to_linux(qproc, mdata_phys, mdata_size);
dma_free_attrs(qproc->dev, mdata_size, mdata_virt, mdata_phys, 0);
/* ============================================================
* PHASE 3: mem_setup (from pil_mss_mem_setup)
* ============================================================ */
/*
* [SAME] SDM845 does NOT set pil_mss_memsetup in DT.
* Downstream: this function returns 0 immediately.
* Mainline: need_pas_mem_setup = false for SDM845.
* Both skip PAS_MEM_SETUP_CMD. No delta.
*/
/* ============================================================
* PHASE 4: MPSS region assignment + segment loading
* (from pil_boot lines 1063-1119)
* ============================================================ */
/*
* [SAME on cold boot, DELTA on SSR]
*
* Cold boot (modem_ssr == false):
* - Skip the reclaim-to-linux (MPSS is already Linux-owned)
* - Go straight to shared assignment
*
* SSR (modem_ssr == true):
* - First reclaim: MSS→HLOS exclusively
* - Then share: HLOS→HLOS+MSS
*
* Mainline does the reclaim unconditionally (lines 1504-1505)
* but on cold boot it's a no-op because mpss_perm already
* equals HLOS. So functionally equivalent on cold boot.
*/
if (modem_ssr) {
/* SSR only: reclaim from previous boot */
los_assign_to_linux(qproc, qproc->mpss_phys, qproc->mpss_size);
}
/*
* >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
* [SAME — this matches mainline line 1508]
*
* Share MPSS between HLOS and MSS during segment loading.
* Downstream: pil_assign_mem_to_subsys_and_linux()
* srcVM = [HLOS], destVM = [HLOS, MSS_MSA], perm = [RW, RW]
* Mainline: q6v5_xfer_mem_ownership(mpss_perm, true, true, ...)
* srcVM from current_perm, destVM = [HLOS, MSS_MSA]
* Same SCM call. VERIFIED matching.
* <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
*/
ret = los_assign_shared(qproc, qproc->mpss_phys, qproc->mpss_size);
if (ret)
goto err_mpss;
/* [SAME] Load each MPSS segment into the shared region */
for_each_segment(seg) {
ptr = memremap(qproc->mpss_phys + offset, seg->memsz,
MEMREMAP_WC);
/* copy from firmware or zero-fill */
memcpy(ptr, seg_data, seg->filesz);
memset(ptr + seg->filesz, 0, seg->memsz - seg->filesz);
memunmap(ptr);
/* [SAME] Program RMB for MBA to verify each segment */
if (!code_length) {
writel(boot_addr, qproc->rmb_base + RMB_PMI_CODE_START_REG);
writel(RMB_CMD_LOAD_READY, qproc->rmb_base + RMB_MBA_COMMAND_REG);
}
code_length += seg->memsz;
writel(code_length, qproc->rmb_base + RMB_PMI_CODE_LENGTH_REG);
}
/* ============================================================
* PHASE 5: Final MPSS handoff + auth complete
* (from pil_boot lines 1108-1127)
* ============================================================ */
/*
* >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
* [DELTA — SUBTLE DIFFERENCE IN THE FINAL HANDOFF]
*
* Downstream: pil_reclaim_mem(region, subsys_vmid)
* srcVM = [HLOS, MSS_MSA] (both listed explicitly)
* destVM = [MSS_MSA]
* This transfers from shared → MSS exclusive.
*
* Mainline: q6v5_xfer_mem_ownership(mpss_perm, false, true, ...)
* srcVM = from current_perm (which is HLOS|MSS after the share)
* destVM = [MSS_MSA]
* Same result — but mainline tracks perm state in mpss_perm
* while downstream hardcodes the srcVM.
*
* FUNCTIONALLY EQUIVALENT. The end state (MSS exclusive) is
* the same. VERIFIED.
* <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
*/
ret = los_reclaim_mem(qproc, qproc->mpss_phys, qproc->mpss_size,
QCOM_SCM_VMID_MSS_MSA);
if (ret)
goto err_mpss;
/* [SAME] Wait for MBA to report authentication complete */
ret = q6v5_rmb_mba_wait(qproc, RMB_MBA_AUTH_COMPLETE, 10000);
/* ============================================================
* PHASE 6: Reclaim MBA + cleanup
* (from pil_msa_mba_auth)
* ============================================================ */
/*
* [DELTA] Downstream reclaims MBA DMA buffer: MSS→HLOS, then
* frees the DMA allocation. Mainline reclaims the mba_region
* reserved-memory. Same assign direction, different region type.
*/
if (qproc->need_mem_protection)
los_assign_to_linux(qproc, mba_phys, SZ_1M);
dma_free_attrs(qproc->dev, SZ_1M, mba_virt, mba_phys, 0);
return 0;
err_mpss:
err_mba:
return ret;
}
/* ================================================================== */
/* SUMMARY OF DELTAS */
/* ================================================================== */
/*
* 1. MBA BUFFER LOCATION
* Downstream: dma_alloc_attrs() — general DDR, dynamic
* Mainline: reserved-memory mba@98500000 — static carveout
* Impact: None observed — MBA boots fine both ways.
*
* 2. MPSS PRE-ASSIGN (the one we fixed)
* Downstream: NEVER assigns MPSS before MBA boot
* Mainline: Was HLOS→MSS exclusively before MBA boot
* Fix: c2923cb49 skips for MSS_SDM845
* Status: VERIFIED WORKING — modem boots clean, no -22
*
* 3. METADATA BUFFER LOCATION
* Downstream: dma_alloc_attrs() — general DDR
* Mainline: reserved-memory mpss-metadata@bfffc000 (16 KB)
* Impact: None observed.
*
* 4. rmtfs REGION SCM ASSIGN
* Downstream: NO SCM call — uses qcom,sharedmem-uio, no TZ
* Mainline: qcom_scm_assign_mem(0xf2700000, 0x202000,
* HLOS→HLOS+MSS_MSA)
* Impact: Assign succeeds on blueline (verified). But this
* EXTRA TZ interaction doesn't exist downstream.
*
* 5. FASTRPC / SLPI VMIDs
* Downstream: Not applicable (different fastrpc driver)
* Mainline: qcom_scm_assign_mem for fastrpc heap with
* [HLOS, SSC_Q6] — TZ rejects with -22
* Impact: SLPI crash-loops. Independent of modem.
*
* 6. OPERATING MODE AT BOOT
* Downstream: modem firmware enters LPM at boot
* Mainline: modem firmware enters OFFLINE at boot
* Impact: THIS IS THE REMAINING BLOCKER.
* The driver-level boot sequence is now correct
* (matching downstream ordering). The firmware
* itself decides LPM vs OFFLINE based on... what?
* NOT the driver — the assign ordering is matching.
* Candidates:
* - ABL/bootloader state (verified-boot chain)
* - QCRIL "UI ready" handshake (rejected by firmware)
* - Some EFS NV item the firmware checks at RF-init
* - Subsystem coordination (SLPI PD state?)
*
* CONCLUSION:
* The driver-level memory assignment is now correct. The modem boots
* clean with zero XPU/assign errors. The remaining 52/offline blocker
* is ABOVE the driver layer — in the modem firmware's own RF-init
* decision logic.
*/