github.com/linuxboot/fiano@v1.2.0/pkg/amd/psb/pspentries.go (about)

     1  // Copyright 2023 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 psb
     6  
     7  import (
     8  	"errors"
     9  	"fmt"
    10  	"os"
    11  	"strings"
    12  
    13  	amd_manifest "github.com/linuxboot/fiano/pkg/amd/manifest"
    14  
    15  	"github.com/jedib0t/go-pretty/v6/table"
    16  )
    17  
    18  const (
    19  	// AMDPublicKeyEntry denotes AMD public key entry in PSP Directory table
    20  	AMDPublicKeyEntry amd_manifest.PSPDirectoryTableEntryType = 0x00
    21  
    22  	// PSPRecoveryBootloader is a recovery instance of PSP bootloader
    23  	PSPRecoveryBootloader amd_manifest.PSPDirectoryTableEntryType = 0x03
    24  
    25  	// SMUOffChipFirmwareEntry points to a region of firmware containing SMU offchip firmware
    26  	SMUOffChipFirmwareEntry amd_manifest.PSPDirectoryTableEntryType = 0x08
    27  
    28  	// ABLPublicKey represents the key used to sign ABL firmware
    29  	ABLPublicKey amd_manifest.PSPDirectoryTableEntryType = 0x0A
    30  
    31  	// SMUOffChipFirmware2Entry points to a region of firmware containing SMU offchip firmware
    32  	SMUOffChipFirmware2Entry amd_manifest.PSPDirectoryTableEntryType = 0x12
    33  
    34  	// UnlockDebugImageEntry points to a region of firmware containing PSP early secure unlock debug image
    35  	UnlockDebugImageEntry amd_manifest.PSPDirectoryTableEntryType = 0x13
    36  
    37  	// SecurityPolicyBinaryEntry points to a region of firmware containing Security Policy Binary
    38  	SecurityPolicyBinaryEntry amd_manifest.PSPDirectoryTableEntryType = 0x24
    39  
    40  	// MP5FirmwareEntry points to a region of firmware containing MP5 Firmware
    41  	MP5FirmwareEntry amd_manifest.PSPDirectoryTableEntryType = 0x2A
    42  
    43  	// AGESABinary0Entry points to a region of firmware containing PSP AGESA Binary 0
    44  	AGESABinary0Entry amd_manifest.PSPDirectoryTableEntryType = 0x30
    45  
    46  	// SEVCodeEntry points to a region of firmware containing SEV Code
    47  	SEVCodeEntry amd_manifest.PSPDirectoryTableEntryType = 0x39
    48  
    49  	// DXIOPHYSRAMFirmwareEntry points to a region of firmware containing DXIO PHY SRAM firmware
    50  	DXIOPHYSRAMFirmwareEntry amd_manifest.PSPDirectoryTableEntryType = 0x42
    51  
    52  	//DRTMTAEntry points to a region of firmware containing DRTM TA
    53  	DRTMTAEntry amd_manifest.PSPDirectoryTableEntryType = 0x47
    54  
    55  	// KeyDatabaseEntry points to region of firmware containing key database
    56  	KeyDatabaseEntry amd_manifest.PSPDirectoryTableEntryType = 0x50
    57  
    58  	// OEMSigningKeyEntry represents the OEM signing key
    59  	OEMSigningKeyEntry amd_manifest.BIOSDirectoryTableEntryType = 0x05
    60  
    61  	// BIOSRTMVolumeEntry represents the RTM volume
    62  	BIOSRTMVolumeEntry amd_manifest.BIOSDirectoryTableEntryType = 0x62
    63  
    64  	// BIOSRTMSignatureEntry represents the entry holding the RTM volume signature
    65  	BIOSRTMSignatureEntry amd_manifest.BIOSDirectoryTableEntryType = 0x07
    66  )
    67  
    68  // PSPEntryType defines the type to hold PSP Entry Type fields
    69  type PSPEntryType uint8
    70  
    71  /*
    72   * Nicely output human-readable names for PSP Entry Types
    73   *
    74   * This doesn't have all the entries mapped, there are still
    75   * several more pages left. It does have all the types
    76   * encountered in the firmware images used to test
    77   * however.
    78   *
    79   */
    80  func (_type PSPEntryType) String() string {
    81  	switch _type {
    82  	case 0x00:
    83  		return "AMD_PUBLIC_KEYS"
    84  	case 0x01:
    85  		return "PSP_BOOT_LOADER"
    86  	case 0x02:
    87  		return "PSP_SECURE_OS"
    88  	case 0x03:
    89  		return "PSP_RECOVERY_BOOTLOADER"
    90  	case 0x04:
    91  		return "PSP_NON_VOLATILE_DATA"
    92  	case 0x08:
    93  		return "SMU_OFF_CHIP_FIRMWARE"
    94  	case 0x09:
    95  		return "AMD_SECURE_DEBUG_KEY"
    96  	case 0x0A:
    97  		return "ABL_PUBLIC_KEY"
    98  	case 0x0B:
    99  		return "PSP_SOFT_FUSE_CHAIN"
   100  	case 0x0C:
   101  		return "PSP_BOOT_LOADED_TRUSTLETS"
   102  	case 0x0D:
   103  		return "PSP_TRUSTLET_PUBLIC_KEY"
   104  	case 0x12:
   105  		return "SMU_OFF_CHIP_FIRMWARE"
   106  	case 0x13:
   107  		return "UNLOCK_DEBUG_IMAGE"
   108  	case 0x20:
   109  		return "IP_DISCOVERY_BINARY"
   110  	case 0x21:
   111  		return "WRAPPED_IKEK"
   112  	case 0x22:
   113  		return "PSP_TOKEN_UNLOCK_DATA"
   114  	case 0x24:
   115  		return "SEC_POLICY_BINARY"
   116  	case 0x25:
   117  		return "MP2_FIRMWARE"
   118  	case 0x26:
   119  		return "MP2_FIRMWARE_PART_TWO"
   120  	case 0x27:
   121  		return "USER_MODE_UNIT_TESTS"
   122  	case 0x28:
   123  		return "SYSTEM_DRIVER_IN_SPI"
   124  	case 0x29:
   125  		return "KVM_IMAGE"
   126  	case 0x2A:
   127  		return "MP5_FIRMWARE"
   128  	case 0x2B:
   129  		return "EMBEDDED_FIRMWARE_STRUCTURE"
   130  	case 0x2C:
   131  		return "TEE_WRITE_ONCE_NVRAM"
   132  	case 0x2D:
   133  		return "EXTERNAL_PSP_BOOTLOADER"
   134  	case 0x2E:
   135  		return "EXTERNAL_MP0"
   136  	case 0x2F:
   137  		return "EXTERNAL_MP1"
   138  	case 0x30:
   139  		return "AGESA_BINARY_0"
   140  	case 0x31:
   141  		return "AGESA_BINARY_1"
   142  	case 0x32:
   143  		return "AGESA_BINARY_2"
   144  	case 0x33:
   145  		return "AGESA_BINARY_3"
   146  	case 0x34:
   147  		return "AGESA_BINARY_4"
   148  	case 0x35:
   149  		return "AGESA_BINARY_5"
   150  	case 0x36:
   151  		return "AGESA_BINARY_6"
   152  	case 0x37:
   153  		return "AGESA_BINARY_7"
   154  	case 0x38:
   155  		return "SEV_DATA"
   156  	case 0x39:
   157  		return "SEV_CODE"
   158  	case 0x3A:
   159  		return "PROCESSOR_SERIAL_WHITELIST"
   160  	case 0x3B:
   161  		return "SERDES_MICROCODE"
   162  	case 0x3C:
   163  		return "VBIOS_PRELOAD"
   164  	case 0x3D:
   165  		return "WLAN_UMAC"
   166  	case 0x3E:
   167  		return "WLAN_IMAC"
   168  	case 0x3F:
   169  		return "WLAN_BLUETOOTH"
   170  	case 0x40:
   171  		return "PSP_DIRECTORY_LEVEL_2"
   172  	case 0x41:
   173  		return "EXTERNAL_MP0_BOOTLOADER"
   174  	case 0x42:
   175  		return "EXTERNAL_DXIO_SRAM_FIRMWARE"
   176  	case 0x43:
   177  		return "EXTERNAL_DXIO_SRAM_PUBLIC_KEY"
   178  	case 0x44:
   179  		return "USB_UNIFIED_PHY_FIRMWARE"
   180  	case 0x45:
   181  		return "SEC_POLICY_BINARY_TOS"
   182  	case 0x46:
   183  		return "EXTERNAL_PSP_BOOTLOADER"
   184  	case 0x47:
   185  		return "DRTM_TA"
   186  	// ... skipped entries ...
   187  	case 0x50:
   188  		return "SPI_ROM_PUBLIC_KEYS"
   189  	}
   190  	return "UNKNOWN"
   191  }
   192  
   193  func getPSPTable(pspFirmware *amd_manifest.PSPFirmware, pspLevel uint) (*amd_manifest.PSPDirectoryTable, error) {
   194  	switch pspLevel {
   195  	case 1:
   196  		return pspFirmware.PSPDirectoryLevel1, nil
   197  	case 2:
   198  		return pspFirmware.PSPDirectoryLevel2, nil
   199  	}
   200  	return nil, fmt.Errorf("cannot extract raw PSP entry, invalid PSP Directory Level requested: %d", pspLevel)
   201  }
   202  
   203  // OutputPSPEntries outputs the PSP entries in an ASCII table format
   204  func OutputPSPEntries(amdFw *amd_manifest.AMDFirmware) error {
   205  	pspDirectoryLevel1Table, err := getPSPTable(amdFw.PSPFirmware(), 1)
   206  	if err != nil {
   207  		return fmt.Errorf("unable to retrieve PSP Directory Level 1 Entries: %w", err)
   208  	}
   209  
   210  	pspDirectoryLevel2Table, err := getPSPTable(amdFw.PSPFirmware(), 2)
   211  	if err != nil {
   212  		return fmt.Errorf("unable to retrieve PSP Directory Level 2 Entries: %w", err)
   213  	}
   214  
   215  	pspDirectories := []amd_manifest.PSPDirectoryTable{*pspDirectoryLevel1Table, *pspDirectoryLevel2Table}
   216  
   217  	for idx, directory := range pspDirectories {
   218  		// PSP Header
   219  		h := table.NewWriter()
   220  		h.SetOutputMirror(os.Stdout)
   221  		h.SetTitle("PSP Directory Level %d Header", idx+1)
   222  		pspCookie := fmt.Sprintf("0x%x", directory.PSPCookie)
   223  		pspChecksum := directory.Checksum
   224  		pspTotalEntries := directory.TotalEntries
   225  		pspAdditionalInfo := fmt.Sprintf("0x%x", directory.AdditionalInfo)
   226  		h.AppendHeader(table.Row{"PSP Cookie", "Checksum", "Total Entries", "Additional Info"})
   227  		h.AppendRow([]interface{}{pspCookie, pspChecksum, pspTotalEntries, pspAdditionalInfo})
   228  		h.Render()
   229  
   230  		// PSP Entries
   231  		t := table.NewWriter()
   232  		t.SetOutputMirror(os.Stdout)
   233  		t.SetTitle("PSP Directory Level %d", idx+1)
   234  		t.AppendHeader(table.Row{"Type", "Hex Type", "SubProgram", "ROM ID", "Size", "Location/Value"})
   235  		for _, entry := range directory.Entries {
   236  			entryType := PSPEntryType(entry.Type)
   237  			entryTypeHex := fmt.Sprintf("0x%-3x", entry.Type)
   238  			entrySubprogram := fmt.Sprintf("0x%-8x", entry.Subprogram)
   239  			entryRomID := fmt.Sprintf("0x%-3x", entry.ROMId)
   240  			entrySize := fmt.Sprintf("%-10d", entry.Size)
   241  			entryLocation := fmt.Sprintf("0x%-10x", entry.LocationOrValue)
   242  			t.AppendRow([]interface{}{entryType, entryTypeHex, entrySubprogram, entryRomID, entrySize, entryLocation})
   243  		}
   244  		t.Render()
   245  	}
   246  	return nil
   247  }
   248  
   249  // ValidatePSPEntries validates signature of PSP entries given their entry values in PSP/BIOS Table
   250  func ValidatePSPEntries(amdFw *amd_manifest.AMDFirmware, keyDB KeySet, directory DirectoryType, entries []uint32) ([]SignatureValidationResult, error) {
   251  	validationResults := make([]SignatureValidationResult, 0, len(entries))
   252  
   253  	for _, entry := range entries {
   254  		entries, err := GetEntries(amdFw.PSPFirmware(), directory, entry)
   255  		if err != nil {
   256  			return nil, fmt.Errorf("could not extract entry 0x%x from PSP table: %w", entry, err)
   257  		}
   258  		if len(entries) == 0 {
   259  			return nil, fmt.Errorf("no entries %d are found in '%s'", entry, directory)
   260  		}
   261  
   262  		for _, entry := range entries {
   263  			validationResult, err := ValidatePSPEntry(amdFw, keyDB, entry.Offset, entry.Length)
   264  			if err != nil {
   265  				return nil, err
   266  			}
   267  			validationResults = append(validationResults, validationResult)
   268  		}
   269  	}
   270  	return validationResults, nil
   271  }
   272  
   273  // ValidatePSPEntry validates signature of a PSP entry
   274  func ValidatePSPEntry(amdFw *amd_manifest.AMDFirmware, keyDB KeySet, offset, length uint64) (SignatureValidationResult, error) {
   275  	image := amdFw.Firmware().ImageBytes()
   276  	data, err := GetRangeBytes(image, offset, length)
   277  	if err != nil {
   278  		return SignatureValidationResult{}, err
   279  	}
   280  
   281  	binary, err := newPSPBinary(data)
   282  	if err != nil {
   283  		return SignatureValidationResult{}, fmt.Errorf("could not create PSB binary from raw data for entry: 0x%x-0x%x: %w", offset, offset+length, err)
   284  	}
   285  	signedBlob, err := binary.getSignedBlob(keyDB)
   286  	var signedElement strings.Builder
   287  	fmt.Fprintf(&signedElement, "PSP entry 0x%x-0x%x", offset, offset+length)
   288  
   289  	if err != nil {
   290  		var sigError *SignatureCheckError
   291  		if errors.As(err, &sigError) {
   292  			return SignatureValidationResult{signedElement: signedElement.String(), signingKey: sigError.SigningKey(), err: err}, nil
   293  		}
   294  		return SignatureValidationResult{signedElement: signedElement.String(), err: err}, nil
   295  	}
   296  
   297  	signature := signedBlob.Signature()
   298  	return SignatureValidationResult{signedElement: signedElement.String(), signingKey: signature.SigningKey()}, nil
   299  }