Path: blob/master/drivers/gpu/nova-core/firmware/booter.rs
29281 views
// SPDX-License-Identifier: GPL-2.012//! Support for loading and patching the `Booter` firmware. `Booter` is a Heavy Secured firmware3//! running on [`Sec2`], that is used on Turing/Ampere to load the GSP firmware into the GSP falcon4//! (and optionally unload it through a separate firmware image).56use core::marker::PhantomData;7use core::mem::size_of;8use core::ops::Deref;910use kernel::device;11use kernel::prelude::*;12use kernel::transmute::FromBytes;1314use crate::dma::DmaObject;15use crate::driver::Bar0;16use crate::falcon::sec2::Sec2;17use crate::falcon::{Falcon, FalconBromParams, FalconFirmware, FalconLoadParams, FalconLoadTarget};18use crate::firmware::{BinFirmware, FirmwareDmaObject, FirmwareSignature, Signed, Unsigned};19use crate::gpu::Chipset;2021/// Local convenience function to return a copy of `S` by reinterpreting the bytes starting at22/// `offset` in `slice`.23fn frombytes_at<S: FromBytes + Sized>(slice: &[u8], offset: usize) -> Result<S> {24slice25.get(offset..offset + size_of::<S>())26.and_then(S::from_bytes_copy)27.ok_or(EINVAL)28}2930/// Heavy-Secured firmware header.31///32/// Such firmwares have an application-specific payload that needs to be patched with a given33/// signature.34#[repr(C)]35#[derive(Debug, Clone)]36struct HsHeaderV2 {37/// Offset to the start of the signatures.38sig_prod_offset: u32,39/// Size in bytes of the signatures.40sig_prod_size: u32,41/// Offset to a `u32` containing the location at which to patch the signature in the microcode42/// image.43patch_loc_offset: u32,44/// Offset to a `u32` containing the index of the signature to patch.45patch_sig_offset: u32,46/// Start offset to the signature metadata.47meta_data_offset: u32,48/// Size in bytes of the signature metadata.49meta_data_size: u32,50/// Offset to a `u32` containing the number of signatures in the signatures section.51num_sig_offset: u32,52/// Offset of the application-specific header.53header_offset: u32,54/// Size in bytes of the application-specific header.55header_size: u32,56}5758// SAFETY: all bit patterns are valid for this type, and it doesn't use interior mutability.59unsafe impl FromBytes for HsHeaderV2 {}6061/// Heavy-Secured Firmware image container.62///63/// This provides convenient access to the fields of [`HsHeaderV2`] that are actually indices to64/// read from in the firmware data.65struct HsFirmwareV2<'a> {66hdr: HsHeaderV2,67fw: &'a [u8],68}6970impl<'a> HsFirmwareV2<'a> {71/// Interprets the header of `bin_fw` as a [`HsHeaderV2`] and returns an instance of72/// `HsFirmwareV2` for further parsing.73///74/// Fails if the header pointed at by `bin_fw` is not within the bounds of the firmware image.75fn new(bin_fw: &BinFirmware<'a>) -> Result<Self> {76frombytes_at::<HsHeaderV2>(bin_fw.fw, bin_fw.hdr.header_offset as usize)77.map(|hdr| Self { hdr, fw: bin_fw.fw })78}7980/// Returns the location at which the signatures should be patched in the microcode image.81///82/// Fails if the offset of the patch location is outside the bounds of the firmware83/// image.84fn patch_location(&self) -> Result<u32> {85frombytes_at::<u32>(self.fw, self.hdr.patch_loc_offset as usize)86}8788/// Returns an iterator to the signatures of the firmware. The iterator can be empty if the89/// firmware is unsigned.90///91/// Fails if the pointed signatures are outside the bounds of the firmware image.92fn signatures_iter(&'a self) -> Result<impl Iterator<Item = BooterSignature<'a>>> {93let num_sig = frombytes_at::<u32>(self.fw, self.hdr.num_sig_offset as usize)?;94let iter = match self.hdr.sig_prod_size.checked_div(num_sig) {95// If there are no signatures, return an iterator that will yield zero elements.96None => (&[] as &[u8]).chunks_exact(1),97Some(sig_size) => {98let patch_sig = frombytes_at::<u32>(self.fw, self.hdr.patch_sig_offset as usize)?;99let signatures_start = (self.hdr.sig_prod_offset + patch_sig) as usize;100101self.fw102// Get signatures range.103.get(signatures_start..signatures_start + self.hdr.sig_prod_size as usize)104.ok_or(EINVAL)?105.chunks_exact(sig_size as usize)106}107};108109// Map the byte slices into signatures.110Ok(iter.map(BooterSignature))111}112}113114/// Signature parameters, as defined in the firmware.115#[repr(C)]116struct HsSignatureParams {117/// Fuse version to use.118fuse_ver: u32,119/// Mask of engine IDs this firmware applies to.120engine_id_mask: u32,121/// ID of the microcode.122ucode_id: u32,123}124125// SAFETY: all bit patterns are valid for this type, and it doesn't use interior mutability.126unsafe impl FromBytes for HsSignatureParams {}127128impl HsSignatureParams {129/// Returns the signature parameters contained in `hs_fw`.130///131/// Fails if the meta data parameter of `hs_fw` is outside the bounds of the firmware image, or132/// if its size doesn't match that of [`HsSignatureParams`].133fn new(hs_fw: &HsFirmwareV2<'_>) -> Result<Self> {134let start = hs_fw.hdr.meta_data_offset as usize;135let end = start136.checked_add(hs_fw.hdr.meta_data_size as usize)137.ok_or(EINVAL)?;138139hs_fw140.fw141.get(start..end)142.and_then(Self::from_bytes_copy)143.ok_or(EINVAL)144}145}146147/// Header for code and data load offsets.148#[repr(C)]149#[derive(Debug, Clone)]150struct HsLoadHeaderV2 {151// Offset at which the code starts.152os_code_offset: u32,153// Total size of the code, for all apps.154os_code_size: u32,155// Offset at which the data starts.156os_data_offset: u32,157// Size of the data.158os_data_size: u32,159// Number of apps following this header. Each app is described by a [`HsLoadHeaderV2App`].160num_apps: u32,161}162163// SAFETY: all bit patterns are valid for this type, and it doesn't use interior mutability.164unsafe impl FromBytes for HsLoadHeaderV2 {}165166impl HsLoadHeaderV2 {167/// Returns the load header contained in `hs_fw`.168///169/// Fails if the header pointed at by `hs_fw` is not within the bounds of the firmware image.170fn new(hs_fw: &HsFirmwareV2<'_>) -> Result<Self> {171frombytes_at::<Self>(hs_fw.fw, hs_fw.hdr.header_offset as usize)172}173}174175/// Header for app code loader.176#[repr(C)]177#[derive(Debug, Clone)]178struct HsLoadHeaderV2App {179/// Offset at which to load the app code.180offset: u32,181/// Length in bytes of the app code.182len: u32,183}184185// SAFETY: all bit patterns are valid for this type, and it doesn't use interior mutability.186unsafe impl FromBytes for HsLoadHeaderV2App {}187188impl HsLoadHeaderV2App {189/// Returns the [`HsLoadHeaderV2App`] for app `idx` of `hs_fw`.190///191/// Fails if `idx` is larger than the number of apps declared in `hs_fw`, or if the header is192/// not within the bounds of the firmware image.193fn new(hs_fw: &HsFirmwareV2<'_>, idx: u32) -> Result<Self> {194let load_hdr = HsLoadHeaderV2::new(hs_fw)?;195if idx >= load_hdr.num_apps {196Err(EINVAL)197} else {198frombytes_at::<Self>(199hs_fw.fw,200(hs_fw.hdr.header_offset as usize)201// Skip the load header...202.checked_add(size_of::<HsLoadHeaderV2>())203// ... and jump to app header `idx`.204.and_then(|offset| {205offset.checked_add((idx as usize).checked_mul(size_of::<Self>())?)206})207.ok_or(EINVAL)?,208)209}210}211}212213/// Signature for Booter firmware. Their size is encoded into the header and not known a compile214/// time, so we just wrap a byte slices on which we can implement [`FirmwareSignature`].215struct BooterSignature<'a>(&'a [u8]);216217impl<'a> AsRef<[u8]> for BooterSignature<'a> {218fn as_ref(&self) -> &[u8] {219self.0220}221}222223impl<'a> FirmwareSignature<BooterFirmware> for BooterSignature<'a> {}224225/// The `Booter` loader firmware, responsible for loading the GSP.226pub(crate) struct BooterFirmware {227// Load parameters for `IMEM` falcon memory.228imem_load_target: FalconLoadTarget,229// Load parameters for `DMEM` falcon memory.230dmem_load_target: FalconLoadTarget,231// BROM falcon parameters.232brom_params: FalconBromParams,233// Device-mapped firmware image.234ucode: FirmwareDmaObject<Self, Signed>,235}236237impl FirmwareDmaObject<BooterFirmware, Unsigned> {238fn new_booter(dev: &device::Device<device::Bound>, data: &[u8]) -> Result<Self> {239DmaObject::from_data(dev, data).map(|ucode| Self(ucode, PhantomData))240}241}242243#[derive(Copy, Clone, Debug, PartialEq)]244pub(crate) enum BooterKind {245Loader,246#[expect(unused)]247Unloader,248}249250impl BooterFirmware {251/// Parses the Booter firmware contained in `fw`, and patches the correct signature so it is252/// ready to be loaded and run on `falcon`.253pub(crate) fn new(254dev: &device::Device<device::Bound>,255kind: BooterKind,256chipset: Chipset,257ver: &str,258falcon: &Falcon<<Self as FalconFirmware>::Target>,259bar: &Bar0,260) -> Result<Self> {261let fw_name = match kind {262BooterKind::Loader => "booter_load",263BooterKind::Unloader => "booter_unload",264};265let fw = super::request_firmware(dev, chipset, fw_name, ver)?;266let bin_fw = BinFirmware::new(&fw)?;267268// The binary firmware embeds a Heavy-Secured firmware.269let hs_fw = HsFirmwareV2::new(&bin_fw)?;270271// The Heavy-Secured firmware embeds a firmware load descriptor.272let load_hdr = HsLoadHeaderV2::new(&hs_fw)?;273274// Offset in `ucode` where to patch the signature.275let patch_loc = hs_fw.patch_location()?;276277let sig_params = HsSignatureParams::new(&hs_fw)?;278let brom_params = FalconBromParams {279// `load_hdr.os_data_offset` is an absolute index, but `pkc_data_offset` is from the280// signature patch location.281pkc_data_offset: patch_loc282.checked_sub(load_hdr.os_data_offset)283.ok_or(EINVAL)?,284engine_id_mask: u16::try_from(sig_params.engine_id_mask).map_err(|_| EINVAL)?,285ucode_id: u8::try_from(sig_params.ucode_id).map_err(|_| EINVAL)?,286};287let app0 = HsLoadHeaderV2App::new(&hs_fw, 0)?;288289// Object containing the firmware microcode to be signature-patched.290let ucode = bin_fw291.data()292.ok_or(EINVAL)293.and_then(|data| FirmwareDmaObject::<Self, _>::new_booter(dev, data))?;294295let ucode_signed = {296let mut signatures = hs_fw.signatures_iter()?.peekable();297298if signatures.peek().is_none() {299// If there are no signatures, then the firmware is unsigned.300ucode.no_patch_signature()301} else {302// Obtain the version from the fuse register, and extract the corresponding303// signature.304let reg_fuse_version = falcon.signature_reg_fuse_version(305bar,306brom_params.engine_id_mask,307brom_params.ucode_id,308)?;309310// `0` means the last signature should be used.311const FUSE_VERSION_USE_LAST_SIG: u32 = 0;312let signature = match reg_fuse_version {313FUSE_VERSION_USE_LAST_SIG => signatures.last(),314// Otherwise hardware fuse version needs to be subtracted to obtain the index.315reg_fuse_version => {316let Some(idx) = sig_params.fuse_ver.checked_sub(reg_fuse_version) else {317dev_err!(dev, "invalid fuse version for Booter firmware\n");318return Err(EINVAL);319};320signatures.nth(idx as usize)321}322}323.ok_or(EINVAL)?;324325ucode.patch_signature(&signature, patch_loc as usize)?326}327};328329Ok(Self {330imem_load_target: FalconLoadTarget {331src_start: app0.offset,332dst_start: 0,333len: app0.len,334},335dmem_load_target: FalconLoadTarget {336src_start: load_hdr.os_data_offset,337dst_start: 0,338len: load_hdr.os_data_size,339},340brom_params,341ucode: ucode_signed,342})343}344}345346impl FalconLoadParams for BooterFirmware {347fn imem_load_params(&self) -> FalconLoadTarget {348self.imem_load_target.clone()349}350351fn dmem_load_params(&self) -> FalconLoadTarget {352self.dmem_load_target.clone()353}354355fn brom_params(&self) -> FalconBromParams {356self.brom_params.clone()357}358359fn boot_addr(&self) -> u32 {360self.imem_load_target.src_start361}362}363364impl Deref for BooterFirmware {365type Target = DmaObject;366367fn deref(&self) -> &Self::Target {368&self.ucode.0369}370}371372impl FalconFirmware for BooterFirmware {373type Target = Sec2;374}375376377