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
392 lines
14 KiB
C
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.
|
|
*/
|