summaryrefslogtreecommitdiffstats
path: root/kernel/gsp_phys_boot.c
diff options
context:
space:
mode:
authorArthur Hanson Rasmusson <arthur@vgpu.io>2026-10-01 00:55:56 -0400
committerArthur Hanson Rasmusson <arthur@vgpu.io>2026-10-01 00:55:56 -0400
commitbef0d867a0d666b98123d4e19edd3084ea95c3c0 (patch)
tree7e32d341455be519ecc64d25822b6a6dde56b16a /kernel/gsp_phys_boot.c
parent5e3ada9c45d72688d1744f6a6191fb2bb4bbd319 (diff)
downloadopen-iov-bef0d867a0d666b98123d4e19edd3084ea95c3c0.tar.gz
open-iov-bef0d867a0d666b98123d4e19edd3084ea95c3c0.zip
gsp-phys: decode the real FB size for the boot layout (stage 3b-prime)
Diffstat (limited to 'kernel/gsp_phys_boot.c')
-rw-r--r--kernel/gsp_phys_boot.c104
1 files changed, 96 insertions, 8 deletions
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 <linux/module.h>
@@ -18,6 +19,7 @@
#include <linux/dma-mapping.h>
#include <linux/slab.h>
#include <linux/limits.h>
+#include <linux/io.h>
#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)
@@ -142,6 +159,59 @@ static u64 gsp_phys_align_up(u64 value, u64 align)
}
/*
+ * 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
* allocation term, clamped to the architecture min/max and to the pre-scrubbed
@@ -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 -