Path: blob/master/drivers/gpu/nova-core/firmware/fwsec.rs
26481 views
// SPDX-License-Identifier: GPL-2.012//! FWSEC is a High Secure firmware that is extracted from the BIOS and performs the first step of3//! the GSP startup by creating the WPR2 memory region and copying critical areas of the VBIOS into4//! it after authenticating them, ensuring they haven't been tampered with. It runs on the GSP5//! falcon.6//!7//! Before being run, it needs to be patched in two areas:8//!9//! - The command to be run, as this firmware can perform several tasks ;10//! - The ucode signature, so the GSP falcon can run FWSEC in HS mode.1112use core::marker::PhantomData;13use core::mem::{align_of, size_of};14use core::ops::Deref;1516use kernel::device::{self, Device};17use kernel::prelude::*;18use kernel::transmute::FromBytes;1920use crate::dma::DmaObject;21use crate::driver::Bar0;22use crate::falcon::gsp::Gsp;23use crate::falcon::{Falcon, FalconBromParams, FalconFirmware, FalconLoadParams, FalconLoadTarget};24use crate::firmware::{FalconUCodeDescV3, FirmwareDmaObject, FirmwareSignature, Signed, Unsigned};25use crate::vbios::Vbios;2627const NVFW_FALCON_APPIF_ID_DMEMMAPPER: u32 = 0x4;2829#[repr(C)]30#[derive(Debug)]31struct FalconAppifHdrV1 {32version: u8,33header_size: u8,34entry_size: u8,35entry_count: u8,36}37// SAFETY: any byte sequence is valid for this struct.38unsafe impl FromBytes for FalconAppifHdrV1 {}3940#[repr(C, packed)]41#[derive(Debug)]42struct FalconAppifV1 {43id: u32,44dmem_base: u32,45}46// SAFETY: any byte sequence is valid for this struct.47unsafe impl FromBytes for FalconAppifV1 {}4849#[derive(Debug)]50#[repr(C, packed)]51struct FalconAppifDmemmapperV3 {52signature: u32,53version: u16,54size: u16,55cmd_in_buffer_offset: u32,56cmd_in_buffer_size: u32,57cmd_out_buffer_offset: u32,58cmd_out_buffer_size: u32,59nvf_img_data_buffer_offset: u32,60nvf_img_data_buffer_size: u32,61printf_buffer_hdr: u32,62ucode_build_time_stamp: u32,63ucode_signature: u32,64init_cmd: u32,65ucode_feature: u32,66ucode_cmd_mask0: u32,67ucode_cmd_mask1: u32,68multi_tgt_tbl: u32,69}70// SAFETY: any byte sequence is valid for this struct.71unsafe impl FromBytes for FalconAppifDmemmapperV3 {}7273#[derive(Debug)]74#[repr(C, packed)]75struct ReadVbios {76ver: u32,77hdr: u32,78addr: u64,79size: u32,80flags: u32,81}82// SAFETY: any byte sequence is valid for this struct.83unsafe impl FromBytes for ReadVbios {}8485#[derive(Debug)]86#[repr(C, packed)]87struct FrtsRegion {88ver: u32,89hdr: u32,90addr: u32,91size: u32,92ftype: u32,93}94// SAFETY: any byte sequence is valid for this struct.95unsafe impl FromBytes for FrtsRegion {}9697const NVFW_FRTS_CMD_REGION_TYPE_FB: u32 = 2;9899#[repr(C, packed)]100struct FrtsCmd {101read_vbios: ReadVbios,102frts_region: FrtsRegion,103}104// SAFETY: any byte sequence is valid for this struct.105unsafe impl FromBytes for FrtsCmd {}106107const NVFW_FALCON_APPIF_DMEMMAPPER_CMD_FRTS: u32 = 0x15;108const NVFW_FALCON_APPIF_DMEMMAPPER_CMD_SB: u32 = 0x19;109110/// Command for the [`FwsecFirmware`] to execute.111pub(crate) enum FwsecCommand {112/// Asks [`FwsecFirmware`] to carve out the WPR2 area and place a verified copy of the VBIOS113/// image into it.114Frts { frts_addr: u64, frts_size: u64 },115/// Asks [`FwsecFirmware`] to load pre-OS apps on the PMU.116#[expect(dead_code)]117Sb,118}119120/// Size of the signatures used in FWSEC.121const BCRT30_RSA3K_SIG_SIZE: usize = 384;122123/// A single signature that can be patched into a FWSEC image.124#[repr(transparent)]125pub(crate) struct Bcrt30Rsa3kSignature([u8; BCRT30_RSA3K_SIG_SIZE]);126127/// SAFETY: A signature is just an array of bytes.128unsafe impl FromBytes for Bcrt30Rsa3kSignature {}129130impl From<[u8; BCRT30_RSA3K_SIG_SIZE]> for Bcrt30Rsa3kSignature {131fn from(sig: [u8; BCRT30_RSA3K_SIG_SIZE]) -> Self {132Self(sig)133}134}135136impl AsRef<[u8]> for Bcrt30Rsa3kSignature {137fn as_ref(&self) -> &[u8] {138&self.0139}140}141142impl FirmwareSignature<FwsecFirmware> for Bcrt30Rsa3kSignature {}143144/// Reinterpret the area starting from `offset` in `fw` as an instance of `T` (which must implement145/// [`FromBytes`]) and return a reference to it.146///147/// # Safety148///149/// Callers must ensure that the region of memory returned is not written for as long as the150/// returned reference is alive.151///152/// TODO[TRSM][COHA]: Remove this and `transmute_mut` once `CoherentAllocation::as_slice` is153/// available and we have a way to transmute objects implementing FromBytes, e.g.:154/// https://lore.kernel.org/lkml/[email protected]/155unsafe fn transmute<'a, 'b, T: Sized + FromBytes>(156fw: &'a DmaObject,157offset: usize,158) -> Result<&'b T> {159if offset + size_of::<T>() > fw.size() {160return Err(EINVAL);161}162if (fw.start_ptr() as usize + offset) % align_of::<T>() != 0 {163return Err(EINVAL);164}165166// SAFETY: we have checked that the pointer is properly aligned that its pointed memory is167// large enough the contains an instance of `T`, which implements `FromBytes`.168Ok(unsafe { &*(fw.start_ptr().add(offset).cast::<T>()) })169}170171/// Reinterpret the area starting from `offset` in `fw` as a mutable instance of `T` (which must172/// implement [`FromBytes`]) and return a reference to it.173///174/// # Safety175///176/// Callers must ensure that the region of memory returned is not read or written for as long as177/// the returned reference is alive.178unsafe fn transmute_mut<'a, 'b, T: Sized + FromBytes>(179fw: &'a mut DmaObject,180offset: usize,181) -> Result<&'b mut T> {182if offset + size_of::<T>() > fw.size() {183return Err(EINVAL);184}185if (fw.start_ptr_mut() as usize + offset) % align_of::<T>() != 0 {186return Err(EINVAL);187}188189// SAFETY: we have checked that the pointer is properly aligned that its pointed memory is190// large enough the contains an instance of `T`, which implements `FromBytes`.191Ok(unsafe { &mut *(fw.start_ptr_mut().add(offset).cast::<T>()) })192}193194/// The FWSEC microcode, extracted from the BIOS and to be run on the GSP falcon.195///196/// It is responsible for e.g. carving out the WPR2 region as the first step of the GSP bootflow.197pub(crate) struct FwsecFirmware {198/// Descriptor of the firmware.199desc: FalconUCodeDescV3,200/// GPU-accessible DMA object containing the firmware.201ucode: FirmwareDmaObject<Self, Signed>,202}203204// We need to load full DMEM pages.205const DMEM_LOAD_SIZE_ALIGN: u32 = 256;206207impl FalconLoadParams for FwsecFirmware {208fn imem_load_params(&self) -> FalconLoadTarget {209FalconLoadTarget {210src_start: 0,211dst_start: self.desc.imem_phys_base,212len: self.desc.imem_load_size,213}214}215216fn dmem_load_params(&self) -> FalconLoadTarget {217FalconLoadTarget {218src_start: self.desc.imem_load_size,219dst_start: self.desc.dmem_phys_base,220// TODO[NUMM]: replace with `align_up` once it lands.221len: self222.desc223.dmem_load_size224.next_multiple_of(DMEM_LOAD_SIZE_ALIGN),225}226}227228fn brom_params(&self) -> FalconBromParams {229FalconBromParams {230pkc_data_offset: self.desc.pkc_data_offset,231engine_id_mask: self.desc.engine_id_mask,232ucode_id: self.desc.ucode_id,233}234}235236fn boot_addr(&self) -> u32 {2370238}239}240241impl Deref for FwsecFirmware {242type Target = DmaObject;243244fn deref(&self) -> &Self::Target {245&self.ucode.0246}247}248249impl FalconFirmware for FwsecFirmware {250type Target = Gsp;251}252253impl FirmwareDmaObject<FwsecFirmware, Unsigned> {254fn new_fwsec(dev: &Device<device::Bound>, bios: &Vbios, cmd: FwsecCommand) -> Result<Self> {255let desc = bios.fwsec_image().header(dev)?;256let ucode = bios.fwsec_image().ucode(dev, desc)?;257let mut dma_object = DmaObject::from_data(dev, ucode)?;258259let hdr_offset = (desc.imem_load_size + desc.interface_offset) as usize;260// SAFETY: we have exclusive access to `dma_object`.261let hdr: &FalconAppifHdrV1 = unsafe { transmute(&dma_object, hdr_offset) }?;262263if hdr.version != 1 {264return Err(EINVAL);265}266267// Find the DMEM mapper section in the firmware.268for i in 0..hdr.entry_count as usize {269let app: &FalconAppifV1 =270// SAFETY: we have exclusive access to `dma_object`.271unsafe {272transmute(273&dma_object,274hdr_offset + hdr.header_size as usize + i * hdr.entry_size as usize275)276}?;277278if app.id != NVFW_FALCON_APPIF_ID_DMEMMAPPER {279continue;280}281282// SAFETY: we have exclusive access to `dma_object`.283let dmem_mapper: &mut FalconAppifDmemmapperV3 = unsafe {284transmute_mut(285&mut dma_object,286(desc.imem_load_size + app.dmem_base) as usize,287)288}?;289290// SAFETY: we have exclusive access to `dma_object`.291let frts_cmd: &mut FrtsCmd = unsafe {292transmute_mut(293&mut dma_object,294(desc.imem_load_size + dmem_mapper.cmd_in_buffer_offset) as usize,295)296}?;297298frts_cmd.read_vbios = ReadVbios {299ver: 1,300hdr: size_of::<ReadVbios>() as u32,301addr: 0,302size: 0,303flags: 2,304};305306dmem_mapper.init_cmd = match cmd {307FwsecCommand::Frts {308frts_addr,309frts_size,310} => {311frts_cmd.frts_region = FrtsRegion {312ver: 1,313hdr: size_of::<FrtsRegion>() as u32,314addr: (frts_addr >> 12) as u32,315size: (frts_size >> 12) as u32,316ftype: NVFW_FRTS_CMD_REGION_TYPE_FB,317};318319NVFW_FALCON_APPIF_DMEMMAPPER_CMD_FRTS320}321FwsecCommand::Sb => NVFW_FALCON_APPIF_DMEMMAPPER_CMD_SB,322};323324// Return early as we found and patched the DMEMMAPPER region.325return Ok(Self(dma_object, PhantomData));326}327328Err(ENOTSUPP)329}330}331332impl FwsecFirmware {333/// Extract the Fwsec firmware from `bios` and patch it to run on `falcon` with the `cmd`334/// command.335pub(crate) fn new(336dev: &Device<device::Bound>,337falcon: &Falcon<Gsp>,338bar: &Bar0,339bios: &Vbios,340cmd: FwsecCommand,341) -> Result<Self> {342let ucode_dma = FirmwareDmaObject::<Self, _>::new_fwsec(dev, bios, cmd)?;343344// Patch signature if needed.345let desc = bios.fwsec_image().header(dev)?;346let ucode_signed = if desc.signature_count != 0 {347let sig_base_img = (desc.imem_load_size + desc.pkc_data_offset) as usize;348let desc_sig_versions = u32::from(desc.signature_versions);349let reg_fuse_version =350falcon.signature_reg_fuse_version(bar, desc.engine_id_mask, desc.ucode_id)?;351dev_dbg!(352dev,353"desc_sig_versions: {:#x}, reg_fuse_version: {}\n",354desc_sig_versions,355reg_fuse_version356);357let signature_idx = {358let reg_fuse_version_bit = 1 << reg_fuse_version;359360// Check if the fuse version is supported by the firmware.361if desc_sig_versions & reg_fuse_version_bit == 0 {362dev_err!(363dev,364"no matching signature: {:#x} {:#x}\n",365reg_fuse_version_bit,366desc_sig_versions,367);368return Err(EINVAL);369}370371// `desc_sig_versions` has one bit set per included signature. Thus, the index of372// the signature to patch is the number of bits in `desc_sig_versions` set to `1`373// before `reg_fuse_version_bit`.374375// Mask of the bits of `desc_sig_versions` to preserve.376let reg_fuse_version_mask = reg_fuse_version_bit.wrapping_sub(1);377378(desc_sig_versions & reg_fuse_version_mask).count_ones() as usize379};380381dev_dbg!(dev, "patching signature with index {}\n", signature_idx);382let signature = bios383.fwsec_image()384.sigs(dev, desc)385.and_then(|sigs| sigs.get(signature_idx).ok_or(EINVAL))?;386387ucode_dma.patch_signature(signature, sig_base_img)?388} else {389ucode_dma.no_patch_signature()390};391392Ok(FwsecFirmware {393desc: desc.clone(),394ucode: ucode_signed,395})396}397398/// Loads the FWSEC firmware into `falcon` and execute it.399pub(crate) fn run(400&self,401dev: &Device<device::Bound>,402falcon: &Falcon<Gsp>,403bar: &Bar0,404) -> Result<()> {405// Reset falcon, load the firmware, and run it.406falcon407.reset(bar)408.inspect_err(|e| dev_err!(dev, "Failed to reset GSP falcon: {:?}\n", e))?;409falcon410.dma_load(bar, self)411.inspect_err(|e| dev_err!(dev, "Failed to load FWSEC firmware: {:?}\n", e))?;412let (mbox0, _) = falcon413.boot(bar, Some(0), None)414.inspect_err(|e| dev_err!(dev, "Failed to boot FWSEC firmware: {:?}\n", e))?;415if mbox0 != 0 {416dev_err!(dev, "FWSEC firmware returned error {}\n", mbox0);417Err(EIO)418} else {419Ok(())420}421}422}423424425