@@ -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.
577686struct 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
582692struct 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