Skip to content

Commit bd581ff

Browse files
johnhubbardGnurou
authored andcommitted
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>
1 parent a5bf742 commit bd581ff

1 file changed

Lines changed: 85 additions & 27 deletions

File tree

drivers/gpu/nova-core/firmware.rs

Lines changed: 85 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// SPDX-License-Identifier: GPL-2.0
2+
// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
23

34
//! Contains structures and functions dedicated to the parsing, building and patching of firmwares
45
//! to be loaded into a given execution unit.
@@ -467,65 +468,122 @@ mod elf {
467468
transmute::FromBytes, //
468469
};
469470

471+
/// Trait to abstract over ELF header differences.
472+
trait ElfHeader: FromBytes {
473+
fn shnum(&self) -> u16;
474+
fn shoff(&self) -> u64;
475+
fn shstrndx(&self) -> u16;
476+
}
477+
478+
/// Trait to abstract over ELF section-header differences.
479+
trait ElfSectionHeader: FromBytes {
480+
fn name(&self) -> u32;
481+
fn offset(&self) -> u64;
482+
fn size(&self) -> u64;
483+
}
484+
485+
/// Trait describing a matching ELF header and section-header format.
486+
trait ElfFormat {
487+
type Header: ElfHeader;
488+
type SectionHeader: ElfSectionHeader;
489+
}
490+
470491
/// Newtype to provide a [`FromBytes`] implementation.
471492
#[repr(transparent)]
472493
struct Elf64Hdr(bindings::elf64_hdr);
473494
// SAFETY: all bit patterns are valid for this type, and it doesn't use interior mutability.
474495
unsafe impl FromBytes for Elf64Hdr {}
475496

497+
impl ElfHeader for Elf64Hdr {
498+
fn shnum(&self) -> u16 {
499+
self.0.e_shnum
500+
}
501+
502+
fn shoff(&self) -> u64 {
503+
self.0.e_shoff
504+
}
505+
506+
fn shstrndx(&self) -> u16 {
507+
self.0.e_shstrndx
508+
}
509+
}
510+
476511
#[repr(transparent)]
477512
struct Elf64SHdr(bindings::elf64_shdr);
478513
// SAFETY: all bit patterns are valid for this type, and it doesn't use interior mutability.
479514
unsafe impl FromBytes for Elf64SHdr {}
480515

516+
impl ElfSectionHeader for Elf64SHdr {
517+
fn name(&self) -> u32 {
518+
self.0.sh_name
519+
}
520+
521+
fn offset(&self) -> u64 {
522+
self.0.sh_offset
523+
}
524+
525+
fn size(&self) -> u64 {
526+
self.0.sh_size
527+
}
528+
}
529+
530+
struct Elf64Format;
531+
532+
impl ElfFormat for Elf64Format {
533+
type Header = Elf64Hdr;
534+
type SectionHeader = Elf64SHdr;
535+
}
536+
481537
/// Returns a NULL-terminated string from the ELF image at `offset`.
482538
fn elf_str(elf: &[u8], offset: u64) -> Option<&str> {
483539
let idx = usize::try_from(offset).ok()?;
484540
let bytes = elf.get(idx..)?;
485541
CStr::from_bytes_until_nul(bytes).ok()?.to_str().ok()
486542
}
487543

488-
/// Tries to extract section with name `name` from the ELF64 image `elf`, and returns it.
489-
pub(super) fn elf64_section<'a, 'b>(elf: &'a [u8], name: &'b str) -> Option<&'a [u8]> {
490-
let hdr = &elf
491-
.get(0..size_of::<bindings::elf64_hdr>())
492-
.and_then(Elf64Hdr::from_bytes)?
493-
.0;
494-
495-
// Get all the section headers.
496-
let mut shdr = {
497-
let shdr_num = usize::from(hdr.e_shnum);
498-
let shdr_start = usize::try_from(hdr.e_shoff).ok()?;
499-
let shdr_end = shdr_num
500-
.checked_mul(size_of::<Elf64SHdr>())
501-
.and_then(|v| v.checked_add(shdr_start))?;
502-
503-
elf.get(shdr_start..shdr_end)
504-
.map(|slice| slice.chunks_exact(size_of::<Elf64SHdr>()))?
505-
};
544+
fn elf_section_generic<'a, F>(elf: &'a [u8], name: &str) -> Option<&'a [u8]>
545+
where
546+
F: ElfFormat,
547+
{
548+
let hdr = F::Header::from_bytes(elf.get(0..size_of::<F::Header>())?)?;
549+
550+
let shdr_num = usize::from(hdr.shnum());
551+
let shdr_start = usize::try_from(hdr.shoff()).ok()?;
552+
let shdr_end = shdr_num
553+
.checked_mul(size_of::<F::SectionHeader>())
554+
.and_then(|v| v.checked_add(shdr_start))?;
555+
556+
// Get all the section headers as an iterator over byte chunks.
557+
let shdr_bytes = elf.get(shdr_start..shdr_end)?;
558+
let mut shdr_iter = shdr_bytes.chunks_exact(size_of::<F::SectionHeader>());
506559

507560
// Get the strings table.
508-
let strhdr = shdr
561+
let strhdr = shdr_iter
509562
.clone()
510-
.nth(usize::from(hdr.e_shstrndx))
511-
.and_then(Elf64SHdr::from_bytes)?;
563+
.nth(usize::from(hdr.shstrndx()))
564+
.and_then(F::SectionHeader::from_bytes)?;
512565

513566
// Find the section which name matches `name` and return it.
514-
shdr.find_map(|sh| {
515-
let hdr = Elf64SHdr::from_bytes(sh)?;
516-
let name_offset = strhdr.0.sh_offset.checked_add(u64::from(hdr.0.sh_name))?;
567+
shdr_iter.find_map(|sh_bytes| {
568+
let sh = F::SectionHeader::from_bytes(sh_bytes)?;
569+
let name_offset = strhdr.offset().checked_add(u64::from(sh.name()))?;
517570
let section_name = elf_str(elf, name_offset)?;
518571

519572
if section_name != name {
520573
return None;
521574
}
522575

523-
let start = usize::try_from(hdr.0.sh_offset).ok()?;
524-
let end = usize::try_from(hdr.0.sh_size)
576+
let start = usize::try_from(sh.offset()).ok()?;
577+
let end = usize::try_from(sh.size())
525578
.ok()
526-
.and_then(|sh_size| start.checked_add(sh_size))?;
579+
.and_then(|sz| start.checked_add(sz))?;
527580

528581
elf.get(start..end)
529582
})
530583
}
584+
585+
/// Tries to extract section with name `name` from the ELF64 image `elf`, and returns it.
586+
pub(super) fn elf64_section<'a>(elf: &'a [u8], name: &str) -> Option<&'a [u8]> {
587+
elf_section_generic::<Elf64Format>(elf, name)
588+
}
531589
}

0 commit comments

Comments
 (0)