xref: /linux/drivers/gpu/nova-core/vbios.rs (revision 220994d61cebfc04f071d69049127657c7e8191b)
1 // SPDX-License-Identifier: GPL-2.0
2 
3 //! VBIOS extraction and parsing.
4 
5 use crate::driver::Bar0;
6 use crate::firmware::fwsec::Bcrt30Rsa3kSignature;
7 use crate::firmware::FalconUCodeDescV3;
8 use core::convert::TryFrom;
9 use kernel::device;
10 use kernel::error::Result;
11 use kernel::pci;
12 use kernel::prelude::*;
13 
14 /// The offset of the VBIOS ROM in the BAR0 space.
15 const ROM_OFFSET: usize = 0x300000;
16 /// The maximum length of the VBIOS ROM to scan into.
17 const BIOS_MAX_SCAN_LEN: usize = 0x100000;
18 /// The size to read ahead when parsing initial BIOS image headers.
19 const BIOS_READ_AHEAD_SIZE: usize = 1024;
20 /// The bit in the last image indicator byte for the PCI Data Structure that
21 /// indicates the last image. Bit 0-6 are reserved, bit 7 is last image bit.
22 const LAST_IMAGE_BIT_MASK: u8 = 0x80;
23 
24 // PMU lookup table entry types. Used to locate PMU table entries
25 // in the Fwsec image, corresponding to falcon ucodes.
26 #[expect(dead_code)]
27 const FALCON_UCODE_ENTRY_APPID_FIRMWARE_SEC_LIC: u8 = 0x05;
28 #[expect(dead_code)]
29 const FALCON_UCODE_ENTRY_APPID_FWSEC_DBG: u8 = 0x45;
30 const FALCON_UCODE_ENTRY_APPID_FWSEC_PROD: u8 = 0x85;
31 
32 /// Vbios Reader for constructing the VBIOS data.
33 struct VbiosIterator<'a> {
34     pdev: &'a pci::Device,
35     bar0: &'a Bar0,
36     /// VBIOS data vector: As BIOS images are scanned, they are added to this vector for reference
37     /// or copying into other data structures. It is the entire scanned contents of the VBIOS which
38     /// progressively extends. It is used so that we do not re-read any contents that are already
39     /// read as we use the cumulative length read so far, and re-read any gaps as we extend the
40     /// length.
41     data: KVec<u8>,
42     /// Current offset of the [`Iterator`].
43     current_offset: usize,
44     /// Indicate whether the last image has been found.
45     last_found: bool,
46 }
47 
48 impl<'a> VbiosIterator<'a> {
new(pdev: &'a pci::Device, bar0: &'a Bar0) -> Result<Self>49     fn new(pdev: &'a pci::Device, bar0: &'a Bar0) -> Result<Self> {
50         Ok(Self {
51             pdev,
52             bar0,
53             data: KVec::new(),
54             current_offset: 0,
55             last_found: false,
56         })
57     }
58 
59     /// Read bytes from the ROM at the current end of the data vector.
read_more(&mut self, len: usize) -> Result60     fn read_more(&mut self, len: usize) -> Result {
61         let current_len = self.data.len();
62         let start = ROM_OFFSET + current_len;
63 
64         // Ensure length is a multiple of 4 for 32-bit reads
65         if len % core::mem::size_of::<u32>() != 0 {
66             dev_err!(
67                 self.pdev.as_ref(),
68                 "VBIOS read length {} is not a multiple of 4\n",
69                 len
70             );
71             return Err(EINVAL);
72         }
73 
74         self.data.reserve(len, GFP_KERNEL)?;
75         // Read ROM data bytes and push directly to `data`.
76         for addr in (start..start + len).step_by(core::mem::size_of::<u32>()) {
77             // Read 32-bit word from the VBIOS ROM
78             let word = self.bar0.try_read32(addr)?;
79 
80             // Convert the `u32` to a 4 byte array and push each byte.
81             word.to_ne_bytes()
82                 .iter()
83                 .try_for_each(|&b| self.data.push(b, GFP_KERNEL))?;
84         }
85 
86         Ok(())
87     }
88 
89     /// Read bytes at a specific offset, filling any gap.
read_more_at_offset(&mut self, offset: usize, len: usize) -> Result90     fn read_more_at_offset(&mut self, offset: usize, len: usize) -> Result {
91         if offset > BIOS_MAX_SCAN_LEN {
92             dev_err!(self.pdev.as_ref(), "Error: exceeded BIOS scan limit.\n");
93             return Err(EINVAL);
94         }
95 
96         // If `offset` is beyond current data size, fill the gap first.
97         let current_len = self.data.len();
98         let gap_bytes = offset.saturating_sub(current_len);
99 
100         // Now read the requested bytes at the offset.
101         self.read_more(gap_bytes + len)
102     }
103 
104     /// Read a BIOS image at a specific offset and create a [`BiosImage`] from it.
105     ///
106     /// `self.data` is extended as needed and a new [`BiosImage`] is returned.
107     /// `context` is a string describing the operation for error reporting.
read_bios_image_at_offset( &mut self, offset: usize, len: usize, context: &str, ) -> Result<BiosImage>108     fn read_bios_image_at_offset(
109         &mut self,
110         offset: usize,
111         len: usize,
112         context: &str,
113     ) -> Result<BiosImage> {
114         let data_len = self.data.len();
115         if offset + len > data_len {
116             self.read_more_at_offset(offset, len).inspect_err(|e| {
117                 dev_err!(
118                     self.pdev.as_ref(),
119                     "Failed to read more at offset {:#x}: {:?}\n",
120                     offset,
121                     e
122                 )
123             })?;
124         }
125 
126         BiosImage::new(self.pdev, &self.data[offset..offset + len]).inspect_err(|err| {
127             dev_err!(
128                 self.pdev.as_ref(),
129                 "Failed to {} at offset {:#x}: {:?}\n",
130                 context,
131                 offset,
132                 err
133             )
134         })
135     }
136 }
137 
138 impl<'a> Iterator for VbiosIterator<'a> {
139     type Item = Result<BiosImage>;
140 
141     /// Iterate over all VBIOS images until the last image is detected or offset
142     /// exceeds scan limit.
next(&mut self) -> Option<Self::Item>143     fn next(&mut self) -> Option<Self::Item> {
144         if self.last_found {
145             return None;
146         }
147 
148         if self.current_offset > BIOS_MAX_SCAN_LEN {
149             dev_err!(
150                 self.pdev.as_ref(),
151                 "Error: exceeded BIOS scan limit, stopping scan\n"
152             );
153             return None;
154         }
155 
156         // Parse image headers first to get image size.
157         let image_size = match self.read_bios_image_at_offset(
158             self.current_offset,
159             BIOS_READ_AHEAD_SIZE,
160             "parse initial BIOS image headers",
161         ) {
162             Ok(image) => image.image_size_bytes(),
163             Err(e) => return Some(Err(e)),
164         };
165 
166         // Now create a new `BiosImage` with the full image data.
167         let full_image = match self.read_bios_image_at_offset(
168             self.current_offset,
169             image_size,
170             "parse full BIOS image",
171         ) {
172             Ok(image) => image,
173             Err(e) => return Some(Err(e)),
174         };
175 
176         self.last_found = full_image.is_last();
177 
178         // Advance to next image (aligned to 512 bytes).
179         self.current_offset += image_size;
180         // TODO[NUMM]: replace with `align_up` once it lands.
181         self.current_offset = self.current_offset.next_multiple_of(512);
182 
183         Some(Ok(full_image))
184     }
185 }
186 
187 pub(crate) struct Vbios {
188     fwsec_image: FwSecBiosImage,
189 }
190 
191 impl Vbios {
192     /// Probe for VBIOS extraction.
193     ///
194     /// Once the VBIOS object is built, `bar0` is not read for [`Vbios`] purposes anymore.
new(pdev: &pci::Device, bar0: &Bar0) -> Result<Vbios>195     pub(crate) fn new(pdev: &pci::Device, bar0: &Bar0) -> Result<Vbios> {
196         // Images to extract from iteration
197         let mut pci_at_image: Option<PciAtBiosImage> = None;
198         let mut first_fwsec_image: Option<FwSecBiosBuilder> = None;
199         let mut second_fwsec_image: Option<FwSecBiosBuilder> = None;
200 
201         // Parse all VBIOS images in the ROM
202         for image_result in VbiosIterator::new(pdev, bar0)? {
203             let full_image = image_result?;
204 
205             dev_dbg!(
206                 pdev.as_ref(),
207                 "Found BIOS image: size: {:#x}, type: {}, last: {}\n",
208                 full_image.image_size_bytes(),
209                 full_image.image_type_str(),
210                 full_image.is_last()
211             );
212 
213             // Get references to images we will need after the loop, in order to
214             // setup the falcon data offset.
215             match full_image {
216                 BiosImage::PciAt(image) => {
217                     pci_at_image = Some(image);
218                 }
219                 BiosImage::FwSec(image) => {
220                     if first_fwsec_image.is_none() {
221                         first_fwsec_image = Some(image);
222                     } else {
223                         second_fwsec_image = Some(image);
224                     }
225                 }
226                 // For now we don't need to handle these
227                 BiosImage::Efi(_image) => {}
228                 BiosImage::Nbsi(_image) => {}
229             }
230         }
231 
232         // Using all the images, setup the falcon data pointer in Fwsec.
233         if let (Some(mut second), Some(first), Some(pci_at)) =
234             (second_fwsec_image, first_fwsec_image, pci_at_image)
235         {
236             second
237                 .setup_falcon_data(pdev, &pci_at, &first)
238                 .inspect_err(|e| dev_err!(pdev.as_ref(), "Falcon data setup failed: {:?}\n", e))?;
239             Ok(Vbios {
240                 fwsec_image: second.build(pdev)?,
241             })
242         } else {
243             dev_err!(
244                 pdev.as_ref(),
245                 "Missing required images for falcon data setup, skipping\n"
246             );
247             Err(EINVAL)
248         }
249     }
250 
fwsec_image(&self) -> &FwSecBiosImage251     pub(crate) fn fwsec_image(&self) -> &FwSecBiosImage {
252         &self.fwsec_image
253     }
254 }
255 
256 /// PCI Data Structure as defined in PCI Firmware Specification
257 #[derive(Debug, Clone)]
258 #[repr(C)]
259 struct PcirStruct {
260     /// PCI Data Structure signature ("PCIR" or "NPDS")
261     signature: [u8; 4],
262     /// PCI Vendor ID (e.g., 0x10DE for NVIDIA)
263     vendor_id: u16,
264     /// PCI Device ID
265     device_id: u16,
266     /// Device List Pointer
267     device_list_ptr: u16,
268     /// PCI Data Structure Length
269     pci_data_struct_len: u16,
270     /// PCI Data Structure Revision
271     pci_data_struct_rev: u8,
272     /// Class code (3 bytes, 0x03 for display controller)
273     class_code: [u8; 3],
274     /// Size of this image in 512-byte blocks
275     image_len: u16,
276     /// Revision Level of the Vendor's ROM
277     vendor_rom_rev: u16,
278     /// ROM image type (0x00 = PC-AT compatible, 0x03 = EFI, 0x70 = NBSI)
279     code_type: u8,
280     /// Last image indicator (0x00 = Not last image, 0x80 = Last image)
281     last_image: u8,
282     /// Maximum Run-time Image Length (units of 512 bytes)
283     max_runtime_image_len: u16,
284 }
285 
286 impl PcirStruct {
new(pdev: &pci::Device, data: &[u8]) -> Result<Self>287     fn new(pdev: &pci::Device, data: &[u8]) -> Result<Self> {
288         if data.len() < core::mem::size_of::<PcirStruct>() {
289             dev_err!(pdev.as_ref(), "Not enough data for PcirStruct\n");
290             return Err(EINVAL);
291         }
292 
293         let mut signature = [0u8; 4];
294         signature.copy_from_slice(&data[0..4]);
295 
296         // Signature should be "PCIR" (0x52494350) or "NPDS" (0x5344504e).
297         if &signature != b"PCIR" && &signature != b"NPDS" {
298             dev_err!(
299                 pdev.as_ref(),
300                 "Invalid signature for PcirStruct: {:?}\n",
301                 signature
302             );
303             return Err(EINVAL);
304         }
305 
306         let mut class_code = [0u8; 3];
307         class_code.copy_from_slice(&data[13..16]);
308 
309         let image_len = u16::from_le_bytes([data[16], data[17]]);
310         if image_len == 0 {
311             dev_err!(pdev.as_ref(), "Invalid image length: 0\n");
312             return Err(EINVAL);
313         }
314 
315         Ok(PcirStruct {
316             signature,
317             vendor_id: u16::from_le_bytes([data[4], data[5]]),
318             device_id: u16::from_le_bytes([data[6], data[7]]),
319             device_list_ptr: u16::from_le_bytes([data[8], data[9]]),
320             pci_data_struct_len: u16::from_le_bytes([data[10], data[11]]),
321             pci_data_struct_rev: data[12],
322             class_code,
323             image_len,
324             vendor_rom_rev: u16::from_le_bytes([data[18], data[19]]),
325             code_type: data[20],
326             last_image: data[21],
327             max_runtime_image_len: u16::from_le_bytes([data[22], data[23]]),
328         })
329     }
330 
331     /// Check if this is the last image in the ROM.
is_last(&self) -> bool332     fn is_last(&self) -> bool {
333         self.last_image & LAST_IMAGE_BIT_MASK != 0
334     }
335 
336     /// Calculate image size in bytes from 512-byte blocks.
image_size_bytes(&self) -> usize337     fn image_size_bytes(&self) -> usize {
338         self.image_len as usize * 512
339     }
340 }
341 
342 /// BIOS Information Table (BIT) Header.
343 ///
344 /// This is the head of the BIT table, that is used to locate the Falcon data. The BIT table (with
345 /// its header) is in the [`PciAtBiosImage`] and the falcon data it is pointing to is in the
346 /// [`FwSecBiosImage`].
347 #[derive(Debug, Clone, Copy)]
348 #[expect(dead_code)]
349 struct BitHeader {
350     /// 0h: BIT Header Identifier (BMP=0x7FFF/BIT=0xB8FF)
351     id: u16,
352     /// 2h: BIT Header Signature ("BIT\0")
353     signature: [u8; 4],
354     /// 6h: Binary Coded Decimal Version, ex: 0x0100 is 1.00.
355     bcd_version: u16,
356     /// 8h: Size of BIT Header (in bytes)
357     header_size: u8,
358     /// 9h: Size of BIT Tokens (in bytes)
359     token_size: u8,
360     /// 10h: Number of token entries that follow
361     token_entries: u8,
362     /// 11h: BIT Header Checksum
363     checksum: u8,
364 }
365 
366 impl BitHeader {
new(data: &[u8]) -> Result<Self>367     fn new(data: &[u8]) -> Result<Self> {
368         if data.len() < 12 {
369             return Err(EINVAL);
370         }
371 
372         let mut signature = [0u8; 4];
373         signature.copy_from_slice(&data[2..6]);
374 
375         // Check header ID and signature
376         let id = u16::from_le_bytes([data[0], data[1]]);
377         if id != 0xB8FF || &signature != b"BIT\0" {
378             return Err(EINVAL);
379         }
380 
381         Ok(BitHeader {
382             id,
383             signature,
384             bcd_version: u16::from_le_bytes([data[6], data[7]]),
385             header_size: data[8],
386             token_size: data[9],
387             token_entries: data[10],
388             checksum: data[11],
389         })
390     }
391 }
392 
393 /// BIT Token Entry: Records in the BIT table followed by the BIT header.
394 #[derive(Debug, Clone, Copy)]
395 #[expect(dead_code)]
396 struct BitToken {
397     /// 00h: Token identifier
398     id: u8,
399     /// 01h: Version of the token data
400     data_version: u8,
401     /// 02h: Size of token data in bytes
402     data_size: u16,
403     /// 04h: Offset to the token data
404     data_offset: u16,
405 }
406 
407 // Define the token ID for the Falcon data
408 const BIT_TOKEN_ID_FALCON_DATA: u8 = 0x70;
409 
410 impl BitToken {
411     /// Find a BIT token entry by BIT ID in a PciAtBiosImage
from_id(image: &PciAtBiosImage, token_id: u8) -> Result<Self>412     fn from_id(image: &PciAtBiosImage, token_id: u8) -> Result<Self> {
413         let header = &image.bit_header;
414 
415         // Offset to the first token entry
416         let tokens_start = image.bit_offset + header.header_size as usize;
417 
418         for i in 0..header.token_entries as usize {
419             let entry_offset = tokens_start + (i * header.token_size as usize);
420 
421             // Make sure we don't go out of bounds
422             if entry_offset + header.token_size as usize > image.base.data.len() {
423                 return Err(EINVAL);
424             }
425 
426             // Check if this token has the requested ID
427             if image.base.data[entry_offset] == token_id {
428                 return Ok(BitToken {
429                     id: image.base.data[entry_offset],
430                     data_version: image.base.data[entry_offset + 1],
431                     data_size: u16::from_le_bytes([
432                         image.base.data[entry_offset + 2],
433                         image.base.data[entry_offset + 3],
434                     ]),
435                     data_offset: u16::from_le_bytes([
436                         image.base.data[entry_offset + 4],
437                         image.base.data[entry_offset + 5],
438                     ]),
439                 });
440             }
441         }
442 
443         // Token not found
444         Err(ENOENT)
445     }
446 }
447 
448 /// PCI ROM Expansion Header as defined in PCI Firmware Specification.
449 ///
450 /// This is header is at the beginning of every image in the set of images in the ROM. It contains
451 /// a pointer to the PCI Data Structure which describes the image. For "NBSI" images (NoteBook
452 /// System Information), the ROM header deviates from the standard and contains an offset to the
453 /// NBSI image however we do not yet parse that in this module and keep it for future reference.
454 #[derive(Debug, Clone, Copy)]
455 #[expect(dead_code)]
456 struct PciRomHeader {
457     /// 00h: Signature (0xAA55)
458     signature: u16,
459     /// 02h: Reserved bytes for processor architecture unique data (20 bytes)
460     reserved: [u8; 20],
461     /// 16h: NBSI Data Offset (NBSI-specific, offset from header to NBSI image)
462     nbsi_data_offset: Option<u16>,
463     /// 18h: Pointer to PCI Data Structure (offset from start of ROM image)
464     pci_data_struct_offset: u16,
465     /// 1Ah: Size of block (this is NBSI-specific)
466     size_of_block: Option<u32>,
467 }
468 
469 impl PciRomHeader {
new(pdev: &pci::Device, data: &[u8]) -> Result<Self>470     fn new(pdev: &pci::Device, data: &[u8]) -> Result<Self> {
471         if data.len() < 26 {
472             // Need at least 26 bytes to read pciDataStrucPtr and sizeOfBlock.
473             return Err(EINVAL);
474         }
475 
476         let signature = u16::from_le_bytes([data[0], data[1]]);
477 
478         // Check for valid ROM signatures.
479         match signature {
480             0xAA55 | 0xBB77 | 0x4E56 => {}
481             _ => {
482                 dev_err!(pdev.as_ref(), "ROM signature unknown {:#x}\n", signature);
483                 return Err(EINVAL);
484             }
485         }
486 
487         // Read the pointer to the PCI Data Structure at offset 0x18.
488         let pci_data_struct_ptr = u16::from_le_bytes([data[24], data[25]]);
489 
490         // Try to read optional fields if enough data.
491         let mut size_of_block = None;
492         let mut nbsi_data_offset = None;
493 
494         if data.len() >= 30 {
495             // Read size_of_block at offset 0x1A.
496             size_of_block = Some(
497                 u32::from(data[29]) << 24
498                     | u32::from(data[28]) << 16
499                     | u32::from(data[27]) << 8
500                     | u32::from(data[26]),
501             );
502         }
503 
504         // For NBSI images, try to read the nbsiDataOffset at offset 0x16.
505         if data.len() >= 24 {
506             nbsi_data_offset = Some(u16::from_le_bytes([data[22], data[23]]));
507         }
508 
509         Ok(PciRomHeader {
510             signature,
511             reserved: [0u8; 20],
512             pci_data_struct_offset: pci_data_struct_ptr,
513             size_of_block,
514             nbsi_data_offset,
515         })
516     }
517 }
518 
519 /// NVIDIA PCI Data Extension Structure.
520 ///
521 /// This is similar to the PCI Data Structure, but is Nvidia-specific and is placed right after the
522 /// PCI Data Structure. It contains some fields that are redundant with the PCI Data Structure, but
523 /// are needed for traversing the BIOS images. It is expected to be present in all BIOS images
524 /// except for NBSI images.
525 #[derive(Debug, Clone)]
526 #[repr(C)]
527 struct NpdeStruct {
528     /// 00h: Signature ("NPDE")
529     signature: [u8; 4],
530     /// 04h: NVIDIA PCI Data Extension Revision
531     npci_data_ext_rev: u16,
532     /// 06h: NVIDIA PCI Data Extension Length
533     npci_data_ext_len: u16,
534     /// 08h: Sub-image Length (in 512-byte units)
535     subimage_len: u16,
536     /// 0Ah: Last image indicator flag
537     last_image: u8,
538 }
539 
540 impl NpdeStruct {
new(pdev: &pci::Device, data: &[u8]) -> Option<Self>541     fn new(pdev: &pci::Device, data: &[u8]) -> Option<Self> {
542         if data.len() < core::mem::size_of::<Self>() {
543             dev_dbg!(pdev.as_ref(), "Not enough data for NpdeStruct\n");
544             return None;
545         }
546 
547         let mut signature = [0u8; 4];
548         signature.copy_from_slice(&data[0..4]);
549 
550         // Signature should be "NPDE" (0x4544504E).
551         if &signature != b"NPDE" {
552             dev_dbg!(
553                 pdev.as_ref(),
554                 "Invalid signature for NpdeStruct: {:?}\n",
555                 signature
556             );
557             return None;
558         }
559 
560         let subimage_len = u16::from_le_bytes([data[8], data[9]]);
561         if subimage_len == 0 {
562             dev_dbg!(pdev.as_ref(), "Invalid subimage length: 0\n");
563             return None;
564         }
565 
566         Some(NpdeStruct {
567             signature,
568             npci_data_ext_rev: u16::from_le_bytes([data[4], data[5]]),
569             npci_data_ext_len: u16::from_le_bytes([data[6], data[7]]),
570             subimage_len,
571             last_image: data[10],
572         })
573     }
574 
575     /// Check if this is the last image in the ROM.
is_last(&self) -> bool576     fn is_last(&self) -> bool {
577         self.last_image & LAST_IMAGE_BIT_MASK != 0
578     }
579 
580     /// Calculate image size in bytes from 512-byte blocks.
image_size_bytes(&self) -> usize581     fn image_size_bytes(&self) -> usize {
582         self.subimage_len as usize * 512
583     }
584 
585     /// Try to find NPDE in the data, the NPDE is right after the PCIR.
find_in_data( pdev: &pci::Device, data: &[u8], rom_header: &PciRomHeader, pcir: &PcirStruct, ) -> Option<Self>586     fn find_in_data(
587         pdev: &pci::Device,
588         data: &[u8],
589         rom_header: &PciRomHeader,
590         pcir: &PcirStruct,
591     ) -> Option<Self> {
592         // Calculate the offset where NPDE might be located
593         // NPDE should be right after the PCIR structure, aligned to 16 bytes
594         let pcir_offset = rom_header.pci_data_struct_offset as usize;
595         let npde_start = (pcir_offset + pcir.pci_data_struct_len as usize + 0x0F) & !0x0F;
596 
597         // Check if we have enough data
598         if npde_start + core::mem::size_of::<Self>() > data.len() {
599             dev_dbg!(pdev.as_ref(), "Not enough data for NPDE\n");
600             return None;
601         }
602 
603         // Try to create NPDE from the data
604         NpdeStruct::new(pdev, &data[npde_start..])
605     }
606 }
607 
608 // Use a macro to implement BiosImage enum and methods. This avoids having to
609 // repeat each enum type when implementing functions like base() in BiosImage.
610 macro_rules! bios_image {
611     (
612         $($variant:ident: $class:ident),* $(,)?
613     ) => {
614         // BiosImage enum with variants for each image type
615         enum BiosImage {
616             $($variant($class)),*
617         }
618 
619         impl BiosImage {
620             /// Get a reference to the common BIOS image data regardless of type
621             fn base(&self) -> &BiosImageBase {
622                 match self {
623                     $(Self::$variant(img) => &img.base),*
624                 }
625             }
626 
627             /// Returns a string representing the type of BIOS image
628             fn image_type_str(&self) -> &'static str {
629                 match self {
630                     $(Self::$variant(_) => stringify!($variant)),*
631                 }
632             }
633         }
634     }
635 }
636 
637 impl BiosImage {
638     /// Check if this is the last image.
is_last(&self) -> bool639     fn is_last(&self) -> bool {
640         let base = self.base();
641 
642         // For NBSI images (type == 0x70), return true as they're
643         // considered the last image
644         if matches!(self, Self::Nbsi(_)) {
645             return true;
646         }
647 
648         // For other image types, check the NPDE first if available
649         if let Some(ref npde) = base.npde {
650             return npde.is_last();
651         }
652 
653         // Otherwise, fall back to checking the PCIR last_image flag
654         base.pcir.is_last()
655     }
656 
657     /// Get the image size in bytes.
image_size_bytes(&self) -> usize658     fn image_size_bytes(&self) -> usize {
659         let base = self.base();
660 
661         // Prefer NPDE image size if available
662         if let Some(ref npde) = base.npde {
663             return npde.image_size_bytes();
664         }
665 
666         // Otherwise, fall back to the PCIR image size
667         base.pcir.image_size_bytes()
668     }
669 
670     /// Create a [`BiosImageBase`] from a byte slice and convert it to a [`BiosImage`] which
671     /// triggers the constructor of the specific BiosImage enum variant.
new(pdev: &pci::Device, data: &[u8]) -> Result<Self>672     fn new(pdev: &pci::Device, data: &[u8]) -> Result<Self> {
673         let base = BiosImageBase::new(pdev, data)?;
674         let image = base.into_image().inspect_err(|e| {
675             dev_err!(pdev.as_ref(), "Failed to create BiosImage: {:?}\n", e);
676         })?;
677 
678         Ok(image)
679     }
680 }
681 
682 bios_image! {
683     PciAt: PciAtBiosImage,   // PCI-AT compatible BIOS image
684     Efi: EfiBiosImage,       // EFI (Extensible Firmware Interface)
685     Nbsi: NbsiBiosImage,     // NBSI (Nvidia Bios System Interface)
686     FwSec: FwSecBiosBuilder, // FWSEC (Firmware Security)
687 }
688 
689 /// The PciAt BIOS image is typically the first BIOS image type found in the BIOS image chain.
690 ///
691 /// It contains the BIT header and the BIT tokens.
692 struct PciAtBiosImage {
693     base: BiosImageBase,
694     bit_header: BitHeader,
695     bit_offset: usize,
696 }
697 
698 struct EfiBiosImage {
699     base: BiosImageBase,
700     // EFI-specific fields can be added here in the future.
701 }
702 
703 struct NbsiBiosImage {
704     base: BiosImageBase,
705     // NBSI-specific fields can be added here in the future.
706 }
707 
708 struct FwSecBiosBuilder {
709     base: BiosImageBase,
710     /// These are temporary fields that are used during the construction of the
711     /// [`FwSecBiosBuilder`].
712     ///
713     /// Once FwSecBiosBuilder is constructed, the `falcon_ucode_offset` will be copied into a new
714     /// [`FwSecBiosImage`].
715     ///
716     /// The offset of the Falcon data from the start of Fwsec image.
717     falcon_data_offset: Option<usize>,
718     /// The [`PmuLookupTable`] starts at the offset of the falcon data pointer.
719     pmu_lookup_table: Option<PmuLookupTable>,
720     /// The offset of the Falcon ucode.
721     falcon_ucode_offset: Option<usize>,
722 }
723 
724 /// The [`FwSecBiosImage`] structure contains the PMU table and the Falcon Ucode.
725 ///
726 /// The PMU table contains voltage/frequency tables as well as a pointer to the Falcon Ucode.
727 pub(crate) struct FwSecBiosImage {
728     base: BiosImageBase,
729     /// The offset of the Falcon ucode.
730     falcon_ucode_offset: usize,
731 }
732 
733 // Convert from BiosImageBase to BiosImage
734 impl TryFrom<BiosImageBase> for BiosImage {
735     type Error = Error;
736 
try_from(base: BiosImageBase) -> Result<Self>737     fn try_from(base: BiosImageBase) -> Result<Self> {
738         match base.pcir.code_type {
739             0x00 => Ok(BiosImage::PciAt(base.try_into()?)),
740             0x03 => Ok(BiosImage::Efi(EfiBiosImage { base })),
741             0x70 => Ok(BiosImage::Nbsi(NbsiBiosImage { base })),
742             0xE0 => Ok(BiosImage::FwSec(FwSecBiosBuilder {
743                 base,
744                 falcon_data_offset: None,
745                 pmu_lookup_table: None,
746                 falcon_ucode_offset: None,
747             })),
748             _ => Err(EINVAL),
749         }
750     }
751 }
752 
753 /// BIOS Image structure containing various headers and reference fields to all BIOS images.
754 ///
755 /// Each BiosImage type has a BiosImageBase type along with other image-specific fields. Note that
756 /// Rust favors composition of types over inheritance.
757 #[derive(Debug)]
758 #[expect(dead_code)]
759 struct BiosImageBase {
760     /// PCI ROM Expansion Header
761     rom_header: PciRomHeader,
762     /// PCI Data Structure
763     pcir: PcirStruct,
764     /// NVIDIA PCI Data Extension (optional)
765     npde: Option<NpdeStruct>,
766     /// Image data (includes ROM header and PCIR)
767     data: KVec<u8>,
768 }
769 
770 impl BiosImageBase {
into_image(self) -> Result<BiosImage>771     fn into_image(self) -> Result<BiosImage> {
772         BiosImage::try_from(self)
773     }
774 
775     /// Creates a new BiosImageBase from raw byte data.
new(pdev: &pci::Device, data: &[u8]) -> Result<Self>776     fn new(pdev: &pci::Device, data: &[u8]) -> Result<Self> {
777         // Ensure we have enough data for the ROM header.
778         if data.len() < 26 {
779             dev_err!(pdev.as_ref(), "Not enough data for ROM header\n");
780             return Err(EINVAL);
781         }
782 
783         // Parse the ROM header.
784         let rom_header = PciRomHeader::new(pdev, &data[0..26])
785             .inspect_err(|e| dev_err!(pdev.as_ref(), "Failed to create PciRomHeader: {:?}\n", e))?;
786 
787         // Get the PCI Data Structure using the pointer from the ROM header.
788         let pcir_offset = rom_header.pci_data_struct_offset as usize;
789         let pcir_data = data
790             .get(pcir_offset..pcir_offset + core::mem::size_of::<PcirStruct>())
791             .ok_or(EINVAL)
792             .inspect_err(|_| {
793                 dev_err!(
794                     pdev.as_ref(),
795                     "PCIR offset {:#x} out of bounds (data length: {})\n",
796                     pcir_offset,
797                     data.len()
798                 );
799                 dev_err!(
800                     pdev.as_ref(),
801                     "Consider reading more data for construction of BiosImage\n"
802                 );
803             })?;
804 
805         let pcir = PcirStruct::new(pdev, pcir_data)
806             .inspect_err(|e| dev_err!(pdev.as_ref(), "Failed to create PcirStruct: {:?}\n", e))?;
807 
808         // Look for NPDE structure if this is not an NBSI image (type != 0x70).
809         let npde = NpdeStruct::find_in_data(pdev, data, &rom_header, &pcir);
810 
811         // Create a copy of the data.
812         let mut data_copy = KVec::new();
813         data_copy.extend_from_slice(data, GFP_KERNEL)?;
814 
815         Ok(BiosImageBase {
816             rom_header,
817             pcir,
818             npde,
819             data: data_copy,
820         })
821     }
822 }
823 
824 impl PciAtBiosImage {
825     /// Find a byte pattern in a slice.
find_byte_pattern(haystack: &[u8], needle: &[u8]) -> Result<usize>826     fn find_byte_pattern(haystack: &[u8], needle: &[u8]) -> Result<usize> {
827         haystack
828             .windows(needle.len())
829             .position(|window| window == needle)
830             .ok_or(EINVAL)
831     }
832 
833     /// Find the BIT header in the [`PciAtBiosImage`].
find_bit_header(data: &[u8]) -> Result<(BitHeader, usize)>834     fn find_bit_header(data: &[u8]) -> Result<(BitHeader, usize)> {
835         let bit_pattern = [0xff, 0xb8, b'B', b'I', b'T', 0x00];
836         let bit_offset = Self::find_byte_pattern(data, &bit_pattern)?;
837         let bit_header = BitHeader::new(&data[bit_offset..])?;
838 
839         Ok((bit_header, bit_offset))
840     }
841 
842     /// Get a BIT token entry from the BIT table in the [`PciAtBiosImage`]
get_bit_token(&self, token_id: u8) -> Result<BitToken>843     fn get_bit_token(&self, token_id: u8) -> Result<BitToken> {
844         BitToken::from_id(self, token_id)
845     }
846 
847     /// Find the Falcon data pointer structure in the [`PciAtBiosImage`].
848     ///
849     /// This is just a 4 byte structure that contains a pointer to the Falcon data in the FWSEC
850     /// image.
falcon_data_ptr(&self, pdev: &pci::Device) -> Result<u32>851     fn falcon_data_ptr(&self, pdev: &pci::Device) -> Result<u32> {
852         let token = self.get_bit_token(BIT_TOKEN_ID_FALCON_DATA)?;
853 
854         // Make sure we don't go out of bounds
855         if token.data_offset as usize + 4 > self.base.data.len() {
856             return Err(EINVAL);
857         }
858 
859         // read the 4 bytes at the offset specified in the token
860         let offset = token.data_offset as usize;
861         let bytes: [u8; 4] = self.base.data[offset..offset + 4].try_into().map_err(|_| {
862             dev_err!(pdev.as_ref(), "Failed to convert data slice to array");
863             EINVAL
864         })?;
865 
866         let data_ptr = u32::from_le_bytes(bytes);
867 
868         if (data_ptr as usize) < self.base.data.len() {
869             dev_err!(pdev.as_ref(), "Falcon data pointer out of bounds\n");
870             return Err(EINVAL);
871         }
872 
873         Ok(data_ptr)
874     }
875 }
876 
877 impl TryFrom<BiosImageBase> for PciAtBiosImage {
878     type Error = Error;
879 
try_from(base: BiosImageBase) -> Result<Self>880     fn try_from(base: BiosImageBase) -> Result<Self> {
881         let data_slice = &base.data;
882         let (bit_header, bit_offset) = PciAtBiosImage::find_bit_header(data_slice)?;
883 
884         Ok(PciAtBiosImage {
885             base,
886             bit_header,
887             bit_offset,
888         })
889     }
890 }
891 
892 /// The [`PmuLookupTableEntry`] structure is a single entry in the [`PmuLookupTable`].
893 ///
894 /// See the [`PmuLookupTable`] description for more information.
895 #[expect(dead_code)]
896 struct PmuLookupTableEntry {
897     application_id: u8,
898     target_id: u8,
899     data: u32,
900 }
901 
902 impl PmuLookupTableEntry {
new(data: &[u8]) -> Result<Self>903     fn new(data: &[u8]) -> Result<Self> {
904         if data.len() < 6 {
905             return Err(EINVAL);
906         }
907 
908         Ok(PmuLookupTableEntry {
909             application_id: data[0],
910             target_id: data[1],
911             data: u32::from_le_bytes(data[2..6].try_into().map_err(|_| EINVAL)?),
912         })
913     }
914 }
915 
916 /// The [`PmuLookupTableEntry`] structure is used to find the [`PmuLookupTableEntry`] for a given
917 /// application ID.
918 ///
919 /// The table of entries is pointed to by the falcon data pointer in the BIT table, and is used to
920 /// locate the Falcon Ucode.
921 #[expect(dead_code)]
922 struct PmuLookupTable {
923     version: u8,
924     header_len: u8,
925     entry_len: u8,
926     entry_count: u8,
927     table_data: KVec<u8>,
928 }
929 
930 impl PmuLookupTable {
new(pdev: &pci::Device, data: &[u8]) -> Result<Self>931     fn new(pdev: &pci::Device, data: &[u8]) -> Result<Self> {
932         if data.len() < 4 {
933             return Err(EINVAL);
934         }
935 
936         let header_len = data[1] as usize;
937         let entry_len = data[2] as usize;
938         let entry_count = data[3] as usize;
939 
940         let required_bytes = header_len + (entry_count * entry_len);
941 
942         if data.len() < required_bytes {
943             dev_err!(
944                 pdev.as_ref(),
945                 "PmuLookupTable data length less than required\n"
946             );
947             return Err(EINVAL);
948         }
949 
950         // Create a copy of only the table data
951         let table_data = {
952             let mut ret = KVec::new();
953             ret.extend_from_slice(&data[header_len..required_bytes], GFP_KERNEL)?;
954             ret
955         };
956 
957         // Debug logging of entries (dumps the table data to dmesg)
958         for i in (header_len..required_bytes).step_by(entry_len) {
959             dev_dbg!(
960                 pdev.as_ref(),
961                 "PMU entry: {:02x?}\n",
962                 &data[i..][..entry_len]
963             );
964         }
965 
966         Ok(PmuLookupTable {
967             version: data[0],
968             header_len: header_len as u8,
969             entry_len: entry_len as u8,
970             entry_count: entry_count as u8,
971             table_data,
972         })
973     }
974 
lookup_index(&self, idx: u8) -> Result<PmuLookupTableEntry>975     fn lookup_index(&self, idx: u8) -> Result<PmuLookupTableEntry> {
976         if idx >= self.entry_count {
977             return Err(EINVAL);
978         }
979 
980         let index = (idx as usize) * self.entry_len as usize;
981         PmuLookupTableEntry::new(&self.table_data[index..])
982     }
983 
984     // find entry by type value
find_entry_by_type(&self, entry_type: u8) -> Result<PmuLookupTableEntry>985     fn find_entry_by_type(&self, entry_type: u8) -> Result<PmuLookupTableEntry> {
986         for i in 0..self.entry_count {
987             let entry = self.lookup_index(i)?;
988             if entry.application_id == entry_type {
989                 return Ok(entry);
990             }
991         }
992 
993         Err(EINVAL)
994     }
995 }
996 
997 impl FwSecBiosBuilder {
setup_falcon_data( &mut self, pdev: &pci::Device, pci_at_image: &PciAtBiosImage, first_fwsec: &FwSecBiosBuilder, ) -> Result998     fn setup_falcon_data(
999         &mut self,
1000         pdev: &pci::Device,
1001         pci_at_image: &PciAtBiosImage,
1002         first_fwsec: &FwSecBiosBuilder,
1003     ) -> Result {
1004         let mut offset = pci_at_image.falcon_data_ptr(pdev)? as usize;
1005         let mut pmu_in_first_fwsec = false;
1006 
1007         // The falcon data pointer assumes that the PciAt and FWSEC images
1008         // are contiguous in memory. However, testing shows the EFI image sits in
1009         // between them. So calculate the offset from the end of the PciAt image
1010         // rather than the start of it. Compensate.
1011         offset -= pci_at_image.base.data.len();
1012 
1013         // The offset is now from the start of the first Fwsec image, however
1014         // the offset points to a location in the second Fwsec image. Since
1015         // the fwsec images are contiguous, subtract the length of the first Fwsec
1016         // image from the offset to get the offset to the start of the second
1017         // Fwsec image.
1018         if offset < first_fwsec.base.data.len() {
1019             pmu_in_first_fwsec = true;
1020         } else {
1021             offset -= first_fwsec.base.data.len();
1022         }
1023 
1024         self.falcon_data_offset = Some(offset);
1025 
1026         if pmu_in_first_fwsec {
1027             self.pmu_lookup_table =
1028                 Some(PmuLookupTable::new(pdev, &first_fwsec.base.data[offset..])?);
1029         } else {
1030             self.pmu_lookup_table = Some(PmuLookupTable::new(pdev, &self.base.data[offset..])?);
1031         }
1032 
1033         match self
1034             .pmu_lookup_table
1035             .as_ref()
1036             .ok_or(EINVAL)?
1037             .find_entry_by_type(FALCON_UCODE_ENTRY_APPID_FWSEC_PROD)
1038         {
1039             Ok(entry) => {
1040                 let mut ucode_offset = entry.data as usize;
1041                 ucode_offset -= pci_at_image.base.data.len();
1042                 if ucode_offset < first_fwsec.base.data.len() {
1043                     dev_err!(pdev.as_ref(), "Falcon Ucode offset not in second Fwsec.\n");
1044                     return Err(EINVAL);
1045                 }
1046                 ucode_offset -= first_fwsec.base.data.len();
1047                 self.falcon_ucode_offset = Some(ucode_offset);
1048             }
1049             Err(e) => {
1050                 dev_err!(
1051                     pdev.as_ref(),
1052                     "PmuLookupTableEntry not found, error: {:?}\n",
1053                     e
1054                 );
1055                 return Err(EINVAL);
1056             }
1057         }
1058         Ok(())
1059     }
1060 
1061     /// Build the final FwSecBiosImage from this builder
build(self, pdev: &pci::Device) -> Result<FwSecBiosImage>1062     fn build(self, pdev: &pci::Device) -> Result<FwSecBiosImage> {
1063         let ret = FwSecBiosImage {
1064             base: self.base,
1065             falcon_ucode_offset: self.falcon_ucode_offset.ok_or(EINVAL)?,
1066         };
1067 
1068         if cfg!(debug_assertions) {
1069             // Print the desc header for debugging
1070             let desc = ret.header(pdev.as_ref())?;
1071             dev_dbg!(pdev.as_ref(), "PmuLookupTableEntry desc: {:#?}\n", desc);
1072         }
1073 
1074         Ok(ret)
1075     }
1076 }
1077 
1078 impl FwSecBiosImage {
1079     /// Get the FwSec header ([`FalconUCodeDescV3`]).
header(&self, dev: &device::Device) -> Result<&FalconUCodeDescV3>1080     pub(crate) fn header(&self, dev: &device::Device) -> Result<&FalconUCodeDescV3> {
1081         // Get the falcon ucode offset that was found in setup_falcon_data.
1082         let falcon_ucode_offset = self.falcon_ucode_offset;
1083 
1084         // Make sure the offset is within the data bounds.
1085         if falcon_ucode_offset + core::mem::size_of::<FalconUCodeDescV3>() > self.base.data.len() {
1086             dev_err!(dev, "fwsec-frts header not contained within BIOS bounds\n");
1087             return Err(ERANGE);
1088         }
1089 
1090         // Read the first 4 bytes to get the version.
1091         let hdr_bytes: [u8; 4] = self.base.data[falcon_ucode_offset..falcon_ucode_offset + 4]
1092             .try_into()
1093             .map_err(|_| EINVAL)?;
1094         let hdr = u32::from_le_bytes(hdr_bytes);
1095         let ver = (hdr & 0xff00) >> 8;
1096 
1097         if ver != 3 {
1098             dev_err!(dev, "invalid fwsec firmware version: {:?}\n", ver);
1099             return Err(EINVAL);
1100         }
1101 
1102         // Return a reference to the FalconUCodeDescV3 structure.
1103         //
1104         // SAFETY: We have checked that `falcon_ucode_offset + size_of::<FalconUCodeDescV3>` is
1105         // within the bounds of `data`. Also, this data vector is from ROM, and the `data` field
1106         // in `BiosImageBase` is immutable after construction.
1107         Ok(unsafe {
1108             &*(self
1109                 .base
1110                 .data
1111                 .as_ptr()
1112                 .add(falcon_ucode_offset)
1113                 .cast::<FalconUCodeDescV3>())
1114         })
1115     }
1116 
1117     /// Get the ucode data as a byte slice
ucode(&self, dev: &device::Device, desc: &FalconUCodeDescV3) -> Result<&[u8]>1118     pub(crate) fn ucode(&self, dev: &device::Device, desc: &FalconUCodeDescV3) -> Result<&[u8]> {
1119         let falcon_ucode_offset = self.falcon_ucode_offset;
1120 
1121         // The ucode data follows the descriptor.
1122         let ucode_data_offset = falcon_ucode_offset + desc.size();
1123         let size = (desc.imem_load_size + desc.dmem_load_size) as usize;
1124 
1125         // Get the data slice, checking bounds in a single operation.
1126         self.base
1127             .data
1128             .get(ucode_data_offset..ucode_data_offset + size)
1129             .ok_or(ERANGE)
1130             .inspect_err(|_| dev_err!(dev, "fwsec ucode data not contained within BIOS bounds\n"))
1131     }
1132 
1133     /// Get the signatures as a byte slice
sigs( &self, dev: &device::Device, desc: &FalconUCodeDescV3, ) -> Result<&[Bcrt30Rsa3kSignature]>1134     pub(crate) fn sigs(
1135         &self,
1136         dev: &device::Device,
1137         desc: &FalconUCodeDescV3,
1138     ) -> Result<&[Bcrt30Rsa3kSignature]> {
1139         // The signatures data follows the descriptor.
1140         let sigs_data_offset = self.falcon_ucode_offset + core::mem::size_of::<FalconUCodeDescV3>();
1141         let sigs_size =
1142             desc.signature_count as usize * core::mem::size_of::<Bcrt30Rsa3kSignature>();
1143 
1144         // Make sure the data is within bounds.
1145         if sigs_data_offset + sigs_size > self.base.data.len() {
1146             dev_err!(
1147                 dev,
1148                 "fwsec signatures data not contained within BIOS bounds\n"
1149             );
1150             return Err(ERANGE);
1151         }
1152 
1153         // SAFETY: we checked that `data + sigs_data_offset + (signature_count *
1154         // sizeof::<Bcrt30Rsa3kSignature>()` is within the bounds of `data`.
1155         Ok(unsafe {
1156             core::slice::from_raw_parts(
1157                 self.base
1158                     .data
1159                     .as_ptr()
1160                     .add(sigs_data_offset)
1161                     .cast::<Bcrt30Rsa3kSignature>(),
1162                 desc.signature_count as usize,
1163             )
1164         })
1165     }
1166 }
1167