xref: /linux/drivers/gpu/nova-core/vbios.rs (revision e54ad0cd3673c93cdafda58505eaa81610fe3aef)
1 // SPDX-License-Identifier: GPL-2.0
2 
3 //! VBIOS extraction and parsing.
4 
5 use core::convert::TryFrom;
6 
7 use kernel::{
8     device,
9     prelude::*,
10     ptr::{
11         Alignable,
12         Alignment, //
13     },
14     transmute::FromBytes,
15     types::ARef,
16 };
17 
18 use crate::{
19     driver::Bar0,
20     firmware::{
21         fwsec::Bcrt30Rsa3kSignature,
22         FalconUCodeDescV3, //
23     },
24     num::FromSafeCast,
25 };
26 
27 /// The offset of the VBIOS ROM in the BAR0 space.
28 const ROM_OFFSET: usize = 0x300000;
29 /// The maximum length of the VBIOS ROM to scan into.
30 const BIOS_MAX_SCAN_LEN: usize = 0x100000;
31 /// The size to read ahead when parsing initial BIOS image headers.
32 const BIOS_READ_AHEAD_SIZE: usize = 1024;
33 /// The bit in the last image indicator byte for the PCI Data Structure that
34 /// indicates the last image. Bit 0-6 are reserved, bit 7 is last image bit.
35 const LAST_IMAGE_BIT_MASK: u8 = 0x80;
36 
37 /// BIOS Image Type from PCI Data Structure code_type field.
38 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
39 #[repr(u8)]
40 enum BiosImageType {
41     /// PC-AT compatible BIOS image (x86 legacy)
42     PciAt = 0x00,
43     /// EFI (Extensible Firmware Interface) BIOS image
44     Efi = 0x03,
45     /// NBSI (Notebook System Information) BIOS image
46     Nbsi = 0x70,
47     /// FwSec (Firmware Security) BIOS image
48     FwSec = 0xE0,
49 }
50 
51 impl TryFrom<u8> for BiosImageType {
52     type Error = Error;
53 
54     fn try_from(code: u8) -> Result<Self> {
55         match code {
56             0x00 => Ok(Self::PciAt),
57             0x03 => Ok(Self::Efi),
58             0x70 => Ok(Self::Nbsi),
59             0xE0 => Ok(Self::FwSec),
60             _ => Err(EINVAL),
61         }
62     }
63 }
64 
65 // PMU lookup table entry types. Used to locate PMU table entries
66 // in the Fwsec image, corresponding to falcon ucodes.
67 #[expect(dead_code)]
68 const FALCON_UCODE_ENTRY_APPID_FIRMWARE_SEC_LIC: u8 = 0x05;
69 #[expect(dead_code)]
70 const FALCON_UCODE_ENTRY_APPID_FWSEC_DBG: u8 = 0x45;
71 const FALCON_UCODE_ENTRY_APPID_FWSEC_PROD: u8 = 0x85;
72 
73 /// Vbios Reader for constructing the VBIOS data.
74 struct VbiosIterator<'a> {
75     dev: &'a device::Device,
76     bar0: &'a Bar0,
77     /// VBIOS data vector: As BIOS images are scanned, they are added to this vector for reference
78     /// or copying into other data structures. It is the entire scanned contents of the VBIOS which
79     /// progressively extends. It is used so that we do not re-read any contents that are already
80     /// read as we use the cumulative length read so far, and re-read any gaps as we extend the
81     /// length.
82     data: KVec<u8>,
83     /// Current offset of the [`Iterator`].
84     current_offset: usize,
85     /// Indicate whether the last image has been found.
86     last_found: bool,
87 }
88 
89 impl<'a> VbiosIterator<'a> {
90     fn new(dev: &'a device::Device, bar0: &'a Bar0) -> Result<Self> {
91         Ok(Self {
92             dev,
93             bar0,
94             data: KVec::new(),
95             current_offset: 0,
96             last_found: false,
97         })
98     }
99 
100     /// Read bytes from the ROM at the current end of the data vector.
101     fn read_more(&mut self, len: usize) -> Result {
102         let current_len = self.data.len();
103         let start = ROM_OFFSET + current_len;
104 
105         // Ensure length is a multiple of 4 for 32-bit reads
106         if len % core::mem::size_of::<u32>() != 0 {
107             dev_err!(
108                 self.dev,
109                 "VBIOS read length {} is not a multiple of 4\n",
110                 len
111             );
112             return Err(EINVAL);
113         }
114 
115         self.data.reserve(len, GFP_KERNEL)?;
116         // Read ROM data bytes and push directly to `data`.
117         for addr in (start..start + len).step_by(core::mem::size_of::<u32>()) {
118             // Read 32-bit word from the VBIOS ROM
119             let word = self.bar0.try_read32(addr)?;
120 
121             // Convert the `u32` to a 4 byte array and push each byte.
122             word.to_ne_bytes()
123                 .iter()
124                 .try_for_each(|&b| self.data.push(b, GFP_KERNEL))?;
125         }
126 
127         Ok(())
128     }
129 
130     /// Read bytes at a specific offset, filling any gap.
131     fn read_more_at_offset(&mut self, offset: usize, len: usize) -> Result {
132         if offset > BIOS_MAX_SCAN_LEN {
133             dev_err!(self.dev, "Error: exceeded BIOS scan limit.\n");
134             return Err(EINVAL);
135         }
136 
137         // If `offset` is beyond current data size, fill the gap first.
138         let current_len = self.data.len();
139         let gap_bytes = offset.saturating_sub(current_len);
140 
141         // Now read the requested bytes at the offset.
142         self.read_more(gap_bytes + len)
143     }
144 
145     /// Read a BIOS image at a specific offset and create a [`BiosImage`] from it.
146     ///
147     /// `self.data` is extended as needed and a new [`BiosImage`] is returned.
148     /// `context` is a string describing the operation for error reporting.
149     fn read_bios_image_at_offset(
150         &mut self,
151         offset: usize,
152         len: usize,
153         context: &str,
154     ) -> Result<BiosImage> {
155         let data_len = self.data.len();
156         if offset + len > data_len {
157             self.read_more_at_offset(offset, len).inspect_err(|e| {
158                 dev_err!(
159                     self.dev,
160                     "Failed to read more at offset {:#x}: {:?}\n",
161                     offset,
162                     e
163                 )
164             })?;
165         }
166 
167         BiosImage::new(self.dev, &self.data[offset..offset + len]).inspect_err(|err| {
168             dev_err!(
169                 self.dev,
170                 "Failed to {} at offset {:#x}: {:?}\n",
171                 context,
172                 offset,
173                 err
174             )
175         })
176     }
177 }
178 
179 impl<'a> Iterator for VbiosIterator<'a> {
180     type Item = Result<BiosImage>;
181 
182     /// Iterate over all VBIOS images until the last image is detected or offset
183     /// exceeds scan limit.
184     fn next(&mut self) -> Option<Self::Item> {
185         if self.last_found {
186             return None;
187         }
188 
189         if self.current_offset > BIOS_MAX_SCAN_LEN {
190             dev_err!(self.dev, "Error: exceeded BIOS scan limit, stopping scan\n");
191             return None;
192         }
193 
194         // Parse image headers first to get image size.
195         let image_size = match self.read_bios_image_at_offset(
196             self.current_offset,
197             BIOS_READ_AHEAD_SIZE,
198             "parse initial BIOS image headers",
199         ) {
200             Ok(image) => image.image_size_bytes(),
201             Err(e) => return Some(Err(e)),
202         };
203 
204         // Now create a new `BiosImage` with the full image data.
205         let full_image = match self.read_bios_image_at_offset(
206             self.current_offset,
207             image_size,
208             "parse full BIOS image",
209         ) {
210             Ok(image) => image,
211             Err(e) => return Some(Err(e)),
212         };
213 
214         self.last_found = full_image.is_last();
215 
216         // Advance to next image (aligned to 512 bytes).
217         self.current_offset += image_size;
218         self.current_offset = self.current_offset.align_up(Alignment::new::<512>())?;
219 
220         Some(Ok(full_image))
221     }
222 }
223 
224 pub(crate) struct Vbios {
225     fwsec_image: FwSecBiosImage,
226 }
227 
228 impl Vbios {
229     /// Probe for VBIOS extraction.
230     ///
231     /// Once the VBIOS object is built, `bar0` is not read for [`Vbios`] purposes anymore.
232     pub(crate) fn new(dev: &device::Device, bar0: &Bar0) -> Result<Vbios> {
233         // Images to extract from iteration
234         let mut pci_at_image: Option<PciAtBiosImage> = None;
235         let mut first_fwsec_image: Option<FwSecBiosBuilder> = None;
236         let mut second_fwsec_image: Option<FwSecBiosBuilder> = None;
237 
238         // Parse all VBIOS images in the ROM
239         for image_result in VbiosIterator::new(dev, bar0)? {
240             let image = image_result?;
241 
242             dev_dbg!(
243                 dev,
244                 "Found BIOS image: size: {:#x}, type: {:?}, last: {}\n",
245                 image.image_size_bytes(),
246                 image.image_type(),
247                 image.is_last()
248             );
249 
250             // Convert to a specific image type
251             match BiosImageType::try_from(image.pcir.code_type) {
252                 Ok(BiosImageType::PciAt) => {
253                     pci_at_image = Some(PciAtBiosImage::try_from(image)?);
254                 }
255                 Ok(BiosImageType::FwSec) => {
256                     let fwsec = FwSecBiosBuilder {
257                         base: image,
258                         falcon_data_offset: None,
259                         pmu_lookup_table: None,
260                         falcon_ucode_offset: None,
261                     };
262                     if first_fwsec_image.is_none() {
263                         first_fwsec_image = Some(fwsec);
264                     } else {
265                         second_fwsec_image = Some(fwsec);
266                     }
267                 }
268                 _ => {
269                     // Ignore other image types or unknown types
270                 }
271             }
272         }
273 
274         // Using all the images, setup the falcon data pointer in Fwsec.
275         if let (Some(mut second), Some(first), Some(pci_at)) =
276             (second_fwsec_image, first_fwsec_image, pci_at_image)
277         {
278             second
279                 .setup_falcon_data(&pci_at, &first)
280                 .inspect_err(|e| dev_err!(dev, "Falcon data setup failed: {:?}\n", e))?;
281             Ok(Vbios {
282                 fwsec_image: second.build()?,
283             })
284         } else {
285             dev_err!(
286                 dev,
287                 "Missing required images for falcon data setup, skipping\n"
288             );
289             Err(EINVAL)
290         }
291     }
292 
293     pub(crate) fn fwsec_image(&self) -> &FwSecBiosImage {
294         &self.fwsec_image
295     }
296 }
297 
298 /// PCI Data Structure as defined in PCI Firmware Specification
299 #[derive(Debug, Clone)]
300 #[repr(C)]
301 struct PcirStruct {
302     /// PCI Data Structure signature ("PCIR" or "NPDS")
303     signature: [u8; 4],
304     /// PCI Vendor ID (e.g., 0x10DE for NVIDIA)
305     vendor_id: u16,
306     /// PCI Device ID
307     device_id: u16,
308     /// Device List Pointer
309     device_list_ptr: u16,
310     /// PCI Data Structure Length
311     pci_data_struct_len: u16,
312     /// PCI Data Structure Revision
313     pci_data_struct_rev: u8,
314     /// Class code (3 bytes, 0x03 for display controller)
315     class_code: [u8; 3],
316     /// Size of this image in 512-byte blocks
317     image_len: u16,
318     /// Revision Level of the Vendor's ROM
319     vendor_rom_rev: u16,
320     /// ROM image type (0x00 = PC-AT compatible, 0x03 = EFI, 0x70 = NBSI)
321     code_type: u8,
322     /// Last image indicator (0x00 = Not last image, 0x80 = Last image)
323     last_image: u8,
324     /// Maximum Run-time Image Length (units of 512 bytes)
325     max_runtime_image_len: u16,
326 }
327 
328 // SAFETY: all bit patterns are valid for `PcirStruct`.
329 unsafe impl FromBytes for PcirStruct {}
330 
331 impl PcirStruct {
332     fn new(dev: &device::Device, data: &[u8]) -> Result<Self> {
333         let (pcir, _) = PcirStruct::from_bytes_copy_prefix(data).ok_or(EINVAL)?;
334 
335         // Signature should be "PCIR" (0x52494350) or "NPDS" (0x5344504e).
336         if &pcir.signature != b"PCIR" && &pcir.signature != b"NPDS" {
337             dev_err!(
338                 dev,
339                 "Invalid signature for PcirStruct: {:?}\n",
340                 pcir.signature
341             );
342             return Err(EINVAL);
343         }
344 
345         if pcir.image_len == 0 {
346             dev_err!(dev, "Invalid image length: 0\n");
347             return Err(EINVAL);
348         }
349 
350         Ok(pcir)
351     }
352 
353     /// Check if this is the last image in the ROM.
354     fn is_last(&self) -> bool {
355         self.last_image & LAST_IMAGE_BIT_MASK != 0
356     }
357 
358     /// Calculate image size in bytes from 512-byte blocks.
359     fn image_size_bytes(&self) -> usize {
360         usize::from(self.image_len) * 512
361     }
362 }
363 
364 /// BIOS Information Table (BIT) Header.
365 ///
366 /// This is the head of the BIT table, that is used to locate the Falcon data. The BIT table (with
367 /// its header) is in the [`PciAtBiosImage`] and the falcon data it is pointing to is in the
368 /// [`FwSecBiosImage`].
369 #[derive(Debug, Clone, Copy)]
370 #[repr(C)]
371 struct BitHeader {
372     /// 0h: BIT Header Identifier (BMP=0x7FFF/BIT=0xB8FF)
373     id: u16,
374     /// 2h: BIT Header Signature ("BIT\0")
375     signature: [u8; 4],
376     /// 6h: Binary Coded Decimal Version, ex: 0x0100 is 1.00.
377     bcd_version: u16,
378     /// 8h: Size of BIT Header (in bytes)
379     header_size: u8,
380     /// 9h: Size of BIT Tokens (in bytes)
381     token_size: u8,
382     /// 10h: Number of token entries that follow
383     token_entries: u8,
384     /// 11h: BIT Header Checksum
385     checksum: u8,
386 }
387 
388 // SAFETY: all bit patterns are valid for `BitHeader`.
389 unsafe impl FromBytes for BitHeader {}
390 
391 impl BitHeader {
392     fn new(data: &[u8]) -> Result<Self> {
393         let (header, _) = BitHeader::from_bytes_copy_prefix(data).ok_or(EINVAL)?;
394 
395         // Check header ID and signature
396         if header.id != 0xB8FF || &header.signature != b"BIT\0" {
397             return Err(EINVAL);
398         }
399 
400         Ok(header)
401     }
402 }
403 
404 /// BIT Token Entry: Records in the BIT table followed by the BIT header.
405 #[derive(Debug, Clone, Copy)]
406 #[expect(dead_code)]
407 struct BitToken {
408     /// 00h: Token identifier
409     id: u8,
410     /// 01h: Version of the token data
411     data_version: u8,
412     /// 02h: Size of token data in bytes
413     data_size: u16,
414     /// 04h: Offset to the token data
415     data_offset: u16,
416 }
417 
418 // Define the token ID for the Falcon data
419 const BIT_TOKEN_ID_FALCON_DATA: u8 = 0x70;
420 
421 impl BitToken {
422     /// Find a BIT token entry by BIT ID in a PciAtBiosImage
423     fn from_id(image: &PciAtBiosImage, token_id: u8) -> Result<Self> {
424         let header = &image.bit_header;
425 
426         // Offset to the first token entry
427         let tokens_start = image.bit_offset + usize::from(header.header_size);
428 
429         for i in 0..usize::from(header.token_entries) {
430             let entry_offset = tokens_start + (i * usize::from(header.token_size));
431 
432             // Make sure we don't go out of bounds
433             if entry_offset + usize::from(header.token_size) > image.base.data.len() {
434                 return Err(EINVAL);
435             }
436 
437             // Check if this token has the requested ID
438             if image.base.data[entry_offset] == token_id {
439                 return Ok(BitToken {
440                     id: image.base.data[entry_offset],
441                     data_version: image.base.data[entry_offset + 1],
442                     data_size: u16::from_le_bytes([
443                         image.base.data[entry_offset + 2],
444                         image.base.data[entry_offset + 3],
445                     ]),
446                     data_offset: u16::from_le_bytes([
447                         image.base.data[entry_offset + 4],
448                         image.base.data[entry_offset + 5],
449                     ]),
450                 });
451             }
452         }
453 
454         // Token not found
455         Err(ENOENT)
456     }
457 }
458 
459 /// PCI ROM Expansion Header as defined in PCI Firmware Specification.
460 ///
461 /// This is header is at the beginning of every image in the set of images in the ROM. It contains
462 /// a pointer to the PCI Data Structure which describes the image. For "NBSI" images (NoteBook
463 /// System Information), the ROM header deviates from the standard and contains an offset to the
464 /// NBSI image however we do not yet parse that in this module and keep it for future reference.
465 #[derive(Debug, Clone, Copy)]
466 #[expect(dead_code)]
467 struct PciRomHeader {
468     /// 00h: Signature (0xAA55)
469     signature: u16,
470     /// 02h: Reserved bytes for processor architecture unique data (20 bytes)
471     reserved: [u8; 20],
472     /// 16h: NBSI Data Offset (NBSI-specific, offset from header to NBSI image)
473     nbsi_data_offset: Option<u16>,
474     /// 18h: Pointer to PCI Data Structure (offset from start of ROM image)
475     pci_data_struct_offset: u16,
476     /// 1Ah: Size of block (this is NBSI-specific)
477     size_of_block: Option<u32>,
478 }
479 
480 impl PciRomHeader {
481     fn new(dev: &device::Device, data: &[u8]) -> Result<Self> {
482         if data.len() < 26 {
483             // Need at least 26 bytes to read pciDataStrucPtr and sizeOfBlock.
484             return Err(EINVAL);
485         }
486 
487         let signature = u16::from_le_bytes([data[0], data[1]]);
488 
489         // Check for valid ROM signatures.
490         match signature {
491             0xAA55 | 0xBB77 | 0x4E56 => {}
492             _ => {
493                 dev_err!(dev, "ROM signature unknown {:#x}\n", signature);
494                 return Err(EINVAL);
495             }
496         }
497 
498         // Read the pointer to the PCI Data Structure at offset 0x18.
499         let pci_data_struct_ptr = u16::from_le_bytes([data[24], data[25]]);
500 
501         // Try to read optional fields if enough data.
502         let mut size_of_block = None;
503         let mut nbsi_data_offset = None;
504 
505         if data.len() >= 30 {
506             // Read size_of_block at offset 0x1A.
507             size_of_block = Some(
508                 u32::from(data[29]) << 24
509                     | u32::from(data[28]) << 16
510                     | u32::from(data[27]) << 8
511                     | u32::from(data[26]),
512             );
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");
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         // Debug logging of entries (dumps the table data to dmesg)
891         for i in (header_len..required_bytes).step_by(entry_len) {
892             dev_dbg!(dev, "PMU entry: {:02x?}\n", &data[i..][..entry_len]);
893         }
894 
895         Ok(PmuLookupTable { header, table_data })
896     }
897 
898     fn lookup_index(&self, idx: u8) -> Result<PmuLookupTableEntry> {
899         if idx >= self.header.entry_count {
900             return Err(EINVAL);
901         }
902 
903         let index = (usize::from(idx)) * usize::from(self.header.entry_len);
904         PmuLookupTableEntry::new(&self.table_data[index..])
905     }
906 
907     // find entry by type value
908     fn find_entry_by_type(&self, entry_type: u8) -> Result<PmuLookupTableEntry> {
909         for i in 0..self.header.entry_count {
910             let entry = self.lookup_index(i)?;
911             if entry.application_id == entry_type {
912                 return Ok(entry);
913             }
914         }
915 
916         Err(EINVAL)
917     }
918 }
919 
920 impl FwSecBiosBuilder {
921     fn setup_falcon_data(
922         &mut self,
923         pci_at_image: &PciAtBiosImage,
924         first_fwsec: &FwSecBiosBuilder,
925     ) -> Result {
926         let mut offset = usize::from_safe_cast(pci_at_image.falcon_data_ptr()?);
927         let mut pmu_in_first_fwsec = false;
928 
929         // The falcon data pointer assumes that the PciAt and FWSEC images
930         // are contiguous in memory. However, testing shows the EFI image sits in
931         // between them. So calculate the offset from the end of the PciAt image
932         // rather than the start of it. Compensate.
933         offset -= pci_at_image.base.data.len();
934 
935         // The offset is now from the start of the first Fwsec image, however
936         // the offset points to a location in the second Fwsec image. Since
937         // the fwsec images are contiguous, subtract the length of the first Fwsec
938         // image from the offset to get the offset to the start of the second
939         // Fwsec image.
940         if offset < first_fwsec.base.data.len() {
941             pmu_in_first_fwsec = true;
942         } else {
943             offset -= first_fwsec.base.data.len();
944         }
945 
946         self.falcon_data_offset = Some(offset);
947 
948         if pmu_in_first_fwsec {
949             self.pmu_lookup_table = Some(PmuLookupTable::new(
950                 &self.base.dev,
951                 &first_fwsec.base.data[offset..],
952             )?);
953         } else {
954             self.pmu_lookup_table = Some(PmuLookupTable::new(
955                 &self.base.dev,
956                 &self.base.data[offset..],
957             )?);
958         }
959 
960         match self
961             .pmu_lookup_table
962             .as_ref()
963             .ok_or(EINVAL)?
964             .find_entry_by_type(FALCON_UCODE_ENTRY_APPID_FWSEC_PROD)
965         {
966             Ok(entry) => {
967                 let mut ucode_offset = usize::from_safe_cast(entry.data);
968                 ucode_offset -= pci_at_image.base.data.len();
969                 if ucode_offset < first_fwsec.base.data.len() {
970                     dev_err!(self.base.dev, "Falcon Ucode offset not in second Fwsec.\n");
971                     return Err(EINVAL);
972                 }
973                 ucode_offset -= first_fwsec.base.data.len();
974                 self.falcon_ucode_offset = Some(ucode_offset);
975             }
976             Err(e) => {
977                 dev_err!(
978                     self.base.dev,
979                     "PmuLookupTableEntry not found, error: {:?}\n",
980                     e
981                 );
982                 return Err(EINVAL);
983             }
984         }
985         Ok(())
986     }
987 
988     /// Build the final FwSecBiosImage from this builder
989     fn build(self) -> Result<FwSecBiosImage> {
990         let ret = FwSecBiosImage {
991             base: self.base,
992             falcon_ucode_offset: self.falcon_ucode_offset.ok_or(EINVAL)?,
993         };
994 
995         if cfg!(debug_assertions) {
996             // Print the desc header for debugging
997             let desc = ret.header()?;
998             dev_dbg!(ret.base.dev, "PmuLookupTableEntry desc: {:#?}\n", desc);
999         }
1000 
1001         Ok(ret)
1002     }
1003 }
1004 
1005 impl FwSecBiosImage {
1006     /// Get the FwSec header ([`FalconUCodeDescV3`]).
1007     pub(crate) fn header(&self) -> Result<&FalconUCodeDescV3> {
1008         // Get the falcon ucode offset that was found in setup_falcon_data.
1009         let falcon_ucode_offset = self.falcon_ucode_offset;
1010 
1011         // Make sure the offset is within the data bounds.
1012         if falcon_ucode_offset + core::mem::size_of::<FalconUCodeDescV3>() > self.base.data.len() {
1013             dev_err!(
1014                 self.base.dev,
1015                 "fwsec-frts header not contained within BIOS bounds\n"
1016             );
1017             return Err(ERANGE);
1018         }
1019 
1020         // Read the first 4 bytes to get the version.
1021         let hdr_bytes: [u8; 4] = self.base.data[falcon_ucode_offset..falcon_ucode_offset + 4]
1022             .try_into()
1023             .map_err(|_| EINVAL)?;
1024         let hdr = u32::from_le_bytes(hdr_bytes);
1025         let ver = (hdr & 0xff00) >> 8;
1026 
1027         if ver != 3 {
1028             dev_err!(self.base.dev, "invalid fwsec firmware version: {:?}\n", ver);
1029             return Err(EINVAL);
1030         }
1031 
1032         // Return a reference to the FalconUCodeDescV3 structure.
1033         //
1034         // SAFETY: We have checked that `falcon_ucode_offset + size_of::<FalconUCodeDescV3>` is
1035         // within the bounds of `data`. Also, this data vector is from ROM, and the `data` field
1036         // in `BiosImageBase` is immutable after construction.
1037         Ok(unsafe {
1038             &*(self
1039                 .base
1040                 .data
1041                 .as_ptr()
1042                 .add(falcon_ucode_offset)
1043                 .cast::<FalconUCodeDescV3>())
1044         })
1045     }
1046 
1047     /// Get the ucode data as a byte slice
1048     pub(crate) fn ucode(&self, desc: &FalconUCodeDescV3) -> Result<&[u8]> {
1049         let falcon_ucode_offset = self.falcon_ucode_offset;
1050 
1051         // The ucode data follows the descriptor.
1052         let ucode_data_offset = falcon_ucode_offset + desc.size();
1053         let size = usize::from_safe_cast(desc.imem_load_size + desc.dmem_load_size);
1054 
1055         // Get the data slice, checking bounds in a single operation.
1056         self.base
1057             .data
1058             .get(ucode_data_offset..ucode_data_offset + size)
1059             .ok_or(ERANGE)
1060             .inspect_err(|_| {
1061                 dev_err!(
1062                     self.base.dev,
1063                     "fwsec ucode data not contained within BIOS bounds\n"
1064                 )
1065             })
1066     }
1067 
1068     /// Get the signatures as a byte slice
1069     pub(crate) fn sigs(&self, desc: &FalconUCodeDescV3) -> Result<&[Bcrt30Rsa3kSignature]> {
1070         // The signatures data follows the descriptor.
1071         let sigs_data_offset = self.falcon_ucode_offset + core::mem::size_of::<FalconUCodeDescV3>();
1072         let sigs_count = usize::from(desc.signature_count);
1073         let sigs_size = sigs_count * core::mem::size_of::<Bcrt30Rsa3kSignature>();
1074 
1075         // Make sure the data is within bounds.
1076         if sigs_data_offset + sigs_size > self.base.data.len() {
1077             dev_err!(
1078                 self.base.dev,
1079                 "fwsec signatures data not contained within BIOS bounds\n"
1080             );
1081             return Err(ERANGE);
1082         }
1083 
1084         // SAFETY: we checked that `data + sigs_data_offset + (signature_count *
1085         // sizeof::<Bcrt30Rsa3kSignature>()` is within the bounds of `data`.
1086         Ok(unsafe {
1087             core::slice::from_raw_parts(
1088                 self.base
1089                     .data
1090                     .as_ptr()
1091                     .add(sigs_data_offset)
1092                     .cast::<Bcrt30Rsa3kSignature>(),
1093                 sigs_count,
1094             )
1095         })
1096     }
1097 }
1098