// 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
}
}
}