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