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