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