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