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