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//! 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
13use core::{
14 marker::PhantomData,
15 mem::size_of,
16 ops::Deref, //
17};
18
19use kernel::{
20 device::{
21 self,
22 Device, //
23 },
24 prelude::*,
25 transmute::{
26 AsBytes,
27 FromBytes, //
28 },
29};
30
31use 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
56const NVFW_FALCON_APPIF_ID_DMEMMAPPER: u32 = 0x4;
57
58#[repr(C)]
59#[derive(Debug)]
60struct 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.
67unsafe impl FromBytes for FalconAppifHdrV1 {}
68
69#[repr(C, packed)]
70#[derive(Debug)]
71struct FalconAppifV1 {
72 id: u32,
73 dmem_base: u32,
74}
75// SAFETY: Any byte sequence is valid for this struct.
76unsafe impl FromBytes for FalconAppifV1 {}
77
78#[derive(Debug)]
79#[repr(C, packed)]
80struct 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.
100unsafe impl FromBytes for FalconAppifDmemmapperV3 {}
101// SAFETY: This struct doesn't contain uninitialized bytes and doesn't have interior mutability.
102unsafe impl AsBytes for FalconAppifDmemmapperV3 {}
103
104#[derive(Debug)]
105#[repr(C, packed)]
106struct 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.
114unsafe impl FromBytes for ReadVbios {}
115// SAFETY: This struct doesn't contain uninitialized bytes and doesn't have interior mutability.
116unsafe impl AsBytes for ReadVbios {}
117
118#[derive(Debug)]
119#[repr(C, packed)]
120struct 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.
128unsafe impl FromBytes for FrtsRegion {}
129// SAFETY: This struct doesn't contain uninitialized bytes and doesn't have interior mutability.
130unsafe impl AsBytes for FrtsRegion {}
131
132const NVFW_FRTS_CMD_REGION_TYPE_FB: u32 = 2;
133
134#[repr(C, packed)]
135struct FrtsCmd {
136 read_vbios: ReadVbios,
137 frts_region: FrtsRegion,
138}
139// SAFETY: Any byte sequence is valid for this struct.
140unsafe impl FromBytes for FrtsCmd {}
141// SAFETY: This struct doesn't contain uninitialized bytes and doesn't have interior mutability.
142unsafe impl AsBytes for FrtsCmd {}
143
144const NVFW_FALCON_APPIF_DMEMMAPPER_CMD_FRTS: u32 = 0x15;
145const NVFW_FALCON_APPIF_DMEMMAPPER_CMD_SB: u32 = 0x19;
146
147/// Command for the [`FwsecFirmware`] to execute.
148pub(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.
158const BCRT30_RSA3K_SIG_SIZE: usize = 384;
159
160/// A single signature that can be patched into a FWSEC image.
161#[repr(transparent)]
162pub(crate) struct Bcrt30Rsa3kSignature([u8; BCRT30_RSA3K_SIG_SIZE]);
163
164/// SAFETY: A signature is just an array of bytes.
165unsafe impl FromBytes for Bcrt30Rsa3kSignature {}
166
167impl From<[u8; BCRT30_RSA3K_SIG_SIZE]> for Bcrt30Rsa3kSignature {
168 fn from(sig: [u8; BCRT30_RSA3K_SIG_SIZE]) -> Self {
169 Self(sig)
170 }
171}
172
173impl AsRef<[u8]> for Bcrt30Rsa3kSignature {
174 fn as_ref(&self) -> &[u8] {
175 &self.0
176 }
177}
178
179impl 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.
190unsafe 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.
206unsafe 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.
219pub(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
226impl 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
256impl Deref for FwsecFirmware {
257 type Target = DmaObject;
258
259 fn deref(&self) -> &Self::Target {
260 &self.ucode.0
261 }
262}
263
264impl FalconFirmware for FwsecFirmware {
265 type Target = Gsp;
266}
267
268impl 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
348impl 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}