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