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