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