xref: /linux/drivers/gpu/nova-core/vbios.rs (revision 47c4846e4319503e9cd0bd0922facd0fb1e383d0)
16fda04e7SJoel Fernandes // SPDX-License-Identifier: GPL-2.0
26fda04e7SJoel Fernandes 
36fda04e7SJoel Fernandes //! VBIOS extraction and parsing.
46fda04e7SJoel Fernandes 
56fda04e7SJoel Fernandes // To be removed when all code is used.
66fda04e7SJoel Fernandes #![expect(dead_code)]
76fda04e7SJoel Fernandes 
86fda04e7SJoel Fernandes use crate::driver::Bar0;
9*47c4846eSJoel Fernandes use crate::firmware::FalconUCodeDescV3;
106fda04e7SJoel Fernandes use core::convert::TryFrom;
11*47c4846eSJoel Fernandes use kernel::device;
126fda04e7SJoel Fernandes use kernel::error::Result;
136fda04e7SJoel Fernandes use kernel::pci;
146fda04e7SJoel Fernandes use kernel::prelude::*;
156fda04e7SJoel Fernandes 
166fda04e7SJoel Fernandes /// The offset of the VBIOS ROM in the BAR0 space.
176fda04e7SJoel Fernandes const ROM_OFFSET: usize = 0x300000;
186fda04e7SJoel Fernandes /// The maximum length of the VBIOS ROM to scan into.
196fda04e7SJoel Fernandes const BIOS_MAX_SCAN_LEN: usize = 0x100000;
206fda04e7SJoel Fernandes /// The size to read ahead when parsing initial BIOS image headers.
216fda04e7SJoel Fernandes const BIOS_READ_AHEAD_SIZE: usize = 1024;
226fda04e7SJoel Fernandes /// The bit in the last image indicator byte for the PCI Data Structure that
236fda04e7SJoel Fernandes /// indicates the last image. Bit 0-6 are reserved, bit 7 is last image bit.
246fda04e7SJoel Fernandes const LAST_IMAGE_BIT_MASK: u8 = 0x80;
256fda04e7SJoel Fernandes 
266fda04e7SJoel Fernandes // PMU lookup table entry types. Used to locate PMU table entries
276fda04e7SJoel Fernandes // in the Fwsec image, corresponding to falcon ucodes.
286fda04e7SJoel Fernandes #[expect(dead_code)]
296fda04e7SJoel Fernandes const FALCON_UCODE_ENTRY_APPID_FIRMWARE_SEC_LIC: u8 = 0x05;
306fda04e7SJoel Fernandes #[expect(dead_code)]
316fda04e7SJoel Fernandes const FALCON_UCODE_ENTRY_APPID_FWSEC_DBG: u8 = 0x45;
326fda04e7SJoel Fernandes const FALCON_UCODE_ENTRY_APPID_FWSEC_PROD: u8 = 0x85;
336fda04e7SJoel Fernandes 
346fda04e7SJoel Fernandes /// Vbios Reader for constructing the VBIOS data.
356fda04e7SJoel Fernandes struct VbiosIterator<'a> {
366fda04e7SJoel Fernandes     pdev: &'a pci::Device,
376fda04e7SJoel Fernandes     bar0: &'a Bar0,
386fda04e7SJoel Fernandes     /// VBIOS data vector: As BIOS images are scanned, they are added to this vector for reference
396fda04e7SJoel Fernandes     /// or copying into other data structures. It is the entire scanned contents of the VBIOS which
406fda04e7SJoel Fernandes     /// progressively extends. It is used so that we do not re-read any contents that are already
416fda04e7SJoel Fernandes     /// read as we use the cumulative length read so far, and re-read any gaps as we extend the
426fda04e7SJoel Fernandes     /// length.
436fda04e7SJoel Fernandes     data: KVec<u8>,
446fda04e7SJoel Fernandes     /// Current offset of the [`Iterator`].
456fda04e7SJoel Fernandes     current_offset: usize,
466fda04e7SJoel Fernandes     /// Indicate whether the last image has been found.
476fda04e7SJoel Fernandes     last_found: bool,
486fda04e7SJoel Fernandes }
496fda04e7SJoel Fernandes 
506fda04e7SJoel Fernandes impl<'a> VbiosIterator<'a> {
516fda04e7SJoel Fernandes     fn new(pdev: &'a pci::Device, bar0: &'a Bar0) -> Result<Self> {
526fda04e7SJoel Fernandes         Ok(Self {
536fda04e7SJoel Fernandes             pdev,
546fda04e7SJoel Fernandes             bar0,
556fda04e7SJoel Fernandes             data: KVec::new(),
566fda04e7SJoel Fernandes             current_offset: 0,
576fda04e7SJoel Fernandes             last_found: false,
586fda04e7SJoel Fernandes         })
596fda04e7SJoel Fernandes     }
606fda04e7SJoel Fernandes 
616fda04e7SJoel Fernandes     /// Read bytes from the ROM at the current end of the data vector.
626fda04e7SJoel Fernandes     fn read_more(&mut self, len: usize) -> Result {
636fda04e7SJoel Fernandes         let current_len = self.data.len();
646fda04e7SJoel Fernandes         let start = ROM_OFFSET + current_len;
656fda04e7SJoel Fernandes 
666fda04e7SJoel Fernandes         // Ensure length is a multiple of 4 for 32-bit reads
676fda04e7SJoel Fernandes         if len % core::mem::size_of::<u32>() != 0 {
686fda04e7SJoel Fernandes             dev_err!(
696fda04e7SJoel Fernandes                 self.pdev.as_ref(),
706fda04e7SJoel Fernandes                 "VBIOS read length {} is not a multiple of 4\n",
716fda04e7SJoel Fernandes                 len
726fda04e7SJoel Fernandes             );
736fda04e7SJoel Fernandes             return Err(EINVAL);
746fda04e7SJoel Fernandes         }
756fda04e7SJoel Fernandes 
766fda04e7SJoel Fernandes         self.data.reserve(len, GFP_KERNEL)?;
776fda04e7SJoel Fernandes         // Read ROM data bytes and push directly to `data`.
786fda04e7SJoel Fernandes         for addr in (start..start + len).step_by(core::mem::size_of::<u32>()) {
796fda04e7SJoel Fernandes             // Read 32-bit word from the VBIOS ROM
806fda04e7SJoel Fernandes             let word = self.bar0.try_read32(addr)?;
816fda04e7SJoel Fernandes 
826fda04e7SJoel Fernandes             // Convert the `u32` to a 4 byte array and push each byte.
836fda04e7SJoel Fernandes             word.to_ne_bytes()
846fda04e7SJoel Fernandes                 .iter()
856fda04e7SJoel Fernandes                 .try_for_each(|&b| self.data.push(b, GFP_KERNEL))?;
866fda04e7SJoel Fernandes         }
876fda04e7SJoel Fernandes 
886fda04e7SJoel Fernandes         Ok(())
896fda04e7SJoel Fernandes     }
906fda04e7SJoel Fernandes 
916fda04e7SJoel Fernandes     /// Read bytes at a specific offset, filling any gap.
926fda04e7SJoel Fernandes     fn read_more_at_offset(&mut self, offset: usize, len: usize) -> Result {
936fda04e7SJoel Fernandes         if offset > BIOS_MAX_SCAN_LEN {
946fda04e7SJoel Fernandes             dev_err!(self.pdev.as_ref(), "Error: exceeded BIOS scan limit.\n");
956fda04e7SJoel Fernandes             return Err(EINVAL);
966fda04e7SJoel Fernandes         }
976fda04e7SJoel Fernandes 
986fda04e7SJoel Fernandes         // If `offset` is beyond current data size, fill the gap first.
996fda04e7SJoel Fernandes         let current_len = self.data.len();
1006fda04e7SJoel Fernandes         let gap_bytes = offset.saturating_sub(current_len);
1016fda04e7SJoel Fernandes 
1026fda04e7SJoel Fernandes         // Now read the requested bytes at the offset.
1036fda04e7SJoel Fernandes         self.read_more(gap_bytes + len)
1046fda04e7SJoel Fernandes     }
1056fda04e7SJoel Fernandes 
1066fda04e7SJoel Fernandes     /// Read a BIOS image at a specific offset and create a [`BiosImage`] from it.
1076fda04e7SJoel Fernandes     ///
1086fda04e7SJoel Fernandes     /// `self.data` is extended as needed and a new [`BiosImage`] is returned.
1096fda04e7SJoel Fernandes     /// `context` is a string describing the operation for error reporting.
1106fda04e7SJoel Fernandes     fn read_bios_image_at_offset(
1116fda04e7SJoel Fernandes         &mut self,
1126fda04e7SJoel Fernandes         offset: usize,
1136fda04e7SJoel Fernandes         len: usize,
1146fda04e7SJoel Fernandes         context: &str,
1156fda04e7SJoel Fernandes     ) -> Result<BiosImage> {
1166fda04e7SJoel Fernandes         let data_len = self.data.len();
1176fda04e7SJoel Fernandes         if offset + len > data_len {
1186fda04e7SJoel Fernandes             self.read_more_at_offset(offset, len).inspect_err(|e| {
1196fda04e7SJoel Fernandes                 dev_err!(
1206fda04e7SJoel Fernandes                     self.pdev.as_ref(),
1216fda04e7SJoel Fernandes                     "Failed to read more at offset {:#x}: {:?}\n",
1226fda04e7SJoel Fernandes                     offset,
1236fda04e7SJoel Fernandes                     e
1246fda04e7SJoel Fernandes                 )
1256fda04e7SJoel Fernandes             })?;
1266fda04e7SJoel Fernandes         }
1276fda04e7SJoel Fernandes 
1286fda04e7SJoel Fernandes         BiosImage::new(self.pdev, &self.data[offset..offset + len]).inspect_err(|err| {
1296fda04e7SJoel Fernandes             dev_err!(
1306fda04e7SJoel Fernandes                 self.pdev.as_ref(),
1316fda04e7SJoel Fernandes                 "Failed to {} at offset {:#x}: {:?}\n",
1326fda04e7SJoel Fernandes                 context,
1336fda04e7SJoel Fernandes                 offset,
1346fda04e7SJoel Fernandes                 err
1356fda04e7SJoel Fernandes             )
1366fda04e7SJoel Fernandes         })
1376fda04e7SJoel Fernandes     }
1386fda04e7SJoel Fernandes }
1396fda04e7SJoel Fernandes 
1406fda04e7SJoel Fernandes impl<'a> Iterator for VbiosIterator<'a> {
1416fda04e7SJoel Fernandes     type Item = Result<BiosImage>;
1426fda04e7SJoel Fernandes 
1436fda04e7SJoel Fernandes     /// Iterate over all VBIOS images until the last image is detected or offset
1446fda04e7SJoel Fernandes     /// exceeds scan limit.
1456fda04e7SJoel Fernandes     fn next(&mut self) -> Option<Self::Item> {
1466fda04e7SJoel Fernandes         if self.last_found {
1476fda04e7SJoel Fernandes             return None;
1486fda04e7SJoel Fernandes         }
1496fda04e7SJoel Fernandes 
1506fda04e7SJoel Fernandes         if self.current_offset > BIOS_MAX_SCAN_LEN {
1516fda04e7SJoel Fernandes             dev_err!(
1526fda04e7SJoel Fernandes                 self.pdev.as_ref(),
1536fda04e7SJoel Fernandes                 "Error: exceeded BIOS scan limit, stopping scan\n"
1546fda04e7SJoel Fernandes             );
1556fda04e7SJoel Fernandes             return None;
1566fda04e7SJoel Fernandes         }
1576fda04e7SJoel Fernandes 
1586fda04e7SJoel Fernandes         // Parse image headers first to get image size.
1596fda04e7SJoel Fernandes         let image_size = match self.read_bios_image_at_offset(
1606fda04e7SJoel Fernandes             self.current_offset,
1616fda04e7SJoel Fernandes             BIOS_READ_AHEAD_SIZE,
1626fda04e7SJoel Fernandes             "parse initial BIOS image headers",
1636fda04e7SJoel Fernandes         ) {
1646fda04e7SJoel Fernandes             Ok(image) => image.image_size_bytes(),
1656fda04e7SJoel Fernandes             Err(e) => return Some(Err(e)),
1666fda04e7SJoel Fernandes         };
1676fda04e7SJoel Fernandes 
1686fda04e7SJoel Fernandes         // Now create a new `BiosImage` with the full image data.
1696fda04e7SJoel Fernandes         let full_image = match self.read_bios_image_at_offset(
1706fda04e7SJoel Fernandes             self.current_offset,
1716fda04e7SJoel Fernandes             image_size,
1726fda04e7SJoel Fernandes             "parse full BIOS image",
1736fda04e7SJoel Fernandes         ) {
1746fda04e7SJoel Fernandes             Ok(image) => image,
1756fda04e7SJoel Fernandes             Err(e) => return Some(Err(e)),
1766fda04e7SJoel Fernandes         };
1776fda04e7SJoel Fernandes 
1786fda04e7SJoel Fernandes         self.last_found = full_image.is_last();
1796fda04e7SJoel Fernandes 
1806fda04e7SJoel Fernandes         // Advance to next image (aligned to 512 bytes).
1816fda04e7SJoel Fernandes         self.current_offset += image_size;
1826fda04e7SJoel Fernandes         // TODO: replace with `align_up` once it lands.
1836fda04e7SJoel Fernandes         self.current_offset = self.current_offset.next_multiple_of(512);
1846fda04e7SJoel Fernandes 
1856fda04e7SJoel Fernandes         Some(Ok(full_image))
1866fda04e7SJoel Fernandes     }
1876fda04e7SJoel Fernandes }
1886fda04e7SJoel Fernandes 
1896fda04e7SJoel Fernandes pub(crate) struct Vbios {
1906fda04e7SJoel Fernandes     fwsec_image: FwSecBiosImage,
1916fda04e7SJoel Fernandes }
1926fda04e7SJoel Fernandes 
1936fda04e7SJoel Fernandes impl Vbios {
1946fda04e7SJoel Fernandes     /// Probe for VBIOS extraction.
1956fda04e7SJoel Fernandes     ///
1966fda04e7SJoel Fernandes     /// Once the VBIOS object is built, `bar0` is not read for [`Vbios`] purposes anymore.
1976fda04e7SJoel Fernandes     pub(crate) fn new(pdev: &pci::Device, bar0: &Bar0) -> Result<Vbios> {
1986fda04e7SJoel Fernandes         // Images to extract from iteration
1996fda04e7SJoel Fernandes         let mut pci_at_image: Option<PciAtBiosImage> = None;
200*47c4846eSJoel Fernandes         let mut first_fwsec_image: Option<FwSecBiosBuilder> = None;
201*47c4846eSJoel Fernandes         let mut second_fwsec_image: Option<FwSecBiosBuilder> = None;
2026fda04e7SJoel Fernandes 
2036fda04e7SJoel Fernandes         // Parse all VBIOS images in the ROM
2046fda04e7SJoel Fernandes         for image_result in VbiosIterator::new(pdev, bar0)? {
2056fda04e7SJoel Fernandes             let full_image = image_result?;
2066fda04e7SJoel Fernandes 
2076fda04e7SJoel Fernandes             dev_dbg!(
2086fda04e7SJoel Fernandes                 pdev.as_ref(),
2096fda04e7SJoel Fernandes                 "Found BIOS image: size: {:#x}, type: {}, last: {}\n",
2106fda04e7SJoel Fernandes                 full_image.image_size_bytes(),
2116fda04e7SJoel Fernandes                 full_image.image_type_str(),
2126fda04e7SJoel Fernandes                 full_image.is_last()
2136fda04e7SJoel Fernandes             );
2146fda04e7SJoel Fernandes 
2156fda04e7SJoel Fernandes             // Get references to images we will need after the loop, in order to
2166fda04e7SJoel Fernandes             // setup the falcon data offset.
2176fda04e7SJoel Fernandes             match full_image {
2186fda04e7SJoel Fernandes                 BiosImage::PciAt(image) => {
2196fda04e7SJoel Fernandes                     pci_at_image = Some(image);
2206fda04e7SJoel Fernandes                 }
2216fda04e7SJoel Fernandes                 BiosImage::FwSec(image) => {
2226fda04e7SJoel Fernandes                     if first_fwsec_image.is_none() {
2236fda04e7SJoel Fernandes                         first_fwsec_image = Some(image);
2246fda04e7SJoel Fernandes                     } else {
2256fda04e7SJoel Fernandes                         second_fwsec_image = Some(image);
2266fda04e7SJoel Fernandes                     }
2276fda04e7SJoel Fernandes                 }
2286fda04e7SJoel Fernandes                 // For now we don't need to handle these
2296fda04e7SJoel Fernandes                 BiosImage::Efi(_image) => {}
2306fda04e7SJoel Fernandes                 BiosImage::Nbsi(_image) => {}
2316fda04e7SJoel Fernandes             }
2326fda04e7SJoel Fernandes         }
2336fda04e7SJoel Fernandes 
2346fda04e7SJoel Fernandes         // Using all the images, setup the falcon data pointer in Fwsec.
235*47c4846eSJoel Fernandes         if let (Some(mut second), Some(first), Some(pci_at)) =
2366fda04e7SJoel Fernandes             (second_fwsec_image, first_fwsec_image, pci_at_image)
2376fda04e7SJoel Fernandes         {
238*47c4846eSJoel Fernandes             second
239*47c4846eSJoel Fernandes                 .setup_falcon_data(pdev, &pci_at, &first)
240*47c4846eSJoel Fernandes                 .inspect_err(|e| dev_err!(pdev.as_ref(), "Falcon data setup failed: {:?}\n", e))?;
2416fda04e7SJoel Fernandes             Ok(Vbios {
242*47c4846eSJoel Fernandes                 fwsec_image: second.build(pdev)?,
2436fda04e7SJoel Fernandes             })
2446fda04e7SJoel Fernandes         } else {
2456fda04e7SJoel Fernandes             dev_err!(
2466fda04e7SJoel Fernandes                 pdev.as_ref(),
2476fda04e7SJoel Fernandes                 "Missing required images for falcon data setup, skipping\n"
2486fda04e7SJoel Fernandes             );
2496fda04e7SJoel Fernandes             Err(EINVAL)
2506fda04e7SJoel Fernandes         }
2516fda04e7SJoel Fernandes     }
252*47c4846eSJoel Fernandes 
253*47c4846eSJoel Fernandes     pub(crate) fn fwsec_image(&self) -> &FwSecBiosImage {
254*47c4846eSJoel Fernandes         &self.fwsec_image
255*47c4846eSJoel Fernandes     }
2566fda04e7SJoel Fernandes }
2576fda04e7SJoel Fernandes 
2586fda04e7SJoel Fernandes /// PCI Data Structure as defined in PCI Firmware Specification
2596fda04e7SJoel Fernandes #[derive(Debug, Clone)]
2606fda04e7SJoel Fernandes #[repr(C)]
2616fda04e7SJoel Fernandes struct PcirStruct {
2626fda04e7SJoel Fernandes     /// PCI Data Structure signature ("PCIR" or "NPDS")
2636fda04e7SJoel Fernandes     signature: [u8; 4],
2646fda04e7SJoel Fernandes     /// PCI Vendor ID (e.g., 0x10DE for NVIDIA)
2656fda04e7SJoel Fernandes     vendor_id: u16,
2666fda04e7SJoel Fernandes     /// PCI Device ID
2676fda04e7SJoel Fernandes     device_id: u16,
2686fda04e7SJoel Fernandes     /// Device List Pointer
2696fda04e7SJoel Fernandes     device_list_ptr: u16,
2706fda04e7SJoel Fernandes     /// PCI Data Structure Length
2716fda04e7SJoel Fernandes     pci_data_struct_len: u16,
2726fda04e7SJoel Fernandes     /// PCI Data Structure Revision
2736fda04e7SJoel Fernandes     pci_data_struct_rev: u8,
2746fda04e7SJoel Fernandes     /// Class code (3 bytes, 0x03 for display controller)
2756fda04e7SJoel Fernandes     class_code: [u8; 3],
2766fda04e7SJoel Fernandes     /// Size of this image in 512-byte blocks
2776fda04e7SJoel Fernandes     image_len: u16,
2786fda04e7SJoel Fernandes     /// Revision Level of the Vendor's ROM
2796fda04e7SJoel Fernandes     vendor_rom_rev: u16,
2806fda04e7SJoel Fernandes     /// ROM image type (0x00 = PC-AT compatible, 0x03 = EFI, 0x70 = NBSI)
2816fda04e7SJoel Fernandes     code_type: u8,
2826fda04e7SJoel Fernandes     /// Last image indicator (0x00 = Not last image, 0x80 = Last image)
2836fda04e7SJoel Fernandes     last_image: u8,
2846fda04e7SJoel Fernandes     /// Maximum Run-time Image Length (units of 512 bytes)
2856fda04e7SJoel Fernandes     max_runtime_image_len: u16,
2866fda04e7SJoel Fernandes }
2876fda04e7SJoel Fernandes 
2886fda04e7SJoel Fernandes impl PcirStruct {
2896fda04e7SJoel Fernandes     fn new(pdev: &pci::Device, data: &[u8]) -> Result<Self> {
2906fda04e7SJoel Fernandes         if data.len() < core::mem::size_of::<PcirStruct>() {
2916fda04e7SJoel Fernandes             dev_err!(pdev.as_ref(), "Not enough data for PcirStruct\n");
2926fda04e7SJoel Fernandes             return Err(EINVAL);
2936fda04e7SJoel Fernandes         }
2946fda04e7SJoel Fernandes 
2956fda04e7SJoel Fernandes         let mut signature = [0u8; 4];
2966fda04e7SJoel Fernandes         signature.copy_from_slice(&data[0..4]);
2976fda04e7SJoel Fernandes 
2986fda04e7SJoel Fernandes         // Signature should be "PCIR" (0x52494350) or "NPDS" (0x5344504e).
2996fda04e7SJoel Fernandes         if &signature != b"PCIR" && &signature != b"NPDS" {
3006fda04e7SJoel Fernandes             dev_err!(
3016fda04e7SJoel Fernandes                 pdev.as_ref(),
3026fda04e7SJoel Fernandes                 "Invalid signature for PcirStruct: {:?}\n",
3036fda04e7SJoel Fernandes                 signature
3046fda04e7SJoel Fernandes             );
3056fda04e7SJoel Fernandes             return Err(EINVAL);
3066fda04e7SJoel Fernandes         }
3076fda04e7SJoel Fernandes 
3086fda04e7SJoel Fernandes         let mut class_code = [0u8; 3];
3096fda04e7SJoel Fernandes         class_code.copy_from_slice(&data[13..16]);
3106fda04e7SJoel Fernandes 
3116fda04e7SJoel Fernandes         let image_len = u16::from_le_bytes([data[16], data[17]]);
3126fda04e7SJoel Fernandes         if image_len == 0 {
3136fda04e7SJoel Fernandes             dev_err!(pdev.as_ref(), "Invalid image length: 0\n");
3146fda04e7SJoel Fernandes             return Err(EINVAL);
3156fda04e7SJoel Fernandes         }
3166fda04e7SJoel Fernandes 
3176fda04e7SJoel Fernandes         Ok(PcirStruct {
3186fda04e7SJoel Fernandes             signature,
3196fda04e7SJoel Fernandes             vendor_id: u16::from_le_bytes([data[4], data[5]]),
3206fda04e7SJoel Fernandes             device_id: u16::from_le_bytes([data[6], data[7]]),
3216fda04e7SJoel Fernandes             device_list_ptr: u16::from_le_bytes([data[8], data[9]]),
3226fda04e7SJoel Fernandes             pci_data_struct_len: u16::from_le_bytes([data[10], data[11]]),
3236fda04e7SJoel Fernandes             pci_data_struct_rev: data[12],
3246fda04e7SJoel Fernandes             class_code,
3256fda04e7SJoel Fernandes             image_len,
3266fda04e7SJoel Fernandes             vendor_rom_rev: u16::from_le_bytes([data[18], data[19]]),
3276fda04e7SJoel Fernandes             code_type: data[20],
3286fda04e7SJoel Fernandes             last_image: data[21],
3296fda04e7SJoel Fernandes             max_runtime_image_len: u16::from_le_bytes([data[22], data[23]]),
3306fda04e7SJoel Fernandes         })
3316fda04e7SJoel Fernandes     }
3326fda04e7SJoel Fernandes 
3336fda04e7SJoel Fernandes     /// Check if this is the last image in the ROM.
3346fda04e7SJoel Fernandes     fn is_last(&self) -> bool {
3356fda04e7SJoel Fernandes         self.last_image & LAST_IMAGE_BIT_MASK != 0
3366fda04e7SJoel Fernandes     }
3376fda04e7SJoel Fernandes 
3386fda04e7SJoel Fernandes     /// Calculate image size in bytes from 512-byte blocks.
3396fda04e7SJoel Fernandes     fn image_size_bytes(&self) -> usize {
3406fda04e7SJoel Fernandes         self.image_len as usize * 512
3416fda04e7SJoel Fernandes     }
3426fda04e7SJoel Fernandes }
3436fda04e7SJoel Fernandes 
344dc70c6aeSJoel Fernandes /// BIOS Information Table (BIT) Header.
345dc70c6aeSJoel Fernandes ///
346dc70c6aeSJoel Fernandes /// This is the head of the BIT table, that is used to locate the Falcon data. The BIT table (with
347dc70c6aeSJoel Fernandes /// its header) is in the [`PciAtBiosImage`] and the falcon data it is pointing to is in the
348dc70c6aeSJoel Fernandes /// [`FwSecBiosImage`].
349dc70c6aeSJoel Fernandes #[derive(Debug, Clone, Copy)]
350dc70c6aeSJoel Fernandes #[expect(dead_code)]
351dc70c6aeSJoel Fernandes struct BitHeader {
352dc70c6aeSJoel Fernandes     /// 0h: BIT Header Identifier (BMP=0x7FFF/BIT=0xB8FF)
353dc70c6aeSJoel Fernandes     id: u16,
354dc70c6aeSJoel Fernandes     /// 2h: BIT Header Signature ("BIT\0")
355dc70c6aeSJoel Fernandes     signature: [u8; 4],
356dc70c6aeSJoel Fernandes     /// 6h: Binary Coded Decimal Version, ex: 0x0100 is 1.00.
357dc70c6aeSJoel Fernandes     bcd_version: u16,
358dc70c6aeSJoel Fernandes     /// 8h: Size of BIT Header (in bytes)
359dc70c6aeSJoel Fernandes     header_size: u8,
360dc70c6aeSJoel Fernandes     /// 9h: Size of BIT Tokens (in bytes)
361dc70c6aeSJoel Fernandes     token_size: u8,
362dc70c6aeSJoel Fernandes     /// 10h: Number of token entries that follow
363dc70c6aeSJoel Fernandes     token_entries: u8,
364dc70c6aeSJoel Fernandes     /// 11h: BIT Header Checksum
365dc70c6aeSJoel Fernandes     checksum: u8,
366dc70c6aeSJoel Fernandes }
367dc70c6aeSJoel Fernandes 
368dc70c6aeSJoel Fernandes impl BitHeader {
369dc70c6aeSJoel Fernandes     fn new(data: &[u8]) -> Result<Self> {
370dc70c6aeSJoel Fernandes         if data.len() < 12 {
371dc70c6aeSJoel Fernandes             return Err(EINVAL);
372dc70c6aeSJoel Fernandes         }
373dc70c6aeSJoel Fernandes 
374dc70c6aeSJoel Fernandes         let mut signature = [0u8; 4];
375dc70c6aeSJoel Fernandes         signature.copy_from_slice(&data[2..6]);
376dc70c6aeSJoel Fernandes 
377dc70c6aeSJoel Fernandes         // Check header ID and signature
378dc70c6aeSJoel Fernandes         let id = u16::from_le_bytes([data[0], data[1]]);
379dc70c6aeSJoel Fernandes         if id != 0xB8FF || &signature != b"BIT\0" {
380dc70c6aeSJoel Fernandes             return Err(EINVAL);
381dc70c6aeSJoel Fernandes         }
382dc70c6aeSJoel Fernandes 
383dc70c6aeSJoel Fernandes         Ok(BitHeader {
384dc70c6aeSJoel Fernandes             id,
385dc70c6aeSJoel Fernandes             signature,
386dc70c6aeSJoel Fernandes             bcd_version: u16::from_le_bytes([data[6], data[7]]),
387dc70c6aeSJoel Fernandes             header_size: data[8],
388dc70c6aeSJoel Fernandes             token_size: data[9],
389dc70c6aeSJoel Fernandes             token_entries: data[10],
390dc70c6aeSJoel Fernandes             checksum: data[11],
391dc70c6aeSJoel Fernandes         })
392dc70c6aeSJoel Fernandes     }
393dc70c6aeSJoel Fernandes }
394dc70c6aeSJoel Fernandes 
395dc70c6aeSJoel Fernandes /// BIT Token Entry: Records in the BIT table followed by the BIT header.
396dc70c6aeSJoel Fernandes #[derive(Debug, Clone, Copy)]
397dc70c6aeSJoel Fernandes #[expect(dead_code)]
398dc70c6aeSJoel Fernandes struct BitToken {
399dc70c6aeSJoel Fernandes     /// 00h: Token identifier
400dc70c6aeSJoel Fernandes     id: u8,
401dc70c6aeSJoel Fernandes     /// 01h: Version of the token data
402dc70c6aeSJoel Fernandes     data_version: u8,
403dc70c6aeSJoel Fernandes     /// 02h: Size of token data in bytes
404dc70c6aeSJoel Fernandes     data_size: u16,
405dc70c6aeSJoel Fernandes     /// 04h: Offset to the token data
406dc70c6aeSJoel Fernandes     data_offset: u16,
407dc70c6aeSJoel Fernandes }
408dc70c6aeSJoel Fernandes 
409dc70c6aeSJoel Fernandes // Define the token ID for the Falcon data
410dc70c6aeSJoel Fernandes const BIT_TOKEN_ID_FALCON_DATA: u8 = 0x70;
411dc70c6aeSJoel Fernandes 
412dc70c6aeSJoel Fernandes impl BitToken {
413dc70c6aeSJoel Fernandes     /// Find a BIT token entry by BIT ID in a PciAtBiosImage
414dc70c6aeSJoel Fernandes     fn from_id(image: &PciAtBiosImage, token_id: u8) -> Result<Self> {
415dc70c6aeSJoel Fernandes         let header = &image.bit_header;
416dc70c6aeSJoel Fernandes 
417dc70c6aeSJoel Fernandes         // Offset to the first token entry
418dc70c6aeSJoel Fernandes         let tokens_start = image.bit_offset + header.header_size as usize;
419dc70c6aeSJoel Fernandes 
420dc70c6aeSJoel Fernandes         for i in 0..header.token_entries as usize {
421dc70c6aeSJoel Fernandes             let entry_offset = tokens_start + (i * header.token_size as usize);
422dc70c6aeSJoel Fernandes 
423dc70c6aeSJoel Fernandes             // Make sure we don't go out of bounds
424dc70c6aeSJoel Fernandes             if entry_offset + header.token_size as usize > image.base.data.len() {
425dc70c6aeSJoel Fernandes                 return Err(EINVAL);
426dc70c6aeSJoel Fernandes             }
427dc70c6aeSJoel Fernandes 
428dc70c6aeSJoel Fernandes             // Check if this token has the requested ID
429dc70c6aeSJoel Fernandes             if image.base.data[entry_offset] == token_id {
430dc70c6aeSJoel Fernandes                 return Ok(BitToken {
431dc70c6aeSJoel Fernandes                     id: image.base.data[entry_offset],
432dc70c6aeSJoel Fernandes                     data_version: image.base.data[entry_offset + 1],
433dc70c6aeSJoel Fernandes                     data_size: u16::from_le_bytes([
434dc70c6aeSJoel Fernandes                         image.base.data[entry_offset + 2],
435dc70c6aeSJoel Fernandes                         image.base.data[entry_offset + 3],
436dc70c6aeSJoel Fernandes                     ]),
437dc70c6aeSJoel Fernandes                     data_offset: u16::from_le_bytes([
438dc70c6aeSJoel Fernandes                         image.base.data[entry_offset + 4],
439dc70c6aeSJoel Fernandes                         image.base.data[entry_offset + 5],
440dc70c6aeSJoel Fernandes                     ]),
441dc70c6aeSJoel Fernandes                 });
442dc70c6aeSJoel Fernandes             }
443dc70c6aeSJoel Fernandes         }
444dc70c6aeSJoel Fernandes 
445dc70c6aeSJoel Fernandes         // Token not found
446dc70c6aeSJoel Fernandes         Err(ENOENT)
447dc70c6aeSJoel Fernandes     }
448dc70c6aeSJoel Fernandes }
449dc70c6aeSJoel Fernandes 
4506fda04e7SJoel Fernandes /// PCI ROM Expansion Header as defined in PCI Firmware Specification.
4516fda04e7SJoel Fernandes ///
4526fda04e7SJoel Fernandes /// This is header is at the beginning of every image in the set of images in the ROM. It contains
4536fda04e7SJoel Fernandes /// a pointer to the PCI Data Structure which describes the image. For "NBSI" images (NoteBook
4546fda04e7SJoel Fernandes /// System Information), the ROM header deviates from the standard and contains an offset to the
4556fda04e7SJoel Fernandes /// NBSI image however we do not yet parse that in this module and keep it for future reference.
4566fda04e7SJoel Fernandes #[derive(Debug, Clone, Copy)]
4576fda04e7SJoel Fernandes #[expect(dead_code)]
4586fda04e7SJoel Fernandes struct PciRomHeader {
4596fda04e7SJoel Fernandes     /// 00h: Signature (0xAA55)
4606fda04e7SJoel Fernandes     signature: u16,
4616fda04e7SJoel Fernandes     /// 02h: Reserved bytes for processor architecture unique data (20 bytes)
4626fda04e7SJoel Fernandes     reserved: [u8; 20],
4636fda04e7SJoel Fernandes     /// 16h: NBSI Data Offset (NBSI-specific, offset from header to NBSI image)
4646fda04e7SJoel Fernandes     nbsi_data_offset: Option<u16>,
4656fda04e7SJoel Fernandes     /// 18h: Pointer to PCI Data Structure (offset from start of ROM image)
4666fda04e7SJoel Fernandes     pci_data_struct_offset: u16,
4676fda04e7SJoel Fernandes     /// 1Ah: Size of block (this is NBSI-specific)
4686fda04e7SJoel Fernandes     size_of_block: Option<u32>,
4696fda04e7SJoel Fernandes }
4706fda04e7SJoel Fernandes 
4716fda04e7SJoel Fernandes impl PciRomHeader {
4726fda04e7SJoel Fernandes     fn new(pdev: &pci::Device, data: &[u8]) -> Result<Self> {
4736fda04e7SJoel Fernandes         if data.len() < 26 {
4746fda04e7SJoel Fernandes             // Need at least 26 bytes to read pciDataStrucPtr and sizeOfBlock.
4756fda04e7SJoel Fernandes             return Err(EINVAL);
4766fda04e7SJoel Fernandes         }
4776fda04e7SJoel Fernandes 
4786fda04e7SJoel Fernandes         let signature = u16::from_le_bytes([data[0], data[1]]);
4796fda04e7SJoel Fernandes 
4806fda04e7SJoel Fernandes         // Check for valid ROM signatures.
4816fda04e7SJoel Fernandes         match signature {
4826fda04e7SJoel Fernandes             0xAA55 | 0xBB77 | 0x4E56 => {}
4836fda04e7SJoel Fernandes             _ => {
4846fda04e7SJoel Fernandes                 dev_err!(pdev.as_ref(), "ROM signature unknown {:#x}\n", signature);
4856fda04e7SJoel Fernandes                 return Err(EINVAL);
4866fda04e7SJoel Fernandes             }
4876fda04e7SJoel Fernandes         }
4886fda04e7SJoel Fernandes 
4896fda04e7SJoel Fernandes         // Read the pointer to the PCI Data Structure at offset 0x18.
4906fda04e7SJoel Fernandes         let pci_data_struct_ptr = u16::from_le_bytes([data[24], data[25]]);
4916fda04e7SJoel Fernandes 
4926fda04e7SJoel Fernandes         // Try to read optional fields if enough data.
4936fda04e7SJoel Fernandes         let mut size_of_block = None;
4946fda04e7SJoel Fernandes         let mut nbsi_data_offset = None;
4956fda04e7SJoel Fernandes 
4966fda04e7SJoel Fernandes         if data.len() >= 30 {
4976fda04e7SJoel Fernandes             // Read size_of_block at offset 0x1A.
4986fda04e7SJoel Fernandes             size_of_block = Some(
4996fda04e7SJoel Fernandes                 (data[29] as u32) << 24
5006fda04e7SJoel Fernandes                     | (data[28] as u32) << 16
5016fda04e7SJoel Fernandes                     | (data[27] as u32) << 8
5026fda04e7SJoel Fernandes                     | (data[26] as u32),
5036fda04e7SJoel Fernandes             );
5046fda04e7SJoel Fernandes         }
5056fda04e7SJoel Fernandes 
5066fda04e7SJoel Fernandes         // For NBSI images, try to read the nbsiDataOffset at offset 0x16.
5076fda04e7SJoel Fernandes         if data.len() >= 24 {
5086fda04e7SJoel Fernandes             nbsi_data_offset = Some(u16::from_le_bytes([data[22], data[23]]));
5096fda04e7SJoel Fernandes         }
5106fda04e7SJoel Fernandes 
5116fda04e7SJoel Fernandes         Ok(PciRomHeader {
5126fda04e7SJoel Fernandes             signature,
5136fda04e7SJoel Fernandes             reserved: [0u8; 20],
5146fda04e7SJoel Fernandes             pci_data_struct_offset: pci_data_struct_ptr,
5156fda04e7SJoel Fernandes             size_of_block,
5166fda04e7SJoel Fernandes             nbsi_data_offset,
5176fda04e7SJoel Fernandes         })
5186fda04e7SJoel Fernandes     }
5196fda04e7SJoel Fernandes }
5206fda04e7SJoel Fernandes 
5216fda04e7SJoel Fernandes /// NVIDIA PCI Data Extension Structure.
5226fda04e7SJoel Fernandes ///
5236fda04e7SJoel Fernandes /// This is similar to the PCI Data Structure, but is Nvidia-specific and is placed right after the
5246fda04e7SJoel Fernandes /// PCI Data Structure. It contains some fields that are redundant with the PCI Data Structure, but
5256fda04e7SJoel Fernandes /// are needed for traversing the BIOS images. It is expected to be present in all BIOS images
5266fda04e7SJoel Fernandes /// except for NBSI images.
5276fda04e7SJoel Fernandes #[derive(Debug, Clone)]
5286fda04e7SJoel Fernandes #[repr(C)]
5296fda04e7SJoel Fernandes struct NpdeStruct {
5306fda04e7SJoel Fernandes     /// 00h: Signature ("NPDE")
5316fda04e7SJoel Fernandes     signature: [u8; 4],
5326fda04e7SJoel Fernandes     /// 04h: NVIDIA PCI Data Extension Revision
5336fda04e7SJoel Fernandes     npci_data_ext_rev: u16,
5346fda04e7SJoel Fernandes     /// 06h: NVIDIA PCI Data Extension Length
5356fda04e7SJoel Fernandes     npci_data_ext_len: u16,
5366fda04e7SJoel Fernandes     /// 08h: Sub-image Length (in 512-byte units)
5376fda04e7SJoel Fernandes     subimage_len: u16,
5386fda04e7SJoel Fernandes     /// 0Ah: Last image indicator flag
5396fda04e7SJoel Fernandes     last_image: u8,
5406fda04e7SJoel Fernandes }
5416fda04e7SJoel Fernandes 
5426fda04e7SJoel Fernandes impl NpdeStruct {
5436fda04e7SJoel Fernandes     fn new(pdev: &pci::Device, data: &[u8]) -> Option<Self> {
5446fda04e7SJoel Fernandes         if data.len() < core::mem::size_of::<Self>() {
5456fda04e7SJoel Fernandes             dev_dbg!(pdev.as_ref(), "Not enough data for NpdeStruct\n");
5466fda04e7SJoel Fernandes             return None;
5476fda04e7SJoel Fernandes         }
5486fda04e7SJoel Fernandes 
5496fda04e7SJoel Fernandes         let mut signature = [0u8; 4];
5506fda04e7SJoel Fernandes         signature.copy_from_slice(&data[0..4]);
5516fda04e7SJoel Fernandes 
5526fda04e7SJoel Fernandes         // Signature should be "NPDE" (0x4544504E).
5536fda04e7SJoel Fernandes         if &signature != b"NPDE" {
5546fda04e7SJoel Fernandes             dev_dbg!(
5556fda04e7SJoel Fernandes                 pdev.as_ref(),
5566fda04e7SJoel Fernandes                 "Invalid signature for NpdeStruct: {:?}\n",
5576fda04e7SJoel Fernandes                 signature
5586fda04e7SJoel Fernandes             );
5596fda04e7SJoel Fernandes             return None;
5606fda04e7SJoel Fernandes         }
5616fda04e7SJoel Fernandes 
5626fda04e7SJoel Fernandes         let subimage_len = u16::from_le_bytes([data[8], data[9]]);
5636fda04e7SJoel Fernandes         if subimage_len == 0 {
5646fda04e7SJoel Fernandes             dev_dbg!(pdev.as_ref(), "Invalid subimage length: 0\n");
5656fda04e7SJoel Fernandes             return None;
5666fda04e7SJoel Fernandes         }
5676fda04e7SJoel Fernandes 
5686fda04e7SJoel Fernandes         Some(NpdeStruct {
5696fda04e7SJoel Fernandes             signature,
5706fda04e7SJoel Fernandes             npci_data_ext_rev: u16::from_le_bytes([data[4], data[5]]),
5716fda04e7SJoel Fernandes             npci_data_ext_len: u16::from_le_bytes([data[6], data[7]]),
5726fda04e7SJoel Fernandes             subimage_len,
5736fda04e7SJoel Fernandes             last_image: data[10],
5746fda04e7SJoel Fernandes         })
5756fda04e7SJoel Fernandes     }
5766fda04e7SJoel Fernandes 
5776fda04e7SJoel Fernandes     /// Check if this is the last image in the ROM.
5786fda04e7SJoel Fernandes     fn is_last(&self) -> bool {
5796fda04e7SJoel Fernandes         self.last_image & LAST_IMAGE_BIT_MASK != 0
5806fda04e7SJoel Fernandes     }
5816fda04e7SJoel Fernandes 
5826fda04e7SJoel Fernandes     /// Calculate image size in bytes from 512-byte blocks.
5836fda04e7SJoel Fernandes     fn image_size_bytes(&self) -> usize {
5846fda04e7SJoel Fernandes         self.subimage_len as usize * 512
5856fda04e7SJoel Fernandes     }
5866fda04e7SJoel Fernandes 
5876fda04e7SJoel Fernandes     /// Try to find NPDE in the data, the NPDE is right after the PCIR.
5886fda04e7SJoel Fernandes     fn find_in_data(
5896fda04e7SJoel Fernandes         pdev: &pci::Device,
5906fda04e7SJoel Fernandes         data: &[u8],
5916fda04e7SJoel Fernandes         rom_header: &PciRomHeader,
5926fda04e7SJoel Fernandes         pcir: &PcirStruct,
5936fda04e7SJoel Fernandes     ) -> Option<Self> {
5946fda04e7SJoel Fernandes         // Calculate the offset where NPDE might be located
5956fda04e7SJoel Fernandes         // NPDE should be right after the PCIR structure, aligned to 16 bytes
5966fda04e7SJoel Fernandes         let pcir_offset = rom_header.pci_data_struct_offset as usize;
5976fda04e7SJoel Fernandes         let npde_start = (pcir_offset + pcir.pci_data_struct_len as usize + 0x0F) & !0x0F;
5986fda04e7SJoel Fernandes 
5996fda04e7SJoel Fernandes         // Check if we have enough data
6006fda04e7SJoel Fernandes         if npde_start + core::mem::size_of::<Self>() > data.len() {
6016fda04e7SJoel Fernandes             dev_dbg!(pdev.as_ref(), "Not enough data for NPDE\n");
6026fda04e7SJoel Fernandes             return None;
6036fda04e7SJoel Fernandes         }
6046fda04e7SJoel Fernandes 
6056fda04e7SJoel Fernandes         // Try to create NPDE from the data
6066fda04e7SJoel Fernandes         NpdeStruct::new(pdev, &data[npde_start..])
6076fda04e7SJoel Fernandes     }
6086fda04e7SJoel Fernandes }
6096fda04e7SJoel Fernandes 
6106fda04e7SJoel Fernandes // Use a macro to implement BiosImage enum and methods. This avoids having to
6116fda04e7SJoel Fernandes // repeat each enum type when implementing functions like base() in BiosImage.
6126fda04e7SJoel Fernandes macro_rules! bios_image {
6136fda04e7SJoel Fernandes     (
6146fda04e7SJoel Fernandes         $($variant:ident: $class:ident),* $(,)?
6156fda04e7SJoel Fernandes     ) => {
6166fda04e7SJoel Fernandes         // BiosImage enum with variants for each image type
6176fda04e7SJoel Fernandes         enum BiosImage {
6186fda04e7SJoel Fernandes             $($variant($class)),*
6196fda04e7SJoel Fernandes         }
6206fda04e7SJoel Fernandes 
6216fda04e7SJoel Fernandes         impl BiosImage {
6226fda04e7SJoel Fernandes             /// Get a reference to the common BIOS image data regardless of type
6236fda04e7SJoel Fernandes             fn base(&self) -> &BiosImageBase {
6246fda04e7SJoel Fernandes                 match self {
6256fda04e7SJoel Fernandes                     $(Self::$variant(img) => &img.base),*
6266fda04e7SJoel Fernandes                 }
6276fda04e7SJoel Fernandes             }
6286fda04e7SJoel Fernandes 
6296fda04e7SJoel Fernandes             /// Returns a string representing the type of BIOS image
6306fda04e7SJoel Fernandes             fn image_type_str(&self) -> &'static str {
6316fda04e7SJoel Fernandes                 match self {
6326fda04e7SJoel Fernandes                     $(Self::$variant(_) => stringify!($variant)),*
6336fda04e7SJoel Fernandes                 }
6346fda04e7SJoel Fernandes             }
6356fda04e7SJoel Fernandes         }
6366fda04e7SJoel Fernandes     }
6376fda04e7SJoel Fernandes }
6386fda04e7SJoel Fernandes 
6396fda04e7SJoel Fernandes impl BiosImage {
6406fda04e7SJoel Fernandes     /// Check if this is the last image.
6416fda04e7SJoel Fernandes     fn is_last(&self) -> bool {
6426fda04e7SJoel Fernandes         let base = self.base();
6436fda04e7SJoel Fernandes 
6446fda04e7SJoel Fernandes         // For NBSI images (type == 0x70), return true as they're
6456fda04e7SJoel Fernandes         // considered the last image
6466fda04e7SJoel Fernandes         if matches!(self, Self::Nbsi(_)) {
6476fda04e7SJoel Fernandes             return true;
6486fda04e7SJoel Fernandes         }
6496fda04e7SJoel Fernandes 
6506fda04e7SJoel Fernandes         // For other image types, check the NPDE first if available
6516fda04e7SJoel Fernandes         if let Some(ref npde) = base.npde {
6526fda04e7SJoel Fernandes             return npde.is_last();
6536fda04e7SJoel Fernandes         }
6546fda04e7SJoel Fernandes 
6556fda04e7SJoel Fernandes         // Otherwise, fall back to checking the PCIR last_image flag
6566fda04e7SJoel Fernandes         base.pcir.is_last()
6576fda04e7SJoel Fernandes     }
6586fda04e7SJoel Fernandes 
6596fda04e7SJoel Fernandes     /// Get the image size in bytes.
6606fda04e7SJoel Fernandes     fn image_size_bytes(&self) -> usize {
6616fda04e7SJoel Fernandes         let base = self.base();
6626fda04e7SJoel Fernandes 
6636fda04e7SJoel Fernandes         // Prefer NPDE image size if available
6646fda04e7SJoel Fernandes         if let Some(ref npde) = base.npde {
6656fda04e7SJoel Fernandes             return npde.image_size_bytes();
6666fda04e7SJoel Fernandes         }
6676fda04e7SJoel Fernandes 
6686fda04e7SJoel Fernandes         // Otherwise, fall back to the PCIR image size
6696fda04e7SJoel Fernandes         base.pcir.image_size_bytes()
6706fda04e7SJoel Fernandes     }
6716fda04e7SJoel Fernandes 
6726fda04e7SJoel Fernandes     /// Create a [`BiosImageBase`] from a byte slice and convert it to a [`BiosImage`] which
6736fda04e7SJoel Fernandes     /// triggers the constructor of the specific BiosImage enum variant.
6746fda04e7SJoel Fernandes     fn new(pdev: &pci::Device, data: &[u8]) -> Result<Self> {
6756fda04e7SJoel Fernandes         let base = BiosImageBase::new(pdev, data)?;
6766fda04e7SJoel Fernandes         let image = base.into_image().inspect_err(|e| {
6776fda04e7SJoel Fernandes             dev_err!(pdev.as_ref(), "Failed to create BiosImage: {:?}\n", e);
6786fda04e7SJoel Fernandes         })?;
6796fda04e7SJoel Fernandes 
6806fda04e7SJoel Fernandes         Ok(image)
6816fda04e7SJoel Fernandes     }
6826fda04e7SJoel Fernandes }
6836fda04e7SJoel Fernandes 
6846fda04e7SJoel Fernandes bios_image! {
6856fda04e7SJoel Fernandes     PciAt: PciAtBiosImage,   // PCI-AT compatible BIOS image
6866fda04e7SJoel Fernandes     Efi: EfiBiosImage,       // EFI (Extensible Firmware Interface)
6876fda04e7SJoel Fernandes     Nbsi: NbsiBiosImage,     // NBSI (Nvidia Bios System Interface)
688*47c4846eSJoel Fernandes     FwSec: FwSecBiosBuilder, // FWSEC (Firmware Security)
6896fda04e7SJoel Fernandes }
6906fda04e7SJoel Fernandes 
691dc70c6aeSJoel Fernandes /// The PciAt BIOS image is typically the first BIOS image type found in the BIOS image chain.
692dc70c6aeSJoel Fernandes ///
693dc70c6aeSJoel Fernandes /// It contains the BIT header and the BIT tokens.
6946fda04e7SJoel Fernandes struct PciAtBiosImage {
6956fda04e7SJoel Fernandes     base: BiosImageBase,
696dc70c6aeSJoel Fernandes     bit_header: BitHeader,
697dc70c6aeSJoel Fernandes     bit_offset: usize,
6986fda04e7SJoel Fernandes }
6996fda04e7SJoel Fernandes 
7006fda04e7SJoel Fernandes struct EfiBiosImage {
7016fda04e7SJoel Fernandes     base: BiosImageBase,
7026fda04e7SJoel Fernandes     // EFI-specific fields can be added here in the future.
7036fda04e7SJoel Fernandes }
7046fda04e7SJoel Fernandes 
7056fda04e7SJoel Fernandes struct NbsiBiosImage {
7066fda04e7SJoel Fernandes     base: BiosImageBase,
7076fda04e7SJoel Fernandes     // NBSI-specific fields can be added here in the future.
7086fda04e7SJoel Fernandes }
7096fda04e7SJoel Fernandes 
710*47c4846eSJoel Fernandes struct FwSecBiosBuilder {
7116fda04e7SJoel Fernandes     base: BiosImageBase,
712*47c4846eSJoel Fernandes     /// These are temporary fields that are used during the construction of the
713*47c4846eSJoel Fernandes     /// [`FwSecBiosBuilder`].
714*47c4846eSJoel Fernandes     ///
715*47c4846eSJoel Fernandes     /// Once FwSecBiosBuilder is constructed, the `falcon_ucode_offset` will be copied into a new
716*47c4846eSJoel Fernandes     /// [`FwSecBiosImage`].
717*47c4846eSJoel Fernandes     ///
718*47c4846eSJoel Fernandes     /// The offset of the Falcon data from the start of Fwsec image.
719*47c4846eSJoel Fernandes     falcon_data_offset: Option<usize>,
720*47c4846eSJoel Fernandes     /// The [`PmuLookupTable`] starts at the offset of the falcon data pointer.
721*47c4846eSJoel Fernandes     pmu_lookup_table: Option<PmuLookupTable>,
722*47c4846eSJoel Fernandes     /// The offset of the Falcon ucode.
723*47c4846eSJoel Fernandes     falcon_ucode_offset: Option<usize>,
724*47c4846eSJoel Fernandes }
725*47c4846eSJoel Fernandes 
726*47c4846eSJoel Fernandes /// The [`FwSecBiosImage`] structure contains the PMU table and the Falcon Ucode.
727*47c4846eSJoel Fernandes ///
728*47c4846eSJoel Fernandes /// The PMU table contains voltage/frequency tables as well as a pointer to the Falcon Ucode.
729*47c4846eSJoel Fernandes pub(crate) struct FwSecBiosImage {
730*47c4846eSJoel Fernandes     base: BiosImageBase,
731*47c4846eSJoel Fernandes     /// The offset of the Falcon ucode.
732*47c4846eSJoel Fernandes     falcon_ucode_offset: usize,
7336fda04e7SJoel Fernandes }
7346fda04e7SJoel Fernandes 
7356fda04e7SJoel Fernandes // Convert from BiosImageBase to BiosImage
7366fda04e7SJoel Fernandes impl TryFrom<BiosImageBase> for BiosImage {
7376fda04e7SJoel Fernandes     type Error = Error;
7386fda04e7SJoel Fernandes 
7396fda04e7SJoel Fernandes     fn try_from(base: BiosImageBase) -> Result<Self> {
7406fda04e7SJoel Fernandes         match base.pcir.code_type {
741dc70c6aeSJoel Fernandes             0x00 => Ok(BiosImage::PciAt(base.try_into()?)),
7426fda04e7SJoel Fernandes             0x03 => Ok(BiosImage::Efi(EfiBiosImage { base })),
7436fda04e7SJoel Fernandes             0x70 => Ok(BiosImage::Nbsi(NbsiBiosImage { base })),
744*47c4846eSJoel Fernandes             0xE0 => Ok(BiosImage::FwSec(FwSecBiosBuilder {
745*47c4846eSJoel Fernandes                 base,
746*47c4846eSJoel Fernandes                 falcon_data_offset: None,
747*47c4846eSJoel Fernandes                 pmu_lookup_table: None,
748*47c4846eSJoel Fernandes                 falcon_ucode_offset: None,
749*47c4846eSJoel Fernandes             })),
7506fda04e7SJoel Fernandes             _ => Err(EINVAL),
7516fda04e7SJoel Fernandes         }
7526fda04e7SJoel Fernandes     }
7536fda04e7SJoel Fernandes }
7546fda04e7SJoel Fernandes 
7556fda04e7SJoel Fernandes /// BIOS Image structure containing various headers and reference fields to all BIOS images.
7566fda04e7SJoel Fernandes ///
7576fda04e7SJoel Fernandes /// Each BiosImage type has a BiosImageBase type along with other image-specific fields. Note that
7586fda04e7SJoel Fernandes /// Rust favors composition of types over inheritance.
7596fda04e7SJoel Fernandes #[derive(Debug)]
7606fda04e7SJoel Fernandes #[expect(dead_code)]
7616fda04e7SJoel Fernandes struct BiosImageBase {
7626fda04e7SJoel Fernandes     /// PCI ROM Expansion Header
7636fda04e7SJoel Fernandes     rom_header: PciRomHeader,
7646fda04e7SJoel Fernandes     /// PCI Data Structure
7656fda04e7SJoel Fernandes     pcir: PcirStruct,
7666fda04e7SJoel Fernandes     /// NVIDIA PCI Data Extension (optional)
7676fda04e7SJoel Fernandes     npde: Option<NpdeStruct>,
7686fda04e7SJoel Fernandes     /// Image data (includes ROM header and PCIR)
7696fda04e7SJoel Fernandes     data: KVec<u8>,
7706fda04e7SJoel Fernandes }
7716fda04e7SJoel Fernandes 
7726fda04e7SJoel Fernandes impl BiosImageBase {
7736fda04e7SJoel Fernandes     fn into_image(self) -> Result<BiosImage> {
7746fda04e7SJoel Fernandes         BiosImage::try_from(self)
7756fda04e7SJoel Fernandes     }
7766fda04e7SJoel Fernandes 
7776fda04e7SJoel Fernandes     /// Creates a new BiosImageBase from raw byte data.
7786fda04e7SJoel Fernandes     fn new(pdev: &pci::Device, data: &[u8]) -> Result<Self> {
7796fda04e7SJoel Fernandes         // Ensure we have enough data for the ROM header.
7806fda04e7SJoel Fernandes         if data.len() < 26 {
7816fda04e7SJoel Fernandes             dev_err!(pdev.as_ref(), "Not enough data for ROM header\n");
7826fda04e7SJoel Fernandes             return Err(EINVAL);
7836fda04e7SJoel Fernandes         }
7846fda04e7SJoel Fernandes 
7856fda04e7SJoel Fernandes         // Parse the ROM header.
7866fda04e7SJoel Fernandes         let rom_header = PciRomHeader::new(pdev, &data[0..26])
7876fda04e7SJoel Fernandes             .inspect_err(|e| dev_err!(pdev.as_ref(), "Failed to create PciRomHeader: {:?}\n", e))?;
7886fda04e7SJoel Fernandes 
7896fda04e7SJoel Fernandes         // Get the PCI Data Structure using the pointer from the ROM header.
7906fda04e7SJoel Fernandes         let pcir_offset = rom_header.pci_data_struct_offset as usize;
7916fda04e7SJoel Fernandes         let pcir_data = data
7926fda04e7SJoel Fernandes             .get(pcir_offset..pcir_offset + core::mem::size_of::<PcirStruct>())
7936fda04e7SJoel Fernandes             .ok_or(EINVAL)
7946fda04e7SJoel Fernandes             .inspect_err(|_| {
7956fda04e7SJoel Fernandes                 dev_err!(
7966fda04e7SJoel Fernandes                     pdev.as_ref(),
7976fda04e7SJoel Fernandes                     "PCIR offset {:#x} out of bounds (data length: {})\n",
7986fda04e7SJoel Fernandes                     pcir_offset,
7996fda04e7SJoel Fernandes                     data.len()
8006fda04e7SJoel Fernandes                 );
8016fda04e7SJoel Fernandes                 dev_err!(
8026fda04e7SJoel Fernandes                     pdev.as_ref(),
8036fda04e7SJoel Fernandes                     "Consider reading more data for construction of BiosImage\n"
8046fda04e7SJoel Fernandes                 );
8056fda04e7SJoel Fernandes             })?;
8066fda04e7SJoel Fernandes 
8076fda04e7SJoel Fernandes         let pcir = PcirStruct::new(pdev, pcir_data)
8086fda04e7SJoel Fernandes             .inspect_err(|e| dev_err!(pdev.as_ref(), "Failed to create PcirStruct: {:?}\n", e))?;
8096fda04e7SJoel Fernandes 
8106fda04e7SJoel Fernandes         // Look for NPDE structure if this is not an NBSI image (type != 0x70).
8116fda04e7SJoel Fernandes         let npde = NpdeStruct::find_in_data(pdev, data, &rom_header, &pcir);
8126fda04e7SJoel Fernandes 
8136fda04e7SJoel Fernandes         // Create a copy of the data.
8146fda04e7SJoel Fernandes         let mut data_copy = KVec::new();
8156fda04e7SJoel Fernandes         data_copy.extend_from_slice(data, GFP_KERNEL)?;
8166fda04e7SJoel Fernandes 
8176fda04e7SJoel Fernandes         Ok(BiosImageBase {
8186fda04e7SJoel Fernandes             rom_header,
8196fda04e7SJoel Fernandes             pcir,
8206fda04e7SJoel Fernandes             npde,
8216fda04e7SJoel Fernandes             data: data_copy,
8226fda04e7SJoel Fernandes         })
8236fda04e7SJoel Fernandes     }
8246fda04e7SJoel Fernandes }
825dc70c6aeSJoel Fernandes 
826dc70c6aeSJoel Fernandes impl PciAtBiosImage {
827dc70c6aeSJoel Fernandes     /// Find a byte pattern in a slice.
828dc70c6aeSJoel Fernandes     fn find_byte_pattern(haystack: &[u8], needle: &[u8]) -> Result<usize> {
829dc70c6aeSJoel Fernandes         haystack
830dc70c6aeSJoel Fernandes             .windows(needle.len())
831dc70c6aeSJoel Fernandes             .position(|window| window == needle)
832dc70c6aeSJoel Fernandes             .ok_or(EINVAL)
833dc70c6aeSJoel Fernandes     }
834dc70c6aeSJoel Fernandes 
835dc70c6aeSJoel Fernandes     /// Find the BIT header in the [`PciAtBiosImage`].
836dc70c6aeSJoel Fernandes     fn find_bit_header(data: &[u8]) -> Result<(BitHeader, usize)> {
837dc70c6aeSJoel Fernandes         let bit_pattern = [0xff, 0xb8, b'B', b'I', b'T', 0x00];
838dc70c6aeSJoel Fernandes         let bit_offset = Self::find_byte_pattern(data, &bit_pattern)?;
839dc70c6aeSJoel Fernandes         let bit_header = BitHeader::new(&data[bit_offset..])?;
840dc70c6aeSJoel Fernandes 
841dc70c6aeSJoel Fernandes         Ok((bit_header, bit_offset))
842dc70c6aeSJoel Fernandes     }
843dc70c6aeSJoel Fernandes 
844dc70c6aeSJoel Fernandes     /// Get a BIT token entry from the BIT table in the [`PciAtBiosImage`]
845dc70c6aeSJoel Fernandes     fn get_bit_token(&self, token_id: u8) -> Result<BitToken> {
846dc70c6aeSJoel Fernandes         BitToken::from_id(self, token_id)
847dc70c6aeSJoel Fernandes     }
848dc70c6aeSJoel Fernandes 
849dc70c6aeSJoel Fernandes     /// Find the Falcon data pointer structure in the [`PciAtBiosImage`].
850dc70c6aeSJoel Fernandes     ///
851dc70c6aeSJoel Fernandes     /// This is just a 4 byte structure that contains a pointer to the Falcon data in the FWSEC
852dc70c6aeSJoel Fernandes     /// image.
853dc70c6aeSJoel Fernandes     fn falcon_data_ptr(&self, pdev: &pci::Device) -> Result<u32> {
854dc70c6aeSJoel Fernandes         let token = self.get_bit_token(BIT_TOKEN_ID_FALCON_DATA)?;
855dc70c6aeSJoel Fernandes 
856dc70c6aeSJoel Fernandes         // Make sure we don't go out of bounds
857dc70c6aeSJoel Fernandes         if token.data_offset as usize + 4 > self.base.data.len() {
858dc70c6aeSJoel Fernandes             return Err(EINVAL);
859dc70c6aeSJoel Fernandes         }
860dc70c6aeSJoel Fernandes 
861dc70c6aeSJoel Fernandes         // read the 4 bytes at the offset specified in the token
862dc70c6aeSJoel Fernandes         let offset = token.data_offset as usize;
863dc70c6aeSJoel Fernandes         let bytes: [u8; 4] = self.base.data[offset..offset + 4].try_into().map_err(|_| {
864dc70c6aeSJoel Fernandes             dev_err!(pdev.as_ref(), "Failed to convert data slice to array");
865dc70c6aeSJoel Fernandes             EINVAL
866dc70c6aeSJoel Fernandes         })?;
867dc70c6aeSJoel Fernandes 
868dc70c6aeSJoel Fernandes         let data_ptr = u32::from_le_bytes(bytes);
869dc70c6aeSJoel Fernandes 
870dc70c6aeSJoel Fernandes         if (data_ptr as usize) < self.base.data.len() {
871dc70c6aeSJoel Fernandes             dev_err!(pdev.as_ref(), "Falcon data pointer out of bounds\n");
872dc70c6aeSJoel Fernandes             return Err(EINVAL);
873dc70c6aeSJoel Fernandes         }
874dc70c6aeSJoel Fernandes 
875dc70c6aeSJoel Fernandes         Ok(data_ptr)
876dc70c6aeSJoel Fernandes     }
877dc70c6aeSJoel Fernandes }
878dc70c6aeSJoel Fernandes 
879dc70c6aeSJoel Fernandes impl TryFrom<BiosImageBase> for PciAtBiosImage {
880dc70c6aeSJoel Fernandes     type Error = Error;
881dc70c6aeSJoel Fernandes 
882dc70c6aeSJoel Fernandes     fn try_from(base: BiosImageBase) -> Result<Self> {
883dc70c6aeSJoel Fernandes         let data_slice = &base.data;
884dc70c6aeSJoel Fernandes         let (bit_header, bit_offset) = PciAtBiosImage::find_bit_header(data_slice)?;
885dc70c6aeSJoel Fernandes 
886dc70c6aeSJoel Fernandes         Ok(PciAtBiosImage {
887dc70c6aeSJoel Fernandes             base,
888dc70c6aeSJoel Fernandes             bit_header,
889dc70c6aeSJoel Fernandes             bit_offset,
890dc70c6aeSJoel Fernandes         })
891dc70c6aeSJoel Fernandes     }
892dc70c6aeSJoel Fernandes }
893*47c4846eSJoel Fernandes 
894*47c4846eSJoel Fernandes /// The [`PmuLookupTableEntry`] structure is a single entry in the [`PmuLookupTable`].
895*47c4846eSJoel Fernandes ///
896*47c4846eSJoel Fernandes /// See the [`PmuLookupTable`] description for more information.
897*47c4846eSJoel Fernandes #[expect(dead_code)]
898*47c4846eSJoel Fernandes struct PmuLookupTableEntry {
899*47c4846eSJoel Fernandes     application_id: u8,
900*47c4846eSJoel Fernandes     target_id: u8,
901*47c4846eSJoel Fernandes     data: u32,
902*47c4846eSJoel Fernandes }
903*47c4846eSJoel Fernandes 
904*47c4846eSJoel Fernandes impl PmuLookupTableEntry {
905*47c4846eSJoel Fernandes     fn new(data: &[u8]) -> Result<Self> {
906*47c4846eSJoel Fernandes         if data.len() < 5 {
907*47c4846eSJoel Fernandes             return Err(EINVAL);
908*47c4846eSJoel Fernandes         }
909*47c4846eSJoel Fernandes 
910*47c4846eSJoel Fernandes         Ok(PmuLookupTableEntry {
911*47c4846eSJoel Fernandes             application_id: data[0],
912*47c4846eSJoel Fernandes             target_id: data[1],
913*47c4846eSJoel Fernandes             data: u32::from_le_bytes(data[2..6].try_into().map_err(|_| EINVAL)?),
914*47c4846eSJoel Fernandes         })
915*47c4846eSJoel Fernandes     }
916*47c4846eSJoel Fernandes }
917*47c4846eSJoel Fernandes 
918*47c4846eSJoel Fernandes /// The [`PmuLookupTableEntry`] structure is used to find the [`PmuLookupTableEntry`] for a given
919*47c4846eSJoel Fernandes /// application ID.
920*47c4846eSJoel Fernandes ///
921*47c4846eSJoel Fernandes /// The table of entries is pointed to by the falcon data pointer in the BIT table, and is used to
922*47c4846eSJoel Fernandes /// locate the Falcon Ucode.
923*47c4846eSJoel Fernandes #[expect(dead_code)]
924*47c4846eSJoel Fernandes struct PmuLookupTable {
925*47c4846eSJoel Fernandes     version: u8,
926*47c4846eSJoel Fernandes     header_len: u8,
927*47c4846eSJoel Fernandes     entry_len: u8,
928*47c4846eSJoel Fernandes     entry_count: u8,
929*47c4846eSJoel Fernandes     table_data: KVec<u8>,
930*47c4846eSJoel Fernandes }
931*47c4846eSJoel Fernandes 
932*47c4846eSJoel Fernandes impl PmuLookupTable {
933*47c4846eSJoel Fernandes     fn new(pdev: &pci::Device, data: &[u8]) -> Result<Self> {
934*47c4846eSJoel Fernandes         if data.len() < 4 {
935*47c4846eSJoel Fernandes             return Err(EINVAL);
936*47c4846eSJoel Fernandes         }
937*47c4846eSJoel Fernandes 
938*47c4846eSJoel Fernandes         let header_len = data[1] as usize;
939*47c4846eSJoel Fernandes         let entry_len = data[2] as usize;
940*47c4846eSJoel Fernandes         let entry_count = data[3] as usize;
941*47c4846eSJoel Fernandes 
942*47c4846eSJoel Fernandes         let required_bytes = header_len + (entry_count * entry_len);
943*47c4846eSJoel Fernandes 
944*47c4846eSJoel Fernandes         if data.len() < required_bytes {
945*47c4846eSJoel Fernandes             dev_err!(
946*47c4846eSJoel Fernandes                 pdev.as_ref(),
947*47c4846eSJoel Fernandes                 "PmuLookupTable data length less than required\n"
948*47c4846eSJoel Fernandes             );
949*47c4846eSJoel Fernandes             return Err(EINVAL);
950*47c4846eSJoel Fernandes         }
951*47c4846eSJoel Fernandes 
952*47c4846eSJoel Fernandes         // Create a copy of only the table data
953*47c4846eSJoel Fernandes         let table_data = {
954*47c4846eSJoel Fernandes             let mut ret = KVec::new();
955*47c4846eSJoel Fernandes             ret.extend_from_slice(&data[header_len..required_bytes], GFP_KERNEL)?;
956*47c4846eSJoel Fernandes             ret
957*47c4846eSJoel Fernandes         };
958*47c4846eSJoel Fernandes 
959*47c4846eSJoel Fernandes         // Debug logging of entries (dumps the table data to dmesg)
960*47c4846eSJoel Fernandes         for i in (header_len..required_bytes).step_by(entry_len) {
961*47c4846eSJoel Fernandes             dev_dbg!(
962*47c4846eSJoel Fernandes                 pdev.as_ref(),
963*47c4846eSJoel Fernandes                 "PMU entry: {:02x?}\n",
964*47c4846eSJoel Fernandes                 &data[i..][..entry_len]
965*47c4846eSJoel Fernandes             );
966*47c4846eSJoel Fernandes         }
967*47c4846eSJoel Fernandes 
968*47c4846eSJoel Fernandes         Ok(PmuLookupTable {
969*47c4846eSJoel Fernandes             version: data[0],
970*47c4846eSJoel Fernandes             header_len: header_len as u8,
971*47c4846eSJoel Fernandes             entry_len: entry_len as u8,
972*47c4846eSJoel Fernandes             entry_count: entry_count as u8,
973*47c4846eSJoel Fernandes             table_data,
974*47c4846eSJoel Fernandes         })
975*47c4846eSJoel Fernandes     }
976*47c4846eSJoel Fernandes 
977*47c4846eSJoel Fernandes     fn lookup_index(&self, idx: u8) -> Result<PmuLookupTableEntry> {
978*47c4846eSJoel Fernandes         if idx >= self.entry_count {
979*47c4846eSJoel Fernandes             return Err(EINVAL);
980*47c4846eSJoel Fernandes         }
981*47c4846eSJoel Fernandes 
982*47c4846eSJoel Fernandes         let index = (idx as usize) * self.entry_len as usize;
983*47c4846eSJoel Fernandes         PmuLookupTableEntry::new(&self.table_data[index..])
984*47c4846eSJoel Fernandes     }
985*47c4846eSJoel Fernandes 
986*47c4846eSJoel Fernandes     // find entry by type value
987*47c4846eSJoel Fernandes     fn find_entry_by_type(&self, entry_type: u8) -> Result<PmuLookupTableEntry> {
988*47c4846eSJoel Fernandes         for i in 0..self.entry_count {
989*47c4846eSJoel Fernandes             let entry = self.lookup_index(i)?;
990*47c4846eSJoel Fernandes             if entry.application_id == entry_type {
991*47c4846eSJoel Fernandes                 return Ok(entry);
992*47c4846eSJoel Fernandes             }
993*47c4846eSJoel Fernandes         }
994*47c4846eSJoel Fernandes 
995*47c4846eSJoel Fernandes         Err(EINVAL)
996*47c4846eSJoel Fernandes     }
997*47c4846eSJoel Fernandes }
998*47c4846eSJoel Fernandes 
999*47c4846eSJoel Fernandes impl FwSecBiosBuilder {
1000*47c4846eSJoel Fernandes     fn setup_falcon_data(
1001*47c4846eSJoel Fernandes         &mut self,
1002*47c4846eSJoel Fernandes         pdev: &pci::Device,
1003*47c4846eSJoel Fernandes         pci_at_image: &PciAtBiosImage,
1004*47c4846eSJoel Fernandes         first_fwsec: &FwSecBiosBuilder,
1005*47c4846eSJoel Fernandes     ) -> Result {
1006*47c4846eSJoel Fernandes         let mut offset = pci_at_image.falcon_data_ptr(pdev)? as usize;
1007*47c4846eSJoel Fernandes         let mut pmu_in_first_fwsec = false;
1008*47c4846eSJoel Fernandes 
1009*47c4846eSJoel Fernandes         // The falcon data pointer assumes that the PciAt and FWSEC images
1010*47c4846eSJoel Fernandes         // are contiguous in memory. However, testing shows the EFI image sits in
1011*47c4846eSJoel Fernandes         // between them. So calculate the offset from the end of the PciAt image
1012*47c4846eSJoel Fernandes         // rather than the start of it. Compensate.
1013*47c4846eSJoel Fernandes         offset -= pci_at_image.base.data.len();
1014*47c4846eSJoel Fernandes 
1015*47c4846eSJoel Fernandes         // The offset is now from the start of the first Fwsec image, however
1016*47c4846eSJoel Fernandes         // the offset points to a location in the second Fwsec image. Since
1017*47c4846eSJoel Fernandes         // the fwsec images are contiguous, subtract the length of the first Fwsec
1018*47c4846eSJoel Fernandes         // image from the offset to get the offset to the start of the second
1019*47c4846eSJoel Fernandes         // Fwsec image.
1020*47c4846eSJoel Fernandes         if offset < first_fwsec.base.data.len() {
1021*47c4846eSJoel Fernandes             pmu_in_first_fwsec = true;
1022*47c4846eSJoel Fernandes         } else {
1023*47c4846eSJoel Fernandes             offset -= first_fwsec.base.data.len();
1024*47c4846eSJoel Fernandes         }
1025*47c4846eSJoel Fernandes 
1026*47c4846eSJoel Fernandes         self.falcon_data_offset = Some(offset);
1027*47c4846eSJoel Fernandes 
1028*47c4846eSJoel Fernandes         if pmu_in_first_fwsec {
1029*47c4846eSJoel Fernandes             self.pmu_lookup_table =
1030*47c4846eSJoel Fernandes                 Some(PmuLookupTable::new(pdev, &first_fwsec.base.data[offset..])?);
1031*47c4846eSJoel Fernandes         } else {
1032*47c4846eSJoel Fernandes             self.pmu_lookup_table = Some(PmuLookupTable::new(pdev, &self.base.data[offset..])?);
1033*47c4846eSJoel Fernandes         }
1034*47c4846eSJoel Fernandes 
1035*47c4846eSJoel Fernandes         match self
1036*47c4846eSJoel Fernandes             .pmu_lookup_table
1037*47c4846eSJoel Fernandes             .as_ref()
1038*47c4846eSJoel Fernandes             .ok_or(EINVAL)?
1039*47c4846eSJoel Fernandes             .find_entry_by_type(FALCON_UCODE_ENTRY_APPID_FWSEC_PROD)
1040*47c4846eSJoel Fernandes         {
1041*47c4846eSJoel Fernandes             Ok(entry) => {
1042*47c4846eSJoel Fernandes                 let mut ucode_offset = entry.data as usize;
1043*47c4846eSJoel Fernandes                 ucode_offset -= pci_at_image.base.data.len();
1044*47c4846eSJoel Fernandes                 if ucode_offset < first_fwsec.base.data.len() {
1045*47c4846eSJoel Fernandes                     dev_err!(pdev.as_ref(), "Falcon Ucode offset not in second Fwsec.\n");
1046*47c4846eSJoel Fernandes                     return Err(EINVAL);
1047*47c4846eSJoel Fernandes                 }
1048*47c4846eSJoel Fernandes                 ucode_offset -= first_fwsec.base.data.len();
1049*47c4846eSJoel Fernandes                 self.falcon_ucode_offset = Some(ucode_offset);
1050*47c4846eSJoel Fernandes             }
1051*47c4846eSJoel Fernandes             Err(e) => {
1052*47c4846eSJoel Fernandes                 dev_err!(
1053*47c4846eSJoel Fernandes                     pdev.as_ref(),
1054*47c4846eSJoel Fernandes                     "PmuLookupTableEntry not found, error: {:?}\n",
1055*47c4846eSJoel Fernandes                     e
1056*47c4846eSJoel Fernandes                 );
1057*47c4846eSJoel Fernandes                 return Err(EINVAL);
1058*47c4846eSJoel Fernandes             }
1059*47c4846eSJoel Fernandes         }
1060*47c4846eSJoel Fernandes         Ok(())
1061*47c4846eSJoel Fernandes     }
1062*47c4846eSJoel Fernandes 
1063*47c4846eSJoel Fernandes     /// Build the final FwSecBiosImage from this builder
1064*47c4846eSJoel Fernandes     fn build(self, pdev: &pci::Device) -> Result<FwSecBiosImage> {
1065*47c4846eSJoel Fernandes         let ret = FwSecBiosImage {
1066*47c4846eSJoel Fernandes             base: self.base,
1067*47c4846eSJoel Fernandes             falcon_ucode_offset: self.falcon_ucode_offset.ok_or(EINVAL)?,
1068*47c4846eSJoel Fernandes         };
1069*47c4846eSJoel Fernandes 
1070*47c4846eSJoel Fernandes         if cfg!(debug_assertions) {
1071*47c4846eSJoel Fernandes             // Print the desc header for debugging
1072*47c4846eSJoel Fernandes             let desc = ret.header(pdev.as_ref())?;
1073*47c4846eSJoel Fernandes             dev_dbg!(pdev.as_ref(), "PmuLookupTableEntry desc: {:#?}\n", desc);
1074*47c4846eSJoel Fernandes         }
1075*47c4846eSJoel Fernandes 
1076*47c4846eSJoel Fernandes         Ok(ret)
1077*47c4846eSJoel Fernandes     }
1078*47c4846eSJoel Fernandes }
1079*47c4846eSJoel Fernandes 
1080*47c4846eSJoel Fernandes impl FwSecBiosImage {
1081*47c4846eSJoel Fernandes     /// Get the FwSec header ([`FalconUCodeDescV3`]).
1082*47c4846eSJoel Fernandes     pub(crate) fn header(&self, dev: &device::Device) -> Result<&FalconUCodeDescV3> {
1083*47c4846eSJoel Fernandes         // Get the falcon ucode offset that was found in setup_falcon_data.
1084*47c4846eSJoel Fernandes         let falcon_ucode_offset = self.falcon_ucode_offset;
1085*47c4846eSJoel Fernandes 
1086*47c4846eSJoel Fernandes         // Make sure the offset is within the data bounds.
1087*47c4846eSJoel Fernandes         if falcon_ucode_offset + core::mem::size_of::<FalconUCodeDescV3>() > self.base.data.len() {
1088*47c4846eSJoel Fernandes             dev_err!(dev, "fwsec-frts header not contained within BIOS bounds\n");
1089*47c4846eSJoel Fernandes             return Err(ERANGE);
1090*47c4846eSJoel Fernandes         }
1091*47c4846eSJoel Fernandes 
1092*47c4846eSJoel Fernandes         // Read the first 4 bytes to get the version.
1093*47c4846eSJoel Fernandes         let hdr_bytes: [u8; 4] = self.base.data[falcon_ucode_offset..falcon_ucode_offset + 4]
1094*47c4846eSJoel Fernandes             .try_into()
1095*47c4846eSJoel Fernandes             .map_err(|_| EINVAL)?;
1096*47c4846eSJoel Fernandes         let hdr = u32::from_le_bytes(hdr_bytes);
1097*47c4846eSJoel Fernandes         let ver = (hdr & 0xff00) >> 8;
1098*47c4846eSJoel Fernandes 
1099*47c4846eSJoel Fernandes         if ver != 3 {
1100*47c4846eSJoel Fernandes             dev_err!(dev, "invalid fwsec firmware version: {:?}\n", ver);
1101*47c4846eSJoel Fernandes             return Err(EINVAL);
1102*47c4846eSJoel Fernandes         }
1103*47c4846eSJoel Fernandes 
1104*47c4846eSJoel Fernandes         // Return a reference to the FalconUCodeDescV3 structure.
1105*47c4846eSJoel Fernandes         //
1106*47c4846eSJoel Fernandes         // SAFETY: We have checked that `falcon_ucode_offset + size_of::<FalconUCodeDescV3>` is
1107*47c4846eSJoel Fernandes         // within the bounds of `data`. Also, this data vector is from ROM, and the `data` field
1108*47c4846eSJoel Fernandes         // in `BiosImageBase` is immutable after construction.
1109*47c4846eSJoel Fernandes         Ok(unsafe {
1110*47c4846eSJoel Fernandes             &*(self
1111*47c4846eSJoel Fernandes                 .base
1112*47c4846eSJoel Fernandes                 .data
1113*47c4846eSJoel Fernandes                 .as_ptr()
1114*47c4846eSJoel Fernandes                 .add(falcon_ucode_offset)
1115*47c4846eSJoel Fernandes                 .cast::<FalconUCodeDescV3>())
1116*47c4846eSJoel Fernandes         })
1117*47c4846eSJoel Fernandes     }
1118*47c4846eSJoel Fernandes 
1119*47c4846eSJoel Fernandes     /// Get the ucode data as a byte slice
1120*47c4846eSJoel Fernandes     pub(crate) fn ucode(&self, dev: &device::Device, desc: &FalconUCodeDescV3) -> Result<&[u8]> {
1121*47c4846eSJoel Fernandes         let falcon_ucode_offset = self.falcon_ucode_offset;
1122*47c4846eSJoel Fernandes 
1123*47c4846eSJoel Fernandes         // The ucode data follows the descriptor.
1124*47c4846eSJoel Fernandes         let ucode_data_offset = falcon_ucode_offset + desc.size();
1125*47c4846eSJoel Fernandes         let size = (desc.imem_load_size + desc.dmem_load_size) as usize;
1126*47c4846eSJoel Fernandes 
1127*47c4846eSJoel Fernandes         // Get the data slice, checking bounds in a single operation.
1128*47c4846eSJoel Fernandes         self.base
1129*47c4846eSJoel Fernandes             .data
1130*47c4846eSJoel Fernandes             .get(ucode_data_offset..ucode_data_offset + size)
1131*47c4846eSJoel Fernandes             .ok_or(ERANGE)
1132*47c4846eSJoel Fernandes             .inspect_err(|_| dev_err!(dev, "fwsec ucode data not contained within BIOS bounds\n"))
1133*47c4846eSJoel Fernandes     }
1134*47c4846eSJoel Fernandes 
1135*47c4846eSJoel Fernandes     /// Get the signatures as a byte slice
1136*47c4846eSJoel Fernandes     pub(crate) fn sigs(&self, dev: &device::Device, desc: &FalconUCodeDescV3) -> Result<&[u8]> {
1137*47c4846eSJoel Fernandes         const SIG_SIZE: usize = 96 * 4;
1138*47c4846eSJoel Fernandes 
1139*47c4846eSJoel Fernandes         // The signatures data follows the descriptor.
1140*47c4846eSJoel Fernandes         let sigs_data_offset = self.falcon_ucode_offset + core::mem::size_of::<FalconUCodeDescV3>();
1141*47c4846eSJoel Fernandes         let size = desc.signature_count as usize * SIG_SIZE;
1142*47c4846eSJoel Fernandes 
1143*47c4846eSJoel Fernandes         // Make sure the data is within bounds.
1144*47c4846eSJoel Fernandes         if sigs_data_offset + size > self.base.data.len() {
1145*47c4846eSJoel Fernandes             dev_err!(
1146*47c4846eSJoel Fernandes                 dev,
1147*47c4846eSJoel Fernandes                 "fwsec signatures data not contained within BIOS bounds\n"
1148*47c4846eSJoel Fernandes             );
1149*47c4846eSJoel Fernandes             return Err(ERANGE);
1150*47c4846eSJoel Fernandes         }
1151*47c4846eSJoel Fernandes 
1152*47c4846eSJoel Fernandes         Ok(&self.base.data[sigs_data_offset..sigs_data_offset + size])
1153*47c4846eSJoel Fernandes     }
1154*47c4846eSJoel Fernandes }
1155