[PATCH 3/3] gpu: nova-core: gsp: map the WPR meta for streaming DMA
From: Maurice Hieronymus
Date: Wed Aug 05 2026 - 17:57:24 EST
`GspFwWprMeta` is filled in once during boot, read out of system memory
by the booter, and dropped when `Gsp::boot()` returns. That is a
streaming transfer, so a coherent allocation buys nothing.
Map a `KBox<GspFwWprMeta>` instead. The mapping is submitted right
after initialization, which makes the contents unreachable for the
duration of the chipset-specific boot sequence and lets `dma_handle()`
be read through a shared reference.
`complete()` is called as soon as `hal.boot()` succeeds, the earliest
point the device is provably done with the metadata: on Tu102 the
Booter-load falcon has halted, on GH100 GSP-FMC has released the
lockdown. If `hal.boot()` fails instead, no such proof exists --
`Gsp::unload()` deliberately carries on past failed steps, so the
falcon may still be reading the buffer -- and `wpr_meta` drops in
flight, trading a one-off leak for a device-side use-after-free.
Signed-off-by: Maurice Hieronymus <mhi@xxxxxxxxxxx>
---
drivers/gpu/nova-core/firmware/booter.rs | 11 +++++++----
drivers/gpu/nova-core/gsp/boot.rs | 20 ++++++++++++++++++--
drivers/gpu/nova-core/gsp/hal.rs | 4 ++--
drivers/gpu/nova-core/gsp/hal/gh100.rs | 4 ++--
drivers/gpu/nova-core/gsp/hal/tu102.rs | 4 ++--
5 files changed, 31 insertions(+), 12 deletions(-)
diff --git a/drivers/gpu/nova-core/firmware/booter.rs b/drivers/gpu/nova-core/firmware/booter.rs
index d9313ac361af..780c7702a777 100644
--- a/drivers/gpu/nova-core/firmware/booter.rs
+++ b/drivers/gpu/nova-core/firmware/booter.rs
@@ -9,9 +9,12 @@
use kernel::{
device,
- dma::Coherent,
+ dma::StreamingInFlight,
prelude::*,
- transmute::FromBytes, //
+ transmute::{
+ AsBytes,
+ FromBytes, //
+ },
};
use crate::{
@@ -402,12 +405,12 @@ pub(crate) fn new(
///
/// Resets SEC2, loads this firmware image, then boots with the WPR metadata
/// address passed via the SEC2 mailboxes.
- pub(crate) fn run<T>(
+ pub(crate) fn run<T: FromBytes + AsBytes>(
&self,
dev: &device::Device<device::Bound>,
bar: Bar0<'_>,
sec2_falcon: &Falcon<Sec2>,
- wpr_meta: &Coherent<T>,
+ wpr_meta: &StreamingInFlight<'_, KBox<T>>,
) -> Result {
sec2_falcon.reset(bar)?;
sec2_falcon.load(dev, bar, self)?;
diff --git a/drivers/gpu/nova-core/gsp/boot.rs b/drivers/gpu/nova-core/gsp/boot.rs
index 8afb62d689cb..300ebf4e843d 100644
--- a/drivers/gpu/nova-core/gsp/boot.rs
+++ b/drivers/gpu/nova-core/gsp/boot.rs
@@ -4,7 +4,10 @@
use kernel::{
bits,
device,
- dma::Coherent,
+ dma::{
+ DataDirection,
+ Streaming, //
+ },
io::poll::read_poll_timeout,
pci,
prelude::*,
@@ -117,7 +120,12 @@ pub(crate) fn boot(
let fb_layout = FbLayout::new(chipset, bar, &gsp_fw)?;
dev_dbg!(dev, "{:#x?}\n", fb_layout);
- let wpr_meta = Coherent::init(dev, GFP_KERNEL, GspFwWprMeta::new(&gsp_fw, &fb_layout))?;
+ let wpr_meta = Streaming::new(
+ dev,
+ KBox::init(GspFwWprMeta::new(&gsp_fw, &fb_layout), GFP_KERNEL)?,
+ DataDirection::ToDevice,
+ )?
+ .submit();
// Perform the chipset-specific boot sequence, and retrieve the unload bundle.
let unload_guard = hal.boot(
@@ -131,6 +139,14 @@ pub(crate) fn boot(
sec2_falcon,
)?;
+ // The chipset-specific boot sequence only succeeds once the device is done reading the
+ // WPR metadata: on Tu102 the Booter-load falcon has halted, on GH100 GSP-FMC has released
+ // the lockdown. If it fails instead, `wpr_meta` drops in flight and leaks, as the falcon
+ // may still be reading the buffer.
+ //
+ // SAFETY: Per the above, the device has finished accessing the buffer.
+ let _ = unsafe { wpr_meta.complete() };
+
gsp_falcon.write_os_version(bar, gsp_fw.bootloader.app_version);
// Poll for RISC-V to become active before continuing.
diff --git a/drivers/gpu/nova-core/gsp/hal.rs b/drivers/gpu/nova-core/gsp/hal.rs
index 04f004856c60..09a523e3a180 100644
--- a/drivers/gpu/nova-core/gsp/hal.rs
+++ b/drivers/gpu/nova-core/gsp/hal.rs
@@ -8,7 +8,7 @@
use kernel::{
device,
- dma::Coherent, //
+ dma::StreamingInFlight, //
};
use crate::{
@@ -61,7 +61,7 @@ fn boot<'a>(
bar: Bar0<'a>,
chipset: Chipset,
fb_layout: &FbLayout,
- wpr_meta: &Coherent<GspFwWprMeta>,
+ wpr_meta: &StreamingInFlight<'_, KBox<GspFwWprMeta>>,
gsp_falcon: &'a Falcon<GspEngine>,
sec2_falcon: &'a Falcon<Sec2>,
) -> Result<BootUnloadGuard<'a>>;
diff --git a/drivers/gpu/nova-core/gsp/hal/gh100.rs b/drivers/gpu/nova-core/gsp/hal/gh100.rs
index 98f5ce197d13..67a79c54a739 100644
--- a/drivers/gpu/nova-core/gsp/hal/gh100.rs
+++ b/drivers/gpu/nova-core/gsp/hal/gh100.rs
@@ -5,7 +5,7 @@
use kernel::{
device,
- dma::Coherent,
+ dma::StreamingInFlight,
io::poll::read_poll_timeout,
time::Delta, //
};
@@ -156,7 +156,7 @@ fn boot<'a>(
bar: Bar0<'a>,
chipset: Chipset,
fb_layout: &FbLayout,
- wpr_meta: &Coherent<GspFwWprMeta>,
+ wpr_meta: &StreamingInFlight<'_, KBox<GspFwWprMeta>>,
gsp_falcon: &'a Falcon<GspEngine>,
sec2_falcon: &'a Falcon<Sec2>,
) -> Result<BootUnloadGuard<'a>> {
diff --git a/drivers/gpu/nova-core/gsp/hal/tu102.rs b/drivers/gpu/nova-core/gsp/hal/tu102.rs
index 2f6301af7113..7d906de6d8bf 100644
--- a/drivers/gpu/nova-core/gsp/hal/tu102.rs
+++ b/drivers/gpu/nova-core/gsp/hal/tu102.rs
@@ -5,7 +5,7 @@
use kernel::{
device,
- dma::Coherent,
+ dma::StreamingInFlight,
io::Io, //
};
@@ -262,7 +262,7 @@ fn boot<'a>(
bar: Bar0<'a>,
chipset: Chipset,
fb_layout: &FbLayout,
- wpr_meta: &Coherent<GspFwWprMeta>,
+ wpr_meta: &StreamingInFlight<'_, KBox<GspFwWprMeta>>,
gsp_falcon: &'a Falcon<GspEngine>,
sec2_falcon: &'a Falcon<Sec2>,
) -> Result<BootUnloadGuard<'a>> {
--
2.54.0