16fda04e7SJoel Fernandes // SPDX-License-Identifier: GPL-2.0 26fda04e7SJoel Fernandes 36fda04e7SJoel Fernandes //! VBIOS extraction and parsing. 46fda04e7SJoel Fernandes 56fda04e7SJoel Fernandes use crate::driver::Bar0; 631f0feefSAlexandre Courbot use crate::firmware::fwsec::Bcrt30Rsa3kSignature; 747c4846eSJoel Fernandes use crate::firmware::FalconUCodeDescV3; 86fda04e7SJoel Fernandes use core::convert::TryFrom; 947c4846eSJoel Fernandes use kernel::device; 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> { new(pdev: &'a pci::Device, bar0: &'a Bar0) -> Result<Self>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. read_more(&mut self, len: usize) -> Result606fda04e7SJoel 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. read_more_at_offset(&mut self, offset: usize, len: usize) -> Result906fda04e7SJoel 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. read_bios_image_at_offset( &mut self, offset: usize, len: usize, context: &str, ) -> Result<BiosImage>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. next(&mut self) -> Option<Self::Item>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; 1803606620bSAlexandre Courbot // TODO[NUMM]: 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. new(pdev: &pci::Device, bar0: &Bar0) -> Result<Vbios>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; 19847c4846eSJoel Fernandes let mut first_fwsec_image: Option<FwSecBiosBuilder> = None; 19947c4846eSJoel Fernandes let mut second_fwsec_image: Option<FwSecBiosBuilder> = 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. 23347c4846eSJoel Fernandes if let (Some(mut second), Some(first), Some(pci_at)) = 2346fda04e7SJoel Fernandes (second_fwsec_image, first_fwsec_image, pci_at_image) 2356fda04e7SJoel Fernandes { 23647c4846eSJoel Fernandes second 23747c4846eSJoel Fernandes .setup_falcon_data(pdev, &pci_at, &first) 23847c4846eSJoel Fernandes .inspect_err(|e| dev_err!(pdev.as_ref(), "Falcon data setup failed: {:?}\n", e))?; 2396fda04e7SJoel Fernandes Ok(Vbios { 24047c4846eSJoel Fernandes fwsec_image: second.build(pdev)?, 2416fda04e7SJoel Fernandes }) 2426fda04e7SJoel Fernandes } else { 2436fda04e7SJoel Fernandes dev_err!( 2446fda04e7SJoel Fernandes pdev.as_ref(), 2456fda04e7SJoel Fernandes "Missing required images for falcon data setup, skipping\n" 2466fda04e7SJoel Fernandes ); 2476fda04e7SJoel Fernandes Err(EINVAL) 2486fda04e7SJoel Fernandes } 2496fda04e7SJoel Fernandes } 25047c4846eSJoel Fernandes fwsec_image(&self) -> &FwSecBiosImage25147c4846eSJoel Fernandes pub(crate) fn fwsec_image(&self) -> &FwSecBiosImage { 25247c4846eSJoel Fernandes &self.fwsec_image 25347c4846eSJoel Fernandes } 2546fda04e7SJoel Fernandes } 2556fda04e7SJoel Fernandes 2566fda04e7SJoel Fernandes /// PCI Data Structure as defined in PCI Firmware Specification 2576fda04e7SJoel Fernandes #[derive(Debug, Clone)] 2586fda04e7SJoel Fernandes #[repr(C)] 2596fda04e7SJoel Fernandes struct PcirStruct { 2606fda04e7SJoel Fernandes /// PCI Data Structure signature ("PCIR" or "NPDS") 2616fda04e7SJoel Fernandes signature: [u8; 4], 2626fda04e7SJoel Fernandes /// PCI Vendor ID (e.g., 0x10DE for NVIDIA) 2636fda04e7SJoel Fernandes vendor_id: u16, 2646fda04e7SJoel Fernandes /// PCI Device ID 2656fda04e7SJoel Fernandes device_id: u16, 2666fda04e7SJoel Fernandes /// Device List Pointer 2676fda04e7SJoel Fernandes device_list_ptr: u16, 2686fda04e7SJoel Fernandes /// PCI Data Structure Length 2696fda04e7SJoel Fernandes pci_data_struct_len: u16, 2706fda04e7SJoel Fernandes /// PCI Data Structure Revision 2716fda04e7SJoel Fernandes pci_data_struct_rev: u8, 2726fda04e7SJoel Fernandes /// Class code (3 bytes, 0x03 for display controller) 2736fda04e7SJoel Fernandes class_code: [u8; 3], 2746fda04e7SJoel Fernandes /// Size of this image in 512-byte blocks 2756fda04e7SJoel Fernandes image_len: u16, 2766fda04e7SJoel Fernandes /// Revision Level of the Vendor's ROM 2776fda04e7SJoel Fernandes vendor_rom_rev: u16, 2786fda04e7SJoel Fernandes /// ROM image type (0x00 = PC-AT compatible, 0x03 = EFI, 0x70 = NBSI) 2796fda04e7SJoel Fernandes code_type: u8, 2806fda04e7SJoel Fernandes /// Last image indicator (0x00 = Not last image, 0x80 = Last image) 2816fda04e7SJoel Fernandes last_image: u8, 2826fda04e7SJoel Fernandes /// Maximum Run-time Image Length (units of 512 bytes) 2836fda04e7SJoel Fernandes max_runtime_image_len: u16, 2846fda04e7SJoel Fernandes } 2856fda04e7SJoel Fernandes 2866fda04e7SJoel Fernandes impl PcirStruct { new(pdev: &pci::Device, data: &[u8]) -> Result<Self>2876fda04e7SJoel Fernandes fn new(pdev: &pci::Device, data: &[u8]) -> Result<Self> { 2886fda04e7SJoel Fernandes if data.len() < core::mem::size_of::<PcirStruct>() { 2896fda04e7SJoel Fernandes dev_err!(pdev.as_ref(), "Not enough data for PcirStruct\n"); 2906fda04e7SJoel Fernandes return Err(EINVAL); 2916fda04e7SJoel Fernandes } 2926fda04e7SJoel Fernandes 2936fda04e7SJoel Fernandes let mut signature = [0u8; 4]; 2946fda04e7SJoel Fernandes signature.copy_from_slice(&data[0..4]); 2956fda04e7SJoel Fernandes 2966fda04e7SJoel Fernandes // Signature should be "PCIR" (0x52494350) or "NPDS" (0x5344504e). 2976fda04e7SJoel Fernandes if &signature != b"PCIR" && &signature != b"NPDS" { 2986fda04e7SJoel Fernandes dev_err!( 2996fda04e7SJoel Fernandes pdev.as_ref(), 3006fda04e7SJoel Fernandes "Invalid signature for PcirStruct: {:?}\n", 3016fda04e7SJoel Fernandes signature 3026fda04e7SJoel Fernandes ); 3036fda04e7SJoel Fernandes return Err(EINVAL); 3046fda04e7SJoel Fernandes } 3056fda04e7SJoel Fernandes 3066fda04e7SJoel Fernandes let mut class_code = [0u8; 3]; 3076fda04e7SJoel Fernandes class_code.copy_from_slice(&data[13..16]); 3086fda04e7SJoel Fernandes 3096fda04e7SJoel Fernandes let image_len = u16::from_le_bytes([data[16], data[17]]); 3106fda04e7SJoel Fernandes if image_len == 0 { 3116fda04e7SJoel Fernandes dev_err!(pdev.as_ref(), "Invalid image length: 0\n"); 3126fda04e7SJoel Fernandes return Err(EINVAL); 3136fda04e7SJoel Fernandes } 3146fda04e7SJoel Fernandes 3156fda04e7SJoel Fernandes Ok(PcirStruct { 3166fda04e7SJoel Fernandes signature, 3176fda04e7SJoel Fernandes vendor_id: u16::from_le_bytes([data[4], data[5]]), 3186fda04e7SJoel Fernandes device_id: u16::from_le_bytes([data[6], data[7]]), 3196fda04e7SJoel Fernandes device_list_ptr: u16::from_le_bytes([data[8], data[9]]), 3206fda04e7SJoel Fernandes pci_data_struct_len: u16::from_le_bytes([data[10], data[11]]), 3216fda04e7SJoel Fernandes pci_data_struct_rev: data[12], 3226fda04e7SJoel Fernandes class_code, 3236fda04e7SJoel Fernandes image_len, 3246fda04e7SJoel Fernandes vendor_rom_rev: u16::from_le_bytes([data[18], data[19]]), 3256fda04e7SJoel Fernandes code_type: data[20], 3266fda04e7SJoel Fernandes last_image: data[21], 3276fda04e7SJoel Fernandes max_runtime_image_len: u16::from_le_bytes([data[22], data[23]]), 3286fda04e7SJoel Fernandes }) 3296fda04e7SJoel Fernandes } 3306fda04e7SJoel Fernandes 3316fda04e7SJoel Fernandes /// Check if this is the last image in the ROM. is_last(&self) -> bool3326fda04e7SJoel Fernandes fn is_last(&self) -> bool { 3336fda04e7SJoel Fernandes self.last_image & LAST_IMAGE_BIT_MASK != 0 3346fda04e7SJoel Fernandes } 3356fda04e7SJoel Fernandes 3366fda04e7SJoel Fernandes /// Calculate image size in bytes from 512-byte blocks. image_size_bytes(&self) -> usize3376fda04e7SJoel Fernandes fn image_size_bytes(&self) -> usize { 3386fda04e7SJoel Fernandes self.image_len as usize * 512 3396fda04e7SJoel Fernandes } 3406fda04e7SJoel Fernandes } 3416fda04e7SJoel Fernandes 342dc70c6aeSJoel Fernandes /// BIOS Information Table (BIT) Header. 343dc70c6aeSJoel Fernandes /// 344dc70c6aeSJoel Fernandes /// This is the head of the BIT table, that is used to locate the Falcon data. The BIT table (with 345dc70c6aeSJoel Fernandes /// its header) is in the [`PciAtBiosImage`] and the falcon data it is pointing to is in the 346dc70c6aeSJoel Fernandes /// [`FwSecBiosImage`]. 347dc70c6aeSJoel Fernandes #[derive(Debug, Clone, Copy)] 348dc70c6aeSJoel Fernandes #[expect(dead_code)] 349dc70c6aeSJoel Fernandes struct BitHeader { 350dc70c6aeSJoel Fernandes /// 0h: BIT Header Identifier (BMP=0x7FFF/BIT=0xB8FF) 351dc70c6aeSJoel Fernandes id: u16, 352dc70c6aeSJoel Fernandes /// 2h: BIT Header Signature ("BIT\0") 353dc70c6aeSJoel Fernandes signature: [u8; 4], 354dc70c6aeSJoel Fernandes /// 6h: Binary Coded Decimal Version, ex: 0x0100 is 1.00. 355dc70c6aeSJoel Fernandes bcd_version: u16, 356dc70c6aeSJoel Fernandes /// 8h: Size of BIT Header (in bytes) 357dc70c6aeSJoel Fernandes header_size: u8, 358dc70c6aeSJoel Fernandes /// 9h: Size of BIT Tokens (in bytes) 359dc70c6aeSJoel Fernandes token_size: u8, 360dc70c6aeSJoel Fernandes /// 10h: Number of token entries that follow 361dc70c6aeSJoel Fernandes token_entries: u8, 362dc70c6aeSJoel Fernandes /// 11h: BIT Header Checksum 363dc70c6aeSJoel Fernandes checksum: u8, 364dc70c6aeSJoel Fernandes } 365dc70c6aeSJoel Fernandes 366dc70c6aeSJoel Fernandes impl BitHeader { new(data: &[u8]) -> Result<Self>367dc70c6aeSJoel Fernandes fn new(data: &[u8]) -> Result<Self> { 368dc70c6aeSJoel Fernandes if data.len() < 12 { 369dc70c6aeSJoel Fernandes return Err(EINVAL); 370dc70c6aeSJoel Fernandes } 371dc70c6aeSJoel Fernandes 372dc70c6aeSJoel Fernandes let mut signature = [0u8; 4]; 373dc70c6aeSJoel Fernandes signature.copy_from_slice(&data[2..6]); 374dc70c6aeSJoel Fernandes 375dc70c6aeSJoel Fernandes // Check header ID and signature 376dc70c6aeSJoel Fernandes let id = u16::from_le_bytes([data[0], data[1]]); 377dc70c6aeSJoel Fernandes if id != 0xB8FF || &signature != b"BIT\0" { 378dc70c6aeSJoel Fernandes return Err(EINVAL); 379dc70c6aeSJoel Fernandes } 380dc70c6aeSJoel Fernandes 381dc70c6aeSJoel Fernandes Ok(BitHeader { 382dc70c6aeSJoel Fernandes id, 383dc70c6aeSJoel Fernandes signature, 384dc70c6aeSJoel Fernandes bcd_version: u16::from_le_bytes([data[6], data[7]]), 385dc70c6aeSJoel Fernandes header_size: data[8], 386dc70c6aeSJoel Fernandes token_size: data[9], 387dc70c6aeSJoel Fernandes token_entries: data[10], 388dc70c6aeSJoel Fernandes checksum: data[11], 389dc70c6aeSJoel Fernandes }) 390dc70c6aeSJoel Fernandes } 391dc70c6aeSJoel Fernandes } 392dc70c6aeSJoel Fernandes 393dc70c6aeSJoel Fernandes /// BIT Token Entry: Records in the BIT table followed by the BIT header. 394dc70c6aeSJoel Fernandes #[derive(Debug, Clone, Copy)] 395dc70c6aeSJoel Fernandes #[expect(dead_code)] 396dc70c6aeSJoel Fernandes struct BitToken { 397dc70c6aeSJoel Fernandes /// 00h: Token identifier 398dc70c6aeSJoel Fernandes id: u8, 399dc70c6aeSJoel Fernandes /// 01h: Version of the token data 400dc70c6aeSJoel Fernandes data_version: u8, 401dc70c6aeSJoel Fernandes /// 02h: Size of token data in bytes 402dc70c6aeSJoel Fernandes data_size: u16, 403dc70c6aeSJoel Fernandes /// 04h: Offset to the token data 404dc70c6aeSJoel Fernandes data_offset: u16, 405dc70c6aeSJoel Fernandes } 406dc70c6aeSJoel Fernandes 407dc70c6aeSJoel Fernandes // Define the token ID for the Falcon data 408dc70c6aeSJoel Fernandes const BIT_TOKEN_ID_FALCON_DATA: u8 = 0x70; 409dc70c6aeSJoel Fernandes 410dc70c6aeSJoel Fernandes impl BitToken { 411dc70c6aeSJoel Fernandes /// Find a BIT token entry by BIT ID in a PciAtBiosImage from_id(image: &PciAtBiosImage, token_id: u8) -> Result<Self>412dc70c6aeSJoel Fernandes fn from_id(image: &PciAtBiosImage, token_id: u8) -> Result<Self> { 413dc70c6aeSJoel Fernandes let header = &image.bit_header; 414dc70c6aeSJoel Fernandes 415dc70c6aeSJoel Fernandes // Offset to the first token entry 416dc70c6aeSJoel Fernandes let tokens_start = image.bit_offset + header.header_size as usize; 417dc70c6aeSJoel Fernandes 418dc70c6aeSJoel Fernandes for i in 0..header.token_entries as usize { 419dc70c6aeSJoel Fernandes let entry_offset = tokens_start + (i * header.token_size as usize); 420dc70c6aeSJoel Fernandes 421dc70c6aeSJoel Fernandes // Make sure we don't go out of bounds 422dc70c6aeSJoel Fernandes if entry_offset + header.token_size as usize > image.base.data.len() { 423dc70c6aeSJoel Fernandes return Err(EINVAL); 424dc70c6aeSJoel Fernandes } 425dc70c6aeSJoel Fernandes 426dc70c6aeSJoel Fernandes // Check if this token has the requested ID 427dc70c6aeSJoel Fernandes if image.base.data[entry_offset] == token_id { 428dc70c6aeSJoel Fernandes return Ok(BitToken { 429dc70c6aeSJoel Fernandes id: image.base.data[entry_offset], 430dc70c6aeSJoel Fernandes data_version: image.base.data[entry_offset + 1], 431dc70c6aeSJoel Fernandes data_size: u16::from_le_bytes([ 432dc70c6aeSJoel Fernandes image.base.data[entry_offset + 2], 433dc70c6aeSJoel Fernandes image.base.data[entry_offset + 3], 434dc70c6aeSJoel Fernandes ]), 435dc70c6aeSJoel Fernandes data_offset: u16::from_le_bytes([ 436dc70c6aeSJoel Fernandes image.base.data[entry_offset + 4], 437dc70c6aeSJoel Fernandes image.base.data[entry_offset + 5], 438dc70c6aeSJoel Fernandes ]), 439dc70c6aeSJoel Fernandes }); 440dc70c6aeSJoel Fernandes } 441dc70c6aeSJoel Fernandes } 442dc70c6aeSJoel Fernandes 443dc70c6aeSJoel Fernandes // Token not found 444dc70c6aeSJoel Fernandes Err(ENOENT) 445dc70c6aeSJoel Fernandes } 446dc70c6aeSJoel Fernandes } 447dc70c6aeSJoel Fernandes 4486fda04e7SJoel Fernandes /// PCI ROM Expansion Header as defined in PCI Firmware Specification. 4496fda04e7SJoel Fernandes /// 4506fda04e7SJoel Fernandes /// This is header is at the beginning of every image in the set of images in the ROM. It contains 4516fda04e7SJoel Fernandes /// a pointer to the PCI Data Structure which describes the image. For "NBSI" images (NoteBook 4526fda04e7SJoel Fernandes /// System Information), the ROM header deviates from the standard and contains an offset to the 4536fda04e7SJoel Fernandes /// NBSI image however we do not yet parse that in this module and keep it for future reference. 4546fda04e7SJoel Fernandes #[derive(Debug, Clone, Copy)] 4556fda04e7SJoel Fernandes #[expect(dead_code)] 4566fda04e7SJoel Fernandes struct PciRomHeader { 4576fda04e7SJoel Fernandes /// 00h: Signature (0xAA55) 4586fda04e7SJoel Fernandes signature: u16, 4596fda04e7SJoel Fernandes /// 02h: Reserved bytes for processor architecture unique data (20 bytes) 4606fda04e7SJoel Fernandes reserved: [u8; 20], 4616fda04e7SJoel Fernandes /// 16h: NBSI Data Offset (NBSI-specific, offset from header to NBSI image) 4626fda04e7SJoel Fernandes nbsi_data_offset: Option<u16>, 4636fda04e7SJoel Fernandes /// 18h: Pointer to PCI Data Structure (offset from start of ROM image) 4646fda04e7SJoel Fernandes pci_data_struct_offset: u16, 4656fda04e7SJoel Fernandes /// 1Ah: Size of block (this is NBSI-specific) 4666fda04e7SJoel Fernandes size_of_block: Option<u32>, 4676fda04e7SJoel Fernandes } 4686fda04e7SJoel Fernandes 4696fda04e7SJoel Fernandes impl PciRomHeader { new(pdev: &pci::Device, data: &[u8]) -> Result<Self>4706fda04e7SJoel Fernandes fn new(pdev: &pci::Device, data: &[u8]) -> Result<Self> { 4716fda04e7SJoel Fernandes if data.len() < 26 { 4726fda04e7SJoel Fernandes // Need at least 26 bytes to read pciDataStrucPtr and sizeOfBlock. 4736fda04e7SJoel Fernandes return Err(EINVAL); 4746fda04e7SJoel Fernandes } 4756fda04e7SJoel Fernandes 4766fda04e7SJoel Fernandes let signature = u16::from_le_bytes([data[0], data[1]]); 4776fda04e7SJoel Fernandes 4786fda04e7SJoel Fernandes // Check for valid ROM signatures. 4796fda04e7SJoel Fernandes match signature { 4806fda04e7SJoel Fernandes 0xAA55 | 0xBB77 | 0x4E56 => {} 4816fda04e7SJoel Fernandes _ => { 4826fda04e7SJoel Fernandes dev_err!(pdev.as_ref(), "ROM signature unknown {:#x}\n", signature); 4836fda04e7SJoel Fernandes return Err(EINVAL); 4846fda04e7SJoel Fernandes } 4856fda04e7SJoel Fernandes } 4866fda04e7SJoel Fernandes 4876fda04e7SJoel Fernandes // Read the pointer to the PCI Data Structure at offset 0x18. 4886fda04e7SJoel Fernandes let pci_data_struct_ptr = u16::from_le_bytes([data[24], data[25]]); 4896fda04e7SJoel Fernandes 4906fda04e7SJoel Fernandes // Try to read optional fields if enough data. 4916fda04e7SJoel Fernandes let mut size_of_block = None; 4926fda04e7SJoel Fernandes let mut nbsi_data_offset = None; 4936fda04e7SJoel Fernandes 4946fda04e7SJoel Fernandes if data.len() >= 30 { 4956fda04e7SJoel Fernandes // Read size_of_block at offset 0x1A. 4966fda04e7SJoel Fernandes size_of_block = Some( 49743ad65ecSDanilo Krummrich u32::from(data[29]) << 24 49843ad65ecSDanilo Krummrich | u32::from(data[28]) << 16 49943ad65ecSDanilo Krummrich | u32::from(data[27]) << 8 50043ad65ecSDanilo Krummrich | u32::from(data[26]), 5016fda04e7SJoel Fernandes ); 5026fda04e7SJoel Fernandes } 5036fda04e7SJoel Fernandes 5046fda04e7SJoel Fernandes // For NBSI images, try to read the nbsiDataOffset at offset 0x16. 5056fda04e7SJoel Fernandes if data.len() >= 24 { 5066fda04e7SJoel Fernandes nbsi_data_offset = Some(u16::from_le_bytes([data[22], data[23]])); 5076fda04e7SJoel Fernandes } 5086fda04e7SJoel Fernandes 5096fda04e7SJoel Fernandes Ok(PciRomHeader { 5106fda04e7SJoel Fernandes signature, 5116fda04e7SJoel Fernandes reserved: [0u8; 20], 5126fda04e7SJoel Fernandes pci_data_struct_offset: pci_data_struct_ptr, 5136fda04e7SJoel Fernandes size_of_block, 5146fda04e7SJoel Fernandes nbsi_data_offset, 5156fda04e7SJoel Fernandes }) 5166fda04e7SJoel Fernandes } 5176fda04e7SJoel Fernandes } 5186fda04e7SJoel Fernandes 5196fda04e7SJoel Fernandes /// NVIDIA PCI Data Extension Structure. 5206fda04e7SJoel Fernandes /// 5216fda04e7SJoel Fernandes /// This is similar to the PCI Data Structure, but is Nvidia-specific and is placed right after the 5226fda04e7SJoel Fernandes /// PCI Data Structure. It contains some fields that are redundant with the PCI Data Structure, but 5236fda04e7SJoel Fernandes /// are needed for traversing the BIOS images. It is expected to be present in all BIOS images 5246fda04e7SJoel Fernandes /// except for NBSI images. 5256fda04e7SJoel Fernandes #[derive(Debug, Clone)] 5266fda04e7SJoel Fernandes #[repr(C)] 5276fda04e7SJoel Fernandes struct NpdeStruct { 5286fda04e7SJoel Fernandes /// 00h: Signature ("NPDE") 5296fda04e7SJoel Fernandes signature: [u8; 4], 5306fda04e7SJoel Fernandes /// 04h: NVIDIA PCI Data Extension Revision 5316fda04e7SJoel Fernandes npci_data_ext_rev: u16, 5326fda04e7SJoel Fernandes /// 06h: NVIDIA PCI Data Extension Length 5336fda04e7SJoel Fernandes npci_data_ext_len: u16, 5346fda04e7SJoel Fernandes /// 08h: Sub-image Length (in 512-byte units) 5356fda04e7SJoel Fernandes subimage_len: u16, 5366fda04e7SJoel Fernandes /// 0Ah: Last image indicator flag 5376fda04e7SJoel Fernandes last_image: u8, 5386fda04e7SJoel Fernandes } 5396fda04e7SJoel Fernandes 5406fda04e7SJoel Fernandes impl NpdeStruct { new(pdev: &pci::Device, data: &[u8]) -> Option<Self>5416fda04e7SJoel Fernandes fn new(pdev: &pci::Device, data: &[u8]) -> Option<Self> { 5426fda04e7SJoel Fernandes if data.len() < core::mem::size_of::<Self>() { 5436fda04e7SJoel Fernandes dev_dbg!(pdev.as_ref(), "Not enough data for NpdeStruct\n"); 5446fda04e7SJoel Fernandes return None; 5456fda04e7SJoel Fernandes } 5466fda04e7SJoel Fernandes 5476fda04e7SJoel Fernandes let mut signature = [0u8; 4]; 5486fda04e7SJoel Fernandes signature.copy_from_slice(&data[0..4]); 5496fda04e7SJoel Fernandes 5506fda04e7SJoel Fernandes // Signature should be "NPDE" (0x4544504E). 5516fda04e7SJoel Fernandes if &signature != b"NPDE" { 5526fda04e7SJoel Fernandes dev_dbg!( 5536fda04e7SJoel Fernandes pdev.as_ref(), 5546fda04e7SJoel Fernandes "Invalid signature for NpdeStruct: {:?}\n", 5556fda04e7SJoel Fernandes signature 5566fda04e7SJoel Fernandes ); 5576fda04e7SJoel Fernandes return None; 5586fda04e7SJoel Fernandes } 5596fda04e7SJoel Fernandes 5606fda04e7SJoel Fernandes let subimage_len = u16::from_le_bytes([data[8], data[9]]); 5616fda04e7SJoel Fernandes if subimage_len == 0 { 5626fda04e7SJoel Fernandes dev_dbg!(pdev.as_ref(), "Invalid subimage length: 0\n"); 5636fda04e7SJoel Fernandes return None; 5646fda04e7SJoel Fernandes } 5656fda04e7SJoel Fernandes 5666fda04e7SJoel Fernandes Some(NpdeStruct { 5676fda04e7SJoel Fernandes signature, 5686fda04e7SJoel Fernandes npci_data_ext_rev: u16::from_le_bytes([data[4], data[5]]), 5696fda04e7SJoel Fernandes npci_data_ext_len: u16::from_le_bytes([data[6], data[7]]), 5706fda04e7SJoel Fernandes subimage_len, 5716fda04e7SJoel Fernandes last_image: data[10], 5726fda04e7SJoel Fernandes }) 5736fda04e7SJoel Fernandes } 5746fda04e7SJoel Fernandes 5756fda04e7SJoel Fernandes /// Check if this is the last image in the ROM. is_last(&self) -> bool5766fda04e7SJoel Fernandes fn is_last(&self) -> bool { 5776fda04e7SJoel Fernandes self.last_image & LAST_IMAGE_BIT_MASK != 0 5786fda04e7SJoel Fernandes } 5796fda04e7SJoel Fernandes 5806fda04e7SJoel Fernandes /// Calculate image size in bytes from 512-byte blocks. image_size_bytes(&self) -> usize5816fda04e7SJoel Fernandes fn image_size_bytes(&self) -> usize { 5826fda04e7SJoel Fernandes self.subimage_len as usize * 512 5836fda04e7SJoel Fernandes } 5846fda04e7SJoel Fernandes 5856fda04e7SJoel Fernandes /// Try to find NPDE in the data, the NPDE is right after the PCIR. find_in_data( pdev: &pci::Device, data: &[u8], rom_header: &PciRomHeader, pcir: &PcirStruct, ) -> Option<Self>5866fda04e7SJoel Fernandes fn find_in_data( 5876fda04e7SJoel Fernandes pdev: &pci::Device, 5886fda04e7SJoel Fernandes data: &[u8], 5896fda04e7SJoel Fernandes rom_header: &PciRomHeader, 5906fda04e7SJoel Fernandes pcir: &PcirStruct, 5916fda04e7SJoel Fernandes ) -> Option<Self> { 5926fda04e7SJoel Fernandes // Calculate the offset where NPDE might be located 5936fda04e7SJoel Fernandes // NPDE should be right after the PCIR structure, aligned to 16 bytes 5946fda04e7SJoel Fernandes let pcir_offset = rom_header.pci_data_struct_offset as usize; 5956fda04e7SJoel Fernandes let npde_start = (pcir_offset + pcir.pci_data_struct_len as usize + 0x0F) & !0x0F; 5966fda04e7SJoel Fernandes 5976fda04e7SJoel Fernandes // Check if we have enough data 5986fda04e7SJoel Fernandes if npde_start + core::mem::size_of::<Self>() > data.len() { 5996fda04e7SJoel Fernandes dev_dbg!(pdev.as_ref(), "Not enough data for NPDE\n"); 6006fda04e7SJoel Fernandes return None; 6016fda04e7SJoel Fernandes } 6026fda04e7SJoel Fernandes 6036fda04e7SJoel Fernandes // Try to create NPDE from the data 6046fda04e7SJoel Fernandes NpdeStruct::new(pdev, &data[npde_start..]) 6056fda04e7SJoel Fernandes } 6066fda04e7SJoel Fernandes } 6076fda04e7SJoel Fernandes 6086fda04e7SJoel Fernandes // Use a macro to implement BiosImage enum and methods. This avoids having to 6096fda04e7SJoel Fernandes // repeat each enum type when implementing functions like base() in BiosImage. 6106fda04e7SJoel Fernandes macro_rules! bios_image { 6116fda04e7SJoel Fernandes ( 6126fda04e7SJoel Fernandes $($variant:ident: $class:ident),* $(,)? 6136fda04e7SJoel Fernandes ) => { 6146fda04e7SJoel Fernandes // BiosImage enum with variants for each image type 6156fda04e7SJoel Fernandes enum BiosImage { 6166fda04e7SJoel Fernandes $($variant($class)),* 6176fda04e7SJoel Fernandes } 6186fda04e7SJoel Fernandes 6196fda04e7SJoel Fernandes impl BiosImage { 6206fda04e7SJoel Fernandes /// Get a reference to the common BIOS image data regardless of type 6216fda04e7SJoel Fernandes fn base(&self) -> &BiosImageBase { 6226fda04e7SJoel Fernandes match self { 6236fda04e7SJoel Fernandes $(Self::$variant(img) => &img.base),* 6246fda04e7SJoel Fernandes } 6256fda04e7SJoel Fernandes } 6266fda04e7SJoel Fernandes 6276fda04e7SJoel Fernandes /// Returns a string representing the type of BIOS image 6286fda04e7SJoel Fernandes fn image_type_str(&self) -> &'static str { 6296fda04e7SJoel Fernandes match self { 6306fda04e7SJoel Fernandes $(Self::$variant(_) => stringify!($variant)),* 6316fda04e7SJoel Fernandes } 6326fda04e7SJoel Fernandes } 6336fda04e7SJoel Fernandes } 6346fda04e7SJoel Fernandes } 6356fda04e7SJoel Fernandes } 6366fda04e7SJoel Fernandes 6376fda04e7SJoel Fernandes impl BiosImage { 6386fda04e7SJoel Fernandes /// Check if this is the last image. is_last(&self) -> bool6396fda04e7SJoel Fernandes fn is_last(&self) -> bool { 6406fda04e7SJoel Fernandes let base = self.base(); 6416fda04e7SJoel Fernandes 6426fda04e7SJoel Fernandes // For NBSI images (type == 0x70), return true as they're 6436fda04e7SJoel Fernandes // considered the last image 6446fda04e7SJoel Fernandes if matches!(self, Self::Nbsi(_)) { 6456fda04e7SJoel Fernandes return true; 6466fda04e7SJoel Fernandes } 6476fda04e7SJoel Fernandes 6486fda04e7SJoel Fernandes // For other image types, check the NPDE first if available 6496fda04e7SJoel Fernandes if let Some(ref npde) = base.npde { 6506fda04e7SJoel Fernandes return npde.is_last(); 6516fda04e7SJoel Fernandes } 6526fda04e7SJoel Fernandes 6536fda04e7SJoel Fernandes // Otherwise, fall back to checking the PCIR last_image flag 6546fda04e7SJoel Fernandes base.pcir.is_last() 6556fda04e7SJoel Fernandes } 6566fda04e7SJoel Fernandes 6576fda04e7SJoel Fernandes /// Get the image size in bytes. image_size_bytes(&self) -> usize6586fda04e7SJoel Fernandes fn image_size_bytes(&self) -> usize { 6596fda04e7SJoel Fernandes let base = self.base(); 6606fda04e7SJoel Fernandes 6616fda04e7SJoel Fernandes // Prefer NPDE image size if available 6626fda04e7SJoel Fernandes if let Some(ref npde) = base.npde { 6636fda04e7SJoel Fernandes return npde.image_size_bytes(); 6646fda04e7SJoel Fernandes } 6656fda04e7SJoel Fernandes 6666fda04e7SJoel Fernandes // Otherwise, fall back to the PCIR image size 6676fda04e7SJoel Fernandes base.pcir.image_size_bytes() 6686fda04e7SJoel Fernandes } 6696fda04e7SJoel Fernandes 6706fda04e7SJoel Fernandes /// Create a [`BiosImageBase`] from a byte slice and convert it to a [`BiosImage`] which 6716fda04e7SJoel Fernandes /// triggers the constructor of the specific BiosImage enum variant. new(pdev: &pci::Device, data: &[u8]) -> Result<Self>6726fda04e7SJoel Fernandes fn new(pdev: &pci::Device, data: &[u8]) -> Result<Self> { 6736fda04e7SJoel Fernandes let base = BiosImageBase::new(pdev, data)?; 6746fda04e7SJoel Fernandes let image = base.into_image().inspect_err(|e| { 6756fda04e7SJoel Fernandes dev_err!(pdev.as_ref(), "Failed to create BiosImage: {:?}\n", e); 6766fda04e7SJoel Fernandes })?; 6776fda04e7SJoel Fernandes 6786fda04e7SJoel Fernandes Ok(image) 6796fda04e7SJoel Fernandes } 6806fda04e7SJoel Fernandes } 6816fda04e7SJoel Fernandes 6826fda04e7SJoel Fernandes bios_image! { 6836fda04e7SJoel Fernandes PciAt: PciAtBiosImage, // PCI-AT compatible BIOS image 6846fda04e7SJoel Fernandes Efi: EfiBiosImage, // EFI (Extensible Firmware Interface) 6856fda04e7SJoel Fernandes Nbsi: NbsiBiosImage, // NBSI (Nvidia Bios System Interface) 68647c4846eSJoel Fernandes FwSec: FwSecBiosBuilder, // FWSEC (Firmware Security) 6876fda04e7SJoel Fernandes } 6886fda04e7SJoel Fernandes 689dc70c6aeSJoel Fernandes /// The PciAt BIOS image is typically the first BIOS image type found in the BIOS image chain. 690dc70c6aeSJoel Fernandes /// 691dc70c6aeSJoel Fernandes /// It contains the BIT header and the BIT tokens. 6926fda04e7SJoel Fernandes struct PciAtBiosImage { 6936fda04e7SJoel Fernandes base: BiosImageBase, 694dc70c6aeSJoel Fernandes bit_header: BitHeader, 695dc70c6aeSJoel Fernandes bit_offset: usize, 6966fda04e7SJoel Fernandes } 6976fda04e7SJoel Fernandes 6986fda04e7SJoel Fernandes struct EfiBiosImage { 6996fda04e7SJoel Fernandes base: BiosImageBase, 7006fda04e7SJoel Fernandes // EFI-specific fields can be added here in the future. 7016fda04e7SJoel Fernandes } 7026fda04e7SJoel Fernandes 7036fda04e7SJoel Fernandes struct NbsiBiosImage { 7046fda04e7SJoel Fernandes base: BiosImageBase, 7056fda04e7SJoel Fernandes // NBSI-specific fields can be added here in the future. 7066fda04e7SJoel Fernandes } 7076fda04e7SJoel Fernandes 70847c4846eSJoel Fernandes struct FwSecBiosBuilder { 7096fda04e7SJoel Fernandes base: BiosImageBase, 71047c4846eSJoel Fernandes /// These are temporary fields that are used during the construction of the 71147c4846eSJoel Fernandes /// [`FwSecBiosBuilder`]. 71247c4846eSJoel Fernandes /// 71347c4846eSJoel Fernandes /// Once FwSecBiosBuilder is constructed, the `falcon_ucode_offset` will be copied into a new 71447c4846eSJoel Fernandes /// [`FwSecBiosImage`]. 71547c4846eSJoel Fernandes /// 71647c4846eSJoel Fernandes /// The offset of the Falcon data from the start of Fwsec image. 71747c4846eSJoel Fernandes falcon_data_offset: Option<usize>, 71847c4846eSJoel Fernandes /// The [`PmuLookupTable`] starts at the offset of the falcon data pointer. 71947c4846eSJoel Fernandes pmu_lookup_table: Option<PmuLookupTable>, 72047c4846eSJoel Fernandes /// The offset of the Falcon ucode. 72147c4846eSJoel Fernandes falcon_ucode_offset: Option<usize>, 72247c4846eSJoel Fernandes } 72347c4846eSJoel Fernandes 72447c4846eSJoel Fernandes /// The [`FwSecBiosImage`] structure contains the PMU table and the Falcon Ucode. 72547c4846eSJoel Fernandes /// 72647c4846eSJoel Fernandes /// The PMU table contains voltage/frequency tables as well as a pointer to the Falcon Ucode. 72747c4846eSJoel Fernandes pub(crate) struct FwSecBiosImage { 72847c4846eSJoel Fernandes base: BiosImageBase, 72947c4846eSJoel Fernandes /// The offset of the Falcon ucode. 73047c4846eSJoel Fernandes falcon_ucode_offset: usize, 7316fda04e7SJoel Fernandes } 7326fda04e7SJoel Fernandes 7336fda04e7SJoel Fernandes // Convert from BiosImageBase to BiosImage 7346fda04e7SJoel Fernandes impl TryFrom<BiosImageBase> for BiosImage { 7356fda04e7SJoel Fernandes type Error = Error; 7366fda04e7SJoel Fernandes try_from(base: BiosImageBase) -> Result<Self>7376fda04e7SJoel Fernandes fn try_from(base: BiosImageBase) -> Result<Self> { 7386fda04e7SJoel Fernandes match base.pcir.code_type { 739dc70c6aeSJoel Fernandes 0x00 => Ok(BiosImage::PciAt(base.try_into()?)), 7406fda04e7SJoel Fernandes 0x03 => Ok(BiosImage::Efi(EfiBiosImage { base })), 7416fda04e7SJoel Fernandes 0x70 => Ok(BiosImage::Nbsi(NbsiBiosImage { base })), 74247c4846eSJoel Fernandes 0xE0 => Ok(BiosImage::FwSec(FwSecBiosBuilder { 74347c4846eSJoel Fernandes base, 74447c4846eSJoel Fernandes falcon_data_offset: None, 74547c4846eSJoel Fernandes pmu_lookup_table: None, 74647c4846eSJoel Fernandes falcon_ucode_offset: None, 74747c4846eSJoel Fernandes })), 7486fda04e7SJoel Fernandes _ => Err(EINVAL), 7496fda04e7SJoel Fernandes } 7506fda04e7SJoel Fernandes } 7516fda04e7SJoel Fernandes } 7526fda04e7SJoel Fernandes 7536fda04e7SJoel Fernandes /// BIOS Image structure containing various headers and reference fields to all BIOS images. 7546fda04e7SJoel Fernandes /// 7556fda04e7SJoel Fernandes /// Each BiosImage type has a BiosImageBase type along with other image-specific fields. Note that 7566fda04e7SJoel Fernandes /// Rust favors composition of types over inheritance. 7576fda04e7SJoel Fernandes #[derive(Debug)] 7586fda04e7SJoel Fernandes #[expect(dead_code)] 7596fda04e7SJoel Fernandes struct BiosImageBase { 7606fda04e7SJoel Fernandes /// PCI ROM Expansion Header 7616fda04e7SJoel Fernandes rom_header: PciRomHeader, 7626fda04e7SJoel Fernandes /// PCI Data Structure 7636fda04e7SJoel Fernandes pcir: PcirStruct, 7646fda04e7SJoel Fernandes /// NVIDIA PCI Data Extension (optional) 7656fda04e7SJoel Fernandes npde: Option<NpdeStruct>, 7666fda04e7SJoel Fernandes /// Image data (includes ROM header and PCIR) 7676fda04e7SJoel Fernandes data: KVec<u8>, 7686fda04e7SJoel Fernandes } 7696fda04e7SJoel Fernandes 7706fda04e7SJoel Fernandes impl BiosImageBase { into_image(self) -> Result<BiosImage>7716fda04e7SJoel Fernandes fn into_image(self) -> Result<BiosImage> { 7726fda04e7SJoel Fernandes BiosImage::try_from(self) 7736fda04e7SJoel Fernandes } 7746fda04e7SJoel Fernandes 7756fda04e7SJoel Fernandes /// Creates a new BiosImageBase from raw byte data. new(pdev: &pci::Device, data: &[u8]) -> Result<Self>7766fda04e7SJoel Fernandes fn new(pdev: &pci::Device, data: &[u8]) -> Result<Self> { 7776fda04e7SJoel Fernandes // Ensure we have enough data for the ROM header. 7786fda04e7SJoel Fernandes if data.len() < 26 { 7796fda04e7SJoel Fernandes dev_err!(pdev.as_ref(), "Not enough data for ROM header\n"); 7806fda04e7SJoel Fernandes return Err(EINVAL); 7816fda04e7SJoel Fernandes } 7826fda04e7SJoel Fernandes 7836fda04e7SJoel Fernandes // Parse the ROM header. 7846fda04e7SJoel Fernandes let rom_header = PciRomHeader::new(pdev, &data[0..26]) 7856fda04e7SJoel Fernandes .inspect_err(|e| dev_err!(pdev.as_ref(), "Failed to create PciRomHeader: {:?}\n", e))?; 7866fda04e7SJoel Fernandes 7876fda04e7SJoel Fernandes // Get the PCI Data Structure using the pointer from the ROM header. 7886fda04e7SJoel Fernandes let pcir_offset = rom_header.pci_data_struct_offset as usize; 7896fda04e7SJoel Fernandes let pcir_data = data 7906fda04e7SJoel Fernandes .get(pcir_offset..pcir_offset + core::mem::size_of::<PcirStruct>()) 7916fda04e7SJoel Fernandes .ok_or(EINVAL) 7926fda04e7SJoel Fernandes .inspect_err(|_| { 7936fda04e7SJoel Fernandes dev_err!( 7946fda04e7SJoel Fernandes pdev.as_ref(), 7956fda04e7SJoel Fernandes "PCIR offset {:#x} out of bounds (data length: {})\n", 7966fda04e7SJoel Fernandes pcir_offset, 7976fda04e7SJoel Fernandes data.len() 7986fda04e7SJoel Fernandes ); 7996fda04e7SJoel Fernandes dev_err!( 8006fda04e7SJoel Fernandes pdev.as_ref(), 8016fda04e7SJoel Fernandes "Consider reading more data for construction of BiosImage\n" 8026fda04e7SJoel Fernandes ); 8036fda04e7SJoel Fernandes })?; 8046fda04e7SJoel Fernandes 8056fda04e7SJoel Fernandes let pcir = PcirStruct::new(pdev, pcir_data) 8066fda04e7SJoel Fernandes .inspect_err(|e| dev_err!(pdev.as_ref(), "Failed to create PcirStruct: {:?}\n", e))?; 8076fda04e7SJoel Fernandes 8086fda04e7SJoel Fernandes // Look for NPDE structure if this is not an NBSI image (type != 0x70). 8096fda04e7SJoel Fernandes let npde = NpdeStruct::find_in_data(pdev, data, &rom_header, &pcir); 8106fda04e7SJoel Fernandes 8116fda04e7SJoel Fernandes // Create a copy of the data. 8126fda04e7SJoel Fernandes let mut data_copy = KVec::new(); 8136fda04e7SJoel Fernandes data_copy.extend_from_slice(data, GFP_KERNEL)?; 8146fda04e7SJoel Fernandes 8156fda04e7SJoel Fernandes Ok(BiosImageBase { 8166fda04e7SJoel Fernandes rom_header, 8176fda04e7SJoel Fernandes pcir, 8186fda04e7SJoel Fernandes npde, 8196fda04e7SJoel Fernandes data: data_copy, 8206fda04e7SJoel Fernandes }) 8216fda04e7SJoel Fernandes } 8226fda04e7SJoel Fernandes } 823dc70c6aeSJoel Fernandes 824dc70c6aeSJoel Fernandes impl PciAtBiosImage { 825dc70c6aeSJoel Fernandes /// Find a byte pattern in a slice. find_byte_pattern(haystack: &[u8], needle: &[u8]) -> Result<usize>826dc70c6aeSJoel Fernandes fn find_byte_pattern(haystack: &[u8], needle: &[u8]) -> Result<usize> { 827dc70c6aeSJoel Fernandes haystack 828dc70c6aeSJoel Fernandes .windows(needle.len()) 829dc70c6aeSJoel Fernandes .position(|window| window == needle) 830dc70c6aeSJoel Fernandes .ok_or(EINVAL) 831dc70c6aeSJoel Fernandes } 832dc70c6aeSJoel Fernandes 833dc70c6aeSJoel Fernandes /// Find the BIT header in the [`PciAtBiosImage`]. find_bit_header(data: &[u8]) -> Result<(BitHeader, usize)>834dc70c6aeSJoel Fernandes fn find_bit_header(data: &[u8]) -> Result<(BitHeader, usize)> { 835dc70c6aeSJoel Fernandes let bit_pattern = [0xff, 0xb8, b'B', b'I', b'T', 0x00]; 836dc70c6aeSJoel Fernandes let bit_offset = Self::find_byte_pattern(data, &bit_pattern)?; 837dc70c6aeSJoel Fernandes let bit_header = BitHeader::new(&data[bit_offset..])?; 838dc70c6aeSJoel Fernandes 839dc70c6aeSJoel Fernandes Ok((bit_header, bit_offset)) 840dc70c6aeSJoel Fernandes } 841dc70c6aeSJoel Fernandes 842dc70c6aeSJoel Fernandes /// Get a BIT token entry from the BIT table in the [`PciAtBiosImage`] get_bit_token(&self, token_id: u8) -> Result<BitToken>843dc70c6aeSJoel Fernandes fn get_bit_token(&self, token_id: u8) -> Result<BitToken> { 844dc70c6aeSJoel Fernandes BitToken::from_id(self, token_id) 845dc70c6aeSJoel Fernandes } 846dc70c6aeSJoel Fernandes 847dc70c6aeSJoel Fernandes /// Find the Falcon data pointer structure in the [`PciAtBiosImage`]. 848dc70c6aeSJoel Fernandes /// 849dc70c6aeSJoel Fernandes /// This is just a 4 byte structure that contains a pointer to the Falcon data in the FWSEC 850dc70c6aeSJoel Fernandes /// image. falcon_data_ptr(&self, pdev: &pci::Device) -> Result<u32>851dc70c6aeSJoel Fernandes fn falcon_data_ptr(&self, pdev: &pci::Device) -> Result<u32> { 852dc70c6aeSJoel Fernandes let token = self.get_bit_token(BIT_TOKEN_ID_FALCON_DATA)?; 853dc70c6aeSJoel Fernandes 854dc70c6aeSJoel Fernandes // Make sure we don't go out of bounds 855dc70c6aeSJoel Fernandes if token.data_offset as usize + 4 > self.base.data.len() { 856dc70c6aeSJoel Fernandes return Err(EINVAL); 857dc70c6aeSJoel Fernandes } 858dc70c6aeSJoel Fernandes 859dc70c6aeSJoel Fernandes // read the 4 bytes at the offset specified in the token 860dc70c6aeSJoel Fernandes let offset = token.data_offset as usize; 861dc70c6aeSJoel Fernandes let bytes: [u8; 4] = self.base.data[offset..offset + 4].try_into().map_err(|_| { 862dc70c6aeSJoel Fernandes dev_err!(pdev.as_ref(), "Failed to convert data slice to array"); 863dc70c6aeSJoel Fernandes EINVAL 864dc70c6aeSJoel Fernandes })?; 865dc70c6aeSJoel Fernandes 866dc70c6aeSJoel Fernandes let data_ptr = u32::from_le_bytes(bytes); 867dc70c6aeSJoel Fernandes 868dc70c6aeSJoel Fernandes if (data_ptr as usize) < self.base.data.len() { 869dc70c6aeSJoel Fernandes dev_err!(pdev.as_ref(), "Falcon data pointer out of bounds\n"); 870dc70c6aeSJoel Fernandes return Err(EINVAL); 871dc70c6aeSJoel Fernandes } 872dc70c6aeSJoel Fernandes 873dc70c6aeSJoel Fernandes Ok(data_ptr) 874dc70c6aeSJoel Fernandes } 875dc70c6aeSJoel Fernandes } 876dc70c6aeSJoel Fernandes 877dc70c6aeSJoel Fernandes impl TryFrom<BiosImageBase> for PciAtBiosImage { 878dc70c6aeSJoel Fernandes type Error = Error; 879dc70c6aeSJoel Fernandes try_from(base: BiosImageBase) -> Result<Self>880dc70c6aeSJoel Fernandes fn try_from(base: BiosImageBase) -> Result<Self> { 881dc70c6aeSJoel Fernandes let data_slice = &base.data; 882dc70c6aeSJoel Fernandes let (bit_header, bit_offset) = PciAtBiosImage::find_bit_header(data_slice)?; 883dc70c6aeSJoel Fernandes 884dc70c6aeSJoel Fernandes Ok(PciAtBiosImage { 885dc70c6aeSJoel Fernandes base, 886dc70c6aeSJoel Fernandes bit_header, 887dc70c6aeSJoel Fernandes bit_offset, 888dc70c6aeSJoel Fernandes }) 889dc70c6aeSJoel Fernandes } 890dc70c6aeSJoel Fernandes } 89147c4846eSJoel Fernandes 89247c4846eSJoel Fernandes /// The [`PmuLookupTableEntry`] structure is a single entry in the [`PmuLookupTable`]. 89347c4846eSJoel Fernandes /// 89447c4846eSJoel Fernandes /// See the [`PmuLookupTable`] description for more information. 89547c4846eSJoel Fernandes #[expect(dead_code)] 89647c4846eSJoel Fernandes struct PmuLookupTableEntry { 89747c4846eSJoel Fernandes application_id: u8, 89847c4846eSJoel Fernandes target_id: u8, 89947c4846eSJoel Fernandes data: u32, 90047c4846eSJoel Fernandes } 90147c4846eSJoel Fernandes 90247c4846eSJoel Fernandes impl PmuLookupTableEntry { new(data: &[u8]) -> Result<Self>90347c4846eSJoel Fernandes fn new(data: &[u8]) -> Result<Self> { 904*14ae91a8SRhys Lloyd if data.len() < 6 { 90547c4846eSJoel Fernandes return Err(EINVAL); 90647c4846eSJoel Fernandes } 90747c4846eSJoel Fernandes 90847c4846eSJoel Fernandes Ok(PmuLookupTableEntry { 90947c4846eSJoel Fernandes application_id: data[0], 91047c4846eSJoel Fernandes target_id: data[1], 91147c4846eSJoel Fernandes data: u32::from_le_bytes(data[2..6].try_into().map_err(|_| EINVAL)?), 91247c4846eSJoel Fernandes }) 91347c4846eSJoel Fernandes } 91447c4846eSJoel Fernandes } 91547c4846eSJoel Fernandes 91647c4846eSJoel Fernandes /// The [`PmuLookupTableEntry`] structure is used to find the [`PmuLookupTableEntry`] for a given 91747c4846eSJoel Fernandes /// application ID. 91847c4846eSJoel Fernandes /// 91947c4846eSJoel Fernandes /// The table of entries is pointed to by the falcon data pointer in the BIT table, and is used to 92047c4846eSJoel Fernandes /// locate the Falcon Ucode. 92147c4846eSJoel Fernandes #[expect(dead_code)] 92247c4846eSJoel Fernandes struct PmuLookupTable { 92347c4846eSJoel Fernandes version: u8, 92447c4846eSJoel Fernandes header_len: u8, 92547c4846eSJoel Fernandes entry_len: u8, 92647c4846eSJoel Fernandes entry_count: u8, 92747c4846eSJoel Fernandes table_data: KVec<u8>, 92847c4846eSJoel Fernandes } 92947c4846eSJoel Fernandes 93047c4846eSJoel Fernandes impl PmuLookupTable { new(pdev: &pci::Device, data: &[u8]) -> Result<Self>93147c4846eSJoel Fernandes fn new(pdev: &pci::Device, data: &[u8]) -> Result<Self> { 93247c4846eSJoel Fernandes if data.len() < 4 { 93347c4846eSJoel Fernandes return Err(EINVAL); 93447c4846eSJoel Fernandes } 93547c4846eSJoel Fernandes 93647c4846eSJoel Fernandes let header_len = data[1] as usize; 93747c4846eSJoel Fernandes let entry_len = data[2] as usize; 93847c4846eSJoel Fernandes let entry_count = data[3] as usize; 93947c4846eSJoel Fernandes 94047c4846eSJoel Fernandes let required_bytes = header_len + (entry_count * entry_len); 94147c4846eSJoel Fernandes 94247c4846eSJoel Fernandes if data.len() < required_bytes { 94347c4846eSJoel Fernandes dev_err!( 94447c4846eSJoel Fernandes pdev.as_ref(), 94547c4846eSJoel Fernandes "PmuLookupTable data length less than required\n" 94647c4846eSJoel Fernandes ); 94747c4846eSJoel Fernandes return Err(EINVAL); 94847c4846eSJoel Fernandes } 94947c4846eSJoel Fernandes 95047c4846eSJoel Fernandes // Create a copy of only the table data 95147c4846eSJoel Fernandes let table_data = { 95247c4846eSJoel Fernandes let mut ret = KVec::new(); 95347c4846eSJoel Fernandes ret.extend_from_slice(&data[header_len..required_bytes], GFP_KERNEL)?; 95447c4846eSJoel Fernandes ret 95547c4846eSJoel Fernandes }; 95647c4846eSJoel Fernandes 95747c4846eSJoel Fernandes // Debug logging of entries (dumps the table data to dmesg) 95847c4846eSJoel Fernandes for i in (header_len..required_bytes).step_by(entry_len) { 95947c4846eSJoel Fernandes dev_dbg!( 96047c4846eSJoel Fernandes pdev.as_ref(), 96147c4846eSJoel Fernandes "PMU entry: {:02x?}\n", 96247c4846eSJoel Fernandes &data[i..][..entry_len] 96347c4846eSJoel Fernandes ); 96447c4846eSJoel Fernandes } 96547c4846eSJoel Fernandes 96647c4846eSJoel Fernandes Ok(PmuLookupTable { 96747c4846eSJoel Fernandes version: data[0], 96847c4846eSJoel Fernandes header_len: header_len as u8, 96947c4846eSJoel Fernandes entry_len: entry_len as u8, 97047c4846eSJoel Fernandes entry_count: entry_count as u8, 97147c4846eSJoel Fernandes table_data, 97247c4846eSJoel Fernandes }) 97347c4846eSJoel Fernandes } 97447c4846eSJoel Fernandes lookup_index(&self, idx: u8) -> Result<PmuLookupTableEntry>97547c4846eSJoel Fernandes fn lookup_index(&self, idx: u8) -> Result<PmuLookupTableEntry> { 97647c4846eSJoel Fernandes if idx >= self.entry_count { 97747c4846eSJoel Fernandes return Err(EINVAL); 97847c4846eSJoel Fernandes } 97947c4846eSJoel Fernandes 98047c4846eSJoel Fernandes let index = (idx as usize) * self.entry_len as usize; 98147c4846eSJoel Fernandes PmuLookupTableEntry::new(&self.table_data[index..]) 98247c4846eSJoel Fernandes } 98347c4846eSJoel Fernandes 98447c4846eSJoel Fernandes // find entry by type value find_entry_by_type(&self, entry_type: u8) -> Result<PmuLookupTableEntry>98547c4846eSJoel Fernandes fn find_entry_by_type(&self, entry_type: u8) -> Result<PmuLookupTableEntry> { 98647c4846eSJoel Fernandes for i in 0..self.entry_count { 98747c4846eSJoel Fernandes let entry = self.lookup_index(i)?; 98847c4846eSJoel Fernandes if entry.application_id == entry_type { 98947c4846eSJoel Fernandes return Ok(entry); 99047c4846eSJoel Fernandes } 99147c4846eSJoel Fernandes } 99247c4846eSJoel Fernandes 99347c4846eSJoel Fernandes Err(EINVAL) 99447c4846eSJoel Fernandes } 99547c4846eSJoel Fernandes } 99647c4846eSJoel Fernandes 99747c4846eSJoel Fernandes impl FwSecBiosBuilder { setup_falcon_data( &mut self, pdev: &pci::Device, pci_at_image: &PciAtBiosImage, first_fwsec: &FwSecBiosBuilder, ) -> Result99847c4846eSJoel Fernandes fn setup_falcon_data( 99947c4846eSJoel Fernandes &mut self, 100047c4846eSJoel Fernandes pdev: &pci::Device, 100147c4846eSJoel Fernandes pci_at_image: &PciAtBiosImage, 100247c4846eSJoel Fernandes first_fwsec: &FwSecBiosBuilder, 100347c4846eSJoel Fernandes ) -> Result { 100447c4846eSJoel Fernandes let mut offset = pci_at_image.falcon_data_ptr(pdev)? as usize; 100547c4846eSJoel Fernandes let mut pmu_in_first_fwsec = false; 100647c4846eSJoel Fernandes 100747c4846eSJoel Fernandes // The falcon data pointer assumes that the PciAt and FWSEC images 100847c4846eSJoel Fernandes // are contiguous in memory. However, testing shows the EFI image sits in 100947c4846eSJoel Fernandes // between them. So calculate the offset from the end of the PciAt image 101047c4846eSJoel Fernandes // rather than the start of it. Compensate. 101147c4846eSJoel Fernandes offset -= pci_at_image.base.data.len(); 101247c4846eSJoel Fernandes 101347c4846eSJoel Fernandes // The offset is now from the start of the first Fwsec image, however 101447c4846eSJoel Fernandes // the offset points to a location in the second Fwsec image. Since 101547c4846eSJoel Fernandes // the fwsec images are contiguous, subtract the length of the first Fwsec 101647c4846eSJoel Fernandes // image from the offset to get the offset to the start of the second 101747c4846eSJoel Fernandes // Fwsec image. 101847c4846eSJoel Fernandes if offset < first_fwsec.base.data.len() { 101947c4846eSJoel Fernandes pmu_in_first_fwsec = true; 102047c4846eSJoel Fernandes } else { 102147c4846eSJoel Fernandes offset -= first_fwsec.base.data.len(); 102247c4846eSJoel Fernandes } 102347c4846eSJoel Fernandes 102447c4846eSJoel Fernandes self.falcon_data_offset = Some(offset); 102547c4846eSJoel Fernandes 102647c4846eSJoel Fernandes if pmu_in_first_fwsec { 102747c4846eSJoel Fernandes self.pmu_lookup_table = 102847c4846eSJoel Fernandes Some(PmuLookupTable::new(pdev, &first_fwsec.base.data[offset..])?); 102947c4846eSJoel Fernandes } else { 103047c4846eSJoel Fernandes self.pmu_lookup_table = Some(PmuLookupTable::new(pdev, &self.base.data[offset..])?); 103147c4846eSJoel Fernandes } 103247c4846eSJoel Fernandes 103347c4846eSJoel Fernandes match self 103447c4846eSJoel Fernandes .pmu_lookup_table 103547c4846eSJoel Fernandes .as_ref() 103647c4846eSJoel Fernandes .ok_or(EINVAL)? 103747c4846eSJoel Fernandes .find_entry_by_type(FALCON_UCODE_ENTRY_APPID_FWSEC_PROD) 103847c4846eSJoel Fernandes { 103947c4846eSJoel Fernandes Ok(entry) => { 104047c4846eSJoel Fernandes let mut ucode_offset = entry.data as usize; 104147c4846eSJoel Fernandes ucode_offset -= pci_at_image.base.data.len(); 104247c4846eSJoel Fernandes if ucode_offset < first_fwsec.base.data.len() { 104347c4846eSJoel Fernandes dev_err!(pdev.as_ref(), "Falcon Ucode offset not in second Fwsec.\n"); 104447c4846eSJoel Fernandes return Err(EINVAL); 104547c4846eSJoel Fernandes } 104647c4846eSJoel Fernandes ucode_offset -= first_fwsec.base.data.len(); 104747c4846eSJoel Fernandes self.falcon_ucode_offset = Some(ucode_offset); 104847c4846eSJoel Fernandes } 104947c4846eSJoel Fernandes Err(e) => { 105047c4846eSJoel Fernandes dev_err!( 105147c4846eSJoel Fernandes pdev.as_ref(), 105247c4846eSJoel Fernandes "PmuLookupTableEntry not found, error: {:?}\n", 105347c4846eSJoel Fernandes e 105447c4846eSJoel Fernandes ); 105547c4846eSJoel Fernandes return Err(EINVAL); 105647c4846eSJoel Fernandes } 105747c4846eSJoel Fernandes } 105847c4846eSJoel Fernandes Ok(()) 105947c4846eSJoel Fernandes } 106047c4846eSJoel Fernandes 106147c4846eSJoel Fernandes /// Build the final FwSecBiosImage from this builder build(self, pdev: &pci::Device) -> Result<FwSecBiosImage>106247c4846eSJoel Fernandes fn build(self, pdev: &pci::Device) -> Result<FwSecBiosImage> { 106347c4846eSJoel Fernandes let ret = FwSecBiosImage { 106447c4846eSJoel Fernandes base: self.base, 106547c4846eSJoel Fernandes falcon_ucode_offset: self.falcon_ucode_offset.ok_or(EINVAL)?, 106647c4846eSJoel Fernandes }; 106747c4846eSJoel Fernandes 106847c4846eSJoel Fernandes if cfg!(debug_assertions) { 106947c4846eSJoel Fernandes // Print the desc header for debugging 107047c4846eSJoel Fernandes let desc = ret.header(pdev.as_ref())?; 107147c4846eSJoel Fernandes dev_dbg!(pdev.as_ref(), "PmuLookupTableEntry desc: {:#?}\n", desc); 107247c4846eSJoel Fernandes } 107347c4846eSJoel Fernandes 107447c4846eSJoel Fernandes Ok(ret) 107547c4846eSJoel Fernandes } 107647c4846eSJoel Fernandes } 107747c4846eSJoel Fernandes 107847c4846eSJoel Fernandes impl FwSecBiosImage { 107947c4846eSJoel Fernandes /// Get the FwSec header ([`FalconUCodeDescV3`]). header(&self, dev: &device::Device) -> Result<&FalconUCodeDescV3>108047c4846eSJoel Fernandes pub(crate) fn header(&self, dev: &device::Device) -> Result<&FalconUCodeDescV3> { 108147c4846eSJoel Fernandes // Get the falcon ucode offset that was found in setup_falcon_data. 108247c4846eSJoel Fernandes let falcon_ucode_offset = self.falcon_ucode_offset; 108347c4846eSJoel Fernandes 108447c4846eSJoel Fernandes // Make sure the offset is within the data bounds. 108547c4846eSJoel Fernandes if falcon_ucode_offset + core::mem::size_of::<FalconUCodeDescV3>() > self.base.data.len() { 108647c4846eSJoel Fernandes dev_err!(dev, "fwsec-frts header not contained within BIOS bounds\n"); 108747c4846eSJoel Fernandes return Err(ERANGE); 108847c4846eSJoel Fernandes } 108947c4846eSJoel Fernandes 109047c4846eSJoel Fernandes // Read the first 4 bytes to get the version. 109147c4846eSJoel Fernandes let hdr_bytes: [u8; 4] = self.base.data[falcon_ucode_offset..falcon_ucode_offset + 4] 109247c4846eSJoel Fernandes .try_into() 109347c4846eSJoel Fernandes .map_err(|_| EINVAL)?; 109447c4846eSJoel Fernandes let hdr = u32::from_le_bytes(hdr_bytes); 109547c4846eSJoel Fernandes let ver = (hdr & 0xff00) >> 8; 109647c4846eSJoel Fernandes 109747c4846eSJoel Fernandes if ver != 3 { 109847c4846eSJoel Fernandes dev_err!(dev, "invalid fwsec firmware version: {:?}\n", ver); 109947c4846eSJoel Fernandes return Err(EINVAL); 110047c4846eSJoel Fernandes } 110147c4846eSJoel Fernandes 110247c4846eSJoel Fernandes // Return a reference to the FalconUCodeDescV3 structure. 110347c4846eSJoel Fernandes // 110447c4846eSJoel Fernandes // SAFETY: We have checked that `falcon_ucode_offset + size_of::<FalconUCodeDescV3>` is 110547c4846eSJoel Fernandes // within the bounds of `data`. Also, this data vector is from ROM, and the `data` field 110647c4846eSJoel Fernandes // in `BiosImageBase` is immutable after construction. 110747c4846eSJoel Fernandes Ok(unsafe { 110847c4846eSJoel Fernandes &*(self 110947c4846eSJoel Fernandes .base 111047c4846eSJoel Fernandes .data 111147c4846eSJoel Fernandes .as_ptr() 111247c4846eSJoel Fernandes .add(falcon_ucode_offset) 111347c4846eSJoel Fernandes .cast::<FalconUCodeDescV3>()) 111447c4846eSJoel Fernandes }) 111547c4846eSJoel Fernandes } 111647c4846eSJoel Fernandes 111747c4846eSJoel Fernandes /// Get the ucode data as a byte slice ucode(&self, dev: &device::Device, desc: &FalconUCodeDescV3) -> Result<&[u8]>111847c4846eSJoel Fernandes pub(crate) fn ucode(&self, dev: &device::Device, desc: &FalconUCodeDescV3) -> Result<&[u8]> { 111947c4846eSJoel Fernandes let falcon_ucode_offset = self.falcon_ucode_offset; 112047c4846eSJoel Fernandes 112147c4846eSJoel Fernandes // The ucode data follows the descriptor. 112247c4846eSJoel Fernandes let ucode_data_offset = falcon_ucode_offset + desc.size(); 112347c4846eSJoel Fernandes let size = (desc.imem_load_size + desc.dmem_load_size) as usize; 112447c4846eSJoel Fernandes 112547c4846eSJoel Fernandes // Get the data slice, checking bounds in a single operation. 112647c4846eSJoel Fernandes self.base 112747c4846eSJoel Fernandes .data 112847c4846eSJoel Fernandes .get(ucode_data_offset..ucode_data_offset + size) 112947c4846eSJoel Fernandes .ok_or(ERANGE) 113047c4846eSJoel Fernandes .inspect_err(|_| dev_err!(dev, "fwsec ucode data not contained within BIOS bounds\n")) 113147c4846eSJoel Fernandes } 113247c4846eSJoel Fernandes 113347c4846eSJoel Fernandes /// Get the signatures as a byte slice sigs( &self, dev: &device::Device, desc: &FalconUCodeDescV3, ) -> Result<&[Bcrt30Rsa3kSignature]>113431f0feefSAlexandre Courbot pub(crate) fn sigs( 113531f0feefSAlexandre Courbot &self, 113631f0feefSAlexandre Courbot dev: &device::Device, 113731f0feefSAlexandre Courbot desc: &FalconUCodeDescV3, 113831f0feefSAlexandre Courbot ) -> Result<&[Bcrt30Rsa3kSignature]> { 113947c4846eSJoel Fernandes // The signatures data follows the descriptor. 114047c4846eSJoel Fernandes let sigs_data_offset = self.falcon_ucode_offset + core::mem::size_of::<FalconUCodeDescV3>(); 114131f0feefSAlexandre Courbot let sigs_size = 114231f0feefSAlexandre Courbot desc.signature_count as usize * core::mem::size_of::<Bcrt30Rsa3kSignature>(); 114347c4846eSJoel Fernandes 114447c4846eSJoel Fernandes // Make sure the data is within bounds. 114531f0feefSAlexandre Courbot if sigs_data_offset + sigs_size > self.base.data.len() { 114647c4846eSJoel Fernandes dev_err!( 114747c4846eSJoel Fernandes dev, 114847c4846eSJoel Fernandes "fwsec signatures data not contained within BIOS bounds\n" 114947c4846eSJoel Fernandes ); 115047c4846eSJoel Fernandes return Err(ERANGE); 115147c4846eSJoel Fernandes } 115247c4846eSJoel Fernandes 115331f0feefSAlexandre Courbot // SAFETY: we checked that `data + sigs_data_offset + (signature_count * 115431f0feefSAlexandre Courbot // sizeof::<Bcrt30Rsa3kSignature>()` is within the bounds of `data`. 115531f0feefSAlexandre Courbot Ok(unsafe { 115631f0feefSAlexandre Courbot core::slice::from_raw_parts( 115731f0feefSAlexandre Courbot self.base 115831f0feefSAlexandre Courbot .data 115931f0feefSAlexandre Courbot .as_ptr() 116031f0feefSAlexandre Courbot .add(sigs_data_offset) 116131f0feefSAlexandre Courbot .cast::<Bcrt30Rsa3kSignature>(), 116231f0feefSAlexandre Courbot desc.signature_count as usize, 116331f0feefSAlexandre Courbot ) 116431f0feefSAlexandre Courbot }) 116547c4846eSJoel Fernandes } 116647c4846eSJoel Fernandes } 1167