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