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