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