[PATCH v4 12/33] gpu: nova-core: don't assume 64-bit firmware images

John Hubbard
2026-02-10T02:46:31Z

Add ElfHeader and ElfSectionHeader traits to abstract out differences
between ELF32 and ELF64. Implement these for ELF64.

This is in preparation for upcoming ELF32 section support, and for
auto-selecting ELF32 or ELF64.

Signed-off-by: John Hubbard <jhubbard@nvidia.com>
---
drivers/gpu/nova-core/firmware.rs | 99 ++++++++++++++++++++++---------
1 file changed, 72 insertions(+), 27 deletions(-)

diff --git a/drivers/gpu/nova-core/firmware.rs b/drivers/gpu/nova-core/firmware.rs
index 72cefc3142ea..6ed76a7e15f1 100644
--- a/drivers/gpu/nova-core/firmware.rs
+++ b/drivers/gpu/nova-core/firmware.rs
@@ -453,17 +453,60 @@ mod elf {
transmute::FromBytes, //
};

- /// Newtype to provide a [`FromBytes`] implementation.
+ /// Trait to abstract over ELF header differences (32-bit vs 64-bit).
+ trait ElfHeader: FromBytes {
+ fn shnum(&self) -> u16;
+ fn shoff(&self) -> u64;
+ fn shstrndx(&self) -> u16;
+ }
+
+ /// Trait to abstract over ELF section header differences (32-bit vs 64-bit).
+ trait ElfSectionHeader: FromBytes {
+ fn name(&self) -> u32;
+ fn offset(&self) -> u64;
+ fn size(&self) -> u64;
+ }
+
+ /// Newtype to provide [`FromBytes`] and [`ElfHeader`] implementations.
#[repr(transparent)]
struct Elf64Hdr(bindings::elf64_hdr);
// SAFETY: all bit patterns are valid for this type, and it doesn't use interior mutability.
unsafe impl FromBytes for Elf64Hdr {}

+ impl ElfHeader for Elf64Hdr {
+ fn shnum(&self) -> u16 {
+ self.0.e_shnum
+ }
+
+ fn shoff(&self) -> u64 {
+ self.0.e_shoff
+ }
+
+ fn shstrndx(&self) -> u16 {
+ self.0.e_shstrndx
+ }
+ }
+
+ /// Newtype to provide [`FromBytes`] and [`ElfSectionHeader`] implementations.
#[repr(transparent)]
struct Elf64SHdr(bindings::elf64_shdr);
// SAFETY: all bit patterns are valid for this type, and it doesn't use interior mutability.
unsafe impl FromBytes for Elf64SHdr {}

+ impl ElfSectionHeader for Elf64SHdr {
+ fn name(&self) -> u32 {
+ self.0.sh_name
+ }
+
+ fn offset(&self) -> u64 {
+ self.0.sh_offset
+ }
+
+ fn size(&self) -> u64 {
+ self.0.sh_size
+ }
+ }
+
/// Returns a NULL-terminated string from the ELF image at `offset`.
fn elf_str(elf: &[u8], offset: u64) -> Option<&str> {
let idx = usize::try_from(offset).ok()?;
@@ -471,47 +514,49 @@ fn elf_str(elf: &[u8], offset: u64) -> Option<&str> {
CStr::from_bytes_until_nul(bytes).ok()?.to_str().ok()
}

- /// Tries to extract section with name `name` from the ELF64 image `elf`, and returns it.
- 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;
+ fn elf_section_generic<'a, H, S>(elf: &'a [u8], name: &str) -> Option<&'a [u8]>
+ where
+ H: ElfHeader,
+ S: ElfSectionHeader,
+ {
+ let hdr = H::from_bytes(elf.get(0..size_of::<H>())?)?;

- // Get all the section headers.
- 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))?;
+ let shdr_num = usize::from(hdr.shnum());
+ let shdr_start = usize::try_from(hdr.shoff()).ok()?;
+ let shdr_end = shdr_num
+ .checked_mul(size_of::())
+ .and_then(|v| v.checked_add(shdr_start))?;

- elf.get(shdr_start..shdr_end)
- .map(|slice| slice.chunks_exact(size_of::<Elf64SHdr>()))?
- };
+ // Get all the section headers as an iterator over byte chunks.
+ let shdr_bytes = elf.get(shdr_start..shdr_end)?;
+ let mut shdr_iter = shdr_bytes.chunks_exact(size_of::());

// Get the strings table.
- let strhdr = shdr
+ let strhdr = shdr_iter
.clone()
- .nth(usize::from(hdr.e_shstrndx))
- .and_then(Elf64SHdr::from_bytes)?;
+ .nth(usize::from(hdr.shstrndx()))
+ .and_then(S::from_bytes)?;

// Find the section which name matches `name` and return it.
- 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))?;
+ shdr_iter.find_map(|sh_bytes| {
+ let sh = S::from_bytes(sh_bytes)?;
+ let name_offset = strhdr.offset().checked_add(u64::from(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)
+ let start = usize::try_from(sh.offset()).ok()?;
+ let end = usize::try_from(sh.size())
.ok()
- .and_then(|sh_size| start.checked_add(sh_size))?;
-
+ .and_then(|sz| start.checked_add(sz))?;
elf.get(start..end)
})
}
+
+ /// Extract the section with name `name` from the ELF64 image `elf`.
+ pub(super) fn elf64_section<'a>(elf: &'a [u8], name: &str) -> Option<&'a [u8]> {
+ elf_section_generic::<Elf64Hdr, Elf64SHdr>(elf, name)
+ }
}
--
2.53.0