use core::marker::PhantomData;
use core::ops::Deref;
use kernel::{
device,
firmware,
prelude::*,
str::CString,
transmute::FromBytes, };
use crate::{
falcon::{
FalconDmaLoadTarget,
FalconFirmware, },
gpu,
num::{
FromSafeCast,
IntoSafeCast, },
};
pub(crate) mod booter;
pub(crate) mod fwsec;
pub(crate) mod gsp;
pub(crate) mod riscv;
pub(crate) const FIRMWARE_VERSION: &str = "570.144";
fn request_firmware(
dev: &device::Device,
chipset: gpu::Chipset,
name: &str,
ver: &str,
) -> Result<firmware::Firmware> {
let chip_name = chipset.name();
CString::try_from_fmt(fmt!("nvidia/{chip_name}/gsp/{name}-{ver}.bin"))
.and_then(|path| firmware::Firmware::request(&path, dev))
}
#[repr(C)]
#[derive(Debug, Clone)]
pub(crate) struct FalconUCodeDescV2 {
hdr: u32,
stored_size: u32,
pub(crate) uncompressed_size: u32,
pub(crate) virtual_entry: u32,
pub(crate) interface_offset: u32,
pub(crate) imem_phys_base: u32,
pub(crate) imem_load_size: u32,
pub(crate) imem_virt_base: u32,
pub(crate) imem_sec_base: u32,
pub(crate) imem_sec_size: u32,
pub(crate) dmem_offset: u32,
pub(crate) dmem_phys_base: u32,
pub(crate) dmem_load_size: u32,
pub(crate) alt_imem_load_size: u32,
pub(crate) alt_dmem_load_size: u32,
}
unsafe impl FromBytes for FalconUCodeDescV2 {}
#[repr(C)]
#[derive(Debug, Clone)]
pub(crate) struct FalconUCodeDescV3 {
hdr: u32,
stored_size: u32,
pub(crate) pkc_data_offset: u32,
pub(crate) interface_offset: u32,
pub(crate) imem_phys_base: u32,
pub(crate) imem_load_size: u32,
pub(crate) imem_virt_base: u32,
pub(crate) dmem_phys_base: u32,
pub(crate) dmem_load_size: u32,
pub(crate) engine_id_mask: u16,
pub(crate) ucode_id: u8,
pub(crate) signature_count: u8,
pub(crate) signature_versions: u16,
_reserved: u16,
}
unsafe impl FromBytes for FalconUCodeDescV3 {}
#[derive(Debug, Clone)]
pub(crate) enum FalconUCodeDesc {
V2(FalconUCodeDescV2),
V3(FalconUCodeDescV3),
}
impl Deref for FalconUCodeDesc {
type Target = dyn FalconUCodeDescriptor;
fn deref(&self) -> &Self::Target {
match self {
FalconUCodeDesc::V2(v2) => v2,
FalconUCodeDesc::V3(v3) => v3,
}
}
}
pub(crate) trait FalconUCodeDescriptor {
fn hdr(&self) -> u32;
fn imem_load_size(&self) -> u32;
fn interface_offset(&self) -> u32;
fn dmem_load_size(&self) -> u32;
fn pkc_data_offset(&self) -> u32;
fn engine_id_mask(&self) -> u16;
fn ucode_id(&self) -> u8;
fn signature_count(&self) -> u8;
fn signature_versions(&self) -> u16;
fn size(&self) -> usize {
let hdr = self.hdr();
const HDR_SIZE_SHIFT: u32 = 16;
const HDR_SIZE_MASK: u32 = 0xffff0000;
((hdr & HDR_SIZE_MASK) >> HDR_SIZE_SHIFT).into_safe_cast()
}
fn imem_sec_load_params(&self) -> FalconDmaLoadTarget;
fn imem_ns_load_params(&self) -> Option<FalconDmaLoadTarget>;
fn dmem_load_params(&self) -> FalconDmaLoadTarget;
}
impl FalconUCodeDescriptor for FalconUCodeDescV2 {
fn hdr(&self) -> u32 {
self.hdr
}
fn imem_load_size(&self) -> u32 {
self.imem_load_size
}
fn interface_offset(&self) -> u32 {
self.interface_offset
}
fn dmem_load_size(&self) -> u32 {
self.dmem_load_size
}
fn pkc_data_offset(&self) -> u32 {
0
}
fn engine_id_mask(&self) -> u16 {
0
}
fn ucode_id(&self) -> u8 {
0
}
fn signature_count(&self) -> u8 {
0
}
fn signature_versions(&self) -> u16 {
0
}
fn imem_sec_load_params(&self) -> FalconDmaLoadTarget {
let imem_sec_start = self.imem_sec_base.saturating_sub(self.imem_virt_base);
FalconDmaLoadTarget {
src_start: imem_sec_start,
dst_start: self.imem_phys_base.saturating_add(imem_sec_start),
len: self.imem_sec_size,
}
}
fn imem_ns_load_params(&self) -> Option<FalconDmaLoadTarget> {
Some(FalconDmaLoadTarget {
src_start: 0,
dst_start: self.imem_phys_base,
len: self.imem_load_size.saturating_sub(self.imem_sec_size),
})
}
fn dmem_load_params(&self) -> FalconDmaLoadTarget {
FalconDmaLoadTarget {
src_start: self.dmem_offset,
dst_start: self.dmem_phys_base,
len: self.dmem_load_size,
}
}
}
impl FalconUCodeDescriptor for FalconUCodeDescV3 {
fn hdr(&self) -> u32 {
self.hdr
}
fn imem_load_size(&self) -> u32 {
self.imem_load_size
}
fn interface_offset(&self) -> u32 {
self.interface_offset
}
fn dmem_load_size(&self) -> u32 {
self.dmem_load_size
}
fn pkc_data_offset(&self) -> u32 {
self.pkc_data_offset
}
fn engine_id_mask(&self) -> u16 {
self.engine_id_mask
}
fn ucode_id(&self) -> u8 {
self.ucode_id
}
fn signature_count(&self) -> u8 {
self.signature_count
}
fn signature_versions(&self) -> u16 {
self.signature_versions
}
fn imem_sec_load_params(&self) -> FalconDmaLoadTarget {
FalconDmaLoadTarget {
src_start: 0,
dst_start: self.imem_phys_base,
len: self.imem_load_size,
}
}
fn imem_ns_load_params(&self) -> Option<FalconDmaLoadTarget> {
None
}
fn dmem_load_params(&self) -> FalconDmaLoadTarget {
FalconDmaLoadTarget {
src_start: self.imem_load_size,
dst_start: self.dmem_phys_base,
len: self.dmem_load_size,
}
}
}
trait SignedState {}
struct Unsigned;
impl SignedState for Unsigned {}
struct Signed;
impl SignedState for Signed {}
struct FirmwareObject<F: FalconFirmware, S: SignedState>(KVVec<u8>, PhantomData<(F, S)>);
trait FirmwareSignature<F: FalconFirmware>: AsRef<[u8]> {}
impl<F: FalconFirmware> FirmwareObject<F, Unsigned> {
fn patch_signature<S: FirmwareSignature<F>>(
mut self,
signature: &S,
signature_start: usize,
) -> Result<FirmwareObject<F, Signed>> {
let signature_bytes = signature.as_ref();
let signature_end = signature_start
.checked_add(signature_bytes.len())
.ok_or(EOVERFLOW)?;
let dst = self
.0
.get_mut(signature_start..signature_end)
.ok_or(EINVAL)?;
dst.copy_from_slice(signature_bytes);
Ok(FirmwareObject(self.0, PhantomData))
}
fn no_patch_signature(self) -> FirmwareObject<F, Signed> {
FirmwareObject(self.0, PhantomData)
}
}
#[repr(C)]
#[derive(Debug, Clone)]
struct BinHdr {
bin_magic: u32,
bin_ver: u32,
bin_size: u32,
header_offset: u32,
data_offset: u32,
data_size: u32,
}
unsafe impl FromBytes for BinHdr {}
struct BinFirmware<'a> {
hdr: BinHdr,
fw: &'a [u8],
}
impl<'a> BinFirmware<'a> {
fn new(fw: &'a firmware::Firmware) -> Result<Self> {
const BIN_MAGIC: u32 = 0x10de;
let fw = fw.data();
fw.get(0..size_of::<BinHdr>())
.and_then(BinHdr::from_bytes_copy)
.and_then(|hdr| {
if hdr.bin_magic == BIN_MAGIC {
Some(hdr)
} else {
None
}
})
.map(|hdr| Self { hdr, fw })
.ok_or(EINVAL)
}
fn data(&self) -> Option<&[u8]> {
let fw_start = usize::from_safe_cast(self.hdr.data_offset);
let fw_size = usize::from_safe_cast(self.hdr.data_size);
let fw_end = fw_start.checked_add(fw_size)?;
self.fw.get(fw_start..fw_end)
}
}
pub(crate) struct ModInfoBuilder<const N: usize>(firmware::ModInfoBuilder<N>);
impl<const N: usize> ModInfoBuilder<N> {
const fn make_entry_file(self, chipset: &str, fw: &str) -> Self {
ModInfoBuilder(
self.0
.new_entry()
.push("nvidia/")
.push(chipset)
.push("/gsp/")
.push(fw)
.push("-")
.push(FIRMWARE_VERSION)
.push(".bin"),
)
}
const fn make_entry_chipset(self, chipset: gpu::Chipset) -> Self {
let name = chipset.name();
let this = self
.make_entry_file(name, "booter_load")
.make_entry_file(name, "booter_unload")
.make_entry_file(name, "bootloader")
.make_entry_file(name, "gsp");
if chipset.needs_fwsec_bootloader() {
this.make_entry_file(name, "gen_bootloader")
} else {
this
}
}
pub(crate) const fn create(
module_name: &'static core::ffi::CStr,
) -> firmware::ModInfoBuilder<N> {
let mut this = Self(firmware::ModInfoBuilder::new(module_name));
let mut i = 0;
while i < gpu::Chipset::ALL.len() {
this = this.make_entry_chipset(gpu::Chipset::ALL[i]);
i += 1;
}
this.0
}
}
mod elf {
use core::mem::size_of;
use kernel::{
bindings,
str::CStr,
transmute::FromBytes, };
#[repr(transparent)]
struct Elf64Hdr(bindings::elf64_hdr);
unsafe impl FromBytes for Elf64Hdr {}
#[repr(transparent)]
struct Elf64SHdr(bindings::elf64_shdr);
unsafe impl FromBytes for Elf64SHdr {}
fn elf_str(elf: &[u8], offset: u64) -> Option<&str> {
let idx = usize::try_from(offset).ok()?;
let bytes = elf.get(idx..)?;
CStr::from_bytes_until_nul(bytes).ok()?.to_str().ok()
}
pub(super) fn elf64_section<'a, 'b>(elf: &'a [u8], name: &'b str) -> Option<&'a [u8]> {
let hdr = &elf
.get(0..size_of::<bindings::elf64_hdr>())
.and_then(Elf64Hdr::from_bytes)?
.0;
let mut shdr = {
let shdr_num = usize::from(hdr.e_shnum);
let shdr_start = usize::try_from(hdr.e_shoff).ok()?;
let shdr_end = shdr_num
.checked_mul(size_of::<Elf64SHdr>())
.and_then(|v| v.checked_add(shdr_start))?;
elf.get(shdr_start..shdr_end)
.map(|slice| slice.chunks_exact(size_of::<Elf64SHdr>()))?
};
let strhdr = shdr
.clone()
.nth(usize::from(hdr.e_shstrndx))
.and_then(Elf64SHdr::from_bytes)?;
shdr.find_map(|sh| {
let hdr = Elf64SHdr::from_bytes(sh)?;
let name_offset = strhdr.0.sh_offset.checked_add(u64::from(hdr.0.sh_name))?;
let section_name = elf_str(elf, name_offset)?;
if section_name != name {
return None;
}
let start = usize::try_from(hdr.0.sh_offset).ok()?;
let end = usize::try_from(hdr.0.sh_size)
.ok()
.and_then(|sh_size| start.checked_add(sh_size))?;
elf.get(start..end)
})
}
}