summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArthur Hanson Rasmusson <arthur@vgpu.io>2026-10-01 01:21:50 -0400
committerArthur Hanson Rasmusson <arthur@vgpu.io>2026-10-01 01:21:50 -0400
commit0055d732c38518bae3a0a58e8d4a6e427317ad8c (patch)
tree33917dd20c82a3003053440305b5aa88204cef5f
parent60afa9a299889e247b0dee1570956b9fbedfbf77 (diff)
downloadopen-iov-0055d732c38518bae3a0a58e8d4a6e427317ad8c.tar.gz
open-iov-0055d732c38518bae3a0a58e8d4a6e427317ad8c.zip
gsp-phys: fix the WPR2-up release hand-back and tighten FWSEC/VBIOS checks
-rw-r--r--kernel/gsp_phys_fwsec.c30
-rw-r--r--kernel/gsp_phys_vbios.c30
2 files changed, 41 insertions, 19 deletions
diff --git a/kernel/gsp_phys_fwsec.c b/kernel/gsp_phys_fwsec.c
index 93a4208..a6f81e2 100644
--- a/kernel/gsp_phys_fwsec.c
+++ b/kernel/gsp_phys_fwsec.c
@@ -413,22 +413,29 @@ static int gsp_phys_falcon_wait_halt(void __iomem *bar0, u32 base,
}
}
-/* Wait for GFW boot completion: falcon halted and progress 0xff. */
+/* Wait for GFW boot completion: falcon halted, PLM lowered and progress 0xff. */
static int gsp_phys_fwsec_wait_gfw_boot(void __iomem *bar0)
{
unsigned long deadline = jiffies +
msecs_to_jiffies(GSP_PHYS_GFW_BOOT_TIMEOUT_MS);
- u32 cpuctl = 0, progress = 0;
+ u32 cpuctl = 0, progress = 0, plm = 0;
for (;;) {
cpuctl = readl(bar0 + GSP_PHYS_GSP_BASE +
GSP_PHYS_FALCON_CPUCTL);
+ plm = readl(bar0 + GSP_PHYS_GFW_PLM);
progress = readl(bar0 + GSP_PHYS_GFW_BOOT) &
GSP_PHYS_GFW_BOOT_PROGRESS_MASK;
+ /*
+ * FWSEC lowers its read-protection level before GFW posts
+ * progress; level0 must be readable for the progress to be
+ * meaningful.
+ */
if ((cpuctl & GSP_PHYS_CPUCTL_HALTED) &&
+ (plm & GSP_PHYS_GFW_PLM_LEVEL0) &&
progress == GSP_PHYS_GFW_BOOT_COMPLETED) {
- pr_info("gsp-phys-fwsec: GFW boot complete: CPUCTL=0x%08x (halted) progress=0xff\n",
- cpuctl);
+ pr_info("gsp-phys-fwsec: GFW boot complete: CPUCTL=0x%08x (halted) PLM=0x%08x progress=0xff\n",
+ cpuctl, plm);
return 0;
}
if (time_after_eq(jiffies, deadline))
@@ -436,8 +443,8 @@ static int gsp_phys_fwsec_wait_gfw_boot(void __iomem *bar0)
msleep(1);
}
- pr_err("gsp-phys-fwsec: GFW boot not complete (CPUCTL=0x%08x progress=0x%x, timeout %ums); not resetting or kicking the falcon\n",
- cpuctl, progress, GSP_PHYS_GFW_BOOT_TIMEOUT_MS);
+ pr_err("gsp-phys-fwsec: GFW boot not complete (CPUCTL=0x%08x PLM=0x%08x progress=0x%x, timeout %ums); not resetting or kicking the falcon\n",
+ cpuctl, plm, progress, GSP_PHYS_GFW_BOOT_TIMEOUT_MS);
return -ETIMEDOUT;
}
@@ -876,6 +883,13 @@ int gsp_phys_fwsec_run(struct gsp_phys_boot *boot, struct pci_dev *pdev,
return -EINVAL;
}
+ if (boot->frts_size != ((u64)GSP_PHYS_FRTS_REGION_SIZE_4K << 12)) {
+ pr_err("gsp-phys-fwsec: FRTS region size mismatch: boot layout 0x%llx, FWSEC constant 0x%llx; refusing stage 3c\n",
+ boot->frts_size,
+ (u64)GSP_PHYS_FRTS_REGION_SIZE_4K << 12);
+ return -EINVAL;
+ }
+
pr_info("gsp-phys-fwsec: stage 3c FWSEC/FRTS on %s: frtsOffset=0x%llx frtsSize=0x%llx\n",
pci_name(pdev), boot->frts_offset, boot->frts_size);
@@ -991,10 +1005,10 @@ int gsp_phys_fwsec_release(struct gsp_phys_boot *boot, struct pci_dev *pdev,
{
int ret;
- if (!fwsec.touched)
- return 0;
if (!pdev || !bar0)
return -EIO;
+ if (!fwsec.touched && !gsp_phys_fwsec_wpr2_up(bar0))
+ return 0;
if (!gsp_phys_fwsec_wpr2_up(bar0)) {
pr_info("gsp-phys-fwsec: release: WPR2 already down; freeing FWSEC staging and handing back\n");
diff --git a/kernel/gsp_phys_vbios.c b/kernel/gsp_phys_vbios.c
index 4c35636..fa7849d 100644
--- a/kernel/gsp_phys_vbios.c
+++ b/kernel/gsp_phys_vbios.c
@@ -12,6 +12,7 @@
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/io.h>
+#include <linux/overflow.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <asm/unaligned.h>
@@ -210,6 +211,12 @@ static int gsp_phys_vbios_find_pci_header(void __iomem *bar0, u32 *pci_offset)
return -EINVAL;
}
+ if (image_offset & 0x3) {
+ pr_err("gsp-phys-vbios: IFR image offset 0x%x is not 4-byte aligned\n",
+ image_offset);
+ return -EINVAL;
+ }
+
if (image_offset > GSP_PHYS_VBIOS_MAX_SIZE - sizeof(u32)) {
pr_err("gsp-phys-vbios: IFR image offset 0x%x out of range\n",
image_offset);
@@ -400,7 +407,7 @@ int gsp_phys_vbios_read(struct gsp_phys_vbios *img, struct pci_dev *pdev,
void __iomem *bar0)
{
u32 pci_offset = 0, bios_size = 0, expansion_rom_offset = 0;
- u32 rom_sig;
+ u32 rom_sig, rom_end;
int ret;
if (!img || !bar0)
@@ -438,9 +445,10 @@ int gsp_phys_vbios_read(struct gsp_phys_vbios *img, struct pci_dev *pdev,
pr_err("gsp-phys-vbios: expansion ROM walk failed: %d\n", ret);
return ret;
}
- if (bios_size > GSP_PHYS_VBIOS_MAX_SIZE) {
- pr_err("gsp-phys-vbios: expansion ROM size 0x%x exceeds the window\n",
- bios_size);
+ if (check_add_overflow(pci_offset, bios_size, &rom_end) ||
+ rom_end > GSP_PHYS_VBIOS_MAX_SIZE) {
+ pr_err("gsp-phys-vbios: expansion ROM span 0x%x+0x%x exceeds the window\n",
+ pci_offset, bios_size);
return -EINVAL;
}
@@ -677,9 +685,7 @@ int gsp_phys_vbios_parse_fwsec(const struct gsp_phys_vbios *img,
&data_ptr))
continue;
} else {
- if (!gsp_phys_img_read(img, token_offset + 4, 2,
- &data_ptr))
- continue;
+ data_ptr = 0;
}
if (token_id == GSP_PHYS_BIT_TOKEN_BIOSDATA &&
@@ -703,10 +709,12 @@ int gsp_phys_vbios_parse_fwsec(const struct gsp_phys_vbios *img,
continue;
{
- u32 table_offset = img->expansion_rom_offset +
- table_ptr;
- u32 table_version, table_hdr_size, entry_size;
- u32 entry_count, j;
+ u32 table_offset, table_version, table_hdr_size;
+ u32 entry_size, entry_count, j;
+
+ if (check_add_overflow(img->expansion_rom_offset,
+ table_ptr, &table_offset))
+ continue;
if (!gsp_phys_img_read(img, table_offset, 1,
&table_version) ||