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