Path: blob/master/drivers/gpu/nova-core/firmware/fwsec.rs
49642 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::{13marker::PhantomData,14mem::size_of,15ops::Deref, //16};1718use kernel::{19device::{20self,21Device, //22},23prelude::*,24transmute::{25AsBytes,26FromBytes, //27},28};2930use crate::{31dma::DmaObject,32driver::Bar0,33falcon::{34gsp::Gsp,35Falcon,36FalconBromParams,37FalconFirmware,38FalconLoadParams,39FalconLoadTarget, //40},41firmware::{42FalconUCodeDescV3,43FirmwareDmaObject,44FirmwareSignature,45Signed,46Unsigned, //47},48num::{49FromSafeCast,50IntoSafeCast, //51},52vbios::Vbios,53};5455const NVFW_FALCON_APPIF_ID_DMEMMAPPER: u32 = 0x4;5657#[repr(C)]58#[derive(Debug)]59struct FalconAppifHdrV1 {60version: u8,61header_size: u8,62entry_size: u8,63entry_count: u8,64}65// SAFETY: Any byte sequence is valid for this struct.66unsafe impl FromBytes for FalconAppifHdrV1 {}6768#[repr(C, packed)]69#[derive(Debug)]70struct FalconAppifV1 {71id: u32,72dmem_base: u32,73}74// SAFETY: Any byte sequence is valid for this struct.75unsafe impl FromBytes for FalconAppifV1 {}7677#[derive(Debug)]78#[repr(C, packed)]79struct FalconAppifDmemmapperV3 {80signature: u32,81version: u16,82size: u16,83cmd_in_buffer_offset: u32,84cmd_in_buffer_size: u32,85cmd_out_buffer_offset: u32,86cmd_out_buffer_size: u32,87nvf_img_data_buffer_offset: u32,88nvf_img_data_buffer_size: u32,89printf_buffer_hdr: u32,90ucode_build_time_stamp: u32,91ucode_signature: u32,92init_cmd: u32,93ucode_feature: u32,94ucode_cmd_mask0: u32,95ucode_cmd_mask1: u32,96multi_tgt_tbl: u32,97}98// SAFETY: Any byte sequence is valid for this struct.99unsafe impl FromBytes for FalconAppifDmemmapperV3 {}100// SAFETY: This struct doesn't contain uninitialized bytes and doesn't have interior mutability.101unsafe impl AsBytes for FalconAppifDmemmapperV3 {}102103#[derive(Debug)]104#[repr(C, packed)]105struct ReadVbios {106ver: u32,107hdr: u32,108addr: u64,109size: u32,110flags: u32,111}112// SAFETY: Any byte sequence is valid for this struct.113unsafe impl FromBytes for ReadVbios {}114// SAFETY: This struct doesn't contain uninitialized bytes and doesn't have interior mutability.115unsafe impl AsBytes for ReadVbios {}116117#[derive(Debug)]118#[repr(C, packed)]119struct FrtsRegion {120ver: u32,121hdr: u32,122addr: u32,123size: u32,124ftype: u32,125}126// SAFETY: Any byte sequence is valid for this struct.127unsafe impl FromBytes for FrtsRegion {}128// SAFETY: This struct doesn't contain uninitialized bytes and doesn't have interior mutability.129unsafe impl AsBytes for FrtsRegion {}130131const NVFW_FRTS_CMD_REGION_TYPE_FB: u32 = 2;132133#[repr(C, packed)]134struct FrtsCmd {135read_vbios: ReadVbios,136frts_region: FrtsRegion,137}138// SAFETY: Any byte sequence is valid for this struct.139unsafe impl FromBytes for FrtsCmd {}140// SAFETY: This struct doesn't contain uninitialized bytes and doesn't have interior mutability.141unsafe impl AsBytes for FrtsCmd {}142143const NVFW_FALCON_APPIF_DMEMMAPPER_CMD_FRTS: u32 = 0x15;144const NVFW_FALCON_APPIF_DMEMMAPPER_CMD_SB: u32 = 0x19;145146/// Command for the [`FwsecFirmware`] to execute.147pub(crate) enum FwsecCommand {148/// Asks [`FwsecFirmware`] to carve out the WPR2 area and place a verified copy of the VBIOS149/// image into it.150Frts { frts_addr: u64, frts_size: u64 },151/// Asks [`FwsecFirmware`] to load pre-OS apps on the PMU.152#[expect(dead_code)]153Sb,154}155156/// Size of the signatures used in FWSEC.157const BCRT30_RSA3K_SIG_SIZE: usize = 384;158159/// A single signature that can be patched into a FWSEC image.160#[repr(transparent)]161pub(crate) struct Bcrt30Rsa3kSignature([u8; BCRT30_RSA3K_SIG_SIZE]);162163/// SAFETY: A signature is just an array of bytes.164unsafe impl FromBytes for Bcrt30Rsa3kSignature {}165166impl From<[u8; BCRT30_RSA3K_SIG_SIZE]> for Bcrt30Rsa3kSignature {167fn from(sig: [u8; BCRT30_RSA3K_SIG_SIZE]) -> Self {168Self(sig)169}170}171172impl AsRef<[u8]> for Bcrt30Rsa3kSignature {173fn as_ref(&self) -> &[u8] {174&self.0175}176}177178impl FirmwareSignature<FwsecFirmware> for Bcrt30Rsa3kSignature {}179180/// Reinterpret the area starting from `offset` in `fw` as an instance of `T` (which must implement181/// [`FromBytes`]) and return a reference to it.182///183/// # Safety184///185/// * Callers must ensure that the device does not read/write to/from memory while the returned186/// reference is live.187/// * Callers must ensure that this call does not race with a write to the same region while188/// the returned reference is live.189unsafe fn transmute<T: Sized + FromBytes>(fw: &DmaObject, offset: usize) -> Result<&T> {190// SAFETY: The safety requirements of the function guarantee the device won't read191// or write to memory while the reference is alive and that this call won't race192// with writes to the same memory region.193T::from_bytes(unsafe { fw.as_slice(offset, size_of::<T>())? }).ok_or(EINVAL)194}195196/// Reinterpret the area starting from `offset` in `fw` as a mutable instance of `T` (which must197/// implement [`FromBytes`]) and return a reference to it.198///199/// # Safety200///201/// * Callers must ensure that the device does not read/write to/from memory while the returned202/// slice is live.203/// * Callers must ensure that this call does not race with a read or write to the same region204/// while the returned slice is live.205unsafe fn transmute_mut<T: Sized + FromBytes + AsBytes>(206fw: &mut DmaObject,207offset: usize,208) -> Result<&mut T> {209// SAFETY: The safety requirements of the function guarantee the device won't read210// or write to memory while the reference is alive and that this call won't race211// with writes or reads to the same memory region.212T::from_bytes_mut(unsafe { fw.as_slice_mut(offset, size_of::<T>())? }).ok_or(EINVAL)213}214215/// The FWSEC microcode, extracted from the BIOS and to be run on the GSP falcon.216///217/// It is responsible for e.g. carving out the WPR2 region as the first step of the GSP bootflow.218pub(crate) struct FwsecFirmware {219/// Descriptor of the firmware.220desc: FalconUCodeDescV3,221/// GPU-accessible DMA object containing the firmware.222ucode: FirmwareDmaObject<Self, Signed>,223}224225impl FalconLoadParams for FwsecFirmware {226fn imem_load_params(&self) -> FalconLoadTarget {227FalconLoadTarget {228src_start: 0,229dst_start: self.desc.imem_phys_base,230len: self.desc.imem_load_size,231}232}233234fn dmem_load_params(&self) -> FalconLoadTarget {235FalconLoadTarget {236src_start: self.desc.imem_load_size,237dst_start: self.desc.dmem_phys_base,238len: self.desc.dmem_load_size,239}240}241242fn brom_params(&self) -> FalconBromParams {243FalconBromParams {244pkc_data_offset: self.desc.pkc_data_offset,245engine_id_mask: self.desc.engine_id_mask,246ucode_id: self.desc.ucode_id,247}248}249250fn boot_addr(&self) -> u32 {2510252}253}254255impl Deref for FwsecFirmware {256type Target = DmaObject;257258fn deref(&self) -> &Self::Target {259&self.ucode.0260}261}262263impl FalconFirmware for FwsecFirmware {264type Target = Gsp;265}266267impl FirmwareDmaObject<FwsecFirmware, Unsigned> {268fn new_fwsec(dev: &Device<device::Bound>, bios: &Vbios, cmd: FwsecCommand) -> Result<Self> {269let desc = bios.fwsec_image().header()?;270let ucode = bios.fwsec_image().ucode(desc)?;271let mut dma_object = DmaObject::from_data(dev, ucode)?;272273let hdr_offset = usize::from_safe_cast(desc.imem_load_size + desc.interface_offset);274// SAFETY: we have exclusive access to `dma_object`.275let hdr: &FalconAppifHdrV1 = unsafe { transmute(&dma_object, hdr_offset) }?;276277if hdr.version != 1 {278return Err(EINVAL);279}280281// Find the DMEM mapper section in the firmware.282for i in 0..usize::from(hdr.entry_count) {283// SAFETY: we have exclusive access to `dma_object`.284let app: &FalconAppifV1 = unsafe {285transmute(286&dma_object,287hdr_offset + usize::from(hdr.header_size) + i * usize::from(hdr.entry_size),288)289}?;290291if app.id != NVFW_FALCON_APPIF_ID_DMEMMAPPER {292continue;293}294let dmem_base = app.dmem_base;295296// SAFETY: we have exclusive access to `dma_object`.297let dmem_mapper: &mut FalconAppifDmemmapperV3 = unsafe {298transmute_mut(299&mut dma_object,300(desc.imem_load_size + dmem_base).into_safe_cast(),301)302}?;303304dmem_mapper.init_cmd = match cmd {305FwsecCommand::Frts { .. } => NVFW_FALCON_APPIF_DMEMMAPPER_CMD_FRTS,306FwsecCommand::Sb => NVFW_FALCON_APPIF_DMEMMAPPER_CMD_SB,307};308let cmd_in_buffer_offset = dmem_mapper.cmd_in_buffer_offset;309310// SAFETY: we have exclusive access to `dma_object`.311let frts_cmd: &mut FrtsCmd = unsafe {312transmute_mut(313&mut dma_object,314(desc.imem_load_size + cmd_in_buffer_offset).into_safe_cast(),315)316}?;317318frts_cmd.read_vbios = ReadVbios {319ver: 1,320hdr: u32::try_from(size_of::<ReadVbios>())?,321addr: 0,322size: 0,323flags: 2,324};325if let FwsecCommand::Frts {326frts_addr,327frts_size,328} = cmd329{330frts_cmd.frts_region = FrtsRegion {331ver: 1,332hdr: u32::try_from(size_of::<FrtsRegion>())?,333addr: u32::try_from(frts_addr >> 12)?,334size: u32::try_from(frts_size >> 12)?,335ftype: NVFW_FRTS_CMD_REGION_TYPE_FB,336};337}338339// Return early as we found and patched the DMEMMAPPER region.340return Ok(Self(dma_object, PhantomData));341}342343Err(ENOTSUPP)344}345}346347impl FwsecFirmware {348/// Extract the Fwsec firmware from `bios` and patch it to run on `falcon` with the `cmd`349/// command.350pub(crate) fn new(351dev: &Device<device::Bound>,352falcon: &Falcon<Gsp>,353bar: &Bar0,354bios: &Vbios,355cmd: FwsecCommand,356) -> Result<Self> {357let ucode_dma = FirmwareDmaObject::<Self, _>::new_fwsec(dev, bios, cmd)?;358359// Patch signature if needed.360let desc = bios.fwsec_image().header()?;361let ucode_signed = if desc.signature_count != 0 {362let sig_base_img = usize::from_safe_cast(desc.imem_load_size + desc.pkc_data_offset);363let desc_sig_versions = u32::from(desc.signature_versions);364let reg_fuse_version =365falcon.signature_reg_fuse_version(bar, desc.engine_id_mask, desc.ucode_id)?;366dev_dbg!(367dev,368"desc_sig_versions: {:#x}, reg_fuse_version: {}\n",369desc_sig_versions,370reg_fuse_version371);372let signature_idx = {373let reg_fuse_version_bit = 1 << reg_fuse_version;374375// Check if the fuse version is supported by the firmware.376if desc_sig_versions & reg_fuse_version_bit == 0 {377dev_err!(378dev,379"no matching signature: {:#x} {:#x}\n",380reg_fuse_version_bit,381desc_sig_versions,382);383return Err(EINVAL);384}385386// `desc_sig_versions` has one bit set per included signature. Thus, the index of387// the signature to patch is the number of bits in `desc_sig_versions` set to `1`388// before `reg_fuse_version_bit`.389390// Mask of the bits of `desc_sig_versions` to preserve.391let reg_fuse_version_mask = reg_fuse_version_bit.wrapping_sub(1);392393usize::from_safe_cast((desc_sig_versions & reg_fuse_version_mask).count_ones())394};395396dev_dbg!(dev, "patching signature with index {}\n", signature_idx);397let signature = bios398.fwsec_image()399.sigs(desc)400.and_then(|sigs| sigs.get(signature_idx).ok_or(EINVAL))?;401402ucode_dma.patch_signature(signature, sig_base_img)?403} else {404ucode_dma.no_patch_signature()405};406407Ok(FwsecFirmware {408desc: desc.clone(),409ucode: ucode_signed,410})411}412413/// Loads the FWSEC firmware into `falcon` and execute it.414pub(crate) fn run(415&self,416dev: &Device<device::Bound>,417falcon: &Falcon<Gsp>,418bar: &Bar0,419) -> Result<()> {420// Reset falcon, load the firmware, and run it.421falcon422.reset(bar)423.inspect_err(|e| dev_err!(dev, "Failed to reset GSP falcon: {:?}\n", e))?;424falcon425.dma_load(bar, self)426.inspect_err(|e| dev_err!(dev, "Failed to load FWSEC firmware: {:?}\n", e))?;427let (mbox0, _) = falcon428.boot(bar, Some(0), None)429.inspect_err(|e| dev_err!(dev, "Failed to boot FWSEC firmware: {:?}\n", e))?;430if mbox0 != 0 {431dev_err!(dev, "FWSEC firmware returned error {}\n", mbox0);432Err(EIO)433} else {434Ok(())435}436}437}438439440