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