use core::marker::PhantomData;
use kernel::{
device,
prelude::*,
transmute::FromBytes, };
use crate::{
driver::Bar0,
falcon::{
sec2::Sec2,
Falcon,
FalconBromParams,
FalconDmaLoadTarget,
FalconDmaLoadable,
FalconFirmware, },
firmware::{
BinFirmware,
FirmwareObject,
FirmwareSignature,
Signed,
Unsigned, },
gpu::Chipset,
num::{
FromSafeCast,
IntoSafeCast, },
};
fn frombytes_at<S: FromBytes + Sized>(slice: &[u8], offset: usize) -> Result<S> {
let end = offset.checked_add(size_of::<S>()).ok_or(EINVAL)?;
slice
.get(offset..end)
.and_then(S::from_bytes_copy)
.ok_or(EINVAL)
}
#[repr(C)]
#[derive(Debug, Clone)]
struct HsHeaderV2 {
sig_prod_offset: u32,
sig_prod_size: u32,
patch_loc_offset: u32,
patch_sig_offset: u32,
meta_data_offset: u32,
meta_data_size: u32,
num_sig_offset: u32,
header_offset: u32,
header_size: u32,
}
unsafe impl FromBytes for HsHeaderV2 {}
struct HsFirmwareV2<'a> {
hdr: HsHeaderV2,
fw: &'a [u8],
}
impl<'a> HsFirmwareV2<'a> {
fn new(bin_fw: &BinFirmware<'a>) -> Result<Self> {
frombytes_at::<HsHeaderV2>(bin_fw.fw, bin_fw.hdr.header_offset.into_safe_cast())
.map(|hdr| Self { hdr, fw: bin_fw.fw })
}
fn patch_location(&self) -> Result<u32> {
frombytes_at::<u32>(self.fw, self.hdr.patch_loc_offset.into_safe_cast())
}
fn signatures_iter(&'a self) -> Result<impl Iterator<Item = BooterSignature<'a>>> {
let num_sig = frombytes_at::<u32>(self.fw, self.hdr.num_sig_offset.into_safe_cast())?;
let iter = match self.hdr.sig_prod_size.checked_div(num_sig) {
None => (&[] as &[u8]).chunks_exact(1),
Some(sig_size) => {
let patch_sig =
frombytes_at::<u32>(self.fw, self.hdr.patch_sig_offset.into_safe_cast())?;
let signatures_start = self
.hdr
.sig_prod_offset
.checked_add(patch_sig)
.map(usize::from_safe_cast)
.ok_or(EINVAL)?;
let signatures_end = signatures_start
.checked_add(usize::from_safe_cast(self.hdr.sig_prod_size))
.ok_or(EINVAL)?;
self.fw
.get(signatures_start..signatures_end)
.ok_or(EINVAL)?
.chunks_exact(sig_size.into_safe_cast())
}
};
Ok(iter.map(BooterSignature))
}
}
#[repr(C)]
struct HsSignatureParams {
fuse_ver: u32,
engine_id_mask: u32,
ucode_id: u32,
}
unsafe impl FromBytes for HsSignatureParams {}
impl HsSignatureParams {
fn new(hs_fw: &HsFirmwareV2<'_>) -> Result<Self> {
let start = usize::from_safe_cast(hs_fw.hdr.meta_data_offset);
let end = start
.checked_add(hs_fw.hdr.meta_data_size.into_safe_cast())
.ok_or(EINVAL)?;
hs_fw
.fw
.get(start..end)
.and_then(Self::from_bytes_copy)
.ok_or(EINVAL)
}
}
#[repr(C)]
#[derive(Debug, Clone)]
struct HsLoadHeaderV2 {
os_code_offset: u32,
os_code_size: u32,
os_data_offset: u32,
os_data_size: u32,
num_apps: u32,
}
unsafe impl FromBytes for HsLoadHeaderV2 {}
impl HsLoadHeaderV2 {
fn new(hs_fw: &HsFirmwareV2<'_>) -> Result<Self> {
frombytes_at::<Self>(hs_fw.fw, hs_fw.hdr.header_offset.into_safe_cast())
}
}
#[repr(C)]
#[derive(Debug, Clone)]
struct HsLoadHeaderV2App {
offset: u32,
len: u32,
}
unsafe impl FromBytes for HsLoadHeaderV2App {}
impl HsLoadHeaderV2App {
fn new(hs_fw: &HsFirmwareV2<'_>, idx: u32) -> Result<Self> {
let load_hdr = HsLoadHeaderV2::new(hs_fw)?;
if idx >= load_hdr.num_apps {
Err(EINVAL)
} else {
frombytes_at::<Self>(
hs_fw.fw,
usize::from_safe_cast(hs_fw.hdr.header_offset)
.checked_add(size_of::<HsLoadHeaderV2>())
.and_then(|offset| {
offset
.checked_add(usize::from_safe_cast(idx).checked_mul(size_of::<Self>())?)
})
.ok_or(EINVAL)?,
)
}
}
}
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> {}
pub(crate) struct BooterFirmware {
imem_sec_load_target: FalconDmaLoadTarget,
imem_ns_load_target: Option<FalconDmaLoadTarget>,
dmem_load_target: FalconDmaLoadTarget,
brom_params: FalconBromParams,
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,
#[expect(unused)]
Unloader,
}
impl BooterFirmware {
pub(crate) fn new(
dev: &device::Device<device::Bound>,
kind: BooterKind,
chipset: Chipset,
ver: &str,
falcon: &Falcon<<Self as FalconFirmware>::Target>,
bar: &Bar0,
) -> Result<Self> {
let fw_name = match kind {
BooterKind::Loader => "booter_load",
BooterKind::Unloader => "booter_unload",
};
let fw = super::request_firmware(dev, chipset, fw_name, ver)?;
let bin_fw = BinFirmware::new(&fw)?;
let hs_fw = HsFirmwareV2::new(&bin_fw)?;
let load_hdr = HsLoadHeaderV2::new(&hs_fw)?;
let patch_loc = hs_fw.patch_location()?;
let sig_params = HsSignatureParams::new(&hs_fw)?;
let brom_params = FalconBromParams {
pkc_data_offset: patch_loc
.checked_sub(load_hdr.os_data_offset)
.ok_or(EINVAL)?,
engine_id_mask: u16::try_from(sig_params.engine_id_mask).map_err(|_| EINVAL)?,
ucode_id: u8::try_from(sig_params.ucode_id).map_err(|_| EINVAL)?,
};
let app0 = HsLoadHeaderV2App::new(&hs_fw, 0)?;
let ucode = bin_fw
.data()
.ok_or(EINVAL)
.and_then(FirmwareObject::<Self, _>::new_booter)?;
let ucode_signed = {
let mut signatures = hs_fw.signatures_iter()?.peekable();
if signatures.peek().is_none() {
ucode.no_patch_signature()
} else {
let reg_fuse_version = falcon.signature_reg_fuse_version(
bar,
brom_params.engine_id_mask,
brom_params.ucode_id,
)?;
const FUSE_VERSION_USE_LAST_SIG: u32 = 0;
let signature = match reg_fuse_version {
FUSE_VERSION_USE_LAST_SIG => signatures.last(),
reg_fuse_version => {
let Some(idx) = sig_params.fuse_ver.checked_sub(reg_fuse_version) else {
dev_err!(dev, "invalid fuse version for Booter firmware\n");
return Err(EINVAL);
};
signatures.nth(idx.into_safe_cast())
}
}
.ok_or(EINVAL)?;
ucode.patch_signature(&signature, patch_loc.into_safe_cast())?
}
};
let (imem_sec_dst_start, imem_ns_load_target) = if chipset <= Chipset::GA100 {
(
app0.offset,
Some(FalconDmaLoadTarget {
src_start: 0,
dst_start: load_hdr.os_code_offset,
len: load_hdr.os_code_size,
}),
)
} else {
(0, None)
};
Ok(Self {
imem_sec_load_target: FalconDmaLoadTarget {
src_start: app0.offset,
dst_start: imem_sec_dst_start,
len: app0.len,
},
imem_ns_load_target,
dmem_load_target: FalconDmaLoadTarget {
src_start: load_hdr.os_data_offset,
dst_start: 0,
len: load_hdr.os_data_size,
},
brom_params,
ucode: ucode_signed,
})
}
}
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
}
}
}