github.com/linuxboot/fiano@v1.2.0/pkg/amd/manifest/bios_directory_table.go (about)

     1  // Copyright 2019 the LinuxBoot Authors. All rights reserved
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package manifest
     6  
     7  import (
     8  	"bytes"
     9  	"encoding/binary"
    10  	"fmt"
    11  	"io"
    12  	"strings"
    13  
    14  	bytes2 "github.com/linuxboot/fiano/pkg/bytes"
    15  )
    16  
    17  // Refer to: AMD Platform Security Processor BIOS Architecture Design Guide for AMD Family 17h and Family 19h
    18  // Processors (NDA), Publication # 55758 Revision: 1.11 Issue Date: August 2020 (1)
    19  
    20  // BIOSDirectoryTableCookie is a special identifier of BIOS Directory table level 1
    21  const BIOSDirectoryTableCookie = 0x44484224 // $BHD
    22  // BIOSDirectoryTableLevel2Cookie is a special identifier of BIOS Directory table level 2
    23  const BIOSDirectoryTableLevel2Cookie = 0x324C4224 // $BL2
    24  
    25  // BIOSDirectoryTableEntryType is an entry type of BIOS Directory table
    26  type BIOSDirectoryTableEntryType uint8
    27  
    28  const (
    29  	// APCBDataEntry represents APCB data entry in BIOS Directory table
    30  	APCBDataEntry BIOSDirectoryTableEntryType = 0x60
    31  	// APOBBinaryEntry denotes APOB binary in BIOS Directory table
    32  	APOBBinaryEntry BIOSDirectoryTableEntryType = 0x61
    33  	// BIOSRTMVolumeEntry represents BIOS RTM Volume entry in BIOS Directory table
    34  	BIOSRTMVolumeEntry BIOSDirectoryTableEntryType = 0x62
    35  	// PMUFirmwareInstructionsEntry represents the instruction portion of PMU firmware
    36  	PMUFirmwareInstructionsEntry BIOSDirectoryTableEntryType = 0x64
    37  	// PMUFirmwareDataEntry represents the data portion of PMU firmware
    38  	PMUFirmwareDataEntry BIOSDirectoryTableEntryType = 0x65
    39  	// MicrocodePatchEntry represents the microcode patch file location
    40  	MicrocodePatchEntry BIOSDirectoryTableEntryType = 0x66
    41  	// APCBDataBackupEntry contains a backup copy of APCB data
    42  	APCBDataBackupEntry BIOSDirectoryTableEntryType = 0x68
    43  	// VideoInterpreterEntry interpreter binary that displays the video image
    44  	VideoInterpreterEntry BIOSDirectoryTableEntryType = 0x69
    45  	// BIOSDirectoryTableLevel2Entry denotes an entry that points to BIOS Directory table level 2
    46  	BIOSDirectoryTableLevel2Entry BIOSDirectoryTableEntryType = 0x70
    47  )
    48  
    49  // BIOSDirectoryTableEntry represents a single entry in BIOS Directory Table
    50  // Table 12 from (1)
    51  type BIOSDirectoryTableEntry struct {
    52  	Type       BIOSDirectoryTableEntryType
    53  	RegionType uint8
    54  
    55  	ResetImage bool
    56  	CopyImage  bool
    57  	ReadOnly   bool
    58  	Compressed bool
    59  	Instance   uint8
    60  	Subprogram uint8
    61  	RomID      uint8
    62  
    63  	Size               uint32
    64  	SourceAddress      uint64
    65  	DestinationAddress uint64
    66  }
    67  
    68  const BIOSDirectoryTableEntrySize = 16
    69  
    70  // BIOSDirectoryTableHeader represents a BIOS Directory Table Header
    71  // Table 11 from (1)
    72  type BIOSDirectoryTableHeader struct {
    73  	BIOSCookie   uint32
    74  	Checksum     uint32
    75  	TotalEntries uint32
    76  	Reserved     uint32
    77  }
    78  
    79  // BIOSDirectoryTable represents a BIOS Directory Table Header with all entries
    80  // Table 11 & Table 12 from (1)
    81  type BIOSDirectoryTable struct {
    82  	BIOSDirectoryTableHeader
    83  
    84  	Entries []BIOSDirectoryTableEntry
    85  }
    86  
    87  func (b BIOSDirectoryTable) String() string {
    88  	var s strings.Builder
    89  	cookieBytes := make([]byte, 4)
    90  	binary.LittleEndian.PutUint32(cookieBytes, b.BIOSCookie)
    91  	fmt.Fprintf(&s, "BIOS Cookie: 0x%x (%s)\n", b.BIOSCookie, cookieBytes)
    92  	fmt.Fprintf(&s, "Checksum: %d\n", b.Checksum)
    93  	fmt.Fprintf(&s, "Total Entries: %d\n", b.TotalEntries)
    94  	fmt.Fprintf(&s, "%-5s | %-10s | %-10s | %-9s | %-8s | %-10s | %-8s | %-10s | %-5s | %-6s | %-13s | %-18s\n",
    95  		"Type",
    96  		"RegionType",
    97  		"ResetImage",
    98  		"CopyImage",
    99  		"ReadOnly",
   100  		"Compressed",
   101  		"Instance",
   102  		"Subprogram",
   103  		"RomID",
   104  		"Size",
   105  		"SourceAddress",
   106  		"DestinationAddress")
   107  	fmt.Fprintf(&s, "%s\n", "----------------------------------------------------------------------------------------------------------------------------------------------------------------")
   108  	for _, entry := range b.Entries {
   109  		fmt.Fprintf(&s, "0x%-3x | 0x%-8x | %-10v | %-9v | %-8v | %-10v | 0x%-6x | 0x%-8x | 0x%-3x | %-6d | 0x%-11x | 0x%-18x\n",
   110  			entry.Type,
   111  			entry.RegionType,
   112  			entry.ResetImage,
   113  			entry.CopyImage,
   114  			entry.ReadOnly,
   115  			entry.Compressed,
   116  			entry.Instance,
   117  			entry.Subprogram,
   118  			entry.RomID,
   119  			entry.Size,
   120  			entry.SourceAddress,
   121  			entry.DestinationAddress)
   122  	}
   123  	return s.String()
   124  }
   125  
   126  // FindBIOSDirectoryTable scans firmware for BIOSDirectoryTableCookie
   127  // and treats remaining bytes as BIOSDirectoryTable
   128  func FindBIOSDirectoryTable(image []byte) (*BIOSDirectoryTable, bytes2.Range, error) {
   129  	// there is no predefined address, search through the whole memory
   130  	var cookieBytes [4]byte
   131  	binary.LittleEndian.PutUint32(cookieBytes[:], BIOSDirectoryTableCookie)
   132  
   133  	var offset uint64
   134  	for {
   135  		idx := bytes.Index(image, cookieBytes[:])
   136  		if idx == -1 {
   137  			break
   138  		}
   139  
   140  		table, bytesRead, err := ParseBIOSDirectoryTable(image[idx:])
   141  		if err != nil {
   142  			shift := uint64(idx + len(cookieBytes))
   143  			image = image[shift:]
   144  			offset += shift
   145  			continue
   146  		}
   147  		return table, bytes2.Range{Offset: offset + uint64(idx), Length: bytesRead}, err
   148  	}
   149  	return nil, bytes2.Range{}, fmt.Errorf("BIOSDirectoryTable is not found")
   150  }
   151  
   152  // ParseBIOSDirectoryTable converts input bytes into BIOSDirectoryTable
   153  func ParseBIOSDirectoryTable(data []byte) (*BIOSDirectoryTable, uint64, error) {
   154  	var table BIOSDirectoryTable
   155  	var totalLength uint64
   156  
   157  	r := bytes.NewBuffer(data)
   158  	if err := readAndCountSize(r, binary.LittleEndian, &table.BIOSCookie, &totalLength); err != nil {
   159  		return nil, 0, err
   160  	}
   161  	if table.BIOSCookie != BIOSDirectoryTableCookie && table.BIOSCookie != BIOSDirectoryTableLevel2Cookie {
   162  		return nil, 0, fmt.Errorf("incorrect cookie: %d", table.BIOSCookie)
   163  	}
   164  
   165  	if err := readAndCountSize(r, binary.LittleEndian, &table.Checksum, &totalLength); err != nil {
   166  		return nil, 0, err
   167  	}
   168  
   169  	if err := readAndCountSize(r, binary.LittleEndian, &table.TotalEntries, &totalLength); err != nil {
   170  		return nil, 0, err
   171  	}
   172  	if err := readAndCountSize(r, binary.LittleEndian, &table.Reserved, &totalLength); err != nil {
   173  		return nil, 0, err
   174  	}
   175  
   176  	sizeRequired := uint64(table.TotalEntries) * BIOSDirectoryTableEntrySize
   177  	if uint64(r.Len()) < sizeRequired {
   178  		return nil, 0, fmt.Errorf("not enough data, required: %d, actual: %d", sizeRequired+totalLength, len(data))
   179  	}
   180  
   181  	table.Entries = make([]BIOSDirectoryTableEntry, 0, table.TotalEntries)
   182  	for idx := uint32(0); idx < table.TotalEntries; idx++ {
   183  		entry, length, err := ParseBIOSDirectoryTableEntry(r)
   184  		if err != nil {
   185  			return nil, 0, err
   186  		}
   187  		table.Entries = append(table.Entries, *entry)
   188  		totalLength += length
   189  	}
   190  	return &table, totalLength, nil
   191  }
   192  
   193  // ParseBIOSDirectoryTableEntry converts input bytes into BIOSDirectoryTableEntry
   194  func ParseBIOSDirectoryTableEntry(r io.Reader) (*BIOSDirectoryTableEntry, uint64, error) {
   195  	var entry BIOSDirectoryTableEntry
   196  	var length uint64
   197  	if err := readAndCountSize(r, binary.LittleEndian, &entry.Type, &length); err != nil {
   198  		return nil, 0, err
   199  	}
   200  	if err := readAndCountSize(r, binary.LittleEndian, &entry.RegionType, &length); err != nil {
   201  		return nil, 0, err
   202  	}
   203  
   204  	var flags uint8
   205  	if err := readAndCountSize(r, binary.LittleEndian, &flags, &length); err != nil {
   206  		return nil, 0, err
   207  	}
   208  	entry.ResetImage = flags&0x1 != 0
   209  	entry.CopyImage = (flags>>1)&0x1 != 0
   210  	entry.ReadOnly = (flags>>2)&0x1 != 0
   211  	entry.Compressed = (flags>>3)&0x1 != 0
   212  	entry.Instance = flags >> 4
   213  
   214  	if err := readAndCountSize(r, binary.LittleEndian, &flags, &length); err != nil {
   215  		return nil, 0, err
   216  	}
   217  	entry.Subprogram = flags & 7
   218  	entry.RomID = (flags >> 3) & 0x3
   219  
   220  	if err := readAndCountSize(r, binary.LittleEndian, &entry.Size, &length); err != nil {
   221  		return nil, 0, err
   222  	}
   223  	if err := readAndCountSize(r, binary.LittleEndian, &entry.SourceAddress, &length); err != nil {
   224  		return nil, 0, err
   225  	}
   226  	if err := readAndCountSize(r, binary.LittleEndian, &entry.DestinationAddress, &length); err != nil {
   227  		return nil, 0, err
   228  	}
   229  	return &entry, length, nil
   230  }