The Linux Kernel Mailing List
 help / color / mirror / Atom feed
* [PATCH] gpu: nova-core: fix incorrect naming/framing of GSP-FMC firmware
@ 2026-08-25  8:31 Alexandre Courbot
  2026-08-25 19:40 ` Danilo Krummrich
  2026-08-25 23:21 ` Eliot Courtney
  0 siblings, 2 replies; 4+ messages in thread
From: Alexandre Courbot @ 2026-08-25  8:31 UTC (permalink / raw)
  To: Danilo Krummrich, David Airlie, Simona Vetter, Alice Ryhl,
	Benno Lossin, Gary Guo
  Cc: John Hubbard, Alistair Popple, Timur Tabi, Eliot Courtney,
	Zhi Wang, nova-gpu, linux-kernel, rust-for-linux,
	Alexandre Courbot

The GSP-FMC firmware code lived in `firmware/fsp.rs` and described
itself as running on the FSP. This is incorrect: GSP-FMC is loaded by
the FSP, but runs on the GSP as the loader and verifier of GSP-RM. FSP
secure-boots on its own before the driver even starts.

Correct the naming and files used to contain the GSP-FMC code, by:

- Renaming `firmware/fsp.rs` to `firmware/gsp_fmc.rs` (as GSP-FMC is not
  the FSP firmware),
- Changing the prefix of some declarations in that file to `FMC` or
  `GspFmc` (interestingly some were already correct),
- Fixing a mismatch (FMC means "First Mutable Code") in `fsp.rst`.

Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
---
 Documentation/gpu/nova/core/fsp.rst                |  2 +-
 drivers/gpu/nova-core/firmware.rs                  |  2 +-
 .../gpu/nova-core/firmware/{fsp.rs => gsp_fmc.rs}  | 55 ++++++++++++----------
 drivers/gpu/nova-core/fsp.rs                       | 29 +++++++-----
 4 files changed, 48 insertions(+), 40 deletions(-)

diff --git a/Documentation/gpu/nova/core/fsp.rst b/Documentation/gpu/nova/core/fsp.rst
index 52d618d22bb8..cc2e3bbb9b53 100644
--- a/Documentation/gpu/nova/core/fsp.rst
+++ b/Documentation/gpu/nova/core/fsp.rst
@@ -42,7 +42,7 @@ On **Hopper/Blackwell** GPUs, FSP replaces this multi-stage process with a
 single message-driven interface::
 
      FSP (hardware root of trust, boots from ROM)
-          -> FMC (Falcon Microcontroller, verified by FSP)
+          -> FMC (First Mutable Code, verified by FSP)
                -> GSP-RM (verified and loaded by FMC)
 
 The driver only needs to:
diff --git a/drivers/gpu/nova-core/firmware.rs b/drivers/gpu/nova-core/firmware.rs
index b49613a90bf0..c16fee6e2b2a 100644
--- a/drivers/gpu/nova-core/firmware.rs
+++ b/drivers/gpu/nova-core/firmware.rs
@@ -23,9 +23,9 @@
 };
 
 pub(crate) mod booter;
-pub(crate) mod fsp;
 pub(crate) mod fwsec;
 pub(crate) mod gsp;
+pub(crate) mod gsp_fmc;
 pub(crate) mod riscv;
 pub(crate) mod tlv;
 
diff --git a/drivers/gpu/nova-core/firmware/fsp.rs b/drivers/gpu/nova-core/firmware/gsp_fmc.rs
similarity index 71%
rename from drivers/gpu/nova-core/firmware/fsp.rs
rename to drivers/gpu/nova-core/firmware/gsp_fmc.rs
index 5462e318410a..fbb0af133f24 100644
--- a/drivers/gpu/nova-core/firmware/fsp.rs
+++ b/drivers/gpu/nova-core/firmware/gsp_fmc.rs
@@ -1,7 +1,8 @@
 // SPDX-License-Identifier: GPL-2.0
 // SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
 
