Path: blob/master/drivers/gpu/nova-core/firmware/booter.rs
49631 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::{7marker::PhantomData,8ops::Deref, //9};1011use kernel::{12device,13prelude::*,14transmute::FromBytes, //15};1617use crate::{18dma::DmaObject,19driver::Bar0,20falcon::{21sec2::Sec2,22Falcon,23FalconBromParams,24FalconFirmware,25FalconLoadParams,26FalconLoadTarget, //27},28firmware::{29BinFirmware,30FirmwareDmaObject,31FirmwareSignature,32Signed,33Unsigned, //34},35gpu::Chipset,36num::{37FromSafeCast,38IntoSafeCast, //39},40};4142/// Local convenience function to return a copy of `S` by reinterpreting the bytes starting at43/// `offset` in `slice`.44fn frombytes_at<S: FromBytes + Sized>(slice: &[u8], offset: usize) -> Result<S> {45slice46.get(offset..offset + size_of::<S>())47.and_then(S::from_bytes_copy)48.ok_or(EINVAL)49}5051/// Heavy-Secured firmware header.52///53/// Such firmwares have an application-specific payload that needs to be patched with a given54/// signature.55#[repr(C)]56#[derive(Debug, Clone)]57struct HsHeaderV2 {58/// Offset to the start of the signatures.59sig_prod_offset: u32,60/// Size in bytes of the signatures.61sig_prod_size: u32,62/// Offset to a `u32` containing the location at which to patch the signature in the microcode63/// image.64patch_loc_offset: u32,65/// Offset to a `u32` containing the index of the signature to patch.66patch_sig_offset: u32,67/// Start offset to the signature metadata.68meta_data_offset: u32,69/// Size in bytes of the signature metadata.70meta_data_size: u32,71/// Offset to a `u32` containing the number of signatures in the signatures section.72num_sig_offset: u32,73/// Offset of the application-specific header.74header_offset: u32,75/// Size in bytes of the application-specific header.76header_size: u32,77}7879// SAFETY: all bit patterns are valid for this type, and it doesn't use interior mutability.80unsafe impl FromBytes for HsHeaderV2 {}8182/// Heavy-Secured Firmware image container.83///84/// This provides convenient access to the fields of [`HsHeaderV2`] that are actually indices to85/// read from in the firmware data.86struct HsFirmwareV2<'a> {87hdr: HsHeaderV2,88fw: &'a [u8],89}9091impl<'a> HsFirmwareV2<'a> {92/// Interprets the header of `bin_fw` as a [`HsHeaderV2`] and returns an instance of93/// `HsFirmwareV2` for further parsing.94///95/// Fails if the header pointed at by `bin_fw` is not within the bounds of the firmware image.96fn new(bin_fw: &BinFirmware<'a>) -> Result<Self> {97frombytes_at::<HsHeaderV2>(bin_fw.fw, bin_fw.hdr.header_offset.into_safe_cast())98.map(|hdr| Self { hdr, fw: bin_fw.fw })99}100101/// Returns the location at which the signatures should be patched in the microcode image.102///103/// Fails if the offset of the patch location is outside the bounds of the firmware104/// image.105fn patch_location(&self) -> Result<u32> {106frombytes_at::<u32>(self.fw, self.hdr.patch_loc_offset.into_safe_cast())107}108109/// Returns an iterator to the signatures of the firmware. The iterator can be empty if the110/// firmware is unsigned.111///112/// Fails if the pointed signatures are outside the bounds of the firmware image.113fn signatures_iter(&'a self) -> Result<impl Iterator<Item = BooterSignature<'a>>> {114let num_sig = frombytes_at::<u32>(self.fw, self.hdr.num_sig_offset.into_safe_cast())?;115let iter = match self.hdr.sig_prod_size.checked_div(num_sig) {116// If there are no signatures, return an iterator that will yield zero elements.117None => (&[] as &[u8]).chunks_exact(1),118Some(sig_size) => {119let patch_sig =120frombytes_at::<u32>(self.fw, self.hdr.patch_sig_offset.into_safe_cast())?;121let signatures_start = usize::from_safe_cast(self.hdr.sig_prod_offset + patch_sig);122123self.fw124// Get signatures range.125.get(126signatures_start127..signatures_start + usize::from_safe_cast(self.hdr.sig_prod_size),128)129.ok_or(EINVAL)?130.chunks_exact(sig_size.into_safe_cast())131}132};133134// Map the byte slices into signatures.135Ok(iter.map(BooterSignature))136}137}138139/// Signature parameters, as defined in the firmware.140#[repr(C)]141struct HsSignatureParams {142/// Fuse version to use.143fuse_ver: u32,144/// Mask of engine IDs this firmware applies to.145engine_id_mask: u32,146/// ID of the microcode.147ucode_id: u32,148}149150// SAFETY: all bit patterns are valid for this type, and it doesn't use interior mutability.151unsafe impl FromBytes for HsSignatureParams {}152153impl HsSignatureParams {154/// Returns the signature parameters contained in `hs_fw`.155///156/// Fails if the meta data parameter of `hs_fw` is outside the bounds of the firmware image, or157/// if its size doesn't match that of [`HsSignatureParams`].158fn new(hs_fw: &HsFirmwareV2<'_>) -> Result<Self> {159let start = usize::from_safe_cast(hs_fw.hdr.meta_data_offset);160let end = start161.checked_add(hs_fw.hdr.meta_data_size.into_safe_cast())162.ok_or(EINVAL)?;163164hs_fw165.fw166.get(start..end)167.and_then(Self::from_bytes_copy)168.ok_or(EINVAL)169}170}171172/// Header for code and data load offsets.173#[repr(C)]174#[derive(Debug, Clone)]175struct HsLoadHeaderV2 {176// Offset at which the code starts.177os_code_offset: u32,178// Total size of the code, for all apps.179os_code_size: u32,180// Offset at which the data starts.181os_data_offset: u32,182// Size of the data.183os_data_size: u32,184// Number of apps following this header. Each app is described by a [`HsLoadHeaderV2App`].185num_apps: u32,186}187188// SAFETY: all bit patterns are valid for this type, and it doesn't use interior mutability.189unsafe impl FromBytes for HsLoadHeaderV2 {}190191impl HsLoadHeaderV2 {192/// Returns the load header contained in `hs_fw`.193///194/// Fails if the header pointed at by `hs_fw` is not within the bounds of the firmware image.195fn new(hs_fw: &HsFirmwareV2<'_>) -> Result<Self> {196frombytes_at::<Self>(hs_fw.fw, hs_fw.hdr.header_offset.into_safe_cast())197}198}199200/// Header for app code loader.201#[repr(C)]202#[derive(Debug, Clone)]203struct HsLoadHeaderV2App {204/// Offset at which to load the app code.205offset: u32,206/// Length in bytes of the app code.207len: u32,208}209210// SAFETY: all bit patterns are valid for this type, and it doesn't use interior mutability.211unsafe impl FromBytes for HsLoadHeaderV2App {}212213impl HsLoadHeaderV2App {214/// Returns the [`HsLoadHeaderV2App`] for app `idx` of `hs_fw`.215///216/// Fails if `idx` is larger than the number of apps declared in `hs_fw`, or if the header is217/// not within the bounds of the firmware image.218fn new(hs_fw: &HsFirmwareV2<'_>, idx: u32) -> Result<Self> {219let load_hdr = HsLoadHeaderV2::new(hs_fw)?;220if idx >= load_hdr.num_apps {221Err(EINVAL)222} else {223frombytes_at::<Self>(224hs_fw.fw,225usize::from_safe_cast(hs_fw.hdr.header_offset)226// Skip the load header...227.checked_add(size_of::<HsLoadHeaderV2>())228// ... and jump to app header `idx`.229.and_then(|offset| {230offset231.checked_add(usize::from_safe_cast(idx).checked_mul(size_of::<Self>())?)232})233.ok_or(EINVAL)?,234)235}236}237}238239/// Signature for Booter firmware. Their size is encoded into the header and not known a compile240/// time, so we just wrap a byte slices on which we can implement [`FirmwareSignature`].241struct BooterSignature<'a>(&'a [u8]);242243impl<'a> AsRef<[u8]> for BooterSignature<'a> {244fn as_ref(&self) -> &[u8] {245self.0246}247}248249impl<'a> FirmwareSignature<BooterFirmware> for BooterSignature<'a> {}250251/// The `Booter` loader firmware, responsible for loading the GSP.252pub(crate) struct BooterFirmware {253// Load parameters for `IMEM` falcon memory.254imem_load_target: FalconLoadTarget,255// Load parameters for `DMEM` falcon memory.256dmem_load_target: FalconLoadTarget,257// BROM falcon parameters.258brom_params: FalconBromParams,259// Device-mapped firmware image.260ucode: FirmwareDmaObject<Self, Signed>,261}262263impl FirmwareDmaObject<BooterFirmware, Unsigned> {264fn new_booter(dev: &device::Device<device::Bound>, data: &[u8]) -> Result<Self> {265DmaObject::from_data(dev, data).map(|ucode| Self(ucode, PhantomData))266}267}268269#[derive(Copy, Clone, Debug, PartialEq)]270pub(crate) enum BooterKind {271Loader,272#[expect(unused)]273Unloader,274}275276impl BooterFirmware {277/// Parses the Booter firmware contained in `fw`, and patches the correct signature so it is278/// ready to be loaded and run on `falcon`.279pub(crate) fn new(280dev: &device::Device<device::Bound>,281kind: BooterKind,282chipset: Chipset,283ver: &str,284falcon: &Falcon<<Self as FalconFirmware>::Target>,285bar: &Bar0,286) -> Result<Self> {287let fw_name = match kind {288BooterKind::Loader => "booter_load",289BooterKind::Unloader => "booter_unload",290};291let fw = super::request_firmware(dev, chipset, fw_name, ver)?;292let bin_fw = BinFirmware::new(&fw)?;293294// The binary firmware embeds a Heavy-Secured firmware.295let hs_fw = HsFirmwareV2::new(&bin_fw)?;296297// The Heavy-Secured firmware embeds a firmware load descriptor.298let load_hdr = HsLoadHeaderV2::new(&hs_fw)?;299300// Offset in `ucode` where to patch the signature.301let patch_loc = hs_fw.patch_location()?;302303let sig_params = HsSignatureParams::new(&hs_fw)?;304let brom_params = FalconBromParams {305// `load_hdr.os_data_offset` is an absolute index, but `pkc_data_offset` is from the306// signature patch location.307pkc_data_offset: patch_loc308.checked_sub(load_hdr.os_data_offset)309.ok_or(EINVAL)?,310engine_id_mask: u16::try_from(sig_params.engine_id_mask).map_err(|_| EINVAL)?,311ucode_id: u8::try_from(sig_params.ucode_id).map_err(|_| EINVAL)?,312};313let app0 = HsLoadHeaderV2App::new(&hs_fw, 0)?;314315// Object containing the firmware microcode to be signature-patched.316let ucode = bin_fw317.data()318.ok_or(EINVAL)319.and_then(|data| FirmwareDmaObject::<Self, _>::new_booter(dev, data))?;320321let ucode_signed = {322let mut signatures = hs_fw.signatures_iter()?.peekable();323324if signatures.peek().is_none() {325// If there are no signatures, then the firmware is unsigned.326ucode.no_patch_signature()327} else {328// Obtain the version from the fuse register, and extract the corresponding329// signature.330let reg_fuse_version = falcon.signature_reg_fuse_version(331bar,332brom_params.engine_id_mask,333brom_params.ucode_id,334)?;335336// `0` means the last signature should be used.337const FUSE_VERSION_USE_LAST_SIG: u32 = 0;338let signature = match reg_fuse_version {339FUSE_VERSION_USE_LAST_SIG => signatures.last(),340// Otherwise hardware fuse version needs to be subtracted to obtain the index.341reg_fuse_version => {342let Some(idx) = sig_params.fuse_ver.checked_sub(reg_fuse_version) else {343dev_err!(dev, "invalid fuse version for Booter firmware\n");344return Err(EINVAL);345};346signatures.nth(idx.into_safe_cast())347}348}349.ok_or(EINVAL)?;350351ucode.patch_signature(&signature, patch_loc.into_safe_cast())?352}353};354355Ok(Self {356imem_load_target: FalconLoadTarget {357src_start: app0.offset,358dst_start: 0,359len: app0.len,360},361dmem_load_target: FalconLoadTarget {362src_start: load_hdr.os_data_offset,363dst_start: 0,364len: load_hdr.os_data_size,365},366brom_params,367ucode: ucode_signed,368})369}370}371372impl FalconLoadParams for BooterFirmware {373fn imem_load_params(&self) -> FalconLoadTarget {374self.imem_load_target.clone()375}376377fn dmem_load_params(&self) -> FalconLoadTarget {378self.dmem_load_target.clone()379}380381fn brom_params(&self) -> FalconBromParams {382self.brom_params.clone()383}384385fn boot_addr(&self) -> u32 {386self.imem_load_target.src_start387}388}389390impl Deref for BooterFirmware {391type Target = DmaObject;392393fn deref(&self) -> &Self::Target {394&self.ucode.0395}396}397398impl FalconFirmware for BooterFirmware {399type Target = Sec2;400}401402403