* [PATCH v3 01/12] gpu: nova-core: gsp: sequencer: use GspBootContext
2026-06-29 12:31 [PATCH v3 00/12] gpu: nova-core: consolidate and streamline GSP boot process Alexandre Courbot
@ 2026-06-29 12:31 ` Alexandre Courbot
2026-06-29 12:31 ` [PATCH v3 02/12] gpu: nova-core: gsp: sequencer: do not store sequence into GspSequencer Alexandre Courbot
` (11 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Alexandre Courbot @ 2026-06-29 12:31 UTC (permalink / raw)
To: Danilo Krummrich, Alice Ryhl, David Airlie, Simona Vetter,
Gary Guo, John Hubbard, Alistair Popple, Timur Tabi,
Eliot Courtney, Zhi Wang
Cc: nova-gpu, dri-devel, linux-kernel, rust-for-linux,
Alexandre Courbot
`GspBootContext` contains all the resources currently carried by
`GspSequencerParams`, so replace the latter with the former for better
integration with the boot process and less code.
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
Reviewed-by: Eliot Courtney <ecourtney@nvidia.com>
---
drivers/gpu/nova-core/gsp/hal/tu102.rs | 21 +++++++-------------
drivers/gpu/nova-core/gsp/sequencer.rs | 36 ++++++++++++----------------------
2 files changed, 20 insertions(+), 37 deletions(-)
diff --git a/drivers/gpu/nova-core/gsp/hal/tu102.rs b/drivers/gpu/nova-core/gsp/hal/tu102.rs
index f8a8541704ee..6ed4ee268086 100644
--- a/drivers/gpu/nova-core/gsp/hal/tu102.rs
+++ b/drivers/gpu/nova-core/gsp/hal/tu102.rs
@@ -37,10 +37,7 @@
GspHal,
UnloadBundle, //
},
- sequencer::{
- GspSequencer,
- GspSequencerParams, //
- },
+ sequencer::GspSequencer,
Gsp,
GspBootContext,
GspFwWprMeta, //
@@ -336,16 +333,12 @@ fn boot<'a>(
}
fn post_boot(&self, gsp: &Gsp, ctx: &GspBootContext<'_>, gsp_fw: &GspFirmware) -> Result {
- // Create and run the GSP sequencer.
- let seq_params = GspSequencerParams {
- bootloader_app_version: gsp_fw.bootloader.app_version,
- libos_dma_handle: gsp.libos.dma_handle(),
- gsp_falcon: ctx.gsp_falcon,
- sec2_falcon: ctx.sec2_falcon,
- dev: ctx.dev(),
- bar: ctx.bar,
- };
- GspSequencer::run(&gsp.cmdq, seq_params)?;
+ GspSequencer::run(
+ &gsp.cmdq,
+ ctx,
+ gsp.libos.dma_handle(),
+ gsp_fw.bootloader.app_version,
+ )?;
Ok(())
}
diff --git a/drivers/gpu/nova-core/gsp/sequencer.rs b/drivers/gpu/nova-core/gsp/sequencer.rs
index e0850d21adca..0da3c3531886 100644
--- a/drivers/gpu/nova-core/gsp/sequencer.rs
+++ b/drivers/gpu/nova-core/gsp/sequencer.rs
@@ -31,6 +31,7 @@
MessageFromGsp, //
},
fw,
+ GspBootContext, //
},
num::FromSafeCast,
sbuffer::SBufferIter,
@@ -338,24 +339,13 @@ fn iter(&self) -> GspSeqIter<'_> {
}
}
-/// Parameters for running the GSP sequencer.
-pub(crate) struct GspSequencerParams<'a> {
- /// Bootloader application version.
- pub(crate) bootloader_app_version: u32,
- /// LibOS DMA handle address.
- pub(crate) libos_dma_handle: u64,
- /// GSP falcon for core operations.
- pub(crate) gsp_falcon: &'a Falcon<Gsp>,
- /// SEC2 falcon for core operations.
- pub(crate) sec2_falcon: &'a Falcon<Sec2>,
- /// Device for logging.
- pub(crate) dev: &'a device::Device,
- /// BAR0 for register access.
- pub(crate) bar: Bar0<'a>,
-}
-
impl<'a> GspSequencer<'a> {
- pub(crate) fn run(cmdq: &Cmdq, params: GspSequencerParams<'a>) -> Result {
+ pub(crate) fn run(
+ cmdq: &Cmdq,
+ ctx: &'a GspBootContext<'_>,
+ libos_dma_handle: u64,
+ bootloader_app_version: u32,
+ ) -> Result {
let seq_info = loop {
match cmdq.receive_msg::<GspSequence>(Cmdq::RECEIVE_TIMEOUT) {
Ok(seq_info) => break seq_info,
@@ -366,12 +356,12 @@ pub(crate) fn run(cmdq: &Cmdq, params: GspSequencerParams<'a>) -> Result {
let sequencer = GspSequencer {
seq_info,
- bar: params.bar,
- sec2_falcon: params.sec2_falcon,
- gsp_falcon: params.gsp_falcon,
- libos_dma_handle: params.libos_dma_handle,
- bootloader_app_version: params.bootloader_app_version,
- dev: params.dev,
+ bar: ctx.bar,
+ sec2_falcon: ctx.sec2_falcon,
+ gsp_falcon: ctx.gsp_falcon,
+ libos_dma_handle,
+ bootloader_app_version,
+ dev: ctx.dev(),
};
dev_dbg!(sequencer.dev, "Running CPU Sequencer commands\n");
--
2.54.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v3 02/12] gpu: nova-core: gsp: sequencer: do not store sequence into GspSequencer
2026-06-29 12:31 [PATCH v3 00/12] gpu: nova-core: consolidate and streamline GSP boot process Alexandre Courbot
2026-06-29 12:31 ` [PATCH v3 01/12] gpu: nova-core: gsp: sequencer: use GspBootContext Alexandre Courbot
@ 2026-06-29 12:31 ` Alexandre Courbot
2026-06-29 12:31 ` [PATCH v3 03/12] gpu: nova-core: gsp: replace BootUnloadGuard with local handlers Alexandre Courbot
` (10 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Alexandre Courbot @ 2026-06-29 12:31 UTC (permalink / raw)
To: Danilo Krummrich, Alice Ryhl, David Airlie, Simona Vetter,
Gary Guo, John Hubbard, Alistair Popple, Timur Tabi,
Eliot Courtney, Zhi Wang
Cc: nova-gpu, dri-devel, linux-kernel, rust-for-linux,
Alexandre Courbot
The sequence is currently stored in the `GspSequencer` even though its
lifetime is limited to `GspSequencer::run`. This object-oriented design
does not play well with the borrow-checker, as `GspSequencer::iter`
borrows a reference to the `GspSequencer`, which makes it difficult to
introduce mutable references in `GspBootContext`, as we want to do in
order to make the `Falcon` references mutable.
Thus, store the sequence locally in `GspSequencer::run`, and move
iterator creation to `GspSeqIter::new` so it no longer needs to borrow
the whole `GspSequencer`.
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
Reviewed-by: Eliot Courtney <ecourtney@nvidia.com>
---
drivers/gpu/nova-core/gsp/sequencer.rs | 35 +++++++++++++++-------------------
1 file changed, 15 insertions(+), 20 deletions(-)
diff --git a/drivers/gpu/nova-core/gsp/sequencer.rs b/drivers/gpu/nova-core/gsp/sequencer.rs
index 0da3c3531886..b5e049f76c28 100644
--- a/drivers/gpu/nova-core/gsp/sequencer.rs
+++ b/drivers/gpu/nova-core/gsp/sequencer.rs
@@ -129,8 +129,6 @@ pub(crate) fn new(data: &[u8], dev: &device::Device) -> Result<(Self, usize)> {
/// GSP Sequencer for executing firmware commands during boot.
pub(crate) struct GspSequencer<'a> {
- /// Sequencer information with command data.
- seq_info: GspSequence,
/// `Bar0` for register access.
bar: Bar0<'a>,
/// SEC2 falcon for core operations.
@@ -271,7 +269,7 @@ fn run(&self, seq: &GspSequencer<'_>) -> Result {
}
/// Iterator over GSP sequencer commands.
-pub(crate) struct GspSeqIter<'a> {
+struct GspSeqIter<'a> {
/// Command data buffer.
cmd_data: &'a [u8],
/// Current position in the buffer.
@@ -284,6 +282,18 @@ pub(crate) struct GspSeqIter<'a> {
dev: &'a device::Device,
}
+impl<'a> GspSeqIter<'a> {
+ fn new(seq: &'a GspSequence, dev: &'a device::Device) -> Self {
+ Self {
+ cmd_data: &seq.cmd_data,
+ current_offset: 0,
+ total_cmds: seq.cmd_index,
+ cmds_processed: 0,
+ dev,
+ }
+ }
+}
+
impl<'a> Iterator for GspSeqIter<'a> {
type Item = Result<GspSeqCmd>;
@@ -325,20 +335,6 @@ fn next(&mut self) -> Option<Self::Item> {
}
}
-impl<'a> GspSequencer<'a> {
- fn iter(&self) -> GspSeqIter<'_> {
- let cmd_data = &self.seq_info.cmd_data[..];
-
- GspSeqIter {
- cmd_data,
- current_offset: 0,
- total_cmds: self.seq_info.cmd_index,
- cmds_processed: 0,
- dev: self.dev,
- }
- }
-}
-
impl<'a> GspSequencer<'a> {
pub(crate) fn run(
cmdq: &Cmdq,
@@ -355,7 +351,6 @@ pub(crate) fn run(
};
let sequencer = GspSequencer {
- seq_info,
bar: ctx.bar,
sec2_falcon: ctx.sec2_falcon,
gsp_falcon: ctx.gsp_falcon,
@@ -366,14 +361,14 @@ pub(crate) fn run(
dev_dbg!(sequencer.dev, "Running CPU Sequencer commands\n");
- for cmd_result in sequencer.iter() {
+ for cmd_result in GspSeqIter::new(&seq_info, sequencer.dev) {
match cmd_result {
Ok(cmd) => cmd.run(&sequencer)?,
Err(e) => {
dev_err!(
sequencer.dev,
"Error running command at index {}\n",
- sequencer.seq_info.cmd_index
+ seq_info.cmd_index
);
return Err(e);
}
--
2.54.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v3 03/12] gpu: nova-core: gsp: replace BootUnloadGuard with local handlers
2026-06-29 12:31 [PATCH v3 00/12] gpu: nova-core: consolidate and streamline GSP boot process Alexandre Courbot
2026-06-29 12:31 ` [PATCH v3 01/12] gpu: nova-core: gsp: sequencer: use GspBootContext Alexandre Courbot
2026-06-29 12:31 ` [PATCH v3 02/12] gpu: nova-core: gsp: sequencer: do not store sequence into GspSequencer Alexandre Courbot
@ 2026-06-29 12:31 ` Alexandre Courbot
2026-06-29 12:31 ` [PATCH v3 04/12] gpu: nova-core: gsp: pass GspBootContext to unload methods Alexandre Courbot
` (9 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Alexandre Courbot @ 2026-06-29 12:31 UTC (permalink / raw)
To: Danilo Krummrich, Alice Ryhl, David Airlie, Simona Vetter,
Gary Guo, John Hubbard, Alistair Popple, Timur Tabi,
Eliot Courtney, Zhi Wang
Cc: nova-gpu, dri-devel, linux-kernel, rust-for-linux,
Alexandre Courbot
When adding the GSP unload capability, we introduced `BootUnloadGuard`
to automatically call `Gsp::unload` whenever an error occurred during
the boot process, in order to try to reset the GSP to a valid state.
This approach is not well-suited to the errors that may occur in HALs:
by definition, an error occurring in the HAL means that the GSP is not
booted; yet the first thing that `Gsp::unload` does is queue a shutdown
message to the GSP, which will inevitably result in a timeout when done
from a HAL.
Furthermore, `BootUnloadGuard` is problematic because it holds
additional references to the boot context, notably the `Falcon`s. These
extra references stand in the way of making some of the `Falcon`'s
methods mutable, since those methods would require exclusive access. As
this behavior is only needed in one place, introducing dedicated types
for it is distracting and unnecessary.
Thus, remove `BootUnloadGuard` and adopt a two-level error handling
strategy:
- HALs are free to handle their errors as they see fit (most likely, by
running their unload bundle if it is ready by the time of the error),
- `Gsp::boot` uses a `ScopeGuard` that runs `Gsp::unload`, since the
GSP should be up and running by the time `GspHal::boot` has returned.
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
---
drivers/gpu/nova-core/gsp/boot.rs | 67 +++-------------------------------
drivers/gpu/nova-core/gsp/hal.rs | 13 +++----
drivers/gpu/nova-core/gsp/hal/gh100.rs | 20 ++++------
drivers/gpu/nova-core/gsp/hal/tu102.rs | 23 +++++++-----
4 files changed, 33 insertions(+), 90 deletions(-)
diff --git a/drivers/gpu/nova-core/gsp/boot.rs b/drivers/gpu/nova-core/gsp/boot.rs
index bb2000b7a78b..0d2213fb1569 100644
--- a/drivers/gpu/nova-core/gsp/boot.rs
+++ b/drivers/gpu/nova-core/gsp/boot.rs
@@ -30,66 +30,6 @@
},
};
-/// Arguments required to call [`Gsp::unload`](super::Gsp::unload).
-///
-/// Stored as their own type to avoid repeating a long and tedious list in [`BootUnloadGuard`].
-pub(super) struct BootUnloadArgs<'a> {
- gsp: &'a super::Gsp,
- dev: &'a device::Device<device::Bound>,
- bar: Bar0<'a>,
- gsp_falcon: &'a Falcon<Gsp>,
- sec2_falcon: &'a Falcon<Sec2>,
- unload_bundle: Option<super::UnloadBundle>,
-}
-
-/// Guard that calls [`Gsp::unload`](super::Gsp::unload) with a
-/// [`UnloadBundle`](super::UnloadBundle) when dropped.
-///
-/// Used to ensure the `UnloadBundle` is run during failure paths.
-pub(super) struct BootUnloadGuard<'a> {
- guard: ScopeGuard<BootUnloadArgs<'a>, fn(BootUnloadArgs<'a>)>,
-}
-
-impl<'a> BootUnloadGuard<'a> {
- /// Wraps `unload_bundle` into a guard that executes it when dropped.
- pub(super) fn new(
- gsp: &'a super::Gsp,
- dev: &'a device::Device<device::Bound>,
- bar: Bar0<'a>,
- gsp_falcon: &'a Falcon<Gsp>,
- sec2_falcon: &'a Falcon<Sec2>,
- unload_bundle: Option<super::UnloadBundle>,
- ) -> Self {
- Self {
- guard: ScopeGuard::new_with_data(
- BootUnloadArgs {
- gsp,
- dev,
- bar,
- gsp_falcon,
- sec2_falcon,
- unload_bundle,
- },
- |args| {
- let _ = super::Gsp::unload(
- args.gsp,
- args.dev,
- args.bar,
- args.gsp_falcon,
- args.sec2_falcon,
- args.unload_bundle,
- );
- },
- ),
- }
- }
-
- /// Disarms the guard and returns the [`UnloadBundle`](super::UnloadBundle) it contains.
- pub(super) fn dismiss(self) -> Option<super::UnloadBundle> {
- self.guard.dismiss().unload_bundle
- }
-}
-
impl super::Gsp {
/// Attempt to boot the GSP.
///
@@ -107,6 +47,7 @@ pub(crate) fn boot(
let bar = ctx.bar;
let chipset = ctx.chipset;
let gsp_falcon = ctx.gsp_falcon;
+ let sec2_falcon = ctx.sec2_falcon;
let dev = pdev.as_ref();
let hal = super::hal::gsp_hal(chipset);
@@ -118,7 +59,11 @@ pub(crate) fn boot(
let wpr_meta = Coherent::init(dev, GFP_KERNEL, GspFwWprMeta::new(&gsp_fw, &fb_layout))?;
// Perform the chipset-specific boot sequence, and retrieve the unload bundle.
- let unload_guard = hal.boot(&self, &ctx, &fb_layout, &wpr_meta)?;
+ let unload_bundle = hal.boot(&self, &ctx, &fb_layout, &wpr_meta)?;
+
+ let unload_guard = ScopeGuard::new_with_data(unload_bundle, |unload_bundle| {
+ let _ = self.unload(dev, bar, gsp_falcon, sec2_falcon, unload_bundle);
+ });
gsp_falcon.write_os_version(bar, gsp_fw.bootloader.app_version);
diff --git a/drivers/gpu/nova-core/gsp/hal.rs b/drivers/gpu/nova-core/gsp/hal.rs
index 51a277fe97bb..0d65a32f9949 100644
--- a/drivers/gpu/nova-core/gsp/hal.rs
+++ b/drivers/gpu/nova-core/gsp/hal.rs
@@ -24,7 +24,6 @@
Chipset, //
},
gsp::{
- boot::BootUnloadGuard,
Gsp,
GspBootContext,
GspFwWprMeta, //
@@ -51,15 +50,15 @@ fn run(
pub(super) trait GspHal: Send {
/// Performs the GSP boot process, loading and running the required firmwares as needed.
///
- /// Upon success, returns a guard that runs the GSP unload sequence if GSP boot does not
- /// complete.
- fn boot<'a>(
+ /// Upon success, returns the [`crate::gsp::UnloadBundle`] to use with [`Gsp::unload`], if one
+ /// could be created.
+ fn boot(
&self,
- gsp: &'a Gsp,
- ctx: &GspBootContext<'a>,
+ gsp: &Gsp,
+ ctx: &GspBootContext<'_>,
fb_layout: &FbLayout,
wpr_meta: &Coherent<GspFwWprMeta>,
- ) -> Result<BootUnloadGuard<'a>>;
+ ) -> Result<Option<crate::gsp::UnloadBundle>>;
/// Performs HAL-specific post-GSP boot tasks.
///
diff --git a/drivers/gpu/nova-core/gsp/hal/gh100.rs b/drivers/gpu/nova-core/gsp/hal/gh100.rs
index 2187e11168b2..bd15a3067ffe 100644
--- a/drivers/gpu/nova-core/gsp/hal/gh100.rs
+++ b/drivers/gpu/nova-core/gsp/hal/gh100.rs
@@ -23,7 +23,6 @@
Fsp, //
},
gsp::{
- boot::BootUnloadGuard,
hal::{
GspHal,
UnloadBundle, //
@@ -145,27 +144,22 @@ impl GspHal for Gh100 {
///
/// This path uses FSP to establish a chain of trust and boot GSP-FMC. FSP handles
/// the GSP boot internally - no manual GSP reset/boot is needed.
- fn boot<'a>(
+ fn boot(
&self,
- gsp: &'a Gsp,
- ctx: &GspBootContext<'a>,
+ gsp: &Gsp,
+ ctx: &GspBootContext<'_>,
fb_layout: &FbLayout,
wpr_meta: &Coherent<GspFwWprMeta>,
- ) -> Result<BootUnloadGuard<'a>> {
+ ) -> Result<Option<crate::gsp::UnloadBundle>> {
let dev = ctx.dev();
let bar = ctx.bar;
let chipset = ctx.chipset;
let gsp_falcon = ctx.gsp_falcon;
- let sec2_falcon = ctx.sec2_falcon;
let unload_bundle = crate::gsp::UnloadBundle(
KBox::new(FspUnloadBundle, GFP_KERNEL)? as KBox<dyn UnloadBundle>
);
- // Wrap the unload bundle into a drop guard so it is automatically run upon failure.
- let unload_guard =
- BootUnloadGuard::new(gsp, dev, bar, gsp_falcon, sec2_falcon, Some(unload_bundle));
-
let mut fsp = Fsp::wait_secure_boot(dev, bar, chipset)?;
let args = FmcBootArgs::new(
@@ -176,11 +170,13 @@ fn boot<'a>(
false,
)?;
- fsp.boot_fmc(dev, bar, fb_layout, &args)?;
+ // Keep the result as we want to wait for lockdown release even in case of error, to make
+ // sure `args` is not accessed by the GSP anymore.
+ let res = fsp.boot_fmc(dev, bar, fb_layout, &args);
wait_for_gsp_lockdown_release(dev, bar, gsp_falcon, args.boot_params_dma_handle())?;
- Ok(unload_guard)
+ res.map(|()| Some(unload_bundle))
}
}
diff --git a/drivers/gpu/nova-core/gsp/hal/tu102.rs b/drivers/gpu/nova-core/gsp/hal/tu102.rs
index 6ed4ee268086..8511cc647596 100644
--- a/drivers/gpu/nova-core/gsp/hal/tu102.rs
+++ b/drivers/gpu/nova-core/gsp/hal/tu102.rs
@@ -6,7 +6,8 @@
use kernel::{
device,
dma::Coherent,
- io::Io, //
+ io::Io,
+ types::ScopeGuard, //
};
use crate::{
@@ -32,7 +33,6 @@
},
gpu::Chipset,
gsp::{
- boot::BootUnloadGuard,
hal::{
GspHal,
UnloadBundle, //
@@ -264,13 +264,13 @@ fn run_fwsec_frts(
struct Tu102;
impl GspHal for Tu102 {
- fn boot<'a>(
+ fn boot(
&self,
- gsp: &'a Gsp,
- ctx: &GspBootContext<'a>,
+ gsp: &Gsp,
+ ctx: &GspBootContext<'_>,
fb_layout: &FbLayout,
wpr_meta: &Coherent<GspFwWprMeta>,
- ) -> Result<BootUnloadGuard<'a>> {
+ ) -> Result<Option<crate::gsp::UnloadBundle>> {
let dev = ctx.dev();
let bar = ctx.bar;
let chipset = ctx.chipset;
@@ -296,9 +296,12 @@ fn boot<'a>(
.ok()
.map(crate::gsp::UnloadBundle);
- // Wrap the unload bundle into a drop guard so it is automatically run upon failure.
- let unload_guard =
- BootUnloadGuard::new(gsp, dev, bar, gsp_falcon, sec2_falcon, unload_bundle);
+ // Run the unload bundle to try and recover the GSP if an error occurs.
+ let unload_guard = ScopeGuard::new_with_data(unload_bundle, |unload_bundle| {
+ if let Some(unload_bundle) = unload_bundle {
+ let _ = unload_bundle.0.run(dev, bar, gsp_falcon, sec2_falcon);
+ }
+ });
// FWSEC-FRTS is not executed on chips where the FRTS region size is 0 (e.g. GA100).
if !fb_layout.frts.is_empty() {
@@ -329,7 +332,7 @@ fn boot<'a>(
)?
.run(dev, bar, sec2_falcon, wpr_meta)?;
- Ok(unload_guard)
+ Ok(unload_guard.dismiss())
}
fn post_boot(&self, gsp: &Gsp, ctx: &GspBootContext<'_>, gsp_fw: &GspFirmware) -> Result {
--
2.54.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v3 04/12] gpu: nova-core: gsp: pass GspBootContext to unload methods
2026-06-29 12:31 [PATCH v3 00/12] gpu: nova-core: consolidate and streamline GSP boot process Alexandre Courbot
` (2 preceding siblings ...)
2026-06-29 12:31 ` [PATCH v3 03/12] gpu: nova-core: gsp: replace BootUnloadGuard with local handlers Alexandre Courbot
@ 2026-06-29 12:31 ` Alexandre Courbot
2026-06-29 12:31 ` [PATCH v3 05/12] gpu: nova-core: gsp: centralize missing unload bundle warnings Alexandre Courbot
` (8 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Alexandre Courbot @ 2026-06-29 12:31 UTC (permalink / raw)
To: Danilo Krummrich, Alice Ryhl, David Airlie, Simona Vetter,
Gary Guo, John Hubbard, Alistair Popple, Timur Tabi,
Eliot Courtney, Zhi Wang
Cc: nova-gpu, dri-devel, linux-kernel, rust-for-linux,
Alexandre Courbot
`GspBootContext` contains the resources required to boot the GSP. As it
turns out, this is also the context required for unloading it.
Reflect that fact by replacing the arguments of `Gsp::unload` and
`UnloadBundle::run` with the `GspBootContext`. This symmetry between
`Gsp::boot` and `Gsp::unload` will also be convenient when we want to
make these methods generic over the boot context corresponding to the
boot method used.
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
---
drivers/gpu/nova-core/gpu.rs | 19 ++++++++++++++++---
drivers/gpu/nova-core/gsp/boot.rs | 28 +++++++++++++---------------
drivers/gpu/nova-core/gsp/hal.rs | 15 +--------------
drivers/gpu/nova-core/gsp/hal/gh100.rs | 13 +++----------
drivers/gpu/nova-core/gsp/hal/tu102.rs | 22 ++++++++++------------
5 files changed, 43 insertions(+), 54 deletions(-)
diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs
index 4d76be429e75..4ee0a43ac4b6 100644
--- a/drivers/gpu/nova-core/gpu.rs
+++ b/drivers/gpu/nova-core/gpu.rs
@@ -269,7 +269,9 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
#[pin_data(PinnedDrop)]
struct GspResources<'gpu> {
/// Device owning the GPU.
- device: &'gpu device::Device<device::Bound>,
+ device: &'gpu pci::Device<device::Bound>,
+ /// Details about the chipset.
+ spec: Spec,
/// MMIO mapping of PCI BAR 0.
bar: Bar0<'gpu>,
/// GSP falcon instance, used for GSP boot up and cleanup.
@@ -312,7 +314,16 @@ fn drop(self: Pin<&mut Self>) {
.gsp
.as_ref()
.get_ref()
- .unload(device, bar, &*this.gsp_falcon, &*this.sec2_falcon, bundle)
+ .unload(
+ GspBootContext {
+ pdev: device,
+ bar,
+ chipset: this.spec.chipset,
+ gsp_falcon: &*this.gsp_falcon,
+ sec2_falcon: &*this.sec2_falcon,
+ },
+ bundle,
+ )
.inspect_err(|e| dev_err!(device, "failed to unload GSP: {:?}\n", e));
}
}
@@ -344,7 +355,9 @@ pub(crate) fn new(
sysmem_flush: SysmemFlush::register(pdev.as_ref(), bar, spec.chipset)?,
gsp_resources <- try_pin_init!(GspResources {
- device: pdev.as_ref(),
+ device: pdev,
+
+ spec: *spec,
bar,
diff --git a/drivers/gpu/nova-core/gsp/boot.rs b/drivers/gpu/nova-core/gsp/boot.rs
index 0d2213fb1569..033b2ee2e2a0 100644
--- a/drivers/gpu/nova-core/gsp/boot.rs
+++ b/drivers/gpu/nova-core/gsp/boot.rs
@@ -3,7 +3,6 @@
use kernel::{
bits,
- device,
dma::Coherent,
io::poll::read_poll_timeout,
prelude::*,
@@ -15,7 +14,6 @@
driver::Bar0,
falcon::{
gsp::Gsp,
- sec2::Sec2,
Falcon, //
},
fb::FbLayout,
@@ -47,7 +45,6 @@ pub(crate) fn boot(
let bar = ctx.bar;
let chipset = ctx.chipset;
let gsp_falcon = ctx.gsp_falcon;
- let sec2_falcon = ctx.sec2_falcon;
let dev = pdev.as_ref();
let hal = super::hal::gsp_hal(chipset);
@@ -61,9 +58,11 @@ pub(crate) fn boot(
// Perform the chipset-specific boot sequence, and retrieve the unload bundle.
let unload_bundle = hal.boot(&self, &ctx, &fb_layout, &wpr_meta)?;
- let unload_guard = ScopeGuard::new_with_data(unload_bundle, |unload_bundle| {
- let _ = self.unload(dev, bar, gsp_falcon, sec2_falcon, unload_bundle);
- });
+ let unload_guard =
+ ScopeGuard::new_with_data((ctx, unload_bundle), |(ctx, unload_bundle)| {
+ let _ = self.unload(ctx, unload_bundle);
+ });
+ let ctx = &unload_guard.0;
gsp_falcon.write_os_version(bar, gsp_fw.bootloader.app_version);
@@ -82,12 +81,12 @@ pub(crate) fn boot(
self.cmdq
.send_command_no_wait(bar, commands::SetRegistry::new())?;
- hal.post_boot(&self, &ctx, &gsp_fw)?;
+ hal.post_boot(&self, ctx, &gsp_fw)?;
// Wait until GSP is fully initialized.
commands::wait_gsp_init_done(&self.cmdq)?;
- Ok(unload_guard.dismiss())
+ Ok(unload_guard.dismiss().1)
}
/// Shut down the GSP and wait until it is offline.
@@ -116,17 +115,16 @@ fn shutdown_gsp(
/// This stops all activity on the GSP.
pub(crate) fn unload(
&self,
- dev: &device::Device<device::Bound>,
- bar: Bar0<'_>,
- gsp_falcon: &Falcon<Gsp>,
- sec2_falcon: &Falcon<Sec2>,
+ ctx: super::GspBootContext<'_>,
unload_bundle: Option<super::UnloadBundle>,
) -> Result {
+ let dev = ctx.dev();
+
// Shut down the GSP. Keep going even in case of error.
let mut res = Self::shutdown_gsp(
&self.cmdq,
- bar,
- gsp_falcon,
+ ctx.bar,
+ ctx.gsp_falcon,
commands::PowerStateLevel::Level0,
)
.inspect_err(|e| dev_err!(dev, "GSP shutdown failed: {:?}\n", e));
@@ -136,7 +134,7 @@ pub(crate) fn unload(
res = res.and(
unload_bundle
.0
- .run(dev, bar, gsp_falcon, sec2_falcon)
+ .run(&ctx)
.inspect_err(|e| dev_err!(dev, "Unload bundle failed: {:?}\n", e)),
);
} else {
diff --git a/drivers/gpu/nova-core/gsp/hal.rs b/drivers/gpu/nova-core/gsp/hal.rs
index 0d65a32f9949..849ca224085b 100644
--- a/drivers/gpu/nova-core/gsp/hal.rs
+++ b/drivers/gpu/nova-core/gsp/hal.rs
@@ -5,18 +5,11 @@
mod tu102;
use kernel::{
- device,
dma::Coherent,
prelude::*, //
};
use crate::{
- driver::Bar0,
- falcon::{
- gsp::Gsp as GspEngine,
- sec2::Sec2,
- Falcon, //
- },
fb::FbLayout,
firmware::gsp::GspFirmware,
gpu::{
@@ -37,13 +30,7 @@
/// required for unloading is prepared at load time, and stored here until it needs to be run.
pub(super) trait UnloadBundle: Send {
/// Performs the steps required to properly reset the GSP after it has been stopped.
- fn run(
- &self,
- dev: &device::Device<device::Bound>,
- bar: Bar0<'_>,
- gsp_falcon: &Falcon<GspEngine>,
- sec2_falcon: &Falcon<Sec2>,
- ) -> Result;
+ fn run(&self, ctx: &GspBootContext<'_>) -> Result;
}
/// Trait implemented by GSP HALs.
diff --git a/drivers/gpu/nova-core/gsp/hal/gh100.rs b/drivers/gpu/nova-core/gsp/hal/gh100.rs
index bd15a3067ffe..8dfbc402c13b 100644
--- a/drivers/gpu/nova-core/gsp/hal/gh100.rs
+++ b/drivers/gpu/nova-core/gsp/hal/gh100.rs
@@ -14,7 +14,6 @@
driver::Bar0,
falcon::{
gsp::Gsp as GspEngine,
- sec2::Sec2,
Falcon, //
},
fb::FbLayout,
@@ -118,22 +117,16 @@ fn wait_for_gsp_lockdown_release(
struct FspUnloadBundle;
impl UnloadBundle for FspUnloadBundle {
- fn run(
- &self,
- dev: &device::Device<device::Bound>,
- bar: Bar0<'_>,
- gsp_falcon: &Falcon<GspEngine>,
- _sec2_falcon: &Falcon<Sec2>,
- ) -> Result {
+ fn run(&self, ctx: &GspBootContext<'_>) -> Result {
// GSP falcon does most of the work of resetting, so just wait for it to finish.
read_poll_timeout(
- || Ok(gsp_falcon.is_riscv_active(bar)),
+ || Ok(ctx.gsp_falcon.is_riscv_active(ctx.bar)),
|&active| !active,
Delta::from_millis(10),
Delta::from_secs(5),
)
.map(|_| ())
- .inspect_err(|_| dev_err!(dev, "GSP falcon failed to halt\n"))
+ .inspect_err(|_| dev_err!(ctx.dev(), "GSP falcon failed to halt\n"))
}
}
diff --git a/drivers/gpu/nova-core/gsp/hal/tu102.rs b/drivers/gpu/nova-core/gsp/hal/tu102.rs
index 8511cc647596..ed9a3d362090 100644
--- a/drivers/gpu/nova-core/gsp/hal/tu102.rs
+++ b/drivers/gpu/nova-core/gsp/hal/tu102.rs
@@ -124,18 +124,15 @@ fn build(
}
impl UnloadBundle for Sec2UnloadBundle {
- fn run(
- &self,
- dev: &device::Device<device::Bound>,
- bar: Bar0<'_>,
- gsp_falcon: &Falcon<GspEngine>,
- sec2_falcon: &Falcon<Sec2>,
- ) -> Result {
+ fn run(&self, ctx: &GspBootContext<'_>) -> Result {
+ let dev = ctx.dev();
+ let bar = ctx.bar;
+
// Run FWSEC-SB to reset the GSP falcon to its pre-libos state.
// Log errors but keep going if it fails.
let fwsec_sb_res = self
.fwsec_sb
- .run(dev, bar, gsp_falcon)
+ .run(dev, bar, ctx.gsp_falcon)
.inspect_err(|e| dev_err!(dev, "FWSEC-SB failed to run: {:?}\n", e));
// Remove WPR2 region if set.
@@ -145,13 +142,14 @@ fn run(
return Ok(());
}
- sec2_falcon.reset(bar)?;
- sec2_falcon.load(dev, bar, &self.booter_unloader)?;
+ ctx.sec2_falcon.reset(bar)?;
+ ctx.sec2_falcon.load(dev, bar, &self.booter_unloader)?;
// Sentinel value to confirm that Booter Unloader has run.
const MAILBOX_SENTINEL: u32 = 0xff;
let (mbox0, _) =
- sec2_falcon.boot(bar, Some(MAILBOX_SENTINEL), Some(MAILBOX_SENTINEL))?;
+ ctx.sec2_falcon
+ .boot(bar, Some(MAILBOX_SENTINEL), Some(MAILBOX_SENTINEL))?;
if mbox0 != 0 {
dev_err!(dev, "Booter Unloader returned error 0x{:x}\n", mbox0);
return Err(EINVAL);
@@ -299,7 +297,7 @@ fn boot(
// Run the unload bundle to try and recover the GSP if an error occurs.
let unload_guard = ScopeGuard::new_with_data(unload_bundle, |unload_bundle| {
if let Some(unload_bundle) = unload_bundle {
- let _ = unload_bundle.0.run(dev, bar, gsp_falcon, sec2_falcon);
+ let _ = unload_bundle.0.run(ctx);
}
});
--
2.54.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v3 05/12] gpu: nova-core: gsp: centralize missing unload bundle warnings
2026-06-29 12:31 [PATCH v3 00/12] gpu: nova-core: consolidate and streamline GSP boot process Alexandre Courbot
` (3 preceding siblings ...)
2026-06-29 12:31 ` [PATCH v3 04/12] gpu: nova-core: gsp: pass GspBootContext to unload methods Alexandre Courbot
@ 2026-06-29 12:31 ` Alexandre Courbot
2026-06-29 12:31 ` [PATCH v3 06/12] gpu: nova-core: gsp: fold TU102 unload bundle construction into HAL method Alexandre Courbot
` (7 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Alexandre Courbot @ 2026-06-29 12:31 UTC (permalink / raw)
To: Danilo Krummrich, Alice Ryhl, David Airlie, Simona Vetter,
Gary Guo, John Hubbard, Alistair Popple, Timur Tabi,
Eliot Courtney, Zhi Wang
Cc: nova-gpu, dri-devel, linux-kernel, rust-for-linux,
Alexandre Courbot
The warning emitted when the unload bundle cannot be constructed is
valid regardless of the boot method, but it was local to `Tu102`. Move
it to `Gsp::boot` so it applies to all boot methods.
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
---
drivers/gpu/nova-core/gsp/boot.rs | 10 +++++++++-
drivers/gpu/nova-core/gsp/hal/tu102.rs | 9 +--------
2 files changed, 10 insertions(+), 9 deletions(-)
diff --git a/drivers/gpu/nova-core/gsp/boot.rs b/drivers/gpu/nova-core/gsp/boot.rs
index 033b2ee2e2a0..5f11c8e10a0a 100644
--- a/drivers/gpu/nova-core/gsp/boot.rs
+++ b/drivers/gpu/nova-core/gsp/boot.rs
@@ -56,7 +56,15 @@ pub(crate) fn boot(
let wpr_meta = Coherent::init(dev, GFP_KERNEL, GspFwWprMeta::new(&gsp_fw, &fb_layout))?;
// Perform the chipset-specific boot sequence, and retrieve the unload bundle.
- let unload_bundle = hal.boot(&self, &ctx, &fb_layout, &wpr_meta)?;
+ let unload_bundle = hal.boot(&self, &ctx, &fb_layout, &wpr_meta)?.or_else(|| {
+ dev_warn!(dev, "The GSP won't be able to unload properly on unbind.\n");
+ dev_warn!(
+ dev,
+ "The GPU will need to be reset before the driver can bind again.\n"
+ );
+
+ None
+ });
let unload_guard =
ScopeGuard::new_with_data((ctx, unload_bundle), |(ctx, unload_bundle)| {
diff --git a/drivers/gpu/nova-core/gsp/hal/tu102.rs b/drivers/gpu/nova-core/gsp/hal/tu102.rs
index ed9a3d362090..d669ce7625a8 100644
--- a/drivers/gpu/nova-core/gsp/hal/tu102.rs
+++ b/drivers/gpu/nova-core/gsp/hal/tu102.rs
@@ -283,14 +283,7 @@ fn boot(
// be probed again.
let unload_bundle =
Sec2UnloadBundle::build(dev, bar, chipset, &bios, gsp_falcon, sec2_falcon)
- .inspect_err(|e| {
- dev_warn!(dev, "Failed to prepare unload firmware: {:?}\n", e);
- dev_warn!(dev, "The GSP won't be able to unload properly on unbind.\n");
- dev_warn!(
- dev,
- "The GPU will need to be reset before the driver can bind again.\n"
- );
- })
+ .inspect_err(|e| dev_warn!(dev, "Failed to prepare unload firmware: {:?}\n", e))
.ok()
.map(crate::gsp::UnloadBundle);
--
2.54.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v3 06/12] gpu: nova-core: gsp: fold TU102 unload bundle construction into HAL method
2026-06-29 12:31 [PATCH v3 00/12] gpu: nova-core: consolidate and streamline GSP boot process Alexandre Courbot
` (4 preceding siblings ...)
2026-06-29 12:31 ` [PATCH v3 05/12] gpu: nova-core: gsp: centralize missing unload bundle warnings Alexandre Courbot
@ 2026-06-29 12:31 ` Alexandre Courbot
2026-06-29 12:31 ` [PATCH v3 07/12] gpu: nova-core: gsp: turn FWSEC execution " Alexandre Courbot
` (6 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Alexandre Courbot @ 2026-06-29 12:31 UTC (permalink / raw)
To: Danilo Krummrich, Alice Ryhl, David Airlie, Simona Vetter,
Gary Guo, John Hubbard, Alistair Popple, Timur Tabi,
Eliot Courtney, Zhi Wang
Cc: nova-gpu, dri-devel, linux-kernel, rust-for-linux,
Alexandre Courbot
The construction of the unload bundle is currently a bit convoluted and
could be done in one function instead of two.
Additionally, turn that function into a method of `Tu102`. A following
patch will turn the "use FWSEC bootloader" property into a flag of the
TU102 HAL itself, and making this a method will allow the code to access
it instead of querying `Chipset`.
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
---
drivers/gpu/nova-core/gsp/hal/tu102.rs | 96 ++++++++++++++++------------------
1 file changed, 45 insertions(+), 51 deletions(-)
diff --git a/drivers/gpu/nova-core/gsp/hal/tu102.rs b/drivers/gpu/nova-core/gsp/hal/tu102.rs
index d669ce7625a8..22b4927cbaa6 100644
--- a/drivers/gpu/nova-core/gsp/hal/tu102.rs
+++ b/drivers/gpu/nova-core/gsp/hal/tu102.rs
@@ -56,23 +56,6 @@ enum FwsecUnloadFirmware {
}
impl FwsecUnloadFirmware {
- /// Loads the FWSEC SB firmware, as well as its bootloader if `chipset` requires it.
- fn new(
- dev: &device::Device<device::Bound>,
- bar: Bar0<'_>,
- chipset: Chipset,
- bios: &Vbios,
- gsp_falcon: &Falcon<GspEngine>,
- ) -> Result<Self> {
- let fwsec_sb = FwsecFirmware::new(dev, gsp_falcon, bar, bios, FwsecCommand::Sb)?;
-
- Ok(if chipset.needs_fwsec_bootloader() {
- Self::WithBl(FwsecFirmwareWithBl::new(fwsec_sb, dev, chipset)?)
- } else {
- Self::WithoutBl(fwsec_sb)
- })
- }
-
/// Runs the FWSEC SB firmware.
fn run(
&self,
@@ -94,35 +77,6 @@ struct Sec2UnloadBundle {
booter_unloader: BooterFirmware,
}
-impl Sec2UnloadBundle {
- /// Load and prepare the resources required to properly reset the GSP after it has been stopped.
- fn build(
- dev: &device::Device<device::Bound>,
- bar: Bar0<'_>,
- chipset: Chipset,
- bios: &Vbios,
- gsp_falcon: &Falcon<GspEngine>,
- sec2_falcon: &Falcon<Sec2>,
- ) -> Result<KBox<dyn UnloadBundle>> {
- KBox::new(
- Self {
- fwsec_sb: FwsecUnloadFirmware::new(dev, bar, chipset, bios, gsp_falcon)?,
- booter_unloader: BooterFirmware::new(
- dev,
- BooterKind::Unloader,
- chipset,
- FIRMWARE_VERSION,
- sec2_falcon,
- bar,
- )?,
- },
- GFP_KERNEL,
- )
- .map(|b| b as KBox<dyn UnloadBundle>)
- .map_err(Into::into)
- }
-}
-
impl UnloadBundle for Sec2UnloadBundle {
fn run(&self, ctx: &GspBootContext<'_>) -> Result {
let dev = ctx.dev();
@@ -261,6 +215,47 @@ fn run_fwsec_frts(
struct Tu102;
+impl Tu102 {
+ /// Load and prepare the resources required to properly reset the GSP after it has been stopped.
+ fn build_unload_bundle(
+ &self,
+ dev: &device::Device<device::Bound>,
+ bar: Bar0<'_>,
+ chipset: Chipset,
+ bios: &Vbios,
+ gsp_falcon: &Falcon<GspEngine>,
+ sec2_falcon: &Falcon<Sec2>,
+ ) -> Result<crate::gsp::UnloadBundle> {
+ // Load the FWSEC SB firmware, as well as its bootloader if required.
+ let fwsec_sb = FwsecFirmware::new(dev, gsp_falcon, bar, bios, FwsecCommand::Sb).and_then(
+ |fwsec_sb| {
+ Ok(if chipset.needs_fwsec_bootloader() {
+ FwsecUnloadFirmware::WithBl(FwsecFirmwareWithBl::new(fwsec_sb, dev, chipset)?)
+ } else {
+ FwsecUnloadFirmware::WithoutBl(fwsec_sb)
+ })
+ },
+ )?;
+
+ KBox::new(
+ Sec2UnloadBundle {
+ fwsec_sb,
+ booter_unloader: BooterFirmware::new(
+ dev,
+ BooterKind::Unloader,
+ chipset,
+ FIRMWARE_VERSION,
+ sec2_falcon,
+ bar,
+ )?,
+ },
+ GFP_KERNEL,
+ )
+ .map(|b| crate::gsp::UnloadBundle(b))
+ .map_err(Into::into)
+ }
+}
+
impl GspHal for Tu102 {
fn boot(
&self,
@@ -281,11 +276,10 @@ fn boot(
//
// If the unload bundle creation fails, the GPU will need to be reset before the driver can
// be probed again.
- let unload_bundle =
- Sec2UnloadBundle::build(dev, bar, chipset, &bios, gsp_falcon, sec2_falcon)
- .inspect_err(|e| dev_warn!(dev, "Failed to prepare unload firmware: {:?}\n", e))
- .ok()
- .map(crate::gsp::UnloadBundle);
+ let unload_bundle = self
+ .build_unload_bundle(dev, bar, chipset, &bios, gsp_falcon, sec2_falcon)
+ .inspect_err(|e| dev_warn!(dev, "Failed to prepare unload firmware: {:?}\n", e))
+ .ok();
// Run the unload bundle to try and recover the GSP if an error occurs.
let unload_guard = ScopeGuard::new_with_data(unload_bundle, |unload_bundle| {
--
2.54.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v3 07/12] gpu: nova-core: gsp: turn FWSEC execution into HAL method
2026-06-29 12:31 [PATCH v3 00/12] gpu: nova-core: consolidate and streamline GSP boot process Alexandre Courbot
` (5 preceding siblings ...)
2026-06-29 12:31 ` [PATCH v3 06/12] gpu: nova-core: gsp: fold TU102 unload bundle construction into HAL method Alexandre Courbot
@ 2026-06-29 12:31 ` Alexandre Courbot
2026-06-29 12:31 ` [PATCH v3 08/12] gpu: nova-core: gsp: make use of FWSEC bootloader a property of the TU102 HAL Alexandre Courbot
` (5 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Alexandre Courbot @ 2026-06-29 12:31 UTC (permalink / raw)
To: Danilo Krummrich, Alice Ryhl, David Airlie, Simona Vetter,
Gary Guo, John Hubbard, Alistair Popple, Timur Tabi,
Eliot Courtney, Zhi Wang
Cc: nova-gpu, dri-devel, linux-kernel, rust-for-linux,
Alexandre Courbot
Turn the `run_fwsec_frts` function into a method of `Tu102`. A following
patch will turn the "use FWSEC bootloader" property into a flag of the
TU102 HAL itself, and making this a method will allow the code to access
it instead of querying `Chipset`.
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
---
drivers/gpu/nova-core/gsp/hal/tu102.rs | 175 +++++++++++++++++----------------
1 file changed, 88 insertions(+), 87 deletions(-)
diff --git a/drivers/gpu/nova-core/gsp/hal/tu102.rs b/drivers/gpu/nova-core/gsp/hal/tu102.rs
index 22b4927cbaa6..98b8756043f6 100644
--- a/drivers/gpu/nova-core/gsp/hal/tu102.rs
+++ b/drivers/gpu/nova-core/gsp/hal/tu102.rs
@@ -127,95 +127,96 @@ fn run(&self, ctx: &GspBootContext<'_>) -> Result {
}
}
-/// Helper function to load and run the FWSEC-FRTS firmware and confirm that it has properly
-/// created the WPR2 region.
-fn run_fwsec_frts(
- dev: &device::Device<device::Bound>,
- chipset: Chipset,
- falcon: &Falcon<GspEngine>,
- bar: Bar0<'_>,
- bios: &Vbios,
- fb_layout: &FbLayout,
-) -> Result {
- // Check that the WPR2 region does not already exist - if it does, we cannot run
- // FWSEC-FRTS until the GPU is reset.
- if bar.read(regs::NV_PFB_PRI_MMU_WPR2_ADDR_HI).higher_bound() != 0 {
- dev_err!(
- dev,
- "WPR2 region already exists - GPU needs to be reset to proceed\n"
- );
- return Err(EBUSY);
- }
-
- // FWSEC-FRTS will create the WPR2 region.
- let fwsec_frts = FwsecFirmware::new(
- dev,
- falcon,
- bar,
- bios,
- FwsecCommand::Frts {
- frts_addr: fb_layout.frts.start,
- frts_size: fb_layout.frts.len(),
- },
- )?;
-
- if chipset.needs_fwsec_bootloader() {
- let fwsec_frts_bl = FwsecFirmwareWithBl::new(fwsec_frts, dev, chipset)?;
- // Load and run the bootloader, which will load FWSEC-FRTS and run it.
- fwsec_frts_bl.run(dev, falcon, bar)?;
- } else {
- // Load and run FWSEC-FRTS directly.
- fwsec_frts.run(dev, falcon, bar)?;
- }
-
- // SCRATCH_E contains the error code for FWSEC-FRTS.
- let frts_status = bar
- .read(regs::NV_PBUS_SW_SCRATCH_0E_FRTS_ERR)
- .frts_err_code();
- if frts_status != 0 {
- dev_err!(
- dev,
- "FWSEC-FRTS returned with error code {:#x}\n",
- frts_status
- );
-
- return Err(EIO);
- }
-
- // Check that the WPR2 region has been created as we requested.
- let (wpr2_lo, wpr2_hi) = (
- bar.read(regs::NV_PFB_PRI_MMU_WPR2_ADDR_LO).lower_bound(),
- bar.read(regs::NV_PFB_PRI_MMU_WPR2_ADDR_HI).higher_bound(),
- );
-
- match (wpr2_lo, wpr2_hi) {
- (_, 0) => {
- dev_err!(dev, "WPR2 region not created after running FWSEC-FRTS\n");
-
- Err(EIO)
- }
- (wpr2_lo, _) if wpr2_lo != fb_layout.frts.start => {
- dev_err!(
- dev,
- "WPR2 region created at unexpected address {:#x}; expected {:#x}\n",
- wpr2_lo,
- fb_layout.frts.start,
- );
-
- Err(EIO)
- }
- (wpr2_lo, wpr2_hi) => {
- dev_dbg!(dev, "WPR2: {:#x}-{:#x}\n", wpr2_lo, wpr2_hi);
- dev_dbg!(dev, "GPU instance built\n");
-
- Ok(())
- }
- }
-}
-
struct Tu102;
impl Tu102 {
+ /// Helper method to load and run the FWSEC-FRTS firmware and confirm that it has properly
+ /// created the WPR2 region.
+ fn run_fwsec_frts(
+ &self,
+ dev: &device::Device<device::Bound>,
+ chipset: Chipset,
+ falcon: &Falcon<GspEngine>,
+ bar: Bar0<'_>,
+ bios: &Vbios,
+ fb_layout: &FbLayout,
+ ) -> Result {
+ // Check that the WPR2 region does not already exist - if it does, we cannot run
+ // FWSEC-FRTS until the GPU is reset.
+ if bar.read(regs::NV_PFB_PRI_MMU_WPR2_ADDR_HI).higher_bound() != 0 {
+ dev_err!(
+ dev,
+ "WPR2 region already exists - GPU needs to be reset to proceed\n"
+ );
+ return Err(EBUSY);
+ }
+
+ // FWSEC-FRTS will create the WPR2 region.
+ let fwsec_frts = FwsecFirmware::new(
+ dev,
+ falcon,
+ bar,
+ bios,
+ FwsecCommand::Frts {
+ frts_addr: fb_layout.frts.start,
+ frts_size: fb_layout.frts.len(),
+ },
+ )?;
+
+ if chipset.needs_fwsec_bootloader() {
+ let fwsec_frts_bl = FwsecFirmwareWithBl::new(fwsec_frts, dev, chipset)?;
+ // Load and run the bootloader, which will load FWSEC-FRTS and run it.
+ fwsec_frts_bl.run(dev, falcon, bar)?;
+ } else {
+ // Load and run FWSEC-FRTS directly.
+ fwsec_frts.run(dev, falcon, bar)?;
+ }
+
+ // SCRATCH_E contains the error code for FWSEC-FRTS.
+ let frts_status = bar
+ .read(regs::NV_PBUS_SW_SCRATCH_0E_FRTS_ERR)
+ .frts_err_code();
+ if frts_status != 0 {
+ dev_err!(
+ dev,
+ "FWSEC-FRTS returned with error code {:#x}\n",
+ frts_status
+ );
+
+ return Err(EIO);
+ }
+
+ // Check that the WPR2 region has been created as we requested.
+ let (wpr2_lo, wpr2_hi) = (
+ bar.read(regs::NV_PFB_PRI_MMU_WPR2_ADDR_LO).lower_bound(),
+ bar.read(regs::NV_PFB_PRI_MMU_WPR2_ADDR_HI).higher_bound(),
+ );
+
+ match (wpr2_lo, wpr2_hi) {
+ (_, 0) => {
+ dev_err!(dev, "WPR2 region not created after running FWSEC-FRTS\n");
+
+ Err(EIO)
+ }
+ (wpr2_lo, _) if wpr2_lo != fb_layout.frts.start => {
+ dev_err!(
+ dev,
+ "WPR2 region created at unexpected address {:#x}; expected {:#x}\n",
+ wpr2_lo,
+ fb_layout.frts.start,
+ );
+
+ Err(EIO)
+ }
+ (wpr2_lo, wpr2_hi) => {
+ dev_dbg!(dev, "WPR2: {:#x}-{:#x}\n", wpr2_lo, wpr2_hi);
+ dev_dbg!(dev, "GPU instance built\n");
+
+ Ok(())
+ }
+ }
+ }
+
/// Load and prepare the resources required to properly reset the GSP after it has been stopped.
fn build_unload_bundle(
&self,
@@ -290,7 +291,7 @@ fn boot(
// FWSEC-FRTS is not executed on chips where the FRTS region size is 0 (e.g. GA100).
if !fb_layout.frts.is_empty() {
- run_fwsec_frts(dev, chipset, gsp_falcon, bar, &bios, fb_layout)?;
+ self.run_fwsec_frts(dev, chipset, gsp_falcon, bar, &bios, fb_layout)?;
}
gsp_falcon.reset(bar)?;
--
2.54.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v3 08/12] gpu: nova-core: gsp: make use of FWSEC bootloader a property of the TU102 HAL
2026-06-29 12:31 [PATCH v3 00/12] gpu: nova-core: consolidate and streamline GSP boot process Alexandre Courbot
` (6 preceding siblings ...)
2026-06-29 12:31 ` [PATCH v3 07/12] gpu: nova-core: gsp: turn FWSEC execution " Alexandre Courbot
@ 2026-06-29 12:31 ` Alexandre Courbot
2026-06-29 12:31 ` [PATCH v3 09/12] gpu: nova-core: introduce GspBootMethod Alexandre Courbot
` (4 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Alexandre Courbot @ 2026-06-29 12:31 UTC (permalink / raw)
To: Danilo Krummrich, Alice Ryhl, David Airlie, Simona Vetter,
Gary Guo, John Hubbard, Alistair Popple, Timur Tabi,
Eliot Courtney, Zhi Wang
Cc: nova-gpu, dri-devel, linux-kernel, rust-for-linux,
Alexandre Courbot
By being in the TU102 HAL, we already know that the GSP boot method is
the SEC2 Booter, so the only variable is whether the FWSEC bootloader is
used or not. Since `Chipset` also includes the variants that boot FSP,
querying it for that information introduces a potential code path where
the chipset actually boots via FSP that the current code doesn't handle.
Turn the use of FWSEC bootloader into a property of the `Tu102` HAL, and
give GA102+ chipsets their own instance with that property set to
`false`. This removes the invalid code path and the only use of
`Chipset` is now to load the correct firmware files.
This also removes some uses of the `Chipset::needs_fwsec_bootloader`
method and prepares the ground for removing it.
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
---
drivers/gpu/nova-core/gsp/hal.rs | 5 ++++-
drivers/gpu/nova-core/gsp/hal/ga102.rs | 14 ++++++++++++++
drivers/gpu/nova-core/gsp/hal/tu102.rs | 15 +++++++++++----
3 files changed, 29 insertions(+), 5 deletions(-)
diff --git a/drivers/gpu/nova-core/gsp/hal.rs b/drivers/gpu/nova-core/gsp/hal.rs
index 849ca224085b..eddb4e8bf510 100644
--- a/drivers/gpu/nova-core/gsp/hal.rs
+++ b/drivers/gpu/nova-core/gsp/hal.rs
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+mod ga102;
mod gh100;
mod tu102;
@@ -59,7 +60,9 @@ fn post_boot(&self, _gsp: &Gsp, _ctx: &GspBootContext<'_>, _gsp_fw: &GspFirmware
/// Returns the GSP HAL to be used for `chipset`.
pub(super) fn gsp_hal(chipset: Chipset) -> &'static dyn GspHal {
match chipset.arch() {
- Architecture::Turing | Architecture::Ampere | Architecture::Ada => tu102::TU102_HAL,
+ Architecture::Turing => tu102::TU102_HAL,
+ Architecture::Ampere if matches!(chipset, Chipset::GA100) => tu102::TU102_HAL,
+ Architecture::Ampere | Architecture::Ada => ga102::GA102_HAL,
Architecture::Hopper | Architecture::BlackwellGB10x | Architecture::BlackwellGB20x => {
gh100::GH100_HAL
}
diff --git a/drivers/gpu/nova-core/gsp/hal/ga102.rs b/drivers/gpu/nova-core/gsp/hal/ga102.rs
new file mode 100644
index 000000000000..ceb3eb39d138
--- /dev/null
+++ b/drivers/gpu/nova-core/gsp/hal/ga102.rs
@@ -0,0 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0
+// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+
+use crate::gsp::hal::{
+ tu102::Tu102,
+ GspHal, //
+};
+
+/// The GA102 HAL is like the TU102 one, except it doesn't use the bootloader.
+const GA102: Tu102 = Tu102 {
+ needs_fwsec_bootloader: false,
+};
+
+pub(super) const GA102_HAL: &dyn GspHal = &GA102;
diff --git a/drivers/gpu/nova-core/gsp/hal/tu102.rs b/drivers/gpu/nova-core/gsp/hal/tu102.rs
index 98b8756043f6..6a114f696c22 100644
--- a/drivers/gpu/nova-core/gsp/hal/tu102.rs
+++ b/drivers/gpu/nova-core/gsp/hal/tu102.rs
@@ -127,7 +127,10 @@ fn run(&self, ctx: &GspBootContext<'_>) -> Result {
}
}
-struct Tu102;
+pub(super) struct Tu102 {
+ /// If `true`, then the FWSEC-FRTS bootloader will be used to load the actual firmware.
+ pub(super) needs_fwsec_bootloader: bool,
+}
impl Tu102 {
/// Helper method to load and run the FWSEC-FRTS firmware and confirm that it has properly
@@ -163,7 +166,7 @@ fn run_fwsec_frts(
},
)?;
- if chipset.needs_fwsec_bootloader() {
+ if self.needs_fwsec_bootloader {
let fwsec_frts_bl = FwsecFirmwareWithBl::new(fwsec_frts, dev, chipset)?;
// Load and run the bootloader, which will load FWSEC-FRTS and run it.
fwsec_frts_bl.run(dev, falcon, bar)?;
@@ -230,7 +233,7 @@ fn build_unload_bundle(
// Load the FWSEC SB firmware, as well as its bootloader if required.
let fwsec_sb = FwsecFirmware::new(dev, gsp_falcon, bar, bios, FwsecCommand::Sb).and_then(
|fwsec_sb| {
- Ok(if chipset.needs_fwsec_bootloader() {
+ Ok(if self.needs_fwsec_bootloader {
FwsecUnloadFirmware::WithBl(FwsecFirmwareWithBl::new(fwsec_sb, dev, chipset)?)
} else {
FwsecUnloadFirmware::WithoutBl(fwsec_sb)
@@ -333,5 +336,9 @@ fn post_boot(&self, gsp: &Gsp, ctx: &GspBootContext<'_>, gsp_fw: &GspFirmware) -
}
}
-const TU102: Tu102 = Tu102;
+/// The TU102 HAL requires the use of the FWSEC bootloader.
+const TU102: Tu102 = Tu102 {
+ needs_fwsec_bootloader: true,
+};
+
pub(super) const TU102_HAL: &dyn GspHal = &TU102;
--
2.54.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v3 09/12] gpu: nova-core: introduce GspBootMethod
2026-06-29 12:31 [PATCH v3 00/12] gpu: nova-core: consolidate and streamline GSP boot process Alexandre Courbot
` (7 preceding siblings ...)
2026-06-29 12:31 ` [PATCH v3 08/12] gpu: nova-core: gsp: make use of FWSEC bootloader a property of the TU102 HAL Alexandre Courbot
@ 2026-06-29 12:31 ` Alexandre Courbot
2026-06-29 12:31 ` [PATCH v3 10/12] gpu: nova-core: avoid repeated calls to pci::Device::as_ref Alexandre Courbot
` (3 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Alexandre Courbot @ 2026-06-29 12:31 UTC (permalink / raw)
To: Danilo Krummrich, Alice Ryhl, David Airlie, Simona Vetter,
Gary Guo, John Hubbard, Alistair Popple, Timur Tabi,
Eliot Courtney, Zhi Wang
Cc: nova-gpu, dri-devel, linux-kernel, rust-for-linux,
Alexandre Courbot
The GSP boot method is currently determined by two ad-hoc methods of
`Chipset`: `uses_fsp` (a boolean telling whether to use the FSP boot
path or the Sec2 Booter one) and `needs_fwsec_bootloader` (another
boolean valid only for the Sec2 Booter method that tells whether the
FWSEC bootloader must be used).
This is neither extensible nor sound: the combination `uses_fsp &&
needs_fwsec_bootloader` is invalid, but can still be expressed.
Thus, unify these two predicates into a single `gsp_boot_method` method
that returns an enum type unambiguously describing the boot method to
use. This ensures that no invalid combination can be expressed, which
makes matching sounder.
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
---
drivers/gpu/nova-core/firmware.rs | 25 ++++++++++++--------
drivers/gpu/nova-core/firmware/fwsec.rs | 5 ++--
drivers/gpu/nova-core/gpu.rs | 16 -------------
drivers/gpu/nova-core/gsp.rs | 42 ++++++++++++++++++++++++++++++++-
drivers/gpu/nova-core/gsp/hal.rs | 22 ++++++++---------
5 files changed, 70 insertions(+), 40 deletions(-)
diff --git a/drivers/gpu/nova-core/firmware.rs b/drivers/gpu/nova-core/firmware.rs
index 80e948bf7511..7a5978f93f4e 100644
--- a/drivers/gpu/nova-core/firmware.rs
+++ b/drivers/gpu/nova-core/firmware.rs
@@ -21,6 +21,7 @@
FalconFirmware, //
},
gpu,
+ gsp::GspBootMethod,
num::{
FromSafeCast,
IntoSafeCast, //
@@ -429,17 +430,21 @@ const fn make_entry_chipset(self, chipset: gpu::Chipset) -> Self {
// FSP-based chipsets (Hopper, Blackwell and later) boot the GSP via the FMC image loaded by
// FSP. Older chipsets use the SEC2 booter instead.
- let this = if chipset.uses_fsp() {
- this.make_entry_file(name, "fmc")
- } else {
- this.make_entry_file(name, "booter_load")
- .make_entry_file(name, "booter_unload")
- };
+ match chipset.gsp_boot_method() {
+ GspBootMethod::Sec2 {
+ needs_fwsec_bootloader,
+ } => {
+ let mut this = this
+ .make_entry_file(name, "booter_load")
+ .make_entry_file(name, "booter_unload");
- if chipset.needs_fwsec_bootloader() {
- this.make_entry_file(name, "gen_bootloader")
- } else {
- this
+ if needs_fwsec_bootloader {
+ this = this.make_entry_file(name, "gen_bootloader")
+ }
+
+ this
+ }
+ GspBootMethod::Fsp => this.make_entry_file(name, "fmc"),
}
}
diff --git a/drivers/gpu/nova-core/firmware/fwsec.rs b/drivers/gpu/nova-core/firmware/fwsec.rs
index 199ae2adb664..a3a43a27ddd7 100644
--- a/drivers/gpu/nova-core/firmware/fwsec.rs
+++ b/drivers/gpu/nova-core/firmware/fwsec.rs
@@ -388,8 +388,9 @@ pub(crate) fn new(
/// Loads the FWSEC firmware into `falcon` and execute it.
///
/// This must only be called on chipsets that do not need the FWSEC bootloader (i.e., where
- /// [`Chipset::needs_fwsec_bootloader()`](crate::gpu::Chipset::needs_fwsec_bootloader) returns
- /// `false`). On chipsets that do, use [`bootloader::FwsecFirmwareWithBl`] instead.
+ /// [`Chipset::gsp_boot_method()`](crate::gpu::Chipset::gsp_boot_method) returns
+ /// `GspBootMethod::Sec2 { needs_fwsec_bootloader: false }`). On chipsets where
+ /// `needs_fwsec_bootloader` is `true`, use [`bootloader::FwsecFirmwareWithBl`] instead.
pub(crate) fn run(
&self,
dev: &Device<device::Bound>,
diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs
index 4ee0a43ac4b6..b3f04239d938 100644
--- a/drivers/gpu/nova-core/gpu.rs
+++ b/drivers/gpu/nova-core/gpu.rs
@@ -133,22 +133,6 @@ pub(crate) const fn arch(self) -> Architecture {
}
}
- /// Returns `true` if this chipset requires the PIO-loaded bootloader in order to boot FWSEC.
- ///
- /// This includes all chipsets < GA102.
- pub(crate) const fn needs_fwsec_bootloader(self) -> bool {
- matches!(self.arch(), Architecture::Turing) || matches!(self, Self::GA100)
- }
-
- /// Returns `true` if this chipset boots via FSP (Hopper and later), which requires the FMC
- /// firmware image.
- pub(crate) const fn uses_fsp(self) -> bool {
- matches!(
- self.arch(),
- Architecture::Hopper | Architecture::BlackwellGB10x | Architecture::BlackwellGB20x
- )
- }
-
/// Returns the address range of the PCI config mirror space.
pub(crate) fn pci_config_mirror_range(self) -> Range<u32> {
hal::gpu_hal(self).pci_config_mirror_range()
diff --git a/drivers/gpu/nova-core/gsp.rs b/drivers/gpu/nova-core/gsp.rs
index 73e93403601c..771b38e6335d 100644
--- a/drivers/gpu/nova-core/gsp.rs
+++ b/drivers/gpu/nova-core/gsp.rs
@@ -38,7 +38,10 @@
sec2::Sec2 as Sec2Falcon,
Falcon, //
},
- gpu::Chipset,
+ gpu::{
+ Architecture,
+ Chipset, //
+ },
gsp::{
cmdq::Cmdq,
fw::{
@@ -217,5 +220,42 @@ pub(crate) fn get_static_info(&self, bar: Bar0<'_>) -> Result<commands::GetGspSt
}
}
+/// Method used to boot the GSP.
+#[derive(Copy, Clone, PartialEq, Eq)]
+pub(crate) enum GspBootMethod {
+ /// GSP is booted by running FWSEC-FRTS on the GSP falcon, followed by the Booter loader on the
+ /// SEC2 falcon.
+ ///
+ /// `needs_fwsec_bootloader` indicates whether the chipset requires the PIO-loaded bootloader in
+ /// order to boot FWSEC.
+ Sec2 { needs_fwsec_bootloader: bool },
+ /// GSP is booted via FSP Chain-of-Trust.
+ Fsp,
+}
+
+impl Chipset {
+ /// Returns the method used to boot the GSP on this chipset.
+ pub(crate) const fn gsp_boot_method(self) -> GspBootMethod {
+ match self.arch() {
+ // All Turing chipsets require the FWSEC bootloader.
+ Architecture::Turing => GspBootMethod::Sec2 {
+ needs_fwsec_bootloader: true,
+ },
+ // GA100 also requires the FWSEC bootloader.
+ Architecture::Ampere if matches!(self, Self::GA100) => GspBootMethod::Sec2 {
+ needs_fwsec_bootloader: true,
+ },
+ // Other Ampere chipsets, as well as Ada chipsets, do not require the FWSEC bootloader.
+ Architecture::Ampere | Architecture::Ada => GspBootMethod::Sec2 {
+ needs_fwsec_bootloader: false,
+ },
+ // Hopper and more recent use the FSP Chain-of-Trust boot method.
+ Architecture::Hopper | Architecture::BlackwellGB10x | Architecture::BlackwellGB20x => {
+ GspBootMethod::Fsp
+ }
+ }
+ }
+}
+
/// Opaque bundle required to unload the GSP. Created by [`Gsp::boot`], consumed by [`Gsp::unload`].
pub(crate) struct UnloadBundle(KBox<dyn hal::UnloadBundle>);
diff --git a/drivers/gpu/nova-core/gsp/hal.rs b/drivers/gpu/nova-core/gsp/hal.rs
index eddb4e8bf510..90f076f640e0 100644
--- a/drivers/gpu/nova-core/gsp/hal.rs
+++ b/drivers/gpu/nova-core/gsp/hal.rs
@@ -13,13 +13,11 @@
use crate::{
fb::FbLayout,
firmware::gsp::GspFirmware,
- gpu::{
- Architecture,
- Chipset, //
- },
+ gpu::Chipset,
gsp::{
Gsp,
GspBootContext,
+ GspBootMethod,
GspFwWprMeta, //
},
};
@@ -59,12 +57,14 @@ fn post_boot(&self, _gsp: &Gsp, _ctx: &GspBootContext<'_>, _gsp_fw: &GspFirmware
/// Returns the GSP HAL to be used for `chipset`.
pub(super) fn gsp_hal(chipset: Chipset) -> &'static dyn GspHal {
- match chipset.arch() {
- Architecture::Turing => tu102::TU102_HAL,
- Architecture::Ampere if matches!(chipset, Chipset::GA100) => tu102::TU102_HAL,
- Architecture::Ampere | Architecture::Ada => ga102::GA102_HAL,
- Architecture::Hopper | Architecture::BlackwellGB10x | Architecture::BlackwellGB20x => {
- gh100::GH100_HAL
- }
+ // The GSP HAL is entirely determined by the boot method.
+ match chipset.gsp_boot_method() {
+ GspBootMethod::Sec2 {
+ needs_fwsec_bootloader: true,
+ } => tu102::TU102_HAL,
+ GspBootMethod::Sec2 {
+ needs_fwsec_bootloader: false,
+ } => ga102::GA102_HAL,
+ GspBootMethod::Fsp => gh100::GH100_HAL,
}
}
--
2.54.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v3 10/12] gpu: nova-core: avoid repeated calls to pci::Device::as_ref
2026-06-29 12:31 [PATCH v3 00/12] gpu: nova-core: consolidate and streamline GSP boot process Alexandre Courbot
` (8 preceding siblings ...)
2026-06-29 12:31 ` [PATCH v3 09/12] gpu: nova-core: introduce GspBootMethod Alexandre Courbot
@ 2026-06-29 12:31 ` Alexandre Courbot
2026-06-29 12:31 ` [PATCH v3 11/12] gpu: nova-core: gsp: pass GspBootContext mutably Alexandre Courbot
` (2 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Alexandre Courbot @ 2026-06-29 12:31 UTC (permalink / raw)
To: Danilo Krummrich, Alice Ryhl, David Airlie, Simona Vetter,
Gary Guo, John Hubbard, Alistair Popple, Timur Tabi,
Eliot Courtney, Zhi Wang
Cc: nova-gpu, dri-devel, linux-kernel, rust-for-linux,
Alexandre Courbot
Add a local `Device` reference created from the `pci::Device` in the
`Gpu` constructor to avoid repeatedly calling `as_ref`.
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
---
drivers/gpu/nova-core/gpu.rs | 18 ++++++++++--------
1 file changed, 10 insertions(+), 8 deletions(-)
diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs
index b3f04239d938..cec21eadf0bf 100644
--- a/drivers/gpu/nova-core/gpu.rs
+++ b/drivers/gpu/nova-core/gpu.rs
@@ -317,9 +317,11 @@ pub(crate) fn new(
pdev: &'gpu pci::Device<device::Core<'_>>,
bar: Bar0<'gpu>,
) -> impl PinInit<Self, Error> + 'gpu {
+ let dev = pdev.as_ref();
+
try_pin_init!(Self {
- spec: Spec::new(pdev.as_ref(), bar).inspect(|spec| {
- dev_info!(pdev,"NVIDIA ({})\n", spec);
+ spec: Spec::new(dev, bar).inspect(|spec| {
+ dev_info!(dev,"NVIDIA ({})\n", spec);
})?,
// We must wait for GFW_BOOT completion before doing any significant setup on the GPU.
@@ -332,11 +334,11 @@ pub(crate) fn new(
unsafe { pdev.dma_set_mask_and_coherent(dma_mask)? };
hal.wait_gfw_boot_completion(bar)
- .inspect_err(|_| dev_err!(pdev, "GFW boot did not complete\n"))?;
+ .inspect_err(|_| dev_err!(dev, "GFW boot did not complete\n"))?;
},
// Initialize this early because `gsp_resources` depends on it.
- sysmem_flush: SysmemFlush::register(pdev.as_ref(), bar, spec.chipset)?,
+ sysmem_flush: SysmemFlush::register(dev, bar, spec.chipset)?,
gsp_resources <- try_pin_init!(GspResources {
device: pdev,
@@ -346,12 +348,12 @@ pub(crate) fn new(
bar,
gsp_falcon: Falcon::new(
- pdev.as_ref(),
+ dev,
spec.chipset,
)
.inspect(|falcon| falcon.clear_swgen0_intr(bar))?,
- sec2_falcon: Falcon::new(pdev.as_ref(), spec.chipset)?,
+ sec2_falcon: Falcon::new(dev, spec.chipset)?,
gsp <- Gsp::new(pdev),
@@ -371,8 +373,8 @@ pub(crate) fn new(
// Obtain and display basic GPU information.
let info = gsp_resources.gsp.get_static_info(bar)?;
match info.gpu_name() {
- Ok(name) => dev_info!(pdev, "GPU name: {}\n", name),
- Err(e) => dev_warn!(pdev, "GPU name unavailable: {:?}\n", e),
+ Ok(name) => dev_info!(dev, "GPU name: {}\n", name),
+ Err(e) => dev_warn!(dev, "GPU name unavailable: {:?}\n", e),
}
if !info.usable_fb_regions.is_empty() {
--
2.54.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v3 11/12] gpu: nova-core: gsp: pass GspBootContext mutably
2026-06-29 12:31 [PATCH v3 00/12] gpu: nova-core: consolidate and streamline GSP boot process Alexandre Courbot
` (9 preceding siblings ...)
2026-06-29 12:31 ` [PATCH v3 10/12] gpu: nova-core: avoid repeated calls to pci::Device::as_ref Alexandre Courbot
@ 2026-06-29 12:31 ` Alexandre Courbot
2026-06-29 12:31 ` [PATCH v3 12/12] gpu: nova-core: store Fsp instance in Gpu Alexandre Courbot
2026-06-29 13:05 ` [PATCH v3 00/12] gpu: nova-core: consolidate and streamline GSP boot process Alexandre Courbot
12 siblings, 0 replies; 14+ messages in thread
From: Alexandre Courbot @ 2026-06-29 12:31 UTC (permalink / raw)
To: Danilo Krummrich, Alice Ryhl, David Airlie, Simona Vetter,
Gary Guo, John Hubbard, Alistair Popple, Timur Tabi,
Eliot Courtney, Zhi Wang
Cc: nova-gpu, dri-devel, linux-kernel, rust-for-linux,
Alexandre Courbot
We want to move the `Fsp` instance into `Gpu`, which will require
passing it as a mutable reference in `GspBootContext`, since
`Fsp::boot_fmc` is a mutable method. In order to use the mutable
references it contains, `GspBootContext` must also be mutable.
We will also follow up by making some methods of the `Falcon`s mutable,
which also requires passing them as mutable references.
Thus, make the `GspBootContext` passed to `Gsp::boot` and `Gsp::unload`
mutable, and pass mutable references to it to the GSP boot HAL methods.
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
---
drivers/gpu/nova-core/gsp/boot.rs | 28 +++++++++++++++-------------
drivers/gpu/nova-core/gsp/hal.rs | 11 ++++++++---
drivers/gpu/nova-core/gsp/hal/gh100.rs | 4 ++--
drivers/gpu/nova-core/gsp/hal/tu102.rs | 6 +++---
4 files changed, 28 insertions(+), 21 deletions(-)
diff --git a/drivers/gpu/nova-core/gsp/boot.rs b/drivers/gpu/nova-core/gsp/boot.rs
index 5f11c8e10a0a..dce07878449e 100644
--- a/drivers/gpu/nova-core/gsp/boot.rs
+++ b/drivers/gpu/nova-core/gsp/boot.rs
@@ -39,7 +39,7 @@ impl super::Gsp {
/// [`Self::unload`]) returned.
pub(crate) fn boot(
self: Pin<&mut Self>,
- ctx: super::GspBootContext<'_>,
+ mut ctx: super::GspBootContext<'_>,
) -> Result<Option<super::UnloadBundle>> {
let pdev = ctx.pdev;
let bar = ctx.bar;
@@ -56,21 +56,23 @@ pub(crate) fn boot(
let wpr_meta = Coherent::init(dev, GFP_KERNEL, GspFwWprMeta::new(&gsp_fw, &fb_layout))?;
// Perform the chipset-specific boot sequence, and retrieve the unload bundle.
- let unload_bundle = hal.boot(&self, &ctx, &fb_layout, &wpr_meta)?.or_else(|| {
- dev_warn!(dev, "The GSP won't be able to unload properly on unbind.\n");
- dev_warn!(
- dev,
- "The GPU will need to be reset before the driver can bind again.\n"
- );
+ let unload_bundle = hal
+ .boot(&self, &mut ctx, &fb_layout, &wpr_meta)?
+ .or_else(|| {
+ dev_warn!(dev, "The GSP won't be able to unload properly on unbind.\n");
+ dev_warn!(
+ dev,
+ "The GPU will need to be reset before the driver can bind again.\n"
+ );
- None
- });
+ None
+ });
- let unload_guard =
+ let mut unload_guard =
ScopeGuard::new_with_data((ctx, unload_bundle), |(ctx, unload_bundle)| {
let _ = self.unload(ctx, unload_bundle);
});
- let ctx = &unload_guard.0;
+ let ctx = &mut unload_guard.0;
gsp_falcon.write_os_version(bar, gsp_fw.bootloader.app_version);
@@ -123,7 +125,7 @@ fn shutdown_gsp(
/// This stops all activity on the GSP.
pub(crate) fn unload(
&self,
- ctx: super::GspBootContext<'_>,
+ mut ctx: super::GspBootContext<'_>,
unload_bundle: Option<super::UnloadBundle>,
) -> Result {
let dev = ctx.dev();
@@ -142,7 +144,7 @@ pub(crate) fn unload(
res = res.and(
unload_bundle
.0
- .run(&ctx)
+ .run(&mut ctx)
.inspect_err(|e| dev_err!(dev, "Unload bundle failed: {:?}\n", e)),
);
} else {
diff --git a/drivers/gpu/nova-core/gsp/hal.rs b/drivers/gpu/nova-core/gsp/hal.rs
index 90f076f640e0..a5d0da05ef5f 100644
--- a/drivers/gpu/nova-core/gsp/hal.rs
+++ b/drivers/gpu/nova-core/gsp/hal.rs
@@ -29,7 +29,7 @@
/// required for unloading is prepared at load time, and stored here until it needs to be run.
pub(super) trait UnloadBundle: Send {
/// Performs the steps required to properly reset the GSP after it has been stopped.
- fn run(&self, ctx: &GspBootContext<'_>) -> Result;
+ fn run(&self, ctx: &mut GspBootContext<'_>) -> Result;
}
/// Trait implemented by GSP HALs.
@@ -41,7 +41,7 @@ pub(super) trait GspHal: Send {
fn boot(
&self,
gsp: &Gsp,
- ctx: &GspBootContext<'_>,
+ ctx: &mut GspBootContext<'_>,
fb_layout: &FbLayout,
wpr_meta: &Coherent<GspFwWprMeta>,
) -> Result<Option<crate::gsp::UnloadBundle>>;
@@ -50,7 +50,12 @@ fn boot(
///
/// This method is called by the GSP boot code after the GSP is confirmed to be running, and
/// after the initialization commands have been pushed onto its queue.
- fn post_boot(&self, _gsp: &Gsp, _ctx: &GspBootContext<'_>, _gsp_fw: &GspFirmware) -> Result {
+ fn post_boot(
+ &self,
+ _gsp: &Gsp,
+ _ctx: &mut GspBootContext<'_>,
+ _gsp_fw: &GspFirmware,
+ ) -> Result {
Ok(())
}
}
diff --git a/drivers/gpu/nova-core/gsp/hal/gh100.rs b/drivers/gpu/nova-core/gsp/hal/gh100.rs
index 8dfbc402c13b..a617b8f5974f 100644
--- a/drivers/gpu/nova-core/gsp/hal/gh100.rs
+++ b/drivers/gpu/nova-core/gsp/hal/gh100.rs
@@ -117,7 +117,7 @@ fn wait_for_gsp_lockdown_release(
struct FspUnloadBundle;
impl UnloadBundle for FspUnloadBundle {
- fn run(&self, ctx: &GspBootContext<'_>) -> Result {
+ fn run(&self, ctx: &mut GspBootContext<'_>) -> Result {
// GSP falcon does most of the work of resetting, so just wait for it to finish.
read_poll_timeout(
|| Ok(ctx.gsp_falcon.is_riscv_active(ctx.bar)),
@@ -140,7 +140,7 @@ impl GspHal for Gh100 {
fn boot(
&self,
gsp: &Gsp,
- ctx: &GspBootContext<'_>,
+ ctx: &mut GspBootContext<'_>,
fb_layout: &FbLayout,
wpr_meta: &Coherent<GspFwWprMeta>,
) -> Result<Option<crate::gsp::UnloadBundle>> {
diff --git a/drivers/gpu/nova-core/gsp/hal/tu102.rs b/drivers/gpu/nova-core/gsp/hal/tu102.rs
index 6a114f696c22..305515ef2700 100644
--- a/drivers/gpu/nova-core/gsp/hal/tu102.rs
+++ b/drivers/gpu/nova-core/gsp/hal/tu102.rs
@@ -78,7 +78,7 @@ struct Sec2UnloadBundle {
}
impl UnloadBundle for Sec2UnloadBundle {
- fn run(&self, ctx: &GspBootContext<'_>) -> Result {
+ fn run(&self, ctx: &mut GspBootContext<'_>) -> Result {
let dev = ctx.dev();
let bar = ctx.bar;
@@ -264,7 +264,7 @@ impl GspHal for Tu102 {
fn boot(
&self,
gsp: &Gsp,
- ctx: &GspBootContext<'_>,
+ ctx: &mut GspBootContext<'_>,
fb_layout: &FbLayout,
wpr_meta: &Coherent<GspFwWprMeta>,
) -> Result<Option<crate::gsp::UnloadBundle>> {
@@ -324,7 +324,7 @@ fn boot(
Ok(unload_guard.dismiss())
}
- fn post_boot(&self, gsp: &Gsp, ctx: &GspBootContext<'_>, gsp_fw: &GspFirmware) -> Result {
+ fn post_boot(&self, gsp: &Gsp, ctx: &mut GspBootContext<'_>, gsp_fw: &GspFirmware) -> Result {
GspSequencer::run(
&gsp.cmdq,
ctx,
--
2.54.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v3 12/12] gpu: nova-core: store Fsp instance in Gpu
2026-06-29 12:31 [PATCH v3 00/12] gpu: nova-core: consolidate and streamline GSP boot process Alexandre Courbot
` (10 preceding siblings ...)
2026-06-29 12:31 ` [PATCH v3 11/12] gpu: nova-core: gsp: pass GspBootContext mutably Alexandre Courbot
@ 2026-06-29 12:31 ` Alexandre Courbot
2026-06-29 13:05 ` [PATCH v3 00/12] gpu: nova-core: consolidate and streamline GSP boot process Alexandre Courbot
12 siblings, 0 replies; 14+ messages in thread
From: Alexandre Courbot @ 2026-06-29 12:31 UTC (permalink / raw)
To: Danilo Krummrich, Alice Ryhl, David Airlie, Simona Vetter,
Gary Guo, John Hubbard, Alistair Popple, Timur Tabi,
Eliot Courtney, Zhi Wang
Cc: nova-gpu, dri-devel, linux-kernel, rust-for-linux,
Alexandre Courbot
The `Fsp` instance was only used in the Hopper+ boot path, and
consequently built locally (and immediately dropped) in it.
This worked well as a temporary measure, but the FSP is a GPU
sub-device, so its lifetime should match the GPU rather than a single
boot invocation.
It will also be needed in other parts of the driver, for instance vGPU.
Thus, create the `Fsp` instance in the `Gpu` constructor and store it
there, passing it to the GSP boot as a mutable reference using
`GspBootContext`. This makes the `Fsp` available even after the GSP is
booted.
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
---
drivers/gpu/nova-core/gpu.rs | 15 ++++++++++++++-
drivers/gpu/nova-core/gsp.rs | 2 ++
drivers/gpu/nova-core/gsp/hal/gh100.rs | 7 ++-----
3 files changed, 18 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs
index cec21eadf0bf..7ed8411f4a08 100644
--- a/drivers/gpu/nova-core/gpu.rs
+++ b/drivers/gpu/nova-core/gpu.rs
@@ -22,11 +22,13 @@
Falcon, //
},
fb::SysmemFlush,
+ fsp::Fsp,
gsp::{
self,
commands::GetGspStaticInfoReply,
Gsp,
- GspBootContext, //
+ GspBootContext,
+ GspBootMethod, //
},
regs,
};
@@ -262,6 +264,10 @@ struct GspResources<'gpu> {
gsp_falcon: Falcon<GspFalcon>,
/// SEC2 falcon instance, used for GSP boot up and cleanup.
sec2_falcon: Falcon<Sec2Falcon>,
+ /// FSP instance, if on an arch that supports it.
+ // TODO: use different resource types for each boot method, and make the relevant Gsp methods
+ // generic against them.
+ fsp: Option<Fsp>,
/// GSP runtime data.
#[pin]
gsp: Gsp,
@@ -305,6 +311,7 @@ fn drop(self: Pin<&mut Self>) {
chipset: this.spec.chipset,
gsp_falcon: &*this.gsp_falcon,
sec2_falcon: &*this.sec2_falcon,
+ fsp: this.fsp.as_mut(),
},
bundle,
)
@@ -355,6 +362,11 @@ pub(crate) fn new(
sec2_falcon: Falcon::new(dev, spec.chipset)?,
+ fsp: match spec.chipset.gsp_boot_method() {
+ GspBootMethod::Sec2 { .. } => None,
+ GspBootMethod::Fsp => Some(Fsp::wait_secure_boot(dev, bar, spec.chipset)?),
+ },
+
gsp <- Gsp::new(pdev),
// This member must be initialized last, so the `UnloadBundle` can never be dropped
@@ -366,6 +378,7 @@ pub(crate) fn new(
chipset: spec.chipset,
gsp_falcon,
sec2_falcon,
+ fsp: fsp.as_mut(),
})?,
}),
diff --git a/drivers/gpu/nova-core/gsp.rs b/drivers/gpu/nova-core/gsp.rs
index 771b38e6335d..ff438506070a 100644
--- a/drivers/gpu/nova-core/gsp.rs
+++ b/drivers/gpu/nova-core/gsp.rs
@@ -38,6 +38,7 @@
sec2::Sec2 as Sec2Falcon,
Falcon, //
},
+ fsp::Fsp,
gpu::{
Architecture,
Chipset, //
@@ -62,6 +63,7 @@ pub(crate) struct GspBootContext<'a> {
pub(crate) chipset: Chipset,
pub(crate) gsp_falcon: &'a Falcon<GspFalcon>,
pub(crate) sec2_falcon: &'a Falcon<Sec2Falcon>,
+ pub(crate) fsp: Option<&'a mut Fsp>,
}
impl<'a> GspBootContext<'a> {
diff --git a/drivers/gpu/nova-core/gsp/hal/gh100.rs b/drivers/gpu/nova-core/gsp/hal/gh100.rs
index a617b8f5974f..bf1776bb7f5d 100644
--- a/drivers/gpu/nova-core/gsp/hal/gh100.rs
+++ b/drivers/gpu/nova-core/gsp/hal/gh100.rs
@@ -17,10 +17,7 @@
Falcon, //
},
fb::FbLayout,
- fsp::{
- FmcBootArgs,
- Fsp, //
- },
+ fsp::FmcBootArgs,
gsp::{
hal::{
GspHal,
@@ -153,7 +150,7 @@ fn boot(
KBox::new(FspUnloadBundle, GFP_KERNEL)? as KBox<dyn UnloadBundle>
);
- let mut fsp = Fsp::wait_secure_boot(dev, bar, chipset)?;
+ let fsp = ctx.fsp.as_mut().ok_or(ENODEV)?;
let args = FmcBootArgs::new(
dev,
--
2.54.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* Re: [PATCH v3 00/12] gpu: nova-core: consolidate and streamline GSP boot process
2026-06-29 12:31 [PATCH v3 00/12] gpu: nova-core: consolidate and streamline GSP boot process Alexandre Courbot
` (11 preceding siblings ...)
2026-06-29 12:31 ` [PATCH v3 12/12] gpu: nova-core: store Fsp instance in Gpu Alexandre Courbot
@ 2026-06-29 13:05 ` Alexandre Courbot
12 siblings, 0 replies; 14+ messages in thread
From: Alexandre Courbot @ 2026-06-29 13:05 UTC (permalink / raw)
To: Danilo Krummrich, Alice Ryhl, David Airlie, Simona Vetter,
Gary Guo, John Hubbard, Alistair Popple, Timur Tabi,
Eliot Courtney, Zhi Wang
Cc: nova-gpu, dri-devel, linux-kernel, rust-for-linux,
Alexandre Courbot
On Mon Jun 29, 2026 at 9:31 PM JST, Alexandre Courbot wrote:
> This series applies on `drm-rust-next`.
... is not true anymore now that [1] has been pushed. ^_^;
I will send a rebase-only v4 shortly, sorry for the noise.
[1] https://lore.kernel.org/all/20260625-drm-bar-refactor-v2-1-9db6b890d92e@proton.me/
^ permalink raw reply [flat|nested] 14+ messages in thread