From: John Hubbard Date: Tue, 2 Jun 2026 03:21:01 +0000 (-0700) Subject: gpu: nova-core: Hopper/Blackwell: add FMC signature extraction X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4d789488d3bec3e2c6b7f282e29cfb1bec6b2c25;p=thirdparty%2Fkernel%2Fstable.git gpu: nova-core: Hopper/Blackwell: add FMC signature extraction Extract the SHA-384 hash, RSA public key, and RSA signature from the FMC ELF32 firmware sections. FSP Chain of Trust verification needs these to validate the FMC image during boot. Signed-off-by: John Hubbard Reviewed-by: Eliot Courtney Link: https://patch.msgid.link/20260602032111.224790-14-jhubbard@nvidia.com [acourbot: derive `Zeroable` on `FmcSignature` for in-place initialization] Co-developed-by: Alexandre Courbot Signed-off-by: Alexandre Courbot --- diff --git a/drivers/gpu/nova-core/firmware/fsp.rs b/drivers/gpu/nova-core/firmware/fsp.rs index 011be1e571c2..9b211426a75a 100644 --- a/drivers/gpu/nova-core/firmware/fsp.rs +++ b/drivers/gpu/nova-core/firmware/fsp.rs @@ -15,13 +15,35 @@ use crate::{ 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. +/// +/// The FMC ELF `publickey` section 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 FMC ELF `signature` section may be shorter, so the remaining bytes are zero-padded. +const FSP_SIG_SIZE: usize = 384; + +/// Structure to hold FMC signatures. +/// +/// C representation is used because this type is used for communication with the FSP. +#[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) struct FspFirmware { /// FMC firmware image data (only the "image" ELF section). #[expect(dead_code)] pub(crate) fmc_image: Coherent<[u8]>, - /// Full FMC ELF for signature extraction. + /// FMC firmware signatures. #[expect(dead_code)] - pub(crate) fmc_elf: Firmware, + pub(crate) fmc_sigs: KBox, } impl FspFirmware { @@ -41,7 +63,68 @@ impl FspFirmware { Ok(Self { fmc_image, - fmc_elf: fw, + fmc_sigs: Self::extract_fmc_signatures(&fw, dev)?, }) } + + /// 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. + fn extract_fmc_signatures( + fmc_fw: &Firmware, + dev: &device::Device, + ) -> Result> { + let get_section = |name: &str, max_len: usize| { + elf::elf_section(fmc_fw.data(), name) + .ok_or(EINVAL) + .inspect_err(|_| dev_err!(dev, "FMC firmware missing '{}' section\n", name)) + .and_then(|section| { + if section.len() > max_len { + dev_err!( + dev, + "FMC {} section size {} > maximum {}\n", + name, + section.len(), + max_len + ); + Err(EINVAL) + } else { + Ok(section) + } + }) + }; + + let hash_section = get_section("hash", FSP_HASH_SIZE)?; + let pkey_section = get_section("publickey", FSP_PKEY_SIZE)?; + let sig_section = get_section("signature", FSP_SIG_SIZE)?; + + // The hash section is a SHA-384 output: it must be exactly FSP_HASH_SIZE bytes. + 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); + } + + // Initialize the signatures in place to avoid building the large `FmcSignatures` on the + // stack, then fill each section from the firmware. + let signatures = KBox::init( + pin_init::init_zeroed::().chain(|sigs| { + // PANIC: src and dst lengths are both FSP_HASH_SIZE (verified above). + sigs.hash384.copy_from_slice(hash_section); + // PANIC: dst is sliced to src.len(); src.len() <= FSP_PKEY_SIZE per `get_section`. + sigs.public_key[..pkey_section.len()].copy_from_slice(pkey_section); + // PANIC: dst is sliced to src.len(); src.len() <= FSP_SIG_SIZE per `get_section`. + sigs.signature[..sig_section.len()].copy_from_slice(sig_section); + Ok(()) + }), + GFP_KERNEL, + )?; + + Ok(signatures) + } }