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