diff options
| author | John Hubbard <jhubbard@nvidia.com> | 2026-06-01 20:20:55 -0700 |
|---|---|---|
| committer | Alexandre Courbot <acourbot@nvidia.com> | 2026-06-02 22:33:15 +0900 |
| commit | bd581ff381443db71c86d5be56f3f70b0030058f (patch) | |
| tree | 8e93e1428c74d4ea0d5af85272f54e5de1592366 /drivers/gpu | |
| parent | a5bf742bc28a4e064059c8d137970e56669a21a0 (diff) | |
| download | lwn-bd581ff381443db71c86d5be56f3f70b0030058f.tar.gz lwn-bd581ff381443db71c86d5be56f3f70b0030058f.zip | |
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>
Diffstat (limited to 'drivers/gpu')
| -rw-r--r-- | drivers/gpu/nova-core/firmware.rs | 112 |
1 files changed, 85 insertions, 27 deletions
diff --git a/drivers/gpu/nova-core/firmware.rs b/drivers/gpu/nova-core/firmware.rs index 3aac073efee2..38088e950980 100644 --- a/drivers/gpu/nova-core/firmware.rs +++ b/drivers/gpu/nova-core/firmware.rs @@ -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 @@ mod elf { 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; - - // 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))?; - - elf.get(shdr_start..shdr_end) - .map(|slice| slice.chunks_exact(size_of::<Elf64SHdr>()))? - }; + 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>())?)?; + + 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))?; + + // 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) + } } |