-//! FSP is a hardware unit that runs FMC firmware.
+//! GSP-FMC (First Mutable Code) is loaded by FSP into GSP to serve as the loader and verifier of
+//! GSP-RM.
 
 use kernel::{
     device,
@@ -17,16 +18,16 @@
     gpu::Chipset, //
 };
 
-/// Size of the FSP SHA-384 hash, in bytes.
-const FSP_HASH_SIZE: usize = 48;
-/// Maximum size of the FSP public key (RSA-3072), in bytes.
+/// Size of the GSP-FMC SHA-384 hash, in bytes.
+const FMC_HASH_SIZE: usize = 48;
+/// Maximum size of the GSP-FMC public key (RSA-3072), in bytes.
 ///
-/// The FMC `PKEY` tag may be shorter, so the remaining bytes are zero-padded.
-const FSP_PKEY_SIZE: usize = 384;
-/// Maximum size of the FSP signature (RSA-3072), in bytes.
+/// The `PKEY` tag may be shorter, so the remaining bytes are zero-padded.
+const FMC_PKEY_SIZE: usize = 384;
+/// Maximum size of the GSP-FMC signature (RSA-3072), in bytes.
 ///
-/// The FMC `SIGN` tag may be shorter, so the remaining bytes are zero-padded.
-const FSP_SIG_SIZE: usize = 384;
+/// The `SIGN` tag may be shorter, so the remaining bytes are zero-padded.
+const FMC_SIG_SIZE: usize = 384;
 
 /// Structure to hold FMC signatures.
 ///
@@ -34,23 +35,27 @@
 #[derive(Debug, Clone, Copy, Zeroable)]
 #[repr(C)]
 pub(crate) struct FmcSignatures {
-    pub(crate) hash384: [u8; FSP_HASH_SIZE],
-    pub(crate) public_key: [u8; FSP_PKEY_SIZE],
-    pub(crate) signature: [u8; FSP_SIG_SIZE],
+    pub(crate) hash384: [u8; FMC_HASH_SIZE],
+    pub(crate) public_key: [u8; FMC_PKEY_SIZE],
+    pub(crate) signature: [u8; FMC_SIG_SIZE],
 }
 
