projects/linux·blob master · refs

376 lines · 12.3 KiB

1// SPDX-License-Identifier: GPL-2.0
2// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
4//! Contains structures and functions dedicated to the parsing, building and patching of firmwares
5//! to be loaded into a given execution unit.
6
7use core::marker::PhantomData;
8use core::ops::Deref;
9
10use kernel::{
11 firmware,
12 prelude::*, //
13};
14
15use crate::{
16 falcon::{
17 FalconDmaLoadTarget,
18 FalconFirmware, //
19 },
20 gpu,
21 gsp::boot_firmware_files,
22 num::IntoSafeCast, //
23};
24
25pub(crate) mod booter;
26pub(crate) mod fsp;
27pub(crate) mod fwsec;
28pub(crate) mod gsp;
29pub(crate) mod riscv;
30pub(crate) mod tlv;
31
32/// Structure used to describe some firmwares, notably FWSEC-FRTS.
33#[repr(C)]
34#[derive(Debug, Clone, FromBytes)]
35pub(crate) struct FalconUCodeDescV2 {
36 /// Header defined by 'NV_BIT_FALCON_UCODE_DESC_HEADER_VDESC*' in OpenRM.
37 hdr: u32,
38 /// Stored size of the ucode after the header, compressed or uncompressed
39 stored_size: u32,
40 /// Uncompressed size of the ucode. If store_size == uncompressed_size, then the ucode
41 /// is not compressed.
42 pub(crate) uncompressed_size: u32,
43 /// Code entry point
44 pub(crate) virtual_entry: u32,
45 /// Offset after the code segment at which the Application Interface Table headers are located.
46 pub(crate) interface_offset: u32,
47 /// Base address at which to load the code segment into 'IMEM'.
48 pub(crate) imem_phys_base: u32,
49 /// Size in bytes of the code to copy into 'IMEM' (includes both secure and non-secure
50 /// segments).
51 pub(crate) imem_load_size: u32,
52 /// Virtual 'IMEM' address (i.e. 'tag') at which the code should start.
53 pub(crate) imem_virt_base: u32,
54 /// Virtual address of secure IMEM segment.
55 pub(crate) imem_sec_base: u32,
56 /// Size of secure IMEM segment.
57 pub(crate) imem_sec_size: u32,
58 /// Offset into stored (uncompressed) image at which DMEM begins.
59 pub(crate) dmem_offset: u32,
60 /// Base address at which to load the data segment into 'DMEM'.
61 pub(crate) dmem_phys_base: u32,
62 /// Size in bytes of the data to copy into 'DMEM'.
63 pub(crate) dmem_load_size: u32,
64 /// "Alternate" Size of data to load into IMEM.
65 pub(crate) alt_imem_load_size: u32,
66 /// "Alternate" Size of data to load into DMEM.
67 pub(crate) alt_dmem_load_size: u32,
68}
69
70/// Structure used to describe some firmwares, notably FWSEC-FRTS.
71#[repr(C)]
72#[derive(Debug, Clone, FromBytes)]
73pub(crate) struct FalconUCodeDescV3 {
74 /// Header defined by `NV_BIT_FALCON_UCODE_DESC_HEADER_VDESC*` in OpenRM.
75 hdr: u32,
76 /// Stored size of the ucode after the header.
77 stored_size: u32,
78 /// Offset in `DMEM` at which the signature is expected to be found.
79 pub(crate) pkc_data_offset: u32,
80 /// Offset after the code segment at which the app headers are located.
81 pub(crate) interface_offset: u32,
82 /// Base address at which to load the code segment into `IMEM`.
83 pub(crate) imem_phys_base: u32,
84 /// Size in bytes of the code to copy into `IMEM`.
85 pub(crate) imem_load_size: u32,
86 /// Virtual `IMEM` address (i.e. `tag`) at which the code should start.
87 pub(crate) imem_virt_base: u32,
88 /// Base address at which to load the data segment into `DMEM`.
89 pub(crate) dmem_phys_base: u32,
90 /// Size in bytes of the data to copy into `DMEM`.
91 pub(crate) dmem_load_size: u32,
92 /// Mask of the falcon engines on which this firmware can run.
93 pub(crate) engine_id_mask: u16,
94 /// ID of the ucode used to infer a fuse register to validate the signature.
95 pub(crate) ucode_id: u8,
96 /// Number of signatures in this firmware.
97 pub(crate) signature_count: u8,
98 /// Versions of the signatures, used to infer a valid signature to use.
99 pub(crate) signature_versions: u16,
100 _reserved: u16,
101}
102
103/// Enum wrapping the different versions of Falcon microcode descriptors.
104///
105/// This allows handling both V2 and V3 descriptor formats through a
106/// unified type, providing version-agnostic access to firmware metadata
107/// via the [`FalconUCodeDescriptor`] trait.
108#[derive(Debug, Clone)]
109pub(crate) enum FalconUCodeDesc {
110 V2(FalconUCodeDescV2),
111 V3(FalconUCodeDescV3),
112}
113
114impl Deref for FalconUCodeDesc {
115 type Target = dyn FalconUCodeDescriptor;
116
117 fn deref(&self) -> &Self::Target {
118 match self {
119 FalconUCodeDesc::V2(v2) => v2,
120 FalconUCodeDesc::V3(v3) => v3,
121 }
122 }
123}
124
125/// Trait providing a common interface for accessing Falcon microcode descriptor fields.
126///
127/// This trait abstracts over the different descriptor versions ([`FalconUCodeDescV2`] and
128/// [`FalconUCodeDescV3`]), allowing code to work with firmware metadata without needing to
129/// know the specific descriptor version. Fields not present return zero.
130pub(crate) trait FalconUCodeDescriptor {
131 fn hdr(&self) -> u32;
132 fn imem_load_size(&self) -> u32;
133 fn interface_offset(&self) -> u32;
134 fn dmem_load_size(&self) -> u32;
135 fn pkc_data_offset(&self) -> u32;
136 fn engine_id_mask(&self) -> u16;
137 fn ucode_id(&self) -> u8;
138 fn signature_count(&self) -> u8;
139 fn signature_versions(&self) -> u16;
140
141 /// Returns the size in bytes of the header.
142 fn size(&self) -> usize {
143 let hdr = self.hdr();
144
145 const HDR_SIZE_SHIFT: u32 = 16;
146 const HDR_SIZE_MASK: u32 = 0xffff0000;
147 ((hdr & HDR_SIZE_MASK) >> HDR_SIZE_SHIFT).into_safe_cast()
148 }
149
150 fn imem_sec_load_params(&self) -> FalconDmaLoadTarget;
151 fn imem_ns_load_params(&self) -> Option<FalconDmaLoadTarget>;
152 fn dmem_load_params(&self) -> FalconDmaLoadTarget;
153}
154
155impl FalconUCodeDescriptor for FalconUCodeDescV2 {
156 fn hdr(&self) -> u32 {
157 self.hdr
158 }
159 fn imem_load_size(&self) -> u32 {
160 self.imem_load_size
161 }
162 fn interface_offset(&self) -> u32 {
163 self.interface_offset
164 }
165 fn dmem_load_size(&self) -> u32 {
166 self.dmem_load_size
167 }
168 fn pkc_data_offset(&self) -> u32 {
169 0
170 }
171 fn engine_id_mask(&self) -> u16 {
172 0
173 }
174 fn ucode_id(&self) -> u8 {
175 0
176 }
177 fn signature_count(&self) -> u8 {
178 0
179 }
180 fn signature_versions(&self) -> u16 {
181 0
182 }
183
184 fn imem_sec_load_params(&self) -> FalconDmaLoadTarget {
185 // `imem_sec_base` is the *virtual* start address of the secure IMEM segment, so subtract
186 // `imem_virt_base` to get its physical offset.
187 let imem_sec_start = self.imem_sec_base.saturating_sub(self.imem_virt_base);
188
189 FalconDmaLoadTarget {
190 src_start: imem_sec_start,
191 dst_start: self.imem_phys_base.saturating_add(imem_sec_start),
192 len: self.imem_sec_size,
193 }
194 }
195
196 fn imem_ns_load_params(&self) -> Option<FalconDmaLoadTarget> {
197 Some(FalconDmaLoadTarget {
198 // Non-secure code always starts at offset 0.
199 src_start: 0,
200 dst_start: self.imem_phys_base,
201 // `imem_load_size` includes the size of the secure segment, so subtract it to
202 // get the correct amount of data to copy.
203 len: self.imem_load_size.saturating_sub(self.imem_sec_size),
204 })
205 }
206
207 fn dmem_load_params(&self) -> FalconDmaLoadTarget {
208 FalconDmaLoadTarget {
209 src_start: self.dmem_offset,
210 dst_start: self.dmem_phys_base,
211 len: self.dmem_load_size,
212 }
213 }
214}
215
216impl FalconUCodeDescriptor for FalconUCodeDescV3 {
217 fn hdr(&self) -> u32 {
218 self.hdr
219 }
220 fn imem_load_size(&self) -> u32 {
221 self.imem_load_size
222 }
223 fn interface_offset(&self) -> u32 {
224 self.interface_offset
225 }
226 fn dmem_load_size(&self) -> u32 {
227 self.dmem_load_size
228 }
229 fn pkc_data_offset(&self) -> u32 {
230 self.pkc_data_offset
231 }
232 fn engine_id_mask(&self) -> u16 {
233 self.engine_id_mask
234 }
235 fn ucode_id(&self) -> u8 {
236 self.ucode_id
237 }
238 fn signature_count(&self) -> u8 {
239 self.signature_count
240 }
241 fn signature_versions(&self) -> u16 {
242 self.signature_versions
243 }
244
245 fn imem_sec_load_params(&self) -> FalconDmaLoadTarget {
246 FalconDmaLoadTarget {
247 // IMEM segment always starts at offset 0.
248 src_start: 0,
249 dst_start: self.imem_phys_base,
250 len: self.imem_load_size,
251 }
252 }
253
254 fn imem_ns_load_params(&self) -> Option<FalconDmaLoadTarget> {
255 // Not used on V3 platforms
256 None
257 }
258
259 fn dmem_load_params(&self) -> FalconDmaLoadTarget {
260 FalconDmaLoadTarget {
261 // DMEM segment starts right after the IMEM one.
262 src_start: self.imem_load_size,
263 dst_start: self.dmem_phys_base,
264 len: self.dmem_load_size,
265 }
266 }
267}
268
269/// Trait implemented by types defining the signed state of a firmware.
270trait SignedState {}
271
272/// Type indicating that the firmware must be signed before it can be used.
273struct Unsigned;
274impl SignedState for Unsigned {}
275
276/// Type indicating that the firmware is signed and ready to be loaded.
277struct Signed;
278impl SignedState for Signed {}
279
280/// Microcode to be loaded into a specific falcon.
281///
282/// This is module-local and meant for sub-modules to use internally.
283///
284/// After construction, a firmware is [`Unsigned`], and must generally be patched with a signature
285/// before it can be loaded (with an exception for development hardware). The
286/// [`Self::patch_signature`] and [`Self::no_patch_signature`] methods are used to transition the
287/// firmware to its [`Signed`] state.
288// TODO: Consider replacing this with a coherent memory object once `CoherentAllocation` supports
289// temporary CPU-exclusive access to the object without unsafe methods.
290struct FirmwareObject<F: FalconFirmware, S: SignedState>(KVVec<u8>, PhantomData<(F, S)>);
291
292/// Trait for signatures to be patched directly into a given firmware.
293///
294/// This is module-local and meant for sub-modules to use internally.
295trait FirmwareSignature<F: FalconFirmware>: AsRef<[u8]> {}
296
297impl<F: FalconFirmware> FirmwareObject<F, Unsigned> {
298 /// Patches the firmware at offset `signature_start` with `signature`.
299 fn patch_signature<S: FirmwareSignature<F>>(
300 mut self,
301 signature: &S,
302 signature_start: usize,
303 ) -> Result<FirmwareObject<F, Signed>> {
304 let signature_bytes = signature.as_ref();
305 let signature_end = signature_start
306 .checked_add(signature_bytes.len())
307 .ok_or(EOVERFLOW)?;
308 let dst = self
309 .0
310 .get_mut(signature_start..signature_end)
311 .ok_or(EINVAL)?;
312
313 // PANIC: `dst` and `signature_bytes` have the same length.
314 dst.copy_from_slice(signature_bytes);
315
316 Ok(FirmwareObject(self.0, PhantomData))
317 }
318
319 /// Mark the firmware as signed without patching it.
320 ///
321 /// This method is used to explicitly confirm that we do not need to sign the firmware, while
322 /// allowing us to continue as if it was. This is typically only needed for development
323 /// hardware.
324 fn no_patch_signature(self) -> FirmwareObject<F, Signed> {
325 FirmwareObject(self.0, PhantomData)
326 }
327}
328
329pub(crate) struct ModInfoBuilder<const N: usize>(firmware::ModInfoBuilder<N>);
330
331impl<const N: usize> ModInfoBuilder<N> {
332 const fn make_entry_file(self, chipset: &str, fw: &str) -> Self {
333 ModInfoBuilder(
334 self.0
335 .new_entry()
336 .push("nvidia/")
337 .push(chipset)
338 .push("/gsp/")
339 .push(fw),
340 )
341 }
342
343 const fn make_entry_chipset(self, chipset: gpu::Chipset) -> Self {
344 let name = chipset.name();
345
346 // GSP firmware files are always present.
347 let mut this = self
348 .make_entry_file(name, "gsp_bootloader.tlv")
349 .make_entry_file(name, "gsp.tlv")
350 .make_entry_file(name, "gsp.bin");
351
352 // Add the firmware files specific to the GSP boot method of `chipset`.
353 let boot_files = boot_firmware_files(chipset);
354 let mut i = 0;
355 while i < boot_files.len() {
356 this = this.make_entry_file(name, boot_files[i]);
357 i += 1;
358 }
359
360 this
361 }
362
363 pub(crate) const fn create(
364 module_name: &'static core::ffi::CStr,
365 ) -> firmware::ModInfoBuilder<N> {
366 let mut this = Self(firmware::ModInfoBuilder::new(module_name));
367 let mut i = 0;
368
369 while i < gpu::Chipset::ALL.len() {
370 this = this.make_entry_chipset(gpu::Chipset::ALL[i]);
371 i += 1;
372 }
373
374 this.0
375 }
376}