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