summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--kernel/gsp_phys_fwsec.c172
-rw-r--r--kernel/gsp_phys_gpu.c15
2 files changed, 186 insertions, 1 deletions
diff --git a/kernel/gsp_phys_fwsec.c b/kernel/gsp_phys_fwsec.c
index c4b0424..dc8c979 100644
--- a/kernel/gsp_phys_fwsec.c
+++ b/kernel/gsp_phys_fwsec.c
@@ -46,6 +46,17 @@ module_param_named(gsp_phys_fwsec_dryrun, gsp_phys_fwsec_dryrun_param, bool,
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)");
+/*
+ * Verbose read-only state dump after a successful FWSEC kick (the failure
+ * path always dumps). Off by default: the dump reads DMEM/IMEM through the
+ * falcon ports, which is only for post-mortem/compare purposes.
+ */
+static bool gsp_phys_fwsec_verbose_param;
+module_param_named(gsp_phys_fwsec_verbose, gsp_phys_fwsec_verbose_param,
+ bool, 0644);
+MODULE_PARM_DESC(gsp_phys_fwsec_verbose,
+ "Also dump the bounded read-only FWSEC kick state (falcon regs, scratch, DMEM/IMEM readback) after a successful halt (default 0)");
+
bool gsp_phys_fwsec_enabled(void)
{
return gsp_phys_fwsec_param;
@@ -65,6 +76,7 @@ bool gsp_phys_fwsec_dryrun_enabled(void)
/* Falcon register offsets, relative to the engine base. */
#define GSP_PHYS_FALCON_MAILBOX0 0x40
#define GSP_PHYS_FALCON_MAILBOX1 0x44
+#define GSP_PHYS_FALCON_OS 0x80
#define GSP_PHYS_FALCON_RM 0x84
#define GSP_PHYS_FALCON_HWCFG2 0xf4
#define GSP_PHYS_FALCON_CPUCTL 0x100
@@ -108,12 +120,16 @@ bool gsp_phys_fwsec_dryrun_enabled(void)
/* IMEM/DMEM access port fields. */
#define GSP_PHYS_FALCON_ADDR_MASK (0x0000fffc)
#define GSP_PHYS_FALCON_AINCW BIT(24)
+#define GSP_PHYS_FALCON_AINCR BIT(25)
#define GSP_PHYS_FALCON_IMEMC_SECURE BIT(28)
/* GPU-side boot state registers. */
#define GSP_PHYS_PMC_BOOT_0 0x00000000
#define GSP_PHYS_VBIOS_SCRATCH_0E 0x001438
#define GSP_PHYS_VBIOS_SCRATCH_15 0x001454
+/* PBUS VBIOS scratch window (the FWSEC error codes live inside it). */
+#define GSP_PHYS_VBIOS_SCRATCH_BASE 0x001400
+#define GSP_PHYS_VBIOS_SCRATCH_WORDS 32
#define GSP_PHYS_WPR2_ADDR_LO 0x1fa824
#define GSP_PHYS_WPR2_ADDR_HI 0x1fa828
/* WPR2 VAL field is bits 31:4 (DRF_VAL returns the unshifted field). */
@@ -124,6 +140,7 @@ bool gsp_phys_fwsec_dryrun_enabled(void)
#define GSP_PHYS_GFW_BOOT_COMPLETED 0xff
#define GSP_PHYS_GFW_PLM 0x118128
#define GSP_PHYS_GFW_PLM_LEVEL0 BIT(0)
+#define GSP_PHYS_RISCV_STATUS 0x111240
#define GSP_PHYS_FUSE_SECURE_GSP_DEBUG_DIS 0x2174c
#define GSP_PHYS_FUSE_SECURE_GSP_DEBUG_DIS_FIELD BIT(0)
@@ -259,6 +276,8 @@ struct gsp_phys_fwsec_ucode {
static struct gsp_phys_fwsec {
bool touched; /* FWSEC device state may be programmed */
+ struct pci_dev *pdev; /* claimed device (failure-dump state) */
+ u32 cmd_offset; /* last patched interface cmd offset */
struct gsp_phys_fwsec_ucode ucode;
} fwsec;
@@ -405,6 +424,34 @@ static void gsp_phys_falcon_imem_copy(void __iomem *bar0, u32 base, u32 dest,
}
}
+/*
+ * Read-only port access used by the failure dump. DMEM has an auto-increment
+ * read mode (AINCR); IMEM has none, so its address is rewritten per word.
+ * Callers pass a fixed, small word count (the dump is always bounded).
+ */
+static void gsp_phys_falcon_dmem_read(void __iomem *bar0, u32 base, u32 dest,
+ u32 *dst, u32 words)
+{
+ u32 i;
+
+ writel((dest & GSP_PHYS_FALCON_ADDR_MASK) | GSP_PHYS_FALCON_AINCR,
+ bar0 + base + GSP_PHYS_FALCON_DMEMC);
+ for (i = 0; i < words; i++)
+ dst[i] = readl(bar0 + base + GSP_PHYS_FALCON_DMEMD);
+}
+
+static void gsp_phys_falcon_imem_read(void __iomem *bar0, u32 base, u32 dest,
+ u32 *dst, u32 words)
+{
+ u32 i;
+
+ for (i = 0; i < words; i++) {
+ writel((dest + i * 4) & GSP_PHYS_FALCON_ADDR_MASK,
+ bar0 + base + GSP_PHYS_FALCON_IMEMC);
+ dst[i] = readl(bar0 + base + GSP_PHYS_FALCON_IMEMD);
+ }
+}
+
static void gsp_phys_falcon_start_cpu(void __iomem *bar0, u32 base)
{
u32 cpuctl = readl(bar0 + base + GSP_PHYS_FALCON_CPUCTL);
@@ -606,6 +653,125 @@ static int gsp_phys_fwsec_bl_desc_fill(const struct gsp_phys_fwsec_ucode *u,
}
/*
+ * Bounded, read-only post-mortem dump for a FWSEC kick: PCI bus-master
+ * state, both falcon cores, WPR2/FRTS state, the PBUS scratch window, the
+ * raw DMEM0 contents and the IMEM code readback. DMEM0 tells us how far
+ * the generic boot-loader got: the 84-byte BL descriptor intact means its
+ * host DMA never delivered anything; the FWSEC data image means the loads
+ * completed and the FWSEC ucode itself never returned. All accesses are
+ * bounded and use the reference's port method (DMEM via AINCR, IMEM
+ * address rewritten per word).
+ */
+static void gsp_phys_fwsec_dump_kick_state(void __iomem *bar0,
+ const struct gsp_phys_fwsec_bl_dmem_desc *bl,
+ const struct gsp_phys_fwsec_ucode *u,
+ u32 bl_dst)
+{
+ u32 words[GSP_PHYS_VBIOS_SCRATCH_WORDS];
+ u32 expected_bl0 = get_unaligned_le32(gsp_phys_sec2_bl_image);
+ u32 expected_code0 = 0;
+ u32 i;
+ u16 pci_cmd = 0;
+ bool have_expected_code = false;
+ bool desc_intact, data_image, code_loaded, bl_intact;
+ const char *verdict;
+
+ if (fwsec.pdev)
+ pci_read_config_word(fwsec.pdev, PCI_COMMAND, &pci_cmd);
+ pr_info("gsp-phys-fwsec: FAILDUMP PCI_COMMAND=0x%04x [bus_master=%s mem=%s io=%s]\n",
+ pci_cmd, pci_cmd & PCI_COMMAND_MASTER ? "on" : "OFF",
+ pci_cmd & PCI_COMMAND_MEMORY ? "on" : "off",
+ pci_cmd & PCI_COMMAND_IO ? "on" : "off");
+
+ pr_info("gsp-phys-fwsec: FAILDUMP GSP CPUCTL=0x%08x HWCFG=0x%08x HWCFG2=0x%08x ENGINE=0x%08x DMACTL=0x%08x BOOTVEC=0x%08x\n",
+ readl(bar0 + GSP_PHYS_GSP_BASE + GSP_PHYS_FALCON_CPUCTL),
+ readl(bar0 + GSP_PHYS_GSP_BASE + GSP_PHYS_FALCON_HWCFG),
+ readl(bar0 + GSP_PHYS_GSP_BASE + GSP_PHYS_FALCON_HWCFG2),
+ readl(bar0 + GSP_PHYS_GSP_BASE + GSP_PHYS_FALCON_ENGINE),
+ readl(bar0 + GSP_PHYS_GSP_BASE + GSP_PHYS_FALCON_DMACTL),
+ readl(bar0 + GSP_PHYS_GSP_BASE + GSP_PHYS_FALCON_BOOTVEC));
+ pr_info("gsp-phys-fwsec: FAILDUMP GSP MBOX0=0x%08x MBOX1=0x%08x FALCON_OS=0x%08x FALCON_RM=0x%08x\n",
+ readl(bar0 + GSP_PHYS_GSP_BASE + GSP_PHYS_FALCON_MAILBOX0),
+ readl(bar0 + GSP_PHYS_GSP_BASE + GSP_PHYS_FALCON_MAILBOX1),
+ readl(bar0 + GSP_PHYS_GSP_BASE + GSP_PHYS_FALCON_OS),
+ readl(bar0 + GSP_PHYS_GSP_BASE + GSP_PHYS_FALCON_RM));
+ pr_info("gsp-phys-fwsec: FAILDUMP SEC2 CPUCTL=0x%08x HWCFG=0x%08x ENGINE=0x%08x MBOX0=0x%08x MBOX1=0x%08x FALCON_OS=0x%08x\n",
+ readl(bar0 + GSP_PHYS_SEC2_BASE + GSP_PHYS_FALCON_CPUCTL),
+ readl(bar0 + GSP_PHYS_SEC2_BASE + GSP_PHYS_FALCON_HWCFG),
+ readl(bar0 + GSP_PHYS_SEC2_BASE + GSP_PHYS_FALCON_ENGINE),
+ readl(bar0 + GSP_PHYS_SEC2_BASE + GSP_PHYS_FALCON_MAILBOX0),
+ readl(bar0 + GSP_PHYS_SEC2_BASE + GSP_PHYS_FALCON_MAILBOX1),
+ readl(bar0 + GSP_PHYS_SEC2_BASE + GSP_PHYS_FALCON_OS));
+
+ pr_info("gsp-phys-fwsec: FAILDUMP state WPR2_LO raw=0x%08x VAL=0x%x WPR2_HI raw=0x%08x VAL=0x%x PLM=0x%08x GFW_BOOT=0x%08x SCRATCH_0E=0x%08x FRTS_ERR=0x%x SCRATCH_15=0x%08x SB_ERR=0x%x RISCV_STATUS=0x%08x\n",
+ readl(bar0 + GSP_PHYS_WPR2_ADDR_LO),
+ gsp_phys_fwsec_wpr2_lo_val(bar0),
+ readl(bar0 + GSP_PHYS_WPR2_ADDR_HI),
+ gsp_phys_fwsec_wpr2_hi_val(bar0),
+ readl(bar0 + GSP_PHYS_GFW_PLM),
+ readl(bar0 + GSP_PHYS_GFW_BOOT),
+ readl(bar0 + GSP_PHYS_VBIOS_SCRATCH_0E),
+ readl(bar0 + GSP_PHYS_VBIOS_SCRATCH_0E) >> 16,
+ readl(bar0 + GSP_PHYS_VBIOS_SCRATCH_15),
+ readl(bar0 + GSP_PHYS_VBIOS_SCRATCH_15) & 0xffff,
+ readl(bar0 + GSP_PHYS_RISCV_STATUS));
+
+ for (i = 0; i < GSP_PHYS_VBIOS_SCRATCH_WORDS; i++)
+ words[i] = readl(bar0 + GSP_PHYS_VBIOS_SCRATCH_BASE + i * 4);
+ pr_info("gsp-phys-fwsec: FAILDUMP scratch 0x1400..0x143f: %*ph\n",
+ 64, (const u8 *)words);
+ pr_info("gsp-phys-fwsec: FAILDUMP scratch 0x1440..0x147f: %*ph\n",
+ 64, (const u8 *)words + 64);
+
+ gsp_phys_falcon_dmem_read(bar0, GSP_PHYS_GSP_BASE, 0, words,
+ sizeof(*bl) / 4);
+ pr_info("gsp-phys-fwsec: FAILDUMP DMEM0 (84 B): %*ph\n",
+ (int)sizeof(*bl), (const u8 *)words);
+ desc_intact = memcmp(words, bl, sizeof(*bl)) == 0;
+ data_image = u->data_va &&
+ u->data_size >= (size_t)u->desc.dmem_phys_base +
+ sizeof(*bl) &&
+ memcmp(words, (const u8 *)u->data_va +
+ u->desc.dmem_phys_base, sizeof(*bl)) == 0;
+
+ gsp_phys_falcon_imem_read(bar0, GSP_PHYS_GSP_BASE, bl_dst, words, 4);
+ bl_intact = words[0] == expected_bl0;
+ pr_info("gsp-phys-fwsec: FAILDUMP IMEM BL @0x%x: %08x %08x %08x %08x (word0 want 0x%08x -> %s)\n",
+ bl_dst, words[0], words[1], words[2], words[3], expected_bl0,
+ bl_intact ? "intact" : "CHANGED");
+
+ if (u->code_va && u->code_size >= (size_t)u->desc.imem_phys_base + 4) {
+ expected_code0 = get_unaligned_le32(u->code_va +
+ u->desc.imem_phys_base);
+ have_expected_code = true;
+ }
+ gsp_phys_falcon_imem_read(bar0, GSP_PHYS_GSP_BASE,
+ u->desc.imem_phys_base, words, 4);
+ code_loaded = have_expected_code && words[0] == expected_code0;
+ pr_info("gsp-phys-fwsec: FAILDUMP IMEM ns code @0x%x: %08x %08x %08x %08x (word0 want 0x%08x -> %s)\n",
+ u->desc.imem_phys_base, words[0], words[1], words[2], words[3],
+ expected_code0, code_loaded ? "loaded" : "NOT loaded");
+
+ if (fwsec.cmd_offset &&
+ u->data_size >= (size_t)fwsec.cmd_offset + 48) {
+ gsp_phys_falcon_dmem_read(bar0, GSP_PHYS_GSP_BASE,
+ fwsec.cmd_offset, words, 12);
+ pr_info("gsp-phys-fwsec: FAILDUMP interface patch @DMEM 0x%x (48 B): %*ph\n",
+ fwsec.cmd_offset, 48, (const u8 *)words);
+ }
+
+ if (data_image)
+ verdict = "FWSEC data image at DMEM0 -> BL loads completed; the FWSEC ucode never returned";
+ else if (desc_intact && code_loaded)
+ verdict = "BL descriptor intact + FWSEC code loaded -> stalled at the data DMA";
+ else if (desc_intact)
+ verdict = "BL descriptor intact, code not loaded -> stalled at the code DMA (or the BL never entered; check bus_master and TRANSCFG)";
+ else
+ verdict = "DMEM0 matches neither the BL descriptor nor the FWSEC data image";
+ pr_info("gsp-phys-fwsec: FAILDUMP verdict: %s\n", verdict);
+}
+
+/*
* 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.
@@ -673,6 +839,8 @@ static int gsp_phys_fwsec_exec_with_loader(void __iomem *bar0,
GSP_PHYS_FWSEC_HALT_TIMEOUT_MS);
pr_info("gsp-phys-fwsec: GSP CPUCTL after FWSEC kick=0x%08x\n",
readl(bar0 + GSP_PHYS_GSP_BASE + GSP_PHYS_FALCON_CPUCTL));
+ if (ret || gsp_phys_fwsec_verbose_param)
+ gsp_phys_fwsec_dump_kick_state(bar0, &bl, u, bl_dst);
return ret;
}
@@ -682,7 +850,8 @@ static int gsp_phys_fwsec_exec_cmd(void __iomem *bar0,
{
int ret;
- ret = gsp_phys_fwsec_patch_interface(u, cmd, cmd_buf, cmd_size, NULL);
+ ret = gsp_phys_fwsec_patch_interface(u, cmd, cmd_buf, cmd_size,
+ &fwsec.cmd_offset);
if (ret)
return ret;
@@ -991,6 +1160,7 @@ int gsp_phys_fwsec_run(struct gsp_phys_boot *boot, struct pci_dev *pdev,
/* From here on the device may carry FWSEC state. */
fwsec.touched = true;
+ fwsec.pdev = pdev;
gsp_phys_read_vbios_desc_init(&frts.read_vbios_desc);
frts.frts_region_desc.version = 1;
diff --git a/kernel/gsp_phys_gpu.c b/kernel/gsp_phys_gpu.c
index 9726b84..0936cf4 100644
--- a/kernel/gsp_phys_gpu.c
+++ b/kernel/gsp_phys_gpu.c
@@ -356,6 +356,7 @@ static void gsp_phys_dump_falcon(void)
static int gsp_phys_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
u32 boot0, boot1;
+ u16 pci_cmd = 0;
int ret;
pr_info("gsp-phys: probe %s\n", pci_name(pdev));
@@ -367,6 +368,20 @@ static int gsp_phys_probe(struct pci_dev *pdev, const struct pci_device_id *id)
}
phys.enabled = true;
+ /*
+ * Read-only observability for the host->GPU DMA path: the previous
+ * driver's remove clears PCI_COMMAND_MASTER and this probe does not
+ * set it (no pci_set_master here on purpose yet), so any GPU-initiated
+ * sysmem DMA (the FWSEC generic-BL code/data fetch) would never
+ * complete. Log the command register at claim so the state is
+ * provable from the cycle log.
+ */
+ pci_read_config_word(pdev, PCI_COMMAND, &pci_cmd);
+ pr_info("gsp-phys: claim PCI_COMMAND=0x%04x [bus_master=%s mem=%s io=%s] dev->is_busmaster=%u\n",
+ pci_cmd, pci_cmd & PCI_COMMAND_MASTER ? "on" : "OFF",
+ pci_cmd & PCI_COMMAND_MEMORY ? "on" : "off",
+ pci_cmd & PCI_COMMAND_IO ? "on" : "off", pdev->is_busmaster);
+
ret = pci_request_regions(pdev, "open-iov-gsp-phys");
if (ret) {
pr_err("gsp-phys: pci_request_regions failed: %d\n", ret);