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