[PATCH v7 20/31] gpu: nova-core: Hopper/Blackwell: add FMC signature extraction
From: John Hubbard
Date: Tue Mar 17 2026 - 18:57:20 EST
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@xxxxxxxxxx>
---
drivers/gpu/nova-core/firmware.rs | 3 +-
drivers/gpu/nova-core/fsp.rs | 79 +++++++++++++++++++++++++++++++
2 files changed, 81 insertions(+), 1 deletion(-)
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 d464ad325881..a13d883373f0 100644
--- a/drivers/gpu/nova-core/fsp.rs
+++ b/drivers/gpu/nova-core/fsp.rs
@@ -105,6 +105,18 @@ 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
+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;
@@ -138,4 +150,71 @@ 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.
+ #[expect(dead_code)]
+ 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)
+ }
}
--
2.53.0