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//! Support for loading and patching the `Booter` firmware. `Booter` is a Heavy Secured firmware
4//! running on [`Sec2`], that is used on Turing/Ampere to load the GSP firmware into the GSP falcon
5//! (and optionally unload it through a separate firmware image).
6
7use core::{
8 marker::PhantomData,
9 ops::Deref, //
10};
11
12use kernel::{
13 device,
14 prelude::*,
15 transmute::FromBytes, //
16};
17
18use crate::{
19 dma::DmaObject,
20 driver::Bar0,
21 falcon::{
22 sec2::Sec2,
23 Falcon,
24 FalconBromParams,
25 FalconFirmware,
26 FalconLoadParams,
27 FalconLoadTarget, //
28 },
29 firmware::{
30 BinFirmware,
31 FirmwareDmaObject,
32 FirmwareSignature,
33 Signed,
34 Unsigned, //
35 },
36 gpu::Chipset,
37 num::{
38 FromSafeCast,
39 IntoSafeCast, //
40 },
41};
42
43/// Local convenience function to return a copy of `S` by reinterpreting the bytes starting at
44/// `offset` in `slice`.
45fn frombytes_at<S: FromBytes + Sized>(slice: &[u8], offset: usize) -> Result<S> {
46 slice
47 .get(offset..offset + size_of::<S>())
48 .and_then(S::from_bytes_copy)
49 .ok_or(EINVAL)
50}
51
52/// Heavy-Secured firmware header.
53///
54/// Such firmwares have an application-specific payload that needs to be patched with a given
55/// signature.
56#[repr(C)]
57#[derive(Debug, Clone)]
58struct HsHeaderV2 {
59 /// Offset to the start of the signatures.
60 sig_prod_offset: u32,
61 /// Size in bytes of the signatures.
62 sig_prod_size: u32,
63 /// Offset to a `u32` containing the location at which to patch the signature in the microcode
64 /// image.
65 patch_loc_offset: u32,
66 /// Offset to a `u32` containing the index of the signature to patch.
67 patch_sig_offset: u32,
68 /// Start offset to the signature metadata.
69 meta_data_offset: u32,
70 /// Size in bytes of the signature metadata.
71 meta_data_size: u32,
72 /// Offset to a `u32` containing the number of signatures in the signatures section.
73 num_sig_offset: u32,
74 /// Offset of the application-specific header.
75 header_offset: u32,
76 /// Size in bytes of the application-specific header.
77 header_size: u32,
78}
79
80// SAFETY: all bit patterns are valid for this type, and it doesn't use interior mutability.
81unsafe impl FromBytes for HsHeaderV2 {}
82
83/// Heavy-Secured Firmware image container.
84///
85/// This provides convenient access to the fields of [`HsHeaderV2`] that are actually indices to
86/// read from in the firmware data.
87struct HsFirmwareV2<'a> {
88 hdr: HsHeaderV2,
89 fw: &'a [u8],
90}
91
92impl<'a> HsFirmwareV2<'a> {
93 /// Interprets the header of `bin_fw` as a [`HsHeaderV2`] and returns an instance of
94 /// `HsFirmwareV2` for further parsing.
95 ///
96 /// Fails if the header pointed at by `bin_fw` is not within the bounds of the firmware image.
97 fn new(bin_fw: &BinFirmware<'a>) -> Result<Self> {
98 frombytes_at::<HsHeaderV2>(bin_fw.fw, bin_fw.hdr.header_offset.into_safe_cast())
99 .map(|hdr| Self { hdr, fw: bin_fw.fw })
100 }
101
102 /// Returns the location at which the signatures should be patched in the microcode image.
103 ///
104 /// Fails if the offset of the patch location is outside the bounds of the firmware
105 /// image.
106 fn patch_location(&self) -> Result<u32> {
107 frombytes_at::<u32>(self.fw, self.hdr.patch_loc_offset.into_safe_cast())
108 }
109
110 /// Returns an iterator to the signatures of the firmware. The iterator can be empty if the
111 /// firmware is unsigned.
112 ///
113 /// Fails if the pointed signatures are outside the bounds of the firmware image.
114 fn signatures_iter(&'a self) -> Result<impl Iterator<Item = BooterSignature<'a>>> {
115 let num_sig = frombytes_at::<u32>(self.fw, self.hdr.num_sig_offset.into_safe_cast())?;
116 let iter = match self.hdr.sig_prod_size.checked_div(num_sig) {
117 // If there are no signatures, return an iterator that will yield zero elements.
118 None => (&[] as &[u8]).chunks_exact(1),
119 Some(sig_size) => {
120 let patch_sig =
121 frombytes_at::<u32>(self.fw, self.hdr.patch_sig_offset.into_safe_cast())?;
122 let signatures_start = usize::from_safe_cast(self.hdr.sig_prod_offset + patch_sig);
123
124 self.fw
125 // Get signatures range.
126 .get(
127 signatures_start
128 ..signatures_start + usize::from_safe_cast(self.hdr.sig_prod_size),
129 )
130 .ok_or(EINVAL)?
131 .chunks_exact(sig_size.into_safe_cast())
132 }
133 };
134
135 // Map the byte slices into signatures.
136 Ok(iter.map(BooterSignature))
137 }
138}
139
140/// Signature parameters, as defined in the firmware.
141#[repr(C)]
142struct HsSignatureParams {
143 /// Fuse version to use.
144 fuse_ver: u32,
145 /// Mask of engine IDs this firmware applies to.
146 engine_id_mask: u32,
147 /// ID of the microcode.
148 ucode_id: u32,
149}
150
151// SAFETY: all bit patterns are valid for this type, and it doesn't use interior mutability.
152unsafe impl FromBytes for HsSignatureParams {}
153
154impl HsSignatureParams {
155 /// Returns the signature parameters contained in `hs_fw`.
156 ///
157 /// Fails if the meta data parameter of `hs_fw` is outside the bounds of the firmware image, or
158 /// if its size doesn't match that of [`HsSignatureParams`].
159 fn new(hs_fw: &HsFirmwareV2<'_>) -> Result<Self> {
160 let start = usize::from_safe_cast(hs_fw.hdr.meta_data_offset);
161 let end = start
162 .checked_add(hs_fw.hdr.meta_data_size.into_safe_cast())
163 .ok_or(EINVAL)?;
164
165 hs_fw
166 .fw
167 .get(start..end)
168 .and_then(Self::from_bytes_copy)
169 .ok_or(EINVAL)
170 }
171}
172
173/// Header for code and data load offsets.
174#[repr(C)]
175#[derive(Debug, Clone)]
176struct HsLoadHeaderV2 {
177 // Offset at which the code starts.
178 os_code_offset: u32,
179 // Total size of the code, for all apps.
180 os_code_size: u32,
181 // Offset at which the data starts.
182 os_data_offset: u32,
183 // Size of the data.
184 os_data_size: u32,
185 // Number of apps following this header. Each app is described by a [`HsLoadHeaderV2App`].
186 num_apps: u32,
187}
188
189// SAFETY: all bit patterns are valid for this type, and it doesn't use interior mutability.
190unsafe impl FromBytes for HsLoadHeaderV2 {}
191
192impl HsLoadHeaderV2 {
193 /// Returns the load header contained in `hs_fw`.
194 ///
195 /// Fails if the header pointed at by `hs_fw` is not within the bounds of the firmware image.
196 fn new(hs_fw: &HsFirmwareV2<'_>) -> Result<Self> {
197 frombytes_at::<Self>(hs_fw.fw, hs_fw.hdr.header_offset.into_safe_cast())
198 }
199}
200
201/// Header for app code loader.
202#[repr(C)]
203#[derive(Debug, Clone)]
204struct HsLoadHeaderV2App {
205 /// Offset at which to load the app code.
206 offset: u32,
207 /// Length in bytes of the app code.
208 len: u32,
209}
210
211// SAFETY: all bit patterns are valid for this type, and it doesn't use interior mutability.
212unsafe impl FromBytes for HsLoadHeaderV2App {}
213
214impl HsLoadHeaderV2App {
215 /// Returns the [`HsLoadHeaderV2App`] for app `idx` of `hs_fw`.
216 ///
217 /// Fails if `idx` is larger than the number of apps declared in `hs_fw`, or if the header is
218 /// not within the bounds of the firmware image.
219 fn new(hs_fw: &HsFirmwareV2<'_>, idx: u32) -> Result<Self> {
220 let load_hdr = HsLoadHeaderV2::new(hs_fw)?;
221 if idx >= load_hdr.num_apps {
222 Err(EINVAL)
223 } else {
224 frombytes_at::<Self>(
225 hs_fw.fw,
226 usize::from_safe_cast(hs_fw.hdr.header_offset)
227 // Skip the load header...
228 .checked_add(size_of::<HsLoadHeaderV2>())
229 // ... and jump to app header `idx`.
230 .and_then(|offset| {
231 offset
232 .checked_add(usize::from_safe_cast(idx).checked_mul(size_of::<Self>())?)
233 })
234 .ok_or(EINVAL)?,
235 )
236 }
237 }
238}
239
240/// Signature for Booter firmware. Their size is encoded into the header and not known a compile
241/// time, so we just wrap a byte slices on which we can implement [`FirmwareSignature`].
242struct BooterSignature<'a>(&'a [u8]);
243
244impl<'a> AsRef<[u8]> for BooterSignature<'a> {
245 fn as_ref(&self) -> &[u8] {
246 self.0
247 }
248}
249
250impl<'a> FirmwareSignature<BooterFirmware> for BooterSignature<'a> {}
251
252/// The `Booter` loader firmware, responsible for loading the GSP.
253pub(crate) struct BooterFirmware {
254 // Load parameters for `IMEM` falcon memory.
255 imem_load_target: FalconLoadTarget,
256 // Load parameters for `DMEM` falcon memory.
257 dmem_load_target: FalconLoadTarget,
258 // BROM falcon parameters.
259 brom_params: FalconBromParams,
260 // Device-mapped firmware image.
261 ucode: FirmwareDmaObject<Self, Signed>,
262}
263
264impl FirmwareDmaObject<BooterFirmware, Unsigned> {
265 fn new_booter(dev: &device::Device<device::Bound>, data: &[u8]) -> Result<Self> {
266 DmaObject::from_data(dev, data).map(|ucode| Self(ucode, PhantomData))
267 }
268}
269
270#[derive(Copy, Clone, Debug, PartialEq)]
271pub(crate) enum BooterKind {
272 Loader,
273 #[expect(unused)]
274 Unloader,
275}
276
277impl BooterFirmware {
278 /// Parses the Booter firmware contained in `fw`, and patches the correct signature so it is
279 /// ready to be loaded and run on `falcon`.
280 pub(crate) fn new(
281 dev: &device::Device<device::Bound>,
282 kind: BooterKind,
283 chipset: Chipset,
284 ver: &str,
285 falcon: &Falcon<<Self as FalconFirmware>::Target>,
286 bar: &Bar0,
287 ) -> Result<Self> {
288 let fw_name = match kind {
289 BooterKind::Loader => "booter_load",
290 BooterKind::Unloader => "booter_unload",
291 };
292 let fw = super::request_firmware(dev, chipset, fw_name, ver)?;
293 let bin_fw = BinFirmware::new(&fw)?;
294
295 // The binary firmware embeds a Heavy-Secured firmware.
296 let hs_fw = HsFirmwareV2::new(&bin_fw)?;
297
298 // The Heavy-Secured firmware embeds a firmware load descriptor.
299 let load_hdr = HsLoadHeaderV2::new(&hs_fw)?;
300
301 // Offset in `ucode` where to patch the signature.
302 let patch_loc = hs_fw.patch_location()?;
303
304 let sig_params = HsSignatureParams::new(&hs_fw)?;
305 let brom_params = FalconBromParams {
306 // `load_hdr.os_data_offset` is an absolute index, but `pkc_data_offset` is from the
307 // signature patch location.
308 pkc_data_offset: patch_loc
309 .checked_sub(load_hdr.os_data_offset)
310 .ok_or(EINVAL)?,
311 engine_id_mask: u16::try_from(sig_params.engine_id_mask).map_err(|_| EINVAL)?,
312 ucode_id: u8::try_from(sig_params.ucode_id).map_err(|_| EINVAL)?,
313 };
314 let app0 = HsLoadHeaderV2App::new(&hs_fw, 0)?;
315
316 // Object containing the firmware microcode to be signature-patched.
317 let ucode = bin_fw
318 .data()
319 .ok_or(EINVAL)
320 .and_then(|data| FirmwareDmaObject::<Self, _>::new_booter(dev, data))?;
321
322 let ucode_signed = {
323 let mut signatures = hs_fw.signatures_iter()?.peekable();
324
325 if signatures.peek().is_none() {
326 // If there are no signatures, then the firmware is unsigned.
327 ucode.no_patch_signature()
328 } else {
329 // Obtain the version from the fuse register, and extract the corresponding
330 // signature.
331 let reg_fuse_version = falcon.signature_reg_fuse_version(
332 bar,
333 brom_params.engine_id_mask,
334 brom_params.ucode_id,
335 )?;
336
337 // `0` means the last signature should be used.
338 const FUSE_VERSION_USE_LAST_SIG: u32 = 0;
339 let signature = match reg_fuse_version {
340 FUSE_VERSION_USE_LAST_SIG => signatures.last(),
341 // Otherwise hardware fuse version needs to be subtracted to obtain the index.
342 reg_fuse_version => {
343 let Some(idx) = sig_params.fuse_ver.checked_sub(reg_fuse_version) else {
344 dev_err!(dev, "invalid fuse version for Booter firmware\n");
345 return Err(EINVAL);
346 };
347 signatures.nth(idx.into_safe_cast())
348 }
349 }
350 .ok_or(EINVAL)?;
351
352 ucode.patch_signature(&signature, patch_loc.into_safe_cast())?
353 }
354 };
355
356 Ok(Self {
357 imem_load_target: FalconLoadTarget {
358 src_start: app0.offset,
359 dst_start: 0,
360 len: app0.len,
361 },
362 dmem_load_target: FalconLoadTarget {
363 src_start: load_hdr.os_data_offset,
364 dst_start: 0,
365 len: load_hdr.os_data_size,
366 },
367 brom_params,
368 ucode: ucode_signed,
369 })
370 }
371}
372
373impl FalconLoadParams for BooterFirmware {
374 fn imem_load_params(&self) -> FalconLoadTarget {
375 self.imem_load_target.clone()
376 }
377
378 fn dmem_load_params(&self) -> FalconLoadTarget {
379 self.dmem_load_target.clone()
380 }
381
382 fn brom_params(&self) -> FalconBromParams {
383 self.brom_params.clone()
384 }
385
386 fn boot_addr(&self) -> u32 {
387 self.imem_load_target.src_start
388 }
389}
390
391impl Deref for BooterFirmware {
392 type Target = DmaObject;
393
394 fn deref(&self) -> &Self::Target {
395 &self.ucode.0
396 }
397}
398
399impl FalconFirmware for BooterFirmware {
400 type Target = Sec2;
401}