drm/nouveau: verify that hardware supports the flush page address
authorTimur Tabi <ttabi@nvidia.com>
Thu, 13 Nov 2025 23:03:23 +0000 (17:03 -0600)
committerLyude Paul <lyude@redhat.com>
Mon, 24 Nov 2025 22:53:22 +0000 (17:53 -0500)
Ensure that the DMA address of the framebuffer flush page is not larger
than its hardware register.

On GPUs older than Hopper, the register for the address can hold up to a
40-bit address (right-shifted by 8 so that it fits in the 32-bit
register), and on Hopper and later it can be 52 bits (64-bit register
where bits 52-63 must be zero).

Recently it was discovered that under certain conditions, the flush page
could be allocated outside this range.  Although this bug was fixed, we
can ensure that any future changes to this code don't accidentally
generate an invalid page address.

Signed-off-by: Timur Tabi <ttabi@nvidia.com>
Reviewed-by: Lyude Paul <lyude@redhat.com>
Signed-off-by: Lyude Paul <lyude@redhat.com>
Link: https://patch.msgid.link/20251113230323.1271726-2-ttabi@nvidia.com
drivers/gpu/drm/nouveau/nvkm/subdev/fb/gb100.c
drivers/gpu/drm/nouveau/nvkm/subdev/fb/gb202.c
drivers/gpu/drm/nouveau/nvkm/subdev/fb/gf100.c
drivers/gpu/drm/nouveau/nvkm/subdev/fb/gh100.c
drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv50.c

index 1c78c88..170776c 100644 (file)
@@ -15,6 +15,9 @@ gb100_fb_sysmem_flush_page_init(struct nvkm_fb *fb)
        const u32 hshub = DRF_LO(NV_PFB_HSHUB0);
        struct nvkm_device *device = fb->subdev.device;
 
+       // Ensure that the address is within hardware limits
+       WARN_ON(fb->sysmem.flush_page_addr > DMA_BIT_MASK(52));
+
        nvkm_wr32(device, hshub + NV_PFB_HSHUB_PCIE_FLUSH_SYSMEM_ADDR_HI, addr_hi);
        nvkm_wr32(device, hshub + NV_PFB_HSHUB_PCIE_FLUSH_SYSMEM_ADDR_LO, addr_lo);
        nvkm_wr32(device, hshub + NV_PFB_HSHUB_EG_PCIE_FLUSH_SYSMEM_ADDR_HI, addr_hi);
index 8485050..a21bf19 100644 (file)
@@ -13,6 +13,9 @@ gb202_fb_sysmem_flush_page_init(struct nvkm_fb *fb)
        struct nvkm_device *device = fb->subdev.device;
        const u64 addr = fb->sysmem.flush_page_addr;
 
+       // Ensure that the address is within hardware limits
+       WARN_ON(fb->sysmem.flush_page_addr > DMA_BIT_MASK(52));
+
        nvkm_wr32(device, NV_PFB_FBHUB0_PCIE_FLUSH_SYSMEM_ADDR_HI, upper_32_bits(addr));
        nvkm_wr32(device, NV_PFB_FBHUB0_PCIE_FLUSH_SYSMEM_ADDR_LO, lower_32_bits(addr));
 }
index 07db9b3..64281a0 100644 (file)
@@ -80,6 +80,9 @@ gf100_fb_init_page(struct nvkm_fb *fb)
 void
 gf100_fb_sysmem_flush_page_init(struct nvkm_fb *fb)
 {
+       // Ensure that the address can actually fit in the register
+       WARN_ON(fb->sysmem.flush_page_addr > DMA_BIT_MASK(40));
+
        nvkm_wr32(fb->subdev.device, 0x100c10, fb->sysmem.flush_page_addr >> 8);
 }
 
index 2d8c51f..8c93940 100644 (file)
@@ -13,6 +13,9 @@ gh100_fb_sysmem_flush_page_init(struct nvkm_fb *fb)
        const u64 addr = fb->sysmem.flush_page_addr >> NV_PFB_NISO_FLUSH_SYSMEM_ADDR_SHIFT;
        struct nvkm_device *device = fb->subdev.device;
 
+       // Ensure that the address is within hardware limits
+       WARN_ON(fb->sysmem.flush_page_addr > DMA_BIT_MASK(52));
+
        nvkm_wr32(device, NV_PFB_FBHUB_PCIE_FLUSH_SYSMEM_ADDR_HI, upper_32_bits(addr));
        nvkm_wr32(device, NV_PFB_FBHUB_PCIE_FLUSH_SYSMEM_ADDR_LO, lower_32_bits(addr));
 }
index a6efbd9..076d968 100644 (file)
@@ -214,6 +214,9 @@ nv50_fb_tags(struct nvkm_fb *base)
 static void
 nv50_fb_sysmem_flush_page_init(struct nvkm_fb *fb)
 {
+       // Ensure that the address can actually fit in the register
+       WARN_ON(fb->sysmem.flush_page_addr > DMA_BIT_MASK(40));
+
        nvkm_wr32(fb->subdev.device, 0x100c08, fb->sysmem.flush_page_addr >> 8);
 }