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