* [PATCH v10 00/28] gpu: nova-core: firmware: Hopper/Blackwell support
@ 2026-04-11 2:49 John Hubbard
2026-04-11 2:49 ` [PATCH v10 01/28] gpu: nova-core: factor .fwsignature* selection into a new find_gsp_sigs_section() John Hubbard
` (27 more replies)
0 siblings, 28 replies; 30+ messages in thread
From: John Hubbard @ 2026-04-11 2:49 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot
Cc: Joel Fernandes, Timur Tabi, Alistair Popple, Eliot Courtney,
Shashank Sharma, Zhi Wang, David Airlie, Simona Vetter,
Bjorn Helgaas, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, rust-for-linux, LKML, John Hubbard
This is based on today's Alex Courbot's drm-rust-next-staging branch[1],
and a branch for this v10 is here:
https://github.com/johnhubbard/linux/tree/nova-core-blackwell-v10
My prerequisite nova-core SizeConstants patch [2] is posted separately,
and also included in the above -v10 branch.
This has been re-tested on Turing, Ampere, and Blackwell. (Just
recently, I installed one of each into the same test machine, without
running out of PCI bar space, woohoo!):
NovaCore 0000:c1:00.0: NVIDIA (Chipset: TU117, Architecture: Turing, Revision: a.1)
NovaCore 0000:c1:00.0: GPU name: NVIDIA T400 4GB
NovaCore 0000:c2:00.0: NVIDIA (Chipset: GB202, Architecture: BlackwellGB20x, Revision: a.1)
NovaCore 0000:c2:00.0: GPU name: NVIDIA RTX PRO 6000 Blackwell Max-Q Workstation Edition
NovaCore 0000:01:00.0: NVIDIA (Chipset: GA104, Architecture: Ampere, Revision: a.1)
NovaCore 0000:01:00.0: GPU name: NVIDIA RTX A4000
Changes in v10:
* Reordered per review (and direct assistance--thanks again) from
Alexandre Courbot: the two refactoring patches (factor .fwsignature*
selection, use GPU Architecture to simplify HALs) now come first,
before GPU identification. The boot_via_fsp stub is introduced early
and completed as FSP features arrive. The SEC2 refactoring, PCI config
mirror, and reserved heap size patches are moved earlier in the
series.
* Made pmuReservedSize conditional on Blackwell dGPU architectures.
Open RM only sets this field for Blackwell (Turing/Ampere/Ada/Hopper
all leave it zero). Added calc_pmu_reserved_size() helper and
FbLayout.pmu_reserved_size field to route the value through the
layout instead of using the constant unconditionally. Replaced
`as u32` cast with usize_into_u32 for PMU_RESERVED_SIZE. (Alexandre)
* Split the GFW boot wait HAL change into two patches: one that moves
the existing behavior into a GpuHal trait, and a second that adds the
Hopper/Blackwell skip.
* Removed the Spec::chipset() accessor (no longer needed after
restructuring). Updated the Copy/Clone commit message accordingly.
* Rebased onto drm-rust-next-staging, which includes
const_align_up(), "move firmware image parsing code to firmware.rs",
"factor out an elf_str() function", and "make WPR heap sizing
fallible" from the v9 series. Series is now 28 patches (was 31).
* Depends on the "rust: sizes: SizeConstants trait" series[N], which
adds typed SZ_* constants (u64::SZ_1M, u32::SZ_4K, etc.). The
nova-core conversion patch ("use SizeConstants trait for u64 size
constants") will be posted separately, but is already included in my
git branch. The Blackwell patches that introduce new SZ_* usage
(larger non-WPR heap, FSP Chain of Trust boot, larger WPR2 heap) use
the trait form from the start.
* Fixed the PCI config mirror commit message: corrected hex offsets to
match the code (older architectures use 0x088000, Hopper/Blackwell
use 0x092000).
* Dropped the never-used nvdm_type_raw() method from the MCTP/NVDM
introducing patch.
* Removed stale Co-developed-by tag from the FSP Chain of Trust boot
commit per Alex's request. Rewrote the commit message to remove
references to the no-longer-existent fmc_full field.
* Added missing #[expect(dead_code)] on GspFmcBootParams in the FSP
secure boot commit, removed when the struct becomes used in the
Chain of Trust boot commit.
Changes in v9:
* Rebased onto today's drm-rust-next.
* Split Architecture::Blackwell into BlackwellGB10x and BlackwellGB20x,
after Gary Guo and Sashiko pointed out that GB10x and GB20x are
distinct enough to warrant separate architecture variants. This
surfaced several bugs where all Blackwell chips were incorrectly
treated as a single group:
* Fixed the FSP boot completion register address for GB10x. GB10x
uses the same address as Hopper (0x000200bc), not the GB20x
address (0x00ad00bc).
* Made the FSP secure boot timeout architecture-dependent. GB20x
now gets 5000ms while Hopper and GB10x keep 4000ms.
* Removed chipset-level match arms that were working around the
single-variant design in fb/hal.rs, firmware/gsp.rs, and regs.rs.
* Simplified find_gsp_sigs_section() to return &'static str instead of
Option<&'static str>, since the Architecture enum is now exhaustive
and every variant has a known signature section name.
* Moved dma_set_mask_and_coherent from probe() into Gpu::new(), with
the unsafe block narrowed to just that call. Gpu::new() now takes
pci::Device<device::Core> instead of device::Bound to support this.
* Dropped the local `chipset` variable in Gpu::new() and accessed
spec.chipset() directly, since Spec is now Copy.
* Changed Spec::chipset() to take self instead of &self, since Spec is
Copy.
* Removed the unnecessary Tu102/Gh100 consts in gpu/hal.rs and used the
unit structs directly.
* Kept a hold on the Firmware object in FspFirmware instead of copying
the FMC ELF into a KVec<u8>.
* Moved the dev_info formatting fix and the GFW_BOOT comment removal
out of the Copy/Clone patch and into the patches that actually touch
those lines.
* Added Reviewed-by tags from Gary Guo and Alice Ryhl.
Changes in v8:
* Added Clone/Copy derives to Spec and Revision. Removed the
unnecessary pin_init_scope wrapping in Gpu::new() that the lack of
Copy had forced. Added a Spec::chipset() accessor.
* Removed implementation-detail sentence from the
Architecture::dma_mask() doccomment.
* Simplified the GPU HAL to two variants (Tu102, Gh100) instead of
four. Renamed "Fsp" to "Gh100" to follow the HAL naming convention.
Removed the spurious GA100 special case. Moved the GFW_BOOT wait into
the HAL method itself instead of returning a bool.
* Increased the GFW_BOOT wait timeout from 4 seconds to 30 seconds,
after Joel found that a different Blackwell SKU required extra time.
* Removed stray Cc lines from each patch.
* Fixed rustfmt issues in gsp/fw.rs and gsp/boot.rs reported by the
kernel test robot against v7 patches 27 and 31.
Changes in v7:
* Rebased onto Alexandre Courbot's rust register!() series in
drm-rust-next, including the related generic I/O accessor and
IoCapable changes.
* Rebased onto drm-rust-next (v7.0-rc4 based).
* Dropped the v6 patches that are already in drm-rust-next: the
aux-device fix, the pdev helper macro patch, and the one-item-per-line
use cleanup.
* Reworked the GPU init pieces per review. DMA mask setup now stays in
driver probe, with the mask width selected by GPU architecture, and
the GFW boot policy now lives in a dedicated GPU HAL.
* Reworked firmware image parsing per review around a single ElfFormat
trait with associated header types. Also added support for both ELF32
and ELF64 images, with automatic format detection.
* Reworked the MCTP/NVDM protocol code to use bitfield! and typed
accessors, removing the open-coded bit handling.
* Reworked the FSP messaging part of the series so that the message
structures are introduced in the first patches that use them, instead
of as a standalone dead-code-only patch. Also changed fmc_full to use
KVec<u8> from the start.
* Split the WPR heap overflow handling out into a separate prep patch.
That patch makes management_overhead() and wpr_heap_size() fallible,
uses checked arithmetic, and leaves the larger WPR2 heap patch with
only the Hopper and Blackwell sizing changes.
* Added a code comment documenting the Hopper and Blackwell PCI config
mirror base change.
Changes in v6:
* Rebased onto drm-rust-next (v7.0-rc1 based).
* Dropped the first two patches from v5 (aux device fix and pdev
macros), which have since been merged independently.
* const_align_up(): reworked per review from Gary Guo, Miguel Ojeda,
and Danilo Krummrich: now returns Option<usize> instead of panicking,
takes an Alignment argument instead of a const generic, and no longer
needs the inline_const feature addition in scripts/Makefile.build.
* The rust/sizes and SZ_*_U64 patches from v5 are no longer included.
I plan to post those as a separate series that depends on this one.
Changes in v5:
* Rebased onto linux.git master.
* Split MCTP protocol into its own module and file.
* Many Rust-based improvements: more use of types, especially. Also
used Result and Option more.
* Lots of cleanup of comments and print output and error handling.
* Added const_align_up() to rust/ and used it in nova-core. This
required enabling a Rust feature: inline_const, as recommended by
Miguel Ojeda.
* Refactoring various things, such as Gpu::new() to own Spec creation,
and several more such things.
* Fixed three Delta::ZERO busy-polls (patches 21, 24, 31) to use
non-zero sleep intervals (after just realizing that it was a bad
choice to have zero in there).
* Reduced GH100/GB100 HAL duplication. Made FSP_PKEY_SIZE/FSP_SIG_SIZE
consistent across patches. Replaced fragile architecture checks with
chipset.arch(). Renamed LIBOS_BLACKWELL.
* Narrowed the scope of some of the #![expect(dead_code)] cases,
although that really only matters within the series, not once it is
fully applied.
[1] https://github.com/Gnurou/linux/commits/drm-rust-next-staging/
[2] https://lore.kernel.org/20260411024118.471294-1-jhubbard@nvidia.com
John Hubbard (28):
gpu: nova-core: factor .fwsignature* selection into a new
find_gsp_sigs_section()
gpu: nova-core: use GPU Architecture to simplify HAL selections
gpu: nova-core: Hopper/Blackwell: basic GPU identification
gpu: nova-core: add Copy/Clone to Spec and Revision
gpu: nova-core: set DMA mask width based on GPU architecture
gpu: nova-core: move GFW boot wait into a GPU HAL
gpu: nova-core: Hopper/Blackwell: skip GFW boot waiting
gpu: nova-core: Blackwell: calculate reserved FB heap size
gpu: nova-core: Hopper/Blackwell: new location for PCI config mirror
gpu: nova-core: refactor SEC2 booter loading into
BooterFirmware::run()
gpu: nova-core: Hopper/Blackwell: integrate FSP boot path into boot()
gpu: nova-core: don't assume 64-bit firmware images
gpu: nova-core: add support for 32-bit firmware images
gpu: nova-core: add auto-detection of 32-bit, 64-bit firmware images
gpu: nova-core: Hopper/Blackwell: add FSP falcon engine stub
gpu: nova-core: Hopper/Blackwell: add FMC firmware image, in support
of FSP
gpu: nova-core: Hopper/Blackwell: add FSP secure boot completion
waiting
gpu: nova-core: Hopper/Blackwell: add FMC signature extraction
gpu: nova-core: Hopper/Blackwell: add FSP falcon EMEM operations
gpu: nova-core: Hopper/Blackwell: add FSP message infrastructure
gpu: nova-core: add MCTP/NVDM protocol types for firmware
communication
gpu: nova-core: Hopper/Blackwell: add FSP send/receive messaging
gpu: nova-core: Hopper/Blackwell: add FspCotVersion type
gpu: nova-core: Hopper/Blackwell: larger non-WPR heap
gpu: nova-core: Hopper/Blackwell: add FSP Chain of Trust boot
gpu: nova-core: Blackwell: use correct sysmem flush registers
gpu: nova-core: Hopper/Blackwell: larger WPR2 (GSP) heap
gpu: nova-core: Hopper/Blackwell: add GSP lockdown release polling
drivers/gpu/nova-core/driver.rs | 16 -
drivers/gpu/nova-core/falcon.rs | 1 +
drivers/gpu/nova-core/falcon/fsp.rs | 234 ++++++++++
drivers/gpu/nova-core/falcon/hal.rs | 21 +-
drivers/gpu/nova-core/fb.rs | 51 ++-
drivers/gpu/nova-core/fb/hal.rs | 36 +-
drivers/gpu/nova-core/fb/hal/ga102.rs | 2 +-
drivers/gpu/nova-core/fb/hal/gb100.rs | 80 ++++
drivers/gpu/nova-core/fb/hal/gb202.rs | 77 ++++
drivers/gpu/nova-core/fb/hal/gh100.rs | 38 ++
drivers/gpu/nova-core/firmware.rs | 176 ++++++--
drivers/gpu/nova-core/firmware/booter.rs | 30 ++
drivers/gpu/nova-core/firmware/fsp.rs | 44 ++
drivers/gpu/nova-core/firmware/gsp.rs | 35 +-
drivers/gpu/nova-core/fsp.rs | 523 +++++++++++++++++++++++
drivers/gpu/nova-core/gfw.rs | 76 ----
drivers/gpu/nova-core/gpu.rs | 62 ++-
drivers/gpu/nova-core/gpu/hal.rs | 28 ++
drivers/gpu/nova-core/gpu/hal/gh100.rs | 18 +
drivers/gpu/nova-core/gpu/hal/tu102.rs | 86 ++++
drivers/gpu/nova-core/gsp/boot.rs | 286 ++++++++++---
drivers/gpu/nova-core/gsp/commands.rs | 8 +-
drivers/gpu/nova-core/gsp/fw.rs | 62 ++-
drivers/gpu/nova-core/gsp/fw/commands.rs | 22 +-
drivers/gpu/nova-core/mctp.rs | 119 ++++++
drivers/gpu/nova-core/nova_core.rs | 3 +-
drivers/gpu/nova-core/regs.rs | 104 +++++
27 files changed, 2000 insertions(+), 238 deletions(-)
create mode 100644 drivers/gpu/nova-core/falcon/fsp.rs
create mode 100644 drivers/gpu/nova-core/fb/hal/gb100.rs
create mode 100644 drivers/gpu/nova-core/fb/hal/gb202.rs
create mode 100644 drivers/gpu/nova-core/fb/hal/gh100.rs
create mode 100644 drivers/gpu/nova-core/firmware/fsp.rs
create mode 100644 drivers/gpu/nova-core/fsp.rs
delete mode 100644 drivers/gpu/nova-core/gfw.rs
create mode 100644 drivers/gpu/nova-core/gpu/hal.rs
create mode 100644 drivers/gpu/nova-core/gpu/hal/gh100.rs
create mode 100644 drivers/gpu/nova-core/gpu/hal/tu102.rs
create mode 100644 drivers/gpu/nova-core/mctp.rs
base-commit: dcba1b1052c8a7381df762597fa50a50ad2efb8d
--
2.53.0
^ permalink raw reply [flat|nested] 30+ messages in thread
* [PATCH v10 01/28] gpu: nova-core: factor .fwsignature* selection into a new find_gsp_sigs_section()
2026-04-11 2:49 [PATCH v10 00/28] gpu: nova-core: firmware: Hopper/Blackwell support John Hubbard
@ 2026-04-11 2:49 ` John Hubbard
2026-04-11 2:49 ` [PATCH v10 02/28] gpu: nova-core: use GPU Architecture to simplify HAL selections John Hubbard
` (26 subsequent siblings)
27 siblings, 0 replies; 30+ messages in thread
From: John Hubbard @ 2026-04-11 2:49 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot
Cc: Joel Fernandes, Timur Tabi, Alistair Popple, Eliot Courtney,
Shashank Sharma, Zhi Wang, David Airlie, Simona Vetter,
Bjorn Helgaas, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, rust-for-linux, LKML, John Hubbard
Keep Gsp::new() from getting too cluttered, by factoring out the
selection of .fwsignature* items. This will continue to grow as we add
GPUs.
Reviewed-by: Gary Guo <gary@garyguo.net>
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
---
drivers/gpu/nova-core/firmware/gsp.rs | 26 ++++++++++++++------------
1 file changed, 14 insertions(+), 12 deletions(-)
diff --git a/drivers/gpu/nova-core/firmware/gsp.rs b/drivers/gpu/nova-core/firmware/gsp.rs
index 2fcc255c3bc8..1fbc2b08123a 100644
--- a/drivers/gpu/nova-core/firmware/gsp.rs
+++ b/drivers/gpu/nova-core/firmware/gsp.rs
@@ -63,6 +63,19 @@ pub(crate) struct GspFirmware {
}
impl GspFirmware {
+ fn find_gsp_sigs_section(chipset: Chipset) -> &'static str {
+ match chipset.arch() {
+ Architecture::Turing if matches!(chipset, Chipset::TU116 | Chipset::TU117) => {
+ ".fwsignature_tu11x"
+ }
+ Architecture::Turing => ".fwsignature_tu10x",
+ // GA100 uses the same firmware as Turing.
+ Architecture::Ampere if chipset == Chipset::GA100 => ".fwsignature_tu10x",
+ Architecture::Ampere => ".fwsignature_ga10x",
+ Architecture::Ada => ".fwsignature_ad10x",
+ }
+ }
+
/// Loads the GSP firmware binaries, map them into `dev`'s address-space, and creates the page
/// tables expected by the GSP bootloader to load it.
pub(crate) fn new<'a>(
@@ -131,18 +144,7 @@ pub(crate) fn new<'a>(
},
size,
signatures: {
- let sigs_section = match chipset.arch() {
- Architecture::Turing
- if matches!(chipset, Chipset::TU116 | Chipset::TU117) =>
- {
- ".fwsignature_tu11x"
- }
- Architecture::Turing => ".fwsignature_tu10x",
- // GA100 uses the same firmware as Turing
- Architecture::Ampere if chipset == Chipset::GA100 => ".fwsignature_tu10x",
- Architecture::Ampere => ".fwsignature_ga10x",
- Architecture::Ada => ".fwsignature_ad10x",
- };
+ let sigs_section = Self::find_gsp_sigs_section(chipset);
elf::elf64_section(firmware.data(), sigs_section)
.ok_or(EINVAL)
--
2.53.0
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [PATCH v10 02/28] gpu: nova-core: use GPU Architecture to simplify HAL selections
2026-04-11 2:49 [PATCH v10 00/28] gpu: nova-core: firmware: Hopper/Blackwell support John Hubbard
2026-04-11 2:49 ` [PATCH v10 01/28] gpu: nova-core: factor .fwsignature* selection into a new find_gsp_sigs_section() John Hubbard
@ 2026-04-11 2:49 ` John Hubbard
2026-04-11 2:49 ` [PATCH v10 03/28] gpu: nova-core: Hopper/Blackwell: basic GPU identification John Hubbard
` (25 subsequent siblings)
27 siblings, 0 replies; 30+ messages in thread
From: John Hubbard @ 2026-04-11 2:49 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot
Cc: Joel Fernandes, Timur Tabi, Alistair Popple, Eliot Courtney,
Shashank Sharma, Zhi Wang, David Airlie, Simona Vetter,
Bjorn Helgaas, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, rust-for-linux, LKML, John Hubbard
Replace per-chipset match arms with Architecture-based matching in the
falcon and FB HAL selection functions. This reduces the number of match
arms that need updating when new chipsets are added within an existing
architecture.
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
---
drivers/gpu/nova-core/falcon/hal.rs | 17 ++++++++++-------
drivers/gpu/nova-core/fb/hal.rs | 18 +++++++++---------
2 files changed, 19 insertions(+), 16 deletions(-)
diff --git a/drivers/gpu/nova-core/falcon/hal.rs b/drivers/gpu/nova-core/falcon/hal.rs
index 71df33c79884..3bfb42684a0e 100644
--- a/drivers/gpu/nova-core/falcon/hal.rs
+++ b/drivers/gpu/nova-core/falcon/hal.rs
@@ -9,7 +9,10 @@
FalconBromParams,
FalconEngine, //
},
- gpu::Chipset,
+ gpu::{
+ Architecture,
+ Chipset, //
+ },
};
mod ga102;
@@ -74,16 +77,16 @@ fn signature_reg_fuse_version(
pub(super) fn falcon_hal<E: FalconEngine + 'static>(
chipset: Chipset,
) -> Result<KBox<dyn FalconHal<E>>> {
- use Chipset::*;
-
- let hal = match chipset {
- TU102 | TU104 | TU106 | TU116 | TU117 => {
+ let hal = match chipset.arch() {
+ Architecture::Turing => {
KBox::new(tu102::Tu102::<E>::new(), GFP_KERNEL)? as KBox<dyn FalconHal<E>>
}
- GA102 | GA103 | GA104 | GA106 | GA107 | AD102 | AD103 | AD104 | AD106 | AD107 => {
+ // TODO: support GA100. Its boot sequence is a lot like Turing, except that it handles the
+ // FRTS steps differently (specifically, it skips FWSEC-FRTS).
+ Architecture::Ampere if chipset == Chipset::GA100 => return Err(ENOTSUPP),
+ Architecture::Ampere | Architecture::Ada => {
KBox::new(ga102::Ga102::<E>::new(), GFP_KERNEL)? as KBox<dyn FalconHal<E>>
}
- _ => return Err(ENOTSUPP),
};
Ok(hal)
diff --git a/drivers/gpu/nova-core/fb/hal.rs b/drivers/gpu/nova-core/fb/hal.rs
index aba0abd8ee00..5a6cb8221e78 100644
--- a/drivers/gpu/nova-core/fb/hal.rs
+++ b/drivers/gpu/nova-core/fb/hal.rs
@@ -4,7 +4,10 @@
use crate::{
driver::Bar0,
- gpu::Chipset, //
+ gpu::{
+ Architecture,
+ Chipset, //
+ },
};
mod ga100;
@@ -29,13 +32,10 @@ pub(crate) trait FbHal {
/// Returns the HAL corresponding to `chipset`.
pub(super) fn fb_hal(chipset: Chipset) -> &'static dyn FbHal {
- use Chipset::*;
-
- match chipset {
- TU102 | TU104 | TU106 | TU117 | TU116 => tu102::TU102_HAL,
- GA100 => ga100::GA100_HAL,
- GA102 | GA103 | GA104 | GA106 | GA107 | AD102 | AD103 | AD104 | AD106 | AD107 => {
- ga102::GA102_HAL
- }
+ match chipset.arch() {
+ Architecture::Turing => tu102::TU102_HAL,
+ Architecture::Ampere if chipset == Chipset::GA100 => ga100::GA100_HAL,
+ Architecture::Ampere => ga102::GA102_HAL,
+ Architecture::Ada => ga102::GA102_HAL,
}
}
--
2.53.0
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [PATCH v10 03/28] gpu: nova-core: Hopper/Blackwell: basic GPU identification
2026-04-11 2:49 [PATCH v10 00/28] gpu: nova-core: firmware: Hopper/Blackwell support John Hubbard
2026-04-11 2:49 ` [PATCH v10 01/28] gpu: nova-core: factor .fwsignature* selection into a new find_gsp_sigs_section() John Hubbard
2026-04-11 2:49 ` [PATCH v10 02/28] gpu: nova-core: use GPU Architecture to simplify HAL selections John Hubbard
@ 2026-04-11 2:49 ` John Hubbard
2026-04-11 3:58 ` Timur Tabi
2026-04-11 2:49 ` [PATCH v10 04/28] gpu: nova-core: add Copy/Clone to Spec and Revision John Hubbard
` (24 subsequent siblings)
27 siblings, 1 reply; 30+ messages in thread
From: John Hubbard @ 2026-04-11 2:49 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot
Cc: Joel Fernandes, Timur Tabi, Alistair Popple, Eliot Courtney,
Shashank Sharma, Zhi Wang, David Airlie, Simona Vetter,
Bjorn Helgaas, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, rust-for-linux, LKML, John Hubbard
Hopper (GH100) and Blackwell identification, including ELF
.fwsignature_* items.
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
---
drivers/gpu/nova-core/falcon/hal.rs | 6 +++++-
drivers/gpu/nova-core/fb/hal.rs | 5 ++++-
drivers/gpu/nova-core/firmware/gsp.rs | 3 +++
drivers/gpu/nova-core/gpu.rs | 18 ++++++++++++++++++
4 files changed, 30 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/nova-core/falcon/hal.rs b/drivers/gpu/nova-core/falcon/hal.rs
index 3bfb42684a0e..51615381b495 100644
--- a/drivers/gpu/nova-core/falcon/hal.rs
+++ b/drivers/gpu/nova-core/falcon/hal.rs
@@ -84,7 +84,11 @@ pub(super) fn falcon_hal<E: FalconEngine + 'static>(
// TODO: support GA100. Its boot sequence is a lot like Turing, except that it handles the
// FRTS steps differently (specifically, it skips FWSEC-FRTS).
Architecture::Ampere if chipset == Chipset::GA100 => return Err(ENOTSUPP),
- Architecture::Ampere | Architecture::Ada => {
+ Architecture::Ampere
+ | Architecture::Ada
+ | Architecture::Hopper
+ | Architecture::BlackwellGB10x
+ | Architecture::BlackwellGB20x => {
KBox::new(ga102::Ga102::<E>::new(), GFP_KERNEL)? as KBox<dyn FalconHal<E>>
}
};
diff --git a/drivers/gpu/nova-core/fb/hal.rs b/drivers/gpu/nova-core/fb/hal.rs
index 5a6cb8221e78..3b3bad0feed0 100644
--- a/drivers/gpu/nova-core/fb/hal.rs
+++ b/drivers/gpu/nova-core/fb/hal.rs
@@ -36,6 +36,9 @@ pub(super) fn fb_hal(chipset: Chipset) -> &'static dyn FbHal {
Architecture::Turing => tu102::TU102_HAL,
Architecture::Ampere if chipset == Chipset::GA100 => ga100::GA100_HAL,
Architecture::Ampere => ga102::GA102_HAL,
- Architecture::Ada => ga102::GA102_HAL,
+ Architecture::Ada
+ | Architecture::Hopper
+ | Architecture::BlackwellGB10x
+ | Architecture::BlackwellGB20x => ga102::GA102_HAL,
}
}
diff --git a/drivers/gpu/nova-core/firmware/gsp.rs b/drivers/gpu/nova-core/firmware/gsp.rs
index 1fbc2b08123a..617ec2aaa93a 100644
--- a/drivers/gpu/nova-core/firmware/gsp.rs
+++ b/drivers/gpu/nova-core/firmware/gsp.rs
@@ -73,6 +73,9 @@ fn find_gsp_sigs_section(chipset: Chipset) -> &'static str {
Architecture::Ampere if chipset == Chipset::GA100 => ".fwsignature_tu10x",
Architecture::Ampere => ".fwsignature_ga10x",
Architecture::Ada => ".fwsignature_ad10x",
+ Architecture::Hopper => ".fwsignature_gh10x",
+ Architecture::BlackwellGB10x => ".fwsignature_gb10x",
+ Architecture::BlackwellGB20x => ".fwsignature_gb20x",
}
}
diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs
index 0f6fe9a1b955..20625e8e26b7 100644
--- a/drivers/gpu/nova-core/gpu.rs
+++ b/drivers/gpu/nova-core/gpu.rs
@@ -86,12 +86,22 @@ fn try_from(value: u32) -> Result<Self, Self::Error> {
GA104 = 0x174,
GA106 = 0x176,
GA107 = 0x177,
+ // Hopper
+ GH100 = 0x180,
// Ada
AD102 = 0x192,
AD103 = 0x193,
AD104 = 0x194,
AD106 = 0x196,
AD107 = 0x197,
+ // Blackwell
+ GB100 = 0x1a0,
+ GB102 = 0x1a2,
+ GB202 = 0x1b2,
+ GB203 = 0x1b3,
+ GB205 = 0x1b5,
+ GB206 = 0x1b6,
+ GB207 = 0x1b7,
});
impl Chipset {
@@ -103,9 +113,14 @@ pub(crate) const fn arch(self) -> Architecture {
Self::GA100 | Self::GA102 | Self::GA103 | Self::GA104 | Self::GA106 | Self::GA107 => {
Architecture::Ampere
}
+ Self::GH100 => Architecture::Hopper,
Self::AD102 | Self::AD103 | Self::AD104 | Self::AD106 | Self::AD107 => {
Architecture::Ada
}
+ Self::GB100 | Self::GB102 => Architecture::BlackwellGB10x,
+ Self::GB202 | Self::GB203 | Self::GB205 | Self::GB206 | Self::GB207 => {
+ Architecture::BlackwellGB20x
+ }
}
}
@@ -137,7 +152,10 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
pub(crate) enum Architecture with TryFrom<Bounded<u32, 6>> {
Turing = 0x16,
Ampere = 0x17,
+ Hopper = 0x18,
Ada = 0x19,
+ BlackwellGB10x = 0x1a,
+ BlackwellGB20x = 0x1b,
}
}
--
2.53.0
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [PATCH v10 04/28] gpu: nova-core: add Copy/Clone to Spec and Revision
2026-04-11 2:49 [PATCH v10 00/28] gpu: nova-core: firmware: Hopper/Blackwell support John Hubbard
` (2 preceding siblings ...)
2026-04-11 2:49 ` [PATCH v10 03/28] gpu: nova-core: Hopper/Blackwell: basic GPU identification John Hubbard
@ 2026-04-11 2:49 ` John Hubbard
2026-04-11 2:49 ` [PATCH v10 05/28] gpu: nova-core: set DMA mask width based on GPU architecture John Hubbard
` (23 subsequent siblings)
27 siblings, 0 replies; 30+ messages in thread
From: John Hubbard @ 2026-04-11 2:49 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot
Cc: Joel Fernandes, Timur Tabi, Alistair Popple, Eliot Courtney,
Shashank Sharma, Zhi Wang, David Airlie, Simona Vetter,
Bjorn Helgaas, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, rust-for-linux, LKML, John Hubbard
Derive Clone and Copy for Revision and Spec. Both are small
value types (4 bytes total) and Copy makes them easier to use
in later patches that pass them across function boundaries.
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
---
drivers/gpu/nova-core/gpu.rs | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs
index 20625e8e26b7..4cf5e1ff830b 100644
--- a/drivers/gpu/nova-core/gpu.rs
+++ b/drivers/gpu/nova-core/gpu.rs
@@ -159,6 +159,7 @@ pub(crate) enum Architecture with TryFrom<Bounded<u32, 6>> {
}
}
+#[derive(Clone, Copy)]
pub(crate) struct Revision {
major: Bounded<u8, 4>,
minor: Bounded<u8, 4>,
@@ -180,6 +181,7 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
}
/// Structure holding a basic description of the GPU: `Chipset` and `Revision`.
+#[derive(Clone, Copy)]
pub(crate) struct Spec {
chipset: Chipset,
revision: Revision,
--
2.53.0
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [PATCH v10 05/28] gpu: nova-core: set DMA mask width based on GPU architecture
2026-04-11 2:49 [PATCH v10 00/28] gpu: nova-core: firmware: Hopper/Blackwell support John Hubbard
` (3 preceding siblings ...)
2026-04-11 2:49 ` [PATCH v10 04/28] gpu: nova-core: add Copy/Clone to Spec and Revision John Hubbard
@ 2026-04-11 2:49 ` John Hubbard
2026-04-11 2:49 ` [PATCH v10 06/28] gpu: nova-core: move GFW boot wait into a GPU HAL John Hubbard
` (22 subsequent siblings)
27 siblings, 0 replies; 30+ messages in thread
From: John Hubbard @ 2026-04-11 2:49 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot
Cc: Joel Fernandes, Timur Tabi, Alistair Popple, Eliot Courtney,
Shashank Sharma, Zhi Wang, David Airlie, Simona Vetter,
Bjorn Helgaas, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, rust-for-linux, LKML, John Hubbard
Replace the hardcoded 47-bit DMA mask with per-architecture values.
Add Architecture::dma_mask() with an exhaustive match, so new
architectures get a compile-time reminder to specify their width.
Set the DMA mask in Gpu::new(). Gpu owns all DMA allocations for
the device, so no concurrent allocations can exist while the
constructor is still running.
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
---
drivers/gpu/nova-core/driver.rs | 16 ----------------
drivers/gpu/nova-core/gpu.rs | 22 ++++++++++++++++++++--
2 files changed, 20 insertions(+), 18 deletions(-)
diff --git a/drivers/gpu/nova-core/driver.rs b/drivers/gpu/nova-core/driver.rs
index 84b0e1703150..3f655337ef6f 100644
--- a/drivers/gpu/nova-core/driver.rs
+++ b/drivers/gpu/nova-core/driver.rs
@@ -4,8 +4,6 @@
auxiliary,
device::Core,
devres::Devres,
- dma::Device,
- dma::DmaMask,
pci,
pci::{
Class,
@@ -38,14 +36,6 @@ pub(crate) struct NovaCore {
const BAR0_SIZE: usize = SZ_16M;
-// For now we only support Ampere which can use up to 47-bit DMA addresses.
-//
-// TODO: Add an abstraction for this to support newer GPUs which may support
-// larger DMA addresses. Limiting these GPUs to smaller address widths won't
-// have any adverse affects, unless installed on systems which require larger
-// DMA addresses. These systems should be quite rare.
-const GPU_DMA_BITS: u32 = 47;
-
pub(crate) type Bar0 = pci::Bar<BAR0_SIZE>;
kernel::pci_device_table!(
@@ -84,16 +74,10 @@ fn probe(pdev: &pci::Device<Core>, _info: &Self::IdInfo) -> impl PinInit<Self, E
pdev.enable_device_mem()?;
pdev.set_master();
- // SAFETY: No concurrent DMA allocations or mappings can be made because
- // the device is still being probed and therefore isn't being used by
- // other threads of execution.
- unsafe { pdev.dma_set_mask_and_coherent(DmaMask::new::<GPU_DMA_BITS>())? };
-
let bar = Arc::pin_init(
pdev.iomap_region_sized::<BAR0_SIZE>(0, c"nova-core/bar0"),
GFP_KERNEL,
)?;
-
Ok(try_pin_init!(Self {
gpu <- Gpu::new(pdev, bar.clone(), bar.access(pdev.as_ref())?),
_reg <- auxiliary::Registration::new(
diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs
index 4cf5e1ff830b..6db646a49519 100644
--- a/drivers/gpu/nova-core/gpu.rs
+++ b/drivers/gpu/nova-core/gpu.rs
@@ -3,6 +3,10 @@
use kernel::{
device,
devres::Devres,
+ dma::{
+ Device,
+ DmaMask, //
+ },
fmt,
io::Io,
num::Bounded,
@@ -159,6 +163,16 @@ pub(crate) enum Architecture with TryFrom<Bounded<u32, 6>> {
}
}
+impl Architecture {
+ /// Returns the DMA mask supported by this architecture.
+ pub(crate) const fn dma_mask(&self) -> DmaMask {
+ match self {
+ Self::Turing | Self::Ampere | Self::Ada => DmaMask::new::<47>(),
+ Self::Hopper | Self::BlackwellGB10x | Self::BlackwellGB20x => DmaMask::new::<52>(),
+ }
+ }
+}
+
#[derive(Clone, Copy)]
pub(crate) struct Revision {
major: Bounded<u8, 4>,
@@ -262,17 +276,21 @@ pub(crate) struct Gpu {
impl Gpu {
pub(crate) fn new<'a>(
- pdev: &'a pci::Device<device::Bound>,
+ pdev: &'a pci::Device<device::Core>,
devres_bar: Arc<Devres<Bar0>>,
bar: &'a Bar0,
) -> impl PinInit<Self, Error> + 'a {
try_pin_init!(Self {
spec: Spec::new(pdev.as_ref(), bar).inspect(|spec| {
- dev_info!(pdev,"NVIDIA ({})\n", spec);
+ dev_info!(pdev, "NVIDIA ({})\n", spec);
})?,
// We must wait for GFW_BOOT completion before doing any significant setup on the GPU.
_: {
+ // SAFETY: `Gpu` owns all DMA allocations for this device, and we are
+ // still constructing it, so no concurrent DMA allocations can exist.
+ unsafe { pdev.dma_set_mask_and_coherent(spec.chipset.arch().dma_mask())? };
+
gfw::wait_gfw_boot_completion(bar)
.inspect_err(|_| dev_err!(pdev, "GFW boot did not complete\n"))?;
},
--
2.53.0
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [PATCH v10 06/28] gpu: nova-core: move GFW boot wait into a GPU HAL
2026-04-11 2:49 [PATCH v10 00/28] gpu: nova-core: firmware: Hopper/Blackwell support John Hubbard
` (4 preceding siblings ...)
2026-04-11 2:49 ` [PATCH v10 05/28] gpu: nova-core: set DMA mask width based on GPU architecture John Hubbard
@ 2026-04-11 2:49 ` John Hubbard
2026-04-11 2:49 ` [PATCH v10 07/28] gpu: nova-core: Hopper/Blackwell: skip GFW boot waiting John Hubbard
` (21 subsequent siblings)
27 siblings, 0 replies; 30+ messages in thread
From: John Hubbard @ 2026-04-11 2:49 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot
Cc: Joel Fernandes, Timur Tabi, Alistair Popple, Eliot Courtney,
Shashank Sharma, Zhi Wang, David Airlie, Simona Vetter,
Bjorn Helgaas, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, rust-for-linux, LKML, John Hubbard
Introduce a GpuHal trait and per-family dispatch so GPU boot
behavior can vary by architecture. Move wait_gfw_boot_completion()
from the standalone gfw module into gpu/hal/tu102.rs as the first
GpuHal implementation. All architectures currently dispatch to this
implementation, preserving existing behavior.
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
---
drivers/gpu/nova-core/gfw.rs | 76 -----------------------
drivers/gpu/nova-core/gpu.rs | 5 +-
drivers/gpu/nova-core/gpu/hal.rs | 29 +++++++++
drivers/gpu/nova-core/gpu/hal/tu102.rs | 86 ++++++++++++++++++++++++++
drivers/gpu/nova-core/nova_core.rs | 1 -
5 files changed, 118 insertions(+), 79 deletions(-)
delete mode 100644 drivers/gpu/nova-core/gfw.rs
create mode 100644 drivers/gpu/nova-core/gpu/hal.rs
create mode 100644 drivers/gpu/nova-core/gpu/hal/tu102.rs
diff --git a/drivers/gpu/nova-core/gfw.rs b/drivers/gpu/nova-core/gfw.rs
deleted file mode 100644
index fb75dd10a172..000000000000
--- a/drivers/gpu/nova-core/gfw.rs
+++ /dev/null
@@ -1,76 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-
-//! GPU Firmware (`GFW`) support, a.k.a `devinit`.
-//!
-//! Upon reset, the GPU runs some firmware code from the BIOS to setup its core parameters. Most of
-//! the GPU is considered unusable until this step is completed, so we must wait on it before
-//! performing driver initialization.
-//!
-//! A clarification about devinit terminology: devinit is a sequence of register read/writes after
-//! reset that performs tasks such as:
-//! 1. Programming VRAM memory controller timings.
-//! 2. Power sequencing.
-//! 3. Clock and PLL configuration.
-//! 4. Thermal management.
-//!
-//! devinit itself is a 'script' which is interpreted by an interpreter program typically running
-//! on the PMU microcontroller.
-//!
-//! Note that the devinit sequence also needs to run during suspend/resume.
-
-use kernel::{
- io::{
- poll::read_poll_timeout,
- Io, //
- },
- prelude::*,
- time::Delta, //
-};
-
-use crate::{
- driver::Bar0,
- regs, //
-};
-
-/// Wait for the `GFW` (GPU firmware) boot completion signal (`GFW_BOOT`), or a 4 seconds timeout.
-///
-/// Upon GPU reset, several microcontrollers (such as PMU, SEC2, GSP etc) run some firmware code to
-/// setup its core parameters. Most of the GPU is considered unusable until this step is completed,
-/// so it must be waited on very early during driver initialization.
-///
-/// The `GFW` code includes several components that need to execute before the driver loads. These
-/// components are located in the VBIOS ROM and executed in a sequence on these different
-/// microcontrollers. The devinit sequence typically runs on the PMU, and the FWSEC runs on the
-/// GSP.
-///
-/// This function waits for a signal indicating that core initialization is complete. Before this
-/// signal is received, little can be done with the GPU. This signal is set by the FWSEC running on
-/// the GSP in Heavy-secured mode.
-pub(crate) fn wait_gfw_boot_completion(bar: &Bar0) -> Result {
- // Before accessing the completion status in `NV_PGC6_AON_SECURE_SCRATCH_GROUP_05`, we must
- // first check `NV_PGC6_AON_SECURE_SCRATCH_GROUP_05_PRIV_LEVEL_MASK`. This is because
- // `NV_PGC6_AON_SECURE_SCRATCH_GROUP_05` becomes accessible only after the secure firmware
- // (FWSEC) lowers the privilege level to allow CPU (LS/Light-secured) access. We can only
- // safely read the status register from CPU (LS/Light-secured) once the mask indicates
- // that the privilege level has been lowered.
- //
- // TIMEOUT: arbitrarily large value. GFW starts running immediately after the GPU is put out of
- // reset, and should complete in less time than that.
- read_poll_timeout(
- || {
- Ok(
- // Check that FWSEC has lowered its protection level before reading the GFW_BOOT
- // status.
- bar.read(regs::NV_PGC6_AON_SECURE_SCRATCH_GROUP_05_PRIV_LEVEL_MASK)
- .read_protection_level0()
- && bar
- .read(regs::NV_PGC6_AON_SECURE_SCRATCH_GROUP_05_0_GFW_BOOT)
- .completed(),
- )
- },
- |&gfw_booted| gfw_booted,
- Delta::from_millis(1),
- Delta::from_secs(4),
- )
- .map(|_| ())
-}
diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs
index 6db646a49519..0b3a62f6ad1b 100644
--- a/drivers/gpu/nova-core/gpu.rs
+++ b/drivers/gpu/nova-core/gpu.rs
@@ -24,11 +24,12 @@
Falcon, //
},
fb::SysmemFlush,
- gfw,
gsp::Gsp,
regs,
};
+mod hal;
+
macro_rules! define_chipset {
({ $($variant:ident = $value:expr),* $(,)* }) =>
{
@@ -291,7 +292,7 @@ pub(crate) fn new<'a>(
// still constructing it, so no concurrent DMA allocations can exist.
unsafe { pdev.dma_set_mask_and_coherent(spec.chipset.arch().dma_mask())? };
- gfw::wait_gfw_boot_completion(bar)
+ hal::gpu_hal(spec.chipset).wait_gfw_boot_completion(bar)
.inspect_err(|_| dev_err!(pdev, "GFW boot did not complete\n"))?;
},
diff --git a/drivers/gpu/nova-core/gpu/hal.rs b/drivers/gpu/nova-core/gpu/hal.rs
new file mode 100644
index 000000000000..a261c6af92be
--- /dev/null
+++ b/drivers/gpu/nova-core/gpu/hal.rs
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: GPL-2.0
+
+use kernel::prelude::*;
+
+use crate::{
+ driver::Bar0,
+ gpu::{
+ Architecture,
+ Chipset, //
+ },
+};
+
+mod tu102;
+
+pub(crate) trait GpuHal {
+ /// Waits for GFW_BOOT completion if required by this hardware family.
+ fn wait_gfw_boot_completion(&self, bar: &Bar0) -> Result;
+}
+
+pub(super) fn gpu_hal(chipset: Chipset) -> &'static dyn GpuHal {
+ match chipset.arch() {
+ Architecture::Turing
+ | Architecture::Ampere
+ | Architecture::Ada
+ | Architecture::Hopper
+ | Architecture::BlackwellGB10x
+ | Architecture::BlackwellGB20x => tu102::TU102_HAL,
+ }
+}
diff --git a/drivers/gpu/nova-core/gpu/hal/tu102.rs b/drivers/gpu/nova-core/gpu/hal/tu102.rs
new file mode 100644
index 000000000000..08dd4434bd72
--- /dev/null
+++ b/drivers/gpu/nova-core/gpu/hal/tu102.rs
@@ -0,0 +1,86 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! GPU Firmware (`GFW`) support, a.k.a `devinit`.
+//!
+//! Upon reset, the GPU runs some firmware code from the BIOS to setup its core parameters. Most of
+//! the GPU is considered unusable until this step is completed, so we must wait on it before
+//! performing driver initialization.
+//!
+//! A clarification about devinit terminology: devinit is a sequence of register read/writes after
+//! reset that performs tasks such as:
+//! 1. Programming VRAM memory controller timings.
+//! 2. Power sequencing.
+//! 3. Clock and PLL configuration.
+//! 4. Thermal management.
+//!
+//! devinit itself is a 'script' which is interpreted by an interpreter program typically running
+//! on the PMU microcontroller.
+//!
+//! Note that the devinit sequence also needs to run during suspend/resume.
+
+use kernel::{
+ io::{
+ poll::read_poll_timeout,
+ Io, //
+ },
+ prelude::*,
+ time::Delta, //
+};
+
+use crate::{
+ driver::Bar0,
+ regs, //
+};
+
+use super::GpuHal;
+
+struct Tu102;
+
+impl GpuHal for Tu102 {
+ /// Wait for the `GFW` (GPU firmware) boot completion signal (`GFW_BOOT`), or a 4 seconds
+ /// timeout.
+ ///
+ /// Upon GPU reset, several microcontrollers (such as PMU, SEC2, GSP etc) run some firmware
+ /// code to setup its core parameters. Most of the GPU is considered unusable until this step
+ /// is completed, so it must be waited on very early during driver initialization.
+ ///
+ /// The `GFW` code includes several components that need to execute before the driver loads.
+ /// These components are located in the VBIOS ROM and executed in a sequence on these different
+ /// microcontrollers. The devinit sequence typically runs on the PMU, and the FWSEC runs on the
+ /// GSP.
+ ///
+ /// This function waits for a signal indicating that core initialization is complete. Before
+ /// this signal is received, little can be done with the GPU. This signal is set by the FWSEC
+ /// running on the GSP in Heavy-secured mode.
+ fn wait_gfw_boot_completion(&self, bar: &Bar0) -> Result {
+ // Before accessing the completion status in `NV_PGC6_AON_SECURE_SCRATCH_GROUP_05`, we must
+ // first check `NV_PGC6_AON_SECURE_SCRATCH_GROUP_05_PRIV_LEVEL_MASK`. This is because
+ // `NV_PGC6_AON_SECURE_SCRATCH_GROUP_05` becomes accessible only after the secure firmware
+ // (FWSEC) lowers the privilege level to allow CPU (LS/Light-secured) access. We can only
+ // safely read the status register from CPU (LS/Light-secured) once the mask indicates
+ // that the privilege level has been lowered.
+ //
+ // TIMEOUT: arbitrarily large value. GFW starts running immediately after the GPU is put
+ // out of reset, and should complete in less time than that.
+ read_poll_timeout(
+ || {
+ Ok(
+ // Check that FWSEC has lowered its protection level before reading the
+ // GFW_BOOT status.
+ bar.read(regs::NV_PGC6_AON_SECURE_SCRATCH_GROUP_05_PRIV_LEVEL_MASK)
+ .read_protection_level0()
+ && bar
+ .read(regs::NV_PGC6_AON_SECURE_SCRATCH_GROUP_05_0_GFW_BOOT)
+ .completed(),
+ )
+ },
+ |&gfw_booted| gfw_booted,
+ Delta::from_millis(1),
+ Delta::from_secs(4),
+ )
+ .map(|_| ())
+ }
+}
+
+const TU102: Tu102 = Tu102;
+pub(super) const TU102_HAL: &dyn GpuHal = &TU102;
diff --git a/drivers/gpu/nova-core/nova_core.rs b/drivers/gpu/nova-core/nova_core.rs
index 04a1fa6b25f8..3a609f6937e4 100644
--- a/drivers/gpu/nova-core/nova_core.rs
+++ b/drivers/gpu/nova-core/nova_core.rs
@@ -17,7 +17,6 @@
mod falcon;
mod fb;
mod firmware;
-mod gfw;
mod gpu;
mod gsp;
#[macro_use]
--
2.53.0
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [PATCH v10 07/28] gpu: nova-core: Hopper/Blackwell: skip GFW boot waiting
2026-04-11 2:49 [PATCH v10 00/28] gpu: nova-core: firmware: Hopper/Blackwell support John Hubbard
` (5 preceding siblings ...)
2026-04-11 2:49 ` [PATCH v10 06/28] gpu: nova-core: move GFW boot wait into a GPU HAL John Hubbard
@ 2026-04-11 2:49 ` John Hubbard
2026-04-11 2:49 ` [PATCH v10 08/28] gpu: nova-core: Blackwell: calculate reserved FB heap size John Hubbard
` (20 subsequent siblings)
27 siblings, 0 replies; 30+ messages in thread
From: John Hubbard @ 2026-04-11 2:49 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot
Cc: Joel Fernandes, Timur Tabi, Alistair Popple, Eliot Courtney,
Shashank Sharma, Zhi Wang, David Airlie, Simona Vetter,
Bjorn Helgaas, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, rust-for-linux, LKML, John Hubbard
Hopper and Blackwell GPUs use FSP-based secure boot and do not
require waiting for GFW_BOOT completion. Add a Gh100 GPU HAL that
returns Ok(()) for wait_gfw_boot_completion(), and route Hopper
and Blackwell architectures to it.
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
---
drivers/gpu/nova-core/gpu/hal.rs | 11 +++++------
drivers/gpu/nova-core/gpu/hal/gh100.rs | 18 ++++++++++++++++++
2 files changed, 23 insertions(+), 6 deletions(-)
create mode 100644 drivers/gpu/nova-core/gpu/hal/gh100.rs
diff --git a/drivers/gpu/nova-core/gpu/hal.rs b/drivers/gpu/nova-core/gpu/hal.rs
index a261c6af92be..788de20ab5d3 100644
--- a/drivers/gpu/nova-core/gpu/hal.rs
+++ b/drivers/gpu/nova-core/gpu/hal.rs
@@ -10,6 +10,7 @@
},
};
+mod gh100;
mod tu102;
pub(crate) trait GpuHal {
@@ -19,11 +20,9 @@ pub(crate) trait GpuHal {
pub(super) fn gpu_hal(chipset: Chipset) -> &'static dyn GpuHal {
match chipset.arch() {
- Architecture::Turing
- | Architecture::Ampere
- | Architecture::Ada
- | Architecture::Hopper
- | Architecture::BlackwellGB10x
- | Architecture::BlackwellGB20x => tu102::TU102_HAL,
+ Architecture::Turing | Architecture::Ampere | Architecture::Ada => tu102::TU102_HAL,
+ Architecture::Hopper | Architecture::BlackwellGB10x | Architecture::BlackwellGB20x => {
+ gh100::GH100_HAL
+ }
}
}
diff --git a/drivers/gpu/nova-core/gpu/hal/gh100.rs b/drivers/gpu/nova-core/gpu/hal/gh100.rs
new file mode 100644
index 000000000000..1ed5bccdda1d
--- /dev/null
+++ b/drivers/gpu/nova-core/gpu/hal/gh100.rs
@@ -0,0 +1,18 @@
+// SPDX-License-Identifier: GPL-2.0
+
+use kernel::prelude::*;
+
+use crate::driver::Bar0;
+
+use super::GpuHal;
+
+struct Gh100;
+
+impl GpuHal for Gh100 {
+ fn wait_gfw_boot_completion(&self, _bar: &Bar0) -> Result {
+ Ok(())
+ }
+}
+
+const GH100: Gh100 = Gh100;
+pub(super) const GH100_HAL: &dyn GpuHal = &GH100;
--
2.53.0
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [PATCH v10 08/28] gpu: nova-core: Blackwell: calculate reserved FB heap size
2026-04-11 2:49 [PATCH v10 00/28] gpu: nova-core: firmware: Hopper/Blackwell support John Hubbard
` (6 preceding siblings ...)
2026-04-11 2:49 ` [PATCH v10 07/28] gpu: nova-core: Hopper/Blackwell: skip GFW boot waiting John Hubbard
@ 2026-04-11 2:49 ` John Hubbard
2026-04-11 2:49 ` [PATCH v10 09/28] gpu: nova-core: Hopper/Blackwell: new location for PCI config mirror John Hubbard
` (19 subsequent siblings)
27 siblings, 0 replies; 30+ messages in thread
From: John Hubbard @ 2026-04-11 2:49 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot
Cc: Joel Fernandes, Timur Tabi, Alistair Popple, Eliot Courtney,
Shashank Sharma, Zhi Wang, David Airlie, Simona Vetter,
Bjorn Helgaas, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, rust-for-linux, LKML, John Hubbard
Various "reserved" areas of FB (frame buffer: vidmem) have to be
calculated, because the GSP booting process needs this information.
PMU_RESERVED_SIZE is computed at compile time using const_align_up() and
applied only for Blackwell dGPU architectures. All other architectures
leave it at zero, matching Open RM behavior.
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
---
drivers/gpu/nova-core/fb.rs | 38 ++++++++++++++++++++++++++++++---
drivers/gpu/nova-core/gsp/fw.rs | 1 +
2 files changed, 36 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/nova-core/fb.rs b/drivers/gpu/nova-core/fb.rs
index 35899e9b2560..c2005e4b4177 100644
--- a/drivers/gpu/nova-core/fb.rs
+++ b/drivers/gpu/nova-core/fb.rs
@@ -12,6 +12,7 @@
io::Io,
prelude::*,
ptr::{
+ const_align_up,
Alignable,
Alignment, //
},
@@ -22,10 +23,16 @@
use crate::{
driver::Bar0,
firmware::gsp::GspFirmware,
- gpu::Chipset,
+ gpu::{
+ Architecture,
+ Chipset, //
+ },
gsp,
- num::FromSafeCast,
- regs, //
+ num::{
+ usize_into_u32,
+ FromSafeCast, //
+ },
+ regs,
};
mod hal;
@@ -168,6 +175,8 @@ pub(crate) struct FbLayout {
pub(crate) wpr2: FbRange,
pub(crate) heap: FbRange,
pub(crate) vf_partition_count: u8,
+ /// PMU reserved memory size, in bytes.
+ pub(crate) pmu_reserved_size: u32,
}
impl FbLayout {
@@ -268,6 +277,29 @@ pub(crate) fn new(chipset: Chipset, bar: &Bar0, gsp_fw: &GspFirmware) -> Result<
wpr2,
heap,
vf_partition_count: 0,
+ pmu_reserved_size: match chipset.arch() {
+ Architecture::BlackwellGB10x | Architecture::BlackwellGB20x => PMU_RESERVED_SIZE,
+ _ => 0,
+ },
})
}
}
+
+/// Returns the PMU reserved memory size for `chipset`.
+#[expect(dead_code)]
+pub(crate) fn calc_pmu_reserved_size(chipset: Chipset) -> u32 {
+ match chipset.arch() {
+ Architecture::BlackwellGB10x | Architecture::BlackwellGB20x => PMU_RESERVED_SIZE,
+ _ => 0,
+ }
+}
+
+/// PMU reserved size, aligned to 128KB.
+pub(crate) const PMU_RESERVED_SIZE: u32 = usize_into_u32::<
+ {
+ match const_align_up(SZ_8M + SZ_16M + SZ_4K, Alignment::new::<SZ_128K>()) {
+ Some(v) => v,
+ None => panic!("PMU_RESERVED_SIZE: alignment overflow"),
+ }
+ },
+>();
diff --git a/drivers/gpu/nova-core/gsp/fw.rs b/drivers/gpu/nova-core/gsp/fw.rs
index 3245793bbe42..5d36604ea1a3 100644
--- a/drivers/gpu/nova-core/gsp/fw.rs
+++ b/drivers/gpu/nova-core/gsp/fw.rs
@@ -247,6 +247,7 @@ pub(crate) fn new<'a>(
fbSize: fb_layout.fb.end - fb_layout.fb.start,
vgaWorkspaceOffset: fb_layout.vga_workspace.start,
vgaWorkspaceSize: fb_layout.vga_workspace.end - fb_layout.vga_workspace.start,
+ pmuReservedSize: fb_layout.pmu_reserved_size,
..Zeroable::init_zeroed()
});
--
2.53.0
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [PATCH v10 09/28] gpu: nova-core: Hopper/Blackwell: new location for PCI config mirror
2026-04-11 2:49 [PATCH v10 00/28] gpu: nova-core: firmware: Hopper/Blackwell support John Hubbard
` (7 preceding siblings ...)
2026-04-11 2:49 ` [PATCH v10 08/28] gpu: nova-core: Blackwell: calculate reserved FB heap size John Hubbard
@ 2026-04-11 2:49 ` John Hubbard
2026-04-11 2:49 ` [PATCH v10 10/28] gpu: nova-core: refactor SEC2 booter loading into BooterFirmware::run() John Hubbard
` (18 subsequent siblings)
27 siblings, 0 replies; 30+ messages in thread
From: John Hubbard @ 2026-04-11 2:49 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot
Cc: Joel Fernandes, Timur Tabi, Alistair Popple, Eliot Courtney,
Shashank Sharma, Zhi Wang, David Airlie, Simona Vetter,
Bjorn Helgaas, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, rust-for-linux, LKML, John Hubbard
Hopper and Blackwell GPUs use a different PCI config space mirror
address (0x092000) compared to older architectures (0x088000). Update
SetSystemInfo to accept a chipset parameter and select the correct
address based on architecture.
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
---
drivers/gpu/nova-core/gsp/boot.rs | 2 +-
drivers/gpu/nova-core/gsp/commands.rs | 8 +++++---
drivers/gpu/nova-core/gsp/fw/commands.rs | 22 +++++++++++++++++++---
3 files changed, 25 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/nova-core/gsp/boot.rs b/drivers/gpu/nova-core/gsp/boot.rs
index 18f356c9178e..1aa869693b75 100644
--- a/drivers/gpu/nova-core/gsp/boot.rs
+++ b/drivers/gpu/nova-core/gsp/boot.rs
@@ -169,7 +169,7 @@ pub(crate) fn boot(
let wpr_meta = Coherent::init(dev, GFP_KERNEL, GspFwWprMeta::new(&gsp_fw, &fb_layout))?;
self.cmdq
- .send_command_no_wait(bar, commands::SetSystemInfo::new(pdev))?;
+ .send_command_no_wait(bar, commands::SetSystemInfo::new(pdev, chipset))?;
self.cmdq
.send_command_no_wait(bar, commands::SetRegistry::new())?;
diff --git a/drivers/gpu/nova-core/gsp/commands.rs b/drivers/gpu/nova-core/gsp/commands.rs
index c89c7b57a751..9551a789433c 100644
--- a/drivers/gpu/nova-core/gsp/commands.rs
+++ b/drivers/gpu/nova-core/gsp/commands.rs
@@ -19,6 +19,7 @@
use crate::{
driver::Bar0,
+ gpu::Chipset,
gsp::{
cmdq::{
Cmdq,
@@ -37,12 +38,13 @@
/// The `GspSetSystemInfo` command.
pub(crate) struct SetSystemInfo<'a> {
pdev: &'a pci::Device<device::Bound>,
+ chipset: Chipset,
}
impl<'a> SetSystemInfo<'a> {
/// Creates a new `GspSetSystemInfo` command using the parameters of `pdev`.
- pub(crate) fn new(pdev: &'a pci::Device<device::Bound>) -> Self {
- Self { pdev }
+ pub(crate) fn new(pdev: &'a pci::Device<device::Bound>, chipset: Chipset) -> Self {
+ Self { pdev, chipset }
}
}
@@ -53,7 +55,7 @@ impl<'a> CommandToGsp for SetSystemInfo<'a> {
type InitError = Error;
fn init(&self) -> impl Init<Self::Command, Self::InitError> {
- GspSetSystemInfo::init(self.pdev)
+ GspSetSystemInfo::init(self.pdev, self.chipset)
}
}
diff --git a/drivers/gpu/nova-core/gsp/fw/commands.rs b/drivers/gpu/nova-core/gsp/fw/commands.rs
index db46276430be..c16acbb0237f 100644
--- a/drivers/gpu/nova-core/gsp/fw/commands.rs
+++ b/drivers/gpu/nova-core/gsp/fw/commands.rs
@@ -10,7 +10,13 @@
}, //
};
-use crate::gsp::GSP_PAGE_SIZE;
+use crate::{
+ gpu::{
+ Architecture,
+ Chipset, //
+ },
+ gsp::GSP_PAGE_SIZE, //
+};
use super::bindings;
@@ -24,7 +30,10 @@ pub(crate) struct GspSetSystemInfo {
impl GspSetSystemInfo {
/// Returns an in-place initializer for the `GspSetSystemInfo` command.
#[allow(non_snake_case)]
- pub(crate) fn init<'a>(dev: &'a pci::Device<device::Bound>) -> impl Init<Self, Error> + 'a {
+ pub(crate) fn init<'a>(
+ dev: &'a pci::Device<device::Bound>,
+ chipset: Chipset,
+ ) -> impl Init<Self, Error> + 'a {
type InnerGspSystemInfo = bindings::GspSystemInfo;
let init_inner = try_init!(InnerGspSystemInfo {
gpuPhysAddr: dev.resource_start(0)?,
@@ -35,7 +44,14 @@ pub(crate) fn init<'a>(dev: &'a pci::Device<device::Bound>) -> impl Init<Self, E
// Using TASK_SIZE in r535_gsp_rpc_set_system_info() seems wrong because
// TASK_SIZE is per-task. That's probably a design issue in GSP-RM though.
maxUserVa: (1 << 47) - 4096,
- pciConfigMirrorBase: 0x088000,
+ // Hopper, Blackwell, and later moved the PCI config mirror window to 0x092000.
+ // Older architectures continue to use the legacy window at 0x088000.
+ pciConfigMirrorBase: match chipset.arch() {
+ Architecture::Turing | Architecture::Ampere | Architecture::Ada => 0x088000,
+ Architecture::Hopper
+ | Architecture::BlackwellGB10x
+ | Architecture::BlackwellGB20x => 0x092000,
+ },
pciConfigMirrorSize: 0x001000,
PCIDeviceID: (u32::from(dev.device_id()) << 16) | u32::from(dev.vendor_id().as_raw()),
--
2.53.0
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [PATCH v10 10/28] gpu: nova-core: refactor SEC2 booter loading into BooterFirmware::run()
2026-04-11 2:49 [PATCH v10 00/28] gpu: nova-core: firmware: Hopper/Blackwell support John Hubbard
` (8 preceding siblings ...)
2026-04-11 2:49 ` [PATCH v10 09/28] gpu: nova-core: Hopper/Blackwell: new location for PCI config mirror John Hubbard
@ 2026-04-11 2:49 ` John Hubbard
2026-04-11 2:49 ` [PATCH v10 11/28] gpu: nova-core: Hopper/Blackwell: integrate FSP boot path into boot() John Hubbard
` (17 subsequent siblings)
27 siblings, 0 replies; 30+ messages in thread
From: John Hubbard @ 2026-04-11 2:49 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot
Cc: Joel Fernandes, Timur Tabi, Alistair Popple, Eliot Courtney,
Shashank Sharma, Zhi Wang, David Airlie, Simona Vetter,
Bjorn Helgaas, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, rust-for-linux, LKML, John Hubbard
Move the SEC2 reset/load/boot sequence into a BooterFirmware::run()
method, and call it from a thin run_booter() helper on Gsp. This is a
pure refactoring with no behavior change, done in preparation for adding
an alternative FSP boot path.
Suggested-by: Danilo Krummrich <dakr@kernel.org>
Co-developed-by: Alexandre Courbot <acourbot@nvidia.com>
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
---
drivers/gpu/nova-core/firmware/booter.rs | 30 +++++++++++++++++
drivers/gpu/nova-core/gsp/boot.rs | 43 +++++++++++-------------
2 files changed, 50 insertions(+), 23 deletions(-)
diff --git a/drivers/gpu/nova-core/firmware/booter.rs b/drivers/gpu/nova-core/firmware/booter.rs
index de2a4536b532..6a41690e72c6 100644
--- a/drivers/gpu/nova-core/firmware/booter.rs
+++ b/drivers/gpu/nova-core/firmware/booter.rs
@@ -8,6 +8,7 @@
use kernel::{
device,
+ dma::Coherent,
prelude::*,
transmute::FromBytes, //
};
@@ -396,6 +397,35 @@ pub(crate) fn new(
ucode: ucode_signed,
})
}
+
+ /// Load and run the booter firmware on SEC2.
+ ///
+ /// Resets SEC2, loads this firmware image, then boots with the WPR metadata
+ /// address passed via the SEC2 mailboxes.
+ pub(crate) fn run<T>(
+ &self,
+ dev: &device::Device<device::Bound>,
+ bar: &Bar0,
+ sec2_falcon: &Falcon<Sec2>,
+ wpr_meta: &Coherent<T>,
+ ) -> Result {
+ sec2_falcon.reset(bar)?;
+ sec2_falcon.load(dev, bar, self)?;
+ let wpr_handle = wpr_meta.dma_handle();
+ let (mbox0, mbox1) = sec2_falcon.boot(
+ bar,
+ Some(wpr_handle as u32),
+ Some((wpr_handle >> 32) as u32),
+ )?;
+ dev_dbg!(dev, "SEC2 MBOX0: {:#x}, MBOX1: {:#x}\n", mbox0, mbox1);
+
+ if mbox0 != 0 {
+ dev_err!(dev, "Booter-load failed with error {:#x}\n", mbox0);
+ return Err(ENODEV);
+ }
+
+ Ok(())
+ }
}
impl FalconDmaLoadable for BooterFirmware {
diff --git a/drivers/gpu/nova-core/gsp/boot.rs b/drivers/gpu/nova-core/gsp/boot.rs
index 1aa869693b75..bb4f0757e084 100644
--- a/drivers/gpu/nova-core/gsp/boot.rs
+++ b/drivers/gpu/nova-core/gsp/boot.rs
@@ -131,6 +131,25 @@ fn run_fwsec_frts(
}
}
+ fn run_booter(
+ dev: &device::Device<device::Bound>,
+ bar: &Bar0,
+ chipset: Chipset,
+ sec2_falcon: &Falcon<Sec2>,
+ wpr_meta: &Coherent<GspFwWprMeta>,
+ ) -> Result {
+ let booter = BooterFirmware::new(
+ dev,
+ BooterKind::Loader,
+ chipset,
+ FIRMWARE_VERSION,
+ sec2_falcon,
+ bar,
+ )?;
+
+ booter.run(dev, bar, sec2_falcon, wpr_meta)
+ }
+
/// Attempt to boot the GSP.
///
/// This is a GPU-dependent and complex procedure that involves loading firmware files from
@@ -157,15 +176,6 @@ pub(crate) fn boot(
Self::run_fwsec_frts(dev, chipset, gsp_falcon, bar, &bios, &fb_layout)?;
- let booter_loader = BooterFirmware::new(
- dev,
- BooterKind::Loader,
- chipset,
- FIRMWARE_VERSION,
- sec2_falcon,
- bar,
- )?;
-
let wpr_meta = Coherent::init(dev, GFP_KERNEL, GspFwWprMeta::new(&gsp_fw, &fb_layout))?;
self.cmdq
@@ -187,20 +197,7 @@ pub(crate) fn boot(
"Using SEC2 to load and run the booter_load firmware...\n"
);
- sec2_falcon.reset(bar)?;
- sec2_falcon.load(dev, bar, &booter_loader)?;
- let wpr_handle = wpr_meta.dma_handle();
- let (mbox0, mbox1) = sec2_falcon.boot(
- bar,
- Some(wpr_handle as u32),
- Some((wpr_handle >> 32) as u32),
- )?;
- dev_dbg!(pdev, "SEC2 MBOX0: {:#x}, MBOX1: {:#x}\n", mbox0, mbox1);
-
- if mbox0 != 0 {
- dev_err!(pdev, "Booter-load failed with error {:#x}\n", mbox0);
- return Err(ENODEV);
- }
+ Self::run_booter(dev, bar, chipset, sec2_falcon, &wpr_meta)?;
gsp_falcon.write_os_version(bar, gsp_fw.bootloader.app_version);
--
2.53.0
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [PATCH v10 11/28] gpu: nova-core: Hopper/Blackwell: integrate FSP boot path into boot()
2026-04-11 2:49 [PATCH v10 00/28] gpu: nova-core: firmware: Hopper/Blackwell support John Hubbard
` (9 preceding siblings ...)
2026-04-11 2:49 ` [PATCH v10 10/28] gpu: nova-core: refactor SEC2 booter loading into BooterFirmware::run() John Hubbard
@ 2026-04-11 2:49 ` John Hubbard
2026-04-11 2:49 ` [PATCH v10 12/28] gpu: nova-core: don't assume 64-bit firmware images John Hubbard
` (16 subsequent siblings)
27 siblings, 0 replies; 30+ messages in thread
From: John Hubbard @ 2026-04-11 2:49 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot
Cc: Joel Fernandes, Timur Tabi, Alistair Popple, Eliot Courtney,
Shashank Sharma, Zhi Wang, David Airlie, Simona Vetter,
Bjorn Helgaas, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, rust-for-linux, LKML, John Hubbard
Add the FSP boot path for Hopper and Blackwell GPUs. These architectures
use FSP with FMC firmware for Chain of Trust boot, rather than SEC2.
boot() now dispatches to boot_via_sec2() or boot_via_fsp() based on
architecture. The SEC2 path keeps its original command ordering. The
FSP path sends SetSystemInfo/SetRegistry after GSP becomes active.
The GSP sequencer only runs for SEC2-based architectures.
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
---
drivers/gpu/nova-core/gsp/boot.rs | 137 ++++++++++++++++++++++--------
1 file changed, 102 insertions(+), 35 deletions(-)
diff --git a/drivers/gpu/nova-core/gsp/boot.rs b/drivers/gpu/nova-core/gsp/boot.rs
index bb4f0757e084..88c7d2106052 100644
--- a/drivers/gpu/nova-core/gsp/boot.rs
+++ b/drivers/gpu/nova-core/gsp/boot.rs
@@ -31,9 +31,13 @@
gsp::GspFirmware,
FIRMWARE_VERSION, //
},
- gpu::Chipset,
+ gpu::{
+ Architecture,
+ Chipset, //
+ },
gsp::{
commands,
+ fw::LibosMemoryRegionInitArgument,
sequencer::{
GspSequencer,
GspSequencerParams, //
@@ -150,6 +154,58 @@ fn run_booter(
booter.run(dev, bar, sec2_falcon, wpr_meta)
}
+ /// Boot GSP via SEC2 booter firmware (Turing/Ampere/Ada path).
+ ///
+ /// This path uses FWSEC-FRTS to set up WPR2, then boots GSP directly,
+ /// then uses SEC2 to run the booter firmware.
+ #[allow(clippy::too_many_arguments)]
+ fn boot_via_sec2(
+ dev: &device::Device<device::Bound>,
+ bar: &Bar0,
+ chipset: Chipset,
+ gsp_falcon: &Falcon<Gsp>,
+ sec2_falcon: &Falcon<Sec2>,
+ fb_layout: &FbLayout,
+ libos: &Coherent<[LibosMemoryRegionInitArgument]>,
+ wpr_meta: &Coherent<GspFwWprMeta>,
+ ) -> Result {
+ // Run FWSEC-FRTS to set up the WPR2 region
+ let bios = Vbios::new(dev, bar)?;
+ Self::run_fwsec_frts(dev, chipset, gsp_falcon, bar, &bios, fb_layout)?;
+
+ // Reset and boot GSP before SEC2
+ gsp_falcon.reset(bar)?;
+ let libos_handle = libos.dma_handle();
+ let (mbox0, mbox1) = gsp_falcon.boot(
+ bar,
+ Some(libos_handle as u32),
+ Some((libos_handle >> 32) as u32),
+ )?;
+ dev_dbg!(dev, "SEC2 MBOX0: {:#x}, MBOX1: {:#x}\n", mbox0, mbox1);
+ dev_dbg!(
+ dev,
+ "Using SEC2 to load and run the booter_load firmware...\n"
+ );
+
+ // Run booter via SEC2
+ Self::run_booter(dev, bar, chipset, sec2_falcon, wpr_meta)
+ }
+
+ /// Boot GSP via FSP Chain of Trust (Hopper/Blackwell+ path).
+ ///
+ /// 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_via_fsp(
+ _dev: &device::Device<device::Bound>,
+ _bar: &Bar0,
+ _chipset: Chipset,
+ _gsp_falcon: &Falcon<Gsp>,
+ _wpr_meta: &Coherent<GspFwWprMeta>,
+ _libos: &Coherent<[LibosMemoryRegionInitArgument]>,
+ ) -> Result {
+ Err(ENOTSUPP)
+ }
+
/// Attempt to boot the GSP.
///
/// This is a GPU-dependent and complex procedure that involves loading firmware files from
@@ -166,39 +222,41 @@ pub(crate) fn boot(
sec2_falcon: &Falcon<Sec2>,
) -> Result {
let dev = pdev.as_ref();
-
- let bios = Vbios::new(dev, bar)?;
+ let uses_sec2 = matches!(
+ chipset.arch(),
+ Architecture::Turing | Architecture::Ampere | Architecture::Ada
+ );
let gsp_fw = KBox::pin_init(GspFirmware::new(dev, chipset, FIRMWARE_VERSION), GFP_KERNEL)?;
let fb_layout = FbLayout::new(chipset, bar, &gsp_fw)?;
dev_dbg!(dev, "{:#x?}\n", fb_layout);
- Self::run_fwsec_frts(dev, chipset, gsp_falcon, bar, &bios, &fb_layout)?;
-
let wpr_meta = Coherent::init(dev, GFP_KERNEL, GspFwWprMeta::new(&gsp_fw, &fb_layout))?;
- self.cmdq
- .send_command_no_wait(bar, commands::SetSystemInfo::new(pdev, chipset))?;
- self.cmdq
- .send_command_no_wait(bar, commands::SetRegistry::new())?;
+ // Architecture-specific boot path
+ if uses_sec2 {
+ // SEC2 path: send commands before GSP reset/boot (original order).
+ self.cmdq
+ .send_command_no_wait(bar, commands::SetSystemInfo::new(pdev, chipset))?;
+ self.cmdq
+ .send_command_no_wait(bar, commands::SetRegistry::new())?;
- gsp_falcon.reset(bar)?;
- let libos_handle = self.libos.dma_handle();
- let (mbox0, mbox1) = gsp_falcon.boot(
- bar,
- Some(libos_handle as u32),
- Some((libos_handle >> 32) as u32),
- )?;
- dev_dbg!(pdev, "GSP MBOX0: {:#x}, MBOX1: {:#x}\n", mbox0, mbox1);
-
- dev_dbg!(
- pdev,
- "Using SEC2 to load and run the booter_load firmware...\n"
- );
-
- Self::run_booter(dev, bar, chipset, sec2_falcon, &wpr_meta)?;
+ Self::boot_via_sec2(
+ dev,
+ bar,
+ chipset,
+ gsp_falcon,
+ sec2_falcon,
+ &fb_layout,
+ &self.libos,
+ &wpr_meta,
+ )?;
+ } else {
+ Self::boot_via_fsp(dev, bar, chipset, gsp_falcon, &wpr_meta, &self.libos)?;
+ }
+ // Common post-boot initialization
gsp_falcon.write_os_version(bar, gsp_fw.bootloader.app_version);
// Poll for RISC-V to become active before running sequencer
@@ -209,18 +267,27 @@ pub(crate) fn boot(
Delta::from_secs(5),
)?;
- dev_dbg!(pdev, "RISC-V active? {}\n", gsp_falcon.is_riscv_active(bar),);
+ dev_dbg!(dev, "RISC-V active? {}\n", gsp_falcon.is_riscv_active(bar));
- // Create and run the GSP sequencer.
- let seq_params = GspSequencerParams {
- bootloader_app_version: gsp_fw.bootloader.app_version,
- libos_dma_handle: libos_handle,
- gsp_falcon,
- sec2_falcon,
- dev: pdev.as_ref().into(),
- bar,
- };
- GspSequencer::run(&self.cmdq, seq_params)?;
+ // For FSP path, send commands after GSP becomes active.
+ if !uses_sec2 {
+ self.cmdq
+ .send_command_no_wait(bar, commands::SetSystemInfo::new(pdev, chipset))?;
+ self.cmdq
+ .send_command_no_wait(bar, commands::SetRegistry::new())?;
+ } else {
+ // SEC2-based architectures need to run the GSP sequencer
+ let libos_handle = self.libos.dma_handle();
+ let seq_params = GspSequencerParams {
+ bootloader_app_version: gsp_fw.bootloader.app_version,
+ libos_dma_handle: libos_handle,
+ gsp_falcon,
+ sec2_falcon,
+ dev: dev.into(),
+ bar,
+ };
+ GspSequencer::run(&self.cmdq, seq_params)?;
+ }
// Wait until GSP is fully initialized.
commands::wait_gsp_init_done(&self.cmdq)?;
--
2.53.0
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [PATCH v10 12/28] gpu: nova-core: don't assume 64-bit firmware images
2026-04-11 2:49 [PATCH v10 00/28] gpu: nova-core: firmware: Hopper/Blackwell support John Hubbard
` (10 preceding siblings ...)
2026-04-11 2:49 ` [PATCH v10 11/28] gpu: nova-core: Hopper/Blackwell: integrate FSP boot path into boot() John Hubbard
@ 2026-04-11 2:49 ` John Hubbard
2026-04-11 2:49 ` [PATCH v10 13/28] gpu: nova-core: add support for 32-bit " John Hubbard
` (15 subsequent siblings)
27 siblings, 0 replies; 30+ messages in thread
From: John Hubbard @ 2026-04-11 2:49 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot
Cc: Joel Fernandes, Timur Tabi, Alistair Popple, Eliot Courtney,
Shashank Sharma, Zhi Wang, David Airlie, Simona Vetter,
Bjorn Helgaas, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, rust-for-linux, LKML, John Hubbard
Introduce a single ELF format abstraction that ties each ELF header
type to its matching section-header type. This keeps the shared
section parser ready for upcoming ELF32 support and avoids mixing
32-bit and 64-bit ELF layouts by mistake.
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
---
drivers/gpu/nova-core/firmware.rs | 111 ++++++++++++++++++++++--------
1 file changed, 84 insertions(+), 27 deletions(-)
diff --git a/drivers/gpu/nova-core/firmware.rs b/drivers/gpu/nova-core/firmware.rs
index 6c2ab69cb605..46c26d749a65 100644
--- a/drivers/gpu/nova-core/firmware.rs
+++ b/drivers/gpu/nova-core/firmware.rs
@@ -473,17 +473,72 @@ mod elf {
transmute::FromBytes, //
};
+ /// Trait to abstract over ELF header differences.
+ trait ElfHeader: FromBytes {
+ fn shnum(&self) -> u16;
+ fn shoff(&self) -> u64;
+ fn shstrndx(&self) -> u16;
+ }
+
+ /// Trait to abstract over ELF section-header differences.
+ trait ElfSectionHeader: FromBytes {
+ fn name(&self) -> u32;
+ fn offset(&self) -> u64;
+ fn size(&self) -> u64;
+ }
+
+ /// Trait describing a matching ELF header and section-header format.
+ trait ElfFormat {
+ type Header: ElfHeader;
+ type SectionHeader: ElfSectionHeader;
+ }
+
/// Newtype to provide a [`FromBytes`] implementation.
#[repr(transparent)]
struct Elf64Hdr(bindings::elf64_hdr);
// SAFETY: all bit patterns are valid for this type, and it doesn't use interior mutability.
unsafe impl FromBytes for Elf64Hdr {}
+ impl ElfHeader for Elf64Hdr {
+ fn shnum(&self) -> u16 {
+ self.0.e_shnum
+ }
+
+ fn shoff(&self) -> u64 {
+ self.0.e_shoff
+ }
+
+ fn shstrndx(&self) -> u16 {
+ self.0.e_shstrndx
+ }
+ }
+
#[repr(transparent)]
struct Elf64SHdr(bindings::elf64_shdr);
// SAFETY: all bit patterns are valid for this type, and it doesn't use interior mutability.
unsafe impl FromBytes for Elf64SHdr {}
+ impl ElfSectionHeader for Elf64SHdr {
+ fn name(&self) -> u32 {
+ self.0.sh_name
+ }
+
+ fn offset(&self) -> u64 {
+ self.0.sh_offset
+ }
+
+ fn size(&self) -> u64 {
+ self.0.sh_size
+ }
+ }
+
+ struct Elf64Format;
+
+ impl ElfFormat for Elf64Format {
+ type Header = Elf64Hdr;
+ type SectionHeader = Elf64SHdr;
+ }
+
/// Returns a NULL-terminated string from the ELF image at `offset`.
fn elf_str(elf: &[u8], offset: u64) -> Option<&str> {
let idx = usize::try_from(offset).ok()?;
@@ -491,47 +546,49 @@ fn elf_str(elf: &[u8], offset: u64) -> Option<&str> {
CStr::from_bytes_until_nul(bytes).ok()?.to_str().ok()
}
- /// Tries to extract section with name `name` from the ELF64 image `elf`, and returns it.
- pub(super) fn elf64_section<'a, 'b>(elf: &'a [u8], name: &'b str) -> Option<&'a [u8]> {
- let hdr = &elf
- .get(0..size_of::<bindings::elf64_hdr>())
- .and_then(Elf64Hdr::from_bytes)?
- .0;
-
- // Get all the section headers.
- let mut shdr = {
- let shdr_num = usize::from(hdr.e_shnum);
- let shdr_start = usize::try_from(hdr.e_shoff).ok()?;
- let shdr_end = shdr_num
- .checked_mul(size_of::<Elf64SHdr>())
- .and_then(|v| v.checked_add(shdr_start))?;
-
- elf.get(shdr_start..shdr_end)
- .map(|slice| slice.chunks_exact(size_of::<Elf64SHdr>()))?
- };
+ fn elf_section_generic<'a, F>(elf: &'a [u8], name: &str) -> Option<&'a [u8]>
+ where
+ F: ElfFormat,
+ {
+ let hdr = F::Header::from_bytes(elf.get(0..size_of::<F::Header>())?)?;
+
+ let shdr_num = usize::from(hdr.shnum());
+ let shdr_start = usize::try_from(hdr.shoff()).ok()?;
+ let shdr_end = shdr_num
+ .checked_mul(size_of::<F::SectionHeader>())
+ .and_then(|v| v.checked_add(shdr_start))?;
+
+ // Get all the section headers as an iterator over byte chunks.
+ let shdr_bytes = elf.get(shdr_start..shdr_end)?;
+ let mut shdr_iter = shdr_bytes.chunks_exact(size_of::<F::SectionHeader>());
// Get the strings table.
- let strhdr = shdr
+ let strhdr = shdr_iter
.clone()
- .nth(usize::from(hdr.e_shstrndx))
- .and_then(Elf64SHdr::from_bytes)?;
+ .nth(usize::from(hdr.shstrndx()))
+ .and_then(F::SectionHeader::from_bytes)?;
// Find the section which name matches `name` and return it.
- shdr.find_map(|sh| {
- let hdr = Elf64SHdr::from_bytes(sh)?;
- let name_offset = strhdr.0.sh_offset.checked_add(u64::from(hdr.0.sh_name))?;
+ shdr_iter.find_map(|sh_bytes| {
+ let sh = F::SectionHeader::from_bytes(sh_bytes)?;
+ let name_offset = strhdr.offset().checked_add(u64::from(sh.name()))?;
let section_name = elf_str(elf, name_offset)?;
if section_name != name {
return None;
}
- let start = usize::try_from(hdr.0.sh_offset).ok()?;
- let end = usize::try_from(hdr.0.sh_size)
+ let start = usize::try_from(sh.offset()).ok()?;
+ let end = usize::try_from(sh.size())
.ok()
- .and_then(|sh_size| start.checked_add(sh_size))?;
+ .and_then(|sz| start.checked_add(sz))?;
elf.get(start..end)
})
}
+
+ /// Tries to extract section with name `name` from the ELF64 image `elf`, and returns it.
+ pub(super) fn elf64_section<'a>(elf: &'a [u8], name: &str) -> Option<&'a [u8]> {
+ elf_section_generic::<Elf64Format>(elf, name)
+ }
}
--
2.53.0
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [PATCH v10 13/28] gpu: nova-core: add support for 32-bit firmware images
2026-04-11 2:49 [PATCH v10 00/28] gpu: nova-core: firmware: Hopper/Blackwell support John Hubbard
` (11 preceding siblings ...)
2026-04-11 2:49 ` [PATCH v10 12/28] gpu: nova-core: don't assume 64-bit firmware images John Hubbard
@ 2026-04-11 2:49 ` John Hubbard
2026-04-11 2:49 ` [PATCH v10 14/28] gpu: nova-core: add auto-detection of 32-bit, 64-bit " John Hubbard
` (14 subsequent siblings)
27 siblings, 0 replies; 30+ messages in thread
From: John Hubbard @ 2026-04-11 2:49 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot
Cc: Joel Fernandes, Timur Tabi, Alistair Popple, Eliot Courtney,
Shashank Sharma, Zhi Wang, David Airlie, Simona Vetter,
Bjorn Helgaas, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, rust-for-linux, LKML, John Hubbard
Add ELF32 header and section header newtypes with ElfHeader and
ElfSectionHeader trait implementations, mirroring the existing ELF64
support. Add elf32_section() for extracting sections from ELF32 images.
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
---
drivers/gpu/nova-core/firmware.rs | 53 +++++++++++++++++++++++++++++++
1 file changed, 53 insertions(+)
diff --git a/drivers/gpu/nova-core/firmware.rs b/drivers/gpu/nova-core/firmware.rs
index 46c26d749a65..a0745c332d4d 100644
--- a/drivers/gpu/nova-core/firmware.rs
+++ b/drivers/gpu/nova-core/firmware.rs
@@ -539,6 +539,53 @@ impl ElfFormat for Elf64Format {
type SectionHeader = Elf64SHdr;
}
+ /// Newtype to provide [`FromBytes`] and [`ElfHeader`] implementations for ELF32.
+ #[repr(transparent)]
+ struct Elf32Hdr(bindings::elf32_hdr);
+ // SAFETY: all bit patterns are valid for this type, and it doesn't use interior mutability.
+ unsafe impl FromBytes for Elf32Hdr {}
+
+ impl ElfHeader for Elf32Hdr {
+ fn shnum(&self) -> u16 {
+ self.0.e_shnum
+ }
+
+ fn shoff(&self) -> u64 {
+ u64::from(self.0.e_shoff)
+ }
+
+ fn shstrndx(&self) -> u16 {
+ self.0.e_shstrndx
+ }
+ }
+
+ /// Newtype to provide [`FromBytes`] and [`ElfSectionHeader`] implementations for ELF32.
+ #[repr(transparent)]
+ struct Elf32SHdr(bindings::elf32_shdr);
+ // SAFETY: all bit patterns are valid for this type, and it doesn't use interior mutability.
+ unsafe impl FromBytes for Elf32SHdr {}
+
+ impl ElfSectionHeader for Elf32SHdr {
+ fn name(&self) -> u32 {
+ self.0.sh_name
+ }
+
+ fn offset(&self) -> u64 {
+ u64::from(self.0.sh_offset)
+ }
+
+ fn size(&self) -> u64 {
+ u64::from(self.0.sh_size)
+ }
+ }
+
+ struct Elf32Format;
+
+ impl ElfFormat for Elf32Format {
+ type Header = Elf32Hdr;
+ type SectionHeader = Elf32SHdr;
+ }
+
/// Returns a NULL-terminated string from the ELF image at `offset`.
fn elf_str(elf: &[u8], offset: u64) -> Option<&str> {
let idx = usize::try_from(offset).ok()?;
@@ -591,4 +638,10 @@ fn elf_section_generic<'a, F>(elf: &'a [u8], name: &str) -> Option<&'a [u8]>
pub(super) fn elf64_section<'a>(elf: &'a [u8], name: &str) -> Option<&'a [u8]> {
elf_section_generic::<Elf64Format>(elf, name)
}
+
+ /// Extract the section with name `name` from the ELF32 image `elf`.
+ #[expect(dead_code)]
+ pub(super) fn elf32_section<'a>(elf: &'a [u8], name: &str) -> Option<&'a [u8]> {
+ elf_section_generic::<Elf32Format>(elf, name)
+ }
}
--
2.53.0
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [PATCH v10 14/28] gpu: nova-core: add auto-detection of 32-bit, 64-bit firmware images
2026-04-11 2:49 [PATCH v10 00/28] gpu: nova-core: firmware: Hopper/Blackwell support John Hubbard
` (12 preceding siblings ...)
2026-04-11 2:49 ` [PATCH v10 13/28] gpu: nova-core: add support for 32-bit " John Hubbard
@ 2026-04-11 2:49 ` John Hubbard
2026-04-11 2:49 ` [PATCH v10 15/28] gpu: nova-core: Hopper/Blackwell: add FSP falcon engine stub John Hubbard
` (13 subsequent siblings)
27 siblings, 0 replies; 30+ messages in thread
From: John Hubbard @ 2026-04-11 2:49 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot
Cc: Joel Fernandes, Timur Tabi, Alistair Popple, Eliot Courtney,
Shashank Sharma, Zhi Wang, David Airlie, Simona Vetter,
Bjorn Helgaas, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, rust-for-linux, LKML, John Hubbard
Add elf_section() which automatically detects ELF32 vs ELF64 based on
the ELF header's class byte, and dispatches to the appropriate parser.
Switch gsp.rs callers from elf64_section() to elf_section(), making
both elf32_section() and elf64_section() private.
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
---
drivers/gpu/nova-core/firmware.rs | 22 ++++++++++++++++++----
drivers/gpu/nova-core/firmware/gsp.rs | 4 ++--
2 files changed, 20 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/nova-core/firmware.rs b/drivers/gpu/nova-core/firmware.rs
index a0745c332d4d..bc217bfc225f 100644
--- a/drivers/gpu/nova-core/firmware.rs
+++ b/drivers/gpu/nova-core/firmware.rs
@@ -634,14 +634,28 @@ fn elf_section_generic<'a, F>(elf: &'a [u8], name: &str) -> Option<&'a [u8]>
})
}
- /// Tries to extract section with name `name` from the ELF64 image `elf`, and returns it.
- pub(super) fn elf64_section<'a>(elf: &'a [u8], name: &str) -> Option<&'a [u8]> {
+ /// Extract the section with name `name` from the ELF64 image `elf`.
+ fn elf64_section<'a>(elf: &'a [u8], name: &str) -> Option<&'a [u8]> {
elf_section_generic::<Elf64Format>(elf, name)
}
/// Extract the section with name `name` from the ELF32 image `elf`.
- #[expect(dead_code)]
- pub(super) fn elf32_section<'a>(elf: &'a [u8], name: &str) -> Option<&'a [u8]> {
+ fn elf32_section<'a>(elf: &'a [u8], name: &str) -> Option<&'a [u8]> {
elf_section_generic::<Elf32Format>(elf, name)
}
+
+ /// Automatically detects ELF32 vs ELF64 based on the ELF header.
+ pub(super) fn elf_section<'a>(elf: &'a [u8], name: &str) -> Option<&'a [u8]> {
+ // Check ELF magic.
+ if elf.len() < 5 || elf.get(0..4)? != b"\x7fELF" {
+ return None;
+ }
+
+ // Check ELF class: 1 = 32-bit, 2 = 64-bit.
+ match elf.get(4)? {
+ 1 => elf32_section(elf, name),
+ 2 => elf64_section(elf, name),
+ _ => None,
+ }
+ }
}
diff --git a/drivers/gpu/nova-core/firmware/gsp.rs b/drivers/gpu/nova-core/firmware/gsp.rs
index 617ec2aaa93a..98ef618e1eff 100644
--- a/drivers/gpu/nova-core/firmware/gsp.rs
+++ b/drivers/gpu/nova-core/firmware/gsp.rs
@@ -89,7 +89,7 @@ pub(crate) fn new<'a>(
pin_init::pin_init_scope(move || {
let firmware = super::request_firmware(dev, chipset, "gsp", ver)?;
- let fw_section = elf::elf64_section(firmware.data(), ".fwimage").ok_or(EINVAL)?;
+ let fw_section = elf::elf_section(firmware.data(), ".fwimage").ok_or(EINVAL)?;
let size = fw_section.len();
@@ -149,7 +149,7 @@ pub(crate) fn new<'a>(
signatures: {
let sigs_section = Self::find_gsp_sigs_section(chipset);
- elf::elf64_section(firmware.data(), sigs_section)
+ elf::elf_section(firmware.data(), sigs_section)
.ok_or(EINVAL)
.and_then(|data| Coherent::from_slice(dev, data, GFP_KERNEL))?
},
--
2.53.0
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [PATCH v10 15/28] gpu: nova-core: Hopper/Blackwell: add FSP falcon engine stub
2026-04-11 2:49 [PATCH v10 00/28] gpu: nova-core: firmware: Hopper/Blackwell support John Hubbard
` (13 preceding siblings ...)
2026-04-11 2:49 ` [PATCH v10 14/28] gpu: nova-core: add auto-detection of 32-bit, 64-bit " John Hubbard
@ 2026-04-11 2:49 ` John Hubbard
2026-04-11 2:49 ` [PATCH v10 16/28] gpu: nova-core: Hopper/Blackwell: add FMC firmware image, in support of FSP John Hubbard
` (12 subsequent siblings)
27 siblings, 0 replies; 30+ messages in thread
From: John Hubbard @ 2026-04-11 2:49 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot
Cc: Joel Fernandes, Timur Tabi, Alistair Popple, Eliot Courtney,
Shashank Sharma, Zhi Wang, David Airlie, Simona Vetter,
Bjorn Helgaas, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, rust-for-linux, LKML, John Hubbard
Add the FSP (Firmware System Processor) falcon engine type that will
handle secure boot and Chain of Trust operations on Hopper and Blackwell
architectures.
The FSP falcon replaces SEC2's role in the boot sequence for these newer
architectures. This initial stub just defines the falcon type and its
base address.
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
---
drivers/gpu/nova-core/falcon.rs | 1 +
drivers/gpu/nova-core/falcon/fsp.rs | 27 +++++++++++++++++++++++++++
drivers/gpu/nova-core/gsp/boot.rs | 7 +++++--
3 files changed, 33 insertions(+), 2 deletions(-)
create mode 100644 drivers/gpu/nova-core/falcon/fsp.rs
diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs
index 24cc2c26e28d..053ce5bea6cd 100644
--- a/drivers/gpu/nova-core/falcon.rs
+++ b/drivers/gpu/nova-core/falcon.rs
@@ -40,6 +40,7 @@
regs,
};
+pub(crate) mod fsp;
pub(crate) mod gsp;
mod hal;
pub(crate) mod sec2;
diff --git a/drivers/gpu/nova-core/falcon/fsp.rs b/drivers/gpu/nova-core/falcon/fsp.rs
new file mode 100644
index 000000000000..57817a594631
--- /dev/null
+++ b/drivers/gpu/nova-core/falcon/fsp.rs
@@ -0,0 +1,27 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! FSP (Firmware System Processor) falcon engine for Hopper/Blackwell GPUs.
+//!
+//! The FSP falcon handles secure boot and Chain of Trust operations
+//! on Hopper and Blackwell architectures, replacing SEC2's role.
+
+use kernel::io::register::RegisterBase;
+
+use crate::falcon::{
+ FalconEngine,
+ PFalcon2Base,
+ PFalconBase, //
+};
+
+/// Type specifying the `Fsp` falcon engine. Cannot be instantiated.
+pub(crate) struct Fsp(());
+
+impl RegisterBase<PFalconBase> for Fsp {
+ const BASE: usize = 0x8f2000;
+}
+
+impl RegisterBase<PFalcon2Base> for Fsp {
+ const BASE: usize = 0x8f3000;
+}
+
+impl FalconEngine for Fsp {}
diff --git a/drivers/gpu/nova-core/gsp/boot.rs b/drivers/gpu/nova-core/gsp/boot.rs
index 88c7d2106052..84943beee9ca 100644
--- a/drivers/gpu/nova-core/gsp/boot.rs
+++ b/drivers/gpu/nova-core/gsp/boot.rs
@@ -13,6 +13,7 @@
use crate::{
driver::Bar0,
falcon::{
+ fsp::Fsp as FspEngine,
gsp::Gsp,
sec2::Sec2,
Falcon, //
@@ -196,13 +197,15 @@ fn boot_via_sec2(
/// 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_via_fsp(
- _dev: &device::Device<device::Bound>,
+ dev: &device::Device<device::Bound>,
_bar: &Bar0,
- _chipset: Chipset,
+ chipset: Chipset,
_gsp_falcon: &Falcon<Gsp>,
_wpr_meta: &Coherent<GspFwWprMeta>,
_libos: &Coherent<[LibosMemoryRegionInitArgument]>,
) -> Result {
+ let _fsp_falcon = Falcon::<FspEngine>::new(dev, chipset)?;
+
Err(ENOTSUPP)
}
--
2.53.0
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [PATCH v10 16/28] gpu: nova-core: Hopper/Blackwell: add FMC firmware image, in support of FSP
2026-04-11 2:49 [PATCH v10 00/28] gpu: nova-core: firmware: Hopper/Blackwell support John Hubbard
` (14 preceding siblings ...)
2026-04-11 2:49 ` [PATCH v10 15/28] gpu: nova-core: Hopper/Blackwell: add FSP falcon engine stub John Hubbard
@ 2026-04-11 2:49 ` John Hubbard
2026-04-11 2:49 ` [PATCH v10 17/28] gpu: nova-core: Hopper/Blackwell: add FSP secure boot completion waiting John Hubbard
` (11 subsequent siblings)
27 siblings, 0 replies; 30+ messages in thread
From: John Hubbard @ 2026-04-11 2:49 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot
Cc: Joel Fernandes, Timur Tabi, Alistair Popple, Eliot Courtney,
Shashank Sharma, Zhi Wang, David Airlie, Simona Vetter,
Bjorn Helgaas, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, rust-for-linux, LKML, John Hubbard
FSP is the Falcon that runs FMC firmware on Hopper and Blackwell.
Load the FMC ELF in two forms: the image section that FSP boots from,
and the full Firmware object for later signature extraction during
Chain of Trust verification.
Co-developed-by: Alexandre Courbot <acourbot@nvidia.com>
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
---
drivers/gpu/nova-core/firmware.rs | 1 +
drivers/gpu/nova-core/firmware/fsp.rs | 45 +++++++++++++++++++++++++++
drivers/gpu/nova-core/gsp/boot.rs | 3 ++
3 files changed, 49 insertions(+)
create mode 100644 drivers/gpu/nova-core/firmware/fsp.rs
diff --git a/drivers/gpu/nova-core/firmware.rs b/drivers/gpu/nova-core/firmware.rs
index bc217bfc225f..bc26807116e4 100644
--- a/drivers/gpu/nova-core/firmware.rs
+++ b/drivers/gpu/nova-core/firmware.rs
@@ -27,6 +27,7 @@
};
pub(crate) mod booter;
+pub(crate) mod fsp;
pub(crate) mod fwsec;
pub(crate) mod gsp;
pub(crate) mod riscv;
diff --git a/drivers/gpu/nova-core/firmware/fsp.rs b/drivers/gpu/nova-core/firmware/fsp.rs
new file mode 100644
index 000000000000..3968bacb7e61
--- /dev/null
+++ b/drivers/gpu/nova-core/firmware/fsp.rs
@@ -0,0 +1,45 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! FSP is a hardware unit that runs FMC firmware.
+
+use kernel::{
+ device,
+ dma::Coherent,
+ firmware::Firmware,
+ prelude::*, //
+};
+
+use crate::{
+ firmware::elf,
+ gpu::Chipset, //
+};
+
+#[expect(dead_code)]
+pub(crate) struct FspFirmware {
+ /// FMC firmware image data (only the "image" ELF section).
+ pub(crate) fmc_image: Coherent<[u8]>,
+ /// Full FMC ELF for signature extraction.
+ pub(crate) fmc_elf: Firmware,
+}
+
+impl FspFirmware {
+ pub(crate) fn new(
+ dev: &device::Device<device::Bound>,
+ chipset: Chipset,
+ ver: &str,
+ ) -> Result<Self> {
+ let fw = super::request_firmware(dev, chipset, "fmc", ver)?;
+
+ // FSP expects only the "image" section, not the entire ELF file.
+ let fmc_image_data = elf::elf_section(fw.data(), "image").ok_or_else(|| {
+ dev_err!(dev, "FMC ELF file missing 'image' section\n");
+ EINVAL
+ })?;
+ let fmc_image = Coherent::from_slice(dev, fmc_image_data, GFP_KERNEL)?;
+
+ Ok(Self {
+ fmc_image,
+ fmc_elf: fw,
+ })
+ }
+}
diff --git a/drivers/gpu/nova-core/gsp/boot.rs b/drivers/gpu/nova-core/gsp/boot.rs
index 84943beee9ca..1998bd230185 100644
--- a/drivers/gpu/nova-core/gsp/boot.rs
+++ b/drivers/gpu/nova-core/gsp/boot.rs
@@ -24,6 +24,7 @@
BooterFirmware,
BooterKind, //
},
+ fsp::FspFirmware,
fwsec::{
bootloader::FwsecFirmwareWithBl,
FwsecCommand,
@@ -206,6 +207,8 @@ fn boot_via_fsp(
) -> Result {
let _fsp_falcon = Falcon::<FspEngine>::new(dev, chipset)?;
+ let _fsp_fw = FspFirmware::new(dev, chipset, FIRMWARE_VERSION)?;
+
Err(ENOTSUPP)
}
--
2.53.0
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [PATCH v10 17/28] gpu: nova-core: Hopper/Blackwell: add FSP secure boot completion waiting
2026-04-11 2:49 [PATCH v10 00/28] gpu: nova-core: firmware: Hopper/Blackwell support John Hubbard
` (15 preceding siblings ...)
2026-04-11 2:49 ` [PATCH v10 16/28] gpu: nova-core: Hopper/Blackwell: add FMC firmware image, in support of FSP John Hubbard
@ 2026-04-11 2:49 ` John Hubbard
2026-04-11 2:49 ` [PATCH v10 18/28] gpu: nova-core: Hopper/Blackwell: add FMC signature extraction John Hubbard
` (10 subsequent siblings)
27 siblings, 0 replies; 30+ messages in thread
From: John Hubbard @ 2026-04-11 2:49 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot
Cc: Joel Fernandes, Timur Tabi, Alistair Popple, Eliot Courtney,
Shashank Sharma, Zhi Wang, David Airlie, Simona Vetter,
Bjorn Helgaas, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, rust-for-linux, LKML, John Hubbard
Add the FSP module with Fsp::wait_secure_boot(), which polls the I2CS
thermal scratch register until FSP signals boot success or the 5-second
timeout expires. Hopper and Blackwell use FSP instead of SEC2 for
secure boot.
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
---
drivers/gpu/nova-core/fsp.rs | 53 ++++++++++++++++++++++++++++++
drivers/gpu/nova-core/gsp/boot.rs | 5 ++-
drivers/gpu/nova-core/nova_core.rs | 1 +
drivers/gpu/nova-core/regs.rs | 29 ++++++++++++++++
4 files changed, 87 insertions(+), 1 deletion(-)
create mode 100644 drivers/gpu/nova-core/fsp.rs
diff --git a/drivers/gpu/nova-core/fsp.rs b/drivers/gpu/nova-core/fsp.rs
new file mode 100644
index 000000000000..55e543e80de8
--- /dev/null
+++ b/drivers/gpu/nova-core/fsp.rs
@@ -0,0 +1,53 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! FSP (Firmware System Processor) interface for Hopper/Blackwell GPUs.
+//!
+//! Hopper/Blackwell use a simplified firmware boot sequence: FMC --> FSP --> GSP.
+//! Unlike Turing/Ampere/Ada, there is NO SEC2 (Security Engine 2) usage.
+//! FSP handles secure boot directly using FMC firmware + Chain of Trust.
+
+use kernel::{
+ device,
+ io::poll::read_poll_timeout,
+ prelude::*,
+ time::Delta, //
+};
+
+use crate::regs;
+
+/// FSP secure boot completion timeout in milliseconds.
+const FSP_SECURE_BOOT_TIMEOUT_MS: i64 = 5000;
+
+/// FSP interface for Hopper/Blackwell GPUs.
+pub(crate) struct Fsp;
+
+impl Fsp {
+ /// Wait for FSP secure boot completion.
+ ///
+ /// Polls the thermal scratch register until FSP signals boot completion
+ /// or timeout occurs.
+ pub(crate) fn wait_secure_boot(
+ dev: &device::Device<device::Bound>,
+ bar: &crate::driver::Bar0,
+ arch: crate::gpu::Architecture,
+ ) -> Result {
+ debug_assert!(
+ regs::read_fsp_boot_complete_status(bar, arch).is_some(),
+ "wait_secure_boot called on non-FSP architecture"
+ );
+
+ let timeout = Delta::from_millis(FSP_SECURE_BOOT_TIMEOUT_MS);
+
+ read_poll_timeout(
+ || regs::read_fsp_boot_complete_status(bar, arch).ok_or(ENOTSUPP),
+ |&status| status == regs::FSP_BOOT_COMPLETE_SUCCESS,
+ Delta::from_millis(10),
+ timeout,
+ )
+ .map_err(|_| {
+ dev_err!(dev, "FSP secure boot completion timeout\n");
+ ETIMEDOUT
+ })
+ .map(|_| ())
+ }
+}
diff --git a/drivers/gpu/nova-core/gsp/boot.rs b/drivers/gpu/nova-core/gsp/boot.rs
index 1998bd230185..9609cef3ff51 100644
--- a/drivers/gpu/nova-core/gsp/boot.rs
+++ b/drivers/gpu/nova-core/gsp/boot.rs
@@ -33,6 +33,7 @@
gsp::GspFirmware,
FIRMWARE_VERSION, //
},
+ fsp::Fsp,
gpu::{
Architecture,
Chipset, //
@@ -199,7 +200,7 @@ fn boot_via_sec2(
/// the GSP boot internally - no manual GSP reset/boot is needed.
fn boot_via_fsp(
dev: &device::Device<device::Bound>,
- _bar: &Bar0,
+ bar: &Bar0,
chipset: Chipset,
_gsp_falcon: &Falcon<Gsp>,
_wpr_meta: &Coherent<GspFwWprMeta>,
@@ -209,6 +210,8 @@ fn boot_via_fsp(
let _fsp_fw = FspFirmware::new(dev, chipset, FIRMWARE_VERSION)?;
+ Fsp::wait_secure_boot(dev, bar, chipset.arch())?;
+
Err(ENOTSUPP)
}
diff --git a/drivers/gpu/nova-core/nova_core.rs b/drivers/gpu/nova-core/nova_core.rs
index 3a609f6937e4..53558ac0f619 100644
--- a/drivers/gpu/nova-core/nova_core.rs
+++ b/drivers/gpu/nova-core/nova_core.rs
@@ -17,6 +17,7 @@
mod falcon;
mod fb;
mod firmware;
+mod fsp;
mod gpu;
mod gsp;
#[macro_use]
diff --git a/drivers/gpu/nova-core/regs.rs b/drivers/gpu/nova-core/regs.rs
index 6faeed73901d..e4de7bfffde1 100644
--- a/drivers/gpu/nova-core/regs.rs
+++ b/drivers/gpu/nova-core/regs.rs
@@ -511,6 +511,35 @@ pub(crate) fn mem_scrubbing_done(self) -> bool {
}
}
+// PTHERM registers
+
+// FSP secure boot completion status register used by FSP to signal boot completion.
+// This is the NV_THERM_I2CS_SCRATCH register.
+// Different architectures use different addresses:
+// - Hopper (GH100) and Blackwell GB10x: 0x000200bc
+// - Blackwell GB20x: 0x00ad00bc
+pub(crate) fn fsp_thermal_scratch_reg_addr(arch: Architecture) -> Result<usize> {
+ match arch {
+ Architecture::Hopper | Architecture::BlackwellGB10x => Ok(0x000200bc),
+ Architecture::BlackwellGB20x => Ok(0x00ad00bc),
+ _ => Err(kernel::error::code::ENOTSUPP),
+ }
+}
+
+/// FSP writes this value to indicate successful boot completion.
+pub(crate) const FSP_BOOT_COMPLETE_SUCCESS: u32 = 0xff;
+
+/// Read FSP boot completion status from the architecture-specific thermal scratch register.
+///
+/// Returns `None` if the architecture does not have an FSP.
+pub(crate) fn read_fsp_boot_complete_status(
+ bar: &crate::driver::Bar0,
+ arch: Architecture,
+) -> Option<u32> {
+ let addr = fsp_thermal_scratch_reg_addr(arch).ok()?;
+ Some(bar.read32(addr))
+}
+
// The modules below provide registers that are not identical on all supported chips. They should
// only be used in HAL modules.
--
2.53.0
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [PATCH v10 18/28] gpu: nova-core: Hopper/Blackwell: add FMC signature extraction
2026-04-11 2:49 [PATCH v10 00/28] gpu: nova-core: firmware: Hopper/Blackwell support John Hubbard
` (16 preceding siblings ...)
2026-04-11 2:49 ` [PATCH v10 17/28] gpu: nova-core: Hopper/Blackwell: add FSP secure boot completion waiting John Hubbard
@ 2026-04-11 2:49 ` John Hubbard
2026-04-11 2:49 ` [PATCH v10 19/28] gpu: nova-core: Hopper/Blackwell: add FSP falcon EMEM operations John Hubbard
` (9 subsequent siblings)
27 siblings, 0 replies; 30+ messages in thread
From: John Hubbard @ 2026-04-11 2:49 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot
Cc: Joel Fernandes, Timur Tabi, Alistair Popple, Eliot Courtney,
Shashank Sharma, Zhi Wang, David Airlie, Simona Vetter,
Bjorn Helgaas, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, rust-for-linux, LKML, John Hubbard
Add extract_fmc_signatures() which extracts SHA-384 hash, RSA public
key, and RSA signature from FMC ELF32 firmware sections. These are
needed for FSP Chain of Trust verification.
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
---
drivers/gpu/nova-core/firmware.rs | 3 +-
drivers/gpu/nova-core/fsp.rs | 78 +++++++++++++++++++++++++++++++
drivers/gpu/nova-core/gsp/boot.rs | 3 +-
3 files changed, 82 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/nova-core/firmware.rs b/drivers/gpu/nova-core/firmware.rs
index bc26807116e4..6d07715b3a49 100644
--- a/drivers/gpu/nova-core/firmware.rs
+++ b/drivers/gpu/nova-core/firmware.rs
@@ -26,6 +26,7 @@
},
};
+pub(crate) use elf::elf_section;
pub(crate) mod booter;
pub(crate) mod fsp;
pub(crate) mod fwsec;
@@ -646,7 +647,7 @@ fn elf32_section<'a>(elf: &'a [u8], name: &str) -> Option<&'a [u8]> {
}
/// Automatically detects ELF32 vs ELF64 based on the ELF header.
- pub(super) fn elf_section<'a>(elf: &'a [u8], name: &str) -> Option<&'a [u8]> {
+ pub(crate) fn elf_section<'a>(elf: &'a [u8], name: &str) -> Option<&'a [u8]> {
// Check ELF magic.
if elf.len() < 5 || elf.get(0..4)? != b"\x7fELF" {
return None;
diff --git a/drivers/gpu/nova-core/fsp.rs b/drivers/gpu/nova-core/fsp.rs
index 55e543e80de8..8287bda795ca 100644
--- a/drivers/gpu/nova-core/fsp.rs
+++ b/drivers/gpu/nova-core/fsp.rs
@@ -18,6 +18,18 @@
/// FSP secure boot completion timeout in milliseconds.
const FSP_SECURE_BOOT_TIMEOUT_MS: i64 = 5000;
+/// Size constraints for FSP security signatures (Hopper/Blackwell).
+const FSP_HASH_SIZE: usize = 48; // SHA-384 hash
+const FSP_PKEY_SIZE: usize = 384; // RSA-3072 public key
+const FSP_SIG_SIZE: usize = 384; // RSA-3072 signature
+
+/// Structure to hold FMC signatures.
+#[derive(Debug, Clone, Copy)]
+pub(crate) struct FmcSignatures {
+ hash384: [u8; FSP_HASH_SIZE],
+ public_key: [u8; FSP_PKEY_SIZE],
+ signature: [u8; FSP_SIG_SIZE],
+}
/// FSP interface for Hopper/Blackwell GPUs.
pub(crate) struct Fsp;
@@ -50,4 +62,70 @@ pub(crate) fn wait_secure_boot(
})
.map(|_| ())
}
+
+ /// Extract FMC firmware signatures for Chain of Trust verification.
+ ///
+ /// Extracts real cryptographic signatures from FMC ELF32 firmware sections.
+ /// Returns signatures in a heap-allocated structure to prevent stack overflow.
+ pub(crate) fn extract_fmc_signatures(
+ dev: &device::Device<device::Bound>,
+ fmc_fw_data: &[u8],
+ ) -> Result<KBox<FmcSignatures>> {
+ let hash_section = crate::firmware::elf_section(fmc_fw_data, "hash")
+ .ok_or(EINVAL)
+ .inspect_err(|_| dev_err!(dev, "FMC firmware missing 'hash' section\n"))?;
+
+ let pkey_section = crate::firmware::elf_section(fmc_fw_data, "publickey")
+ .ok_or(EINVAL)
+ .inspect_err(|_| dev_err!(dev, "FMC firmware missing 'publickey' section\n"))?;
+
+ let sig_section = crate::firmware::elf_section(fmc_fw_data, "signature")
+ .ok_or(EINVAL)
+ .inspect_err(|_| dev_err!(dev, "FMC firmware missing 'signature' section\n"))?;
+
+ if hash_section.len() != FSP_HASH_SIZE {
+ dev_err!(
+ dev,
+ "FMC hash section size {} != expected {}\n",
+ hash_section.len(),
+ FSP_HASH_SIZE
+ );
+ return Err(EINVAL);
+ }
+
+ if pkey_section.len() > FSP_PKEY_SIZE {
+ dev_err!(
+ dev,
+ "FMC publickey section size {} > maximum {}\n",
+ pkey_section.len(),
+ FSP_PKEY_SIZE
+ );
+ return Err(EINVAL);
+ }
+
+ if sig_section.len() > FSP_SIG_SIZE {
+ dev_err!(
+ dev,
+ "FMC signature section size {} > maximum {}\n",
+ sig_section.len(),
+ FSP_SIG_SIZE
+ );
+ return Err(EINVAL);
+ }
+
+ let mut signatures = KBox::new(
+ FmcSignatures {
+ hash384: [0u8; FSP_HASH_SIZE],
+ public_key: [0u8; FSP_PKEY_SIZE],
+ signature: [0u8; FSP_SIG_SIZE],
+ },
+ GFP_KERNEL,
+ )?;
+
+ signatures.hash384.copy_from_slice(hash_section);
+ signatures.public_key[..pkey_section.len()].copy_from_slice(pkey_section);
+ signatures.signature[..sig_section.len()].copy_from_slice(sig_section);
+
+ Ok(signatures)
+ }
}
diff --git a/drivers/gpu/nova-core/gsp/boot.rs b/drivers/gpu/nova-core/gsp/boot.rs
index 9609cef3ff51..739624af1cef 100644
--- a/drivers/gpu/nova-core/gsp/boot.rs
+++ b/drivers/gpu/nova-core/gsp/boot.rs
@@ -208,7 +208,8 @@ fn boot_via_fsp(
) -> Result {
let _fsp_falcon = Falcon::<FspEngine>::new(dev, chipset)?;
- let _fsp_fw = FspFirmware::new(dev, chipset, FIRMWARE_VERSION)?;
+ let fsp_fw = FspFirmware::new(dev, chipset, FIRMWARE_VERSION)?;
+ let _signatures = Fsp::extract_fmc_signatures(dev, fsp_fw.fmc_elf.data())?;
Fsp::wait_secure_boot(dev, bar, chipset.arch())?;
--
2.53.0
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [PATCH v10 19/28] gpu: nova-core: Hopper/Blackwell: add FSP falcon EMEM operations
2026-04-11 2:49 [PATCH v10 00/28] gpu: nova-core: firmware: Hopper/Blackwell support John Hubbard
` (17 preceding siblings ...)
2026-04-11 2:49 ` [PATCH v10 18/28] gpu: nova-core: Hopper/Blackwell: add FMC signature extraction John Hubbard
@ 2026-04-11 2:49 ` John Hubbard
2026-04-11 2:49 ` [PATCH v10 20/28] gpu: nova-core: Hopper/Blackwell: add FSP message infrastructure John Hubbard
` (8 subsequent siblings)
27 siblings, 0 replies; 30+ messages in thread
From: John Hubbard @ 2026-04-11 2:49 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot
Cc: Joel Fernandes, Timur Tabi, Alistair Popple, Eliot Courtney,
Shashank Sharma, Zhi Wang, David Airlie, Simona Vetter,
Bjorn Helgaas, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, rust-for-linux, LKML, John Hubbard
Add external memory (EMEM) read/write operations to the GPU's FSP falcon
engine. These operations use Falcon PIO (Programmed I/O) to communicate
with the FSP through indirect memory access.
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
---
drivers/gpu/nova-core/falcon/fsp.rs | 149 +++++++++++++++++++++++++++-
drivers/gpu/nova-core/regs.rs | 16 +++
2 files changed, 160 insertions(+), 5 deletions(-)
diff --git a/drivers/gpu/nova-core/falcon/fsp.rs b/drivers/gpu/nova-core/falcon/fsp.rs
index 57817a594631..27344da6680c 100644
--- a/drivers/gpu/nova-core/falcon/fsp.rs
+++ b/drivers/gpu/nova-core/falcon/fsp.rs
@@ -5,12 +5,29 @@
//! The FSP falcon handles secure boot and Chain of Trust operations
//! on Hopper and Blackwell architectures, replacing SEC2's role.
-use kernel::io::register::RegisterBase;
+use kernel::{
+ io::{
+ register::{
+ RegisterBase,
+ WithBase, //
+ },
+ Io,
+ IoCapable, //
+ },
+ num::Bounded,
+ prelude::*,
+ ptr::Alignment, //
+};
-use crate::falcon::{
- FalconEngine,
- PFalcon2Base,
- PFalconBase, //
+use crate::{
+ driver::Bar0,
+ falcon::{
+ Falcon,
+ FalconEngine,
+ PFalcon2Base,
+ PFalconBase, //
+ },
+ regs,
};
/// Type specifying the `Fsp` falcon engine. Cannot be instantiated.
@@ -25,3 +42,125 @@ impl RegisterBase<PFalcon2Base> for Fsp {
}
impl FalconEngine for Fsp {}
+
+/// Maximum addressable EMEM size, derived from the 24-bit offset field
+/// in NV_PFALCON_FALCON_EMEM_CTL.
+const EMEM_MAX_SIZE: Alignment = Alignment::new::<{ 1 << 24 }>();
+
+/// I/O backend for the FSP falcon's external memory (EMEM).
+///
+/// Each 32-bit access programs a byte offset via the EMEM_CTL register,
+/// then reads or writes through the EMEM_DATA register.
+struct Emem<'a> {
+ bar: &'a Bar0,
+}
+
+impl<'a> Emem<'a> {
+ fn new(bar: &'a Bar0) -> Self {
+ Self { bar }
+ }
+}
+
+impl IoCapable<u32> for Emem<'_> {
+ unsafe fn io_read(&self, address: usize) -> u32 {
+ // PANIC: Per the `io_read` SAFETY comment, `address` is within the I/O bounds of `Self` and
+ // thus less than `EMEM_MAX_SIZE`, meaning the `else` block is never taken.
+ let Some(offset) =
+ Bounded::<usize, { EMEM_MAX_SIZE.log2() }>::try_new(address).map(Bounded::cast::<u32>)
+ else {
+ unreachable!()
+ };
+
+ self.bar.write(
+ WithBase::of::<Fsp>(),
+ regs::NV_PFALCON_FALCON_EMEM_CTL::zeroed()
+ .with_rd_mode(true)
+ .with_offset(offset),
+ );
+
+ self.bar
+ .read(regs::NV_PFALCON_FALCON_EMEM_DATA::of::<Fsp>())
+ .data()
+ }
+
+ unsafe fn io_write(&self, value: u32, address: usize) {
+ // PANIC: Per the `io_write` SAFETY comment, `address` is within the I/O bounds of `Self` and
+ // thus less than `EMEM_MAX_SIZE`, meaning the `else` block is never taken.
+ let Some(offset) =
+ Bounded::<usize, { EMEM_MAX_SIZE.log2() }>::try_new(address).map(Bounded::cast::<u32>)
+ else {
+ unreachable!()
+ };
+
+ self.bar.write(
+ WithBase::of::<Fsp>(),
+ regs::NV_PFALCON_FALCON_EMEM_CTL::zeroed()
+ .with_wr_mode(true)
+ .with_offset(offset),
+ );
+
+ self.bar.write(
+ WithBase::of::<Fsp>(),
+ regs::NV_PFALCON_FALCON_EMEM_DATA::zeroed().with_data(value),
+ );
+ }
+}
+
+impl Io for Emem<'_> {
+ fn addr(&self) -> usize {
+ 0
+ }
+
+ fn maxsize(&self) -> usize {
+ EMEM_MAX_SIZE.as_usize()
+ }
+}
+
+impl Falcon<Fsp> {
+ /// Returns an EMEM I/O accessor for this FSP falcon.
+ fn emem<'a>(&self, bar: &'a Bar0) -> Emem<'a> {
+ Emem::new(bar)
+ }
+
+ /// Writes `data` to FSP external memory at byte `offset`.
+ ///
+ /// Data is interpreted as little-endian 32-bit words.
+ /// Returns `EINVAL` if offset or data length is not 4-byte aligned.
+ #[expect(dead_code)]
+ fn write_emem(&self, bar: &Bar0, offset: u32, data: &[u8]) -> Result {
+ if offset % 4 != 0 || data.len() % 4 != 0 {
+ return Err(EINVAL);
+ }
+
+ let emem = self.emem(bar);
+ let mut off = offset as usize;
+ for chunk in data.chunks_exact(4) {
+ let word = u32::from_le_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]);
+ emem.try_write32(word, off)?;
+ off += 4;
+ }
+
+ Ok(())
+ }
+
+ /// Reads FSP external memory at byte `offset` into `data`.
+ ///
+ /// Data is stored as little-endian 32-bit words.
+ /// Returns `EINVAL` if offset or data length is not 4-byte aligned.
+ #[expect(dead_code)]
+ fn read_emem(&self, bar: &Bar0, offset: u32, data: &mut [u8]) -> Result {
+ if offset % 4 != 0 || data.len() % 4 != 0 {
+ return Err(EINVAL);
+ }
+
+ let emem = self.emem(bar);
+ let mut off = offset as usize;
+ for chunk in data.chunks_exact_mut(4) {
+ let word = emem.try_read32(off)?;
+ chunk.copy_from_slice(&word.to_le_bytes());
+ off += 4;
+ }
+
+ Ok(())
+ }
+}
diff --git a/drivers/gpu/nova-core/regs.rs b/drivers/gpu/nova-core/regs.rs
index e4de7bfffde1..5ba6c2aedd7e 100644
--- a/drivers/gpu/nova-core/regs.rs
+++ b/drivers/gpu/nova-core/regs.rs
@@ -425,6 +425,22 @@ pub(crate) fn vga_workspace_addr(self) -> Option<u64> {
pub(crate) NV_PFALCON_FBIF_CTL(u32) @ PFalconBase + 0x00000624 {
7:7 allow_phys_no_ctx => bool;
}
+
+ // Falcon EMEM PIO registers (used by FSP on Hopper/Blackwell).
+ // These provide the falcon external memory communication interface.
+ pub(crate) NV_PFALCON_FALCON_EMEM_CTL(u32) @ PFalconBase + 0x00000ac0 {
+ /// EMEM byte offset (must be 4-byte aligned)
+ 23:0 offset;
+ /// Write mode
+ 24:24 wr_mode => bool;
+ /// Read mode
+ 25:25 rd_mode => bool;
+ }
+
+ pub(crate) NV_PFALCON_FALCON_EMEM_DATA(u32) @ PFalconBase + 0x00000ac4 {
+ /// EMEM data register
+ 31:0 data => u32;
+ }
}
impl NV_PFALCON_FALCON_DMACTL {
--
2.53.0
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [PATCH v10 20/28] gpu: nova-core: Hopper/Blackwell: add FSP message infrastructure
2026-04-11 2:49 [PATCH v10 00/28] gpu: nova-core: firmware: Hopper/Blackwell support John Hubbard
` (18 preceding siblings ...)
2026-04-11 2:49 ` [PATCH v10 19/28] gpu: nova-core: Hopper/Blackwell: add FSP falcon EMEM operations John Hubbard
@ 2026-04-11 2:49 ` John Hubbard
2026-04-11 2:49 ` [PATCH v10 21/28] gpu: nova-core: add MCTP/NVDM protocol types for firmware communication John Hubbard
` (7 subsequent siblings)
27 siblings, 0 replies; 30+ messages in thread
From: John Hubbard @ 2026-04-11 2:49 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot
Cc: Joel Fernandes, Timur Tabi, Alistair Popple, Eliot Courtney,
Shashank Sharma, Zhi Wang, David Airlie, Simona Vetter,
Bjorn Helgaas, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, rust-for-linux, LKML, John Hubbard
Add the FSP messaging infrastructure needed for Chain of Trust
communication on Hopper/Blackwell GPUs.
Reviewed-by: Joel Fernandes <joelagnelf@nvidia.com>
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
Acked-by: Danilo Krummrich <dakr@kernel.org>
---
drivers/gpu/nova-core/falcon/fsp.rs | 75 ++++++++++++++++++++++++++++-
drivers/gpu/nova-core/regs.rs | 21 ++++++++
2 files changed, 94 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/nova-core/falcon/fsp.rs b/drivers/gpu/nova-core/falcon/fsp.rs
index 27344da6680c..c2f509880c8e 100644
--- a/drivers/gpu/nova-core/falcon/fsp.rs
+++ b/drivers/gpu/nova-core/falcon/fsp.rs
@@ -126,7 +126,6 @@ fn emem<'a>(&self, bar: &'a Bar0) -> Emem<'a> {
///
/// Data is interpreted as little-endian 32-bit words.
/// Returns `EINVAL` if offset or data length is not 4-byte aligned.
- #[expect(dead_code)]
fn write_emem(&self, bar: &Bar0, offset: u32, data: &[u8]) -> Result {
if offset % 4 != 0 || data.len() % 4 != 0 {
return Err(EINVAL);
@@ -147,7 +146,6 @@ fn write_emem(&self, bar: &Bar0, offset: u32, data: &[u8]) -> Result {
///
/// Data is stored as little-endian 32-bit words.
/// Returns `EINVAL` if offset or data length is not 4-byte aligned.
- #[expect(dead_code)]
fn read_emem(&self, bar: &Bar0, offset: u32, data: &mut [u8]) -> Result {
if offset % 4 != 0 || data.len() % 4 != 0 {
return Err(EINVAL);
@@ -163,4 +161,77 @@ fn read_emem(&self, bar: &Bar0, offset: u32, data: &mut [u8]) -> Result {
Ok(())
}
+
+ /// Poll FSP for incoming data.
+ ///
+ /// Returns the size of available data in bytes, or 0 if no data is available.
+ ///
+ /// The FSP message queue is not circular - pointers are reset to 0 after each
+ /// message exchange, so `tail >= head` is always true when data is present.
+ #[expect(unused)]
+ pub(crate) fn poll_msgq(&self, bar: &Bar0) -> u32 {
+ let head = bar.read(regs::NV_PFSP_MSGQ_HEAD).address();
+ let tail = bar.read(regs::NV_PFSP_MSGQ_TAIL).address();
+
+ if head == tail {
+ return 0;
+ }
+
+ // TAIL points at last DWORD written, so add 4 to get total size
+ tail.saturating_sub(head) + 4
+ }
+
+ /// Send message to FSP.
+ ///
+ /// Writes a message to FSP EMEM and updates queue pointers to notify FSP.
+ ///
+ /// # Arguments
+ /// * `bar` - BAR0 memory mapping
+ /// * `packet` - Message data (must be 4-byte aligned in length)
+ ///
+ /// # Returns
+ /// `Ok(())` on success, `Err(EINVAL)` if packet is empty or not 4-byte aligned
+ #[expect(unused)]
+ pub(crate) fn send_msg(&self, bar: &Bar0, packet: &[u8]) -> Result {
+ if packet.is_empty() {
+ return Err(EINVAL);
+ }
+
+ // Write message to EMEM at offset 0 (validates 4-byte alignment)
+ self.write_emem(bar, 0, packet)?;
+
+ // Update queue pointers - TAIL points at last DWORD written
+ let tail_offset = u32::try_from(packet.len() - 4).map_err(|_| EINVAL)?;
+ bar.write_reg(regs::NV_PFSP_QUEUE_TAIL::zeroed().with_address(tail_offset));
+ bar.write_reg(regs::NV_PFSP_QUEUE_HEAD::zeroed().with_address(0));
+
+ Ok(())
+ }
+
+ /// Receive message from FSP.
+ ///
+ /// Reads a message from FSP EMEM and resets queue pointers.
+ ///
+ /// # Arguments
+ /// * `bar` - BAR0 memory mapping
+ /// * `buffer` - Buffer to receive message data
+ /// * `size` - Size of message to read in bytes (from `poll_msgq`)
+ ///
+ /// # Returns
+ /// `Ok(bytes_read)` on success, `Err(EINVAL)` if size is 0, exceeds buffer, or not aligned
+ #[expect(unused)]
+ pub(crate) fn recv_msg(&self, bar: &Bar0, buffer: &mut [u8], size: usize) -> Result<usize> {
+ if size == 0 || size > buffer.len() {
+ return Err(EINVAL);
+ }
+
+ // Read response from EMEM at offset 0 (validates 4-byte alignment)
+ self.read_emem(bar, 0, &mut buffer[..size])?;
+
+ // Reset message queue pointers after reading
+ bar.write_reg(regs::NV_PFSP_MSGQ_TAIL::zeroed().with_address(0));
+ bar.write_reg(regs::NV_PFSP_MSGQ_HEAD::zeroed().with_address(0));
+
+ Ok(size)
+ }
}
diff --git a/drivers/gpu/nova-core/regs.rs b/drivers/gpu/nova-core/regs.rs
index 5ba6c2aedd7e..c3ccae0c235f 100644
--- a/drivers/gpu/nova-core/regs.rs
+++ b/drivers/gpu/nova-core/regs.rs
@@ -527,6 +527,27 @@ pub(crate) fn mem_scrubbing_done(self) -> bool {
}
}
+// FSP (Firmware System Processor) queue registers for Hopper/Blackwell Chain of Trust
+// These registers manage falcon EMEM communication queues
+
+register! {
+ pub(crate) NV_PFSP_QUEUE_HEAD(u32) @ 0x008f2c00 {
+ 31:0 address => u32;
+ }
+
+ pub(crate) NV_PFSP_QUEUE_TAIL(u32) @ 0x008f2c04 {
+ 31:0 address => u32;
+ }
+
+ pub(crate) NV_PFSP_MSGQ_HEAD(u32) @ 0x008f2c80 {
+ 31:0 address => u32;
+ }
+
+ pub(crate) NV_PFSP_MSGQ_TAIL(u32) @ 0x008f2c84 {
+ 31:0 address => u32;
+ }
+}
+
// PTHERM registers
// FSP secure boot completion status register used by FSP to signal boot completion.
--
2.53.0
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [PATCH v10 21/28] gpu: nova-core: add MCTP/NVDM protocol types for firmware communication
2026-04-11 2:49 [PATCH v10 00/28] gpu: nova-core: firmware: Hopper/Blackwell support John Hubbard
` (19 preceding siblings ...)
2026-04-11 2:49 ` [PATCH v10 20/28] gpu: nova-core: Hopper/Blackwell: add FSP message infrastructure John Hubbard
@ 2026-04-11 2:49 ` John Hubbard
2026-04-11 2:49 ` [PATCH v10 22/28] gpu: nova-core: Hopper/Blackwell: add FSP send/receive messaging John Hubbard
` (6 subsequent siblings)
27 siblings, 0 replies; 30+ messages in thread
From: John Hubbard @ 2026-04-11 2:49 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot
Cc: Joel Fernandes, Timur Tabi, Alistair Popple, Eliot Courtney,
Shashank Sharma, Zhi Wang, David Airlie, Simona Vetter,
Bjorn Helgaas, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, rust-for-linux, LKML, John Hubbard
Add the MCTP (Management Component Transport Protocol) and NVDM (NVIDIA
Device Management) wire-format types used for communication between the
kernel driver and GPU firmware processors.
This includes typed MCTP transport headers, NVDM message headers, and
NVDM message type identifiers. Both the FSP boot path and the upcoming
GSP RPC message queue share this protocol layer.
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
---
drivers/gpu/nova-core/mctp.rs | 121 +++++++++++++++++++++++++++++
drivers/gpu/nova-core/nova_core.rs | 1 +
2 files changed, 122 insertions(+)
create mode 100644 drivers/gpu/nova-core/mctp.rs
diff --git a/drivers/gpu/nova-core/mctp.rs b/drivers/gpu/nova-core/mctp.rs
new file mode 100644
index 000000000000..680ed19d196e
--- /dev/null
+++ b/drivers/gpu/nova-core/mctp.rs
@@ -0,0 +1,121 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! MCTP/NVDM protocol types for NVIDIA GPU firmware communication.
+//!
+//! MCTP (Management Component Transport Protocol) carries NVDM (NVIDIA
+//! Device Management) messages between the kernel driver and GPU firmware
+//! processors such as FSP and GSP.
+
+#![expect(dead_code)]
+
+/// NVDM message type identifiers carried over MCTP.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+#[repr(u8)]
+pub(crate) enum NvdmType {
+ /// Chain of Trust boot message.
+ Cot = 0x14,
+ /// FSP command response.
+ FspResponse = 0x15,
+}
+
+impl TryFrom<u8> for NvdmType {
+ type Error = u8;
+
+ fn try_from(value: u8) -> Result<Self, Self::Error> {
+ match value {
+ x if x == Self::Cot as u8 => Ok(Self::Cot),
+ x if x == Self::FspResponse as u8 => Ok(Self::FspResponse),
+ _ => Err(value),
+ }
+ }
+}
+
+impl From<NvdmType> for u8 {
+ fn from(value: NvdmType) -> Self {
+ value as u8
+ }
+}
+
+bitfield! {
+ pub(crate) struct MctpHeader(u32), "MCTP transport header for NVIDIA firmware messages." {
+ 31:31 som as bool, "Start-of-message bit.";
+ 30:30 eom as bool, "End-of-message bit.";
+ 29:28 seq as u8, "Packet sequence number.";
+ 23:16 seid as u8, "Source endpoint ID.";
+ }
+}
+
+impl MctpHeader {
+ /// Build a single-packet MCTP header (SOM=1, EOM=1, SEQ=0, SEID=0).
+ pub(crate) fn single_packet() -> Self {
+ Self::default().set_som(true).set_eom(true)
+ }
+
+ /// Return the raw packed u32.
+ pub(crate) const fn raw(self) -> u32 {
+ self.0
+ }
+
+ /// Check if this is a complete single-packet message (SOM=1 and EOM=1).
+ pub(crate) fn is_single_packet(self) -> bool {
+ self.som() && self.eom()
+ }
+}
+
+impl From<u32> for MctpHeader {
+ fn from(raw: u32) -> Self {
+ Self(raw)
+ }
+}
+
+/// MCTP message type for PCI vendor-defined messages.
+const MSG_TYPE_VENDOR_PCI: u8 = 0x7e;
+
+/// NVIDIA PCI vendor ID.
+const VENDOR_ID_NV: u16 = 0x10de;
+
+bitfield! {
+ pub(crate) struct NvdmHeader(u32), "NVIDIA Vendor-Defined Message header over MCTP." {
+ 31:24 raw_nvdm_type as u8, "Raw NVDM message type.";
+ 23:8 vendor_id as u16, "PCI vendor ID.";
+ 6:0 msg_type as u8, "MCTP vendor-defined message type.";
+ }
+}
+
+impl NvdmHeader {
+ /// Build an NVDM header for the given message type.
+ pub(crate) fn new(nvdm_type: NvdmType) -> Self {
+ Self::default()
+ .set_msg_type(MSG_TYPE_VENDOR_PCI)
+ .set_vendor_id(VENDOR_ID_NV)
+ .set_nvdm_type(nvdm_type)
+ }
+
+ /// Return the raw packed u32.
+ pub(crate) const fn raw(self) -> u32 {
+ self.0
+ }
+
+ /// Extract the NVDM type field as a typed value.
+ pub(crate) fn nvdm_type(self) -> core::result::Result<NvdmType, u8> {
+ NvdmType::try_from(self.raw_nvdm_type())
+ }
+
+ /// Set the NVDM type field from a typed value.
+ pub(crate) fn set_nvdm_type(self, nvdm_type: NvdmType) -> Self {
+ self.set_raw_nvdm_type(u8::from(nvdm_type))
+ }
+
+ /// Validate this header against the expected NVIDIA NVDM format and type.
+ pub(crate) fn validate(self, expected_type: NvdmType) -> bool {
+ self.msg_type() == MSG_TYPE_VENDOR_PCI
+ && self.vendor_id() == VENDOR_ID_NV
+ && matches!(self.nvdm_type(), Ok(nvdm_type) if nvdm_type == expected_type)
+ }
+}
+
+impl From<u32> for NvdmHeader {
+ fn from(raw: u32) -> Self {
+ Self(raw)
+ }
+}
diff --git a/drivers/gpu/nova-core/nova_core.rs b/drivers/gpu/nova-core/nova_core.rs
index 53558ac0f619..bfa0430b07b9 100644
--- a/drivers/gpu/nova-core/nova_core.rs
+++ b/drivers/gpu/nova-core/nova_core.rs
@@ -20,6 +20,7 @@
mod fsp;
mod gpu;
mod gsp;
+mod mctp;
#[macro_use]
mod num;
mod regs;
--
2.53.0
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [PATCH v10 22/28] gpu: nova-core: Hopper/Blackwell: add FSP send/receive messaging
2026-04-11 2:49 [PATCH v10 00/28] gpu: nova-core: firmware: Hopper/Blackwell support John Hubbard
` (20 preceding siblings ...)
2026-04-11 2:49 ` [PATCH v10 21/28] gpu: nova-core: add MCTP/NVDM protocol types for firmware communication John Hubbard
@ 2026-04-11 2:49 ` John Hubbard
2026-04-11 2:49 ` [PATCH v10 23/28] gpu: nova-core: Hopper/Blackwell: add FspCotVersion type John Hubbard
` (5 subsequent siblings)
27 siblings, 0 replies; 30+ messages in thread
From: John Hubbard @ 2026-04-11 2:49 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot
Cc: Joel Fernandes, Timur Tabi, Alistair Popple, Eliot Courtney,
Shashank Sharma, Zhi Wang, David Airlie, Simona Vetter,
Bjorn Helgaas, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, rust-for-linux, LKML, John Hubbard
Add send_sync_fsp() which sends an MCTP/NVDM message to FSP and waits
for the response. Response validation uses the typed MctpHeader and
NvdmHeader wrappers from the previous commit.
A MessageToFsp trait provides the NVDM type constant for each message
struct, so send_sync_fsp() can verify that the response matches the
request.
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
---
drivers/gpu/nova-core/falcon/fsp.rs | 3 -
drivers/gpu/nova-core/fsp.rs | 123 ++++++++++++++++++++++++++++
2 files changed, 123 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/nova-core/falcon/fsp.rs b/drivers/gpu/nova-core/falcon/fsp.rs
index c2f509880c8e..1e03f386d185 100644
--- a/drivers/gpu/nova-core/falcon/fsp.rs
+++ b/drivers/gpu/nova-core/falcon/fsp.rs
@@ -168,7 +168,6 @@ fn read_emem(&self, bar: &Bar0, offset: u32, data: &mut [u8]) -> Result {
///
/// The FSP message queue is not circular - pointers are reset to 0 after each
/// message exchange, so `tail >= head` is always true when data is present.
- #[expect(unused)]
pub(crate) fn poll_msgq(&self, bar: &Bar0) -> u32 {
let head = bar.read(regs::NV_PFSP_MSGQ_HEAD).address();
let tail = bar.read(regs::NV_PFSP_MSGQ_TAIL).address();
@@ -191,7 +190,6 @@ pub(crate) fn poll_msgq(&self, bar: &Bar0) -> u32 {
///
/// # Returns
/// `Ok(())` on success, `Err(EINVAL)` if packet is empty or not 4-byte aligned
- #[expect(unused)]
pub(crate) fn send_msg(&self, bar: &Bar0, packet: &[u8]) -> Result {
if packet.is_empty() {
return Err(EINVAL);
@@ -219,7 +217,6 @@ pub(crate) fn send_msg(&self, bar: &Bar0, packet: &[u8]) -> Result {
///
/// # Returns
/// `Ok(bytes_read)` on success, `Err(EINVAL)` if size is 0, exceeds buffer, or not aligned
- #[expect(unused)]
pub(crate) fn recv_msg(&self, bar: &Bar0, buffer: &mut [u8], size: usize) -> Result<usize> {
if size == 0 || size > buffer.len() {
return Err(EINVAL);
diff --git a/drivers/gpu/nova-core/fsp.rs b/drivers/gpu/nova-core/fsp.rs
index 8287bda795ca..b489845f733a 100644
--- a/drivers/gpu/nova-core/fsp.rs
+++ b/drivers/gpu/nova-core/fsp.rs
@@ -15,6 +15,15 @@
use crate::regs;
+use crate::mctp::{
+ MctpHeader,
+ NvdmHeader,
+ NvdmType, //
+};
+
+/// FSP message timeout in milliseconds.
+const FSP_MSG_TIMEOUT_MS: i64 = 2000;
+
/// FSP secure boot completion timeout in milliseconds.
const FSP_SECURE_BOOT_TIMEOUT_MS: i64 = 5000;
@@ -30,6 +39,37 @@ pub(crate) struct FmcSignatures {
public_key: [u8; FSP_PKEY_SIZE],
signature: [u8; FSP_SIG_SIZE],
}
+
+/// FSP Command Response payload structure.
+/// NVDM_PAYLOAD_COMMAND_RESPONSE structure.
+#[repr(C, packed)]
+#[derive(Clone, Copy)]
+struct NvdmPayloadCommandResponse {
+ task_id: u32,
+ command_nvdm_type: u32,
+ error_code: u32,
+}
+
+/// Complete FSP response structure with MCTP and NVDM headers.
+#[repr(C, packed)]
+#[derive(Clone, Copy)]
+struct FspResponse {
+ mctp_header: u32,
+ nvdm_header: u32,
+ response: NvdmPayloadCommandResponse,
+}
+
+// SAFETY: FspResponse is a packed C struct with only integral fields.
+unsafe impl FromBytes for FspResponse {}
+
+/// Trait implemented by types representing a message to send to FSP.
+///
+/// This provides [`Fsp::send_sync_fsp`] with the information it needs to send
+/// a given message, following the same pattern as GSP's `CommandToGsp`.
+pub(crate) trait MessageToFsp: AsBytes {
+ /// NVDM type identifying this message to FSP.
+ const NVDM_TYPE: u32;
+}
/// FSP interface for Hopper/Blackwell GPUs.
pub(crate) struct Fsp;
@@ -128,4 +168,87 @@ pub(crate) fn extract_fmc_signatures(
Ok(signatures)
}
+
+ /// Send message to FSP and wait for response.
+ #[expect(dead_code)]
+ fn send_sync_fsp<M>(
+ dev: &device::Device<device::Bound>,
+ bar: &crate::driver::Bar0,
+ fsp_falcon: &crate::falcon::Falcon<crate::falcon::fsp::Fsp>,
+ msg: &M,
+ ) -> Result
+ where
+ M: MessageToFsp,
+ {
+ fsp_falcon.send_msg(bar, msg.as_bytes())?;
+
+ let timeout = Delta::from_millis(FSP_MSG_TIMEOUT_MS);
+ let packet_size = read_poll_timeout(
+ || Ok(fsp_falcon.poll_msgq(bar)),
+ |&size| size > 0,
+ Delta::from_millis(10),
+ timeout,
+ )
+ .map_err(|_| {
+ dev_err!(dev, "FSP response timeout\n");
+ ETIMEDOUT
+ })?;
+
+ let packet_size = packet_size as usize;
+ let mut response_buf = KVec::<u8>::new();
+ response_buf.resize(packet_size, 0, GFP_KERNEL)?;
+ fsp_falcon.recv_msg(bar, &mut response_buf, packet_size)?;
+
+ if response_buf.len() < core::mem::size_of::<FspResponse>() {
+ dev_err!(dev, "FSP response too small: {}\n", response_buf.len());
+ return Err(EIO);
+ }
+
+ let response = FspResponse::from_bytes(&response_buf[..]).ok_or(EIO)?;
+
+ let mctp_header: MctpHeader = response.mctp_header.into();
+ let nvdm_header: NvdmHeader = response.nvdm_header.into();
+ let command_nvdm_type = response.response.command_nvdm_type;
+ let error_code = response.response.error_code;
+
+ if !mctp_header.is_single_packet() {
+ dev_err!(
+ dev,
+ "Unexpected MCTP header in FSP reply: {:#x}\n",
+ mctp_header.raw()
+ );
+ return Err(EIO);
+ }
+
+ if !nvdm_header.validate(NvdmType::FspResponse) {
+ dev_err!(
+ dev,
+ "Unexpected NVDM header in FSP reply: {:#x}\n",
+ nvdm_header.raw()
+ );
+ return Err(EIO);
+ }
+
+ if command_nvdm_type != M::NVDM_TYPE {
+ dev_err!(
+ dev,
+ "Expected NVDM type {:#x} in reply, got {:#x}\n",
+ M::NVDM_TYPE,
+ command_nvdm_type
+ );
+ return Err(EIO);
+ }
+
+ if error_code != 0 {
+ dev_err!(
+ dev,
+ "NVDM command {:#x} failed with error {:#x}\n",
+ M::NVDM_TYPE,
+ error_code
+ );
+ return Err(EIO);
+ }
+
+ Ok(())
+ }
}
--
2.53.0
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [PATCH v10 23/28] gpu: nova-core: Hopper/Blackwell: add FspCotVersion type
2026-04-11 2:49 [PATCH v10 00/28] gpu: nova-core: firmware: Hopper/Blackwell support John Hubbard
` (21 preceding siblings ...)
2026-04-11 2:49 ` [PATCH v10 22/28] gpu: nova-core: Hopper/Blackwell: add FSP send/receive messaging John Hubbard
@ 2026-04-11 2:49 ` John Hubbard
2026-04-11 2:49 ` [PATCH v10 24/28] gpu: nova-core: Hopper/Blackwell: larger non-WPR heap John Hubbard
` (4 subsequent siblings)
27 siblings, 0 replies; 30+ messages in thread
From: John Hubbard @ 2026-04-11 2:49 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot
Cc: Joel Fernandes, Timur Tabi, Alistair Popple, Eliot Courtney,
Shashank Sharma, Zhi Wang, David Airlie, Simona Vetter,
Bjorn Helgaas, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, rust-for-linux, LKML, John Hubbard
Add FspCotVersion to represent the FSP Chain of Trust protocol version,
and Chipset::fsp_cot_version() which returns the version for each
architecture. Hopper uses version 1, Blackwell uses version 2.
Non-FSP architectures return None.
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
---
drivers/gpu/nova-core/fsp.rs | 19 +++++++++++++++++++
drivers/gpu/nova-core/gpu.rs | 16 ++++++++++++++++
2 files changed, 35 insertions(+)
diff --git a/drivers/gpu/nova-core/fsp.rs b/drivers/gpu/nova-core/fsp.rs
index b489845f733a..bc98a5c3cfaa 100644
--- a/drivers/gpu/nova-core/fsp.rs
+++ b/drivers/gpu/nova-core/fsp.rs
@@ -21,6 +21,25 @@
NvdmType, //
};
+/// FSP Chain of Trust protocol version.
+///
+/// Hopper (GH100) uses version 1, Blackwell uses version 2.
+#[derive(Debug, Clone, Copy)]
+pub(crate) struct FspCotVersion(u16);
+
+impl FspCotVersion {
+ /// Create a new FSP COT version.
+ pub(crate) const fn new(version: u16) -> Self {
+ Self(version)
+ }
+
+ /// Return the raw protocol version number for the wire format.
+ #[expect(dead_code)]
+ pub(crate) const fn raw(self) -> u16 {
+ self.0
+ }
+}
+
/// FSP message timeout in milliseconds.
const FSP_MSG_TIMEOUT_MS: i64 = 2000;
diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs
index 0b3a62f6ad1b..5d7fd810687d 100644
--- a/drivers/gpu/nova-core/gpu.rs
+++ b/drivers/gpu/nova-core/gpu.rs
@@ -24,6 +24,7 @@
Falcon, //
},
fb::SysmemFlush,
+ fsp::FspCotVersion,
gsp::Gsp,
regs,
};
@@ -135,6 +136,21 @@ pub(crate) const fn arch(self) -> Architecture {
pub(crate) const fn needs_fwsec_bootloader(self) -> bool {
matches!(self.arch(), Architecture::Turing) || matches!(self, Self::GA100)
}
+
+ /// Returns the FSP Chain of Trust (COT) protocol version for this chipset.
+ ///
+ /// Hopper (GH100) uses version 1, Blackwell uses version 2.
+ /// Returns `None` for architectures that do not use FSP.
+ #[expect(dead_code)]
+ pub(crate) const fn fsp_cot_version(&self) -> Option<FspCotVersion> {
+ match self.arch() {
+ Architecture::Hopper => Some(FspCotVersion::new(1)),
+ Architecture::BlackwellGB10x | Architecture::BlackwellGB20x => {
+ Some(FspCotVersion::new(2))
+ }
+ _ => None,
+ }
+ }
}
// TODO
--
2.53.0
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [PATCH v10 24/28] gpu: nova-core: Hopper/Blackwell: larger non-WPR heap
2026-04-11 2:49 [PATCH v10 00/28] gpu: nova-core: firmware: Hopper/Blackwell support John Hubbard
` (22 preceding siblings ...)
2026-04-11 2:49 ` [PATCH v10 23/28] gpu: nova-core: Hopper/Blackwell: add FspCotVersion type John Hubbard
@ 2026-04-11 2:49 ` John Hubbard
2026-04-11 2:49 ` [PATCH v10 25/28] gpu: nova-core: Hopper/Blackwell: add FSP Chain of Trust boot John Hubbard
` (3 subsequent siblings)
27 siblings, 0 replies; 30+ messages in thread
From: John Hubbard @ 2026-04-11 2:49 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot
Cc: Joel Fernandes, Timur Tabi, Alistair Popple, Eliot Courtney,
Shashank Sharma, Zhi Wang, David Airlie, Simona Vetter,
Bjorn Helgaas, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, rust-for-linux, LKML, John Hubbard
Add dedicated FB HALs for Hopper (GH100) and Blackwell (GB100) with
architecture-specific non-WPR heap sizes. Hopper uses 2 MiB, Blackwell
uses 2 MiB + 128 KiB. These are needed for the larger reserved memory
regions that Hopper/Blackwell GPUs require.
Also adds the non_wpr_heap_size() method to the FbHal trait, and
the total_reserved_size field to FbLayout.
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
---
drivers/gpu/nova-core/fb.rs | 14 +++++++---
drivers/gpu/nova-core/fb/hal.rs | 19 +++++++++-----
drivers/gpu/nova-core/fb/hal/ga102.rs | 2 +-
drivers/gpu/nova-core/fb/hal/gb100.rs | 38 +++++++++++++++++++++++++++
drivers/gpu/nova-core/fb/hal/gh100.rs | 38 +++++++++++++++++++++++++++
5 files changed, 101 insertions(+), 10 deletions(-)
create mode 100644 drivers/gpu/nova-core/fb/hal/gb100.rs
create mode 100644 drivers/gpu/nova-core/fb/hal/gh100.rs
diff --git a/drivers/gpu/nova-core/fb.rs b/drivers/gpu/nova-core/fb.rs
index c2005e4b4177..756ff283a908 100644
--- a/drivers/gpu/nova-core/fb.rs
+++ b/drivers/gpu/nova-core/fb.rs
@@ -103,6 +103,15 @@ pub(crate) fn unregister(&self, bar: &Bar0) {
}
}
+/// Calculate non-WPR heap size based on chipset architecture.
+/// This matches the logic used in FSP for consistency.
+pub(crate) fn calc_non_wpr_heap_size(chipset: Chipset) -> u64 {
+ hal::fb_hal(chipset)
+ .non_wpr_heap_size()
+ .map(u64::from)
+ .unwrap_or(u64::SZ_1M)
+}
+
pub(crate) struct FbRange(Range<u64>);
impl FbRange {
@@ -262,9 +271,8 @@ pub(crate) fn new(chipset: Chipset, bar: &Bar0, gsp_fw: &GspFirmware) -> Result<
};
let heap = {
- const HEAP_SIZE: u64 = u64::SZ_1M;
-
- FbRange(wpr2.start - HEAP_SIZE..wpr2.start)
+ let heap_size = calc_non_wpr_heap_size(chipset);
+ FbRange(wpr2.start - heap_size..wpr2.start)
};
Ok(Self {
diff --git a/drivers/gpu/nova-core/fb/hal.rs b/drivers/gpu/nova-core/fb/hal.rs
index 3b3bad0feed0..478f80d640c1 100644
--- a/drivers/gpu/nova-core/fb/hal.rs
+++ b/drivers/gpu/nova-core/fb/hal.rs
@@ -12,6 +12,8 @@
mod ga100;
mod ga102;
+mod gb100;
+mod gh100;
mod tu102;
pub(crate) trait FbHal {
@@ -28,17 +30,22 @@ pub(crate) trait FbHal {
/// Returns the VRAM size, in bytes.
fn vidmem_size(&self, bar: &Bar0) -> u64;
+
+ /// Returns the non-WPR heap size for GPUs that need large reserved memory.
+ ///
+ /// Returns `None` for GPUs that don't need extra reserved memory.
+ fn non_wpr_heap_size(&self) -> Option<u32> {
+ None
+ }
}
/// Returns the HAL corresponding to `chipset`.
-pub(super) fn fb_hal(chipset: Chipset) -> &'static dyn FbHal {
+pub(crate) fn fb_hal(chipset: Chipset) -> &'static dyn FbHal {
match chipset.arch() {
Architecture::Turing => tu102::TU102_HAL,
Architecture::Ampere if chipset == Chipset::GA100 => ga100::GA100_HAL,
- Architecture::Ampere => ga102::GA102_HAL,
- Architecture::Ada
- | Architecture::Hopper
- | Architecture::BlackwellGB10x
- | Architecture::BlackwellGB20x => ga102::GA102_HAL,
+ Architecture::Ampere | Architecture::Ada => ga102::GA102_HAL,
+ Architecture::Hopper => gh100::GH100_HAL,
+ Architecture::BlackwellGB10x | Architecture::BlackwellGB20x => gb100::GB100_HAL,
}
}
diff --git a/drivers/gpu/nova-core/fb/hal/ga102.rs b/drivers/gpu/nova-core/fb/hal/ga102.rs
index 4b9f0f74d0e7..79c5a44f6a29 100644
--- a/drivers/gpu/nova-core/fb/hal/ga102.rs
+++ b/drivers/gpu/nova-core/fb/hal/ga102.rs
@@ -11,7 +11,7 @@
regs, //
};
-fn vidmem_size_ga102(bar: &Bar0) -> u64 {
+pub(super) fn vidmem_size_ga102(bar: &Bar0) -> u64 {
bar.read(regs::NV_USABLE_FB_SIZE_IN_MB).usable_fb_size()
}
diff --git a/drivers/gpu/nova-core/fb/hal/gb100.rs b/drivers/gpu/nova-core/fb/hal/gb100.rs
new file mode 100644
index 000000000000..bead99a6ca76
--- /dev/null
+++ b/drivers/gpu/nova-core/fb/hal/gb100.rs
@@ -0,0 +1,38 @@
+// SPDX-License-Identifier: GPL-2.0
+
+use kernel::prelude::*;
+
+use crate::{
+ driver::Bar0,
+ fb::hal::FbHal, //
+};
+
+struct Gb100;
+
+impl FbHal for Gb100 {
+ fn read_sysmem_flush_page(&self, bar: &Bar0) -> u64 {
+ super::ga100::read_sysmem_flush_page_ga100(bar)
+ }
+
+ fn write_sysmem_flush_page(&self, bar: &Bar0, addr: u64) -> Result {
+ super::ga100::write_sysmem_flush_page_ga100(bar, addr);
+
+ Ok(())
+ }
+
+ fn supports_display(&self, bar: &Bar0) -> bool {
+ super::ga100::display_enabled_ga100(bar)
+ }
+
+ fn vidmem_size(&self, bar: &Bar0) -> u64 {
+ super::ga102::vidmem_size_ga102(bar)
+ }
+
+ fn non_wpr_heap_size(&self) -> Option<u32> {
+ // 2 MiB + 128 KiB non-WPR heap for Blackwell (see Open RM: kgspCalculateFbLayout_GB100).
+ Some(0x220000)
+ }
+}
+
+const GB100: Gb100 = Gb100;
+pub(super) const GB100_HAL: &dyn FbHal = &GB100;
diff --git a/drivers/gpu/nova-core/fb/hal/gh100.rs b/drivers/gpu/nova-core/fb/hal/gh100.rs
new file mode 100644
index 000000000000..32d7414e6243
--- /dev/null
+++ b/drivers/gpu/nova-core/fb/hal/gh100.rs
@@ -0,0 +1,38 @@
+// SPDX-License-Identifier: GPL-2.0
+
+use kernel::prelude::*;
+
+use crate::{
+ driver::Bar0,
+ fb::hal::FbHal, //
+};
+
+struct Gh100;
+
+impl FbHal for Gh100 {
+ fn read_sysmem_flush_page(&self, bar: &Bar0) -> u64 {
+ super::ga100::read_sysmem_flush_page_ga100(bar)
+ }
+
+ fn write_sysmem_flush_page(&self, bar: &Bar0, addr: u64) -> Result {
+ super::ga100::write_sysmem_flush_page_ga100(bar, addr);
+
+ Ok(())
+ }
+
+ fn supports_display(&self, bar: &Bar0) -> bool {
+ super::ga100::display_enabled_ga100(bar)
+ }
+
+ fn vidmem_size(&self, bar: &Bar0) -> u64 {
+ super::ga102::vidmem_size_ga102(bar)
+ }
+
+ fn non_wpr_heap_size(&self) -> Option<u32> {
+ // 2 MiB non-WPR heap for Hopper (see Open RM: kgspCalculateFbLayout_GH100).
+ Some(0x200000)
+ }
+}
+
+const GH100: Gh100 = Gh100;
+pub(super) const GH100_HAL: &dyn FbHal = &GH100;
--
2.53.0
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [PATCH v10 25/28] gpu: nova-core: Hopper/Blackwell: add FSP Chain of Trust boot
2026-04-11 2:49 [PATCH v10 00/28] gpu: nova-core: firmware: Hopper/Blackwell support John Hubbard
` (23 preceding siblings ...)
2026-04-11 2:49 ` [PATCH v10 24/28] gpu: nova-core: Hopper/Blackwell: larger non-WPR heap John Hubbard
@ 2026-04-11 2:49 ` John Hubbard
2026-04-11 2:49 ` [PATCH v10 26/28] gpu: nova-core: Blackwell: use correct sysmem flush registers John Hubbard
` (2 subsequent siblings)
27 siblings, 0 replies; 30+ messages in thread
From: John Hubbard @ 2026-04-11 2:49 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot
Cc: Joel Fernandes, Timur Tabi, Alistair Popple, Eliot Courtney,
Shashank Sharma, Zhi Wang, David Airlie, Simona Vetter,
Bjorn Helgaas, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, rust-for-linux, LKML, John Hubbard
Add boot_fmc() which builds and sends the Chain of Trust message to FSP.
FmcBootArgs bundles the DMA-coherent boot parameters that FSP reads at
boot time.
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
---
drivers/gpu/nova-core/fb.rs | 1 -
drivers/gpu/nova-core/firmware/fsp.rs | 1 -
drivers/gpu/nova-core/fsp.rs | 257 +++++++++++++++++++++++++-
drivers/gpu/nova-core/gpu.rs | 1 -
drivers/gpu/nova-core/gsp/boot.rs | 26 ++-
drivers/gpu/nova-core/mctp.rs | 2 -
6 files changed, 275 insertions(+), 13 deletions(-)
diff --git a/drivers/gpu/nova-core/fb.rs b/drivers/gpu/nova-core/fb.rs
index 756ff283a908..2b3a2bb2f1db 100644
--- a/drivers/gpu/nova-core/fb.rs
+++ b/drivers/gpu/nova-core/fb.rs
@@ -294,7 +294,6 @@ pub(crate) fn new(chipset: Chipset, bar: &Bar0, gsp_fw: &GspFirmware) -> Result<
}
/// Returns the PMU reserved memory size for `chipset`.
-#[expect(dead_code)]
pub(crate) fn calc_pmu_reserved_size(chipset: Chipset) -> u32 {
match chipset.arch() {
Architecture::BlackwellGB10x | Architecture::BlackwellGB20x => PMU_RESERVED_SIZE,
diff --git a/drivers/gpu/nova-core/firmware/fsp.rs b/drivers/gpu/nova-core/firmware/fsp.rs
index 3968bacb7e61..301345aa491c 100644
--- a/drivers/gpu/nova-core/firmware/fsp.rs
+++ b/drivers/gpu/nova-core/firmware/fsp.rs
@@ -14,7 +14,6 @@
gpu::Chipset, //
};
-#[expect(dead_code)]
pub(crate) struct FspFirmware {
/// FMC firmware image data (only the "image" ELF section).
pub(crate) fmc_image: Coherent<[u8]>,
diff --git a/drivers/gpu/nova-core/fsp.rs b/drivers/gpu/nova-core/fsp.rs
index bc98a5c3cfaa..75e06b2d5f06 100644
--- a/drivers/gpu/nova-core/fsp.rs
+++ b/drivers/gpu/nova-core/fsp.rs
@@ -8,9 +8,25 @@
use kernel::{
device,
+ dma::{
+ Coherent,
+ CoherentBox, //
+ },
io::poll::read_poll_timeout,
prelude::*,
- time::Delta, //
+ ptr::{
+ Alignable,
+ Alignment, //
+ },
+ sizes::{
+ SizeConstants,
+ SZ_2M, //
+ },
+ time::Delta,
+ transmute::{
+ AsBytes,
+ FromBytes, //
+ },
};
use crate::regs;
@@ -34,7 +50,6 @@ pub(crate) const fn new(version: u16) -> Self {
}
/// Return the raw protocol version number for the wire format.
- #[expect(dead_code)]
pub(crate) const fn raw(self) -> u16 {
self.0
}
@@ -46,6 +61,89 @@ pub(crate) const fn raw(self) -> u16 {
/// FSP secure boot completion timeout in milliseconds.
const FSP_SECURE_BOOT_TIMEOUT_MS: i64 = 5000;
+/// GSP FMC initialization parameters.
+#[repr(C)]
+#[derive(Debug, Clone, Copy, Default)]
+struct GspFmcInitParams {
+ /// CC initialization "registry keys".
+ regkeys: u32,
+}
+
+// SAFETY: GspFmcInitParams is a simple C struct with only primitive types.
+unsafe impl AsBytes for GspFmcInitParams {}
+// SAFETY: All bit patterns are valid for the primitive fields.
+unsafe impl FromBytes for GspFmcInitParams {}
+
+/// GSP ACR (Authenticated Code RAM) boot parameters.
+#[repr(C)]
+#[derive(Debug, Clone, Copy, Default)]
+struct GspAcrBootGspRmParams {
+ /// Physical memory aperture through which gspRmDescPa is accessed.
+ target: u32,
+ /// Size in bytes of the GSP-RM descriptor structure.
+ gsp_rm_desc_size: u32,
+ /// Physical offset in the target aperture of the GSP-RM descriptor structure.
+ gsp_rm_desc_offset: u64,
+ /// Physical offset in FB to set the start of the WPR containing GSP-RM.
+ wpr_carveout_offset: u64,
+ /// Size in bytes of the WPR containing GSP-RM.
+ wpr_carveout_size: u32,
+ /// Whether to boot GSP-RM or GSP-Proxy through ACR.
+ b_is_gsp_rm_boot: u32,
+}
+
+// SAFETY: GspAcrBootGspRmParams is a simple C struct with only primitive types.
+unsafe impl AsBytes for GspAcrBootGspRmParams {}
+// SAFETY: All bit patterns are valid for the primitive fields.
+unsafe impl FromBytes for GspAcrBootGspRmParams {}
+
+/// GSP RM boot parameters.
+#[repr(C)]
+#[derive(Debug, Clone, Copy, Default)]
+struct GspRmParams {
+ /// Physical memory aperture through which bootArgsOffset is accessed.
+ target: u32,
+ /// Physical offset in the memory aperture that will be passed to GSP-RM.
+ boot_args_offset: u64,
+}
+
+// SAFETY: GspRmParams is a simple C struct with only primitive types.
+unsafe impl AsBytes for GspRmParams {}
+// SAFETY: All bit patterns are valid for the primitive fields.
+unsafe impl FromBytes for GspRmParams {}
+
+/// GSP SPDM (Security Protocol and Data Model) parameters.
+#[repr(C)]
+#[derive(Debug, Clone, Copy, Default)]
+struct GspSpdmParams {
+ /// Physical memory aperture through which all addresses are accessed.
+ target: u32,
+ /// Physical offset in the memory aperture where SPDM payload buffer is stored.
+ payload_buffer_offset: u64,
+ /// Size of the above payload buffer.
+ payload_buffer_size: u32,
+}
+
+// SAFETY: GspSpdmParams is a simple C struct with only primitive types.
+unsafe impl AsBytes for GspSpdmParams {}
+// SAFETY: All bit patterns are valid for the primitive fields.
+unsafe impl FromBytes for GspSpdmParams {}
+
+/// Complete GSP FMC boot parameters passed to FSP.
+#[repr(C)]
+#[derive(Debug, Clone, Copy, Default)]
+pub(crate) struct GspFmcBootParams {
+ init_params: GspFmcInitParams,
+ boot_gsp_rm_params: GspAcrBootGspRmParams,
+ gsp_rm_params: GspRmParams,
+ gsp_spdm_params: GspSpdmParams,
+}
+
+// SAFETY: GspFmcBootParams is composed of C structs with only primitive types.
+unsafe impl AsBytes for GspFmcBootParams {}
+// SAFETY: All bit patterns are valid for the primitive fields.
+unsafe impl FromBytes for GspFmcBootParams {}
+
/// Size constraints for FSP security signatures (Hopper/Blackwell).
const FSP_HASH_SIZE: usize = 48; // SHA-384 hash
const FSP_PKEY_SIZE: usize = 384; // RSA-3072 public key
@@ -69,6 +167,35 @@ struct NvdmPayloadCommandResponse {
error_code: u32,
}
+/// NVDM (NVIDIA Device Management) COT (Chain of Trust) payload structure.
+/// This is the main message payload sent to FSP for Chain of Trust.
+#[repr(C, packed)]
+#[derive(Clone, Copy)]
+struct NvdmPayloadCot {
+ version: u16,
+ size: u16,
+ gsp_fmc_sysmem_offset: u64,
+ frts_sysmem_offset: u64,
+ frts_sysmem_size: u32,
+ frts_vidmem_offset: u64,
+ frts_vidmem_size: u32,
+ hash384: [u8; FSP_HASH_SIZE],
+ public_key: [u8; FSP_PKEY_SIZE],
+ signature: [u8; FSP_SIG_SIZE],
+ gsp_boot_args_sysmem_offset: u64,
+}
+
+/// Complete FSP message structure with MCTP and NVDM headers.
+#[repr(C, packed)]
+#[derive(Clone, Copy)]
+struct FspMessage {
+ mctp_header: u32,
+ nvdm_header: u32,
+ cot: NvdmPayloadCot,
+}
+
+// SAFETY: FspMessage is a packed C struct with only integral fields.
+unsafe impl AsBytes for FspMessage {}
/// Complete FSP response structure with MCTP and NVDM headers.
#[repr(C, packed)]
#[derive(Clone, Copy)]
@@ -89,6 +216,73 @@ pub(crate) trait MessageToFsp: AsBytes {
/// NVDM type identifying this message to FSP.
const NVDM_TYPE: u32;
}
+
+impl MessageToFsp for FspMessage {
+ const NVDM_TYPE: u32 = NvdmType::Cot as u32;
+}
+
+/// Bundled arguments for FMC boot via FSP Chain of Trust.
+pub(crate) struct FmcBootArgs<'a> {
+ chipset: crate::gpu::Chipset,
+ fmc_image_fw: &'a Coherent<[u8]>,
+ fmc_boot_params: Coherent<GspFmcBootParams>,
+ resume: bool,
+ signatures: &'a FmcSignatures,
+}
+
+impl<'a> FmcBootArgs<'a> {
+ /// Build FMC boot arguments, allocating the DMA-coherent boot parameter
+ /// structure that FSP will read.
+ #[allow(clippy::too_many_arguments)]
+ pub(crate) fn new(
+ dev: &device::Device<device::Bound>,
+ chipset: crate::gpu::Chipset,
+ fmc_image_fw: &'a Coherent<[u8]>,
+ wpr_meta_addr: u64,
+ wpr_meta_size: u32,
+ libos_addr: u64,
+ resume: bool,
+ signatures: &'a FmcSignatures,
+ ) -> Result<Self> {
+ // `GSP_DMA_TARGET_*` is not in the current Rust bindings yet.
+ const GSP_DMA_TARGET_COHERENT_SYSTEM: u32 = 1;
+ const GSP_DMA_TARGET_NONCOHERENT_SYSTEM: u32 = 2;
+
+ let mut fmc_boot_params = CoherentBox::<GspFmcBootParams>::zeroed(dev, GFP_KERNEL)?;
+
+ // Blackwell FSP expects wpr_carveout_offset and wpr_carveout_size to be zero;
+ // it obtains WPR info from other sources.
+ fmc_boot_params.boot_gsp_rm_params = GspAcrBootGspRmParams {
+ target: GSP_DMA_TARGET_COHERENT_SYSTEM,
+ gsp_rm_desc_size: wpr_meta_size,
+ gsp_rm_desc_offset: wpr_meta_addr,
+ b_is_gsp_rm_boot: 1,
+ ..Default::default()
+ };
+
+ fmc_boot_params.gsp_rm_params = GspRmParams {
+ target: GSP_DMA_TARGET_NONCOHERENT_SYSTEM,
+ boot_args_offset: libos_addr,
+ };
+
+ let fmc_boot_params: Coherent<GspFmcBootParams> = fmc_boot_params.into();
+
+ Ok(Self {
+ chipset,
+ fmc_image_fw,
+ fmc_boot_params,
+ resume,
+ signatures,
+ })
+ }
+
+ /// DMA address of the FMC boot parameters, needed after boot for lockdown
+ /// release polling.
+ #[expect(dead_code)]
+ pub(crate) fn boot_params_dma_handle(&self) -> u64 {
+ self.fmc_boot_params.dma_handle()
+ }
+}
/// FSP interface for Hopper/Blackwell GPUs.
pub(crate) struct Fsp;
@@ -188,8 +382,65 @@ pub(crate) fn extract_fmc_signatures(
Ok(signatures)
}
+ /// Boot GSP FMC via FSP Chain of Trust.
+ ///
+ /// Builds the COT message from the pre-configured [`FmcBootArgs`], sends it
+ /// to FSP, and waits for the response.
+ pub(crate) fn boot_fmc(
+ dev: &device::Device<device::Bound>,
+ bar: &crate::driver::Bar0,
+ fsp_falcon: &crate::falcon::Falcon<crate::falcon::fsp::Fsp>,
+ args: &FmcBootArgs<'_>,
+ ) -> Result {
+ dev_dbg!(dev, "Starting FSP boot sequence for {}\n", args.chipset);
+
+ let fmc_addr = args.fmc_image_fw.dma_handle();
+ let fmc_boot_params_addr = args.fmc_boot_params.dma_handle();
+
+ // frts_offset is relative to FB end: FRTS_location = FB_END - frts_offset
+ let frts_offset = if !args.resume {
+ let frts_reserved_size = crate::fb::calc_non_wpr_heap_size(args.chipset)
+ .checked_add(u64::from(crate::fb::calc_pmu_reserved_size(args.chipset)))
+ .ok_or(EINVAL)?;
+
+ frts_reserved_size
+ .align_up(Alignment::new::<SZ_2M>())
+ .ok_or(EINVAL)?
+ } else {
+ 0
+ };
+ let frts_size: u32 = if !args.resume { u32::SZ_1M } else { 0 };
+
+ let msg = KBox::new(
+ FspMessage {
+ mctp_header: MctpHeader::single_packet().raw(),
+ nvdm_header: NvdmHeader::new(NvdmType::Cot).raw(),
+
+ cot: NvdmPayloadCot {
+ version: args.chipset.fsp_cot_version().ok_or(ENOTSUPP)?.raw(),
+ size: u16::try_from(core::mem::size_of::<NvdmPayloadCot>())
+ .map_err(|_| EINVAL)?,
+ gsp_fmc_sysmem_offset: fmc_addr,
+ frts_sysmem_offset: 0,
+ frts_sysmem_size: 0,
+ frts_vidmem_offset: frts_offset,
+ frts_vidmem_size: frts_size,
+ hash384: args.signatures.hash384,
+ public_key: args.signatures.public_key,
+ signature: args.signatures.signature,
+ gsp_boot_args_sysmem_offset: fmc_boot_params_addr,
+ },
+ },
+ GFP_KERNEL,
+ )?;
+
+ Self::send_sync_fsp(dev, bar, fsp_falcon, &*msg)?;
+
+ dev_dbg!(dev, "FSP Chain of Trust completed successfully\n");
+ Ok(())
+ }
+
/// Send message to FSP and wait for response.
- #[expect(dead_code)]
fn send_sync_fsp<M>(
dev: &device::Device<device::Bound>,
bar: &crate::driver::Bar0,
diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs
index 5d7fd810687d..0a05d038ab76 100644
--- a/drivers/gpu/nova-core/gpu.rs
+++ b/drivers/gpu/nova-core/gpu.rs
@@ -141,7 +141,6 @@ pub(crate) const fn needs_fwsec_bootloader(self) -> bool {
///
/// Hopper (GH100) uses version 1, Blackwell uses version 2.
/// Returns `None` for architectures that do not use FSP.
- #[expect(dead_code)]
pub(crate) const fn fsp_cot_version(&self) -> Option<FspCotVersion> {
match self.arch() {
Architecture::Hopper => Some(FspCotVersion::new(1)),
diff --git a/drivers/gpu/nova-core/gsp/boot.rs b/drivers/gpu/nova-core/gsp/boot.rs
index 739624af1cef..703c9ee48363 100644
--- a/drivers/gpu/nova-core/gsp/boot.rs
+++ b/drivers/gpu/nova-core/gsp/boot.rs
@@ -33,7 +33,10 @@
gsp::GspFirmware,
FIRMWARE_VERSION, //
},
- fsp::Fsp,
+ fsp::{
+ FmcBootArgs,
+ Fsp, //
+ },
gpu::{
Architecture,
Chipset, //
@@ -203,16 +206,29 @@ fn boot_via_fsp(
bar: &Bar0,
chipset: Chipset,
_gsp_falcon: &Falcon<Gsp>,
- _wpr_meta: &Coherent<GspFwWprMeta>,
- _libos: &Coherent<[LibosMemoryRegionInitArgument]>,
+ wpr_meta: &Coherent<GspFwWprMeta>,
+ libos: &Coherent<[LibosMemoryRegionInitArgument]>,
) -> Result {
- let _fsp_falcon = Falcon::<FspEngine>::new(dev, chipset)?;
+ let fsp_falcon = Falcon::<FspEngine>::new(dev, chipset)?;
let fsp_fw = FspFirmware::new(dev, chipset, FIRMWARE_VERSION)?;
- let _signatures = Fsp::extract_fmc_signatures(dev, fsp_fw.fmc_elf.data())?;
+ let signatures = Fsp::extract_fmc_signatures(dev, fsp_fw.fmc_elf.data())?;
Fsp::wait_secure_boot(dev, bar, chipset.arch())?;
+ let args = FmcBootArgs::new(
+ dev,
+ chipset,
+ &fsp_fw.fmc_image,
+ wpr_meta.dma_handle(),
+ core::mem::size_of::<GspFwWprMeta>() as u32,
+ libos.dma_handle(),
+ false,
+ &signatures,
+ )?;
+
+ Fsp::boot_fmc(dev, bar, &fsp_falcon, &args)?;
+
Err(ENOTSUPP)
}
diff --git a/drivers/gpu/nova-core/mctp.rs b/drivers/gpu/nova-core/mctp.rs
index 680ed19d196e..c23e8ec69636 100644
--- a/drivers/gpu/nova-core/mctp.rs
+++ b/drivers/gpu/nova-core/mctp.rs
@@ -6,8 +6,6 @@
//! Device Management) messages between the kernel driver and GPU firmware
//! processors such as FSP and GSP.
-#![expect(dead_code)]
-
/// NVDM message type identifiers carried over MCTP.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
--
2.53.0
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [PATCH v10 26/28] gpu: nova-core: Blackwell: use correct sysmem flush registers
2026-04-11 2:49 [PATCH v10 00/28] gpu: nova-core: firmware: Hopper/Blackwell support John Hubbard
` (24 preceding siblings ...)
2026-04-11 2:49 ` [PATCH v10 25/28] gpu: nova-core: Hopper/Blackwell: add FSP Chain of Trust boot John Hubbard
@ 2026-04-11 2:49 ` John Hubbard
2026-04-11 2:49 ` [PATCH v10 27/28] gpu: nova-core: Hopper/Blackwell: larger WPR2 (GSP) heap John Hubbard
2026-04-11 2:49 ` [PATCH v10 28/28] gpu: nova-core: Hopper/Blackwell: add GSP lockdown release polling John Hubbard
27 siblings, 0 replies; 30+ messages in thread
From: John Hubbard @ 2026-04-11 2:49 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot
Cc: Joel Fernandes, Timur Tabi, Alistair Popple, Eliot Courtney,
Shashank Sharma, Zhi Wang, David Airlie, Simona Vetter,
Bjorn Helgaas, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, rust-for-linux, LKML, John Hubbard
Blackwell GPUs moved the sysmem flush page registers away from the
legacy NV_PFB_NISO_FLUSH_SYSMEM_ADDR used by Ampere/Ada.
GB10x uses HSHUB0 registers, with both a primary and EG (egress) pair
that must be programmed to the same address. GB20x uses FBHUB0
registers.
Add separate GB100 and GB202 fb HALs, and split the Blackwell HAL
dispatch so that each uses its respective registers.
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
---
drivers/gpu/nova-core/fb/hal.rs | 8 ++-
drivers/gpu/nova-core/fb/hal/gb100.rs | 54 ++++++++++++++++---
drivers/gpu/nova-core/fb/hal/gb202.rs | 77 +++++++++++++++++++++++++++
drivers/gpu/nova-core/regs.rs | 36 +++++++++++++
4 files changed, 168 insertions(+), 7 deletions(-)
create mode 100644 drivers/gpu/nova-core/fb/hal/gb202.rs
diff --git a/drivers/gpu/nova-core/fb/hal.rs b/drivers/gpu/nova-core/fb/hal.rs
index 478f80d640c1..65edf07c3222 100644
--- a/drivers/gpu/nova-core/fb/hal.rs
+++ b/drivers/gpu/nova-core/fb/hal.rs
@@ -13,9 +13,14 @@
mod ga100;
mod ga102;
mod gb100;
+mod gb202;
mod gh100;
mod tu102;
+/// Non-WPR heap size for Blackwell (2 MiB + 128 KiB).
+/// See Open RM: kgspCalculateFbLayout_GB100.
+const BLACKWELL_NON_WPR_HEAP_SIZE: u32 = 0x220000;
+
pub(crate) trait FbHal {
/// Returns the address of the currently-registered sysmem flush page.
fn read_sysmem_flush_page(&self, bar: &Bar0) -> u64;
@@ -46,6 +51,7 @@ pub(crate) fn fb_hal(chipset: Chipset) -> &'static dyn FbHal {
Architecture::Ampere if chipset == Chipset::GA100 => ga100::GA100_HAL,
Architecture::Ampere | Architecture::Ada => ga102::GA102_HAL,
Architecture::Hopper => gh100::GH100_HAL,
- Architecture::BlackwellGB10x | Architecture::BlackwellGB20x => gb100::GB100_HAL,
+ Architecture::BlackwellGB10x => gb100::GB100_HAL,
+ Architecture::BlackwellGB20x => gb202::GB202_HAL,
}
}
diff --git a/drivers/gpu/nova-core/fb/hal/gb100.rs b/drivers/gpu/nova-core/fb/hal/gb100.rs
index bead99a6ca76..c6e2a505e6ae 100644
--- a/drivers/gpu/nova-core/fb/hal/gb100.rs
+++ b/drivers/gpu/nova-core/fb/hal/gb100.rs
@@ -1,21 +1,64 @@
// SPDX-License-Identifier: GPL-2.0
-use kernel::prelude::*;
+//! Blackwell GB10x framebuffer HAL.
+//!
+//! GB10x GPUs use HSHUB0 registers for the sysmem flush page. Both the primary and EG (egress)
+//! register pairs must be programmed to the same address, as required by hardware.
+
+use kernel::{
+ io::Io,
+ num::Bounded,
+ prelude::*, //
+};
use crate::{
driver::Bar0,
- fb::hal::FbHal, //
+ fb::hal::FbHal,
+ regs, //
};
struct Gb100;
+fn read_sysmem_flush_page_gb100(bar: &Bar0) -> u64 {
+ let lo = u64::from(
+ bar.read(regs::NV_PFB_HSHUB0_PCIE_FLUSH_SYSMEM_ADDR_LO)
+ .adr(),
+ );
+ let hi = u64::from(
+ bar.read(regs::NV_PFB_HSHUB0_PCIE_FLUSH_SYSMEM_ADDR_HI)
+ .adr(),
+ );
+
+ lo | (hi << 32)
+}
+
+fn write_sysmem_flush_page_gb100(bar: &Bar0, addr: Bounded<u64, 52>) {
+ // CAST: lower 32 bits. Hardware ignores bits 7:0.
+ let addr_lo = *addr as u32;
+ let addr_hi = addr.shr::<32, 20>().cast::<u32>();
+
+ // Write HI first. The hardware will trigger the flush on the LO write.
+
+ // Primary HSHUB pair.
+ bar.write_reg(regs::NV_PFB_HSHUB0_PCIE_FLUSH_SYSMEM_ADDR_HI::zeroed().with_adr(addr_hi));
+ bar.write_reg(regs::NV_PFB_HSHUB0_PCIE_FLUSH_SYSMEM_ADDR_LO::zeroed().with_adr(addr_lo));
+
+ // EG (egress) pair -- must match the primary pair.
+ bar.write_reg(regs::NV_PFB_HSHUB0_EG_PCIE_FLUSH_SYSMEM_ADDR_HI::zeroed().with_adr(addr_hi));
+ bar.write_reg(regs::NV_PFB_HSHUB0_EG_PCIE_FLUSH_SYSMEM_ADDR_LO::zeroed().with_adr(addr_lo));
+}
+
impl FbHal for Gb100 {
fn read_sysmem_flush_page(&self, bar: &Bar0) -> u64 {
- super::ga100::read_sysmem_flush_page_ga100(bar)
+ read_sysmem_flush_page_gb100(bar)
}
fn write_sysmem_flush_page(&self, bar: &Bar0, addr: u64) -> Result {
- super::ga100::write_sysmem_flush_page_ga100(bar, addr);
+ let addr: Bounded<u64, 52> = Bounded::<u64, 64>::from(addr)
+ .try_shrink::<52>()
+ .ok_or(EINVAL)?;
+
+ write_sysmem_flush_page_gb100(bar, addr);
Ok(())
}
@@ -29,8 +72,7 @@ fn vidmem_size(&self, bar: &Bar0) -> u64 {
}
fn non_wpr_heap_size(&self) -> Option<u32> {
- // 2 MiB + 128 KiB non-WPR heap for Blackwell (see Open RM: kgspCalculateFbLayout_GB100).
- Some(0x220000)
+ Some(super::BLACKWELL_NON_WPR_HEAP_SIZE)
}
}
diff --git a/drivers/gpu/nova-core/fb/hal/gb202.rs b/drivers/gpu/nova-core/fb/hal/gb202.rs
new file mode 100644
index 000000000000..0eaabc9920c7
--- /dev/null
+++ b/drivers/gpu/nova-core/fb/hal/gb202.rs
@@ -0,0 +1,77 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Blackwell GB20x framebuffer HAL.
+//!
+//! GB20x GPUs moved the sysmem flush registers from `NV_PFB_NISO_FLUSH_SYSMEM_ADDR` to
+//! `NV_PFB_FBHUB0_PCIE_FLUSH_SYSMEM_ADDR_{LO,HI}`.
+
+use kernel::{
+ io::Io,
+ num::Bounded,
+ prelude::*, //
+};
+
+use crate::{
+ driver::Bar0,
+ fb::hal::FbHal,
+ regs, //
+};
+
+struct Gb202;
+
+fn read_sysmem_flush_page_gb202(bar: &Bar0) -> u64 {
+ let lo = u64::from(
+ bar.read(regs::NV_PFB_FBHUB0_PCIE_FLUSH_SYSMEM_ADDR_LO)
+ .adr(),
+ );
+ let hi = u64::from(
+ bar.read(regs::NV_PFB_FBHUB0_PCIE_FLUSH_SYSMEM_ADDR_HI)
+ .adr(),
+ );
+
+ lo | (hi << 32)
+}
+
+fn write_sysmem_flush_page_gb202(bar: &Bar0, addr: Bounded<u64, 52>) {
+ // Write HI first. The hardware will trigger the flush on the LO write.
+ bar.write_reg(
+ regs::NV_PFB_FBHUB0_PCIE_FLUSH_SYSMEM_ADDR_HI::zeroed()
+ .with_adr(addr.shr::<32, 20>().cast::<u32>()),
+ );
+ bar.write_reg(
+ regs::NV_PFB_FBHUB0_PCIE_FLUSH_SYSMEM_ADDR_LO::zeroed()
+ // CAST: lower 32 bits. Hardware ignores bits 7:0.
+ .with_adr(*addr as u32),
+ );
+}
+
+impl FbHal for Gb202 {
+ fn read_sysmem_flush_page(&self, bar: &Bar0) -> u64 {
+ read_sysmem_flush_page_gb202(bar)
+ }
+
+ fn write_sysmem_flush_page(&self, bar: &Bar0, addr: u64) -> Result {
+ let addr: Bounded<u64, 52> = Bounded::<u64, 64>::from(addr)
+ .try_shrink::<52>()
+ .ok_or(EINVAL)?;
+
+ write_sysmem_flush_page_gb202(bar, addr);
+
+ Ok(())
+ }
+
+ fn supports_display(&self, bar: &Bar0) -> bool {
+ super::ga100::display_enabled_ga100(bar)
+ }
+
+ fn vidmem_size(&self, bar: &Bar0) -> u64 {
+ super::ga102::vidmem_size_ga102(bar)
+ }
+
+ fn non_wpr_heap_size(&self) -> Option<u32> {
+ Some(super::BLACKWELL_NON_WPR_HEAP_SIZE)
+ }
+}
+
+const GB202: Gb202 = Gb202;
+pub(super) const GB202_HAL: &dyn FbHal = &GB202;
diff --git a/drivers/gpu/nova-core/regs.rs b/drivers/gpu/nova-core/regs.rs
index c3ccae0c235f..77b11c7de3f8 100644
--- a/drivers/gpu/nova-core/regs.rs
+++ b/drivers/gpu/nova-core/regs.rs
@@ -145,6 +145,42 @@ fn fmt(&self, f: &mut kernel::fmt::Formatter<'_>) -> kernel::fmt::Result {
/// Bits 12..40 of the higher (exclusive) bound of the WPR2 region.
31:4 hi_val;
}
+
+ // Blackwell GB10x sysmem flush registers (HSHUB0).
+ //
+ // GB10x GPUs use two pairs of HSHUB registers for sysmembar: a primary pair and an EG
+ // (egress) pair. Both must be programmed to the same address. Hardware ignores bits 7:0
+ // of each LO register. HSHUB0 base is 0x00891000.
+
+ pub(crate) NV_PFB_HSHUB0_PCIE_FLUSH_SYSMEM_ADDR_LO(u32) @ 0x00891e50 {
+ 31:0 adr => u32;
+ }
+
+ pub(crate) NV_PFB_HSHUB0_PCIE_FLUSH_SYSMEM_ADDR_HI(u32) @ 0x00891e54 {
+ 19:0 adr;
+ }
+
+ pub(crate) NV_PFB_HSHUB0_EG_PCIE_FLUSH_SYSMEM_ADDR_LO(u32) @ 0x008916c0 {
+ 31:0 adr => u32;
+ }
+
+ pub(crate) NV_PFB_HSHUB0_EG_PCIE_FLUSH_SYSMEM_ADDR_HI(u32) @ 0x008916c4 {
+ 19:0 adr;
+ }
+
+ // Blackwell GB20x sysmem flush registers (FBHUB0).
+ //
+ // Unlike the older NV_PFB_NISO_FLUSH_SYSMEM_ADDR registers which encode the address with an
+ // 8-bit right-shift, these registers take the raw address split into lower/upper 32-bit halves.
+ // The hardware ignores bits 7:0 of the LO register.
+
+ pub(crate) NV_PFB_FBHUB0_PCIE_FLUSH_SYSMEM_ADDR_LO(u32) @ 0x008a1d58 {
+ 31:0 adr => u32;
+ }
+
+ pub(crate) NV_PFB_FBHUB0_PCIE_FLUSH_SYSMEM_ADDR_HI(u32) @ 0x008a1d5c {
+ 19:0 adr;
+ }
}
impl NV_PFB_PRI_MMU_LOCAL_MEMORY_RANGE {
--
2.53.0
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [PATCH v10 27/28] gpu: nova-core: Hopper/Blackwell: larger WPR2 (GSP) heap
2026-04-11 2:49 [PATCH v10 00/28] gpu: nova-core: firmware: Hopper/Blackwell support John Hubbard
` (25 preceding siblings ...)
2026-04-11 2:49 ` [PATCH v10 26/28] gpu: nova-core: Blackwell: use correct sysmem flush registers John Hubbard
@ 2026-04-11 2:49 ` John Hubbard
2026-04-11 2:49 ` [PATCH v10 28/28] gpu: nova-core: Hopper/Blackwell: add GSP lockdown release polling John Hubbard
27 siblings, 0 replies; 30+ messages in thread
From: John Hubbard @ 2026-04-11 2:49 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot
Cc: Joel Fernandes, Timur Tabi, Alistair Popple, Eliot Courtney,
Shashank Sharma, Zhi Wang, David Airlie, Simona Vetter,
Bjorn Helgaas, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, rust-for-linux, LKML, John Hubbard
Hopper, Blackwell and later GPUs require a larger heap for WPR2.
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
---
drivers/gpu/nova-core/gsp/fw.rs | 61 +++++++++++++++++++++++++--------
1 file changed, 47 insertions(+), 14 deletions(-)
diff --git a/drivers/gpu/nova-core/gsp/fw.rs b/drivers/gpu/nova-core/gsp/fw.rs
index 5d36604ea1a3..7352952e4ef1 100644
--- a/drivers/gpu/nova-core/gsp/fw.rs
+++ b/drivers/gpu/nova-core/gsp/fw.rs
@@ -103,21 +103,40 @@ enum GspFwHeapParams {}
/// Minimum required alignment for the GSP heap.
const GSP_HEAP_ALIGNMENT: Alignment = Alignment::new::<{ 1 << 20 }>();
+// These constants override the generated bindings for architecture-specific heap sizing.
+//
+// 14MB for Hopper/Blackwell+.
+const GSP_FW_HEAP_PARAM_BASE_RM_SIZE_GH100: u64 = 14 * u64::SZ_1M;
+// 142MB client alloc for ~188MB total.
+const GSP_FW_HEAP_PARAM_CLIENT_ALLOC_SIZE_GH100: u64 = 142 * u64::SZ_1M;
+// Hopper/Blackwell+ minimum heap size: 170MB (88 + 12 + 70).
+// See Open RM: GSP_FW_HEAP_SIZE_OVERRIDE_LIBOS3_BAREMETAL_MIN_MB for the base 88MB,
+// plus Hopper+ additions in kgspCalculateGspFwHeapSize_GH100.
+const GSP_FW_HEAP_SIZE_OVERRIDE_LIBOS3_BAREMETAL_MIN_MB_HOPPER: u64 = 170;
+
impl GspFwHeapParams {
/// Returns the amount of GSP-RM heap memory used during GSP-RM boot and initialization (up to
/// and including the first client subdevice allocation).
- fn base_rm_size(_chipset: Chipset) -> u64 {
- // TODO: this needs to be updated to return the correct value for Hopper+ once support for
- // them is added:
- // u64::from(bindings::GSP_FW_HEAP_PARAM_BASE_RM_SIZE_GH100)
- u64::from(bindings::GSP_FW_HEAP_PARAM_BASE_RM_SIZE_TU10X)
+ fn base_rm_size(chipset: Chipset) -> u64 {
+ use crate::gpu::Architecture;
+ match chipset.arch() {
+ Architecture::Hopper | Architecture::BlackwellGB10x | Architecture::BlackwellGB20x => {
+ GSP_FW_HEAP_PARAM_BASE_RM_SIZE_GH100
+ }
+ _ => u64::from(bindings::GSP_FW_HEAP_PARAM_BASE_RM_SIZE_TU10X),
+ }
}
/// Returns the amount of heap memory required to support a single channel allocation.
- fn client_alloc_size() -> u64 {
- u64::from(bindings::GSP_FW_HEAP_PARAM_CLIENT_ALLOC_SIZE)
- .align_up(GSP_HEAP_ALIGNMENT)
- .unwrap_or(u64::MAX)
+ fn client_alloc_size(chipset: Chipset) -> Result<u64> {
+ use crate::gpu::Architecture;
+ let size = match chipset.arch() {
+ Architecture::Hopper | Architecture::BlackwellGB10x | Architecture::BlackwellGB20x => {
+ GSP_FW_HEAP_PARAM_CLIENT_ALLOC_SIZE_GH100
+ }
+ _ => u64::from(bindings::GSP_FW_HEAP_PARAM_CLIENT_ALLOC_SIZE),
+ };
+ size.align_up(GSP_HEAP_ALIGNMENT).ok_or(EINVAL)
}
/// Returns the amount of memory to reserve for management purposes for a framebuffer of size
@@ -160,12 +179,26 @@ impl LibosParams {
* u64::SZ_1M,
};
+ /// Hopper/Blackwell+ GPUs need a larger minimum heap size than the bindings specify.
+ /// The r570 bindings set LIBOS3_BAREMETAL_MIN_MB to 88MB, but Hopper/Blackwell+ actually
+ /// requires 170MB (88 + 12 + 70).
+ const LIBOS_HOPPER: LibosParams = LibosParams {
+ carveout_size: num::u32_as_u64(bindings::GSP_FW_HEAP_PARAM_OS_SIZE_LIBOS3_BAREMETAL),
+ allowed_heap_size: GSP_FW_HEAP_SIZE_OVERRIDE_LIBOS3_BAREMETAL_MIN_MB_HOPPER * u64::SZ_1M
+ ..num::u32_as_u64(bindings::GSP_FW_HEAP_SIZE_OVERRIDE_LIBOS3_BAREMETAL_MAX_MB)
+ * u64::SZ_1M,
+ };
+
/// Returns the libos parameters corresponding to `chipset`.
pub(crate) fn from_chipset(chipset: Chipset) -> &'static LibosParams {
- if chipset < Chipset::GA102 {
- &Self::LIBOS2
- } else {
- &Self::LIBOS3
+ use crate::gpu::Architecture;
+ match chipset.arch() {
+ Architecture::Turing => &Self::LIBOS2,
+ Architecture::Ampere if chipset == Chipset::GA100 => &Self::LIBOS2,
+ Architecture::Ampere | Architecture::Ada => &Self::LIBOS3,
+ Architecture::Hopper | Architecture::BlackwellGB10x | Architecture::BlackwellGB20x => {
+ &Self::LIBOS_HOPPER
+ }
}
}
@@ -179,7 +212,7 @@ pub(crate) fn wpr_heap_size(&self, chipset: Chipset, fb_size: u64) -> Result<u64
// RM boot working memory,
.saturating_add(GspFwHeapParams::base_rm_size(chipset))
// One RM client,
- .saturating_add(GspFwHeapParams::client_alloc_size())
+ .saturating_add(GspFwHeapParams::client_alloc_size(chipset)?)
// Overhead for memory management.
.saturating_add(GspFwHeapParams::management_overhead(fb_size)?)
// Clamp to the supported heap sizes.
--
2.53.0
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [PATCH v10 28/28] gpu: nova-core: Hopper/Blackwell: add GSP lockdown release polling
2026-04-11 2:49 [PATCH v10 00/28] gpu: nova-core: firmware: Hopper/Blackwell support John Hubbard
` (26 preceding siblings ...)
2026-04-11 2:49 ` [PATCH v10 27/28] gpu: nova-core: Hopper/Blackwell: larger WPR2 (GSP) heap John Hubbard
@ 2026-04-11 2:49 ` John Hubbard
27 siblings, 0 replies; 30+ messages in thread
From: John Hubbard @ 2026-04-11 2:49 UTC (permalink / raw)
To: Danilo Krummrich, Alexandre Courbot
Cc: Joel Fernandes, Timur Tabi, Alistair Popple, Eliot Courtney,
Shashank Sharma, Zhi Wang, David Airlie, Simona Vetter,
Bjorn Helgaas, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, rust-for-linux, LKML, John Hubbard
On Hopper and Blackwell, FSP boots GSP with hardware lockdown enabled.
After FSP Chain of Trust completes, the driver must poll for lockdown
release before proceeding with GSP initialization. Add the register
bit and helper functions needed for this polling.
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
---
drivers/gpu/nova-core/fsp.rs | 1 -
drivers/gpu/nova-core/gsp/boot.rs | 90 +++++++++++++++++++++++++++++--
drivers/gpu/nova-core/regs.rs | 2 +
3 files changed, 88 insertions(+), 5 deletions(-)
diff --git a/drivers/gpu/nova-core/fsp.rs b/drivers/gpu/nova-core/fsp.rs
index 75e06b2d5f06..db23a1995327 100644
--- a/drivers/gpu/nova-core/fsp.rs
+++ b/drivers/gpu/nova-core/fsp.rs
@@ -278,7 +278,6 @@ pub(crate) fn new(
/// DMA address of the FMC boot parameters, needed after boot for lockdown
/// release polling.
- #[expect(dead_code)]
pub(crate) fn boot_params_dma_handle(&self) -> u64 {
self.fmc_boot_params.dma_handle()
}
diff --git a/drivers/gpu/nova-core/gsp/boot.rs b/drivers/gpu/nova-core/gsp/boot.rs
index 703c9ee48363..d9d8b4e0af06 100644
--- a/drivers/gpu/nova-core/gsp/boot.rs
+++ b/drivers/gpu/nova-core/gsp/boot.rs
@@ -3,8 +3,11 @@
use kernel::{
device,
dma::Coherent,
- io::poll::read_poll_timeout,
- io::Io,
+ io::{
+ poll::read_poll_timeout,
+ register::WithBase, //
+ Io,
+ },
pci,
prelude::*,
time::Delta, //
@@ -54,6 +57,54 @@
vbios::Vbios,
};
+/// GSP lockdown pattern written by firmware to mbox0 while RISC-V branch privilege
+/// lockdown is active. The low byte varies, the upper 24 bits are fixed.
+const GSP_LOCKDOWN_PATTERN: u32 = 0xbadf4100;
+const GSP_LOCKDOWN_MASK: u32 = 0xffffff00;
+
+/// GSP falcon mailbox state, used to track lockdown release status.
+struct GspMbox {
+ mbox0: u32,
+ mbox1: u32,
+}
+
+impl GspMbox {
+ /// Read both mailboxes from the GSP falcon.
+ fn read(gsp_falcon: &Falcon<Gsp>, bar: &Bar0) -> Self {
+ Self {
+ mbox0: gsp_falcon.read_mailbox0(bar),
+ mbox1: gsp_falcon.read_mailbox1(bar),
+ }
+ }
+
+ /// Returns true if the lockdown pattern is present in mbox0.
+ fn is_locked_down(&self) -> bool {
+ self.mbox0 != 0 && (self.mbox0 & GSP_LOCKDOWN_MASK) == GSP_LOCKDOWN_PATTERN
+ }
+
+ /// Combines mailbox0 and mailbox1 into a 64-bit address.
+ fn combined_addr(&self) -> u64 {
+ (u64::from(self.mbox1) << 32) | u64::from(self.mbox0)
+ }
+
+ /// Returns true if GSP lockdown has been released.
+ ///
+ /// Checks the lockdown pattern, validates the boot params address,
+ /// and verifies the HWCFG2 lockdown bit is clear.
+ fn lockdown_released(&self, bar: &Bar0, fmc_boot_params_addr: u64) -> bool {
+ if self.is_locked_down() {
+ return false;
+ }
+
+ if self.mbox0 != 0 && self.combined_addr() != fmc_boot_params_addr {
+ return true;
+ }
+
+ let hwcfg2 = bar.read(regs::NV_PFALCON_FALCON_HWCFG2::of::<Gsp>());
+ !hwcfg2.riscv_br_priv_lockdown()
+ }
+}
+
impl super::Gsp {
/// Helper function to load and run the FWSEC-FRTS firmware and confirm that it has properly
/// created the WPR2 region.
@@ -160,6 +211,34 @@ fn run_booter(
booter.run(dev, bar, sec2_falcon, wpr_meta)
}
+ /// Wait for GSP lockdown to be released after FSP Chain of Trust.
+ fn wait_for_gsp_lockdown_release(
+ dev: &device::Device<device::Bound>,
+ bar: &Bar0,
+ gsp_falcon: &Falcon<Gsp>,
+ fmc_boot_params_addr: u64,
+ ) -> Result {
+ dev_dbg!(dev, "Waiting for GSP lockdown release\n");
+
+ let mbox = read_poll_timeout(
+ || Ok(GspMbox::read(gsp_falcon, bar)),
+ |mbox| mbox.lockdown_released(bar, fmc_boot_params_addr),
+ Delta::from_millis(10),
+ Delta::from_secs(30),
+ )
+ .inspect_err(|_| {
+ dev_err!(dev, "GSP lockdown release timeout\n");
+ })?;
+
+ if mbox.mbox0 != 0 {
+ dev_err!(dev, "GSP-FMC boot failed (mbox: {:#x})\n", mbox.mbox0);
+ return Err(EIO);
+ }
+
+ dev_dbg!(dev, "GSP lockdown released\n");
+ Ok(())
+ }
+
/// Boot GSP via SEC2 booter firmware (Turing/Ampere/Ada path).
///
/// This path uses FWSEC-FRTS to set up WPR2, then boots GSP directly,
@@ -205,7 +284,7 @@ fn boot_via_fsp(
dev: &device::Device<device::Bound>,
bar: &Bar0,
chipset: Chipset,
- _gsp_falcon: &Falcon<Gsp>,
+ gsp_falcon: &Falcon<Gsp>,
wpr_meta: &Coherent<GspFwWprMeta>,
libos: &Coherent<[LibosMemoryRegionInitArgument]>,
) -> Result {
@@ -229,7 +308,10 @@ fn boot_via_fsp(
Fsp::boot_fmc(dev, bar, &fsp_falcon, &args)?;
- Err(ENOTSUPP)
+ let fmc_boot_params_addr = args.boot_params_dma_handle();
+ Self::wait_for_gsp_lockdown_release(dev, bar, gsp_falcon, fmc_boot_params_addr)?;
+
+ Ok(())
}
/// Attempt to boot the GSP.
diff --git a/drivers/gpu/nova-core/regs.rs b/drivers/gpu/nova-core/regs.rs
index 77b11c7de3f8..c7935abde2aa 100644
--- a/drivers/gpu/nova-core/regs.rs
+++ b/drivers/gpu/nova-core/regs.rs
@@ -349,6 +349,8 @@ pub(crate) fn vga_workspace_addr(self) -> Option<u64> {
pub(crate) NV_PFALCON_FALCON_HWCFG2(u32) @ PFalconBase + 0x000000f4 {
/// Signal indicating that reset is completed (GA102+).
31:31 reset_ready => bool;
+ /// RISC-V branch privilege lockdown bit.
+ 13:13 riscv_br_priv_lockdown => bool;
/// Set to 0 after memory scrubbing is completed.
12:12 mem_scrubbing => bool;
10:10 riscv => bool;
--
2.53.0
^ permalink raw reply related [flat|nested] 30+ messages in thread
* Re: [PATCH v10 03/28] gpu: nova-core: Hopper/Blackwell: basic GPU identification
2026-04-11 2:49 ` [PATCH v10 03/28] gpu: nova-core: Hopper/Blackwell: basic GPU identification John Hubbard
@ 2026-04-11 3:58 ` Timur Tabi
0 siblings, 0 replies; 30+ messages in thread
From: Timur Tabi @ 2026-04-11 3:58 UTC (permalink / raw)
To: Alexandre Courbot, dakr@kernel.org, John Hubbard
Cc: aliceryhl@google.com, lossin@kernel.org, Shashank Sharma,
boqun.feng@gmail.com, a.hindborg@kernel.org, Zhi Wang,
simona@ffwll.ch, alex.gaynor@gmail.com, ojeda@kernel.org,
tmgross@umich.edu, linux-kernel@vger.kernel.org,
rust-for-linux@vger.kernel.org, bjorn3_gh@protonmail.com,
Eliot Courtney, airlied@gmail.com, Joel Fernandes,
bhelgaas@google.com, gary@garyguo.net, Alistair Popple
On Fri, 2026-04-10 at 19:49 -0700, John Hubbard wrote:
> + | Architecture::BlackwellGB10x
> + | Architecture::BlackwellGB20x => {
Can you add something to the patch description that explains why Blackwell, unlike all of the
olther architectures, is represented by two Architecture enums?
^ permalink raw reply [flat|nested] 30+ messages in thread
end of thread, other threads:[~2026-04-11 3:58 UTC | newest]
Thread overview: 30+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-11 2:49 [PATCH v10 00/28] gpu: nova-core: firmware: Hopper/Blackwell support John Hubbard
2026-04-11 2:49 ` [PATCH v10 01/28] gpu: nova-core: factor .fwsignature* selection into a new find_gsp_sigs_section() John Hubbard
2026-04-11 2:49 ` [PATCH v10 02/28] gpu: nova-core: use GPU Architecture to simplify HAL selections John Hubbard
2026-04-11 2:49 ` [PATCH v10 03/28] gpu: nova-core: Hopper/Blackwell: basic GPU identification John Hubbard
2026-04-11 3:58 ` Timur Tabi
2026-04-11 2:49 ` [PATCH v10 04/28] gpu: nova-core: add Copy/Clone to Spec and Revision John Hubbard
2026-04-11 2:49 ` [PATCH v10 05/28] gpu: nova-core: set DMA mask width based on GPU architecture John Hubbard
2026-04-11 2:49 ` [PATCH v10 06/28] gpu: nova-core: move GFW boot wait into a GPU HAL John Hubbard
2026-04-11 2:49 ` [PATCH v10 07/28] gpu: nova-core: Hopper/Blackwell: skip GFW boot waiting John Hubbard
2026-04-11 2:49 ` [PATCH v10 08/28] gpu: nova-core: Blackwell: calculate reserved FB heap size John Hubbard
2026-04-11 2:49 ` [PATCH v10 09/28] gpu: nova-core: Hopper/Blackwell: new location for PCI config mirror John Hubbard
2026-04-11 2:49 ` [PATCH v10 10/28] gpu: nova-core: refactor SEC2 booter loading into BooterFirmware::run() John Hubbard
2026-04-11 2:49 ` [PATCH v10 11/28] gpu: nova-core: Hopper/Blackwell: integrate FSP boot path into boot() John Hubbard
2026-04-11 2:49 ` [PATCH v10 12/28] gpu: nova-core: don't assume 64-bit firmware images John Hubbard
2026-04-11 2:49 ` [PATCH v10 13/28] gpu: nova-core: add support for 32-bit " John Hubbard
2026-04-11 2:49 ` [PATCH v10 14/28] gpu: nova-core: add auto-detection of 32-bit, 64-bit " John Hubbard
2026-04-11 2:49 ` [PATCH v10 15/28] gpu: nova-core: Hopper/Blackwell: add FSP falcon engine stub John Hubbard
2026-04-11 2:49 ` [PATCH v10 16/28] gpu: nova-core: Hopper/Blackwell: add FMC firmware image, in support of FSP John Hubbard
2026-04-11 2:49 ` [PATCH v10 17/28] gpu: nova-core: Hopper/Blackwell: add FSP secure boot completion waiting John Hubbard
2026-04-11 2:49 ` [PATCH v10 18/28] gpu: nova-core: Hopper/Blackwell: add FMC signature extraction John Hubbard
2026-04-11 2:49 ` [PATCH v10 19/28] gpu: nova-core: Hopper/Blackwell: add FSP falcon EMEM operations John Hubbard
2026-04-11 2:49 ` [PATCH v10 20/28] gpu: nova-core: Hopper/Blackwell: add FSP message infrastructure John Hubbard
2026-04-11 2:49 ` [PATCH v10 21/28] gpu: nova-core: add MCTP/NVDM protocol types for firmware communication John Hubbard
2026-04-11 2:49 ` [PATCH v10 22/28] gpu: nova-core: Hopper/Blackwell: add FSP send/receive messaging John Hubbard
2026-04-11 2:49 ` [PATCH v10 23/28] gpu: nova-core: Hopper/Blackwell: add FspCotVersion type John Hubbard
2026-04-11 2:49 ` [PATCH v10 24/28] gpu: nova-core: Hopper/Blackwell: larger non-WPR heap John Hubbard
2026-04-11 2:49 ` [PATCH v10 25/28] gpu: nova-core: Hopper/Blackwell: add FSP Chain of Trust boot John Hubbard
2026-04-11 2:49 ` [PATCH v10 26/28] gpu: nova-core: Blackwell: use correct sysmem flush registers John Hubbard
2026-04-11 2:49 ` [PATCH v10 27/28] gpu: nova-core: Hopper/Blackwell: larger WPR2 (GSP) heap John Hubbard
2026-04-11 2:49 ` [PATCH v10 28/28] gpu: nova-core: Hopper/Blackwell: add GSP lockdown release polling John Hubbard
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox