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