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