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