Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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
6use core::marker::PhantomData;
7
8use kernel::device;
9use kernel::firmware;
10use kernel::prelude::*;
11use kernel::str::CString;
12
13use crate::dma::DmaObject;
14use crate::falcon::FalconFirmware;
15use crate::gpu;
16use crate::gpu::Chipset;
17
18pub(crate) mod fwsec;
19
20pub(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)]
24pub(crate) struct Firmware {
25 booter_load: firmware::Firmware,
26 booter_unload: firmware::Firmware,
27 bootloader: firmware::Firmware,
28 gsp: firmware::Firmware,
29}
30
31impl 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)]
54pub(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
84impl FalconUCodeDescV3 {
85 /// Returns the size in bytes of the header.
86 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.
95trait SignedState {}
96
97/// Type indicating that the firmware must be signed before it can be used.
98struct Unsigned;
99impl SignedState for Unsigned {}
100
101/// Type indicating that the firmware is signed and ready to be loaded.
102struct Signed;
103impl 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.
113struct 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.
118trait FirmwareSignature<F: FalconFirmware>: AsRef<[u8]> {}
119
120impl<F: FalconFirmware> FirmwareDmaObject<F, Unsigned> {
121 /// Patches the firmware at offset `sig_base_img` with `signature`.
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.
148 fn no_patch_signature(self) -> FirmwareDmaObject<F, Signed> {
149 FirmwareDmaObject(self.0, PhantomData)
150 }
151}
152
153pub(crate) struct ModInfoBuilder<const N: usize>(firmware::ModInfoBuilder<N>);
154
155impl<const N: usize> ModInfoBuilder<N> {
156 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
170 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
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}