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