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