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