xref: /linux/drivers/gpu/nova-core/firmware/fsp.rs (revision 570f7e331f5febb30f1384817463c7e42b65ca7d)
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     prelude::*, //
10 };
11 
12 use crate::{
13     firmware::tlv::{
14         request_tlv, //
15         Tlv,
16     },
17     gpu::Chipset, //
18 };
19 
20 /// Size of the FSP SHA-384 hash, in bytes.
21 const FSP_HASH_SIZE: usize = 48;
22 /// Maximum size of the FSP public key (RSA-3072), in bytes.
23 ///
24 /// The FMC `PKEY` tag may be shorter, so the remaining bytes are zero-padded.
25 const FSP_PKEY_SIZE: usize = 384;
26 /// Maximum size of the FSP signature (RSA-3072), in bytes.
27 ///
28 /// The FMC `SIGN` tag may be shorter, so the remaining bytes are zero-padded.
29 const FSP_SIG_SIZE: usize = 384;
30 
31 /// Structure to hold FMC signatures.
32 ///
33 /// C representation is used because this type is used for communication with the FSP.
34 #[derive(Debug, Clone, Copy, Zeroable)]
35 #[repr(C)]
36 pub(crate) struct FmcSignatures {
37     pub(crate) hash384: [u8; FSP_HASH_SIZE],
38     pub(crate) public_key: [u8; FSP_PKEY_SIZE],
39     pub(crate) signature: [u8; FSP_SIG_SIZE],
40 }
41 
42 pub(crate) struct FspFirmware {
43     /// FMC firmware image data
44     pub(crate) fmc_image: Coherent<[u8]>,
45     /// FMC firmware signatures.
46     pub(crate) fmc_sigs: KBox<FmcSignatures>,
47 }
48 
49 impl FspFirmware {
50     pub(crate) fn new(dev: &device::Device<device::Bound>, chipset: Chipset) -> Result<Self> {
51         let fw = request_tlv(dev, chipset, "fmc")?;
52         let tlv = Tlv::new(fw.data())?;
53         dev_dbg!(dev, "loaded fsp firmware v{}\n", tlv.get_string(b"VERS")?);
54 
55         let fmc_image_data = tlv.get_bytes(b"BLOB")?;
56         let fmc_image = Coherent::from_slice(dev, fmc_image_data, GFP_KERNEL)?;
57 
58         Ok(Self {
59             fmc_image,
60             fmc_sigs: Self::extract_fmc_signatures(&tlv, dev)?,
61         })
62     }
63 
64     /// Extract FMC firmware signatures for Chain of Trust verification.
65     ///
66     /// Extracts real cryptographic signatures from FMC TLV firmware tags.
67     /// Returns signatures in a heap-allocated structure to prevent stack overflow.
68     fn extract_fmc_signatures(tlv: &Tlv<'_>, dev: &device::Device) -> Result<KBox<FmcSignatures>> {
69         let hash_section = tlv.get_bytes(b"HASH")?;
70         let pkey_section = tlv.get_bytes(b"PKEY")?;
71         let sig_section = tlv.get_bytes(b"SIGN")?;
72 
73         // The hash section is a SHA-384 output: it must be exactly FSP_HASH_SIZE bytes.
74         if hash_section.len() != FSP_HASH_SIZE {
75             dev_err!(
76                 dev,
77                 "FMC hash section size {} != expected {}\n",
78                 hash_section.len(),
79                 FSP_HASH_SIZE
80             );
81             return Err(EINVAL);
82         }
83 
84         // The key and signature sections are zero-padded to a fixed maximum, so they may be
85         // shorter, but must not exceed the destination buffers.
86         if pkey_section.len() > FSP_PKEY_SIZE {
87             dev_err!(
88                 dev,
89                 "FMC public key section size {} > maximum {}\n",
90                 pkey_section.len(),
91                 FSP_PKEY_SIZE
92             );
93             return Err(EINVAL);
94         }
95         if sig_section.len() > FSP_SIG_SIZE {
96             dev_err!(
97                 dev,
98                 "FMC signature section size {} > maximum {}\n",
99                 sig_section.len(),
100                 FSP_SIG_SIZE
101             );
102             return Err(EINVAL);
103         }
104 
105         // Initialize the signatures in place to avoid building the large `FmcSignatures` on the
106         // stack, then fill each section from the firmware.
107         let signatures = KBox::init(
108             pin_init::init_zeroed::<FmcSignatures>().chain(|sigs| {
109                 // PANIC: src and dst lengths are both FSP_HASH_SIZE (verified above).
110                 sigs.hash384.copy_from_slice(hash_section);
111                 // PANIC: dst is sliced to src.len(); src.len() <= FSP_PKEY_SIZE (verified above).
112                 sigs.public_key[..pkey_section.len()].copy_from_slice(pkey_section);
113                 // PANIC: dst is sliced to src.len(); src.len() <= FSP_SIG_SIZE (verified above).
114                 sigs.signature[..sig_section.len()].copy_from_slice(sig_section);
115                 Ok(())
116             }),
117             GFP_KERNEL,
118         )?;
119 
120         Ok(signatures)
121     }
122 }
123