xref: /linux/drivers/gpu/nova-core/firmware/fsp.rs (revision a355d8142f343cffd28ff0f97c251a0334d3c0b3)
1 // SPDX-License-Identifier: GPL-2.0
2 // SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3 
4 //! FSP is a hardware unit that runs FMC firmware.
5 
6 use kernel::{
7     device,
8     dma::Coherent,
9     firmware::Firmware,
10     prelude::*, //
11 };
12 
13 use crate::{
14     firmware::elf,
15     gpu::Chipset, //
16 };
17 
18 /// Size of the FSP SHA-384 hash, in bytes.
19 const FSP_HASH_SIZE: usize = 48;
20 /// Maximum size of the FSP public key (RSA-3072), in bytes.
21 ///
22 /// The FMC ELF `publickey` section may be shorter, so the remaining bytes are zero-padded.
23 const FSP_PKEY_SIZE: usize = 384;
24 /// Maximum size of the FSP signature (RSA-3072), in bytes.
25 ///
26 /// The FMC ELF `signature` section may be shorter, so the remaining bytes are zero-padded.
27 const FSP_SIG_SIZE: usize = 384;
28 
29 /// Structure to hold FMC signatures.
30 ///
31 /// C representation is used because this type is used for communication with the FSP.
32 #[derive(Debug, Clone, Copy, Zeroable)]
33 #[repr(C)]
34 pub(crate) struct FmcSignatures {
35     pub(crate) hash384: [u8; FSP_HASH_SIZE],
36     pub(crate) public_key: [u8; FSP_PKEY_SIZE],
37     pub(crate) signature: [u8; FSP_SIG_SIZE],
38 }
39 
40 pub(crate) struct FspFirmware {
41     /// FMC firmware image data (only the "image" ELF section).
42     #[expect(dead_code)]
43     pub(crate) fmc_image: Coherent<[u8]>,
44     /// FMC firmware signatures.
45     #[expect(dead_code)]
46     pub(crate) fmc_sigs: KBox<FmcSignatures>,
47 }
48 
49 impl FspFirmware {
50     pub(crate) fn new(
51         dev: &device::Device<device::Bound>,
52         chipset: Chipset,
53         ver: &str,
54     ) -> Result<Self> {
55         let fw = super::request_firmware(dev, chipset, "fmc", ver)?;
56 
57         // FSP expects only the "image" section, not the entire ELF file.
58         let fmc_image_data = elf::elf_section(fw.data(), "image").ok_or_else(|| {
59             dev_err!(dev, "FMC ELF file missing 'image' section\n");
60             EINVAL
61         })?;
62         let fmc_image = Coherent::from_slice(dev, fmc_image_data, GFP_KERNEL)?;
63 
64         Ok(Self {
65             fmc_image,
66             fmc_sigs: Self::extract_fmc_signatures(&fw, dev)?,
67         })
68     }
69 
70     /// Extract FMC firmware signatures for Chain of Trust verification.
71     ///
72     /// Extracts real cryptographic signatures from FMC ELF32 firmware sections.
73     /// Returns signatures in a heap-allocated structure to prevent stack overflow.
74     fn extract_fmc_signatures(
75         fmc_fw: &Firmware,
76         dev: &device::Device,
77     ) -> Result<KBox<FmcSignatures>> {
78         let get_section = |name: &str, max_len: usize| {
79             elf::elf_section(fmc_fw.data(), name)
80                 .ok_or(EINVAL)
81                 .inspect_err(|_| dev_err!(dev, "FMC firmware missing '{}' section\n", name))
82                 .and_then(|section| {
83                     if section.len() > max_len {
84                         dev_err!(
85                             dev,
86                             "FMC {} section size {} > maximum {}\n",
87                             name,
88                             section.len(),
89                             max_len
90                         );
91                         Err(EINVAL)
92                     } else {
93                         Ok(section)
94                     }
95                 })
96         };
97 
98         let hash_section = get_section("hash", FSP_HASH_SIZE)?;
99         let pkey_section = get_section("publickey", FSP_PKEY_SIZE)?;
100         let sig_section = get_section("signature", FSP_SIG_SIZE)?;
101 
102         // The hash section is a SHA-384 output: it must be exactly FSP_HASH_SIZE bytes.
103         if hash_section.len() != FSP_HASH_SIZE {
104             dev_err!(
105                 dev,
106                 "FMC hash section size {} != expected {}\n",
107                 hash_section.len(),
108                 FSP_HASH_SIZE
109             );
110             return Err(EINVAL);
111         }
112 
113         // Initialize the signatures in place to avoid building the large `FmcSignatures` on the
114         // stack, then fill each section from the firmware.
115         let signatures = KBox::init(
116             pin_init::init_zeroed::<FmcSignatures>().chain(|sigs| {
117                 // PANIC: src and dst lengths are both FSP_HASH_SIZE (verified above).
118                 sigs.hash384.copy_from_slice(hash_section);
119                 // PANIC: dst is sliced to src.len(); src.len() <= FSP_PKEY_SIZE per `get_section`.
120                 sigs.public_key[..pkey_section.len()].copy_from_slice(pkey_section);
121                 // PANIC: dst is sliced to src.len(); src.len() <= FSP_SIG_SIZE per `get_section`.
122                 sigs.signature[..sig_section.len()].copy_from_slice(sig_section);
123                 Ok(())
124             }),
125             GFP_KERNEL,
126         )?;
127 
128         Ok(signatures)
129     }
130 }
131