xref: /linux/drivers/gpu/nova-core/firmware.rs (revision 570f7e331f5febb30f1384817463c7e42b65ca7d)
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 /// Structure used to describe some firmwares, notably FWSEC-FRTS.
33 #[repr(C)]
34 #[derive(Debug, Clone, FromBytes)]
35 pub(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)]
73 pub(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)]
109 pub(crate) enum FalconUCodeDesc {
110     V2(FalconUCodeDescV2),
111     V3(FalconUCodeDescV3),
112 }
113 
114 impl 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.
130 pub(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 
155 impl 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 
216 impl 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.
270 trait SignedState {}
271 
272 /// Type indicating that the firmware must be signed before it can be used.
273 struct Unsigned;
274 impl SignedState for Unsigned {}
275 
276 /// Type indicating that the firmware is signed and ready to be loaded.
277 struct Signed;
278 impl 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.
290 struct 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.
295 trait FirmwareSignature<F: FalconFirmware>: AsRef<[u8]> {}
296 
297 impl<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 
329 pub(crate) struct ModInfoBuilder<const N: usize>(firmware::ModInfoBuilder<N>);
330 
331 impl<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 }
377