xref: /linux/drivers/gpu/nova-core/firmware.rs (revision 40286d6379aacfcc053253ef78dc78b09addffda)
1 // SPDX-License-Identifier: GPL-2.0
2 
3 //! Contains structures and functions dedicated to the parsing, building and patching of firmwares
4 //! to be loaded into a given execution unit.
5 
6 use core::marker::PhantomData;
7 use core::ops::Deref;
8 
9 use kernel::{
10     device,
11     firmware,
12     prelude::*,
13     str::CString,
14     transmute::FromBytes, //
15 };
16 
17 use crate::{
18     falcon::{
19         FalconDmaLoadTarget,
20         FalconFirmware, //
21     },
22     gpu,
23     num::{
24         FromSafeCast,
25         IntoSafeCast, //
26     },
27 };
28 
29 pub(crate) mod booter;
30 pub(crate) mod fwsec;
31 pub(crate) mod gsp;
32 pub(crate) mod riscv;
33 
34 pub(crate) const FIRMWARE_VERSION: &str = "570.144";
35 
36 /// Requests the GPU firmware `name` suitable for `chipset`, with version `ver`.
37 fn request_firmware(
38     dev: &device::Device,
39     chipset: gpu::Chipset,
40     name: &str,
41     ver: &str,
42 ) -> Result<firmware::Firmware> {
43     let chip_name = chipset.name();
44 
45     CString::try_from_fmt(fmt!("nvidia/{chip_name}/gsp/{name}-{ver}.bin"))
46         .and_then(|path| firmware::Firmware::request(&path, dev))
47 }
48 
49 /// Structure used to describe some firmwares, notably FWSEC-FRTS.
50 #[repr(C)]
51 #[derive(Debug, Clone)]
52 pub(crate) struct FalconUCodeDescV2 {
53     /// Header defined by 'NV_BIT_FALCON_UCODE_DESC_HEADER_VDESC*' in OpenRM.
54     hdr: u32,
55     /// Stored size of the ucode after the header, compressed or uncompressed
56     stored_size: u32,
57     /// Uncompressed size of the ucode.  If store_size == uncompressed_size, then the ucode
58     /// is not compressed.
59     pub(crate) uncompressed_size: u32,
60     /// Code entry point
61     pub(crate) virtual_entry: u32,
62     /// Offset after the code segment at which the Application Interface Table headers are located.
63     pub(crate) interface_offset: u32,
64     /// Base address at which to load the code segment into 'IMEM'.
65     pub(crate) imem_phys_base: u32,
66     /// Size in bytes of the code to copy into 'IMEM' (includes both secure and non-secure
67     /// segments).
68     pub(crate) imem_load_size: u32,
69     /// Virtual 'IMEM' address (i.e. 'tag') at which the code should start.
70     pub(crate) imem_virt_base: u32,
71     /// Virtual address of secure IMEM segment.
72     pub(crate) imem_sec_base: u32,
73     /// Size of secure IMEM segment.
74     pub(crate) imem_sec_size: u32,
75     /// Offset into stored (uncompressed) image at which DMEM begins.
76     pub(crate) dmem_offset: u32,
77     /// Base address at which to load the data segment into 'DMEM'.
78     pub(crate) dmem_phys_base: u32,
79     /// Size in bytes of the data to copy into 'DMEM'.
80     pub(crate) dmem_load_size: u32,
81     /// "Alternate" Size of data to load into IMEM.
82     pub(crate) alt_imem_load_size: u32,
83     /// "Alternate" Size of data to load into DMEM.
84     pub(crate) alt_dmem_load_size: u32,
85 }
86 
87 // SAFETY: all bit patterns are valid for this type, and it doesn't use interior mutability.
88 unsafe impl FromBytes for FalconUCodeDescV2 {}
89 
90 /// Structure used to describe some firmwares, notably FWSEC-FRTS.
91 #[repr(C)]
92 #[derive(Debug, Clone)]
93 pub(crate) struct FalconUCodeDescV3 {
94     /// Header defined by `NV_BIT_FALCON_UCODE_DESC_HEADER_VDESC*` in OpenRM.
95     hdr: u32,
96     /// Stored size of the ucode after the header.
97     stored_size: u32,
98     /// Offset in `DMEM` at which the signature is expected to be found.
99     pub(crate) pkc_data_offset: u32,
100     /// Offset after the code segment at which the app headers are located.
101     pub(crate) interface_offset: u32,
102     /// Base address at which to load the code segment into `IMEM`.
103     pub(crate) imem_phys_base: u32,
104     /// Size in bytes of the code to copy into `IMEM`.
105     pub(crate) imem_load_size: u32,
106     /// Virtual `IMEM` address (i.e. `tag`) at which the code should start.
107     pub(crate) imem_virt_base: u32,
108     /// Base address at which to load the data segment into `DMEM`.
109     pub(crate) dmem_phys_base: u32,
110     /// Size in bytes of the data to copy into `DMEM`.
111     pub(crate) dmem_load_size: u32,
112     /// Mask of the falcon engines on which this firmware can run.
113     pub(crate) engine_id_mask: u16,
114     /// ID of the ucode used to infer a fuse register to validate the signature.
115     pub(crate) ucode_id: u8,
116     /// Number of signatures in this firmware.
117     pub(crate) signature_count: u8,
118     /// Versions of the signatures, used to infer a valid signature to use.
119     pub(crate) signature_versions: u16,
120     _reserved: u16,
121 }
122 
123 // SAFETY: all bit patterns are valid for this type, and it doesn't use
124 // interior mutability.
125 unsafe impl FromBytes for FalconUCodeDescV3 {}
126 
127 /// Enum wrapping the different versions of Falcon microcode descriptors.
128 ///
129 /// This allows handling both V2 and V3 descriptor formats through a
130 /// unified type, providing version-agnostic access to firmware metadata
131 /// via the [`FalconUCodeDescriptor`] trait.
132 #[derive(Debug, Clone)]
133 pub(crate) enum FalconUCodeDesc {
134     V2(FalconUCodeDescV2),
135     V3(FalconUCodeDescV3),
136 }
137 
138 impl Deref for FalconUCodeDesc {
139     type Target = dyn FalconUCodeDescriptor;
140 
141     fn deref(&self) -> &Self::Target {
142         match self {
143             FalconUCodeDesc::V2(v2) => v2,
144             FalconUCodeDesc::V3(v3) => v3,
145         }
146     }
147 }
148 
149 /// Trait providing a common interface for accessing Falcon microcode descriptor fields.
150 ///
151 /// This trait abstracts over the different descriptor versions ([`FalconUCodeDescV2`] and
152 /// [`FalconUCodeDescV3`]), allowing code to work with firmware metadata without needing to
153 /// know the specific descriptor version. Fields not present return zero.
154 pub(crate) trait FalconUCodeDescriptor {
155     fn hdr(&self) -> u32;
156     fn imem_load_size(&self) -> u32;
157     fn interface_offset(&self) -> u32;
158     fn dmem_load_size(&self) -> u32;
159     fn pkc_data_offset(&self) -> u32;
160     fn engine_id_mask(&self) -> u16;
161     fn ucode_id(&self) -> u8;
162     fn signature_count(&self) -> u8;
163     fn signature_versions(&self) -> u16;
164 
165     /// Returns the size in bytes of the header.
166     fn size(&self) -> usize {
167         let hdr = self.hdr();
168 
169         const HDR_SIZE_SHIFT: u32 = 16;
170         const HDR_SIZE_MASK: u32 = 0xffff0000;
171         ((hdr & HDR_SIZE_MASK) >> HDR_SIZE_SHIFT).into_safe_cast()
172     }
173 
174     fn imem_sec_load_params(&self) -> FalconDmaLoadTarget;
175     fn imem_ns_load_params(&self) -> Option<FalconDmaLoadTarget>;
176     fn dmem_load_params(&self) -> FalconDmaLoadTarget;
177 }
178 
179 impl FalconUCodeDescriptor for FalconUCodeDescV2 {
180     fn hdr(&self) -> u32 {
181         self.hdr
182     }
183     fn imem_load_size(&self) -> u32 {
184         self.imem_load_size
185     }
186     fn interface_offset(&self) -> u32 {
187         self.interface_offset
188     }
189     fn dmem_load_size(&self) -> u32 {
190         self.dmem_load_size
191     }
192     fn pkc_data_offset(&self) -> u32 {
193         0
194     }
195     fn engine_id_mask(&self) -> u16 {
196         0
197     }
198     fn ucode_id(&self) -> u8 {
199         0
200     }
201     fn signature_count(&self) -> u8 {
202         0
203     }
204     fn signature_versions(&self) -> u16 {
205         0
206     }
207 
208     fn imem_sec_load_params(&self) -> FalconDmaLoadTarget {
209         // `imem_sec_base` is the *virtual* start address of the secure IMEM segment, so subtract
210         // `imem_virt_base` to get its physical offset.
211         let imem_sec_start = self.imem_sec_base.saturating_sub(self.imem_virt_base);
212 
213         FalconDmaLoadTarget {
214             src_start: imem_sec_start,
215             dst_start: self.imem_phys_base.saturating_add(imem_sec_start),
216             len: self.imem_sec_size,
217         }
218     }
219 
220     fn imem_ns_load_params(&self) -> Option<FalconDmaLoadTarget> {
221         Some(FalconDmaLoadTarget {
222             // Non-secure code always starts at offset 0.
223             src_start: 0,
224             dst_start: self.imem_phys_base,
225             // `imem_load_size` includes the size of the secure segment, so subtract it to
226             // get the correct amount of data to copy.
227             len: self.imem_load_size.saturating_sub(self.imem_sec_size),
228         })
229     }
230 
231     fn dmem_load_params(&self) -> FalconDmaLoadTarget {
232         FalconDmaLoadTarget {
233             src_start: self.dmem_offset,
234             dst_start: self.dmem_phys_base,
235             len: self.dmem_load_size,
236         }
237     }
238 }
239 
240 impl FalconUCodeDescriptor for FalconUCodeDescV3 {
241     fn hdr(&self) -> u32 {
242         self.hdr
243     }
244     fn imem_load_size(&self) -> u32 {
245         self.imem_load_size
246     }
247     fn interface_offset(&self) -> u32 {
248         self.interface_offset
249     }
250     fn dmem_load_size(&self) -> u32 {
251         self.dmem_load_size
252     }
253     fn pkc_data_offset(&self) -> u32 {
254         self.pkc_data_offset
255     }
256     fn engine_id_mask(&self) -> u16 {
257         self.engine_id_mask
258     }
259     fn ucode_id(&self) -> u8 {
260         self.ucode_id
261     }
262     fn signature_count(&self) -> u8 {
263         self.signature_count
264     }
265     fn signature_versions(&self) -> u16 {
266         self.signature_versions
267     }
268 
269     fn imem_sec_load_params(&self) -> FalconDmaLoadTarget {
270         FalconDmaLoadTarget {
271             // IMEM segment always starts at offset 0.
272             src_start: 0,
273             dst_start: self.imem_phys_base,
274             len: self.imem_load_size,
275         }
276     }
277 
278     fn imem_ns_load_params(&self) -> Option<FalconDmaLoadTarget> {
279         // Not used on V3 platforms
280         None
281     }
282 
283     fn dmem_load_params(&self) -> FalconDmaLoadTarget {
284         FalconDmaLoadTarget {
285             // DMEM segment starts right after the IMEM one.
286             src_start: self.imem_load_size,
287             dst_start: self.dmem_phys_base,
288             len: self.dmem_load_size,
289         }
290     }
291 }
292 
293 /// Trait implemented by types defining the signed state of a firmware.
294 trait SignedState {}
295 
296 /// Type indicating that the firmware must be signed before it can be used.
297 struct Unsigned;
298 impl SignedState for Unsigned {}
299 
300 /// Type indicating that the firmware is signed and ready to be loaded.
301 struct Signed;
302 impl SignedState for Signed {}
303 
304 /// Microcode to be loaded into a specific falcon.
305 ///
306 /// This is module-local and meant for sub-modules to use internally.
307 ///
308 /// After construction, a firmware is [`Unsigned`], and must generally be patched with a signature
309 /// before it can be loaded (with an exception for development hardware). The
310 /// [`Self::patch_signature`] and [`Self::no_patch_signature`] methods are used to transition the
311 /// firmware to its [`Signed`] state.
312 // TODO: Consider replacing this with a coherent memory object once `CoherentAllocation` supports
313 // temporary CPU-exclusive access to the object without unsafe methods.
314 struct FirmwareObject<F: FalconFirmware, S: SignedState>(KVVec<u8>, PhantomData<(F, S)>);
315 
316 /// Trait for signatures to be patched directly into a given firmware.
317 ///
318 /// This is module-local and meant for sub-modules to use internally.
319 trait FirmwareSignature<F: FalconFirmware>: AsRef<[u8]> {}
320 
321 impl<F: FalconFirmware> FirmwareObject<F, Unsigned> {
322     /// Patches the firmware at offset `signature_start` with `signature`.
323     fn patch_signature<S: FirmwareSignature<F>>(
324         mut self,
325         signature: &S,
326         signature_start: usize,
327     ) -> Result<FirmwareObject<F, Signed>> {
328         let signature_bytes = signature.as_ref();
329         let signature_end = signature_start
330             .checked_add(signature_bytes.len())
331             .ok_or(EOVERFLOW)?;
332         let dst = self
333             .0
334             .get_mut(signature_start..signature_end)
335             .ok_or(EINVAL)?;
336 
337         // PANIC: `dst` and `signature_bytes` have the same length.
338         dst.copy_from_slice(signature_bytes);
339 
340         Ok(FirmwareObject(self.0, PhantomData))
341     }
342 
343     /// Mark the firmware as signed without patching it.
344     ///
345     /// This method is used to explicitly confirm that we do not need to sign the firmware, while
346     /// allowing us to continue as if it was. This is typically only needed for development
347     /// hardware.
348     fn no_patch_signature(self) -> FirmwareObject<F, Signed> {
349         FirmwareObject(self.0, PhantomData)
350     }
351 }
352 
353 /// Header common to most firmware files.
354 #[repr(C)]
355 #[derive(Debug, Clone)]
356 struct BinHdr {
357     /// Magic number, must be `0x10de`.
358     bin_magic: u32,
359     /// Version of the header.
360     bin_ver: u32,
361     /// Size in bytes of the binary (to be ignored).
362     bin_size: u32,
363     /// Offset of the start of the application-specific header.
364     header_offset: u32,
365     /// Offset of the start of the data payload.
366     data_offset: u32,
367     /// Size in bytes of the data payload.
368     data_size: u32,
369 }
370 
371 // SAFETY: all bit patterns are valid for this type, and it doesn't use interior mutability.
372 unsafe impl FromBytes for BinHdr {}
373 
374 // A firmware blob starting with a `BinHdr`.
375 struct BinFirmware<'a> {
376     hdr: BinHdr,
377     fw: &'a [u8],
378 }
379 
380 impl<'a> BinFirmware<'a> {
381     /// Interpret `fw` as a firmware image starting with a [`BinHdr`], and returns the
382     /// corresponding [`BinFirmware`] that can be used to extract its payload.
383     fn new(fw: &'a firmware::Firmware) -> Result<Self> {
384         const BIN_MAGIC: u32 = 0x10de;
385         let fw = fw.data();
386 
387         fw.get(0..size_of::<BinHdr>())
388             // Extract header.
389             .and_then(BinHdr::from_bytes_copy)
390             // Validate header.
391             .and_then(|hdr| {
392                 if hdr.bin_magic == BIN_MAGIC {
393                     Some(hdr)
394                 } else {
395                     None
396                 }
397             })
398             .map(|hdr| Self { hdr, fw })
399             .ok_or(EINVAL)
400     }
401 
402     /// Returns the data payload of the firmware, or `None` if the data range is out of bounds of
403     /// the firmware image.
404     fn data(&self) -> Option<&[u8]> {
405         let fw_start = usize::from_safe_cast(self.hdr.data_offset);
406         let fw_size = usize::from_safe_cast(self.hdr.data_size);
407         let fw_end = fw_start.checked_add(fw_size)?;
408 
409         self.fw.get(fw_start..fw_end)
410     }
411 }
412 
413 pub(crate) struct ModInfoBuilder<const N: usize>(firmware::ModInfoBuilder<N>);
414 
415 impl<const N: usize> ModInfoBuilder<N> {
416     const fn make_entry_file(self, chipset: &str, fw: &str) -> Self {
417         ModInfoBuilder(
418             self.0
419                 .new_entry()
420                 .push("nvidia/")
421                 .push(chipset)
422                 .push("/gsp/")
423                 .push(fw)
424                 .push("-")
425                 .push(FIRMWARE_VERSION)
426                 .push(".bin"),
427         )
428     }
429 
430     const fn make_entry_chipset(self, chipset: gpu::Chipset) -> Self {
431         let name = chipset.name();
432 
433         let this = self
434             .make_entry_file(name, "booter_load")
435             .make_entry_file(name, "booter_unload")
436             .make_entry_file(name, "bootloader")
437             .make_entry_file(name, "gsp");
438 
439         if chipset.needs_fwsec_bootloader() {
440             this.make_entry_file(name, "gen_bootloader")
441         } else {
442             this
443         }
444     }
445 
446     pub(crate) const fn create(
447         module_name: &'static core::ffi::CStr,
448     ) -> firmware::ModInfoBuilder<N> {
449         let mut this = Self(firmware::ModInfoBuilder::new(module_name));
450         let mut i = 0;
451 
452         while i < gpu::Chipset::ALL.len() {
453             this = this.make_entry_chipset(gpu::Chipset::ALL[i]);
454             i += 1;
455         }
456 
457         this.0
458     }
459 }
460 
461 /// Ad-hoc and temporary module to extract sections from ELF images.
462 ///
463 /// Some firmware images are currently packaged as ELF files, where sections names are used as keys
464 /// to specific and related bits of data. Future firmware versions are scheduled to move away from
465 /// that scheme before nova-core becomes stable, which means this module will eventually be
466 /// removed.
467 mod elf {
468     use core::mem::size_of;
469 
470     use kernel::{
471         bindings,
472         str::CStr,
473         transmute::FromBytes, //
474     };
475 
476     /// Newtype to provide a [`FromBytes`] implementation.
477     #[repr(transparent)]
478     struct Elf64Hdr(bindings::elf64_hdr);
479     // SAFETY: all bit patterns are valid for this type, and it doesn't use interior mutability.
480     unsafe impl FromBytes for Elf64Hdr {}
481 
482     #[repr(transparent)]
483     struct Elf64SHdr(bindings::elf64_shdr);
484     // SAFETY: all bit patterns are valid for this type, and it doesn't use interior mutability.
485     unsafe impl FromBytes for Elf64SHdr {}
486 
487     /// Returns a NULL-terminated string from the ELF image at `offset`.
488     fn elf_str(elf: &[u8], offset: u64) -> Option<&str> {
489         let idx = usize::try_from(offset).ok()?;
490         let bytes = elf.get(idx..)?;
491         CStr::from_bytes_until_nul(bytes).ok()?.to_str().ok()
492     }
493 
494     /// Tries to extract section with name `name` from the ELF64 image `elf`, and returns it.
495     pub(super) fn elf64_section<'a, 'b>(elf: &'a [u8], name: &'b str) -> Option<&'a [u8]> {
496         let hdr = &elf
497             .get(0..size_of::<bindings::elf64_hdr>())
498             .and_then(Elf64Hdr::from_bytes)?
499             .0;
500 
501         // Get all the section headers.
502         let mut shdr = {
503             let shdr_num = usize::from(hdr.e_shnum);
504             let shdr_start = usize::try_from(hdr.e_shoff).ok()?;
505             let shdr_end = shdr_num
506                 .checked_mul(size_of::<Elf64SHdr>())
507                 .and_then(|v| v.checked_add(shdr_start))?;
508 
509             elf.get(shdr_start..shdr_end)
510                 .map(|slice| slice.chunks_exact(size_of::<Elf64SHdr>()))?
511         };
512 
513         // Get the strings table.
514         let strhdr = shdr
515             .clone()
516             .nth(usize::from(hdr.e_shstrndx))
517             .and_then(Elf64SHdr::from_bytes)?;
518 
519         // Find the section which name matches `name` and return it.
520         shdr.find_map(|sh| {
521             let hdr = Elf64SHdr::from_bytes(sh)?;
522             let name_offset = strhdr.0.sh_offset.checked_add(u64::from(hdr.0.sh_name))?;
523             let section_name = elf_str(elf, name_offset)?;
524 
525             if section_name != name {
526                 return None;
527             }
528 
529             let start = usize::try_from(hdr.0.sh_offset).ok()?;
530             let end = usize::try_from(hdr.0.sh_size)
531                 .ok()
532                 .and_then(|sh_size| start.checked_add(sh_size))?;
533 
534             elf.get(start..end)
535         })
536     }
537 }
538