projects/linux·blob master · refs

122 lines · 4.2 KiB

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
6use kernel::{
7 device,
8 dma::Coherent,
9 prelude::*, //
10};
11
12use 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.
21const 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.
25const 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.
29const 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)]
36pub(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
42pub(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
49impl 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}