xref: /linux/drivers/gpu/nova-core/firmware/tlv.rs (revision 570f7e331f5febb30f1384817463c7e42b65ca7d)
1 // SPDX-License-Identifier: GPL-2.0
2 // SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3 
4 use kernel::{
5     device,
6     firmware,
7     prelude::*,
8     str::CString, //
9 };
10 
11 use crate::{
12     gpu,
13     num::*, //
14 };
15 
16 /// Requests the GPU firmware TLV `name` suitable for `chipset`.
17 pub(crate) fn request_tlv(
18     dev: &device::Device,
19     chipset: gpu::Chipset,
20     name: &str,
21 ) -> Result<firmware::Firmware> {
22     let chip_name = chipset.name();
23 
24     let filename = CString::try_from_fmt(fmt!("nvidia/{chip_name}/gsp/{name}.tlv"))?;
25 
26     dev_dbg!(dev, "loading firmware image {:?}\n", &filename);
27 
28     firmware::Firmware::request(&filename, dev)
29 }
30 
31 struct TlvBlock<'a> {
32     tag: [u8; 4],
33     value: &'a [u8],
34 }
35 
36 /// On-wire TLV block header: 4-byte ASCII tag + little-endian payload length (bytes, excluding
37 /// padding to a 4-byte boundary).
38 struct TlvBlockHeader {
39     tag: [u8; 4],
40     length: usize,
41 }
42 
43 impl TlvBlockHeader {
44     const SIZE: usize = size_of::<[u8; 4]>() + size_of::<u32>();
45 
46     /// Parses the first [`Self::SIZE`] bytes of `hdr` (caller may pass a longer slice).
47     fn parse(hdr: &[u8]) -> Option<Self> {
48         let hdr = hdr.get(..Self::SIZE)?;
49         let tag = <[u8; 4]>::try_from(hdr.get(..4)?).ok()?;
50         if !tag.is_ascii() {
51             return None;
52         }
53         let len_arr = <[u8; 4]>::try_from(hdr.get(4..Self::SIZE)?).ok()?;
54         let length = u32_as_usize(u32::from_le_bytes(len_arr));
55         Some(Self { tag, length })
56     }
57 }
58 
59 /// Iterator over the [`TlvBlock`]s of a [`Tlv`].
60 ///
61 /// # Invariants
62 ///
63 /// `pos` is a byte offset into `tlv.data` that always lies on a block boundary (in the sense
64 /// of the [`Tlv`] invariant): it is either the start of a well-formed block, or equal to
65 /// `tlv.data.len()` (end of iteration).
66 struct TlvIter<'tlv, 'a> {
67     tlv: &'tlv Tlv<'a>,
68     pos: usize,
69 }
70 
71 impl<'tlv, 'a> Iterator for TlvIter<'tlv, 'a> {
72     type Item = TlvBlock<'a>;
73 
74     /// Returns the block starting at `self.pos` and advances the cursor past it, or [`None`]
75     /// once the cursor reaches the end of the data or encounters an error.
76     ///
77     /// Note that errors cannot actually occur because the data is validated in the constructor.
78     fn next(&mut self) -> Option<Self::Item> {
79         if self.pos >= self.tlv.data.len() {
80             return None;
81         }
82 
83         let tail = self.tlv.data.get(self.pos..)?;
84 
85         let hdr = tail.get(..TlvBlockHeader::SIZE)?;
86         let header = TlvBlockHeader::parse(hdr)?;
87 
88         let stored_size = header.length.checked_next_multiple_of(4)?;
89         let advance = TlvBlockHeader::SIZE.checked_add(stored_size)?;
90         let payload_end = TlvBlockHeader::SIZE.checked_add(header.length)?;
91 
92         let value = tail
93             .get(..advance)?
94             .get(TlvBlockHeader::SIZE..payload_end)?;
95 
96         // INVARIANT: by the `Tlv` invariant the block at `self.pos` occupies exactly `advance`
97         // bytes, so `self.pos + advance` is the next block boundary (or `data.len()`).
98         self.pos = self.pos.checked_add(advance)?;
99 
100         Some(TlvBlock {
101             tag: header.tag,
102             value,
103         })
104     }
105 }
106 
107 /// The post-header part of a validated TLV (type, length, value) firmware image.
108 ///
109 /// TLV firmware images start with a 4-byte "NVFW" magic header, followed by a sequence of
110 /// blocks. Each block has a 4-byte type tag, a 4-byte length field, and a data payload
111 /// (value) whose stored size is the length rounded up to the nearest multiple of 4.
112 ///
113 /// [`Self::new`] checks the magic header and walks every block: tags must be ASCII,
114 /// lengths and padding must fit without overflow, and the byte stream after `NVFW` must
115 /// be exactly partitionable into blocks (no trailing partial header or slack). After
116 /// that, [`TlvIter`] only signals end-of-stream via [`None`], not parse failure.
117 ///
118 /// Although the spec forbids duplicate tags, neither the constructor nor the iterator
119 /// enforces this restriction.  Instead, duplicate tags are simply ignored.
120 ///
121 /// # Invariants
122 ///
123 /// `data` is a validated TLV payload (the bytes *after* the `NVFW` magic): it is the exact
124 /// concatenation of zero or more well-formed blocks, with no trailing partial header or slack.
125 /// Consequently, any offset `o` into `data` that is a block boundary and satisfies
126 /// `o < data.len()` is the start of a complete block whose header parses and whose stored
127 /// extent (`TlvBlockHeader::SIZE + header.length.next_multiple_of(4)` bytes) lies within
128 /// `data`. `data.len()` is itself a boundary.
129 pub(crate) struct Tlv<'a> {
130     data: &'a [u8],
131 }
132 
133 impl<'a> Tlv<'a> {
134     const MAGIC: &'static [u8; 4] = b"NVFW";
135 
136     /// Parses `data` as a TLV firmware image, returning [`EINVAL`] if the image is malformed.
137     pub(crate) fn new(data: &'a [u8]) -> Result<Self> {
138         // Verify that the magic bytes exist and are the correct value
139         let magic_len = Self::MAGIC.len();
140         if data
141             .get(..magic_len)
142             .is_none_or(|magic| magic != Self::MAGIC)
143         {
144             return Err(EINVAL);
145         }
146 
147         // The payload is the contiguous sequence of TLV blocks after the magic.
148         let payload = data.get(magic_len..).ok_or(EINVAL)?;
149 
150         // The spec says every TLV must have a VERS tag.
151         let mut has_vers = false;
152 
153         let mut rest = payload;
154         while !rest.is_empty() {
155             // Validate and extract the header (type, length).
156             let Some(header): Option<TlvBlockHeader> = rest
157                 .get(..TlvBlockHeader::SIZE)
158                 .and_then(TlvBlockHeader::parse)
159             else {
160                 return Err(EINVAL);
161             };
162 
163             has_vers |= header.tag == *b"VERS";
164 
165             // The `length` field of a TLV block contains the actual byte length of the
166             // value, but each TLV block is aligned to a 4-byte boundary.
167             let Some(stored_size) = header.length.checked_next_multiple_of(4) else {
168                 return Err(EINVAL);
169             };
170 
171             let length = TlvBlockHeader::SIZE
172                 .checked_add(stored_size)
173                 .ok_or(EINVAL)?;
174 
175             rest = rest.split_at_checked(length).ok_or(EINVAL)?.1;
176         }
177 
178         if !has_vers {
179             return Err(EINVAL);
180         }
181 
182         // INVARIANT: the loop above walked `payload` block-by-block. For each block, the
183         // header is parsed (`TlvBlockHeader::parse` rejects non-ASCII tags), and the
184         // stored extent (`SIZE + length.next_multiple_of(4)`) is computed without
185         // overflow and split off `rest` only when it fits. The loop ends only when `rest`
186         // is empty, so the byte stream is an exact concatenation of blocks with no
187         // trailing partial header or slack.
188         Ok(Self { data: payload })
189     }
190 
191     fn iter(&self) -> TlvIter<'_, 'a> {
192         // INVARIANT: 0 is a block boundary, either the start of the first block,
193         // or `data.len()` when `data` is empty.
194         TlvIter { tlv: self, pos: 0 }
195     }
196 
197     fn find(&self, tag: &[u8; 4]) -> Result<TlvBlock<'a>> {
198         self.iter().find(|b| b.tag == *tag).ok_or(EINVAL)
199     }
200 
201     /// Return a slice of bytes.
202     ///
203     /// Returns `EINVAL` if the value is empty.
204     pub(crate) fn get_bytes(&self, tag: &[u8; 4]) -> Result<&'a [u8]> {
205         let tlv = self.find(tag)?;
206 
207         // Treat empty value as an error, to avoid trying to parse nothing.
208         if tlv.value.is_empty() {
209             return Err(EINVAL); // TODO: Use ENODATA once available.
210         }
211 
212         Ok(tlv.value)
213     }
214 
215     /// Return a little-endian u32.
216     pub(crate) fn get_u32(&self, tag: &[u8; 4]) -> Result<u32> {
217         let tlv = self.find(tag)?;
218 
219         tlv.value
220             .try_into()
221             .ok()
222             .map(u32::from_le_bytes)
223             .ok_or(EINVAL)
224     }
225 
226     /// Return a string value.
227     pub(crate) fn get_string(&self, tag: &[u8; 4]) -> Result<&'a str> {
228         let tlv = self.find(tag)?;
229 
230         let bytes = tlv.value;
231 
232         // Strings can only contain printable ASCII characters.
233         if bytes.iter().any(|&b| !(32..127).contains(&b)) {
234             return Err(EINVAL);
235         }
236 
237         core::str::from_utf8(bytes).map_err(|_| EINVAL)
238     }
239 
240     /// Obtain the nth signature from a SIGN tag.  If `index` is None,
241     /// then return the last signature.
242     pub(crate) fn get_signature(&self, index: Option<usize>) -> Result<&'a [u8]> {
243         let num_sigs: usize = match self.get_u32(b"NSIG")? {
244             0 => return Err(EINVAL),
245             n => n.into_safe_cast(),
246         };
247 
248         let sig_bytes = self.get_bytes(b"SIGN")?;
249 
250         // Ensure that sig_bytes can be divided evenly into chunks.
251         if sig_bytes.len() % num_sigs != 0 {
252             return Err(EINVAL);
253         }
254 
255         // num_sigs cannot be 0, and sig_bytes cannot be empty, so this cannot panic.
256         let sig_size = sig_bytes.len() / num_sigs;
257 
258         let index = index.unwrap_or(num_sigs - 1);
259 
260         sig_bytes.chunks_exact(sig_size).nth(index).ok_or(EINVAL)
261     }
262 }
263