summaryrefslogtreecommitdiffstats
path: root/kernel/gsp_phys_fwsec.c
diff options
context:
space:
mode:
authorArthur Hanson Rasmusson <arthur@vgpu.io>2026-10-01 07:22:09 -0400
committerArthur Hanson Rasmusson <arthur@vgpu.io>2026-10-01 07:22:09 -0400
commite7c068e2e831c6c85e233578767fffe591876a88 (patch)
treea413806c189a53f76a88f621ecc2464bb8931733 /kernel/gsp_phys_fwsec.c
parent1e5f46e8a3d48a69a95025d375871bed87874587 (diff)
downloadopen-iov-e7c068e2e831c6c85e233578767fffe591876a88.tar.gz
open-iov-e7c068e2e831c6c85e233578767fffe591876a88.zip
gsp-phys: read-only FWSEC dry-run mode; match the 6-byte BIT token decode
Diffstat (limited to 'kernel/gsp_phys_fwsec.c')
-rw-r--r--kernel/gsp_phys_fwsec.c378
1 files changed, 344 insertions, 34 deletions
diff --git a/kernel/gsp_phys_fwsec.c b/kernel/gsp_phys_fwsec.c
index a6f81e2..bf422df 100644
--- a/kernel/gsp_phys_fwsec.c
+++ b/kernel/gsp_phys_fwsec.c
@@ -27,6 +27,7 @@
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/jiffies.h>
+#include <linux/crc32.h>
#include <asm/unaligned.h>
#include "gsp_phys_fwsec.h"
@@ -39,11 +40,22 @@ module_param_named(gsp_phys_fwsec, gsp_phys_fwsec_param, bool, 0644);
MODULE_PARM_DESC(gsp_phys_fwsec,
"Run the stage-3c FWSEC/FRTS verification on the claimed GPU after the boot layout succeeds (default 0; device writes)");
+static bool gsp_phys_fwsec_dryrun_param;
+module_param_named(gsp_phys_fwsec_dryrun, gsp_phys_fwsec_dryrun_param, bool,
+ 0644);
+MODULE_PARM_DESC(gsp_phys_fwsec_dryrun,
+ "Read-only validation of the stage-3c VBIOS/BIT/FWSEC parse and the staging/placement math on the claimed GPU after the boot layout succeeds (default 0; no device writes)");
+
bool gsp_phys_fwsec_enabled(void)
{
return gsp_phys_fwsec_param;
}
+bool gsp_phys_fwsec_dryrun_enabled(void)
+{
+ return gsp_phys_fwsec_dryrun_param;
+}
+
/* Falcon engine bases (BAR0 absolute). */
#define GSP_PHYS_GSP_BASE 0x00110000
#define GSP_PHYS_GSP_FBIF_BASE 0x00110600
@@ -79,6 +91,13 @@ bool gsp_phys_fwsec_enabled(void)
#define GSP_PHYS_DMACTL_DMEM_SCRUBBING BIT(1)
#define GSP_PHYS_DMACTL_IMEM_SCRUBBING BIT(2)
#define GSP_PHYS_HWCFG_IMEM_SIZE 0x1ff
+/*
+ * SEC2 falcon HWCFG DMEM capacity: bits 17:9 in 256-byte blocks (the v04
+ * falcon reports IMEM at 8:0 and DMEM at 17:9). Used to check the Booter
+ * Unload DMEM data section against the engine's real capacity.
+ */
+#define GSP_PHYS_HWCFG_DMEM_SIZE 0x0003fe00
+#define GSP_PHYS_HWCFG_DMEM_SIZE_SHIFT 9
/* FBIF CTL / TRANSCFG fields. */
#define GSP_PHYS_FBIF_CTL_ALLOW_PHYS_NO_CTX BIT(7)
@@ -460,10 +479,12 @@ static void gsp_phys_read_vbios_desc_init(struct gsp_phys_fwsec_read_vbios_desc
* Patch the FWSEC interface in the staged DMEM copy for one command: walk
* the application-interface header to the DMEM mapper entry, program
* init_cmd and copy the command payload into the mapper's input buffer.
+ * When cmd_offset is non-NULL it receives the input-buffer offset (used by
+ * the read-only dry-run to dump the exact command bytes).
*/
static int gsp_phys_fwsec_patch_interface(struct gsp_phys_fwsec_ucode *u,
u32 cmd, const void *cmd_buf,
- u32 cmd_size)
+ u32 cmd_size, u32 *cmd_offset)
{
struct gsp_phys_fwsec_intf_header *hdr;
struct gsp_phys_fwsec_dmem_mapper *mapper = NULL;
@@ -522,6 +543,8 @@ static int gsp_phys_fwsec_patch_interface(struct gsp_phys_fwsec_ucode *u,
}
memcpy(data + mapper->cmd_in_buffer_offset, cmd_buf, cmd_size);
+ if (cmd_offset)
+ *cmd_offset = mapper->cmd_in_buffer_offset;
pr_info("gsp-phys-fwsec: interface patched: mapper@0x%x init_cmd=0x%x cmd@0x%x (0x%x bytes), out buffer 0x%x/0x%x\n",
(u32)((u8 *)mapper - data), cmd,
mapper->cmd_in_buffer_offset, cmd_size,
@@ -530,19 +553,16 @@ static int gsp_phys_fwsec_patch_interface(struct gsp_phys_fwsec_ucode *u,
}
/*
- * Prepare and run one FWSEC command on the GSP falcon through the generic
- * SEC2 boot-loader: patch the interface, reset the falcon, then load the
- * boot-loader at the top of IMEM with the FWSEC code/data descriptors.
+ * Compute the 84-byte boot-loader DMEM descriptor for the staged FWSEC
+ * image. Pure host-memory math with the same bounds checks the real run
+ * applies; the read-only dry-run calls this to log the exact plan.
*/
-static int gsp_phys_fwsec_exec_with_loader(void __iomem *bar0,
- struct gsp_phys_fwsec_ucode *u)
+static int gsp_phys_fwsec_bl_desc_fill(const struct gsp_phys_fwsec_ucode *u,
+ struct gsp_phys_fwsec_bl_dmem_desc *bl)
{
- struct gsp_phys_fwsec_bl_dmem_desc bl;
const struct gsp_phys_fwsec_desc *d = &u->desc;
s64 secure_off64;
- u32 secure_off, secure_size, block_count, bl_dst;
- u32 transcfg, cpuctl;
- int ret;
+ u32 secure_off, secure_size;
secure_off64 = (s64)d->imem_sec_base - d->imem_virt_base +
d->imem_phys_base;
@@ -563,6 +583,38 @@ static int gsp_phys_fwsec_exec_with_loader(void __iomem *bar0,
return -EINVAL;
}
+ memset(bl, 0, sizeof(*bl));
+ bl->ctx_dma = 4; /* physical system memory DMA context */
+ bl->code_dma_lo = lower_32_bits(u->code_dma);
+ bl->code_dma_hi = upper_32_bits(u->code_dma);
+ bl->non_secure_code_off = d->imem_phys_base;
+ bl->non_secure_code_size = d->imem_load_size - d->imem_sec_size;
+ bl->secure_code_off = secure_off;
+ bl->secure_code_size = secure_size;
+ bl->code_entry_point = 0;
+ bl->data_dma_lo = lower_32_bits(u->data_dma);
+ bl->data_dma_hi = upper_32_bits(u->data_dma);
+ bl->data_size = d->dmem_load_size;
+ return 0;
+}
+
+/*
+ * Prepare and run one FWSEC command on the GSP falcon through the generic
+ * SEC2 boot-loader: patch the interface, reset the falcon, then load the
+ * boot-loader at the top of IMEM with the FWSEC code/data descriptors.
+ */
+static int gsp_phys_fwsec_exec_with_loader(void __iomem *bar0,
+ struct gsp_phys_fwsec_ucode *u)
+{
+ struct gsp_phys_fwsec_bl_dmem_desc bl;
+ u32 block_count, bl_dst;
+ u32 transcfg, cpuctl;
+ int ret;
+
+ ret = gsp_phys_fwsec_bl_desc_fill(u, &bl);
+ if (ret)
+ return ret;
+
gsp_phys_falcon_disable_ctx_req(bar0, GSP_PHYS_GSP_BASE,
GSP_PHYS_GSP_FBIF_BASE);
@@ -579,19 +631,6 @@ static int gsp_phys_fwsec_exec_with_loader(void __iomem *bar0,
readl(bar0 + GSP_PHYS_GSP_FBIF_BASE +
GSP_PHYS_FBIF_TRANSCFG(4)));
- memset(&bl, 0, sizeof(bl));
- bl.ctx_dma = 4; /* physical system memory DMA context */
- bl.code_dma_lo = lower_32_bits(u->code_dma);
- bl.code_dma_hi = upper_32_bits(u->code_dma);
- bl.non_secure_code_off = d->imem_phys_base;
- bl.non_secure_code_size = d->imem_load_size - d->imem_sec_size;
- bl.secure_code_off = secure_off;
- bl.secure_code_size = secure_size;
- bl.code_entry_point = 0;
- bl.data_dma_lo = lower_32_bits(u->data_dma);
- bl.data_dma_hi = upper_32_bits(u->data_dma);
- bl.data_size = d->dmem_load_size;
-
gsp_phys_falcon_dmem_copy(bar0, GSP_PHYS_GSP_BASE, 0, (const u8 *)&bl,
sizeof(bl));
pr_info("gsp-phys-fwsec: BL descriptor at DMEM0: ctxDma=%u codeDma=0x%llx nsOff/Size=0x%x/0x%x secOff/Size=0x%x/0x%x dataDma=0x%llx dataSize=0x%x\n",
@@ -636,7 +675,7 @@ static int gsp_phys_fwsec_exec_cmd(void __iomem *bar0,
{
int ret;
- ret = gsp_phys_fwsec_patch_interface(u, cmd, cmd_buf, cmd_size);
+ ret = gsp_phys_fwsec_patch_interface(u, cmd, cmd_buf, cmd_size, NULL);
if (ret)
return ret;
@@ -647,7 +686,8 @@ static int gsp_phys_fwsec_exec_cmd(void __iomem *bar0,
return gsp_phys_fwsec_exec_with_loader(bar0, u);
}
-static void gsp_phys_fwsec_ucode_free(struct pci_dev *pdev);
+static void gsp_phys_fwsec_ucode_free(struct pci_dev *pdev,
+ struct gsp_phys_fwsec_ucode *u);
/*
* Stage the FWSEC code/data images into device-visible coherent memory. The
@@ -656,9 +696,9 @@ static void gsp_phys_fwsec_ucode_free(struct pci_dev *pdev);
*/
static int gsp_phys_fwsec_ucode_stage(struct pci_dev *pdev,
const struct gsp_phys_vbios *vbios,
- const struct gsp_phys_fwsec_desc *desc)
+ const struct gsp_phys_fwsec_desc *desc,
+ struct gsp_phys_fwsec_ucode *u)
{
- struct gsp_phys_fwsec_ucode *u = &fwsec.ucode;
u32 image_start = desc->desc_offset + desc->desc_size;
u32 code_alloc, data_alloc, secure_off, secure_size;
s64 secure_off64;
@@ -713,7 +753,7 @@ static int gsp_phys_fwsec_ucode_stage(struct pci_dev *pdev,
if (!u->data_va) {
pr_err("gsp-phys-fwsec: FWSEC data allocation (0x%x) failed\n",
data_alloc);
- gsp_phys_fwsec_ucode_free(pdev);
+ gsp_phys_fwsec_ucode_free(pdev, u);
return -ENOMEM;
}
@@ -732,10 +772,9 @@ static int gsp_phys_fwsec_ucode_stage(struct pci_dev *pdev,
return 0;
}
-static void gsp_phys_fwsec_ucode_free(struct pci_dev *pdev)
+static void gsp_phys_fwsec_ucode_free(struct pci_dev *pdev,
+ struct gsp_phys_fwsec_ucode *u)
{
- struct gsp_phys_fwsec_ucode *u = &fwsec.ucode;
-
if (u->code_va)
dma_free_coherent(&pdev->dev, u->code_size, u->code_va,
u->code_dma);
@@ -938,7 +977,7 @@ int gsp_phys_fwsec_run(struct gsp_phys_boot *boot, struct pci_dev *pdev,
return ret;
}
- ret = gsp_phys_fwsec_ucode_stage(pdev, &vbios, &desc);
+ ret = gsp_phys_fwsec_ucode_stage(pdev, &vbios, &desc, &fwsec.ucode);
gsp_phys_vbios_free(&vbios);
if (ret)
return ret;
@@ -1000,6 +1039,277 @@ recover:
return ret;
}
+/*
+ * Read-only dry-run of stage 3c: locate and parse the VBIOS FWSEC image,
+ * read the ucode head/tail and CRC, read the capacity registers the staging
+ * math depends on, stage into host DMA memory and compute the complete
+ * placement plan - without a single BAR0 write, falcon reset or DMA kick.
+ * readl() of state registers is the only device interaction. Any failed
+ * read-only precondition is logged under the dry-run prefix and returned so
+ * the cycle is visibly red.
+ */
+int gsp_phys_fwsec_dryrun_run(struct gsp_phys_boot *boot, struct pci_dev *pdev,
+ void __iomem *bar0)
+{
+ struct gsp_phys_vbios vbios = { };
+ struct gsp_phys_fwsec_desc desc;
+ struct gsp_phys_fwsec_frts_cmd frts;
+ struct gsp_phys_fwsec_ucode u = { };
+ struct gsp_phys_fwsec_bl_dmem_desc bl;
+ u32 wpr2_hi, wpr2_lo, hwcfg2, fuse;
+ u32 gsp_hwcfg, sec2_hwcfg, imem_blocks, bl_blocks, dmem_blocks;
+ u32 cmd_off = 0, image_start, data_start;
+ const u8 *code;
+ u32 code_crc, data_crc;
+ bool use_debug_app, is_fallback, uload_ok;
+ int ret;
+
+ if (!boot || !pdev || !bar0)
+ return -EINVAL;
+
+ if (gsp_phys_fwsec_param) {
+ pr_err("gsp-phys-fwsec-dryrun: gsp_phys_fwsec=1 is also set; the device-writing FWSEC run and this read-only dry-run are mutually exclusive - refusing both\n");
+ return -EINVAL;
+ }
+ if (!boot->initialized) {
+ pr_err("gsp-phys-fwsec-dryrun: boot layout not initialised; refusing dry-run\n");
+ return -EINVAL;
+ }
+ if (boot->frts_size != ((u64)GSP_PHYS_FRTS_REGION_SIZE_4K << 12)) {
+ pr_err("gsp-phys-fwsec-dryrun: FRTS region size mismatch: boot layout 0x%llx, FWSEC constant 0x%llx; refusing dry-run\n",
+ boot->frts_size,
+ (u64)GSP_PHYS_FRTS_REGION_SIZE_4K << 12);
+ return -EINVAL;
+ }
+
+ pr_info("gsp-phys-fwsec-dryrun: READ-ONLY stage 3c validation on %s: frtsOffset=0x%llx frtsSize=0x%llx (no BAR0 writes, no falcon reset, no DMA kick)\n",
+ pci_name(pdev), boot->frts_offset, boot->frts_size);
+
+ /* Read-only preconditions, each pass/fail. */
+ wpr2_lo = gsp_phys_fwsec_wpr2_lo_val(bar0);
+ wpr2_hi = gsp_phys_fwsec_wpr2_hi_val(bar0);
+ pr_info("gsp-phys-fwsec-dryrun: precondition WPR2 down: LO raw=0x%08x VAL=0x%x HI raw=0x%08x VAL=0x%x -> %s\n",
+ readl(bar0 + GSP_PHYS_WPR2_ADDR_LO), wpr2_lo,
+ readl(bar0 + GSP_PHYS_WPR2_ADDR_HI), wpr2_hi,
+ wpr2_hi == 0 ? "PASS" : "FAIL");
+ if (wpr2_hi != 0) {
+ pr_err("gsp-phys-fwsec-dryrun: WPR2 already up; a real stage 3c would refuse, dry-run stops here\n");
+ return -EBUSY;
+ }
+
+ ret = gsp_phys_fwsec_wait_gfw_boot(bar0);
+ pr_info("gsp-phys-fwsec-dryrun: precondition GFW boot complete (halted + PLM level0 + progress 0xff) -> %s\n",
+ ret ? "FAIL" : "PASS");
+ if (ret)
+ return ret;
+
+ hwcfg2 = readl(bar0 + GSP_PHYS_GSP_BASE + GSP_PHYS_FALCON_HWCFG2);
+ pr_info("gsp-phys-fwsec-dryrun: precondition RISC-V core enabled (HWCFG2[10]): HWCFG2=0x%08x -> %s\n",
+ hwcfg2, (hwcfg2 & BIT(10)) ? "PASS" : "FAIL");
+ if (!(hwcfg2 & BIT(10)))
+ return -ENODEV;
+
+ pr_info("gsp-phys-fwsec-dryrun: precondition falcon state (read-only): GSP_CPUCTL=0x%08x SEC2_CPUCTL=0x%08x\n",
+ readl(bar0 + GSP_PHYS_GSP_BASE + GSP_PHYS_FALCON_CPUCTL),
+ readl(bar0 + GSP_PHYS_SEC2_BASE + GSP_PHYS_FALCON_CPUCTL));
+
+ /* VBIOS PROM read + IFR/expansion-ROM/BIT/FWSEC parse (real code). */
+ ret = gsp_phys_vbios_read(&vbios, pdev, bar0);
+ if (ret)
+ return ret;
+
+ fuse = readl(bar0 + GSP_PHYS_FUSE_SECURE_GSP_DEBUG_DIS);
+ use_debug_app = !(fuse & GSP_PHYS_FUSE_SECURE_GSP_DEBUG_DIS_FIELD);
+
+ ret = gsp_phys_vbios_parse_fwsec(&vbios, use_debug_app, &desc);
+ if (ret) {
+ pr_err("gsp-phys-fwsec-dryrun: FWSEC parse from VBIOS failed: %d\n",
+ ret);
+ gsp_phys_vbios_free(&vbios);
+ return ret;
+ }
+
+ is_fallback = desc.app_id == GSP_PHYS_FWSEC_APPID_FIRMWARE_SEC_LIC;
+ image_start = desc.desc_offset + desc.desc_size;
+ data_start = image_start + desc.dmem_offset;
+
+ pr_info("gsp-phys-fwsec-dryrun: VBIOS summary: ROM size=0x%zx pciOffset=0x%x expansionRomOffset=0x%x IFRversion=%u VBIOSversion=0x%llx\n",
+ vbios.size, vbios.pci_offset, vbios.expansion_rom_offset,
+ vbios.ifr_version, vbios.version);
+ pr_info("gsp-phys-fwsec-dryrun: BIT summary: header@0x%x tokenType=%u-byte (%s) tokens=%u; selected entry appId=0x%x targetId=%u (%s, fuse SECURE_GSP_DEBUG_DIS=0x%08x)\n",
+ vbios.bit_addr, vbios.bit_token_size,
+ vbios.bit_token_size >= GSP_PHYS_BIT_TOKEN_SIZE_8 ?
+ "2b1w1d u32 DataPtr" : "2b2w u16 DataPtr",
+ vbios.bit_token_entries, desc.app_id, desc.target_id,
+ is_fallback ? "fallback FIRMWARE_SEC_LIC 0x05" :
+ (use_debug_app ? "debug 0x45" : "production 0x85"), fuse);
+ pr_info("gsp-phys-fwsec-dryrun: FWSEC descriptor type=V%u size=%u at 0x%x; ucode code at 0x%x size=0x%x, data at 0x%x size=0x%x, interfaceOff=0x%x entry=0x%x\n",
+ desc.desc_version, desc.desc_size, desc.desc_offset,
+ image_start, desc.imem_load_size, data_start,
+ desc.dmem_load_size, desc.interface_offset,
+ desc.virtual_entry);
+
+ /* First/last 4 words + CRC over the extracted image (bounds-checked). */
+ if (image_start > vbios.size ||
+ desc.imem_load_size > vbios.size - image_start ||
+ desc.imem_load_size < 16 ||
+ data_start > vbios.size ||
+ desc.dmem_load_size > vbios.size - data_start) {
+ pr_err("gsp-phys-fwsec-dryrun: FWSEC image outside the ROM copy (image=0x%x imem=0x%x data=0x%x dmem=0x%x rom=0x%zx); refusing\n",
+ image_start, desc.imem_load_size, data_start,
+ desc.dmem_load_size, vbios.size);
+ gsp_phys_vbios_free(&vbios);
+ return -EINVAL;
+ }
+
+ code = vbios.data + image_start;
+ code_crc = crc32_le(0, code, desc.imem_load_size);
+ data_crc = crc32_le(0, vbios.data + data_start, desc.dmem_load_size);
+ pr_info("gsp-phys-fwsec-dryrun: ucode first 4 words: %08x %08x %08x %08x\n",
+ get_unaligned_le32(code), get_unaligned_le32(code + 4),
+ get_unaligned_le32(code + 8), get_unaligned_le32(code + 12));
+ pr_info("gsp-phys-fwsec-dryrun: ucode last 4 words: %08x %08x %08x %08x\n",
+ get_unaligned_le32(code + desc.imem_load_size - 16),
+ get_unaligned_le32(code + desc.imem_load_size - 12),
+ get_unaligned_le32(code + desc.imem_load_size - 8),
+ get_unaligned_le32(code + desc.imem_load_size - 4));
+ pr_info("gsp-phys-fwsec-dryrun: CRC32 code(0x%x bytes)=0x%08x data(0x%x bytes)=0x%08x\n",
+ desc.imem_load_size, code_crc, desc.dmem_load_size, data_crc);
+
+ /* Capacity registers the staging/placement math depends on. */
+ gsp_hwcfg = readl(bar0 + GSP_PHYS_GSP_BASE + GSP_PHYS_FALCON_HWCFG);
+ imem_blocks = gsp_hwcfg & GSP_PHYS_HWCFG_IMEM_SIZE;
+ bl_blocks = imem_blocks >= GSP_PHYS_SEC2_BL_CODE_SIZE / 256 ?
+ imem_blocks - GSP_PHYS_SEC2_BL_CODE_SIZE / 256 : 0;
+ pr_info("gsp-phys-fwsec-dryrun: GSP falcon HWCFG=0x%08x IMEM_SIZE=%u blocks (256 B) -> generic BL at block %u (IMEM addr 0x%x), tag 0x%x, fits=%s\n",
+ gsp_hwcfg, imem_blocks, bl_blocks, bl_blocks << 8,
+ GSP_PHYS_SEC2_BL_START_TAG,
+ imem_blocks * 256 >= GSP_PHYS_SEC2_BL_CODE_SIZE ? "yes" :
+ "no");
+ if (imem_blocks * 256 < GSP_PHYS_SEC2_BL_CODE_SIZE) {
+ pr_err("gsp-phys-fwsec-dryrun: GSP IMEM too small for the generic boot-loader (%u blocks); refusing\n",
+ imem_blocks);
+ gsp_phys_vbios_free(&vbios);
+ return -EINVAL;
+ }
+
+ sec2_hwcfg = readl(bar0 + GSP_PHYS_SEC2_BASE + GSP_PHYS_FALCON_HWCFG);
+ dmem_blocks = (sec2_hwcfg & GSP_PHYS_HWCFG_DMEM_SIZE) >>
+ GSP_PHYS_HWCFG_DMEM_SIZE_SHIFT;
+ pr_info("gsp-phys-fwsec-dryrun: SEC2 falcon HWCFG=0x%08x IMEM_SIZE=%u blocks DMEM_SIZE=%u blocks (0x%x bytes) vs booter-unload DMEM data 0x%x -> %s\n",
+ sec2_hwcfg, sec2_hwcfg & GSP_PHYS_HWCFG_IMEM_SIZE,
+ dmem_blocks, dmem_blocks * 256,
+ GSP_PHYS_BOOTER_UNLOAD_DATA_SIZE,
+ dmem_blocks * 256 >= GSP_PHYS_BOOTER_UNLOAD_DATA_SIZE ?
+ "PASS" : "FAIL");
+ if (dmem_blocks == 0) {
+ pr_err("gsp-phys-fwsec-dryrun: SEC2 HWCFG DMEM_SIZE (bits 17:9) decodes to zero - the capacity is not readable/reporting on this part; refusing\n");
+ gsp_phys_vbios_free(&vbios);
+ return -ENODEV;
+ }
+ if (dmem_blocks * 256 < GSP_PHYS_BOOTER_UNLOAD_DATA_SIZE) {
+ pr_err("gsp-phys-fwsec-dryrun: Booter-Unload DMEM data 0x%x does not fit SEC2 DMEM 0x%x; the unload split would not load; refusing\n",
+ GSP_PHYS_BOOTER_UNLOAD_DATA_SIZE, dmem_blocks * 256);
+ gsp_phys_vbios_free(&vbios);
+ return -ENOSPC;
+ }
+
+ /* Stage into host DMA memory (no device access) and patch the cmd. */
+ ret = gsp_phys_fwsec_ucode_stage(pdev, &vbios, &desc, &u);
+ gsp_phys_vbios_free(&vbios);
+ if (ret)
+ return ret;
+
+ gsp_phys_read_vbios_desc_init(&frts.read_vbios_desc);
+ frts.frts_region_desc.version = 1;
+ frts.frts_region_desc.size = sizeof(frts.frts_region_desc);
+ frts.frts_region_desc.frts_region_offset4k =
+ (u32)(boot->frts_offset >> 12);
+ frts.frts_region_desc.frts_region_size = GSP_PHYS_FRTS_REGION_SIZE_4K;
+ frts.frts_region_desc.frts_region_media_type =
+ GSP_PHYS_FRTS_REGION_MEDIA_FB;
+
+ ret = gsp_phys_fwsec_patch_interface(&u, GSP_PHYS_FWSEC_CMD_FRTS, &frts,
+ sizeof(frts), &cmd_off);
+ if (ret)
+ goto out_free;
+
+ ret = gsp_phys_fwsec_bl_desc_fill(&u, &bl);
+ if (ret)
+ goto out_free;
+
+ pr_info("gsp-phys-fwsec-dryrun: staging plan (host DMA only): code alloc=0x%zx dma=0x%llx (imemLoad=0x%x secOff/Size=0x%x/0x%x), data alloc=0x%zx dma=0x%llx (dmemLoad=0x%x)\n",
+ u.code_size, (u64)u.code_dma, u.desc.imem_load_size,
+ bl.secure_code_off, bl.secure_code_size, u.data_size,
+ (u64)u.data_dma, u.desc.dmem_load_size);
+ pr_info("gsp-phys-fwsec-dryrun: 84-byte BL descriptor: reserved[4]=0 signature[4]=0 ctxDma=%u codeDma=0x%llx nsOff/Size=0x%x/0x%x secOff/Size=0x%x/0x%x entry=0x%x dataDma=0x%llx dataSize=0x%x argc=%u argv=%u\n",
+ bl.ctx_dma,
+ ((u64)bl.code_dma_hi << 32) | bl.code_dma_lo,
+ bl.non_secure_code_off, bl.non_secure_code_size,
+ bl.secure_code_off, bl.secure_code_size,
+ bl.code_entry_point,
+ ((u64)bl.data_dma_hi << 32) | bl.data_dma_lo,
+ bl.data_size, bl.argc, bl.argv);
+ pr_info("gsp-phys-fwsec-dryrun: generic-BL placement from real HWCFG: block %u IMEM addr 0x%x BOOTVEC=0x%x code 0x%x bytes tag 0x%x\n",
+ bl_blocks, bl_blocks << 8, GSP_PHYS_SEC2_BL_START_TAG << 8,
+ GSP_PHYS_SEC2_BL_CODE_SIZE, GSP_PHYS_SEC2_BL_START_TAG);
+ pr_info("gsp-phys-fwsec-dryrun: FRTS region: frtsOffset>>12=0x%x size4K=0x%x media=FB(%u); readVbiosDesc{version=%u size=%u flags=%u}\n",
+ frts.frts_region_desc.frts_region_offset4k,
+ frts.frts_region_desc.frts_region_size,
+ frts.frts_region_desc.frts_region_media_type,
+ frts.read_vbios_desc.version, frts.read_vbios_desc.size,
+ frts.read_vbios_desc.flags);
+ pr_info("gsp-phys-fwsec-dryrun: 44-byte interface-patch block at DMEM 0x%x: %*ph\n",
+ cmd_off, (int)sizeof(frts), (const u8 *)u.data_va + cmd_off);
+ pr_info("gsp-phys-fwsec-dryrun: expected WPR2 after a real run: HI VAL nonzero; LO VAL=0x%x raw encoding=0x%08x\n",
+ (u32)(boot->frts_offset >> 12),
+ (u32)((boot->frts_offset >> 12) << 4));
+
+ /* Bindata header/size/tag verification (compile-time vs image). */
+ pr_info("gsp-phys-fwsec-dryrun: generic SEC2 BL bindata: image=0x%zx code=0x%x at 0 data=0x100 at 0x200 blStartTag=0x%x blDmemDescLoadOff=0 -> %s\n",
+ sizeof(gsp_phys_sec2_bl_image), GSP_PHYS_SEC2_BL_CODE_SIZE,
+ GSP_PHYS_SEC2_BL_START_TAG,
+ sizeof(gsp_phys_sec2_bl_image) == 0x300 &&
+ GSP_PHYS_SEC2_BL_CODE_SIZE + 0x100 ==
+ sizeof(gsp_phys_sec2_bl_image) ? "PASS" : "FAIL");
+
+ uload_ok = sizeof(gsp_phys_booter_unload_image) ==
+ GSP_PHYS_BOOTER_UNLOAD_IMAGE_SIZE &&
+ GSP_PHYS_BOOTER_UNLOAD_NS_CODE_OFF == 0 &&
+ GSP_PHYS_BOOTER_UNLOAD_SEC_CODE_OFF ==
+ GSP_PHYS_BOOTER_UNLOAD_NS_CODE_OFF +
+ GSP_PHYS_BOOTER_UNLOAD_NS_CODE_SIZE &&
+ GSP_PHYS_BOOTER_UNLOAD_DATA_OFF ==
+ GSP_PHYS_BOOTER_UNLOAD_SEC_CODE_OFF +
+ GSP_PHYS_BOOTER_UNLOAD_SEC_CODE_SIZE &&
+ GSP_PHYS_BOOTER_UNLOAD_DATA_OFF +
+ GSP_PHYS_BOOTER_UNLOAD_DATA_SIZE ==
+ GSP_PHYS_BOOTER_UNLOAD_IMAGE_SIZE;
+ pr_info("gsp-phys-fwsec-dryrun: Booter-Unload bindata: image=0x%zx nsCode=0x%x@0x%x secCode=0x%x@0x%x codeEnd=0x%x data=0x%x@0x%x dataEnd=0x%x mailboxTag=0x%x/0x%x sig@0x4e00=%08x -> %s\n",
+ sizeof(gsp_phys_booter_unload_image),
+ GSP_PHYS_BOOTER_UNLOAD_NS_CODE_SIZE,
+ GSP_PHYS_BOOTER_UNLOAD_NS_CODE_OFF,
+ GSP_PHYS_BOOTER_UNLOAD_SEC_CODE_SIZE,
+ GSP_PHYS_BOOTER_UNLOAD_SEC_CODE_OFF,
+ GSP_PHYS_BOOTER_UNLOAD_SEC_CODE_OFF +
+ GSP_PHYS_BOOTER_UNLOAD_SEC_CODE_SIZE,
+ GSP_PHYS_BOOTER_UNLOAD_DATA_SIZE,
+ GSP_PHYS_BOOTER_UNLOAD_DATA_OFF,
+ GSP_PHYS_BOOTER_UNLOAD_DATA_OFF +
+ GSP_PHYS_BOOTER_UNLOAD_DATA_SIZE,
+ GSP_PHYS_BOOTER_UNLOAD_MBOX, GSP_PHYS_BOOTER_UNLOAD_MBOX,
+ get_unaligned_le32(gsp_phys_booter_unload_image + 0x4e00),
+ uload_ok ? "PASS" : "FAIL");
+
+ gsp_phys_fwsec_ucode_free(pdev, &u);
+ pr_info("gsp-phys-fwsec-dryrun: stage 3c dry-run complete: VBIOS/BIT/FWSEC parse, capacity registers, staging and placement plan validated; no device writes (WPR2 stays down, no falcon reset, no DMA kick)\n");
+ return 0;
+
+out_free:
+ gsp_phys_fwsec_ucode_free(pdev, &u);
+ return ret;
+}
+
int gsp_phys_fwsec_release(struct gsp_phys_boot *boot, struct pci_dev *pdev,
void __iomem *bar0)
{
@@ -1012,14 +1322,14 @@ int gsp_phys_fwsec_release(struct gsp_phys_boot *boot, struct pci_dev *pdev,
if (!gsp_phys_fwsec_wpr2_up(bar0)) {
pr_info("gsp-phys-fwsec: release: WPR2 already down; freeing FWSEC staging and handing back\n");
- gsp_phys_fwsec_ucode_free(pdev);
+ gsp_phys_fwsec_ucode_free(pdev, &fwsec.ucode);
fwsec.touched = false;
return 0;
}
pr_info("gsp-phys-fwsec: release: WPR2 is up; tearing down before hand-back\n");
ret = gsp_phys_fwsec_teardown(bar0);
- gsp_phys_fwsec_ucode_free(pdev);
+ gsp_phys_fwsec_ucode_free(pdev, &fwsec.ucode);
if (ret || gsp_phys_fwsec_wpr2_up(bar0)) {
pr_err("gsp-phys-fwsec: release: WPR2 still up after teardown (HI=0x%08x); refusing the hand-back to the distribution driver\n",