-pub(crate) struct FspFirmware {
+pub(crate) struct GspFmcFirmware {
     /// FMC firmware image data
     pub(crate) fmc_image: Coherent<[u8]>,
     /// FMC firmware signatures.
     pub(crate) fmc_sigs: KBox<FmcSignatures>,
 }
 
-impl FspFirmware {
+impl GspFmcFirmware {
     pub(crate) fn new(dev: &device::Device<device::Bound>, chipset: Chipset) -> Result<Self> {
         let fw = request_tlv(dev, chipset, "fmc")?;
         let tlv = Tlv::new(fw.data())?;
-        dev_dbg!(dev, "loaded fsp firmware v{}\n", tlv.get_string(b"VERS")?);
+        dev_dbg!(
+            dev,
+            "loaded GSP-FMC firmware v{}\n",
+            tlv.get_string(b"VERS")?
+        );
 
         let fmc_image_data = tlv.get_bytes(b"BLOB")?;
         let fmc_image = Coherent::from_slice(dev, fmc_image_data, GFP_KERNEL)?;
@@ -70,34 +75,34 @@ fn extract_fmc_signatures(tlv: &Tlv<'_>, dev: &device::Device) -> Result<KBox<Fm
         let pkey_section = tlv.get_bytes(b"PKEY")?;
         let sig_section = tlv.get_bytes(b"SIGN")?;
 
-        // The hash section is a SHA-384 output: it must be exactly FSP_HASH_SIZE bytes.
-        if hash_section.len() != FSP_HASH_SIZE {
+        // The hash section is a SHA-384 output: it must be exactly `FMC_HASH_SIZE` bytes.
+        if hash_section.len() != FMC_HASH_SIZE {
             dev_err!(
                 dev,
                 "FMC hash section size {} != expected {}\n",
                 hash_section.len(),
-                FSP_HASH_SIZE
+                FMC_HASH_SIZE
             );
             return Err(EINVAL);
         }
 
         // The key and signature sections are zero-padded to a fixed maximum, so they may be
         // shorter, but must not exceed the destination buffers.
-        if pkey_section.len() > FSP_PKEY_SIZE {
+        if pkey_section.len() > FMC_PKEY_SIZE {
             dev_err!(
                 dev,
                 "FMC public key section size {} > maximum {}\n",
                 pkey_section.len(),
-                FSP_PKEY_SIZE
+                FMC_PKEY_SIZE
             );
             return Err(EINVAL);
         }
-        if sig_section.len() > FSP_SIG_SIZE {
+        if sig_section.len() > FMC_SIG_SIZE {
             dev_err!(
                 dev,
                 "FMC signature section size {} > maximum {}\n",
                 sig_section.len(),
-                FSP_SIG_SIZE
+                FMC_SIG_SIZE
             );
             return Err(EINVAL);
         }
@@ -106,11 +111,11 @@ fn extract_fmc_signatures(tlv: &Tlv<'_>, dev: &device::Device) -> Result<KBox<Fm
         // stack, then fill each section from the firmware.
         let signatures = KBox::init(
             pin_init::init_zeroed::<FmcSignatures>().chain(|sigs| {
-                // PANIC: src and dst lengths are both FSP_HASH_SIZE (verified above).
+                // PANIC: src and dst lengths are both `FMC_HASH_SIZE` (verified above).
                 sigs.hash384.copy_from_slice(hash_section);
-                // PANIC: dst is sliced to src.len(); src.len() <= FSP_PKEY_SIZE (verified above).
+                // PANIC: dst is sliced to src.len(); src.len() <= `FMC_PKEY_SIZE` (verified above).
                 sigs.public_key[..pkey_section.len()].copy_from_slice(pkey_section);
-                // PANIC: dst is sliced to src.len(); src.len() <= FSP_SIG_SIZE (verified above).
+                // PANIC: dst is sliced to src.len(); src.len() <= `FMC_SIG_SIZE` (verified above).
                 sigs.signature[..sig_section.len()].copy_from_slice(sig_section);
                 Ok(())
             }),
diff --git a/drivers/gpu/nova-core/fsp.rs b/drivers/gpu/nova-core/fsp.rs
index ab685fb4168f..d2be19b8bff9 100644
--- a/drivers/gpu/nova-core/fsp.rs
+++ b/drivers/gpu/nova-core/fsp.rs
@@ -3,9 +3,12 @@
 
 //! FSP (Foundation Security Processor) interface for Hopper/Blackwell GPUs.
 //!
-//! Hopper/Blackwell use a simplified firmware boot sequence: FMC, then FSP, then GSP.
+//! Hopper/Blackwell use a simplified firmware boot sequence: FSP secure-boots independently before
+//! the driver starts. The driver then sends FSP a Chain-of-Trust request containing the GSP-FMC
+//! image. FSP authenticates the image and launches GSP-FMC on the GSP RISC-V core; GSP-FMC
+//! subsequently authenticates and boots GSP-RM.
+//!
 //! Unlike Turing/Ampere/Ada, there is no SEC2 (Security Engine 2) usage.
-//! FSP handles secure boot directly using FMC firmware and Chain of Trust.
 
 use kernel::{
     device,
@@ -32,9 +35,9 @@
         Falcon, //
     },
     fb::FbSizes,
-    firmware::fsp::{
+    firmware::gsp_fmc::{
         FmcSignatures,
-        FspFirmware, //
+        GspFmcFirmware, //
     },
     gpu::Chipset,
     gsp::{
@@ -267,7 +270,7 @@ fn frts_vidmem_offset(hal: &dyn hal::FspHal, fb_info: &FbSizes) -> Result<u64> {
     /// Returns an in-place initializer for [`FspCotMessage`].
     fn new<'a>(
         fb_info: &FbSizes,
-        fsp_fw: &'a FspFirmware,
+        fmc_fw: &'a GspFmcFirmware,
         args: &'a FmcBootArgs<'_>,
     ) -> Result<impl Init<Self> + 'a> {
         let hal = hal::fsp_hal(args.chipset).ok_or(ENOTSUPP)?;
@@ -296,13 +299,13 @@ fn new<'a>(
         .chain(move |msg| {
             msg.cot.version = version;
             msg.cot.size = size;
-            msg.cot.gsp_fmc_sysmem_offset = fsp_fw.fmc_image.dma_address();
+            msg.cot.gsp_fmc_sysmem_offset = fmc_fw.fmc_image.dma_address();
             msg.cot.frts_vidmem_offset = frts_vidmem_offset;
             msg.cot.frts_vidmem_size = frts_size;
             // frts_sysmem_* are left at zero because this path places FRTS in vidmem. The sysmem
             // fields point to an FRTS buffer in sysmem instead, for systems without VRAM.
             msg.cot.gsp_boot_args_sysmem_offset = args.fmc_boot_params.dma_address();
-            msg.cot.sigs = *fsp_fw.fmc_sigs;
+            msg.cot.sigs = *fmc_fw.fmc_sigs;
 
             Ok(())
         }))
@@ -381,12 +384,12 @@ pub(crate) fn boot_params(&self) -> &Coherent<GspFmcBootParams> {
 
 /// FSP interface for Hopper/Blackwell GPUs.
 ///
-/// An `Fsp` is produced by [`Fsp::wait_secure_boot`], which only returns once FSP secure boot
-/// has completed. It owns the FSP falcon and the FMC firmware, which are used for the subsequent
+/// An `Fsp` is produced by [`Fsp::wait_secure_boot`], which only returns once FSP secure boot has
+/// completed. It owns the FSP falcon and the GSP-FMC firmware, which are used for the subsequent
 /// Chain of Trust boot.
 pub(crate) struct Fsp<'a> {
     falcon: Falcon<'a, FspEngine>,
-    fsp_fw: FspFirmware,
+    fmc_fw: GspFmcFirmware,
 }
 
 impl<'a> Fsp<'a> {
@@ -422,7 +425,7 @@ fn wait_secure_boot(
         const FSP_SECURE_BOOT_TIMEOUT_MS: i64 = 5000;
 
         let falcon = Falcon::<FspEngine>::new(dev, chipset, bar)?;
-        let fsp_fw = FspFirmware::new(dev, chipset)?;
+        let fmc_fw = GspFmcFirmware::new(dev, chipset)?;
 
         read_poll_timeout(
             || Ok(hal.fsp_boot_status(bar)),
@@ -434,7 +437,7 @@ fn wait_secure_boot(
             dev_err!(dev, "FSP secure boot completion error: {:?}\n", e);
         })?;
 
-        Ok(Fsp { falcon, fsp_fw })
+        Ok(Fsp { falcon, fmc_fw })
     }
 
     /// Sends a message to FSP and waits for the response.
@@ -540,7 +543,7 @@ pub(crate) fn boot_fmc(
     ) -> Result {
         dev_dbg!(dev, "Starting FSP boot sequence for {}\n", args.chipset);
 
-        let msg = KBox::init(FspCotMessage::new(fb_info, &self.fsp_fw, args)?, GFP_KERNEL)?;
+        let msg = KBox::init(FspCotMessage::new(fb_info, &self.fmc_fw, args)?, GFP_KERNEL)?;
 
         let _response_buf = self.send_sync_fsp(dev, &*msg)?;
 

---
base-commit: 66498c75b4f8017f62d720d9b59675bdf3abce91
change-id: 20260723-gsp-fmc-rename-da0a5019f441

Best regards,
--  
Alexandre Courbot <acourbot@nvidia.com>


^ permalink raw reply related	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2026-08-26  8:14 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-25  8:31 [PATCH] gpu: nova-core: fix incorrect naming/framing of GSP-FMC firmware Alexandre Courbot
2026-08-25 19:40 ` Danilo Krummrich
2026-08-26  8:14   ` Zhi Wang
2026-08-25 23:21 ` Eliot Courtney

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox