File Index
Files and directories
Sign in
FILE

fwsec.rs

File preview with syntax highlighting.

linux-6.19.14/drivers/gpu/nova-core/firmware/fwsec.rs RUST
  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}
~/ Read-only file preview.