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