//! FWSEC is a High Secure firmware that is extracted from the BIOS and performs the first step of //! the GSP startup by creating the WPR2 memory region and copying critical areas of the VBIOS into //! it after authenticating them, ensuring they haven't been tampered with. It runs on the GSP //! falcon. //! //! Before being run, it needs to be patched in two areas: //! //! - The command to be run, as this firmware can perform several tasks ; //! - The ucode signature, so the GSP falcon can run FWSEC in HS mode.
use core::marker::PhantomData; use core::mem::{align_of, size_of}; use core::ops::Deref;
use kernel::device::{self, Device}; use kernel::prelude::*; use kernel::transmute::FromBytes;
#[derive(Debug)] #[repr(C, packed)] struct ReadVbios {
ver: u32,
hdr: u32,
addr: u64,
size: u32,
flags: u32,
} // SAFETY: any byte sequence is valid for this struct. unsafeimpl FromBytes for ReadVbios {}
#[derive(Debug)] #[repr(C, packed)] struct FrtsRegion {
ver: u32,
hdr: u32,
addr: u32,
size: u32,
ftype: u32,
} // SAFETY: any byte sequence is valid for this struct. unsafeimpl FromBytes for FrtsRegion {}
const NVFW_FRTS_CMD_REGION_TYPE_FB: u32 = 2;
#[repr(C, packed)] struct FrtsCmd {
read_vbios: ReadVbios,
frts_region: FrtsRegion,
} // SAFETY: any byte sequence is valid for this struct. unsafeimpl FromBytes for FrtsCmd {}
/// Command for the [`FwsecFirmware`] to execute. pub(crate) enum FwsecCommand { /// Asks [`FwsecFirmware`] to carve out the WPR2 area and place a verified copy of the VBIOS /// image into it.
Frts { frts_addr: u64, frts_size: u64 }, /// Asks [`FwsecFirmware`] to load pre-OS apps on the PMU. #[expect(dead_code)]
Sb,
}
/// Size of the signatures used in FWSEC. const BCRT30_RSA3K_SIG_SIZE: usize = 384;
/// A single signature that can be patched into a FWSEC image. #[repr(transparent)] pub(crate) struct Bcrt30Rsa3kSignature([u8; BCRT30_RSA3K_SIG_SIZE]);
/// SAFETY: A signature is just an array of bytes. unsafeimpl FromBytes for Bcrt30Rsa3kSignature {}
impl FirmwareSignature<FwsecFirmware> for Bcrt30Rsa3kSignature {}
/// Reinterpret the area starting from `offset` in `fw` as an instance of `T` (which must implement /// [`FromBytes`]) and return a reference to it. /// /// # Safety /// /// Callers must ensure that the region of memory returned is not written for as long as the /// returned reference is alive. /// /// TODO[TRSM][COHA]: Remove this and `transmute_mut` once `CoherentAllocation::as_slice` is /// available and we have a way to transmute objects implementing FromBytes, e.g.: /// https://lore.kernel.org/lkml/20250330234039.29814-1-christiansantoslima21@gmail.com/ unsafefn transmute<'a, 'b, T: Sized + FromBytes>(
fw: &'a DmaObject,
offset: usize,
) -> Result<&'b T> { if offset + size_of::<T>() > fw.size() { return Err(EINVAL);
} if (fw.start_ptr() as usize + offset) % align_of::<T>() != 0 { return Err(EINVAL);
}
// SAFETY: we have checked that the pointer is properly aligned that its pointed memory is // large enough the contains an instance of `T`, which implements `FromBytes`.
Ok(unsafe { &*(fw.start_ptr().add(offset).cast::<T>()) })
}
/// Reinterpret the area starting from `offset` in `fw` as a mutable instance of `T` (which must /// implement [`FromBytes`]) and return a reference to it. /// /// # Safety /// /// Callers must ensure that the region of memory returned is not read or written for as long as /// the returned reference is alive. unsafefn transmute_mut<'a, 'b, T: Sized + FromBytes>(
fw: &'a mut DmaObject,
offset: usize,
) -> Result<&'b mut T> { if offset + size_of::<T>() > fw.size() { return Err(EINVAL);
} if (fw.start_ptr_mut() as usize + offset) % align_of::<T>() != 0 { return Err(EINVAL);
}
// SAFETY: we have checked that the pointer is properly aligned that its pointed memory is // large enough the contains an instance of `T`, which implements `FromBytes`.
Ok(unsafe { &mut *(fw.start_ptr_mut().add(offset).cast::<T>()) })
}
/// The FWSEC microcode, extracted from the BIOS and to be run on the GSP falcon. /// /// It is responsible for e.g. carving out the WPR2 region as the first step of the GSP bootflow. pub(crate) struct FwsecFirmware { /// Descriptor of the firmware.
desc: FalconUCodeDescV3, /// GPU-accessible DMA object containing the firmware.
ucode: FirmwareDmaObject<Self, Signed>,
}
// We need to load full DMEM pages. const DMEM_LOAD_SIZE_ALIGN: u32 = 256;
let hdr_offset = (desc.imem_load_size + desc.interface_offset) as usize; // SAFETY: we have exclusive access to `dma_object`. let hdr: &FalconAppifHdrV1 = unsafe { transmute(&dma_object, hdr_offset) }?;
if hdr.version != 1 { return Err(EINVAL);
}
// Find the DMEM mapper section in the firmware. for i in0..hdr.entry_count as usize { let app: &FalconAppifV1 = // SAFETY: we have exclusive access to `dma_object`. unsafe {
transmute(
&dma_object,
hdr_offset + hdr.header_size as usize + i * hdr.entry_size as usize
)
}?;
if app.id != NVFW_FALCON_APPIF_ID_DMEMMAPPER { continue;
}
// SAFETY: we have exclusive access to `dma_object`. let dmem_mapper: &mut FalconAppifDmemmapperV3 = unsafe {
transmute_mut(
&mut dma_object,
(desc.imem_load_size + app.dmem_base) as usize,
)
}?;
// SAFETY: we have exclusive access to `dma_object`. let frts_cmd: &mut FrtsCmd = unsafe {
transmute_mut(
&mut dma_object,
(desc.imem_load_size + dmem_mapper.cmd_in_buffer_offset) as usize,
)
}?;
// Return early as we found and patched the DMEMMAPPER region. return Ok(Self(dma_object, PhantomData));
}
Err(ENOTSUPP)
}
}
impl FwsecFirmware { /// Extract the Fwsec firmware from `bios` and patch it to run on `falcon` with the `cmd` /// command. pub(crate) fn new(
dev: &Device<device::Bound>,
falcon: &Falcon<Gsp>,
bar: &Bar0,
bios: &Vbios,
cmd: FwsecCommand,
) -> Result<Self> { let ucode_dma = FirmwareDmaObject::<Self, _>::new_fwsec(dev, bios, cmd)?;
// Patch signature if needed. let desc = bios.fwsec_image().header(dev)?; let ucode_signed = if desc.signature_count != 0 { let sig_base_img = (desc.imem_load_size + desc.pkc_data_offset) as usize; let desc_sig_versions = u32::from(desc.signature_versions); let reg_fuse_version =
falcon.signature_reg_fuse_version(bar, desc.engine_id_mask, desc.ucode_id)?;
dev_dbg!(
dev, "desc_sig_versions: {:#x}, reg_fuse_version: {}\n",
desc_sig_versions,
reg_fuse_version
); let signature_idx = { let reg_fuse_version_bit = 1 << reg_fuse_version;
// Check if the fuse version is supported by the firmware. if desc_sig_versions & reg_fuse_version_bit == 0 {
dev_err!(
dev, "no matching signature: {:#x} {:#x}\n",
reg_fuse_version_bit,
desc_sig_versions,
); return Err(EINVAL);
}
// `desc_sig_versions` has one bit set per included signature. Thus, the index of // the signature to patch is the number of bits in `desc_sig_versions` set to `1` // before `reg_fuse_version_bit`.
// Mask of the bits of `desc_sig_versions` to preserve. let reg_fuse_version_mask = reg_fuse_version_bit.wrapping_sub(1);
(desc_sig_versions & reg_fuse_version_mask).count_ones() as usize
};
dev_dbg!(dev, "patching signature with index {}\n", signature_idx); let signature = bios
.fwsec_image()
.sigs(dev, desc)
.and_then(|sigs| sigs.get(signature_idx).ok_or(EINVAL))?;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.