From 8065d67e9a7e2f22490406755e4df429b89136d0 Mon Sep 17 00:00:00 2001 From: Arthur Hanson Rasmusson Date: Thu, 1 Oct 2026 00:55:56 -0400 Subject: gsp-phys: decode the real FB size for the boot layout (stage 3b-prime) --- kernel/gsp_phys_boot.c | 104 +++++++++++++++++++++++++++++++++++++++++++++---- kernel/gsp_phys_boot.h | 9 +++-- kernel/gsp_phys_gpu.c | 2 +- 3 files changed, 103 insertions(+), 12 deletions(-) (limited to 'kernel') diff --git a/kernel/gsp_phys_boot.c b/kernel/gsp_phys_boot.c index 0118e06..0322c70 100644 --- a/kernel/gsp_phys_boot.c +++ b/kernel/gsp_phys_boot.c @@ -7,9 +7,10 @@ * layout computation the distribution driver performs for Turing, fills the * 256-byte WprMeta structure and logs every field. * - * Stage 3b writes nothing to the device: no BAR/register access happens here, - * the metadata page is filled in host memory only. Disabled by default - * (gsp_phys_layout=N); claim must be enabled too (gsp_phys_gpu=1). + * Stage 3b writes nothing to the device: the only device access is a + * read-only sample of the video-memory-size register in BAR0; the metadata + * page is filled in host memory only. Disabled by default (gsp_phys_layout=N); + * claim must be enabled too (gsp_phys_gpu=1). */ #include @@ -18,6 +19,7 @@ #include #include #include +#include #include "gsp_phys_boot.h" @@ -60,6 +62,21 @@ bool gsp_phys_layout_enabled(void) #define GSP_PHYS_FW_HEAP_MAX_MB 256 #define GSP_PHYS_PRESCRUBBED_TOP_FB (256 * GSP_PHYS_MB) +/* + * Video-memory size register in the PFB PRI MMU block (read-only). The VBIOS + * devinit programs the lower bound of the usable local-memory range before + * the driver runs: a 6-bit mantissa in bits 9:4 scaled by 2^(scale+20), with + * the ECC carve-out reported in bit 30. + */ +#define GSP_PHYS_FB_LOCAL_MEMORY_RANGE 0x00100ce0 +#define GSP_PHYS_FB_LOWER_SCALE 0x0000000f +#define GSP_PHYS_FB_LOWER_SCALE_SHIFT 0 +#define GSP_PHYS_FB_LOWER_MAG 0x000003f0 +#define GSP_PHYS_FB_LOWER_MAG_SHIFT 4 +#define GSP_PHYS_FB_ECC_MODE BIT(30) +#define GSP_PHYS_FB_SIZE_MIN (256 * GSP_PHYS_MB) +#define GSP_PHYS_FB_SIZE_MAX (64ULL << 30) + #define GSP_PHYS_WPR_META_MAGIC 0xdc3aae21371a60b3ULL #define GSP_PHYS_WPR_META_REVISION 1 #define GSP_PHYS_WPR_META_FLAG_CLOCK_BOOST (1U << 0) @@ -141,6 +158,59 @@ static u64 gsp_phys_align_up(u64 value, u64 align) return (value + align - 1) & ~(align - 1); } +/* + * Decode the real usable FB size from the VBIOS-programmed local-memory-range + * register: mantissa (bits 9:4) << (scale (bits 3:0) + 20), reduced by 1/16 + * when ECC is enabled (bit 30). Read-only. Returns 0 with the decoded byte + * size, or a negative error when the register decodes to zero or garbage - + * the caller must fail the layout, never fall back to the BAR1 aperture. + */ +static int gsp_phys_fb_size_decode(void __iomem *bar0, u64 fw_image_size, + u64 *out_fb_size) +{ + u32 reg, mag, scale; + bool ecc; + u64 fb_size; + + reg = readl(bar0 + GSP_PHYS_FB_LOCAL_MEMORY_RANGE); + mag = (reg & GSP_PHYS_FB_LOWER_MAG) >> GSP_PHYS_FB_LOWER_MAG_SHIFT; + scale = (reg & GSP_PHYS_FB_LOWER_SCALE) >> GSP_PHYS_FB_LOWER_SCALE_SHIFT; + ecc = !!(reg & GSP_PHYS_FB_ECC_MODE); + + fb_size = (u64)mag << (scale + 20); + if (ecc) + fb_size = fb_size / 16 * 15; + + pr_info("gsp-phys-boot: FB size register[0x%06x]=0x%08x lowerMag=%u lowerScale=%u ecc=%u -> real usable FB size 0x%llx (%llu GiB)\n", + GSP_PHYS_FB_LOCAL_MEMORY_RANGE, reg, mag, scale, + (unsigned int)ecc, fb_size, fb_size >> 30); + + if (!mag || !fb_size) { + pr_err("gsp-phys-boot: FB size register decodes to zero (reg=0x%08x mag=%u scale=%u); refusing layout\n", + reg, mag, scale); + return -EINVAL; + } + if (fb_size < GSP_PHYS_FB_SIZE_MIN || fb_size > GSP_PHYS_FB_SIZE_MAX) { + pr_err("gsp-phys-boot: decoded real FB size 0x%llx outside sane range [0x%llx..0x%llx]; refusing layout\n", + fb_size, (u64)GSP_PHYS_FB_SIZE_MIN, + (u64)GSP_PHYS_FB_SIZE_MAX); + return -ERANGE; + } + if (!IS_ALIGNED(fb_size, GSP_PHYS_PAGE_SIZE)) { + pr_err("gsp-phys-boot: decoded real FB size 0x%llx is not page-aligned; refusing layout\n", + fb_size); + return -ERANGE; + } + if (fw_image_size && fb_size <= fw_image_size) { + pr_err("gsp-phys-boot: decoded real FB size 0x%llx does not fit the staged GSP-RM image (0x%llx); refusing layout\n", + fb_size, fw_image_size); + return -ERANGE; + } + + *out_fb_size = fb_size; + return 0; +} + /* * GSP FW (WPR) heap size for a bare-metal Turing part. Ported from the * reference: a base RM reservation, a per-GB-of-FB term, and a client-channel @@ -221,9 +291,11 @@ static void gsp_phys_wpr_meta_dump(const struct gsp_phys_wpr_meta *m) * compute/record the WPR layout in the metadata page. No device writes. */ int gsp_phys_boot_layout_init(struct gsp_phys_boot *boot, struct pci_dev *pdev, - u64 fw_image_size, resource_size_t usable_fb_size) + void __iomem *bar0, u64 fw_image_size, + resource_size_t bar1_len) { struct gsp_phys_wpr_meta *meta; + u64 usable_fb_size; u64 vbios_reserved_offset; u64 non_wpr_heap_size, wpr_meta_size; u64 pre_wpr_heap, post_wpr_heap, wpr_heap_size; @@ -231,15 +303,23 @@ int gsp_phys_boot_layout_init(struct gsp_phys_boot *boot, struct pci_dev *pdev, if (!gsp_phys_layout_enabled() || boot->initialized) return 0; - if (!pdev || !fw_image_size) + if (!pdev || !bar0 || !fw_image_size) return -EINVAL; BUILD_BUG_ON(sizeof(struct gsp_phys_wpr_meta) != 256); - pr_info("gsp-phys-boot: stage 3b layout+allocation on %s (no device writes)\n", + pr_info("gsp-phys-boot: stage 3b layout+allocation on %s (BAR0 read-only, no device writes)\n", pci_name(pdev)); - pr_info("gsp-phys-boot: usable FB = 0x%llx (approximation: BAR1 aperture length, NOT the physical FB size)\n", - (u64)usable_fb_size); + + ret = gsp_phys_fb_size_decode(bar0, fw_image_size, &usable_fb_size); + if (ret) + return ret; + + pr_info("gsp-phys-boot: real FB size = 0x%llx (%llu GiB), replacing the BAR1-aperture approximation\n", + usable_fb_size, usable_fb_size >> 30); + pr_info("gsp-phys-boot: BAR1 aperture = 0x%llx (%llu MiB); decoded real FB size difference %+lld bytes\n", + (u64)bar1_len, (u64)bar1_len >> 20, + (long long)((s64)usable_fb_size - (s64)bar1_len)); pr_info("gsp-phys-boot: staged GSP-RM image (.fwimage) size = 0x%llx\n", fw_image_size); pr_info("gsp-phys-boot: stub: boot binary size assumed 0x%x (boot ucode is compiled-in bindata, not a .fwimage section)\n", @@ -313,6 +393,14 @@ int gsp_phys_boot_layout_init(struct gsp_phys_boot *boot, struct pci_dev *pdev, wpr_heap_size = gsp_phys_fw_heap_size(usable_fb_size, pre_wpr_heap, post_wpr_heap); + if (meta->gsp_fw_offset < pre_wpr_heap + wpr_heap_size) { + pr_err("gsp-phys-boot: real FB size 0x%llx too small: image (0x%llx) + WPR heap (0x%llx) + pre-WPR regions (0x%llx) do not fit below the VGA workspace\n", + usable_fb_size, meta->size_of_radix3_elf, wpr_heap_size, + pre_wpr_heap); + ret = -ERANGE; + goto err; + } + meta->gsp_fw_heap_offset = gsp_phys_align_down(meta->gsp_fw_offset - wpr_heap_size, GSP_PHYS_MB); meta->gsp_fw_heap_size = gsp_phys_align_down(meta->gsp_fw_offset - diff --git a/kernel/gsp_phys_boot.h b/kernel/gsp_phys_boot.h index 8ab203a..3f6ad6e 100644 --- a/kernel/gsp_phys_boot.h +++ b/kernel/gsp_phys_boot.h @@ -8,8 +8,10 @@ /* * Real-GSP boot bring-up stage 3b: boot-argument DMA memory and the computed * WPR/FB layout. Allocated and filled by gsp_phys_boot.c while the module owns - * the physical GPU. Stage 3b writes no device registers; the WPR metadata page - * is filled in host memory only, ready for the later FWSEC/booter stages. + * the physical GPU. Stage 3b writes no device registers; the only device + * access is a read-only sample of the video-memory-size register in BAR0, and + * the WPR metadata page is filled in host memory only, ready for the later + * FWSEC/booter stages. */ struct gsp_phys_boot { bool initialized; @@ -46,7 +48,8 @@ struct gsp_phys_boot { bool gsp_phys_layout_enabled(void); int gsp_phys_boot_layout_init(struct gsp_phys_boot *boot, struct pci_dev *pdev, - u64 fw_image_size, resource_size_t usable_fb_size); + void __iomem *bar0, u64 fw_image_size, + resource_size_t bar1_len); void gsp_phys_boot_layout_fini(struct gsp_phys_boot *boot, struct pci_dev *pdev); #endif /* GSP_PHYS_BOOT_H */ diff --git a/kernel/gsp_phys_gpu.c b/kernel/gsp_phys_gpu.c index a2ff631..a0f564f 100644 --- a/kernel/gsp_phys_gpu.c +++ b/kernel/gsp_phys_gpu.c @@ -696,7 +696,7 @@ int gsp_phys_gpu_claim(void) pr_info("gsp-phys: real GSP firmware staged (backend rung 2)\n"); if (gsp_phys_layout_enabled() && - gsp_phys_boot_layout_init(&phys.boot, phys.pdev, + gsp_phys_boot_layout_init(&phys.boot, phys.pdev, phys.bar0, phys.fw_image_size, phys.bar1_len)) pr_warn("gsp-phys: boot layout/allocation failed; " "backend stays at rung 2\n"); -- cgit v1.2.3