[PATCH 07/13] gpu: nova-core: gsp: ensure lifetime for FMC boot DMA allocations

From: Eliot Courtney

Date: Mon Jun 15 2026 - 10:47:39 EST


Currently, `FmcBootArgs` takes DMA handles directly, rather than
references to the `Coherent` for them. This is error prone, so instead
store lifetime'd references to the `Coherent` allocation.

Signed-off-by: Eliot Courtney <ecourtney@xxxxxxxxxx>
---
drivers/gpu/nova-core/fsp.rs | 32 ++++++++++++++++++++------------
drivers/gpu/nova-core/gsp.rs | 8 +++-----
drivers/gpu/nova-core/gsp/hal/gh100.rs | 19 +++++++------------
3 files changed, 30 insertions(+), 29 deletions(-)

diff --git a/drivers/gpu/nova-core/fsp.rs b/drivers/gpu/nova-core/fsp.rs
index 4b97d1fb505e..3f3211eae4d0 100644
--- a/drivers/gpu/nova-core/fsp.rs
+++ b/drivers/gpu/nova-core/fsp.rs
@@ -39,7 +39,11 @@
FIRMWARE_VERSION, //
},
gpu::Chipset,
- gsp::GspFmcBootParams,
+ gsp::{
+ GspFmcBootParams,
+ GspFwWprMeta,
+ LibosMemoryRegionInitArgument, //
+ },
mctp::{
MctpHeader,
NvdmHeader,
@@ -133,7 +137,7 @@ impl FspCotMessage {
fn new<'a>(
fb_layout: &FbLayout,
fsp_fw: &'a FspFirmware,
- args: &'a FmcBootArgs,
+ args: &'a FmcBootArgs<'_>,
) -> Result<impl Init<Self> + 'a> {
// frts_vidmem_offset is measured from the end of FB, so FRTS sits at
// (end of FB) - frts_vidmem_offset.
@@ -187,35 +191,39 @@ impl MessageToFsp for FspCotMessage {
}

/// Bundled arguments for FMC boot via FSP Chain of Trust.
-pub(crate) struct FmcBootArgs {
+pub(crate) struct FmcBootArgs<'a> {
chipset: Chipset,
fmc_boot_params: Coherent<GspFmcBootParams>,
resume: bool,
+ // Additional dependencies required to be kept alive for FMC boot.
+ _wpr_meta: &'a Coherent<GspFwWprMeta>,
+ _libos: &'a Coherent<[LibosMemoryRegionInitArgument]>,
}

-impl FmcBootArgs {
+impl<'a> FmcBootArgs<'a> {
/// Builds FMC boot arguments, allocating the DMA-coherent boot parameter
/// structure that FSP will read.
pub(crate) fn new(
dev: &device::Device<device::Bound>,
chipset: Chipset,
- wpr_meta_addr: u64,
- libos_addr: u64,
+ wpr_meta: &'a Coherent<GspFwWprMeta>,
+ libos: &'a Coherent<[LibosMemoryRegionInitArgument]>,
resume: bool,
) -> Result<Self> {
- let init = GspFmcBootParams::new(wpr_meta_addr, libos_addr);
+ let init = GspFmcBootParams::new(wpr_meta.dma_handle(), libos.dma_handle());

Ok(Self {
chipset,
fmc_boot_params: Coherent::<GspFmcBootParams>::init(dev, GFP_KERNEL, init)?,
resume,
+ _wpr_meta: wpr_meta,
+ _libos: libos,
})
}

- /// DMA address of the FMC boot parameters, needed after boot for lockdown
- /// release polling.
- pub(crate) fn boot_params_dma_handle(&self) -> u64 {
- self.fmc_boot_params.dma_handle()
+ /// Returns the FMC boot parameters allocation.
+ pub(crate) fn boot_params(&self) -> &Coherent<GspFmcBootParams> {
+ &self.fmc_boot_params
}
}

@@ -332,7 +340,7 @@ pub(crate) fn boot_fmc(
dev: &device::Device<device::Bound>,
bar: Bar0<'_>,
fb_layout: &FbLayout,
- args: &FmcBootArgs,
+ args: &FmcBootArgs<'_>,
) -> Result {
dev_dbg!(dev, "Starting FSP boot sequence for {}\n", args.chipset);

diff --git a/drivers/gpu/nova-core/gsp.rs b/drivers/gpu/nova-core/gsp.rs
index 385b4c09582b..b93c1fe8461e 100644
--- a/drivers/gpu/nova-core/gsp.rs
+++ b/drivers/gpu/nova-core/gsp.rs
@@ -28,16 +28,14 @@
pub(crate) use fw::{
GspFmcBootParams,
GspFwWprMeta,
+ LibosMemoryRegionInitArgument,
LibosParams, //
};

use crate::{
gsp::cmdq::Cmdq,
- gsp::fw::{
- GspArgumentsPadded,
- LibosMemoryRegionInitArgument, //
- },
- num,
+ gsp::fw::GspArgumentsPadded,
+ num, //
};

pub(crate) const GSP_PAGE_SHIFT: usize = 12;
diff --git a/drivers/gpu/nova-core/gsp/hal/gh100.rs b/drivers/gpu/nova-core/gsp/hal/gh100.rs
index 3e499563c9bc..31498ae7abd2 100644
--- a/drivers/gpu/nova-core/gsp/hal/gh100.rs
+++ b/drivers/gpu/nova-core/gsp/hal/gh100.rs
@@ -30,6 +30,7 @@
UnloadBundle, //
},
Gsp,
+ GspFmcBootParams,
GspFwWprMeta, //
},
};
@@ -62,13 +63,13 @@ fn lockdown_released_or_error(
&self,
gsp_falcon: &Falcon<GspEngine>,
bar: Bar0<'_>,
- fmc_boot_params_addr: u64,
+ fmc_boot_params: &Coherent<GspFmcBootParams>,
) -> bool {
// GSP-FMC normally clears the boot parameters address from the mailboxes early during
// boot. If the address is still there, keep polling rather than treating it as an error.
// Any other non-zero mailbox0 value is a GSP-FMC error code.
if self.mbox0 != 0 {
- return self.combined_addr() != fmc_boot_params_addr;
+ return self.combined_addr() != fmc_boot_params.dma_handle();
}

!gsp_falcon.riscv_branch_privilege_lockdown(bar)
@@ -80,7 +81,7 @@ fn wait_for_gsp_lockdown_release(
dev: &device::Device<device::Bound>,
bar: Bar0<'_>,
gsp_falcon: &Falcon<GspEngine>,
- fmc_boot_params_addr: u64,
+ fmc_boot_params: &Coherent<GspFmcBootParams>,
) -> Result {
dev_dbg!(dev, "Waiting for GSP lockdown release\n");

@@ -95,7 +96,7 @@ fn wait_for_gsp_lockdown_release(
},
|mbox| match mbox {
None => false,
- Some(mbox) => mbox.lockdown_released_or_error(gsp_falcon, bar, fmc_boot_params_addr),
+ Some(mbox) => mbox.lockdown_released_or_error(gsp_falcon, bar, fmc_boot_params),
},
Delta::from_millis(10),
Delta::from_secs(30),
@@ -156,13 +157,7 @@ fn boot<'a>(
gsp_falcon: &'a Falcon<GspEngine>,
sec2_falcon: &'a Falcon<Sec2>,
) -> Result<BootUnloadGuard<'a>> {
- let args = FmcBootArgs::new(
- dev,
- chipset,
- wpr_meta.dma_handle(),
- gsp.libos.dma_handle(),
- false,
- )?;
+ let args = FmcBootArgs::new(dev, chipset, wpr_meta, &gsp.libos, false)?;

let mut fsp = Fsp::wait_secure_boot(dev, bar, chipset)?;

@@ -176,7 +171,7 @@ fn boot<'a>(

fsp.boot_fmc(dev, bar, fb_layout, &args)?;

- wait_for_gsp_lockdown_release(dev, bar, gsp_falcon, args.boot_params_dma_handle())?;
+ wait_for_gsp_lockdown_release(dev, bar, gsp_falcon, args.boot_params())?;

Ok(unload_guard)
}

--
2.54.0