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