blob: 7b879f13a61e10465c6aa1defac5595fe26b69a9 [file] [edit]
// SPDX-License-Identifier: GPL-2.0
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
use kernel::{
device,
firmware,
prelude::*,
str::CString, //
};
use crate::{
gpu,
num::*, //
};
/// Requests the GPU firmware TLV `name` suitable for `chipset`.
pub(crate) fn request_tlv(
dev: &device::Device,
chipset: gpu::Chipset,
name: &str,
) -> Result<firmware::Firmware> {
let chip_name = chipset.name();
let filename = CString::try_from_fmt(fmt!("nvidia/{chip_name}/gsp/{name}.tlv"))?;
dev_dbg!(dev, "loading firmware image {:?}\n", &filename);
firmware::Firmware::request(&filename, dev)
}
struct TlvBlock<'a> {
tag: [u8; 4],
value: &'a [u8],
}
/// On-wire TLV block header: 4-byte ASCII tag + little-endian payload length (bytes, excluding
/// padding to a 4-byte boundary).
struct TlvBlockHeader {
tag: [u8; 4],
length: usize,
}
impl TlvBlockHeader {
const SIZE: usize = size_of::<[u8; 4]>() + size_of::<u32>();
/// Parses the first [`Self::SIZE`] bytes of `hdr` (caller may pass a longer slice).
fn parse(hdr: &[u8]) -> Option<Self> {
let hdr = hdr.get(..Self::SIZE)?;
let tag = <[u8; 4]>::try_from(hdr.get(..4)?).ok()?;
if !tag.is_ascii() {
return None;
}
let len_arr = <[u8; 4]>::try_from(hdr.get(4..Self::SIZE)?).ok()?;
let length = u32_as_usize(u32::from_le_bytes(len_arr));
Some(Self { tag, length })
}
}
/// Iterator over the [`TlvBlock`]s of a [`Tlv`].
///
/// # Invariants
///
/// `pos` is a byte offset into `tlv.data` that always lies on a block boundary (in the sense
/// of the [`Tlv`] invariant): it is either the start of a well-formed block, or equal to
/// `tlv.data.len()` (end of iteration).
struct TlvIter<'tlv, 'a> {
tlv: &'tlv Tlv<'a>,
pos: usize,
}
impl<'tlv, 'a> Iterator for TlvIter<'tlv, 'a> {
type Item = TlvBlock<'a>;
/// Returns the block starting at `self.pos` and advances the cursor past it, or [`None`]
/// once the cursor reaches the end of the data or encounters an error.
///
/// Note that errors cannot actually occur because the data is validated in the constructor.
fn next(&mut self) -> Option<Self::Item> {
if self.pos >= self.tlv.data.len() {
return None;
}
let tail = self.tlv.data.get(self.pos..)?;
let hdr = tail.get(..TlvBlockHeader::SIZE)?;
let header = TlvBlockHeader::parse(hdr)?;
let stored_size = header.length.checked_next_multiple_of(4)?;
let advance = TlvBlockHeader::SIZE.checked_add(stored_size)?;
let payload_end = TlvBlockHeader::SIZE.checked_add(header.length)?;
let value = tail
.get(..advance)?
.get(TlvBlockHeader::SIZE..payload_end)?;
// INVARIANT: by the `Tlv` invariant the block at `self.pos` occupies exactly `advance`
// bytes, so `self.pos + advance` is the next block boundary (or `data.len()`).
self.pos = self.pos.checked_add(advance)?;
Some(TlvBlock {
tag: header.tag,
value,
})
}
}
/// The post-header part of a validated TLV (type, length, value) firmware image.
///
/// TLV firmware images start with a 4-byte "NVFW" magic header, followed by a sequence of
/// blocks. Each block has a 4-byte type tag, a 4-byte length field, and a data payload
/// (value) whose stored size is the length rounded up to the nearest multiple of 4.
///
/// [`Self::new`] checks the magic header and walks every block: tags must be ASCII,
/// lengths and padding must fit without overflow, and the byte stream after `NVFW` must
/// be exactly partitionable into blocks (no trailing partial header or slack). After
/// that, [`TlvIter`] only signals end-of-stream via [`None`], not parse failure.
///
/// Although the spec forbids duplicate tags, neither the constructor nor the iterator
/// enforces this restriction. Instead, duplicate tags are simply ignored.
///
/// # Invariants
///
/// `data` is a validated TLV payload (the bytes *after* the `NVFW` magic): it is the exact
/// concatenation of zero or more well-formed blocks, with no trailing partial header or slack.
/// Consequently, any offset `o` into `data` that is a block boundary and satisfies
/// `o < data.len()` is the start of a complete block whose header parses and whose stored
/// extent (`TlvBlockHeader::SIZE + header.length.next_multiple_of(4)` bytes) lies within
/// `data`. `data.len()` is itself a boundary.
pub(crate) struct Tlv<'a> {
data: &'a [u8],
}
impl<'a> Tlv<'a> {
const MAGIC: &'static [u8; 4] = b"NVFW";
/// Parses `data` as a TLV firmware image, returning [`EINVAL`] if the image is malformed.
pub(crate) fn new(data: &'a [u8]) -> Result<Self> {
// Verify that the magic bytes exist and are the correct value
let magic_len = Self::MAGIC.len();
if data
.get(..magic_len)
.is_none_or(|magic| magic != Self::MAGIC)
{
return Err(EINVAL);
}
// The payload is the contiguous sequence of TLV blocks after the magic.
let payload = data.get(magic_len..).ok_or(EINVAL)?;
// The spec says every TLV must have a VERS tag.
let mut has_vers = false;
let mut rest = payload;
while !rest.is_empty() {
// Validate and extract the header (type, length).
let Some(header): Option<TlvBlockHeader> = rest
.get(..TlvBlockHeader::SIZE)
.and_then(TlvBlockHeader::parse)
else {
return Err(EINVAL);
};
has_vers |= header.tag == *b"VERS";
// The `length` field of a TLV block contains the actual byte length of the
// value, but each TLV block is aligned to a 4-byte boundary.
let Some(stored_size) = header.length.checked_next_multiple_of(4) else {
return Err(EINVAL);
};
let length = TlvBlockHeader::SIZE
.checked_add(stored_size)
.ok_or(EINVAL)?;
rest = rest.split_at_checked(length).ok_or(EINVAL)?.1;
}
if !has_vers {
return Err(EINVAL);
}
// INVARIANT: the loop above walked `payload` block-by-block. For each block, the
// header is parsed (`TlvBlockHeader::parse` rejects non-ASCII tags), and the
// stored extent (`SIZE + length.next_multiple_of(4)`) is computed without
// overflow and split off `rest` only when it fits. The loop ends only when `rest`
// is empty, so the byte stream is an exact concatenation of blocks with no
// trailing partial header or slack.
Ok(Self { data: payload })
}
fn iter(&self) -> TlvIter<'_, 'a> {
// INVARIANT: 0 is a block boundary, either the start of the first block,
// or `data.len()` when `data` is empty.
TlvIter { tlv: self, pos: 0 }
}
fn find(&self, tag: &[u8; 4]) -> Result<TlvBlock<'a>> {
self.iter().find(|b| b.tag == *tag).ok_or(EINVAL)
}
/// Return a slice of bytes.
///
/// Returns `EINVAL` if the value is empty.
pub(crate) fn get_bytes(&self, tag: &[u8; 4]) -> Result<&'a [u8]> {
let tlv = self.find(tag)?;
// Treat empty value as an error, to avoid trying to parse nothing.
if tlv.value.is_empty() {
return Err(EINVAL); // TODO: Use ENODATA once available.
}
Ok(tlv.value)
}
/// Return a little-endian u32.
pub(crate) fn get_u32(&self, tag: &[u8; 4]) -> Result<u32> {
let tlv = self.find(tag)?;
tlv.value
.try_into()
.ok()
.map(u32::from_le_bytes)
.ok_or(EINVAL)
}
/// Return a string value.
pub(crate) fn get_string(&self, tag: &[u8; 4]) -> Result<&'a str> {
let tlv = self.find(tag)?;
let bytes = tlv.value;
// Strings can only contain printable ASCII characters.
if bytes.iter().any(|&b| !(32..127).contains(&b)) {
return Err(EINVAL);
}
core::str::from_utf8(bytes).map_err(|_| EINVAL)
}
/// Obtain the nth signature from a SIGN tag. If `index` is None,
/// then return the last signature.
pub(crate) fn get_signature(&self, index: Option<usize>) -> Result<&'a [u8]> {
let num_sigs: usize = match self.get_u32(b"NSIG")? {
0 => return Err(EINVAL),
n => n.into_safe_cast(),
};
let sig_bytes = self.get_bytes(b"SIGN")?;
// Ensure that sig_bytes can be divided evenly into chunks.
if sig_bytes.len() % num_sigs != 0 {
return Err(EINVAL);
}
// num_sigs cannot be 0, and sig_bytes cannot be empty, so this cannot panic.
let sig_size = sig_bytes.len() / num_sigs;
let index = index.unwrap_or(num_sigs - 1);
sig_bytes.chunks_exact(sig_size).nth(index).ok_or(EINVAL)
}
}