Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

gpu: nova-core: vbios: Add support to look up PMU table in FWSEC

The PMU table in the FWSEC image has to be located to locate the start
of the Falcon ucode in the same or another FWSEC image. Add support for
the same.

Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com>
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
Link: https://lore.kernel.org/r/20250619-nova-frts-v6-18-ecf41ef99252@nvidia.com
[ Re-format and use markdown in comments. - Danilo ]
Signed-off-by: Danilo Krummrich <dakr@kernel.org>

authored by

Joel Fernandes and committed by
Danilo Krummrich
dc70c6ae 6fda04e7

+180 -2
+180 -2
drivers/gpu/nova-core/vbios.rs
··· 333 333 } 334 334 } 335 335 336 + /// BIOS Information Table (BIT) Header. 337 + /// 338 + /// This is the head of the BIT table, that is used to locate the Falcon data. The BIT table (with 339 + /// its header) is in the [`PciAtBiosImage`] and the falcon data it is pointing to is in the 340 + /// [`FwSecBiosImage`]. 341 + #[derive(Debug, Clone, Copy)] 342 + #[expect(dead_code)] 343 + struct BitHeader { 344 + /// 0h: BIT Header Identifier (BMP=0x7FFF/BIT=0xB8FF) 345 + id: u16, 346 + /// 2h: BIT Header Signature ("BIT\0") 347 + signature: [u8; 4], 348 + /// 6h: Binary Coded Decimal Version, ex: 0x0100 is 1.00. 349 + bcd_version: u16, 350 + /// 8h: Size of BIT Header (in bytes) 351 + header_size: u8, 352 + /// 9h: Size of BIT Tokens (in bytes) 353 + token_size: u8, 354 + /// 10h: Number of token entries that follow 355 + token_entries: u8, 356 + /// 11h: BIT Header Checksum 357 + checksum: u8, 358 + } 359 + 360 + impl BitHeader { 361 + fn new(data: &[u8]) -> Result<Self> { 362 + if data.len() < 12 { 363 + return Err(EINVAL); 364 + } 365 + 366 + let mut signature = [0u8; 4]; 367 + signature.copy_from_slice(&data[2..6]); 368 + 369 + // Check header ID and signature 370 + let id = u16::from_le_bytes([data[0], data[1]]); 371 + if id != 0xB8FF || &signature != b"BIT\0" { 372 + return Err(EINVAL); 373 + } 374 + 375 + Ok(BitHeader { 376 + id, 377 + signature, 378 + bcd_version: u16::from_le_bytes([data[6], data[7]]), 379 + header_size: data[8], 380 + token_size: data[9], 381 + token_entries: data[10], 382 + checksum: data[11], 383 + }) 384 + } 385 + } 386 + 387 + /// BIT Token Entry: Records in the BIT table followed by the BIT header. 388 + #[derive(Debug, Clone, Copy)] 389 + #[expect(dead_code)] 390 + struct BitToken { 391 + /// 00h: Token identifier 392 + id: u8, 393 + /// 01h: Version of the token data 394 + data_version: u8, 395 + /// 02h: Size of token data in bytes 396 + data_size: u16, 397 + /// 04h: Offset to the token data 398 + data_offset: u16, 399 + } 400 + 401 + // Define the token ID for the Falcon data 402 + const BIT_TOKEN_ID_FALCON_DATA: u8 = 0x70; 403 + 404 + impl BitToken { 405 + /// Find a BIT token entry by BIT ID in a PciAtBiosImage 406 + fn from_id(image: &PciAtBiosImage, token_id: u8) -> Result<Self> { 407 + let header = &image.bit_header; 408 + 409 + // Offset to the first token entry 410 + let tokens_start = image.bit_offset + header.header_size as usize; 411 + 412 + for i in 0..header.token_entries as usize { 413 + let entry_offset = tokens_start + (i * header.token_size as usize); 414 + 415 + // Make sure we don't go out of bounds 416 + if entry_offset + header.token_size as usize > image.base.data.len() { 417 + return Err(EINVAL); 418 + } 419 + 420 + // Check if this token has the requested ID 421 + if image.base.data[entry_offset] == token_id { 422 + return Ok(BitToken { 423 + id: image.base.data[entry_offset], 424 + data_version: image.base.data[entry_offset + 1], 425 + data_size: u16::from_le_bytes([ 426 + image.base.data[entry_offset + 2], 427 + image.base.data[entry_offset + 3], 428 + ]), 429 + data_offset: u16::from_le_bytes([ 430 + image.base.data[entry_offset + 4], 431 + image.base.data[entry_offset + 5], 432 + ]), 433 + }); 434 + } 435 + } 436 + 437 + // Token not found 438 + Err(ENOENT) 439 + } 440 + } 441 + 336 442 /// PCI ROM Expansion Header as defined in PCI Firmware Specification. 337 443 /// 338 444 /// This is header is at the beginning of every image in the set of images in the ROM. It contains ··· 680 574 FwSec: FwSecBiosImage, // FWSEC (Firmware Security) 681 575 } 682 576 577 + /// The PciAt BIOS image is typically the first BIOS image type found in the BIOS image chain. 578 + /// 579 + /// It contains the BIT header and the BIT tokens. 683 580 struct PciAtBiosImage { 684 581 base: BiosImageBase, 685 - // PCI-AT-specific fields can be added here in the future. 582 + bit_header: BitHeader, 583 + bit_offset: usize, 686 584 } 687 585 688 586 struct EfiBiosImage { ··· 710 600 711 601 fn try_from(base: BiosImageBase) -> Result<Self> { 712 602 match base.pcir.code_type { 713 - 0x00 => Ok(BiosImage::PciAt(PciAtBiosImage { base })), 603 + 0x00 => Ok(BiosImage::PciAt(base.try_into()?)), 714 604 0x03 => Ok(BiosImage::Efi(EfiBiosImage { base })), 715 605 0x70 => Ok(BiosImage::Nbsi(NbsiBiosImage { base })), 716 606 0xE0 => Ok(BiosImage::FwSec(FwSecBiosImage { base })), ··· 786 676 pcir, 787 677 npde, 788 678 data: data_copy, 679 + }) 680 + } 681 + } 682 + 683 + impl PciAtBiosImage { 684 + /// Find a byte pattern in a slice. 685 + fn find_byte_pattern(haystack: &[u8], needle: &[u8]) -> Result<usize> { 686 + haystack 687 + .windows(needle.len()) 688 + .position(|window| window == needle) 689 + .ok_or(EINVAL) 690 + } 691 + 692 + /// Find the BIT header in the [`PciAtBiosImage`]. 693 + fn find_bit_header(data: &[u8]) -> Result<(BitHeader, usize)> { 694 + let bit_pattern = [0xff, 0xb8, b'B', b'I', b'T', 0x00]; 695 + let bit_offset = Self::find_byte_pattern(data, &bit_pattern)?; 696 + let bit_header = BitHeader::new(&data[bit_offset..])?; 697 + 698 + Ok((bit_header, bit_offset)) 699 + } 700 + 701 + /// Get a BIT token entry from the BIT table in the [`PciAtBiosImage`] 702 + fn get_bit_token(&self, token_id: u8) -> Result<BitToken> { 703 + BitToken::from_id(self, token_id) 704 + } 705 + 706 + /// Find the Falcon data pointer structure in the [`PciAtBiosImage`]. 707 + /// 708 + /// This is just a 4 byte structure that contains a pointer to the Falcon data in the FWSEC 709 + /// image. 710 + fn falcon_data_ptr(&self, pdev: &pci::Device) -> Result<u32> { 711 + let token = self.get_bit_token(BIT_TOKEN_ID_FALCON_DATA)?; 712 + 713 + // Make sure we don't go out of bounds 714 + if token.data_offset as usize + 4 > self.base.data.len() { 715 + return Err(EINVAL); 716 + } 717 + 718 + // read the 4 bytes at the offset specified in the token 719 + let offset = token.data_offset as usize; 720 + let bytes: [u8; 4] = self.base.data[offset..offset + 4].try_into().map_err(|_| { 721 + dev_err!(pdev.as_ref(), "Failed to convert data slice to array"); 722 + EINVAL 723 + })?; 724 + 725 + let data_ptr = u32::from_le_bytes(bytes); 726 + 727 + if (data_ptr as usize) < self.base.data.len() { 728 + dev_err!(pdev.as_ref(), "Falcon data pointer out of bounds\n"); 729 + return Err(EINVAL); 730 + } 731 + 732 + Ok(data_ptr) 733 + } 734 + } 735 + 736 + impl TryFrom<BiosImageBase> for PciAtBiosImage { 737 + type Error = Error; 738 + 739 + fn try_from(base: BiosImageBase) -> Result<Self> { 740 + let data_slice = &base.data; 741 + let (bit_header, bit_offset) = PciAtBiosImage::find_bit_header(data_slice)?; 742 + 743 + Ok(PciAtBiosImage { 744 + base, 745 + bit_header, 746 + bit_offset, 789 747 }) 790 748 } 791 749 }