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