diff options
| author | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-10-01 08:13:34 -0400 |
|---|---|---|
| committer | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-10-01 08:13:56 -0400 |
| commit | 8f1187e6ebff5e2664d19e3e4c55af2e0f6205b4 (patch) | |
| tree | ddf5075a63c5b0056b08c7577c8d8cab2e3bcfb8 /kernel/gsp_phys_fwsec.c | |
| parent | b0f76b3a2b38f9e231cb71c88446a8ba91d2c586 (diff) | |
| download | open-iov-8f1187e6ebff5e2664d19e3e4c55af2e0f6205b4.tar.gz open-iov-8f1187e6ebff5e2664d19e3e4c55af2e0f6205b4.zip | |
gsp-phys: fix the booter-unload handoff seed (correct scratch register/access)
Diffstat (limited to 'kernel/gsp_phys_fwsec.c')
| -rw-r--r-- | kernel/gsp_phys_fwsec.c | 188 |
1 files changed, 171 insertions, 17 deletions
diff --git a/kernel/gsp_phys_fwsec.c b/kernel/gsp_phys_fwsec.c index 3c63a95..1fda977 100644 --- a/kernel/gsp_phys_fwsec.c +++ b/kernel/gsp_phys_fwsec.c @@ -151,6 +151,35 @@ bool gsp_phys_fwsec_dryrun_enabled(void) #define GSP_PHYS_SCRATCH14_HANDOFF_MASK 0x0f000000u #define GSP_PHYS_SCRATCH14_HANDOFF_SHIFT 24 #define GSP_PHYS_SCRATCH14_BOOTER_VERSION_TU10X 0u +/* + * Every BSI secure scratch has a companion privilege-level mask. The scratch + * is write-protected to level3 (the SEC2/HS context) by default, so a bare + * host write is silently dropped: opening WRITE_PROTECTION for the host level + * is required before seeding, and the mask is restored afterwards. The BSI + * scratch PLM is at PRIV_LEVEL_MASK(i) = 0x118080 + i*4; scratch 14 -> 0xb8. + */ +#define GSP_PHYS_BSI_VPR_SCRATCH14_PLM 0x001180b8 +#define GSP_PHYS_SCRATCH_PLM_ALL_LEVELS 0x000000ffu +/* + * PFB MMU WPR window (host-portable port of the booter unlock) and the MMU + * privilege mask/fuse that gate host writes to it. + */ +#define GSP_PHYS_PFB_MMU_PRIV_LEVEL_MASK 0x00100cdc +#define GSP_PHYS_PFB_MMU_WPR_ALLOW_READ 0x001fa814 +#define GSP_PHYS_PFB_MMU_WPR_ALLOW_WRITE 0x001fa818 +#define GSP_PHYS_PFB_MMU_WPR2_ALLOW_MASK 0x00000f00u +#define GSP_PHYS_FUSE_SECURE_MMU_PLM_WR_SECURE 0x00021198 +#define GSP_PHYS_FALCON_SEC_CFGA(i) (0x001f9040u + (i) * 8u) +#define GSP_PHYS_FALCON_SEC_CFGB(i) (0x001f9044u + (i) * 8u) +#define GSP_PHYS_FALCON_SEC_SUBWPR_COUNT 8 +#define GSP_PHYS_FALCON_GSP_CFGA(i) (0x001f9080u + (i) * 8u) +#define GSP_PHYS_FALCON_GSP_CFGB(i) (0x001f9084u + (i) * 8u) +#define GSP_PHYS_FALCON_GSP_SUBWPR_COUNT 4 +#define GSP_PHYS_FALCON_NVDEC0_CFGA(i) (0x001f91c0u + (i) * 8u) +#define GSP_PHYS_FALCON_NVDEC0_CFGB(i) (0x001f91c4u + (i) * 8u) +#define GSP_PHYS_NVDEC0_SUBWPR_GSPRM_BOOTER_WPR2 3 +#define GSP_PHYS_SUBWPR_INVALID_LO 0xfffffff0u +#define GSP_PHYS_SUBWPR_INVALID_HI 0x00000000u #define GSP_PHYS_GFW_PLM 0x118128 #define GSP_PHYS_GFW_PLM_LEVEL0 BIT(0) #define GSP_PHYS_RISCV_STATUS 0x111240 @@ -974,38 +1003,88 @@ static void gsp_phys_fwsec_ucode_free(struct pci_dev *pdev, } /* + * Open every privilege level on a PLM by setting its READ/WRITE protection + * nibbles; the caller restores the original value. Returns true when the + * readback shows the requested protection bits took (a bare write is dropped + * when the mask is fuse-locked, e.g. mmu_plm for the MMU PLM). + */ +static bool gsp_phys_plm_open(void __iomem *bar0, u32 addr, u32 *orig) +{ + u32 before = readl(bar0 + addr); + u32 after; + + writel((before & ~GSP_PHYS_SCRATCH_PLM_ALL_LEVELS) | + GSP_PHYS_SCRATCH_PLM_ALL_LEVELS, bar0 + addr); + after = readl(bar0 + addr); + *orig = before; + return (after & GSP_PHYS_SCRATCH_PLM_ALL_LEVELS) == + GSP_PHYS_SCRATCH_PLM_ALL_LEVELS; +} + +/* * Seed the VPR secure-scratch sequence state the Booter Unload ucode gates on * at entry. In the reference lifecycle those bits are produced by Booter Load, * AHESASC, Booter Reload and ACR Unload; a FWSEC-only WPR2 carve (stage 3c) * never runs those ucodes, so the unload refuses with its sequence/handoff * error (observed SEC2 M0=0x29) before it touches WPR2 and WPR2 stays up. - * Setting the four handoff bits DONE and the Booter binary version to the - * Turing build version (0) restores the state Booter Load would have left so - * the unload can run; the unload itself clears both fields on completion. - * The WPR meta is not consumed by the unload path at all (it only resets the - * GSP engine, invalidates the sub-WPRs and locks WPR2 down). + * The scratch is write-protected to level3 by its companion PLM, so the host + * write only sticks after the PLM's WRITE_PROTECTION is opened; the original + * mask is restored before returning. The WPR meta is not consumed by the + * unload path at all (it only resets the GSP engine, invalidates the sub-WPRs + * and unlocks WPR2). */ -static void gsp_phys_booter_seed_sequence_state(void __iomem *bar0) +static bool gsp_phys_booter_seed_sequence_state(void __iomem *bar0) { - u32 before, after; + u32 plm_addr = GSP_PHYS_BSI_VPR_SCRATCH14_PLM; + u32 scratch_addr = GSP_PHYS_BSI_VPR_SECURE_SCRATCH_14; + u32 plm_orig, before, after, readback; + bool opened, seeded; + + before = readl(bar0 + scratch_addr); + opened = gsp_phys_plm_open(bar0, plm_addr, &plm_orig); + if (opened) + before = readl(bar0 + scratch_addr); - before = readl(bar0 + GSP_PHYS_BSI_VPR_SECURE_SCRATCH_14); after = before & ~(GSP_PHYS_SCRATCH14_BOOTER_VERSION_MASK | GSP_PHYS_SCRATCH14_HANDOFF_MASK); after |= GSP_PHYS_SCRATCH14_BOOTER_VERSION_TU10X << GSP_PHYS_SCRATCH14_BOOTER_VERSION_SHIFT; after |= GSP_PHYS_SCRATCH14_HANDOFF_MASK; - writel(after, bar0 + GSP_PHYS_BSI_VPR_SECURE_SCRATCH_14); - - pr_info("gsp-phys-fwsec: booter sequence seed: scratch14 0x%08x -> 0x%08x (booterVersion=%u want %u, handoffs 0x%x)\n", - before, readl(bar0 + GSP_PHYS_BSI_VPR_SECURE_SCRATCH_14), - (readl(bar0 + GSP_PHYS_BSI_VPR_SECURE_SCRATCH_14) & - GSP_PHYS_SCRATCH14_BOOTER_VERSION_MASK) >> + if (opened) + writel(after, bar0 + scratch_addr); + readback = readl(bar0 + scratch_addr); + + /* Restore the scratch's original access protection. */ + writel(plm_orig, bar0 + plm_addr); + seeded = opened && + (readback & (GSP_PHYS_SCRATCH14_BOOTER_VERSION_MASK | + GSP_PHYS_SCRATCH14_HANDOFF_MASK)) == + (after & (GSP_PHYS_SCRATCH14_BOOTER_VERSION_MASK | + GSP_PHYS_SCRATCH14_HANDOFF_MASK)); + + pr_info("gsp-phys-fwsec: booter sequence seed: PLM@0x%x 0x%08x (host-open=%s, restored 0x%08x), scratch14 0x%08x -> 0x%08x (booterVersion=%u want %u, handoffs 0x%x) -> %s\n", + plm_addr, plm_orig, opened ? "yes" : "no", + readl(bar0 + plm_addr), before, readback, + (readback & GSP_PHYS_SCRATCH14_BOOTER_VERSION_MASK) >> GSP_PHYS_SCRATCH14_BOOTER_VERSION_SHIFT, GSP_PHYS_SCRATCH14_BOOTER_VERSION_TU10X, - (readl(bar0 + GSP_PHYS_BSI_VPR_SECURE_SCRATCH_14) & - GSP_PHYS_SCRATCH14_HANDOFF_MASK) >> - GSP_PHYS_SCRATCH14_HANDOFF_SHIFT); + (readback & GSP_PHYS_SCRATCH14_HANDOFF_MASK) >> + GSP_PHYS_SCRATCH14_HANDOFF_SHIFT, + seeded ? "SEEDED" : "NOT SEEDED (host write blocked)"); + + if (!seeded) + pr_warn("gsp-phys-fwsec: scratch14 write protection is not host-open; the booter unload handoff gate cannot be satisfied in a FWSEC-only carve\n"); + return seeded; +} + +/* + * Invalidate one falcon sub-WPR (reference s_booterGetSubWprAddrVals with + * bValid=false: ADDR_LO=0xFFFFFFFF, ADDR_HI=0, allow masks 0). + */ +static void gsp_phys_sub_wpr_invalidate(void __iomem *bar0, u32 cfga, u32 cfgb) +{ + writel(GSP_PHYS_SUBWPR_INVALID_LO, bar0 + cfga); + writel(GSP_PHYS_SUBWPR_INVALID_HI, bar0 + cfgb); } /* @@ -1078,6 +1157,76 @@ static int gsp_phys_booter_unload(void __iomem *bar0) } /* + * Last-resort host port of the booter unload's teardown actions, used when + * the handoff gate cannot pass (a FWSEC-only carve never runs Booter + * Load/AHESASC/Booter Reload/ACR Unload). It mirrors booterUnload_TU10X: + * full GSP reset, invalidate the SEC/GSP/NVDEC sub-WPRs, then + * booterUnlockWpr2_TU10X (WPR2 VAL invalid on both address halves and WPR2 + * read/write allow = 0). The WPR registers sit behind the MMU privilege mask + * (fuse mmu_plm), so the mask is opened first and only written through when + * the readback proves the host is allowed; otherwise nothing is touched and + * the caller's fail-safe keeps the hand-back refused. + */ +static void gsp_phys_host_wpr2_unlock(void __iomem *bar0) +{ + u32 plm_addr = GSP_PHYS_PFB_MMU_PRIV_LEVEL_MASK; + u32 plm_orig, fuse, reg; + u32 i; + bool opened; + + fuse = readl(bar0 + GSP_PHYS_FUSE_SECURE_MMU_PLM_WR_SECURE); + opened = gsp_phys_plm_open(bar0, plm_addr, &plm_orig); + pr_info("gsp-phys-fwsec: host WPR2 unlock: MMU PLM@0x%x 0x%08x -> 0x%08x (host-open=%s), fuse SECURE_MMU_PLM_WR_SECURE=0x%08x\n", + plm_addr, plm_orig, readl(bar0 + plm_addr), + opened ? "yes" : "no", fuse); + if (!opened) { + writel(plm_orig, bar0 + plm_addr); + pr_warn("gsp-phys-fwsec: MMU PLM write protection is fuse-locked (restored 0x%08x); not touching WPR2 (fail-safe stays in force, reboot with approval may be required)\n", + readl(bar0 + plm_addr)); + return; + } + + gsp_phys_falcon_reset(bar0, GSP_PHYS_GSP_BASE); + + for (i = 0; i < GSP_PHYS_FALCON_SEC_SUBWPR_COUNT; i++) + gsp_phys_sub_wpr_invalidate(bar0, + GSP_PHYS_FALCON_SEC_CFGA(i), + GSP_PHYS_FALCON_SEC_CFGB(i)); + for (i = 0; i < GSP_PHYS_FALCON_GSP_SUBWPR_COUNT; i++) + gsp_phys_sub_wpr_invalidate(bar0, + GSP_PHYS_FALCON_GSP_CFGA(i), + GSP_PHYS_FALCON_GSP_CFGB(i)); + gsp_phys_sub_wpr_invalidate(bar0, + GSP_PHYS_FALCON_NVDEC0_CFGA(GSP_PHYS_NVDEC0_SUBWPR_GSPRM_BOOTER_WPR2), + GSP_PHYS_FALCON_NVDEC0_CFGB(GSP_PHYS_NVDEC0_SUBWPR_GSPRM_BOOTER_WPR2)); + + reg = readl(bar0 + GSP_PHYS_WPR2_ADDR_HI); + writel(reg & ~(GSP_PHYS_WPR2_VAL_MASK << GSP_PHYS_WPR2_VAL_SHIFT), + bar0 + GSP_PHYS_WPR2_ADDR_HI); + reg = readl(bar0 + GSP_PHYS_WPR2_ADDR_LO); + writel((reg & ~(GSP_PHYS_WPR2_VAL_MASK << GSP_PHYS_WPR2_VAL_SHIFT)) | + (GSP_PHYS_WPR2_VAL_MASK << GSP_PHYS_WPR2_VAL_SHIFT), + bar0 + GSP_PHYS_WPR2_ADDR_LO); + reg = readl(bar0 + GSP_PHYS_PFB_MMU_WPR_ALLOW_READ); + writel(reg & ~GSP_PHYS_PFB_MMU_WPR2_ALLOW_MASK, + bar0 + GSP_PHYS_PFB_MMU_WPR_ALLOW_READ); + reg = readl(bar0 + GSP_PHYS_PFB_MMU_WPR_ALLOW_WRITE); + writel(reg & ~GSP_PHYS_PFB_MMU_WPR2_ALLOW_MASK, + bar0 + GSP_PHYS_PFB_MMU_WPR_ALLOW_WRITE); + + pr_info("gsp-phys-fwsec: host WPR2 unlock applied: WPR2_LO=0x%08x WPR2_HI=0x%08x ALLOW_READ=0x%08x ALLOW_WRITE=0x%08x\n", + readl(bar0 + GSP_PHYS_WPR2_ADDR_LO), + readl(bar0 + GSP_PHYS_WPR2_ADDR_HI), + readl(bar0 + GSP_PHYS_PFB_MMU_WPR_ALLOW_READ), + readl(bar0 + GSP_PHYS_PFB_MMU_WPR_ALLOW_WRITE)); + + /* Restore the board's original privilege mask (best effort). */ + writel(plm_orig, bar0 + plm_addr); + pr_info("gsp-phys-fwsec: host WPR2 unlock: MMU PLM restored to 0x%08x\n", + readl(bar0 + plm_addr)); +} + +/* * Teardown: FWSEC-SB (puts the pre-OS applications back) followed by the * booter unload (removes WPR2). The final criterion is WPR2 down, not the * individual ucode statuses. @@ -1127,6 +1276,11 @@ static int gsp_phys_fwsec_teardown(void __iomem *bar0) pr_err("gsp-phys-fwsec: booter unload failed: %d\n", ret); if (gsp_phys_fwsec_wpr2_up(bar0)) { + pr_warn("gsp-phys-fwsec: booter unload left WPR2 up (its handoff gate is unsatisfiable after a FWSEC-only carve); trying the host-side WPR2 unlock\n"); + gsp_phys_host_wpr2_unlock(bar0); + } + + if (gsp_phys_fwsec_wpr2_up(bar0)) { pr_err("gsp-phys-fwsec: teardown did not clear WPR2 (HI=0x%08x)\n", readl(bar0 + GSP_PHYS_WPR2_ADDR_HI)); return -EIO; |
