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