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