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