Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/gpu/nova-core/firmware/booter.rs
49631 views
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
7
use core::{
8
marker::PhantomData,
9
ops::Deref, //
10
};
11
12
use kernel::{
13
device,
14
prelude::*,
15
transmute::FromBytes, //
16
};
17
18
use 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`.
45
fn 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)]
58
struct 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.
81
unsafe 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.
87
struct HsFirmwareV2<'a> {
88
hdr: HsHeaderV2,
89
fw: &'a [u8],
90
}
91
92
impl<'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)]
142
struct 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.
152
unsafe impl FromBytes for HsSignatureParams {}
153
154
impl 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)]
176
struct 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.
190
unsafe impl FromBytes for HsLoadHeaderV2 {}
191
192
impl 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)]
204
struct 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.
212
unsafe impl FromBytes for HsLoadHeaderV2App {}
213
214
impl 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`].
242
struct BooterSignature<'a>(&'a [u8]);
243
244
impl<'a> AsRef<[u8]> for BooterSignature<'a> {
245
fn as_ref(&self) -> &[u8] {
246
self.0
247
}
248
}
249
250
impl<'a> FirmwareSignature<BooterFirmware> for BooterSignature<'a> {}
251
252
/// The `Booter` loader firmware, responsible for loading the GSP.
253
pub(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
264
impl 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)]
271
pub(crate) enum BooterKind {
272
Loader,
273
#[expect(unused)]
274
Unloader,
275
}
276
277
impl 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
373
impl 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
391
impl Deref for BooterFirmware {
392
type Target = DmaObject;
393
394
fn deref(&self) -> &Self::Target {
395
&self.ucode.0
396
}
397
}
398
399
impl FalconFirmware for BooterFirmware {
400
type Target = Sec2;
401
}
402
403