// SPDX-License-Identifier: GPL-2.0// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. //! Support for loading and patching the `Booter` firmware. `Booter` is a Heavy Secured firmware//! running on [`Sec2`], that is used on Turing/Ampere to load the GSP firmware into the GSP falcon//! (and optionally unload it through a separate firmware image). use core::marker::PhantomData; use kernel::{ device, dma::Coherent, prelude::*, //}; use crate::{ falcon::{ sec2::Sec2, Falcon, FalconBromParams, FalconDmaLoadTarget, FalconDmaLoadable, FalconFirmware, // }, firmware::{ tlv::{ request_tlv, // Tlv, }, FirmwareObject, FirmwareSignature, Signed, Unsigned, // }, gpu::Chipset, num::IntoSafeCast,}; /// Signature for Booter firmware. Their size is encoded into the header and not known a compile/// time, so we just wrap a byte slices on which we can implement [`FirmwareSignature`].struct BooterSignature<'a>(&'a [u8]); impl<'a> AsRef<[u8]> for BooterSignature<'a> { fn as_ref(&self) -> &[u8] { self.0 }} impl<'a> FirmwareSignature<BooterFirmware> for BooterSignature<'a> {} /// The `Booter` loader firmware, responsible for loading the GSP.pub(crate) struct BooterFirmware { // Load parameters for Secure `IMEM` falcon memory. imem_sec_load_target: FalconDmaLoadTarget, // Load parameters for Non-Secure `IMEM` falcon memory, // used only on Turing and GA100 imem_ns_load_target: Option<FalconDmaLoadTarget>, // Load parameters for `DMEM` falcon memory. dmem_load_target: FalconDmaLoadTarget, // BROM falcon parameters. brom_params: FalconBromParams, // Device-mapped firmware image. ucode: FirmwareObject<Self, Signed>,} impl FirmwareObject<BooterFirmware, Unsigned> { fn new_booter(data: &[u8]) -> Result<Self> { let mut ucode = KVVec::new(); ucode.extend_from_slice(data, GFP_KERNEL)?; Ok(Self(ucode, PhantomData)) }} #[derive(Copy, Clone, Debug, PartialEq)]pub(crate) enum BooterKind { Loader, Unloader,} impl BooterFirmware { /// Parses the Booter firmware contained in `fw`, and patches the correct signature so it is /// ready to be loaded and run on `falcon`. pub(crate) fn new( dev: &device::Device<device::Bound>, kind: BooterKind, chipset: Chipset, falcon: &Falcon<'_, <Self as FalconFirmware>::Target>, ) -> Result<Self> { let fw_name = match kind { BooterKind::Loader => "booter_load", BooterKind::Unloader => "booter_unload", }; let fw = request_tlv(dev, chipset, fw_name)?; let tlv = Tlv::new(fw.data())?; dev_dbg!( dev, "loaded {} firmware v{}\n", fw_name, tlv.get_string(b"VERS")? ); let os_data_offset = tlv.get_u32(b"DAOF")?; let os_data_size = tlv.get_u32(b"DASZ")?; let os_code_offset = tlv.get_u32(b"CDOF")?; let os_code_size = tlv.get_u32(b"CDSZ")?; let patch_loc = tlv.get_u32(b"PLOC")?; let fuse_version: usize = tlv.get_u32(b"FUSE")?.into_safe_cast(); let engine_id = tlv.get_u32(b"ENID")?; let ucode_id = tlv.get_u32(b"UCID")?; let app0_code_offset = tlv.get_u32(b"A0CO")?; let app0_code_size = tlv.get_u32(b"A0CS")?; let brom_params = FalconBromParams { // `os_data_offset` is an absolute index, but `pkc_data_offset` is from the // signature patch location. pkc_data_offset: patch_loc.checked_sub(os_data_offset).ok_or(EINVAL)?, engine_id_mask: u16::try_from(engine_id).map_err(|_| EINVAL)?, ucode_id: u8::try_from(ucode_id).map_err(|_| EINVAL)?, }; let ucode = tlv .get_bytes(b"BLOB") .and_then(FirmwareObject::<Self, _>::new_booter)?; // Obtain the version from the fuse register, and extract the corresponding // signature. let reg_fuse_version: usize = falcon .signature_reg_fuse_version(brom_params.engine_id_mask, brom_params.ucode_id)? .into_safe_cast(); const FUSE_VERSION_USE_LAST_SIG: usize = 0; let index = match reg_fuse_version { // `0` means the last signature should be used. FUSE_VERSION_USE_LAST_SIG => None, // Otherwise, hardware fuse version needs to be subtracted to obtain the index. _ => Some(fuse_version.checked_sub(reg_fuse_version).ok_or(EINVAL)?), }; // Extract the nth signature. Booter is always signed. let sig_chunk = tlv.get_signature(index)?; let signature = BooterSignature(sig_chunk); let ucode_signed = ucode.patch_signature(&signature, patch_loc.into_safe_cast())?; // There are two versions of Booter, one for Turing/GA100, and another for // GA102+. The extraction of the IMEM sections differs between the two // versions. Unfortunately, the file names are the same, and the headers // don't indicate the versions. The only way to differentiate is by the Chipset. let (imem_sec_dst_start, imem_ns_load_target) = if chipset <= Chipset::GA100 { ( app0_code_offset, Some(FalconDmaLoadTarget { src_start: 0, dst_start: os_code_offset, len: os_code_size, }), ) } else { (0, None) }; Ok(Self { imem_sec_load_target: FalconDmaLoadTarget { src_start: app0_code_offset, dst_start: imem_sec_dst_start, len: app0_code_size, }, imem_ns_load_target, dmem_load_target: FalconDmaLoadTarget { src_start: os_data_offset, dst_start: 0, len: os_data_size, }, brom_params, ucode: ucode_signed, }) } /// Load and run the booter firmware on SEC2. /// /// Resets SEC2, loads this firmware image, then boots with the WPR metadata /// address passed via the SEC2 mailboxes. pub(crate) fn run<T>( &self, dev: &device::Device<device::Bound>, sec2_falcon: &Falcon<'_, Sec2>, wpr_meta: &Coherent<T>, ) -> Result { sec2_falcon.reset()?; sec2_falcon.load(self)?; let wpr_dma_address = wpr_meta.dma_address(); let (mbox0, mbox1) = sec2_falcon.boot( Some(wpr_dma_address as u32), Some((wpr_dma_address >> 32) as u32), )?; dev_dbg!(dev, "SEC2 MBOX0: {:#x}, MBOX1: {:#x}\n", mbox0, mbox1); if mbox0 != 0 { dev_err!(dev, "Booter-load failed with error {:#x}\n", mbox0); return Err(ENODEV); } Ok(()) }} impl FalconDmaLoadable for BooterFirmware { fn as_slice(&self) -> &[u8] { self.ucode.0.as_slice() } fn imem_sec_load_params(&self) -> FalconDmaLoadTarget { self.imem_sec_load_target.clone() } fn imem_ns_load_params(&self) -> Option<FalconDmaLoadTarget> { self.imem_ns_load_target.clone() } fn dmem_load_params(&self) -> FalconDmaLoadTarget { self.dmem_load_target.clone() }} impl FalconFirmware for BooterFirmware { type Target = Sec2; fn brom_params(&self) -> FalconBromParams { self.brom_params.clone() } fn boot_addr(&self) -> u32 { if let Some(ns_target) = &self.imem_ns_load_target { ns_target.dst_start } else { self.imem_sec_load_target.src_start } }}
// SPDX-License-Identifier: GPL-2.0
// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
//! Support for loading and patching the `Booter` firmware. `Booter` is a Heavy Secured firmware
//! running on [`Sec2`], that is used on Turing/Ampere to load the GSP firmware into the GSP falcon
//! (and optionally unload it through a separate firmware image).
use core::marker::PhantomData;
use kernel::{
device,
dma::Coherent,
prelude::*, //
};
use crate::{
falcon::{
sec2::Sec2,
Falcon,
FalconBromParams,
FalconDmaLoadTarget,
FalconDmaLoadable,
FalconFirmware, //
},
firmware::{
tlv::{
request_tlv, //
Tlv,
},
FirmwareObject,
FirmwareSignature,
Signed,
Unsigned, //
},
gpu::Chipset,
num::IntoSafeCast,
};
/// Signature for Booter firmware. Their size is encoded into the header and not known a compile
/// time, so we just wrap a byte slices on which we can implement [`FirmwareSignature`].
struct BooterSignature<'a>(&'a [u8]);
impl<'a> AsRef<[u8]> for BooterSignature<'a> {
fn as_ref(&self) -> &[u8] {
self.0
}
}
impl<'a> FirmwareSignature<BooterFirmware> for BooterSignature<'a> {}
/// The `Booter` loader firmware, responsible for loading the GSP.
pub(crate) struct BooterFirmware {
// Load parameters for Secure `IMEM` falcon memory.
imem_sec_load_target: FalconDmaLoadTarget,
// Load parameters for Non-Secure `IMEM` falcon memory,
// used only on Turing and GA100
imem_ns_load_target: Option<FalconDmaLoadTarget>,
// Load parameters for `DMEM` falcon memory.
dmem_load_target: FalconDmaLoadTarget,
// BROM falcon parameters.
brom_params: FalconBromParams,
// Device-mapped firmware image.
ucode: FirmwareObject<Self, Signed>,
}
impl FirmwareObject<BooterFirmware, Unsigned> {
fn new_booter(data: &[u8]) -> Result<Self> {
let mut ucode = KVVec::new();
ucode.extend_from_slice(data, GFP_KERNEL)?;
Ok(Self(ucode, PhantomData))
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub(crate) enum BooterKind {
Loader,
Unloader,
}
impl BooterFirmware {
/// Parses the Booter firmware contained in `fw`, and patches the correct signature so it is
/// ready to be loaded and run on `falcon`.
pub(crate) fn new(
dev: &device::Device<device::Bound>,
kind: BooterKind,
chipset: Chipset,
falcon: &Falcon<'_, <Self as FalconFirmware>::Target>,
) -> Result<Self> {
let fw_name = match kind {
BooterKind::Loader => "booter_load",
BooterKind::Unloader => "booter_unload",
};
let fw = request_tlv(dev, chipset, fw_name)?;
let tlv = Tlv::new(fw.data())?;
dev_dbg!(
dev,
"loaded {} firmware v{}\n",
fw_name,
tlv.get_string(b"VERS")?
);
let os_data_offset = tlv.get_u32(b"DAOF")?;
let os_data_size = tlv.get_u32(b"DASZ")?;
let os_code_offset = tlv.get_u32(b"CDOF")?;
let os_code_size = tlv.get_u32(b"CDSZ")?;
let patch_loc = tlv.get_u32(b"PLOC")?;
let fuse_version: usize = tlv.get_u32(b"FUSE")?.into_safe_cast();
let engine_id = tlv.get_u32(b"ENID")?;
let ucode_id = tlv.get_u32(b"UCID")?;
let app0_code_offset = tlv.get_u32(b"A0CO")?;
let app0_code_size = tlv.get_u32(b"A0CS")?;
let brom_params = FalconBromParams {
// `os_data_offset` is an absolute index, but `pkc_data_offset` is from the
// signature patch location.
pkc_data_offset: patch_loc.checked_sub(os_data_offset).ok_or(EINVAL)?,
engine_id_mask: u16::try_from(engine_id).map_err(|_| EINVAL)?,
ucode_id: u8::try_from(ucode_id).map_err(|_| EINVAL)?,
};
let ucode = tlv
.get_bytes(b"BLOB")
.and_then(FirmwareObject::<Self, _>::new_booter)?;
// Obtain the version from the fuse register, and extract the corresponding
// signature.
let reg_fuse_version: usize = falcon
.signature_reg_fuse_version(brom_params.engine_id_mask, brom_params.ucode_id)?
.into_safe_cast();
const FUSE_VERSION_USE_LAST_SIG: usize = 0;
let index = match reg_fuse_version {
// `0` means the last signature should be used.
FUSE_VERSION_USE_LAST_SIG => None,
// Otherwise, hardware fuse version needs to be subtracted to obtain the index.
_ => Some(fuse_version.checked_sub(reg_fuse_version).ok_or(EINVAL)?),
};
// Extract the nth signature. Booter is always signed.
let sig_chunk = tlv.get_signature(index)?;
let signature = BooterSignature(sig_chunk);
let ucode_signed = ucode.patch_signature(&signature, patch_loc.into_safe_cast())?;
// There are two versions of Booter, one for Turing/GA100, and another for
// GA102+. The extraction of the IMEM sections differs between the two
// versions. Unfortunately, the file names are the same, and the headers
// don't indicate the versions. The only way to differentiate is by the Chipset.
let (imem_sec_dst_start, imem_ns_load_target) = if chipset <= Chipset::GA100 {
(
app0_code_offset,
Some(FalconDmaLoadTarget {
src_start: 0,
dst_start: os_code_offset,
len: os_code_size,
}),
)
} else {
(0, None)
};
Ok(Self {
imem_sec_load_target: FalconDmaLoadTarget {
src_start: app0_code_offset,
dst_start: imem_sec_dst_start,
len: app0_code_size,
},
imem_ns_load_target,
dmem_load_target: FalconDmaLoadTarget {
src_start: os_data_offset,
dst_start: 0,
len: os_data_size,
},
brom_params,
ucode: ucode_signed,
})
}
/// Load and run the booter firmware on SEC2.
///
/// Resets SEC2, loads this firmware image, then boots with the WPR metadata
/// address passed via the SEC2 mailboxes.
pub(crate) fn run<T>(
&self,
dev: &device::Device<device::Bound>,
sec2_falcon: &Falcon<'_, Sec2>,
wpr_meta: &Coherent<T>,
) -> Result {
sec2_falcon.reset()?;
sec2_falcon.load(self)?;
let wpr_dma_address = wpr_meta.dma_address();
let (mbox0, mbox1) = sec2_falcon.boot(
Some(wpr_dma_address as u32),
Some((wpr_dma_address >> 32) as u32),
)?;
dev_dbg!(dev, "SEC2 MBOX0: {:#x}, MBOX1: {:#x}\n", mbox0, mbox1);
if mbox0 != 0 {
dev_err!(dev, "Booter-load failed with error {:#x}\n", mbox0);
return Err(ENODEV);
}
Ok(())
}
}
impl FalconDmaLoadable for BooterFirmware {
fn as_slice(&self) -> &[u8] {
self.ucode.0.as_slice()
}
fn imem_sec_load_params(&self) -> FalconDmaLoadTarget {
self.imem_sec_load_target.clone()
}
fn imem_ns_load_params(&self) -> Option<FalconDmaLoadTarget> {
self.imem_ns_load_target.clone()
}
fn dmem_load_params(&self) -> FalconDmaLoadTarget {
self.dmem_load_target.clone()
}
}
impl FalconFirmware for BooterFirmware {
type Target = Sec2;
fn brom_params(&self) -> FalconBromParams {
self.brom_params.clone()
}
fn boot_addr(&self) -> u32 {
if let Some(ns_target) = &self.imem_ns_load_target {
ns_target.dst_start
} else {
self.imem_sec_load_target.src_start
}
}
}