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; 96fda04e7SJoel Fernandes use core::convert::TryFrom; 106fda04e7SJoel Fernandes use kernel::error::Result; 116fda04e7SJoel Fernandes use kernel::pci; 126fda04e7SJoel Fernandes use kernel::prelude::*; 136fda04e7SJoel Fernandes 146fda04e7SJoel Fernandes /// The offset of the VBIOS ROM in the BAR0 space. 156fda04e7SJoel Fernandes const ROM_OFFSET: usize = 0x300000; 166fda04e7SJoel Fernandes /// The maximum length of the VBIOS ROM to scan into. 176fda04e7SJoel Fernandes const BIOS_MAX_SCAN_LEN: usize = 0x100000; 186fda04e7SJoel Fernandes /// The size to read ahead when parsing initial BIOS image headers. 196fda04e7SJoel Fernandes const BIOS_READ_AHEAD_SIZE: usize = 1024; 206fda04e7SJoel Fernandes /// The bit in the last image indicator byte for the PCI Data Structure that 216fda04e7SJoel Fernandes /// indicates the last image. Bit 0-6 are reserved, bit 7 is last image bit. 226fda04e7SJoel Fernandes const LAST_IMAGE_BIT_MASK: u8 = 0x80; 236fda04e7SJoel Fernandes 246fda04e7SJoel Fernandes // PMU lookup table entry types. Used to locate PMU table entries 256fda04e7SJoel Fernandes // in the Fwsec image, corresponding to falcon ucodes. 266fda04e7SJoel Fernandes #[expect(dead_code)] 276fda04e7SJoel Fernandes const FALCON_UCODE_ENTRY_APPID_FIRMWARE_SEC_LIC: u8 = 0x05; 286fda04e7SJoel Fernandes #[expect(dead_code)] 296fda04e7SJoel Fernandes const FALCON_UCODE_ENTRY_APPID_FWSEC_DBG: u8 = 0x45; 306fda04e7SJoel Fernandes const FALCON_UCODE_ENTRY_APPID_FWSEC_PROD: u8 = 0x85; 316fda04e7SJoel Fernandes 326fda04e7SJoel Fernandes /// Vbios Reader for constructing the VBIOS data. 336fda04e7SJoel Fernandes struct VbiosIterator<'a> { 346fda04e7SJoel Fernandes pdev: &'a pci::Device, 356fda04e7SJoel Fernandes bar0: &'a Bar0, 366fda04e7SJoel Fernandes /// VBIOS data vector: As BIOS images are scanned, they are added to this vector for reference 376fda04e7SJoel Fernandes /// or copying into other data structures. It is the entire scanned contents of the VBIOS which 386fda04e7SJoel Fernandes /// progressively extends. It is used so that we do not re-read any contents that are already 396fda04e7SJoel Fernandes /// read as we use the cumulative length read so far, and re-read any gaps as we extend the 406fda04e7SJoel Fernandes /// length. 416fda04e7SJoel Fernandes data: KVec<u8>, 426fda04e7SJoel Fernandes /// Current offset of the [`Iterator`]. 436fda04e7SJoel Fernandes current_offset: usize, 446fda04e7SJoel Fernandes /// Indicate whether the last image has been found. 456fda04e7SJoel Fernandes last_found: bool, 466fda04e7SJoel Fernandes } 476fda04e7SJoel Fernandes 486fda04e7SJoel Fernandes impl<'a> VbiosIterator<'a> { 496fda04e7SJoel Fernandes fn new(pdev: &'a pci::Device, bar0: &'a Bar0) -> Result<Self> { 506fda04e7SJoel Fernandes Ok(Self { 516fda04e7SJoel Fernandes pdev, 526fda04e7SJoel Fernandes bar0, 536fda04e7SJoel Fernandes data: KVec::new(), 546fda04e7SJoel Fernandes current_offset: 0, 556fda04e7SJoel Fernandes last_found: false, 566fda04e7SJoel Fernandes }) 576fda04e7SJoel Fernandes } 586fda04e7SJoel Fernandes 596fda04e7SJoel Fernandes /// Read bytes from the ROM at the current end of the data vector. 606fda04e7SJoel Fernandes fn read_more(&mut self, len: usize) -> Result { 616fda04e7SJoel Fernandes let current_len = self.data.len(); 626fda04e7SJoel Fernandes let start = ROM_OFFSET + current_len; 636fda04e7SJoel Fernandes 646fda04e7SJoel Fernandes // Ensure length is a multiple of 4 for 32-bit reads 656fda04e7SJoel Fernandes if len % core::mem::size_of::<u32>() != 0 { 666fda04e7SJoel Fernandes dev_err!( 676fda04e7SJoel Fernandes self.pdev.as_ref(), 686fda04e7SJoel Fernandes "VBIOS read length {} is not a multiple of 4\n", 696fda04e7SJoel Fernandes len 706fda04e7SJoel Fernandes ); 716fda04e7SJoel Fernandes return Err(EINVAL); 726fda04e7SJoel Fernandes } 736fda04e7SJoel Fernandes 746fda04e7SJoel Fernandes self.data.reserve(len, GFP_KERNEL)?; 756fda04e7SJoel Fernandes // Read ROM data bytes and push directly to `data`. 766fda04e7SJoel Fernandes for addr in (start..start + len).step_by(core::mem::size_of::<u32>()) { 776fda04e7SJoel Fernandes // Read 32-bit word from the VBIOS ROM 786fda04e7SJoel Fernandes let word = self.bar0.try_read32(addr)?; 796fda04e7SJoel Fernandes 806fda04e7SJoel Fernandes // Convert the `u32` to a 4 byte array and push each byte. 816fda04e7SJoel Fernandes word.to_ne_bytes() 826fda04e7SJoel Fernandes .iter() 836fda04e7SJoel Fernandes .try_for_each(|&b| self.data.push(b, GFP_KERNEL))?; 846fda04e7SJoel Fernandes } 856fda04e7SJoel Fernandes 866fda04e7SJoel Fernandes Ok(()) 876fda04e7SJoel Fernandes } 886fda04e7SJoel Fernandes 896fda04e7SJoel Fernandes /// Read bytes at a specific offset, filling any gap. 906fda04e7SJoel Fernandes fn read_more_at_offset(&mut self, offset: usize, len: usize) -> Result { 916fda04e7SJoel Fernandes if offset > BIOS_MAX_SCAN_LEN { 926fda04e7SJoel Fernandes dev_err!(self.pdev.as_ref(), "Error: exceeded BIOS scan limit.\n"); 936fda04e7SJoel Fernandes return Err(EINVAL); 946fda04e7SJoel Fernandes } 956fda04e7SJoel Fernandes 966fda04e7SJoel Fernandes // If `offset` is beyond current data size, fill the gap first. 976fda04e7SJoel Fernandes let current_len = self.data.len(); 986fda04e7SJoel Fernandes let gap_bytes = offset.saturating_sub(current_len); 996fda04e7SJoel Fernandes 1006fda04e7SJoel Fernandes // Now read the requested bytes at the offset. 1016fda04e7SJoel Fernandes self.read_more(gap_bytes + len) 1026fda04e7SJoel Fernandes } 1036fda04e7SJoel Fernandes 1046fda04e7SJoel Fernandes /// Read a BIOS image at a specific offset and create a [`BiosImage`] from it. 1056fda04e7SJoel Fernandes /// 1066fda04e7SJoel Fernandes /// `self.data` is extended as needed and a new [`BiosImage`] is returned. 1076fda04e7SJoel Fernandes /// `context` is a string describing the operation for error reporting. 1086fda04e7SJoel Fernandes fn read_bios_image_at_offset( 1096fda04e7SJoel Fernandes &mut self, 1106fda04e7SJoel Fernandes offset: usize, 1116fda04e7SJoel Fernandes len: usize, 1126fda04e7SJoel Fernandes context: &str, 1136fda04e7SJoel Fernandes ) -> Result<BiosImage> { 1146fda04e7SJoel Fernandes let data_len = self.data.len(); 1156fda04e7SJoel Fernandes if offset + len > data_len { 1166fda04e7SJoel Fernandes self.read_more_at_offset(offset, len).inspect_err(|e| { 1176fda04e7SJoel Fernandes dev_err!( 1186fda04e7SJoel Fernandes self.pdev.as_ref(), 1196fda04e7SJoel Fernandes "Failed to read more at offset {:#x}: {:?}\n", 1206fda04e7SJoel Fernandes offset, 1216fda04e7SJoel Fernandes e 1226fda04e7SJoel Fernandes ) 1236fda04e7SJoel Fernandes })?; 1246fda04e7SJoel Fernandes } 1256fda04e7SJoel Fernandes 1266fda04e7SJoel Fernandes BiosImage::new(self.pdev, &self.data[offset..offset + len]).inspect_err(|err| { 1276fda04e7SJoel Fernandes dev_err!( 1286fda04e7SJoel Fernandes self.pdev.as_ref(), 1296fda04e7SJoel Fernandes "Failed to {} at offset {:#x}: {:?}\n", 1306fda04e7SJoel Fernandes context, 1316fda04e7SJoel Fernandes offset, 1326fda04e7SJoel Fernandes err 1336fda04e7SJoel Fernandes ) 1346fda04e7SJoel Fernandes }) 1356fda04e7SJoel Fernandes } 1366fda04e7SJoel Fernandes } 1376fda04e7SJoel Fernandes 1386fda04e7SJoel Fernandes impl<'a> Iterator for VbiosIterator<'a> { 1396fda04e7SJoel Fernandes type Item = Result<BiosImage>; 1406fda04e7SJoel Fernandes 1416fda04e7SJoel Fernandes /// Iterate over all VBIOS images until the last image is detected or offset 1426fda04e7SJoel Fernandes /// exceeds scan limit. 1436fda04e7SJoel Fernandes fn next(&mut self) -> Option<Self::Item> { 1446fda04e7SJoel Fernandes if self.last_found { 1456fda04e7SJoel Fernandes return None; 1466fda04e7SJoel Fernandes } 1476fda04e7SJoel Fernandes 1486fda04e7SJoel Fernandes if self.current_offset > BIOS_MAX_SCAN_LEN { 1496fda04e7SJoel Fernandes dev_err!( 1506fda04e7SJoel Fernandes self.pdev.as_ref(), 1516fda04e7SJoel Fernandes "Error: exceeded BIOS scan limit, stopping scan\n" 1526fda04e7SJoel Fernandes ); 1536fda04e7SJoel Fernandes return None; 1546fda04e7SJoel Fernandes } 1556fda04e7SJoel Fernandes 1566fda04e7SJoel Fernandes // Parse image headers first to get image size. 1576fda04e7SJoel Fernandes let image_size = match self.read_bios_image_at_offset( 1586fda04e7SJoel Fernandes self.current_offset, 1596fda04e7SJoel Fernandes BIOS_READ_AHEAD_SIZE, 1606fda04e7SJoel Fernandes "parse initial BIOS image headers", 1616fda04e7SJoel Fernandes ) { 1626fda04e7SJoel Fernandes Ok(image) => image.image_size_bytes(), 1636fda04e7SJoel Fernandes Err(e) => return Some(Err(e)), 1646fda04e7SJoel Fernandes }; 1656fda04e7SJoel Fernandes 1666fda04e7SJoel Fernandes // Now create a new `BiosImage` with the full image data. 1676fda04e7SJoel Fernandes let full_image = match self.read_bios_image_at_offset( 1686fda04e7SJoel Fernandes self.current_offset, 1696fda04e7SJoel Fernandes image_size, 1706fda04e7SJoel Fernandes "parse full BIOS image", 1716fda04e7SJoel Fernandes ) { 1726fda04e7SJoel Fernandes Ok(image) => image, 1736fda04e7SJoel Fernandes Err(e) => return Some(Err(e)), 1746fda04e7SJoel Fernandes }; 1756fda04e7SJoel Fernandes 1766fda04e7SJoel Fernandes self.last_found = full_image.is_last(); 1776fda04e7SJoel Fernandes 1786fda04e7SJoel Fernandes // Advance to next image (aligned to 512 bytes). 1796fda04e7SJoel Fernandes self.current_offset += image_size; 1806fda04e7SJoel Fernandes // TODO: replace with `align_up` once it lands. 1816fda04e7SJoel Fernandes self.current_offset = self.current_offset.next_multiple_of(512); 1826fda04e7SJoel Fernandes 1836fda04e7SJoel Fernandes Some(Ok(full_image)) 1846fda04e7SJoel Fernandes } 1856fda04e7SJoel Fernandes } 1866fda04e7SJoel Fernandes 1876fda04e7SJoel Fernandes pub(crate) struct Vbios { 1886fda04e7SJoel Fernandes fwsec_image: FwSecBiosImage, 1896fda04e7SJoel Fernandes } 1906fda04e7SJoel Fernandes 1916fda04e7SJoel Fernandes impl Vbios { 1926fda04e7SJoel Fernandes /// Probe for VBIOS extraction. 1936fda04e7SJoel Fernandes /// 1946fda04e7SJoel Fernandes /// Once the VBIOS object is built, `bar0` is not read for [`Vbios`] purposes anymore. 1956fda04e7SJoel Fernandes pub(crate) fn new(pdev: &pci::Device, bar0: &Bar0) -> Result<Vbios> { 1966fda04e7SJoel Fernandes // Images to extract from iteration 1976fda04e7SJoel Fernandes let mut pci_at_image: Option<PciAtBiosImage> = None; 1986fda04e7SJoel Fernandes let mut first_fwsec_image: Option<FwSecBiosImage> = None; 1996fda04e7SJoel Fernandes let mut second_fwsec_image: Option<FwSecBiosImage> = None; 2006fda04e7SJoel Fernandes 2016fda04e7SJoel Fernandes // Parse all VBIOS images in the ROM 2026fda04e7SJoel Fernandes for image_result in VbiosIterator::new(pdev, bar0)? { 2036fda04e7SJoel Fernandes let full_image = image_result?; 2046fda04e7SJoel Fernandes 2056fda04e7SJoel Fernandes dev_dbg!( 2066fda04e7SJoel Fernandes pdev.as_ref(), 2076fda04e7SJoel Fernandes "Found BIOS image: size: {:#x}, type: {}, last: {}\n", 2086fda04e7SJoel Fernandes full_image.image_size_bytes(), 2096fda04e7SJoel Fernandes full_image.image_type_str(), 2106fda04e7SJoel Fernandes full_image.is_last() 2116fda04e7SJoel Fernandes ); 2126fda04e7SJoel Fernandes 2136fda04e7SJoel Fernandes // Get references to images we will need after the loop, in order to 2146fda04e7SJoel Fernandes // setup the falcon data offset. 2156fda04e7SJoel Fernandes match full_image { 2166fda04e7SJoel Fernandes BiosImage::PciAt(image) => { 2176fda04e7SJoel Fernandes pci_at_image = Some(image); 2186fda04e7SJoel Fernandes } 2196fda04e7SJoel Fernandes BiosImage::FwSec(image) => { 2206fda04e7SJoel Fernandes if first_fwsec_image.is_none() { 2216fda04e7SJoel Fernandes first_fwsec_image = Some(image); 2226fda04e7SJoel Fernandes } else { 2236fda04e7SJoel Fernandes second_fwsec_image = Some(image); 2246fda04e7SJoel Fernandes } 2256fda04e7SJoel Fernandes } 2266fda04e7SJoel Fernandes // For now we don't need to handle these 2276fda04e7SJoel Fernandes BiosImage::Efi(_image) => {} 2286fda04e7SJoel Fernandes BiosImage::Nbsi(_image) => {} 2296fda04e7SJoel Fernandes } 2306fda04e7SJoel Fernandes } 2316fda04e7SJoel Fernandes 2326fda04e7SJoel Fernandes // Using all the images, setup the falcon data pointer in Fwsec. 2336fda04e7SJoel Fernandes // These are temporarily unused images and will be used in later patches. 2346fda04e7SJoel Fernandes if let (Some(second), Some(_first), Some(_pci_at)) = 2356fda04e7SJoel Fernandes (second_fwsec_image, first_fwsec_image, pci_at_image) 2366fda04e7SJoel Fernandes { 2376fda04e7SJoel Fernandes Ok(Vbios { 2386fda04e7SJoel Fernandes fwsec_image: second, 2396fda04e7SJoel Fernandes }) 2406fda04e7SJoel Fernandes } else { 2416fda04e7SJoel Fernandes dev_err!( 2426fda04e7SJoel Fernandes pdev.as_ref(), 2436fda04e7SJoel Fernandes "Missing required images for falcon data setup, skipping\n" 2446fda04e7SJoel Fernandes ); 2456fda04e7SJoel Fernandes Err(EINVAL) 2466fda04e7SJoel Fernandes } 2476fda04e7SJoel Fernandes } 2486fda04e7SJoel Fernandes } 2496fda04e7SJoel Fernandes 2506fda04e7SJoel Fernandes /// PCI Data Structure as defined in PCI Firmware Specification 2516fda04e7SJoel Fernandes #[derive(Debug, Clone)] 2526fda04e7SJoel Fernandes #[repr(C)] 2536fda04e7SJoel Fernandes struct PcirStruct { 2546fda04e7SJoel Fernandes /// PCI Data Structure signature ("PCIR" or "NPDS") 2556fda04e7SJoel Fernandes signature: [u8; 4], 2566fda04e7SJoel Fernandes /// PCI Vendor ID (e.g., 0x10DE for NVIDIA) 2576fda04e7SJoel Fernandes vendor_id: u16, 2586fda04e7SJoel Fernandes /// PCI Device ID 2596fda04e7SJoel Fernandes device_id: u16, 2606fda04e7SJoel Fernandes /// Device List Pointer 2616fda04e7SJoel Fernandes device_list_ptr: u16, 2626fda04e7SJoel Fernandes /// PCI Data Structure Length 2636fda04e7SJoel Fernandes pci_data_struct_len: u16, 2646fda04e7SJoel Fernandes /// PCI Data Structure Revision 2656fda04e7SJoel Fernandes pci_data_struct_rev: u8, 2666fda04e7SJoel Fernandes /// Class code (3 bytes, 0x03 for display controller) 2676fda04e7SJoel Fernandes class_code: [u8; 3], 2686fda04e7SJoel Fernandes /// Size of this image in 512-byte blocks 2696fda04e7SJoel Fernandes image_len: u16, 2706fda04e7SJoel Fernandes /// Revision Level of the Vendor's ROM 2716fda04e7SJoel Fernandes vendor_rom_rev: u16, 2726fda04e7SJoel Fernandes /// ROM image type (0x00 = PC-AT compatible, 0x03 = EFI, 0x70 = NBSI) 2736fda04e7SJoel Fernandes code_type: u8, 2746fda04e7SJoel Fernandes /// Last image indicator (0x00 = Not last image, 0x80 = Last image) 2756fda04e7SJoel Fernandes last_image: u8, 2766fda04e7SJoel Fernandes /// Maximum Run-time Image Length (units of 512 bytes) 2776fda04e7SJoel Fernandes max_runtime_image_len: u16, 2786fda04e7SJoel Fernandes } 2796fda04e7SJoel Fernandes 2806fda04e7SJoel Fernandes impl PcirStruct { 2816fda04e7SJoel Fernandes fn new(pdev: &pci::Device, data: &[u8]) -> Result<Self> { 2826fda04e7SJoel Fernandes if data.len() < core::mem::size_of::<PcirStruct>() { 2836fda04e7SJoel Fernandes dev_err!(pdev.as_ref(), "Not enough data for PcirStruct\n"); 2846fda04e7SJoel Fernandes return Err(EINVAL); 2856fda04e7SJoel Fernandes } 2866fda04e7SJoel Fernandes 2876fda04e7SJoel Fernandes let mut signature = [0u8; 4]; 2886fda04e7SJoel Fernandes signature.copy_from_slice(&data[0..4]); 2896fda04e7SJoel Fernandes 2906fda04e7SJoel Fernandes // Signature should be "PCIR" (0x52494350) or "NPDS" (0x5344504e). 2916fda04e7SJoel Fernandes if &signature != b"PCIR" && &signature != b"NPDS" { 2926fda04e7SJoel Fernandes dev_err!( 2936fda04e7SJoel Fernandes pdev.as_ref(), 2946fda04e7SJoel Fernandes "Invalid signature for PcirStruct: {:?}\n", 2956fda04e7SJoel Fernandes signature 2966fda04e7SJoel Fernandes ); 2976fda04e7SJoel Fernandes return Err(EINVAL); 2986fda04e7SJoel Fernandes } 2996fda04e7SJoel Fernandes 3006fda04e7SJoel Fernandes let mut class_code = [0u8; 3]; 3016fda04e7SJoel Fernandes class_code.copy_from_slice(&data[13..16]); 3026fda04e7SJoel Fernandes 3036fda04e7SJoel Fernandes let image_len = u16::from_le_bytes([data[16], data[17]]); 3046fda04e7SJoel Fernandes if image_len == 0 { 3056fda04e7SJoel Fernandes dev_err!(pdev.as_ref(), "Invalid image length: 0\n"); 3066fda04e7SJoel Fernandes return Err(EINVAL); 3076fda04e7SJoel Fernandes } 3086fda04e7SJoel Fernandes 3096fda04e7SJoel Fernandes Ok(PcirStruct { 3106fda04e7SJoel Fernandes signature, 3116fda04e7SJoel Fernandes vendor_id: u16::from_le_bytes([data[4], data[5]]), 3126fda04e7SJoel Fernandes device_id: u16::from_le_bytes([data[6], data[7]]), 3136fda04e7SJoel Fernandes device_list_ptr: u16::from_le_bytes([data[8], data[9]]), 3146fda04e7SJoel Fernandes pci_data_struct_len: u16::from_le_bytes([data[10], data[11]]), 3156fda04e7SJoel Fernandes pci_data_struct_rev: data[12], 3166fda04e7SJoel Fernandes class_code, 3176fda04e7SJoel Fernandes image_len, 3186fda04e7SJoel Fernandes vendor_rom_rev: u16::from_le_bytes([data[18], data[19]]), 3196fda04e7SJoel Fernandes code_type: data[20], 3206fda04e7SJoel Fernandes last_image: data[21], 3216fda04e7SJoel Fernandes max_runtime_image_len: u16::from_le_bytes([data[22], data[23]]), 3226fda04e7SJoel Fernandes }) 3236fda04e7SJoel Fernandes } 3246fda04e7SJoel Fernandes 3256fda04e7SJoel Fernandes /// Check if this is the last image in the ROM. 3266fda04e7SJoel Fernandes fn is_last(&self) -> bool { 3276fda04e7SJoel Fernandes self.last_image & LAST_IMAGE_BIT_MASK != 0 3286fda04e7SJoel Fernandes } 3296fda04e7SJoel Fernandes 3306fda04e7SJoel Fernandes /// Calculate image size in bytes from 512-byte blocks. 3316fda04e7SJoel Fernandes fn image_size_bytes(&self) -> usize { 3326fda04e7SJoel Fernandes self.image_len as usize * 512 3336fda04e7SJoel Fernandes } 3346fda04e7SJoel Fernandes } 3356fda04e7SJoel Fernandes 336*dc70c6aeSJoel Fernandes /// BIOS Information Table (BIT) Header. 337*dc70c6aeSJoel Fernandes /// 338*dc70c6aeSJoel Fernandes /// This is the head of the BIT table, that is used to locate the Falcon data. The BIT table (with 339*dc70c6aeSJoel Fernandes /// its header) is in the [`PciAtBiosImage`] and the falcon data it is pointing to is in the 340*dc70c6aeSJoel Fernandes /// [`FwSecBiosImage`]. 341*dc70c6aeSJoel Fernandes #[derive(Debug, Clone, Copy)] 342*dc70c6aeSJoel Fernandes #[expect(dead_code)] 343*dc70c6aeSJoel Fernandes struct BitHeader { 344*dc70c6aeSJoel Fernandes /// 0h: BIT Header Identifier (BMP=0x7FFF/BIT=0xB8FF) 345*dc70c6aeSJoel Fernandes id: u16, 346*dc70c6aeSJoel Fernandes /// 2h: BIT Header Signature ("BIT\0") 347*dc70c6aeSJoel Fernandes signature: [u8; 4], 348*dc70c6aeSJoel Fernandes /// 6h: Binary Coded Decimal Version, ex: 0x0100 is 1.00. 349*dc70c6aeSJoel Fernandes bcd_version: u16, 350*dc70c6aeSJoel Fernandes /// 8h: Size of BIT Header (in bytes) 351*dc70c6aeSJoel Fernandes header_size: u8, 352*dc70c6aeSJoel Fernandes /// 9h: Size of BIT Tokens (in bytes) 353*dc70c6aeSJoel Fernandes token_size: u8, 354*dc70c6aeSJoel Fernandes /// 10h: Number of token entries that follow 355*dc70c6aeSJoel Fernandes token_entries: u8, 356*dc70c6aeSJoel Fernandes /// 11h: BIT Header Checksum 357*dc70c6aeSJoel Fernandes checksum: u8, 358*dc70c6aeSJoel Fernandes } 359*dc70c6aeSJoel Fernandes 360*dc70c6aeSJoel Fernandes impl BitHeader { 361*dc70c6aeSJoel Fernandes fn new(data: &[u8]) -> Result<Self> { 362*dc70c6aeSJoel Fernandes if data.len() < 12 { 363*dc70c6aeSJoel Fernandes return Err(EINVAL); 364*dc70c6aeSJoel Fernandes } 365*dc70c6aeSJoel Fernandes 366*dc70c6aeSJoel Fernandes let mut signature = [0u8; 4]; 367*dc70c6aeSJoel Fernandes signature.copy_from_slice(&data[2..6]); 368*dc70c6aeSJoel Fernandes 369*dc70c6aeSJoel Fernandes // Check header ID and signature 370*dc70c6aeSJoel Fernandes let id = u16::from_le_bytes([data[0], data[1]]); 371*dc70c6aeSJoel Fernandes if id != 0xB8FF || &signature != b"BIT\0" { 372*dc70c6aeSJoel Fernandes return Err(EINVAL); 373*dc70c6aeSJoel Fernandes } 374*dc70c6aeSJoel Fernandes 375*dc70c6aeSJoel Fernandes Ok(BitHeader { 376*dc70c6aeSJoel Fernandes id, 377*dc70c6aeSJoel Fernandes signature, 378*dc70c6aeSJoel Fernandes bcd_version: u16::from_le_bytes([data[6], data[7]]), 379*dc70c6aeSJoel Fernandes header_size: data[8], 380*dc70c6aeSJoel Fernandes token_size: data[9], 381*dc70c6aeSJoel Fernandes token_entries: data[10], 382*dc70c6aeSJoel Fernandes checksum: data[11], 383*dc70c6aeSJoel Fernandes }) 384*dc70c6aeSJoel Fernandes } 385*dc70c6aeSJoel Fernandes } 386*dc70c6aeSJoel Fernandes 387*dc70c6aeSJoel Fernandes /// BIT Token Entry: Records in the BIT table followed by the BIT header. 388*dc70c6aeSJoel Fernandes #[derive(Debug, Clone, Copy)] 389*dc70c6aeSJoel Fernandes #[expect(dead_code)] 390*dc70c6aeSJoel Fernandes struct BitToken { 391*dc70c6aeSJoel Fernandes /// 00h: Token identifier 392*dc70c6aeSJoel Fernandes id: u8, 393*dc70c6aeSJoel Fernandes /// 01h: Version of the token data 394*dc70c6aeSJoel Fernandes data_version: u8, 395*dc70c6aeSJoel Fernandes /// 02h: Size of token data in bytes 396*dc70c6aeSJoel Fernandes data_size: u16, 397*dc70c6aeSJoel Fernandes /// 04h: Offset to the token data 398*dc70c6aeSJoel Fernandes data_offset: u16, 399*dc70c6aeSJoel Fernandes } 400*dc70c6aeSJoel Fernandes 401*dc70c6aeSJoel Fernandes // Define the token ID for the Falcon data 402*dc70c6aeSJoel Fernandes const BIT_TOKEN_ID_FALCON_DATA: u8 = 0x70; 403*dc70c6aeSJoel Fernandes 404*dc70c6aeSJoel Fernandes impl BitToken { 405*dc70c6aeSJoel Fernandes /// Find a BIT token entry by BIT ID in a PciAtBiosImage 406*dc70c6aeSJoel Fernandes fn from_id(image: &PciAtBiosImage, token_id: u8) -> Result<Self> { 407*dc70c6aeSJoel Fernandes let header = &image.bit_header; 408*dc70c6aeSJoel Fernandes 409*dc70c6aeSJoel Fernandes // Offset to the first token entry 410*dc70c6aeSJoel Fernandes let tokens_start = image.bit_offset + header.header_size as usize; 411*dc70c6aeSJoel Fernandes 412*dc70c6aeSJoel Fernandes for i in 0..header.token_entries as usize { 413*dc70c6aeSJoel Fernandes let entry_offset = tokens_start + (i * header.token_size as usize); 414*dc70c6aeSJoel Fernandes 415*dc70c6aeSJoel Fernandes // Make sure we don't go out of bounds 416*dc70c6aeSJoel Fernandes if entry_offset + header.token_size as usize > image.base.data.len() { 417*dc70c6aeSJoel Fernandes return Err(EINVAL); 418*dc70c6aeSJoel Fernandes } 419*dc70c6aeSJoel Fernandes 420*dc70c6aeSJoel Fernandes // Check if this token has the requested ID 421*dc70c6aeSJoel Fernandes if image.base.data[entry_offset] == token_id { 422*dc70c6aeSJoel Fernandes return Ok(BitToken { 423*dc70c6aeSJoel Fernandes id: image.base.data[entry_offset], 424*dc70c6aeSJoel Fernandes data_version: image.base.data[entry_offset + 1], 425*dc70c6aeSJoel Fernandes data_size: u16::from_le_bytes([ 426*dc70c6aeSJoel Fernandes image.base.data[entry_offset + 2], 427*dc70c6aeSJoel Fernandes image.base.data[entry_offset + 3], 428*dc70c6aeSJoel Fernandes ]), 429*dc70c6aeSJoel Fernandes data_offset: u16::from_le_bytes([ 430*dc70c6aeSJoel Fernandes image.base.data[entry_offset + 4], 431*dc70c6aeSJoel Fernandes image.base.data[entry_offset + 5], 432*dc70c6aeSJoel Fernandes ]), 433*dc70c6aeSJoel Fernandes }); 434*dc70c6aeSJoel Fernandes } 435*dc70c6aeSJoel Fernandes } 436*dc70c6aeSJoel Fernandes 437*dc70c6aeSJoel Fernandes // Token not found 438*dc70c6aeSJoel Fernandes Err(ENOENT) 439*dc70c6aeSJoel Fernandes } 440*dc70c6aeSJoel Fernandes } 441*dc70c6aeSJoel Fernandes 4426fda04e7SJoel Fernandes /// PCI ROM Expansion Header as defined in PCI Firmware Specification. 4436fda04e7SJoel Fernandes /// 4446fda04e7SJoel Fernandes /// This is header is at the beginning of every image in the set of images in the ROM. It contains 4456fda04e7SJoel Fernandes /// a pointer to the PCI Data Structure which describes the image. For "NBSI" images (NoteBook 4466fda04e7SJoel Fernandes /// System Information), the ROM header deviates from the standard and contains an offset to the 4476fda04e7SJoel Fernandes /// NBSI image however we do not yet parse that in this module and keep it for future reference. 4486fda04e7SJoel Fernandes #[derive(Debug, Clone, Copy)] 4496fda04e7SJoel Fernandes #[expect(dead_code)] 4506fda04e7SJoel Fernandes struct PciRomHeader { 4516fda04e7SJoel Fernandes /// 00h: Signature (0xAA55) 4526fda04e7SJoel Fernandes signature: u16, 4536fda04e7SJoel Fernandes /// 02h: Reserved bytes for processor architecture unique data (20 bytes) 4546fda04e7SJoel Fernandes reserved: [u8; 20], 4556fda04e7SJoel Fernandes /// 16h: NBSI Data Offset (NBSI-specific, offset from header to NBSI image) 4566fda04e7SJoel Fernandes nbsi_data_offset: Option<u16>, 4576fda04e7SJoel Fernandes /// 18h: Pointer to PCI Data Structure (offset from start of ROM image) 4586fda04e7SJoel Fernandes pci_data_struct_offset: u16, 4596fda04e7SJoel Fernandes /// 1Ah: Size of block (this is NBSI-specific) 4606fda04e7SJoel Fernandes size_of_block: Option<u32>, 4616fda04e7SJoel Fernandes } 4626fda04e7SJoel Fernandes 4636fda04e7SJoel Fernandes impl PciRomHeader { 4646fda04e7SJoel Fernandes fn new(pdev: &pci::Device, data: &[u8]) -> Result<Self> { 4656fda04e7SJoel Fernandes if data.len() < 26 { 4666fda04e7SJoel Fernandes // Need at least 26 bytes to read pciDataStrucPtr and sizeOfBlock. 4676fda04e7SJoel Fernandes return Err(EINVAL); 4686fda04e7SJoel Fernandes } 4696fda04e7SJoel Fernandes 4706fda04e7SJoel Fernandes let signature = u16::from_le_bytes([data[0], data[1]]); 4716fda04e7SJoel Fernandes 4726fda04e7SJoel Fernandes // Check for valid ROM signatures. 4736fda04e7SJoel Fernandes match signature { 4746fda04e7SJoel Fernandes 0xAA55 | 0xBB77 | 0x4E56 => {} 4756fda04e7SJoel Fernandes _ => { 4766fda04e7SJoel Fernandes dev_err!(pdev.as_ref(), "ROM signature unknown {:#x}\n", signature); 4776fda04e7SJoel Fernandes return Err(EINVAL); 4786fda04e7SJoel Fernandes } 4796fda04e7SJoel Fernandes } 4806fda04e7SJoel Fernandes 4816fda04e7SJoel Fernandes // Read the pointer to the PCI Data Structure at offset 0x18. 4826fda04e7SJoel Fernandes let pci_data_struct_ptr = u16::from_le_bytes([data[24], data[25]]); 4836fda04e7SJoel Fernandes 4846fda04e7SJoel Fernandes // Try to read optional fields if enough data. 4856fda04e7SJoel Fernandes let mut size_of_block = None; 4866fda04e7SJoel Fernandes let mut nbsi_data_offset = None; 4876fda04e7SJoel Fernandes 4886fda04e7SJoel Fernandes if data.len() >= 30 { 4896fda04e7SJoel Fernandes // Read size_of_block at offset 0x1A. 4906fda04e7SJoel Fernandes size_of_block = Some( 4916fda04e7SJoel Fernandes (data[29] as u32) << 24 4926fda04e7SJoel Fernandes | (data[28] as u32) << 16 4936fda04e7SJoel Fernandes | (data[27] as u32) << 8 4946fda04e7SJoel Fernandes | (data[26] as u32), 4956fda04e7SJoel Fernandes ); 4966fda04e7SJoel Fernandes } 4976fda04e7SJoel Fernandes 4986fda04e7SJoel Fernandes // For NBSI images, try to read the nbsiDataOffset at offset 0x16. 4996fda04e7SJoel Fernandes if data.len() >= 24 { 5006fda04e7SJoel Fernandes nbsi_data_offset = Some(u16::from_le_bytes([data[22], data[23]])); 5016fda04e7SJoel Fernandes } 5026fda04e7SJoel Fernandes 5036fda04e7SJoel Fernandes Ok(PciRomHeader { 5046fda04e7SJoel Fernandes signature, 5056fda04e7SJoel Fernandes reserved: [0u8; 20], 5066fda04e7SJoel Fernandes pci_data_struct_offset: pci_data_struct_ptr, 5076fda04e7SJoel Fernandes size_of_block, 5086fda04e7SJoel Fernandes nbsi_data_offset, 5096fda04e7SJoel Fernandes }) 5106fda04e7SJoel Fernandes } 5116fda04e7SJoel Fernandes } 5126fda04e7SJoel Fernandes 5136fda04e7SJoel Fernandes /// NVIDIA PCI Data Extension Structure. 5146fda04e7SJoel Fernandes /// 5156fda04e7SJoel Fernandes /// This is similar to the PCI Data Structure, but is Nvidia-specific and is placed right after the 5166fda04e7SJoel Fernandes /// PCI Data Structure. It contains some fields that are redundant with the PCI Data Structure, but 5176fda04e7SJoel Fernandes /// are needed for traversing the BIOS images. It is expected to be present in all BIOS images 5186fda04e7SJoel Fernandes /// except for NBSI images. 5196fda04e7SJoel Fernandes #[derive(Debug, Clone)] 5206fda04e7SJoel Fernandes #[repr(C)] 5216fda04e7SJoel Fernandes struct NpdeStruct { 5226fda04e7SJoel Fernandes /// 00h: Signature ("NPDE") 5236fda04e7SJoel Fernandes signature: [u8; 4], 5246fda04e7SJoel Fernandes /// 04h: NVIDIA PCI Data Extension Revision 5256fda04e7SJoel Fernandes npci_data_ext_rev: u16, 5266fda04e7SJoel Fernandes /// 06h: NVIDIA PCI Data Extension Length 5276fda04e7SJoel Fernandes npci_data_ext_len: u16, 5286fda04e7SJoel Fernandes /// 08h: Sub-image Length (in 512-byte units) 5296fda04e7SJoel Fernandes subimage_len: u16, 5306fda04e7SJoel Fernandes /// 0Ah: Last image indicator flag 5316fda04e7SJoel Fernandes last_image: u8, 5326fda04e7SJoel Fernandes } 5336fda04e7SJoel Fernandes 5346fda04e7SJoel Fernandes impl NpdeStruct { 5356fda04e7SJoel Fernandes fn new(pdev: &pci::Device, data: &[u8]) -> Option<Self> { 5366fda04e7SJoel Fernandes if data.len() < core::mem::size_of::<Self>() { 5376fda04e7SJoel Fernandes dev_dbg!(pdev.as_ref(), "Not enough data for NpdeStruct\n"); 5386fda04e7SJoel Fernandes return None; 5396fda04e7SJoel Fernandes } 5406fda04e7SJoel Fernandes 5416fda04e7SJoel Fernandes let mut signature = [0u8; 4]; 5426fda04e7SJoel Fernandes signature.copy_from_slice(&data[0..4]); 5436fda04e7SJoel Fernandes 5446fda04e7SJoel Fernandes // Signature should be "NPDE" (0x4544504E). 5456fda04e7SJoel Fernandes if &signature != b"NPDE" { 5466fda04e7SJoel Fernandes dev_dbg!( 5476fda04e7SJoel Fernandes pdev.as_ref(), 5486fda04e7SJoel Fernandes "Invalid signature for NpdeStruct: {:?}\n", 5496fda04e7SJoel Fernandes signature 5506fda04e7SJoel Fernandes ); 5516fda04e7SJoel Fernandes return None; 5526fda04e7SJoel Fernandes } 5536fda04e7SJoel Fernandes 5546fda04e7SJoel Fernandes let subimage_len = u16::from_le_bytes([data[8], data[9]]); 5556fda04e7SJoel Fernandes if subimage_len == 0 { 5566fda04e7SJoel Fernandes dev_dbg!(pdev.as_ref(), "Invalid subimage length: 0\n"); 5576fda04e7SJoel Fernandes return None; 5586fda04e7SJoel Fernandes } 5596fda04e7SJoel Fernandes 5606fda04e7SJoel Fernandes Some(NpdeStruct { 5616fda04e7SJoel Fernandes signature, 5626fda04e7SJoel Fernandes npci_data_ext_rev: u16::from_le_bytes([data[4], data[5]]), 5636fda04e7SJoel Fernandes npci_data_ext_len: u16::from_le_bytes([data[6], data[7]]), 5646fda04e7SJoel Fernandes subimage_len, 5656fda04e7SJoel Fernandes last_image: data[10], 5666fda04e7SJoel Fernandes }) 5676fda04e7SJoel Fernandes } 5686fda04e7SJoel Fernandes 5696fda04e7SJoel Fernandes /// Check if this is the last image in the ROM. 5706fda04e7SJoel Fernandes fn is_last(&self) -> bool { 5716fda04e7SJoel Fernandes self.last_image & LAST_IMAGE_BIT_MASK != 0 5726fda04e7SJoel Fernandes } 5736fda04e7SJoel Fernandes 5746fda04e7SJoel Fernandes /// Calculate image size in bytes from 512-byte blocks. 5756fda04e7SJoel Fernandes fn image_size_bytes(&self) -> usize { 5766fda04e7SJoel Fernandes self.subimage_len as usize * 512 5776fda04e7SJoel Fernandes } 5786fda04e7SJoel Fernandes 5796fda04e7SJoel Fernandes /// Try to find NPDE in the data, the NPDE is right after the PCIR. 5806fda04e7SJoel Fernandes fn find_in_data( 5816fda04e7SJoel Fernandes pdev: &pci::Device, 5826fda04e7SJoel Fernandes data: &[u8], 5836fda04e7SJoel Fernandes rom_header: &PciRomHeader, 5846fda04e7SJoel Fernandes pcir: &PcirStruct, 5856fda04e7SJoel Fernandes ) -> Option<Self> { 5866fda04e7SJoel Fernandes // Calculate the offset where NPDE might be located 5876fda04e7SJoel Fernandes // NPDE should be right after the PCIR structure, aligned to 16 bytes 5886fda04e7SJoel Fernandes let pcir_offset = rom_header.pci_data_struct_offset as usize; 5896fda04e7SJoel Fernandes let npde_start = (pcir_offset + pcir.pci_data_struct_len as usize + 0x0F) & !0x0F; 5906fda04e7SJoel Fernandes 5916fda04e7SJoel Fernandes // Check if we have enough data 5926fda04e7SJoel Fernandes if npde_start + core::mem::size_of::<Self>() > data.len() { 5936fda04e7SJoel Fernandes dev_dbg!(pdev.as_ref(), "Not enough data for NPDE\n"); 5946fda04e7SJoel Fernandes return None; 5956fda04e7SJoel Fernandes } 5966fda04e7SJoel Fernandes 5976fda04e7SJoel Fernandes // Try to create NPDE from the data 5986fda04e7SJoel Fernandes NpdeStruct::new(pdev, &data[npde_start..]) 5996fda04e7SJoel Fernandes } 6006fda04e7SJoel Fernandes } 6016fda04e7SJoel Fernandes 6026fda04e7SJoel Fernandes // Use a macro to implement BiosImage enum and methods. This avoids having to 6036fda04e7SJoel Fernandes // repeat each enum type when implementing functions like base() in BiosImage. 6046fda04e7SJoel Fernandes macro_rules! bios_image { 6056fda04e7SJoel Fernandes ( 6066fda04e7SJoel Fernandes $($variant:ident: $class:ident),* $(,)? 6076fda04e7SJoel Fernandes ) => { 6086fda04e7SJoel Fernandes // BiosImage enum with variants for each image type 6096fda04e7SJoel Fernandes enum BiosImage { 6106fda04e7SJoel Fernandes $($variant($class)),* 6116fda04e7SJoel Fernandes } 6126fda04e7SJoel Fernandes 6136fda04e7SJoel Fernandes impl BiosImage { 6146fda04e7SJoel Fernandes /// Get a reference to the common BIOS image data regardless of type 6156fda04e7SJoel Fernandes fn base(&self) -> &BiosImageBase { 6166fda04e7SJoel Fernandes match self { 6176fda04e7SJoel Fernandes $(Self::$variant(img) => &img.base),* 6186fda04e7SJoel Fernandes } 6196fda04e7SJoel Fernandes } 6206fda04e7SJoel Fernandes 6216fda04e7SJoel Fernandes /// Returns a string representing the type of BIOS image 6226fda04e7SJoel Fernandes fn image_type_str(&self) -> &'static str { 6236fda04e7SJoel Fernandes match self { 6246fda04e7SJoel Fernandes $(Self::$variant(_) => stringify!($variant)),* 6256fda04e7SJoel Fernandes } 6266fda04e7SJoel Fernandes } 6276fda04e7SJoel Fernandes } 6286fda04e7SJoel Fernandes } 6296fda04e7SJoel Fernandes } 6306fda04e7SJoel Fernandes 6316fda04e7SJoel Fernandes impl BiosImage { 6326fda04e7SJoel Fernandes /// Check if this is the last image. 6336fda04e7SJoel Fernandes fn is_last(&self) -> bool { 6346fda04e7SJoel Fernandes let base = self.base(); 6356fda04e7SJoel Fernandes 6366fda04e7SJoel Fernandes // For NBSI images (type == 0x70), return true as they're 6376fda04e7SJoel Fernandes // considered the last image 6386fda04e7SJoel Fernandes if matches!(self, Self::Nbsi(_)) { 6396fda04e7SJoel Fernandes return true; 6406fda04e7SJoel Fernandes } 6416fda04e7SJoel Fernandes 6426fda04e7SJoel Fernandes // For other image types, check the NPDE first if available 6436fda04e7SJoel Fernandes if let Some(ref npde) = base.npde { 6446fda04e7SJoel Fernandes return npde.is_last(); 6456fda04e7SJoel Fernandes } 6466fda04e7SJoel Fernandes 6476fda04e7SJoel Fernandes // Otherwise, fall back to checking the PCIR last_image flag 6486fda04e7SJoel Fernandes base.pcir.is_last() 6496fda04e7SJoel Fernandes } 6506fda04e7SJoel Fernandes 6516fda04e7SJoel Fernandes /// Get the image size in bytes. 6526fda04e7SJoel Fernandes fn image_size_bytes(&self) -> usize { 6536fda04e7SJoel Fernandes let base = self.base(); 6546fda04e7SJoel Fernandes 6556fda04e7SJoel Fernandes // Prefer NPDE image size if available 6566fda04e7SJoel Fernandes if let Some(ref npde) = base.npde { 6576fda04e7SJoel Fernandes return npde.image_size_bytes(); 6586fda04e7SJoel Fernandes } 6596fda04e7SJoel Fernandes 6606fda04e7SJoel Fernandes // Otherwise, fall back to the PCIR image size 6616fda04e7SJoel Fernandes base.pcir.image_size_bytes() 6626fda04e7SJoel Fernandes } 6636fda04e7SJoel Fernandes 6646fda04e7SJoel Fernandes /// Create a [`BiosImageBase`] from a byte slice and convert it to a [`BiosImage`] which 6656fda04e7SJoel Fernandes /// triggers the constructor of the specific BiosImage enum variant. 6666fda04e7SJoel Fernandes fn new(pdev: &pci::Device, data: &[u8]) -> Result<Self> { 6676fda04e7SJoel Fernandes let base = BiosImageBase::new(pdev, data)?; 6686fda04e7SJoel Fernandes let image = base.into_image().inspect_err(|e| { 6696fda04e7SJoel Fernandes dev_err!(pdev.as_ref(), "Failed to create BiosImage: {:?}\n", e); 6706fda04e7SJoel Fernandes })?; 6716fda04e7SJoel Fernandes 6726fda04e7SJoel Fernandes Ok(image) 6736fda04e7SJoel Fernandes } 6746fda04e7SJoel Fernandes } 6756fda04e7SJoel Fernandes 6766fda04e7SJoel Fernandes bios_image! { 6776fda04e7SJoel Fernandes PciAt: PciAtBiosImage, // PCI-AT compatible BIOS image 6786fda04e7SJoel Fernandes Efi: EfiBiosImage, // EFI (Extensible Firmware Interface) 6796fda04e7SJoel Fernandes Nbsi: NbsiBiosImage, // NBSI (Nvidia Bios System Interface) 6806fda04e7SJoel Fernandes FwSec: FwSecBiosImage, // FWSEC (Firmware Security) 6816fda04e7SJoel Fernandes } 6826fda04e7SJoel Fernandes 683*dc70c6aeSJoel Fernandes /// The PciAt BIOS image is typically the first BIOS image type found in the BIOS image chain. 684*dc70c6aeSJoel Fernandes /// 685*dc70c6aeSJoel Fernandes /// It contains the BIT header and the BIT tokens. 6866fda04e7SJoel Fernandes struct PciAtBiosImage { 6876fda04e7SJoel Fernandes base: BiosImageBase, 688*dc70c6aeSJoel Fernandes bit_header: BitHeader, 689*dc70c6aeSJoel Fernandes bit_offset: usize, 6906fda04e7SJoel Fernandes } 6916fda04e7SJoel Fernandes 6926fda04e7SJoel Fernandes struct EfiBiosImage { 6936fda04e7SJoel Fernandes base: BiosImageBase, 6946fda04e7SJoel Fernandes // EFI-specific fields can be added here in the future. 6956fda04e7SJoel Fernandes } 6966fda04e7SJoel Fernandes 6976fda04e7SJoel Fernandes struct NbsiBiosImage { 6986fda04e7SJoel Fernandes base: BiosImageBase, 6996fda04e7SJoel Fernandes // NBSI-specific fields can be added here in the future. 7006fda04e7SJoel Fernandes } 7016fda04e7SJoel Fernandes 7026fda04e7SJoel Fernandes struct FwSecBiosImage { 7036fda04e7SJoel Fernandes base: BiosImageBase, 7046fda04e7SJoel Fernandes // FWSEC-specific fields can be added here in the future. 7056fda04e7SJoel Fernandes } 7066fda04e7SJoel Fernandes 7076fda04e7SJoel Fernandes // Convert from BiosImageBase to BiosImage 7086fda04e7SJoel Fernandes impl TryFrom<BiosImageBase> for BiosImage { 7096fda04e7SJoel Fernandes type Error = Error; 7106fda04e7SJoel Fernandes 7116fda04e7SJoel Fernandes fn try_from(base: BiosImageBase) -> Result<Self> { 7126fda04e7SJoel Fernandes match base.pcir.code_type { 713*dc70c6aeSJoel Fernandes 0x00 => Ok(BiosImage::PciAt(base.try_into()?)), 7146fda04e7SJoel Fernandes 0x03 => Ok(BiosImage::Efi(EfiBiosImage { base })), 7156fda04e7SJoel Fernandes 0x70 => Ok(BiosImage::Nbsi(NbsiBiosImage { base })), 7166fda04e7SJoel Fernandes 0xE0 => Ok(BiosImage::FwSec(FwSecBiosImage { base })), 7176fda04e7SJoel Fernandes _ => Err(EINVAL), 7186fda04e7SJoel Fernandes } 7196fda04e7SJoel Fernandes } 7206fda04e7SJoel Fernandes } 7216fda04e7SJoel Fernandes 7226fda04e7SJoel Fernandes /// BIOS Image structure containing various headers and reference fields to all BIOS images. 7236fda04e7SJoel Fernandes /// 7246fda04e7SJoel Fernandes /// Each BiosImage type has a BiosImageBase type along with other image-specific fields. Note that 7256fda04e7SJoel Fernandes /// Rust favors composition of types over inheritance. 7266fda04e7SJoel Fernandes #[derive(Debug)] 7276fda04e7SJoel Fernandes #[expect(dead_code)] 7286fda04e7SJoel Fernandes struct BiosImageBase { 7296fda04e7SJoel Fernandes /// PCI ROM Expansion Header 7306fda04e7SJoel Fernandes rom_header: PciRomHeader, 7316fda04e7SJoel Fernandes /// PCI Data Structure 7326fda04e7SJoel Fernandes pcir: PcirStruct, 7336fda04e7SJoel Fernandes /// NVIDIA PCI Data Extension (optional) 7346fda04e7SJoel Fernandes npde: Option<NpdeStruct>, 7356fda04e7SJoel Fernandes /// Image data (includes ROM header and PCIR) 7366fda04e7SJoel Fernandes data: KVec<u8>, 7376fda04e7SJoel Fernandes } 7386fda04e7SJoel Fernandes 7396fda04e7SJoel Fernandes impl BiosImageBase { 7406fda04e7SJoel Fernandes fn into_image(self) -> Result<BiosImage> { 7416fda04e7SJoel Fernandes BiosImage::try_from(self) 7426fda04e7SJoel Fernandes } 7436fda04e7SJoel Fernandes 7446fda04e7SJoel Fernandes /// Creates a new BiosImageBase from raw byte data. 7456fda04e7SJoel Fernandes fn new(pdev: &pci::Device, data: &[u8]) -> Result<Self> { 7466fda04e7SJoel Fernandes // Ensure we have enough data for the ROM header. 7476fda04e7SJoel Fernandes if data.len() < 26 { 7486fda04e7SJoel Fernandes dev_err!(pdev.as_ref(), "Not enough data for ROM header\n"); 7496fda04e7SJoel Fernandes return Err(EINVAL); 7506fda04e7SJoel Fernandes } 7516fda04e7SJoel Fernandes 7526fda04e7SJoel Fernandes // Parse the ROM header. 7536fda04e7SJoel Fernandes let rom_header = PciRomHeader::new(pdev, &data[0..26]) 7546fda04e7SJoel Fernandes .inspect_err(|e| dev_err!(pdev.as_ref(), "Failed to create PciRomHeader: {:?}\n", e))?; 7556fda04e7SJoel Fernandes 7566fda04e7SJoel Fernandes // Get the PCI Data Structure using the pointer from the ROM header. 7576fda04e7SJoel Fernandes let pcir_offset = rom_header.pci_data_struct_offset as usize; 7586fda04e7SJoel Fernandes let pcir_data = data 7596fda04e7SJoel Fernandes .get(pcir_offset..pcir_offset + core::mem::size_of::<PcirStruct>()) 7606fda04e7SJoel Fernandes .ok_or(EINVAL) 7616fda04e7SJoel Fernandes .inspect_err(|_| { 7626fda04e7SJoel Fernandes dev_err!( 7636fda04e7SJoel Fernandes pdev.as_ref(), 7646fda04e7SJoel Fernandes "PCIR offset {:#x} out of bounds (data length: {})\n", 7656fda04e7SJoel Fernandes pcir_offset, 7666fda04e7SJoel Fernandes data.len() 7676fda04e7SJoel Fernandes ); 7686fda04e7SJoel Fernandes dev_err!( 7696fda04e7SJoel Fernandes pdev.as_ref(), 7706fda04e7SJoel Fernandes "Consider reading more data for construction of BiosImage\n" 7716fda04e7SJoel Fernandes ); 7726fda04e7SJoel Fernandes })?; 7736fda04e7SJoel Fernandes 7746fda04e7SJoel Fernandes let pcir = PcirStruct::new(pdev, pcir_data) 7756fda04e7SJoel Fernandes .inspect_err(|e| dev_err!(pdev.as_ref(), "Failed to create PcirStruct: {:?}\n", e))?; 7766fda04e7SJoel Fernandes 7776fda04e7SJoel Fernandes // Look for NPDE structure if this is not an NBSI image (type != 0x70). 7786fda04e7SJoel Fernandes let npde = NpdeStruct::find_in_data(pdev, data, &rom_header, &pcir); 7796fda04e7SJoel Fernandes 7806fda04e7SJoel Fernandes // Create a copy of the data. 7816fda04e7SJoel Fernandes let mut data_copy = KVec::new(); 7826fda04e7SJoel Fernandes data_copy.extend_from_slice(data, GFP_KERNEL)?; 7836fda04e7SJoel Fernandes 7846fda04e7SJoel Fernandes Ok(BiosImageBase { 7856fda04e7SJoel Fernandes rom_header, 7866fda04e7SJoel Fernandes pcir, 7876fda04e7SJoel Fernandes npde, 7886fda04e7SJoel Fernandes data: data_copy, 7896fda04e7SJoel Fernandes }) 7906fda04e7SJoel Fernandes } 7916fda04e7SJoel Fernandes } 792*dc70c6aeSJoel Fernandes 793*dc70c6aeSJoel Fernandes impl PciAtBiosImage { 794*dc70c6aeSJoel Fernandes /// Find a byte pattern in a slice. 795*dc70c6aeSJoel Fernandes fn find_byte_pattern(haystack: &[u8], needle: &[u8]) -> Result<usize> { 796*dc70c6aeSJoel Fernandes haystack 797*dc70c6aeSJoel Fernandes .windows(needle.len()) 798*dc70c6aeSJoel Fernandes .position(|window| window == needle) 799*dc70c6aeSJoel Fernandes .ok_or(EINVAL) 800*dc70c6aeSJoel Fernandes } 801*dc70c6aeSJoel Fernandes 802*dc70c6aeSJoel Fernandes /// Find the BIT header in the [`PciAtBiosImage`]. 803*dc70c6aeSJoel Fernandes fn find_bit_header(data: &[u8]) -> Result<(BitHeader, usize)> { 804*dc70c6aeSJoel Fernandes let bit_pattern = [0xff, 0xb8, b'B', b'I', b'T', 0x00]; 805*dc70c6aeSJoel Fernandes let bit_offset = Self::find_byte_pattern(data, &bit_pattern)?; 806*dc70c6aeSJoel Fernandes let bit_header = BitHeader::new(&data[bit_offset..])?; 807*dc70c6aeSJoel Fernandes 808*dc70c6aeSJoel Fernandes Ok((bit_header, bit_offset)) 809*dc70c6aeSJoel Fernandes } 810*dc70c6aeSJoel Fernandes 811*dc70c6aeSJoel Fernandes /// Get a BIT token entry from the BIT table in the [`PciAtBiosImage`] 812*dc70c6aeSJoel Fernandes fn get_bit_token(&self, token_id: u8) -> Result<BitToken> { 813*dc70c6aeSJoel Fernandes BitToken::from_id(self, token_id) 814*dc70c6aeSJoel Fernandes } 815*dc70c6aeSJoel Fernandes 816*dc70c6aeSJoel Fernandes /// Find the Falcon data pointer structure in the [`PciAtBiosImage`]. 817*dc70c6aeSJoel Fernandes /// 818*dc70c6aeSJoel Fernandes /// This is just a 4 byte structure that contains a pointer to the Falcon data in the FWSEC 819*dc70c6aeSJoel Fernandes /// image. 820*dc70c6aeSJoel Fernandes fn falcon_data_ptr(&self, pdev: &pci::Device) -> Result<u32> { 821*dc70c6aeSJoel Fernandes let token = self.get_bit_token(BIT_TOKEN_ID_FALCON_DATA)?; 822*dc70c6aeSJoel Fernandes 823*dc70c6aeSJoel Fernandes // Make sure we don't go out of bounds 824*dc70c6aeSJoel Fernandes if token.data_offset as usize + 4 > self.base.data.len() { 825*dc70c6aeSJoel Fernandes return Err(EINVAL); 826*dc70c6aeSJoel Fernandes } 827*dc70c6aeSJoel Fernandes 828*dc70c6aeSJoel Fernandes // read the 4 bytes at the offset specified in the token 829*dc70c6aeSJoel Fernandes let offset = token.data_offset as usize; 830*dc70c6aeSJoel Fernandes let bytes: [u8; 4] = self.base.data[offset..offset + 4].try_into().map_err(|_| { 831*dc70c6aeSJoel Fernandes dev_err!(pdev.as_ref(), "Failed to convert data slice to array"); 832*dc70c6aeSJoel Fernandes EINVAL 833*dc70c6aeSJoel Fernandes })?; 834*dc70c6aeSJoel Fernandes 835*dc70c6aeSJoel Fernandes let data_ptr = u32::from_le_bytes(bytes); 836*dc70c6aeSJoel Fernandes 837*dc70c6aeSJoel Fernandes if (data_ptr as usize) < self.base.data.len() { 838*dc70c6aeSJoel Fernandes dev_err!(pdev.as_ref(), "Falcon data pointer out of bounds\n"); 839*dc70c6aeSJoel Fernandes return Err(EINVAL); 840*dc70c6aeSJoel Fernandes } 841*dc70c6aeSJoel Fernandes 842*dc70c6aeSJoel Fernandes Ok(data_ptr) 843*dc70c6aeSJoel Fernandes } 844*dc70c6aeSJoel Fernandes } 845*dc70c6aeSJoel Fernandes 846*dc70c6aeSJoel Fernandes impl TryFrom<BiosImageBase> for PciAtBiosImage { 847*dc70c6aeSJoel Fernandes type Error = Error; 848*dc70c6aeSJoel Fernandes 849*dc70c6aeSJoel Fernandes fn try_from(base: BiosImageBase) -> Result<Self> { 850*dc70c6aeSJoel Fernandes let data_slice = &base.data; 851*dc70c6aeSJoel Fernandes let (bit_header, bit_offset) = PciAtBiosImage::find_bit_header(data_slice)?; 852*dc70c6aeSJoel Fernandes 853*dc70c6aeSJoel Fernandes Ok(PciAtBiosImage { 854*dc70c6aeSJoel Fernandes base, 855*dc70c6aeSJoel Fernandes bit_header, 856*dc70c6aeSJoel Fernandes bit_offset, 857*dc70c6aeSJoel Fernandes }) 858*dc70c6aeSJoel Fernandes } 859*dc70c6aeSJoel Fernandes } 860