// 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. use kernel::{ device, dma::Coherent, firmware::Firmware, prelude::*, // }; use crate::{ firmware::elf, 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). pub(crate) fmc_image: Coherent<[u8]>, /// FMC firmware signatures. pub(crate) fmc_sigs: KBox, } impl FspFirmware { pub(crate) fn new( dev: &device::Device, chipset: Chipset, ver: &str, ) -> Result { 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_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) } }