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