github.com/linuxboot/fiano@v1.2.0/pkg/amd/manifest/psp_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  // PSPDirectoryTableCookie is a special identifier of PSP Directory table level 1
    21  const PSPDirectoryTableCookie = 0x50535024 // "$PSP"
    22  // PSPDirectoryTableLevel2Cookie is a special identifier of PSP Directory table level 2
    23  const PSPDirectoryTableLevel2Cookie = 0x324C5024 // "$PL2"
    24  
    25  // PSPDirectoryTableEntryType is an entry type of PSP Directory table
    26  type PSPDirectoryTableEntryType uint8
    27  
    28  const (
    29  	// AMDPublicKeyEntry denotes AMD public key entry in PSP Directory table
    30  	AMDPublicKeyEntry PSPDirectoryTableEntryType = 0x00
    31  	// PSPBootloaderFirmwareEntry denotes a PSP bootloader firmware entry in PSP Directory table
    32  	PSPBootloaderFirmwareEntry PSPDirectoryTableEntryType = 0x01
    33  	// PSPDirectoryTableLevel2Entry denotes an entry that points to PSP Directory table level 2
    34  	PSPDirectoryTableLevel2Entry PSPDirectoryTableEntryType = 0x40
    35  )
    36  
    37  // PSPDirectoryTableEntry represents a single entry in PSP Directory Table
    38  // Table 5 in (1)
    39  type PSPDirectoryTableEntry struct {
    40  	Type            PSPDirectoryTableEntryType
    41  	Subprogram      uint8
    42  	ROMId           uint8
    43  	Size            uint32
    44  	LocationOrValue uint64
    45  }
    46  
    47  const PSPDirectoryTableEntrySize = 16
    48  
    49  // PSPDirectoryTableHeader represents a BIOS Directory Table Header
    50  // Tables 3&4 from (1)
    51  type PSPDirectoryTableHeader struct {
    52  	PSPCookie      uint32
    53  	Checksum       uint32
    54  	TotalEntries   uint32
    55  	AdditionalInfo uint32
    56  }
    57  
    58  // PSPDirectoryTable represents PSP Directory Table Header with all entries
    59  // Table 5 in (1)
    60  type PSPDirectoryTable struct {
    61  	PSPDirectoryTableHeader
    62  
    63  	Entries []PSPDirectoryTableEntry
    64  }
    65  
    66  func (p PSPDirectoryTable) String() string {
    67  	var s strings.Builder
    68  	cookieBytes := make([]byte, 4)
    69  	binary.LittleEndian.PutUint32(cookieBytes, p.PSPCookie)
    70  	fmt.Fprintf(&s, "PSP Cookie: 0x%x (%s)\n", p.PSPCookie, cookieBytes)
    71  	fmt.Fprintf(&s, "Checksum: %d\n", p.Checksum)
    72  	fmt.Fprintf(&s, "Total Entries: %d\n", p.TotalEntries)
    73  	fmt.Fprintf(&s, "Additional Info: 0x%x\n\n", p.AdditionalInfo)
    74  	fmt.Fprintf(&s, "%-5s | %-8s | %-5s | %-10s | %-10s\n",
    75  		"Type",
    76  		"Subprogram",
    77  		"ROMId",
    78  		"Size",
    79  		"Location/Value")
    80  	fmt.Fprintf(&s, "%s\n", "------------------------------------------------------------------------")
    81  	for _, entry := range p.Entries {
    82  		fmt.Fprintf(&s, "0x%-3x | 0x%-8x | 0x%-3x | %-10d | 0x%-10x\n",
    83  			entry.Type,
    84  			entry.Subprogram,
    85  			entry.ROMId,
    86  			entry.Size,
    87  			entry.LocationOrValue)
    88  	}
    89  	return s.String()
    90  }
    91  
    92  // FindPSPDirectoryTable scans firmware for PSPDirectoryTableCookie
    93  // and treats remaining bytes as PSPDirectoryTable
    94  func FindPSPDirectoryTable(image []byte) (*PSPDirectoryTable, bytes2.Range, error) {
    95  	// there is no predefined address, search through the whole memory
    96  	cookieBytes := make([]byte, 4)
    97  	binary.LittleEndian.PutUint32(cookieBytes, PSPDirectoryTableCookie)
    98  
    99  	var offset uint64
   100  	for {
   101  		idx := bytes.Index(image, cookieBytes)
   102  		if idx == -1 {
   103  			break
   104  		}
   105  
   106  		table, length, err := ParsePSPDirectoryTable(image[idx:])
   107  		if err != nil {
   108  			shift := uint64(idx + len(cookieBytes))
   109  			image = image[idx+len(cookieBytes):]
   110  			offset += shift
   111  			continue
   112  		}
   113  		return table, bytes2.Range{Offset: offset + uint64(idx), Length: length}, err
   114  	}
   115  	return nil, bytes2.Range{}, fmt.Errorf("PSPDirectoryTable is not found")
   116  }
   117  
   118  // ParsePSPDirectoryTable converts input bytes into PSPDirectoryTable
   119  func ParsePSPDirectoryTable(data []byte) (*PSPDirectoryTable, uint64, error) {
   120  	var table PSPDirectoryTable
   121  	var totalLength uint64
   122  
   123  	r := bytes.NewBuffer(data)
   124  	if err := readAndCountSize(r, binary.LittleEndian, &table.PSPCookie, &totalLength); err != nil {
   125  		return nil, 0, err
   126  	}
   127  	if table.PSPCookie != PSPDirectoryTableCookie && table.PSPCookie != PSPDirectoryTableLevel2Cookie {
   128  		return nil, 0, fmt.Errorf("incorrect cookie: %d", table.PSPCookie)
   129  	}
   130  	if err := readAndCountSize(r, binary.LittleEndian, &table.Checksum, &totalLength); err != nil {
   131  		return nil, 0, err
   132  	}
   133  	if err := readAndCountSize(r, binary.LittleEndian, &table.TotalEntries, &totalLength); err != nil {
   134  		return nil, 0, err
   135  	}
   136  	if err := readAndCountSize(r, binary.LittleEndian, &table.AdditionalInfo, &totalLength); err != nil {
   137  		return nil, 0, err
   138  	}
   139  
   140  	sizeRequired := uint64(table.TotalEntries) * PSPDirectoryTableEntrySize
   141  	if uint64(r.Len()) < sizeRequired {
   142  		return nil, 0, fmt.Errorf("not enough data, required: %d, actual: %d", sizeRequired+totalLength, len(data))
   143  	}
   144  
   145  	for idx := uint32(0); idx < table.TotalEntries; idx++ {
   146  		entry, length, err := ParsePSPDirectoryTableEntry(r)
   147  		if err != nil {
   148  			return nil, 0, err
   149  		}
   150  		totalLength += length
   151  		table.Entries = append(table.Entries, *entry)
   152  	}
   153  	return &table, totalLength, nil
   154  }
   155  
   156  // ParsePSPDirectoryTableEntry converts input bytes into PSPDirectoryTableEntry
   157  func ParsePSPDirectoryTableEntry(r io.Reader) (*PSPDirectoryTableEntry, uint64, error) {
   158  	var entry PSPDirectoryTableEntry
   159  	var length uint64
   160  
   161  	if err := readAndCountSize(r, binary.LittleEndian, &entry.Type, &length); err != nil {
   162  		return nil, 0, err
   163  	}
   164  	if err := readAndCountSize(r, binary.LittleEndian, &entry.Subprogram, &length); err != nil {
   165  		return nil, 0, err
   166  	}
   167  
   168  	var flags uint16
   169  	if err := readAndCountSize(r, binary.LittleEndian, &flags, &length); err != nil {
   170  		return nil, 0, err
   171  	}
   172  	entry.ROMId = uint8(flags>>14) & 0x3
   173  
   174  	if err := readAndCountSize(r, binary.LittleEndian, &entry.Size, &length); err != nil {
   175  		return nil, 0, err
   176  	}
   177  	if err := readAndCountSize(r, binary.LittleEndian, &entry.LocationOrValue, &length); err != nil {
   178  		return nil, 0, err
   179  	}
   180  	return &entry, length, nil
   181  }