Skip to content

Commit dc70c6a

Browse files
Joel FernandesDanilo Krummrich
authored andcommitted
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 <[email protected]> Signed-off-by: Alexandre Courbot <[email protected]> Link: https://lore.kernel.org/r/[email protected] [ Re-format and use markdown in comments. - Danilo ] Signed-off-by: Danilo Krummrich <[email protected]>
1 parent 6fda04e commit dc70c6a

File tree

1 file changed

+180
-2
lines changed

1 file changed

+180
-2
lines changed

drivers/gpu/nova-core/vbios.rs

Lines changed: 180 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,112 @@ impl PcirStruct {
333333
}
334334
}
335335

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+
336442
/// PCI ROM Expansion Header as defined in PCI Firmware Specification.
337443
///
338444
/// This is header is at the beginning of every image in the set of images in the ROM. It contains
@@ -574,9 +680,13 @@ bios_image! {
574680
FwSec: FwSecBiosImage, // FWSEC (Firmware Security)
575681
}
576682

683+
/// The PciAt BIOS image is typically the first BIOS image type found in the BIOS image chain.
684+
///
685+
/// It contains the BIT header and the BIT tokens.
577686
struct PciAtBiosImage {
578687
base: BiosImageBase,
579-
// PCI-AT-specific fields can be added here in the future.
688+
bit_header: BitHeader,
689+
bit_offset: usize,
580690
}
581691

582692
struct EfiBiosImage {
@@ -600,7 +710,7 @@ impl TryFrom<BiosImageBase> for BiosImage {
600710

601711
fn try_from(base: BiosImageBase) -> Result<Self> {
602712
match base.pcir.code_type {
603-
0x00 => Ok(BiosImage::PciAt(PciAtBiosImage { base })),
713+
0x00 => Ok(BiosImage::PciAt(base.try_into()?)),
604714
0x03 => Ok(BiosImage::Efi(EfiBiosImage { base })),
605715
0x70 => Ok(BiosImage::Nbsi(NbsiBiosImage { base })),
606716
0xE0 => Ok(BiosImage::FwSec(FwSecBiosImage { base })),
@@ -679,3 +789,71 @@ impl BiosImageBase {
679789
})
680790
}
681791
}
792+
793+
impl PciAtBiosImage {
794+
/// Find a byte pattern in a slice.
795+
fn find_byte_pattern(haystack: &[u8], needle: &[u8]) -> Result<usize> {
796+
haystack
797+
.windows(needle.len())
798+
.position(|window| window == needle)
799+
.ok_or(EINVAL)
800+
}
801+
802+
/// Find the BIT header in the [`PciAtBiosImage`].
803+
fn find_bit_header(data: &[u8]) -> Result<(BitHeader, usize)> {
804+
let bit_pattern = [0xff, 0xb8, b'B', b'I', b'T', 0x00];
805+
let bit_offset = Self::find_byte_pattern(data, &bit_pattern)?;
806+
let bit_header = BitHeader::new(&data[bit_offset..])?;
807+
808+
Ok((bit_header, bit_offset))
809+
}
810+
811+
/// Get a BIT token entry from the BIT table in the [`PciAtBiosImage`]
812+
fn get_bit_token(&self, token_id: u8) -> Result<BitToken> {
813+
BitToken::from_id(self, token_id)
814+
}
815+
816+
/// Find the Falcon data pointer structure in the [`PciAtBiosImage`].
817+
///
818+
/// This is just a 4 byte structure that contains a pointer to the Falcon data in the FWSEC
819+
/// image.
820+
fn falcon_data_ptr(&self, pdev: &pci::Device) -> Result<u32> {
821+
let token = self.get_bit_token(BIT_TOKEN_ID_FALCON_DATA)?;
822+
823+
// Make sure we don't go out of bounds
824+
if token.data_offset as usize + 4 > self.base.data.len() {
825+
return Err(EINVAL);
826+
}
827+
828+
// read the 4 bytes at the offset specified in the token
829+
let offset = token.data_offset as usize;
830+
let bytes: [u8; 4] = self.base.data[offset..offset + 4].try_into().map_err(|_| {
831+
dev_err!(pdev.as_ref(), "Failed to convert data slice to array");
832+
EINVAL
833+
})?;
834+
835+
let data_ptr = u32::from_le_bytes(bytes);
836+
837+
if (data_ptr as usize) < self.base.data.len() {
838+
dev_err!(pdev.as_ref(), "Falcon data pointer out of bounds\n");
839+
return Err(EINVAL);
840+
}
841+
842+
Ok(data_ptr)
843+
}
844+
}
845+
846+
impl TryFrom<BiosImageBase> for PciAtBiosImage {
847+
type Error = Error;
848+
849+
fn try_from(base: BiosImageBase) -> Result<Self> {
850+
let data_slice = &base.data;
851+
let (bit_header, bit_offset) = PciAtBiosImage::find_bit_header(data_slice)?;
852+
853+
Ok(PciAtBiosImage {
854+
base,
855+
bit_header,
856+
bit_offset,
857+
})
858+
}
859+
}

0 commit comments

Comments
 (0)