[PATCH] gpu: nova-core: fix incorrect naming/framing of GSP-FMC firmware

Alexandre Courbot <[email protected]>
Newsgroups org.kernel.vger.rust-for-linux,dev.linux.lists.nova-gpu,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
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 <[email protected]>
---
 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 <[email protected]>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.