[PATCH v3 20/33] gpu: nova-core: gsp: return the static GPU configuration from boot

From: John Hubbard

Date: Thu Sep 17 2026 - 21:10:42 EST


Once GSP-RM has started, it reports the static GPU configuration: the
GPU's name, its usable framebuffer regions and its BAR1 page directory.
Everything that the driver builds on top of the GSP depends on that
configuration. In the r000 boot protocol the configuration arrives as
the reply to the request that ends boot, so the boot sequence holds it
when it returns.

Nova-core fetched the configuration in a separate step after boot, once
the interrupt handler was registered, so on r000 the boot sequence would
have had to keep the reply for that step to read.

Return the configuration from the boot sequence together with the unload
bundle, and drop the separate step.

Assisted-by: LLM
Signed-off-by: John Hubbard <jhubbard@xxxxxxxxxx>
---
drivers/gpu/nova-core/gpu.rs | 44 ++++++++++++++-------------
drivers/gpu/nova-core/gsp.rs | 13 +++++---
drivers/gpu/nova-core/gsp/boot.rs | 15 ++++++---
drivers/gpu/nova-core/gsp/commands.rs | 2 +-
4 files changed, 42 insertions(+), 32 deletions(-)

diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs
index e7f1458330b4..0ed0f4722dc5 100644
--- a/drivers/gpu/nova-core/gpu.rs
+++ b/drivers/gpu/nova-core/gpu.rs
@@ -34,7 +34,6 @@
fsp::Fsp,
gsp::{
self,
- commands::GspStaticInfo,
Gsp,
GspBootContext, //
},
@@ -298,8 +297,15 @@ struct GspResources<'gpu> {
/// GSP runtime data.
#[pin]
gsp: Gsp<'gpu>,
- /// GSP unload firmware bundle, if any.
- unload_bundle: Option<gsp::UnloadBundle<'gpu>>,
+ /// The static GPU configuration and the unload bundle that the boot sequence returned.
+ boot_result: gsp::BootResult<'gpu>,
+}
+
+impl GspResources<'_> {
+ /// Returns the static GPU configuration that GSP-RM reported at boot.
+ fn static_info(&self) -> &gsp::commands::GspStaticInfo {
+ &self.boot_result.static_info
+ }
}

