@@ -333,6 +333,112 @@ impl PcirStruct {
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
@@ -574,9 +680,13 @@ bios_image! {
574
680
FwSec : FwSecBiosImage , // FWSEC (Firmware Security)
575
681
}
576
682
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.
577
686
struct PciAtBiosImage {
578
687
base : BiosImageBase ,
579
- // PCI-AT-specific fields can be added here in the future.
688
+ bit_header : BitHeader ,
689
+ bit_offset : usize ,
580
690
}
581
691
582
692
struct EfiBiosImage {
@@ -600,7 +710,7 @@ impl TryFrom<BiosImageBase> for BiosImage {
600
710
601
711
fn try_from ( base : BiosImageBase ) -> Result < Self > {
602
712
match base. pcir . code_type {
603
- 0x00 => Ok ( BiosImage :: PciAt ( PciAtBiosImage { base } ) ) ,
713
+ 0x00 => Ok ( BiosImage :: PciAt ( base. try_into ( ) ? ) ) ,
604
714
0x03 => Ok ( BiosImage :: Efi ( EfiBiosImage { base } ) ) ,
605
715
0x70 => Ok ( BiosImage :: Nbsi ( NbsiBiosImage { base } ) ) ,
606
716
0xE0 => Ok ( BiosImage :: FwSec ( FwSecBiosImage { base } ) ) ,
@@ -679,3 +789,71 @@ impl BiosImageBase {
679
789
} )
680
790
}
681
791
}
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