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 }