14257d317SJohn Hubbard // SPDX-License-Identifier: GPL-2.0 24257d317SJohn Hubbard // SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 34257d317SJohn Hubbard 44257d317SJohn Hubbard //! FSP is a hardware unit that runs FMC firmware. 54257d317SJohn Hubbard 64257d317SJohn Hubbard use kernel::{ 74257d317SJohn Hubbard device, 84257d317SJohn Hubbard dma::Coherent, 94257d317SJohn Hubbard firmware::Firmware, 104257d317SJohn Hubbard prelude::*, // 114257d317SJohn Hubbard }; 124257d317SJohn Hubbard 134257d317SJohn Hubbard use crate::{ 144257d317SJohn Hubbard firmware::elf, 154257d317SJohn Hubbard gpu::Chipset, // 164257d317SJohn Hubbard }; 174257d317SJohn Hubbard 18*4d789488SJohn Hubbard /// Size of the FSP SHA-384 hash, in bytes. 19*4d789488SJohn Hubbard const FSP_HASH_SIZE: usize = 48; 20*4d789488SJohn Hubbard /// Maximum size of the FSP public key (RSA-3072), in bytes. 21*4d789488SJohn Hubbard /// 22*4d789488SJohn Hubbard /// The FMC ELF `publickey` section may be shorter, so the remaining bytes are zero-padded. 23*4d789488SJohn Hubbard const FSP_PKEY_SIZE: usize = 384; 24*4d789488SJohn Hubbard /// Maximum size of the FSP signature (RSA-3072), in bytes. 25*4d789488SJohn Hubbard /// 26*4d789488SJohn Hubbard /// The FMC ELF `signature` section may be shorter, so the remaining bytes are zero-padded. 27*4d789488SJohn Hubbard const FSP_SIG_SIZE: usize = 384; 28*4d789488SJohn Hubbard 29*4d789488SJohn Hubbard /// Structure to hold FMC signatures. 30*4d789488SJohn Hubbard /// 31*4d789488SJohn Hubbard /// C representation is used because this type is used for communication with the FSP. 32*4d789488SJohn Hubbard #[derive(Debug, Clone, Copy, Zeroable)] 33*4d789488SJohn Hubbard #[repr(C)] 34*4d789488SJohn Hubbard pub(crate) struct FmcSignatures { 35*4d789488SJohn Hubbard pub(crate) hash384: [u8; FSP_HASH_SIZE], 36*4d789488SJohn Hubbard pub(crate) public_key: [u8; FSP_PKEY_SIZE], 37*4d789488SJohn Hubbard pub(crate) signature: [u8; FSP_SIG_SIZE], 38*4d789488SJohn Hubbard } 39*4d789488SJohn Hubbard 404257d317SJohn Hubbard pub(crate) struct FspFirmware { 414257d317SJohn Hubbard /// FMC firmware image data (only the "image" ELF section). 424257d317SJohn Hubbard pub(crate) fmc_image: Coherent<[u8]>, 43*4d789488SJohn Hubbard /// FMC firmware signatures. 44*4d789488SJohn Hubbard pub(crate) fmc_sigs: KBox<FmcSignatures>, 454257d317SJohn Hubbard } 464257d317SJohn Hubbard 474257d317SJohn Hubbard impl FspFirmware { 484257d317SJohn Hubbard pub(crate) fn new( 494257d317SJohn Hubbard dev: &device::Device<device::Bound>, 504257d317SJohn Hubbard chipset: Chipset, 514257d317SJohn Hubbard ver: &str, 524257d317SJohn Hubbard ) -> Result<Self> { 534257d317SJohn Hubbard let fw = super::request_firmware(dev, chipset, "fmc", ver)?; 544257d317SJohn Hubbard 554257d317SJohn Hubbard // FSP expects only the "image" section, not the entire ELF file. 564257d317SJohn Hubbard let fmc_image_data = elf::elf_section(fw.data(), "image").ok_or_else(|| { 574257d317SJohn Hubbard dev_err!(dev, "FMC ELF file missing 'image' section\n"); 584257d317SJohn Hubbard EINVAL 594257d317SJohn Hubbard })?; 604257d317SJohn Hubbard let fmc_image = Coherent::from_slice(dev, fmc_image_data, GFP_KERNEL)?; 614257d317SJohn Hubbard 624257d317SJohn Hubbard Ok(Self { 634257d317SJohn Hubbard fmc_image, 64*4d789488SJohn Hubbard fmc_sigs: Self::extract_fmc_signatures(&fw, dev)?, 654257d317SJohn Hubbard }) 664257d317SJohn Hubbard } 67*4d789488SJohn Hubbard 68*4d789488SJohn Hubbard /// Extract FMC firmware signatures for Chain of Trust verification. 69*4d789488SJohn Hubbard /// 70*4d789488SJohn Hubbard /// Extracts real cryptographic signatures from FMC ELF32 firmware sections. 71*4d789488SJohn Hubbard /// Returns signatures in a heap-allocated structure to prevent stack overflow. 72*4d789488SJohn Hubbard fn extract_fmc_signatures( 73*4d789488SJohn Hubbard fmc_fw: &Firmware, 74*4d789488SJohn Hubbard dev: &device::Device, 75*4d789488SJohn Hubbard ) -> Result<KBox<FmcSignatures>> { 76*4d789488SJohn Hubbard let get_section = |name: &str, max_len: usize| { 77*4d789488SJohn Hubbard elf::elf_section(fmc_fw.data(), name) 78*4d789488SJohn Hubbard .ok_or(EINVAL) 79*4d789488SJohn Hubbard .inspect_err(|_| dev_err!(dev, "FMC firmware missing '{}' section\n", name)) 80*4d789488SJohn Hubbard .and_then(|section| { 81*4d789488SJohn Hubbard if section.len() > max_len { 82*4d789488SJohn Hubbard dev_err!( 83*4d789488SJohn Hubbard dev, 84*4d789488SJohn Hubbard "FMC {} section size {} > maximum {}\n", 85*4d789488SJohn Hubbard name, 86*4d789488SJohn Hubbard section.len(), 87*4d789488SJohn Hubbard max_len 88*4d789488SJohn Hubbard ); 89*4d789488SJohn Hubbard Err(EINVAL) 90*4d789488SJohn Hubbard } else { 91*4d789488SJohn Hubbard Ok(section) 92*4d789488SJohn Hubbard } 93*4d789488SJohn Hubbard }) 94*4d789488SJohn Hubbard }; 95*4d789488SJohn Hubbard 96*4d789488SJohn Hubbard let hash_section = get_section("hash", FSP_HASH_SIZE)?; 97*4d789488SJohn Hubbard let pkey_section = get_section("publickey", FSP_PKEY_SIZE)?; 98*4d789488SJohn Hubbard let sig_section = get_section("signature", FSP_SIG_SIZE)?; 99*4d789488SJohn Hubbard 100*4d789488SJohn Hubbard // The hash section is a SHA-384 output: it must be exactly FSP_HASH_SIZE bytes. 101*4d789488SJohn Hubbard if hash_section.len() != FSP_HASH_SIZE { 102*4d789488SJohn Hubbard dev_err!( 103*4d789488SJohn Hubbard dev, 104*4d789488SJohn Hubbard "FMC hash section size {} != expected {}\n", 105*4d789488SJohn Hubbard hash_section.len(), 106*4d789488SJohn Hubbard FSP_HASH_SIZE 107*4d789488SJohn Hubbard ); 108*4d789488SJohn Hubbard return Err(EINVAL); 109*4d789488SJohn Hubbard } 110*4d789488SJohn Hubbard 111*4d789488SJohn Hubbard // Initialize the signatures in place to avoid building the large `FmcSignatures` on the 112*4d789488SJohn Hubbard // stack, then fill each section from the firmware. 113*4d789488SJohn Hubbard let signatures = KBox::init( 114*4d789488SJohn Hubbard pin_init::init_zeroed::<FmcSignatures>().chain(|sigs| { 115*4d789488SJohn Hubbard // PANIC: src and dst lengths are both FSP_HASH_SIZE (verified above). 116*4d789488SJohn Hubbard sigs.hash384.copy_from_slice(hash_section); 117*4d789488SJohn Hubbard // PANIC: dst is sliced to src.len(); src.len() <= FSP_PKEY_SIZE per `get_section`. 118*4d789488SJohn Hubbard sigs.public_key[..pkey_section.len()].copy_from_slice(pkey_section); 119*4d789488SJohn Hubbard // PANIC: dst is sliced to src.len(); src.len() <= FSP_SIG_SIZE per `get_section`. 120*4d789488SJohn Hubbard sigs.signature[..sig_section.len()].copy_from_slice(sig_section); 121*4d789488SJohn Hubbard Ok(()) 122*4d789488SJohn Hubbard }), 123*4d789488SJohn Hubbard GFP_KERNEL, 124*4d789488SJohn Hubbard )?; 125*4d789488SJohn Hubbard 126*4d789488SJohn Hubbard Ok(signatures) 127*4d789488SJohn Hubbard } 1284257d317SJohn Hubbard } 129