xref: /linux/drivers/gpu/nova-core/firmware.rs (revision 352af6a011d586ff042db4b2d1f7421875eb8a14)
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 
8 use kernel::device;
9 use kernel::firmware;
10 use kernel::prelude::*;
11 use kernel::str::CString;
12 
13 use crate::dma::DmaObject;
14 use crate::falcon::FalconFirmware;
15 use crate::gpu;
16 use crate::gpu::Chipset;
17 
18 pub(crate) mod fwsec;
19 
20 pub(crate) const FIRMWARE_VERSION: &str = "535.113.01";
21 
22 /// Structure encapsulating the firmware blobs required for the GPU to operate.
23 #[expect(dead_code)]
24 pub(crate) struct Firmware {
25     booter_load: firmware::Firmware,
26     booter_unload: firmware::Firmware,
27     bootloader: firmware::Firmware,
28     gsp: firmware::Firmware,
29 }
30 
31 impl Firmware {
new(dev: &device::Device, chipset: Chipset, ver: &str) -> Result<Firmware>32     pub(crate) fn new(dev: &device::Device, chipset: Chipset, ver: &str) -> Result<Firmware> {
33         let mut chip_name = CString::try_from_fmt(fmt!("{chipset}"))?;
34         chip_name.make_ascii_lowercase();
35         let chip_name = &*chip_name;
36 
37         let request = |name_| {
38             CString::try_from_fmt(fmt!("nvidia/{chip_name}/gsp/{name_}-{ver}.bin"))
39                 .and_then(|path| firmware::Firmware::request(&path, dev))
40         };
41 
42         Ok(Firmware {
43             booter_load: request("booter_load")?,
44             booter_unload: request("booter_unload")?,
45             bootloader: request("bootloader")?,
46             gsp: request("gsp")?,
47         })
48     }
49 }
50 
51 /// Structure used to describe some firmwares, notably FWSEC-FRTS.
52 #[repr(C)]
53 #[derive(Debug, Clone)]
54 pub(crate) struct FalconUCodeDescV3 {
55     /// Header defined by `NV_BIT_FALCON_UCODE_DESC_HEADER_VDESC*` in OpenRM.
56     hdr: u32,
57     /// Stored size of the ucode after the header.
58     stored_size: u32,
59     /// Offset in `DMEM` at which the signature is expected to be found.
60     pub(crate) pkc_data_offset: u32,
61     /// Offset after the code segment at which the app headers are located.
62     pub(crate) interface_offset: u32,
63     /// Base address at which to load the code segment into `IMEM`.
64     pub(crate) imem_phys_base: u32,
65     /// Size in bytes of the code to copy into `IMEM`.
66     pub(crate) imem_load_size: u32,
67     /// Virtual `IMEM` address (i.e. `tag`) at which the code should start.
68     pub(crate) imem_virt_base: u32,
69     /// Base address at which to load the data segment into `DMEM`.
70     pub(crate) dmem_phys_base: u32,
71     /// Size in bytes of the data to copy into `DMEM`.
72     pub(crate) dmem_load_size: u32,
73     /// Mask of the falcon engines on which this firmware can run.
74     pub(crate) engine_id_mask: u16,
75     /// ID of the ucode used to infer a fuse register to validate the signature.
76     pub(crate) ucode_id: u8,
77     /// Number of signatures in this firmware.
78     pub(crate) signature_count: u8,
79     /// Versions of the signatures, used to infer a valid signature to use.
80     pub(crate) signature_versions: u16,
81     _reserved: u16,
82 }
83 
84 impl FalconUCodeDescV3 {
85     /// Returns the size in bytes of the header.
size(&self) -> usize86     pub(crate) fn size(&self) -> usize {
87         const HDR_SIZE_SHIFT: u32 = 16;
88         const HDR_SIZE_MASK: u32 = 0xffff0000;
89 
90         ((self.hdr & HDR_SIZE_MASK) >> HDR_SIZE_SHIFT) as usize
91     }
92 }
93 
94 /// Trait implemented by types defining the signed state of a firmware.
95 trait SignedState {}
96 
97 /// Type indicating that the firmware must be signed before it can be used.
98 struct Unsigned;
99 impl SignedState for Unsigned {}
100 
101 /// Type indicating that the firmware is signed and ready to be loaded.
102 struct Signed;
103 impl SignedState for Signed {}
104 
105 /// A [`DmaObject`] containing a specific microcode ready to be loaded into a falcon.
106 ///
107 /// This is module-local and meant for sub-modules to use internally.
108 ///
109 /// After construction, a firmware is [`Unsigned`], and must generally be patched with a signature
110 /// before it can be loaded (with an exception for development hardware). The
111 /// [`Self::patch_signature`] and [`Self::no_patch_signature`] methods are used to transition the
112 /// firmware to its [`Signed`] state.
113 struct FirmwareDmaObject<F: FalconFirmware, S: SignedState>(DmaObject, PhantomData<(F, S)>);
114 
115 /// Trait for signatures to be patched directly into a given firmware.
116 ///
117 /// This is module-local and meant for sub-modules to use internally.
118 trait FirmwareSignature<F: FalconFirmware>: AsRef<[u8]> {}
119 
120 impl<F: FalconFirmware> FirmwareDmaObject<F, Unsigned> {
121     /// Patches the firmware at offset `sig_base_img` with `signature`.
patch_signature<S: FirmwareSignature<F>>( mut self, signature: &S, sig_base_img: usize, ) -> Result<FirmwareDmaObject<F, Signed>>122     fn patch_signature<S: FirmwareSignature<F>>(
123         mut self,
124         signature: &S,
125         sig_base_img: usize,
126     ) -> Result<FirmwareDmaObject<F, Signed>> {
127         let signature_bytes = signature.as_ref();
128         if sig_base_img + signature_bytes.len() > self.0.size() {
129             return Err(EINVAL);
130         }
131 
132         // SAFETY: We are the only user of this object, so there cannot be any race.
133         let dst = unsafe { self.0.start_ptr_mut().add(sig_base_img) };
134 
135         // SAFETY: `signature` and `dst` are valid, properly aligned, and do not overlap.
136         unsafe {
137             core::ptr::copy_nonoverlapping(signature_bytes.as_ptr(), dst, signature_bytes.len())
138         };
139 
140         Ok(FirmwareDmaObject(self.0, PhantomData))
141     }
142 
143     /// Mark the firmware as signed without patching it.
144     ///
145     /// This method is used to explicitly confirm that we do not need to sign the firmware, while
146     /// allowing us to continue as if it was. This is typically only needed for development
147     /// hardware.
no_patch_signature(self) -> FirmwareDmaObject<F, Signed>148     fn no_patch_signature(self) -> FirmwareDmaObject<F, Signed> {
149         FirmwareDmaObject(self.0, PhantomData)
150     }
151 }
152 
153 pub(crate) struct ModInfoBuilder<const N: usize>(firmware::ModInfoBuilder<N>);
154 
155 impl<const N: usize> ModInfoBuilder<N> {
make_entry_file(self, chipset: &str, fw: &str) -> Self156     const fn make_entry_file(self, chipset: &str, fw: &str) -> Self {
157         ModInfoBuilder(
158             self.0
159                 .new_entry()
160                 .push("nvidia/")
161                 .push(chipset)
162                 .push("/gsp/")
163                 .push(fw)
164                 .push("-")
165                 .push(FIRMWARE_VERSION)
166                 .push(".bin"),
167         )
168     }
169 
make_entry_chipset(self, chipset: &str) -> Self170     const fn make_entry_chipset(self, chipset: &str) -> Self {
171         self.make_entry_file(chipset, "booter_load")
172             .make_entry_file(chipset, "booter_unload")
173             .make_entry_file(chipset, "bootloader")
174             .make_entry_file(chipset, "gsp")
175     }
176 
create( module_name: &'static kernel::str::CStr, ) -> firmware::ModInfoBuilder<N>177     pub(crate) const fn create(
178         module_name: &'static kernel::str::CStr,
179     ) -> firmware::ModInfoBuilder<N> {
180         let mut this = Self(firmware::ModInfoBuilder::new(module_name));
181         let mut i = 0;
182 
183         while i < gpu::Chipset::NAMES.len() {
184             this = this.make_entry_chipset(gpu::Chipset::NAMES[i]);
185             i += 1;
186         }
187 
188         this.0
189     }
190 }
191