xref: /linux/drivers/gpu/nova-core/firmware/fsp.rs (revision 9611c0ce215a66770ccbe5c126bf57ba8c31bcad)
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