gpu: nova-core: don't assume 64-bit firmware images

Introduce a single ELF format abstraction that ties each ELF header
type to its matching section-header type. This keeps the shared
section parser ready for upcoming ELF32 support and avoids mixing
32-bit and 64-bit ELF layouts by mistake.

Signed-off-by: John Hubbard <jhubbard@nvidia.com>
Reviewed-by: Eliot Courtney <ecourtney@nvidia.com>
Link: https://patch.msgid.link/20260602032111.224790-8-jhubbard@nvidia.com
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
This commit is contained in:
John Hubbard 2026-06-01 20:20:55 -07:00 committed by Alexandre Courbot
parent a5bf742bc2
commit bd581ff381

View File

@ -1,4 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
//! Contains structures and functions dedicated to the parsing, building and patching of firmwares
//! to be loaded into a given execution unit.
@ -467,17 +468,72 @@ mod elf {
transmute::FromBytes, //
};
/// Trait to abstract over ELF header differences.
trait ElfHeader: FromBytes {
fn shnum(&self) -> u16;
fn shoff(&self) -> u64;
fn shstrndx(&self) -> u16;
}
/// Trait to abstract over ELF section-header differences.
trait ElfSectionHeader: FromBytes {
fn name(&self) -> u32;
fn offset(&self) -> u64;
fn size(&self) -> u64;
}
/// Trait describing a matching ELF header and section-header format.
trait ElfFormat {
type Header: ElfHeader;
type SectionHeader: ElfSectionHeader;
}
/// Newtype to provide a [`FromBytes`] implementation.
#[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
}
}
#[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
}
}
struct Elf64Format;
impl ElfFormat for Elf64Format {
type Header = Elf64Hdr;
type SectionHeader = Elf64SHdr;
}
/// 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()?;
@ -485,47 +541,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, F>(elf: &'a [u8], name: &str) -> Option<&'a [u8]>
where
F: ElfFormat,
{
let hdr = F::Header::from_bytes(elf.get(0..size_of::<F::Header>())?)?;
// 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::<F::SectionHeader>())
.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::<F::SectionHeader>());
// 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(F::SectionHeader::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 = F::SectionHeader::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)
})
}
/// Tries to extract section with name `name` from the ELF64 image `elf`, and returns it.
pub(super) fn elf64_section<'a>(elf: &'a [u8], name: &str) -> Option<&'a [u8]> {
elf_section_generic::<Elf64Format>(elf, name)
}
}