Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/gpu/nova-core/firmware/fwsec.rs
49642 views
1
// SPDX-License-Identifier: GPL-2.0
2
3
//! FWSEC is a High Secure firmware that is extracted from the BIOS and performs the first step of
4
//! the GSP startup by creating the WPR2 memory region and copying critical areas of the VBIOS into
5
//! it after authenticating them, ensuring they haven't been tampered with. It runs on the GSP
6
//! falcon.
7
//!
8
//! Before being run, it needs to be patched in two areas:
9
//!
10
//! - The command to be run, as this firmware can perform several tasks ;
11
//! - The ucode signature, so the GSP falcon can run FWSEC in HS mode.
12
13
use core::{
14
marker::PhantomData,
15
mem::size_of,
16
ops::Deref, //
17
};
18
19
use kernel::{
20
device::{
21
self,
22
Device, //
23
},
24
prelude::*,
25
transmute::{
26
AsBytes,
27
FromBytes, //
28
},
29
};
30
31
use crate::{
32
dma::DmaObject,
33
driver::Bar0,
34
falcon::{
35
gsp::Gsp,
36
Falcon,
37
FalconBromParams,
38
FalconFirmware,
39
FalconLoadParams,
40
FalconLoadTarget, //
41
},
42
firmware::{
43
FalconUCodeDescV3,
44
FirmwareDmaObject,
45
FirmwareSignature,
46
Signed,
47
Unsigned, //
48
},
49
num::{
50
FromSafeCast,
51
IntoSafeCast, //
52
},
53
vbios::Vbios,
54
};
55
56
const NVFW_FALCON_APPIF_ID_DMEMMAPPER: u32 = 0x4;
57
58
#[repr(C)]
59
#[derive(Debug)]
60
struct FalconAppifHdrV1 {
61
version: u8,
62
header_size: u8,
63
entry_size: u8,
64
entry_count: u8,
65
}
66
// SAFETY: Any byte sequence is valid for this struct.
67
unsafe impl FromBytes for FalconAppifHdrV1 {}
68
69
#[repr(C, packed)]
70
#[derive(Debug)]
71
struct FalconAppifV1 {
72
id: u32,
73
dmem_base: u32,
74
}
75
// SAFETY: Any byte sequence is valid for this struct.
76
unsafe impl FromBytes for FalconAppifV1 {}
77
78
#[derive(Debug)]
79
#[repr(C, packed)]
80
struct FalconAppifDmemmapperV3 {
81
signature: u32,
82
version: u16,
83
size: u16,
84
cmd_in_buffer_offset: u32,
85
cmd_in_buffer_size: u32,
86
cmd_out_buffer_offset: u32,
87
cmd_out_buffer_size: u32,
88
nvf_img_data_buffer_offset: u32,
89
nvf_img_data_buffer_size: u32,
90
printf_buffer_hdr: u32,
91
ucode_build_time_stamp: u32,
92
ucode_signature: u32,
93
init_cmd: u32,
94
ucode_feature: u32,
95
ucode_cmd_mask0: u32,
96
ucode_cmd_mask1: u32,
97
multi_tgt_tbl: u32,
98
}
99
// SAFETY: Any byte sequence is valid for this struct.
100
unsafe impl FromBytes for FalconAppifDmemmapperV3 {}
101
// SAFETY: This struct doesn't contain uninitialized bytes and doesn't have interior mutability.
102
unsafe impl AsBytes for FalconAppifDmemmapperV3 {}
103
104
#[derive(Debug)]
105
#[repr(C, packed)]
106
struct ReadVbios {
107
ver: u32,
108
hdr: u32,
109
addr: u64,
110
size: u32,
111
flags: u32,
112
}
113
// SAFETY: Any byte sequence is valid for this struct.
114
unsafe impl FromBytes for ReadVbios {}
115
// SAFETY: This struct doesn't contain uninitialized bytes and doesn't have interior mutability.
116
unsafe impl AsBytes for ReadVbios {}
117
118
#[derive(Debug)]
119
#[repr(C, packed)]
120
struct FrtsRegion {
121
ver: u32,
122
hdr: u32,
123
addr: u32,
124
size: u32,
125
ftype: u32,
126
}
127
// SAFETY: Any byte sequence is valid for this struct.
128
unsafe impl FromBytes for FrtsRegion {}
129
// SAFETY: This struct doesn't contain uninitialized bytes and doesn't have interior mutability.
130
unsafe impl AsBytes for FrtsRegion {}
131
132
const NVFW_FRTS_CMD_REGION_TYPE_FB: u32 = 2;
133
134
#[repr(C, packed)]
135
struct FrtsCmd {
136
read_vbios: ReadVbios,
137
frts_region: FrtsRegion,
138
}
139
// SAFETY: Any byte sequence is valid for this struct.
140
unsafe impl FromBytes for FrtsCmd {}
141
// SAFETY: This struct doesn't contain uninitialized bytes and doesn't have interior mutability.
142
unsafe impl AsBytes for FrtsCmd {}
143
144
const NVFW_FALCON_APPIF_DMEMMAPPER_CMD_FRTS: u32 = 0x15;
145
const NVFW_FALCON_APPIF_DMEMMAPPER_CMD_SB: u32 = 0x19;
146
147
/// Command for the [`FwsecFirmware`] to execute.
148
pub(crate) enum FwsecCommand {
149
/// Asks [`FwsecFirmware`] to carve out the WPR2 area and place a verified copy of the VBIOS
150
/// image into it.
151
Frts { frts_addr: u64, frts_size: u64 },
152
/// Asks [`FwsecFirmware`] to load pre-OS apps on the PMU.
153
#[expect(dead_code)]
154
Sb,
155
}
156
157
/// Size of the signatures used in FWSEC.
158
const BCRT30_RSA3K_SIG_SIZE: usize = 384;
159
160
/// A single signature that can be patched into a FWSEC image.
161
#[repr(transparent)]
162
pub(crate) struct Bcrt30Rsa3kSignature([u8; BCRT30_RSA3K_SIG_SIZE]);
163
164
/// SAFETY: A signature is just an array of bytes.
165
unsafe impl FromBytes for Bcrt30Rsa3kSignature {}
166
167
impl From<[u8; BCRT30_RSA3K_SIG_SIZE]> for Bcrt30Rsa3kSignature {
168
fn from(sig: [u8; BCRT30_RSA3K_SIG_SIZE]) -> Self {
169
Self(sig)
170
}
171
}
172
173
impl AsRef<[u8]> for Bcrt30Rsa3kSignature {
174
fn as_ref(&self) -> &[u8] {
175
&self.0
176
}
177
}
178
179
impl FirmwareSignature<FwsecFirmware> for Bcrt30Rsa3kSignature {}
180
181
/// Reinterpret the area starting from `offset` in `fw` as an instance of `T` (which must implement
182
/// [`FromBytes`]) and return a reference to it.
183
///
184
/// # Safety
185
///
186
/// * Callers must ensure that the device does not read/write to/from memory while the returned
187
/// reference is live.
188
/// * Callers must ensure that this call does not race with a write to the same region while
189
/// the returned reference is live.
190
unsafe fn transmute<T: Sized + FromBytes>(fw: &DmaObject, offset: usize) -> Result<&T> {
191
// SAFETY: The safety requirements of the function guarantee the device won't read
192
// or write to memory while the reference is alive and that this call won't race
193
// with writes to the same memory region.
194
T::from_bytes(unsafe { fw.as_slice(offset, size_of::<T>())? }).ok_or(EINVAL)
195
}
196
197
/// Reinterpret the area starting from `offset` in `fw` as a mutable instance of `T` (which must
198
/// implement [`FromBytes`]) and return a reference to it.
199
///
200
/// # Safety
201
///
202
/// * Callers must ensure that the device does not read/write to/from memory while the returned
203
/// slice is live.
204
/// * Callers must ensure that this call does not race with a read or write to the same region
205
/// while the returned slice is live.
206
unsafe fn transmute_mut<T: Sized + FromBytes + AsBytes>(
207
fw: &mut DmaObject,
208
offset: usize,
209
) -> Result<&mut T> {
210
// SAFETY: The safety requirements of the function guarantee the device won't read
211
// or write to memory while the reference is alive and that this call won't race
212
// with writes or reads to the same memory region.
213
T::from_bytes_mut(unsafe { fw.as_slice_mut(offset, size_of::<T>())? }).ok_or(EINVAL)
214
}
215
216
/// The FWSEC microcode, extracted from the BIOS and to be run on the GSP falcon.
217
///
218
/// It is responsible for e.g. carving out the WPR2 region as the first step of the GSP bootflow.
219
pub(crate) struct FwsecFirmware {
220
/// Descriptor of the firmware.
221
desc: FalconUCodeDescV3,
222
/// GPU-accessible DMA object containing the firmware.
223
ucode: FirmwareDmaObject<Self, Signed>,
224
}
225
226
impl FalconLoadParams for FwsecFirmware {
227
fn imem_load_params(&self) -> FalconLoadTarget {
228
FalconLoadTarget {
229
src_start: 0,
230
dst_start: self.desc.imem_phys_base,
231
len: self.desc.imem_load_size,
232
}
233
}
234
235
fn dmem_load_params(&self) -> FalconLoadTarget {
236
FalconLoadTarget {
237
src_start: self.desc.imem_load_size,
238
dst_start: self.desc.dmem_phys_base,
239
len: self.desc.dmem_load_size,
240
}
241
}
242
243
fn brom_params(&self) -> FalconBromParams {
244
FalconBromParams {
245
pkc_data_offset: self.desc.pkc_data_offset,
246
engine_id_mask: self.desc.engine_id_mask,
247
ucode_id: self.desc.ucode_id,
248
}
249
}
250
251
fn boot_addr(&self) -> u32 {
252
0
253
}
254
}
255
256
impl Deref for FwsecFirmware {
257
type Target = DmaObject;
258
259
fn deref(&self) -> &Self::Target {
260
&self.ucode.0
261
}
262
}
263
264
impl FalconFirmware for FwsecFirmware {
265
type Target = Gsp;
266
}
267
268
impl FirmwareDmaObject<FwsecFirmware, Unsigned> {
269
fn new_fwsec(dev: &Device<device::Bound>, bios: &Vbios, cmd: FwsecCommand) -> Result<Self> {
270
let desc = bios.fwsec_image().header()?;
271
let ucode = bios.fwsec_image().ucode(desc)?;
272
let mut dma_object = DmaObject::from_data(dev, ucode)?;
273
274
let hdr_offset = usize::from_safe_cast(desc.imem_load_size + desc.interface_offset);
275
// SAFETY: we have exclusive access to `dma_object`.
276
let hdr: &FalconAppifHdrV1 = unsafe { transmute(&dma_object, hdr_offset) }?;
277
278
if hdr.version != 1 {
279
return Err(EINVAL);
280
}
281
282
// Find the DMEM mapper section in the firmware.
283
for i in 0..usize::from(hdr.entry_count) {
284
// SAFETY: we have exclusive access to `dma_object`.
285
let app: &FalconAppifV1 = unsafe {
286
transmute(
287
&dma_object,
288
hdr_offset + usize::from(hdr.header_size) + i * usize::from(hdr.entry_size),
289
)
290
}?;
291
292
if app.id != NVFW_FALCON_APPIF_ID_DMEMMAPPER {
293
continue;
294
}
295
let dmem_base = app.dmem_base;
296
297
// SAFETY: we have exclusive access to `dma_object`.
298
let dmem_mapper: &mut FalconAppifDmemmapperV3 = unsafe {
299
transmute_mut(
300
&mut dma_object,
301
(desc.imem_load_size + dmem_base).into_safe_cast(),
302
)
303
}?;
304
305
dmem_mapper.init_cmd = match cmd {
306
FwsecCommand::Frts { .. } => NVFW_FALCON_APPIF_DMEMMAPPER_CMD_FRTS,
307
FwsecCommand::Sb => NVFW_FALCON_APPIF_DMEMMAPPER_CMD_SB,
308
};
309
let cmd_in_buffer_offset = dmem_mapper.cmd_in_buffer_offset;
310
311
// SAFETY: we have exclusive access to `dma_object`.
312
let frts_cmd: &mut FrtsCmd = unsafe {
313
transmute_mut(
314
&mut dma_object,
315
(desc.imem_load_size + cmd_in_buffer_offset).into_safe_cast(),
316
)
317
}?;
318
319
frts_cmd.read_vbios = ReadVbios {
320
ver: 1,
321
hdr: u32::try_from(size_of::<ReadVbios>())?,
322
addr: 0,
323
size: 0,
324
flags: 2,
325
};
326
if let FwsecCommand::Frts {
327
frts_addr,
328
frts_size,
329
} = cmd
330
{
331
frts_cmd.frts_region = FrtsRegion {
332
ver: 1,
333
hdr: u32::try_from(size_of::<FrtsRegion>())?,
334
addr: u32::try_from(frts_addr >> 12)?,
335
size: u32::try_from(frts_size >> 12)?,
336
ftype: NVFW_FRTS_CMD_REGION_TYPE_FB,
337
};
338
}
339
340
// Return early as we found and patched the DMEMMAPPER region.
341
return Ok(Self(dma_object, PhantomData));
342
}
343
344
Err(ENOTSUPP)
345
}
346
}
347
348
impl FwsecFirmware {
349
/// Extract the Fwsec firmware from `bios` and patch it to run on `falcon` with the `cmd`
350
/// command.
351
pub(crate) fn new(
352
dev: &Device<device::Bound>,
353
falcon: &Falcon<Gsp>,
354
bar: &Bar0,
355
bios: &Vbios,
356
cmd: FwsecCommand,
357
) -> Result<Self> {
358
let ucode_dma = FirmwareDmaObject::<Self, _>::new_fwsec(dev, bios, cmd)?;
359
360
// Patch signature if needed.
361
let desc = bios.fwsec_image().header()?;
362
let ucode_signed = if desc.signature_count != 0 {
363
let sig_base_img = usize::from_safe_cast(desc.imem_load_size + desc.pkc_data_offset);
364
let desc_sig_versions = u32::from(desc.signature_versions);
365
let reg_fuse_version =
366
falcon.signature_reg_fuse_version(bar, desc.engine_id_mask, desc.ucode_id)?;
367
dev_dbg!(
368
dev,
369
"desc_sig_versions: {:#x}, reg_fuse_version: {}\n",
370
desc_sig_versions,
371
reg_fuse_version
372
);
373
let signature_idx = {
374
let reg_fuse_version_bit = 1 << reg_fuse_version;
375
376
// Check if the fuse version is supported by the firmware.
377
if desc_sig_versions & reg_fuse_version_bit == 0 {
378
dev_err!(
379
dev,
380
"no matching signature: {:#x} {:#x}\n",
381
reg_fuse_version_bit,
382
desc_sig_versions,
383
);
384
return Err(EINVAL);
385
}
386
387
// `desc_sig_versions` has one bit set per included signature. Thus, the index of
388
// the signature to patch is the number of bits in `desc_sig_versions` set to `1`
389
// before `reg_fuse_version_bit`.
390
391
// Mask of the bits of `desc_sig_versions` to preserve.
392
let reg_fuse_version_mask = reg_fuse_version_bit.wrapping_sub(1);
393
394
usize::from_safe_cast((desc_sig_versions & reg_fuse_version_mask).count_ones())
395
};
396
397
dev_dbg!(dev, "patching signature with index {}\n", signature_idx);
398
let signature = bios
399
.fwsec_image()
400
.sigs(desc)
401
.and_then(|sigs| sigs.get(signature_idx).ok_or(EINVAL))?;
402
403
ucode_dma.patch_signature(signature, sig_base_img)?
404
} else {
405
ucode_dma.no_patch_signature()
406
};
407
408
Ok(FwsecFirmware {
409
desc: desc.clone(),
410
ucode: ucode_signed,
411
})
412
}
413
414
/// Loads the FWSEC firmware into `falcon` and execute it.
415
pub(crate) fn run(
416
&self,
417
dev: &Device<device::Bound>,
418
falcon: &Falcon<Gsp>,
419
bar: &Bar0,
420
) -> Result<()> {
421
// Reset falcon, load the firmware, and run it.
422
falcon
423
.reset(bar)
424
.inspect_err(|e| dev_err!(dev, "Failed to reset GSP falcon: {:?}\n", e))?;
425
falcon
426
.dma_load(bar, self)
427
.inspect_err(|e| dev_err!(dev, "Failed to load FWSEC firmware: {:?}\n", e))?;
428
let (mbox0, _) = falcon
429
.boot(bar, Some(0), None)
430
.inspect_err(|e| dev_err!(dev, "Failed to boot FWSEC firmware: {:?}\n", e))?;
431
if mbox0 != 0 {
432
dev_err!(dev, "FWSEC firmware returned error {}\n", mbox0);
433
Err(EIO)
434
} else {
435
Ok(())
436
}
437
}
438
}
439
440