/// Structure holding the resources required to operate the GPU.
@@ -313,8 +319,6 @@ pub(crate) struct Gpu<'gpu> {
/// before the GSP is unloaded.
#[pin]
_gsp_irq: GspIrq<'gpu>,
- /// Static GPU information as provided by the GSP.
- gsp_static_info: GspStaticInfo,
/// GPU memory manager owning memory management resources.
///
/// Must be kept declared *before* `gsp_resources`, so that its components are dropped while
@@ -347,7 +351,7 @@ fn drop(self: Pin<&mut Self>) {
let this = self.project();
let device = *this.device;
let bar = *this.bar;
- let bundle = this.unload_bundle.take();
+ let bundle = this.boot_result.unload_bundle.take();

let _ = this
.gsp
@@ -416,10 +420,10 @@ pub(crate) fn new<'a>(

gsp <- Gsp::new(pdev, bar),

- // This member must be initialized last, so the `UnloadBundle` can never be dropped
- // from outside of the constructed `GspResources`, ensuring that the unload sequence
- // is properly run in case of failure.
- unload_bundle: gsp.boot(GspBootContext {
+ // This member must be initialized last, so that the unload bundle can never be
+ // dropped from outside the constructed `GspResources`, and the unload sequence runs
+ // on a failure.
+ boot_result: gsp.boot(GspBootContext {
pdev,
bar,
chipset: spec.chipset,
@@ -454,9 +458,8 @@ pub(crate) fn new<'a>(
gsp_resources.gsp.cmdq.drain()?;
},

- gsp_static_info: {
- // Obtain and display basic GPU information.
- let info = gsp_resources.gsp.get_static_info()?;
+ _: {
+ let info = gsp_resources.static_info();
match info.gpu_name() {
Ok(name) => dev_info!(dev, "GPU name: {}\n", name),
Err(e) => dev_warn!(dev, "GPU name unavailable: {:?}\n", e),
@@ -476,13 +479,12 @@ pub(crate) fn new<'a>(
/ u64::SZ_1M
);
}
-
- info
},

// Create GPU memory manager owning memory management resources.
mm: {
- let usable_vram = gsp_static_info.usable_fb_regions.first().ok_or(ENODEV)?;
+ let info = gsp_resources.static_info();
+ let usable_vram = info.usable_fb_regions.first().ok_or(ENODEV)?;
let buddy_params = GpuBuddyParams {
base_offset: usable_vram.start,
size: usable_vram.end - usable_vram.start,
@@ -493,13 +495,13 @@ pub(crate) fn new<'a>(
bar,
gsp_resources.spec.chipset,
buddy_params,
- VramAddress::from_raw(gsp_static_info.total_fb_end),
+ VramAddress::from_raw(info.total_fb_end),
)?
},

// Create BAR1 user interface for CPU access to GPU virtual memory.
bar_user: {
- let pdb_addr = VramAddress::from_raw(gsp_static_info.bar1_pde_base);
+ let pdb_addr = VramAddress::from_raw(gsp_resources.static_info().bar1_pde_base);
let bar1_idx = crate::driver::bar1_resource_index(pdev)?;
let bar1_size = pdev.resource_len(bar1_idx)?;
Arc::pin_init(
@@ -520,14 +522,14 @@ pub(crate) fn new<'a>(
pub(crate) fn run_selftests(self: Pin<&mut Self>, pdev: &pci::Device<device::Bound>) {
let this = self.project();
let dev = pdev.as_ref();
- let regions = &this.gsp_static_info.usable_fb_regions;
+ let info = this.gsp_resources.static_info();

if let Err(err) = crate::mm::selftest::run(
dev,
this.mm,
- regions,
+ &info.usable_fb_regions,
this.bar_user,
- this.gsp_static_info.bar1_pde_base,
+ info.bar1_pde_base,
this.spec.chipset,
) {
dev_err!(dev, "self-tests failed: {:?}\n", err);
diff --git a/drivers/gpu/nova-core/gsp.rs b/drivers/gpu/nova-core/gsp.rs
index 6e7a057188c5..90b4c3380f11 100644
--- a/drivers/gpu/nova-core/gsp.rs
+++ b/drivers/gpu/nova-core/gsp.rs
@@ -219,12 +219,15 @@ pub(crate) fn new(
}))
})
}
-
- /// Query the GSP for the static GPU information.
- pub(crate) fn get_static_info(&self) -> Result<commands::GspStaticInfo> {
- self.cmdq.send_command(commands::GetGspStaticInfo)
- }
}

/// Opaque bundle required to unload the GSP. Created by [`Gsp::boot`], consumed by [`Gsp::unload`].
pub(crate) struct UnloadBundle<'a>(KBox<dyn hal::UnloadBundle + 'a>);
+
+/// The results of [`Gsp::boot`]: the static GPU configuration and the unload bundle.
+pub(crate) struct BootResult<'a> {
+ /// The unload bundle for [`Gsp::unload`], if one could be built.
+ pub(crate) unload_bundle: Option<UnloadBundle<'a>>,
+ /// The static GPU configuration, as GSP-RM reported it at the end of boot.
+ pub(crate) static_info: commands::GspStaticInfo,
+}
diff --git a/drivers/gpu/nova-core/gsp/boot.rs b/drivers/gpu/nova-core/gsp/boot.rs
index c805b42dd7bc..86a122438d44 100644
--- a/drivers/gpu/nova-core/gsp/boot.rs
+++ b/drivers/gpu/nova-core/gsp/boot.rs
@@ -339,18 +339,18 @@ fn handle_load_exec_hs_binary(&self, payload_0: &[u8], payload_1: &[u8]) -> Resu
}

impl<'gsp> super::Gsp<'gsp> {
- /// Attempt to boot the GSP.
+ /// Boots the GSP.
///
/// This is a GPU-dependent and complex procedure that involves loading firmware files from
/// user-space, patching them with signatures, and building firmware-specific intricate data
/// structures that the GSP will use at runtime.
///
- /// Upon return, the GSP is up and running, and its unload bundle (to be given as argument to
- /// [`Self::unload`]) returned.
+ /// Returns, with the GSP running, the static configuration that GSP-RM reported and the
+ /// unload bundle for [`Self::unload`].
pub(crate) fn boot(
self: Pin<&mut Self>,
mut ctx: super::GspBootContext<'_, 'gsp>,
- ) -> Result<Option<super::UnloadBundle<'gsp>>> {
+ ) -> Result<super::BootResult<'gsp>> {
let pdev = ctx.pdev;
let chipset = ctx.chipset;
let gsp_falcon = ctx.gsp_falcon;
@@ -398,7 +398,12 @@ pub(crate) fn boot(
// Wait until GSP is fully initialized.
commands::wait_gsp_init_done(&self.cmdq)?;

- Ok(unload_guard.dismiss().1)
+ let static_info = self.cmdq.send_command(commands::GetGspStaticInfo)?;
+
+ Ok(super::BootResult {
+ unload_bundle: unload_guard.dismiss().1,
+ static_info,
+ })
}

/// Shut down the GSP and wait until it is offline.
diff --git a/drivers/gpu/nova-core/gsp/commands.rs b/drivers/gpu/nova-core/gsp/commands.rs
index b3b28e397ebd..d866297fa0a5 100644
--- a/drivers/gpu/nova-core/gsp/commands.rs
+++ b/drivers/gpu/nova-core/gsp/commands.rs
@@ -205,7 +205,7 @@ fn init(&self) -> impl Init<Self::Command, Self::InitError> {
}
}

-/// The reply from the GSP to the [`GetGspStaticInfo`] command.
+/// The static GPU configuration, which GSP-RM reports in reply to [`GetGspStaticInfo`].
pub(crate) struct GspStaticInfo {
gpu_name: [u8; 64],
/// BAR1 Page Directory Entry base address.
--
2.55.0