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