From 61e5cdf388f8c748fef3d5f5af82640fa79cc4c5 Mon Sep 17 00:00:00 2001 From: zwei4 Date: Fri, 10 Mar 2017 15:52:07 +0800 Subject: Add BPDT library. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: zwei4 --- Silicon/BroxtonSoC/BroxtonSiPkg/BroxtonSiPkg.dec | 4 +- .../BroxtonSiPkg/Include/Library/BpdtLib.h | 240 +++++++ .../BroxtonSiPkg/Library/BpdtLib/BpdtLib.c | 798 +++++++++++++++++++++ .../BroxtonSiPkg/Library/BpdtLib/BpdtLib.inf | 49 ++ 4 files changed, 1090 insertions(+), 1 deletion(-) create mode 100644 Silicon/BroxtonSoC/BroxtonSiPkg/Include/Library/BpdtLib.h create mode 100644 Silicon/BroxtonSoC/BroxtonSiPkg/Library/BpdtLib/BpdtLib.c create mode 100644 Silicon/BroxtonSoC/BroxtonSiPkg/Library/BpdtLib/BpdtLib.inf diff --git a/Silicon/BroxtonSoC/BroxtonSiPkg/BroxtonSiPkg.dec b/Silicon/BroxtonSoC/BroxtonSiPkg/BroxtonSiPkg.dec index 2ee4008c63..da728e8886 100644 --- a/Silicon/BroxtonSoC/BroxtonSiPkg/BroxtonSiPkg.dec +++ b/Silicon/BroxtonSoC/BroxtonSiPkg/BroxtonSiPkg.dec @@ -238,7 +238,9 @@ gDciConfigGuid = { 0x9af1e14a, 0xe6b7, 0x4b64, { 0xbb, 0x33, 0x9b, 0x1, 0x33, 0x4c, 0x24, 0xfe } } gP2sbConfigGuid = { 0x11354a0c, 0x781e, 0x44a1, { 0xa7, 0x87, 0xc0, 0x17, 0x8c, 0x8d, 0x57, 0xf } } gInterruptConfigGuid = { 0x097dccd0, 0xf570, 0x41f6, { 0x9d, 0xf0, 0x72, 0xe2, 0x5a, 0xbe, 0xd3, 0x98 } } - + gEfiBpdtLibBp1DataGuid = { 0x7a4ecf02, 0xa1cf, 0x4e42, { 0xb6, 0x33, 0xfe, 0x95, 0x54, 0xbb, 0x60, 0xf8 } } + gEfiBpdtLibBp2DataGuid = { 0x75b0886e, 0xcd6a, 0x469f, { 0xae, 0x0b, 0x8c, 0xed, 0x90, 0x33, 0xd1, 0x99 } } + [Includes.common] Include Library diff --git a/Silicon/BroxtonSoC/BroxtonSiPkg/Include/Library/BpdtLib.h b/Silicon/BroxtonSoC/BroxtonSiPkg/Include/Library/BpdtLib.h new file mode 100644 index 0000000000..87683ab90e --- /dev/null +++ b/Silicon/BroxtonSoC/BroxtonSiPkg/Include/Library/BpdtLib.h @@ -0,0 +1,240 @@ +/** @file + Interface definitons of BPDT(Boot Partition Description Table) library. + + Copyright (c) 2006 - 2017, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __BPDT_HEADER_H__ +#define __BPDT_HEADER_H__ + +extern EFI_GUID gEfiBpdtLibBp1DataGuid; +extern EFI_GUID gEfiBpdtLibBp2DataGuid; + +#define BPDT_SIGN_GREEN 0x000055AA //Normal Boot +#define BPDT_SIGN_YELLOW 0x00AA55AA //Recovery Boot + +typedef enum { + BootPart1, + BootPart2, + BootPartMax +} BOOT_PARITION_SELECT; + +typedef struct { + VOID *DataPtr; + UINT32 Size; +} BPDT_PAYLOAD_DATA; + +typedef enum { + BpdtOemSmip, // 0 + BpdtCseRbe, // 1 + BpdtCseBup, // 2 + BpdtUcode, // 3 + BpdtIbb, // 4 + BpdtSbpdt, // 5 - Secondary BPDT within a BPDT Entry + BpdtObb, // 6 + BpdtCseMain, // 7 + BpdtIsh, // 8 + BpdtCseIdlm, // 9 + BpdtIfpOverride, //10 + BpdtDebugTokens, //11 + BpdtUfsPhyConfig, //12 + BpdtUfsGppLunId, //13 + BpdtPmc, //14 + BpdtIunit, //15 + BpdtMaxType +} BPDT_ENTRY_TYPES; + +typedef struct { + UINT32 Signature; + UINT16 DscCount; + UINT16 BpdtVer; + UINT32 RedundantBlockXorSum; + UINT32 IfwiVer; + UINT64 FitToolVer; +} BPDT_HEADER; + + +typedef struct { + UINT16 Type; + UINT16 Flags; + UINT32 LbpOffset; // Offset of Sub-Partition starting from base of LBP + UINT32 Size; // Size of the Sub-Partition +} BPDT_ENTRY; + + +typedef struct { + UINT32 HeaderMarker; + UINT32 NumOfEntries; + UINT8 HeaderVersion; + UINT8 EntryVersion; + UINT8 HeaderLength; + UINT8 Checksum; + UINT32 SubPartName; +} SUBPART_DIR_HEADER; + + +typedef struct { + CHAR8 EntryName[12]; + UINT32 EntryOffset:25; + UINT32 Huffman:1; + UINT32 Reserved1:6; + UINT32 EntrySize; + UINT32 Reserved2; +} SUBPART_DIR_ENTRY; + +typedef enum { + HashIbbl = 0, + HashIbbm, + HashObb +} BPM_HASH_SELECT; + +/// +/// Boot Policy Manifest (BPM) data structure +/// +typedef struct { + UINT32 ExtType; ///< 0x00000013 (Little-Endian) + UINT32 ExtLength; + UINT32 NemData; + UINT32 IbblHashAlgo; ///< 0x00000002 + UINT32 IbblHashSize; ///< 0x00000020 + UINT8 IbblHash[0x20]; + UINT32 IbbmHashAlgo; + UINT32 IbbmHashSize; + UINT8 IbbmHash[0x20]; + UINT32 ObbHashAlgo; + UINT32 ObbHashSize; + UINT8 ObbHash[0x20]; + UINT8 Reserved[124]; ///< Others data +} BPM_DATA_FILE; + + +/** + Locate and return a pointer to the selected Logical Boot Partition. + + For block devices, the address will be the location in memory that the BP was copied to. + For memory mapped device (eg SPI), the address will be the location on the device. + + @param[in] LbpSel Which BP to locate on the boot device + @param[out] Address The address of the selected BP on the boot device + + @retval EFI_SUCCESS The operation completed successfully. + @retval other The selected BP could not be found. +**/ +EFI_STATUS +EFIAPI +GetBootPartitionPointer ( + IN BOOT_PARITION_SELECT LbpSel, + OUT VOID **LbpPointer + ); + +/** + Parse the Boot Partition Descriptor Table of the provided Logical Boot Partition. + Note: "payload" is the Sub-Partition pointed to by the BDPT Entry of the given type. + + @param[in] LbpPointer Pointer to the start of the BootPartition to be parsed + @param[in] EntryType The entry type of the subparition(payload) to look for + @param[out] BpdtPayloadPtr Address of the Struct to put the results into (MUST be allocated by caller) + + @retval EFI_SUCCESS The operation completed successfully. +**/ +EFI_STATUS +EFIAPI +GetBpdtPayloadDataRaw ( + IN VOID *LbpPointer, + IN BPDT_ENTRY_TYPES EntryType, + OUT BPDT_PAYLOAD_DATA *BpdtPayloadPtr + ); + +/** + Parse the Boot Partition Descriptor Table on the selected Logical Boot Partition. + Takes the BP as input since some BPDT entry types can exist in both locations. + Note: "payload" is the Sub-Partition pointed to by the BDPT Entry of the given type. + + @param[in] BpSel Boot partition index + @param[in] EntryType The entry type of the subparition(payload) to look for + @param[out] BpdtPayloadPtr Pointer to a Struct (in a HOB) containing the Size and Absolute address + in memory(or spi) of the BPDT payload(subpart) of EntryType. + If the caller shadows the Payload for performance, it should update this value + + @retval EFI_SUCCESS The operation completed successfully. +**/ +EFI_STATUS +EFIAPI +GetBpdtPayloadData ( + IN BOOT_PARITION_SELECT BpSel, + IN BPDT_ENTRY_TYPES EntryType, + OUT BPDT_PAYLOAD_DATA **BpdtPayloadPtr + ); + + +/** + Search the Subpartition pointed to by BpdtPayloadPtr for a Directory entry with "EntryName", + then fill in the Struct pointed to by SubPartPayloadPtr with the Addres and Size of the Dir Entry. + Note: "payload" is the Data pointed to by the Directory Entry with the given name. + + @param[in] BpdtPayloadPtr Location and Size of the Payload to search + @param[in] EntryName the String name of the Directory entry to look for + @param[out] SubPartPayloadPtr Address of the Struct to put the results into (MUST be allocated by caller) + + @retval EFI_SUCCESS The operation completed successfully. +**/ +EFI_STATUS +EFIAPI +GetSubPartitionPayloadData ( + IN BPDT_PAYLOAD_DATA *BpdtPayloadPtr, + IN CONST CHAR8 *EntryName, + OUT BPDT_PAYLOAD_DATA *SubPartPayloadPtr + ); + + +/** + This is a wrapper function for using VerifyHashBpm that will only + check the hash for the payloads used during normal boot. + + If you want to check other payloads (ie during recovery flow), then the + calling code should locate and pass in the payload directly to VerifyHashBpm(). + + @param[in] PayloadSel Which hash in BPM to compare with. + + @retval EFI_SUCCESS The function completed successfully and the Hash matched. + @retval other An error occured when locating and computing the Hash. +**/ +EFI_STATUS +EFIAPI +LocateAndVerifyHashBpm ( + IN BPM_HASH_SELECT PayloadSel + ); + + +/** + Computes the Hash of a given data block and compares to the one in the Boot Policy Metadata. + + @param[in] PayloadSel Which hash in BPM to compare with. + @param[in] PayloadPtr Pointer to the begining of the data to be hashed. + @param[in] DataSize Size of the data to be hashed. + @param[in] BpmPtr Pointer to the BPM structure. + + @retval EFI_SUCCESS If the function completed successfully. + @retval EFI_ABORTED If the attempt to compute the hash fails. + @retval EFI_INVALID_PARAMETER If the hash type or size in BPM is unsupported. + @retval EFI_SECURITY_VIOLATION If the has does not match the one in BPM. +**/ +EFI_STATUS +EFIAPI +VerifyHashBpm ( + IN BPM_HASH_SELECT PayloadSel, + IN BPM_DATA_FILE *BpmPtr, + IN VOID *PayloadPtr, + IN UINT32 DataSize + ); + +#endif diff --git a/Silicon/BroxtonSoC/BroxtonSiPkg/Library/BpdtLib/BpdtLib.c b/Silicon/BroxtonSoC/BroxtonSiPkg/Library/BpdtLib/BpdtLib.c new file mode 100644 index 0000000000..b65e8ae836 --- /dev/null +++ b/Silicon/BroxtonSoC/BroxtonSiPkg/Library/BpdtLib/BpdtLib.c @@ -0,0 +1,798 @@ +/** @file + This file contains the implementation of BpdtLib library. + + The library exposes an abstract interface for accessing boot data + stored in the BPDT format on the Logical Boot Partition of the boot device. + + Copyright (c) 2016 - 2017, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DIR_HDR_SIGNATURE SIGNATURE_32('$', 'C', 'P', 'D') +#define DIR_ENTRY_NAME_SIZE 12 //This is spec defined to be bytes +#define SPLIT_ENTRY_PART_1 BIT0 +#define SPLIT_ENTRY_PART_2 BIT1 +#define SPLIT_ENTRY_FLAGS (BIT1 | BIT0) +#define BPDT_HOB_DATA_SIZE (sizeof(BPDT_PAYLOAD_DATA) * BpdtMaxType) +#define SpiMmioBoundary 0xFFFC0000 + +/** + Calculate the Address in SPI of the selected Boot Partition + + @param[in] BpSel Which BP to locate on the SPI + @param[out] BpFlashLinearAddress The linear flash address of selected BP + @param[out] LbpPointer A pointer to the selected Boot partition on this media + + @retval EFI_SUCCESS The operation completed successfully. +**/ +EFI_STATUS +EFIAPI +InternalGetBpSpi ( + IN BOOT_PARITION_SELECT BpSel, + OUT UINT32 *BpFlashLinearAddress, + OUT VOID **LbpPointer + ) +{ + UINT32 BiosAddr; + + // + // Compute BP FlashLinearAddress. + // + switch (BpSel) { + case BootPart1: + *BpFlashLinearAddress = 0x1000; + break; + case BootPart2: + if (HeciPciRead16(R_SEC_DevID_VID) != 0xFFFF) { + // + // BP2 linear address is the midpoint between BIOS base and expansion data base. + // + *BpFlashLinearAddress = (GetSpiFlashRegionBase(BIOS) + GetSpiFlashRegionBase(DeviceExpansion1)) / 2; + } else { + + *BpFlashLinearAddress = 0x00800000>>1; + } + break; + default: + return EFI_NOT_FOUND; + break; + } + DEBUG ((DEBUG_INFO, "BpFlashLinearAddress = %X\n", *BpFlashLinearAddress)); + + // + // Calculate Boot partition physical address. + // FLA[26:0] <= (Flash_Region_Limit) - (FFFF_FFFCh - bios_address) + // + BiosAddr = GetSpiFlashRegionLimit(BIOS) + 0xFFC - *BpFlashLinearAddress; + *LbpPointer = (VOID *)((UINTN)0xFFFFFFFC - BiosAddr); + DEBUG ((DEBUG_INFO, "Flash address of BP%d = %X\n", BpSel+1, *LbpPointer)); + + return EFI_SUCCESS; +} + +/** + SPI CMD read w/a. Because the upper 256k of address space is mapped to CSE SRAM, + it cannot be directly accessed via MMIO from host. + + @param[in] BpSel Which BP to locate on the SPI + @param[in out] CombinedSpiObb Combined OBB data. + + @retval EFI_SUCCESS The operation completed successfully. +**/ +EFI_STATUS +EFIAPI +InternalCheckAndJoinUpperSpi ( + IN BOOT_PARITION_SELECT BpSel, + IN OUT BPDT_PAYLOAD_DATA *CombinedSpiObb + ) +{ + VOID *UpperSpiObb; + VOID *LbpPointer; + UINT32 Bp2LinearAddr; + UINT32 SpiPart1Size; + UINT32 SpiPart2Size; + EFI_STATUS Status; + SC_SPI_PROTOCOL *SpiPpi; + CONST EFI_PEI_SERVICES **PeiServices; + + // + // Check if Obb part is in upper spi address space. + // If the entry is from emmc this will be false, it can only be true for SPI. + // + if ((UINT32)CombinedSpiObb->DataPtr + CombinedSpiObb->Size >= SpiMmioBoundary) { + + //Get Linear Addr of BpSel (on BXT this will be BP2, since it is) + InternalGetBpSpi (BpSel, &Bp2LinearAddr, &LbpPointer); + + SpiPart1Size = SpiMmioBoundary - (UINT32)CombinedSpiObb->DataPtr; + SpiPart2Size = CombinedSpiObb->Size - SpiPart1Size; + + UpperSpiObb = AllocatePages (EFI_SIZE_TO_PAGES (CombinedSpiObb->Size)); + + CopyMem (UpperSpiObb, CombinedSpiObb->DataPtr, SpiPart1Size); //Copy MemMapped part of Spi. + + // + // Install ScSpi Ppi. + // + InstallScSpi (); + PeiServices = GetPeiServicesTablePointer (); + Status = (*PeiServices)->LocatePpi ( + PeiServices, + &gScSpiPpiGuid, + 0, + NULL, + (VOID **) &SpiPpi + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Cannot Locate gScSpiPpiGuid: %r. Halting system.\n", Status)); + CpuDeadLoop (); + } + + // + // Use SPI cmd to read entry into memory. + // + Status = SpiPpi->FlashRead ( + SpiPpi, + FlashRegionBios, + Bp2LinearAddr + SpiPart1Size, + SpiPart2Size, + (UINT8 *)((UINTN)UpperSpiObb + SpiPart1Size) + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Read SPI fail: %r. Halting system.\n", Status)); + CpuDeadLoop (); + } + + CombinedSpiObb->DataPtr = UpperSpiObb; + } + return EFI_SUCCESS; +} + + +/** + This function will handle locating and combining both parts of Split Obb, + regardless of which BootPartition that part1 or part2 is located on. + + @param[in] BpSel Which BP to locate on the SPI + @param[out] ObbPayloadPtr Pointer to other part of Obb (hopefully on Bp1). + It does not matter if this ptr is actual memory or mmio, + since it needs to be combined anyway. + + @retval EFI_SUCCESS The operation completed successfully. + @retval other The selected BP could not be found. +**/ +EFI_STATUS +EFIAPI +InternalLocateSplitObb ( + IN BOOT_PARITION_SELECT BpSel, + OUT BPDT_PAYLOAD_DATA *ObbPayloadPtr + ) +{ + UINT8 index; + VOID *LbpPointer; + VOID *LbpOffset; + BPDT_HEADER *BpdtHeaderPtr; + BPDT_HEADER *sBpdtHeader; + BPDT_ENTRY *BpdtEntryPtr; + EFI_STATUS Status; + + Status = GetBootPartitionPointer (BpSel, &LbpPointer); + ASSERT_EFI_ERROR (Status); + + BpdtHeaderPtr = (BPDT_HEADER *)LbpPointer; + Status = EFI_NOT_FOUND; + do { + // + //Reset secondary header each time to ensure proper exit. + // + sBpdtHeader = NULL; + BpdtEntryPtr = (BPDT_ENTRY *)((UINTN)BpdtHeaderPtr + sizeof(BPDT_HEADER)); + + if (BpdtHeaderPtr->Signature != BPDT_SIGN_GREEN) { + DEBUG ((EFI_D_INFO, "Signature of BPDT Header is invalid - Stop Parsing BP%d.\n", BpSel)); + break; + } + for (index=0; index < BpdtHeaderPtr->DscCount; index++, BpdtEntryPtr++) { + if (BpdtEntryPtr->LbpOffset == 0) { + DEBUG ((EFI_D_INFO, "Skipping Stub-entry for Bpdt type: 0x%x \n", BpdtEntryPtr->Type)); + continue; + } + LbpOffset = (VOID *)((UINTN)LbpPointer + BpdtEntryPtr->LbpOffset); + + if (BpdtEntryPtr->Type == BpdtSbpdt) { + sBpdtHeader = (BPDT_HEADER *)LbpOffset; + continue; + } + if (BpdtEntryPtr->Type == BpdtObb) { + ObbPayloadPtr->DataPtr = LbpOffset; + ObbPayloadPtr->Size = BpdtEntryPtr->Size; + Status = EFI_SUCCESS; + break; + } + } + if (sBpdtHeader != NULL) { + BpdtHeaderPtr = sBpdtHeader; + } + } while (sBpdtHeader != NULL); + + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Unable to locate part of Split Obb: %r. Halting system.\n", Status)); + CpuDeadLoop (); + } + return Status; +} + + +/** + Locate and return a pointer to the selected Logical Boot Partition. + + For block devices, the address will be the location in memory that the BP was copied to. + For memory mapped device (eg SPI), the address will be the location on the device. + + @param[in] LbpSel Which BP to locate on the boot device + @param[out] Address The address of the selected BP on the boot device + + @retval EFI_SUCCESS The operation completed successfully. + @retval other The selected BP could not be found. +**/ +EFI_STATUS +EFIAPI +GetBootPartitionPointer ( + IN BOOT_PARITION_SELECT LbpSel, + OUT VOID **LbpPointer + ) +{ + UINT32 BpLinearAddr; + EFI_STATUS Status; + + DEBUG ((EFI_D_INFO, "CSE Boot Device is SPI.\n")); + Status = InternalGetBpSpi (LbpSel, &BpLinearAddr, LbpPointer); + + return Status; +} + +/** + Parse the Boot Partition Descriptor Table of the provided Logical Boot Partition. + Note: "payload" is the Sub-Partition pointed to by the BDPT Entry of the given type. + + @param[in] LbpPointer Pointer to the start of the BootPartition to be parsed + @param[in] EntryType The entry type of the subparition(payload) to look for + @param[out] BpdtPayloadPtr Address of the Struct to put the results into (MUST be allocated by caller) + + @retval EFI_SUCCESS The operation completed successfully. +**/ +EFI_STATUS +EFIAPI +GetBpdtPayloadDataRaw ( + IN VOID *LbpPointer, + IN BPDT_ENTRY_TYPES EntryType, + OUT BPDT_PAYLOAD_DATA *BpdtPayloadPtr + ) +{ + UINT8 Index; + VOID *LbpOffset; + BPDT_HEADER *BpdtHeaderPtr; + BPDT_HEADER *sBpdtHeader; + BPDT_ENTRY *BpdtEntryPtr; + + + BpdtHeaderPtr = (BPDT_HEADER *)LbpPointer; + do { + // + //Reset secondary header each time to ensure proper exit. + // + sBpdtHeader = NULL; + BpdtEntryPtr = (BPDT_ENTRY *)((UINTN)BpdtHeaderPtr + sizeof(BPDT_HEADER)); + + DEBUG ((DEBUG_INFO, "BpdtHeaderPtr->Signature = %x\n", BpdtHeaderPtr->Signature)); + if ((BpdtHeaderPtr->Signature != BPDT_SIGN_GREEN) && (BpdtHeaderPtr->Signature != BPDT_SIGN_YELLOW)) { + DEBUG ((EFI_D_ERROR, "Signature of BPDT Header is invalid - Stop Parsing RawBP.\n")); + return EFI_NOT_FOUND; + } + for (Index=0; Index < BpdtHeaderPtr->DscCount; Index++, BpdtEntryPtr++) { + if (BpdtEntryPtr->LbpOffset == 0) { + DEBUG ((EFI_D_INFO, "Skipping Stub-entry for Bpdt type: %d\n", BpdtEntryPtr->Type)); + continue; + } + LbpOffset = (VOID *)((UINTN)LbpPointer + BpdtEntryPtr->LbpOffset); + + if (BpdtEntryPtr->Type == BpdtSbpdt) { + sBpdtHeader = (BPDT_HEADER *)LbpOffset; + } + if (BpdtEntryPtr->Type == EntryType) { + DEBUG ((EFI_D_INFO, "Found BPDT Entry. Type: %d, Addr: 0x%x\n", BpdtEntryPtr->Type, LbpOffset)); + BpdtPayloadPtr->DataPtr = LbpOffset; + BpdtPayloadPtr->Size = BpdtEntryPtr->Size; + return EFI_SUCCESS; + } + } + if (sBpdtHeader != NULL) { + BpdtHeaderPtr = sBpdtHeader; + } + } while (sBpdtHeader != NULL); + + BpdtPayloadPtr->DataPtr = NULL; + BpdtPayloadPtr->Size = 0; + return EFI_NOT_FOUND; +} + +/** + Parse the Boot Partition Descriptor Table on the selected Logical Boot Partition. + Takes the BP as input since some BPDT entry types can exist in both locations. + Note: "payload" is the Sub-Partition pointed to by the BDPT Entry of the given type. + + @param[in] LbpSel Which BP to locate on the boot device + @param[in] EntryType The entry type of the subparition(payload) to look for + @param[out] BpdtPayloadPtr Pointer to a Struct (in a HOB) containing the Size and Absolute address + in memory(or spi) of the BPDT payload(subpart) of EntryType. + If the caller shadows the Payload for performance, it should update this value + + @retval EFI_SUCCESS The operation completed successfully. +**/ +EFI_STATUS +EFIAPI +GetBpdtPayloadData ( + IN BOOT_PARITION_SELECT BpSel, + IN BPDT_ENTRY_TYPES EntryType, + OUT BPDT_PAYLOAD_DATA **BpdtPayloadPtr + ) +{ + UINT8 Index; + VOID *LbpOffset; + VOID *LbpBuffer = NULL; + VOID *CombinedObb; + BPDT_PAYLOAD_DATA *BpdtPayloadList = NULL; + BPDT_PAYLOAD_DATA *AdditionalPayloadList = NULL; + BPDT_PAYLOAD_DATA AdditionalObbInfo; + BPDT_PAYLOAD_DATA *ObbInfoPart1Ptr; + BPDT_PAYLOAD_DATA *ObbInfoPart2Ptr; + BPDT_HEADER *BpdtHeaderPtr; + BPDT_HEADER *sBpdtHeader; + BPDT_ENTRY *BpdtEntryPtr; + BPDT_ENTRY *ObbEntryPtr = NULL; + EFI_HOB_GUID_TYPE *GuidHobPtr; + EFI_STATUS Status; + EFI_GUID CurrentBpdtDataGuid; + EFI_GUID AdditionalDataGuid; + BOOT_PARITION_SELECT AdditionalObbBp; + + // + // Set the data guid's to the correct HOB's based on the selected BP. + // If Current contains Split OBB, then Additional needs to be the BP with the other SplitObb. + // + switch (BpSel) { + case BootPart1: + CurrentBpdtDataGuid = gEfiBpdtLibBp1DataGuid; + AdditionalDataGuid = gEfiBpdtLibBp2DataGuid; + AdditionalObbBp = BootPart2; + break; + case BootPart2: + CurrentBpdtDataGuid = gEfiBpdtLibBp2DataGuid; + AdditionalDataGuid = gEfiBpdtLibBp1DataGuid; + AdditionalObbBp = BootPart1; + break; + default: + DEBUG ((EFI_D_ERROR, "Invalid BootPartion parameter: %d\n", BpSel)); + return EFI_NOT_FOUND; + break; + } + + *BpdtPayloadPtr = NULL; + GuidHobPtr = GetFirstGuidHob (&CurrentBpdtDataGuid); + if (GuidHobPtr != NULL) { + DEBUG ((EFI_D_INFO, "BPDT %d already parsed. Getting Payload info from HOB\n", BpSel+1)); + BpdtPayloadList = GET_GUID_HOB_DATA (GuidHobPtr); + *BpdtPayloadPtr = &BpdtPayloadList[EntryType]; + if (*BpdtPayloadPtr == NULL) { + DEBUG ((EFI_D_WARN, "Requested Payload info in NULL: %d\n", EntryType)); + return EFI_NOT_FOUND; + } else { + return EFI_SUCCESS; + } + } + + // + //Must use ZeroPool here to ensure correct error handling of missing entries. + // + BpdtPayloadList = AllocateZeroPool (BPDT_HOB_DATA_SIZE); + if (BpdtPayloadList == NULL) { + ASSERT (BpdtPayloadList != NULL); + return EFI_OUT_OF_RESOURCES; + } + + Status = GetBootPartitionPointer (BpSel, &LbpBuffer); + ASSERT_EFI_ERROR (Status); + + // + // Parse the BPDT and store the Entry offsets into the array. + // + BpdtHeaderPtr = (BPDT_HEADER *)LbpBuffer; + do { + // + //reset secondary header each time to ensure proper exit. + // + sBpdtHeader = NULL; + BpdtEntryPtr = (BPDT_ENTRY *)((UINTN)BpdtHeaderPtr + sizeof(BPDT_HEADER)); + + if (BpdtHeaderPtr->Signature != BPDT_SIGN_GREEN) { + DEBUG ((EFI_D_ERROR, "Signature of BPDT Header is invalid - Stop Parsing BP%d.\n", BpSel+1)); + return EFI_NOT_FOUND; + } + for (Index=0; Index < BpdtHeaderPtr->DscCount; Index++, BpdtEntryPtr++) { + if (BpdtEntryPtr->LbpOffset == 0) { + DEBUG ((EFI_D_INFO, "Skipping Stub-entry for Bpdt type: %d\n", BpdtEntryPtr->Type)); + continue; + } + LbpOffset = (VOID *)((UINTN)LbpBuffer + BpdtEntryPtr->LbpOffset); + + if (BpdtEntryPtr->Type == BpdtSbpdt) { + sBpdtHeader = (BPDT_HEADER *)LbpOffset; + } + if (BpdtEntryPtr->Type < BpdtMaxType) { + DEBUG ((EFI_D_INFO, "Saving Info of BPDT Entry. Type: %d, Addr: 0x%x\n", BpdtEntryPtr->Type, LbpOffset)); + BpdtPayloadList[BpdtEntryPtr->Type].DataPtr = LbpOffset; + BpdtPayloadList[BpdtEntryPtr->Type].Size = BpdtEntryPtr->Size; + } + if (BpdtEntryPtr->Type == BpdtObb) { + ObbEntryPtr = BpdtEntryPtr; + } + } + if (sBpdtHeader != NULL) { + BpdtHeaderPtr = sBpdtHeader; + } + } while (sBpdtHeader != NULL); + + + // + // If the additional data HOB contains the address of Obb, then the special cases for + // Obb were already handled by a previous call to this function (no need to recombine =). + // + GuidHobPtr = GetFirstGuidHob (&AdditionalDataGuid); + if (GuidHobPtr != NULL) { + AdditionalPayloadList = GET_GUID_HOB_DATA (GuidHobPtr); + } + if (AdditionalPayloadList != NULL && AdditionalPayloadList[BpdtObb].DataPtr != NULL) { + // + // using copymem will auto scale if the struct defintion changes. + // + CopyMem (&BpdtPayloadList[BpdtObb], &AdditionalPayloadList[BpdtObb], sizeof (BPDT_PAYLOAD_DATA)); + } else { + // + // Check for Payload overlap with non-MMIO SPI region. + // + InternalCheckAndJoinUpperSpi (BpSel, &BpdtPayloadList[BpdtObb]); + + // + // look for and handle the Split OBB case. + // + if (ObbEntryPtr != NULL && (ObbEntryPtr->Flags & SPLIT_ENTRY_FLAGS)) { + + DEBUG ((EFI_D_INFO, "Attempting to resolve Split Obb..\n")); + InternalLocateSplitObb (AdditionalObbBp, &AdditionalObbInfo); + + // + // Since each Obb part could be on either BP, then we also need to check + // it for overlap into the non-MMIO accessible SPI region. + // + InternalCheckAndJoinUpperSpi (AdditionalObbBp, &AdditionalObbInfo); + + if (ObbEntryPtr->Flags & SPLIT_ENTRY_PART_1) { + ObbInfoPart1Ptr = &BpdtPayloadList[BpdtObb]; + ObbInfoPart2Ptr = &AdditionalObbInfo; + } else { + ObbInfoPart1Ptr = &AdditionalObbInfo; + ObbInfoPart2Ptr = &BpdtPayloadList[BpdtObb]; + } + + // + // Combine the final parts together and set the output address to the result. + // + CombinedObb = AllocatePages (EFI_SIZE_TO_PAGES ((UINTN)ObbInfoPart1Ptr->Size + ObbInfoPart2Ptr->Size)); + CopyMem (CombinedObb, ObbInfoPart1Ptr->DataPtr, ObbInfoPart1Ptr->Size); + CopyMem ((VOID *)((UINTN)CombinedObb+ObbInfoPart1Ptr->Size), ObbInfoPart2Ptr->DataPtr, ObbInfoPart2Ptr->Size); + BpdtPayloadList[BpdtObb].DataPtr = CombinedObb; + BpdtPayloadList[BpdtObb].Size = ObbInfoPart1Ptr->Size + ObbInfoPart2Ptr->Size; + } + } + + // + // Build HOB for BPDT data. + // Need to use a HOB (or local PPI) instead of a module global since the library will be called in PreMem, + // and PreMem globals will not work if executing in place (ie SPI boot with no code in CAR). + // + GuidHobPtr = BuildGuidDataHob ( + &gEfiBpdtLibBp2DataGuid, + BpdtPayloadList, + BPDT_HOB_DATA_SIZE + ); + *BpdtPayloadPtr = &BpdtPayloadList[EntryType]; + FreePool(BpdtPayloadList); + + if (GuidHobPtr == NULL || *BpdtPayloadPtr == NULL) { + DEBUG ((EFI_D_ERROR, "Unable to create BPDT HOB.\n")); + return EFI_NOT_FOUND; + } + return EFI_SUCCESS; +} + + +/** + Search the Subpartition pointed to by BpdtPayloadPtr for a Directory entry with "EntryName", + then fill in the Struct pointed to by SubPartPayloadPtr with the Addres and Size of the Dir Entry. + Note: "payload" is the Data pointed to by the Directory Entry with the given name. + + @param[in] BpdtPayloadPtr Location and Size of the Payload to search + @param[in] EntryName the String name of the Directory entry to look for + @param[out] SubPartPayloadPtr Address of the Struct to put the results into (MUST be allocated by caller) + + @retval EFI_SUCCESS The operation completed successfully. +**/ +EFI_STATUS +EFIAPI +GetSubPartitionPayloadData ( + IN BPDT_PAYLOAD_DATA *BpdtPayloadPtr, + IN CONST CHAR8 *EntryName, + OUT BPDT_PAYLOAD_DATA *SubPartPayloadPtr + ) +{ + UINT8 Index; + CHAR8 SearchName[DIR_ENTRY_NAME_SIZE]; + UINTN SearchNameSize; + SUBPART_DIR_HEADER *DirectoryHeader; + SUBPART_DIR_ENTRY *DirectoryEntry; + + // + // Pad Name with zeros (per spec) to ensure correct match. + // + ZeroMem(SearchName, DIR_ENTRY_NAME_SIZE); + + // + // Safely handle the input string to ensure we dont copy garbage when EntryName is less than 12. + // + SearchNameSize = AsciiStrnLenS (EntryName, DIR_ENTRY_NAME_SIZE); + CopyMem(SearchName, EntryName, SearchNameSize); + + DEBUG ((EFI_D_INFO, "Looking for Directory Entry with Name: %a\n", SearchName)); + DirectoryHeader = (SUBPART_DIR_HEADER *)BpdtPayloadPtr->DataPtr; + DirectoryEntry = (SUBPART_DIR_ENTRY *)((UINTN)DirectoryHeader + sizeof(SUBPART_DIR_HEADER)); + + if (DirectoryHeader->HeaderMarker != DIR_HDR_SIGNATURE) { + DEBUG ((EFI_D_ERROR, "Directory Header is invalid - return Not_Found.\n")); + return EFI_NOT_FOUND; + } + + for (Index = 0; Index < DirectoryHeader->NumOfEntries; Index++, DirectoryEntry++) { + DEBUG ((EFI_D_INFO, " %a Entry is located at %x\n", + DirectoryEntry->EntryName, (UINTN)DirectoryHeader+DirectoryEntry->EntryOffset)); + if (!CompareMem(DirectoryEntry->EntryName, SearchName, DIR_ENTRY_NAME_SIZE)) { + SubPartPayloadPtr->DataPtr = (VOID *)((UINTN)DirectoryHeader + DirectoryEntry->EntryOffset); + SubPartPayloadPtr->Size = DirectoryEntry->EntrySize; + return EFI_SUCCESS; + } + } + + DEBUG ((EFI_D_WARN, "Entry not found.\n")); + return EFI_NOT_FOUND; +} + + +/** + This is a wrapper function for using VerifyHashBpm that will only + check the hash for the payloads used during normal boot. + + If you want to check other payloads (ie during recovery flow), then the + calling code should locate and pass in the payload directly to VerifyHashBpm(). + + @param[in] PayloadSel Which hash in BPM to compare with. + + @retval EFI_SUCCESS The function completed successfully and the Hash matched. + @retval other An error occured when locating and computing the Hash. +**/ +EFI_STATUS +EFIAPI +LocateAndVerifyHashBpm ( + IN BPM_HASH_SELECT PayloadSel + ) +{ + + UINT8 Index; + EFI_STATUS Status; + VOID *HashDataPtr; + UINT32 HashDataSize; + UINT32 FvTailAddr; + BPM_DATA_FILE *BpmPtr; + BPDT_PAYLOAD_DATA *IbbPayloadPtr; + BPDT_PAYLOAD_DATA *ObbPayloadPtr; + BPDT_PAYLOAD_DATA SubPartPayload; + SUBPART_DIR_HEADER *DirectoryHeader; + SUBPART_DIR_ENTRY *DirectoryEntry; + + DEBUG ((EFI_D_INFO, "Searching IBB for BPM..\n" )); + GetBpdtPayloadData (BootPart1, BpdtIbb, &IbbPayloadPtr); + if (IbbPayloadPtr == NULL) { + return EFI_NOT_FOUND; + } + Status = GetSubPartitionPayloadData (IbbPayloadPtr, "BPM.met", &SubPartPayload); + if (EFI_ERROR (Status)) { + return Status; + } + BpmPtr = (BPM_DATA_FILE *)SubPartPayload.DataPtr; + + switch (PayloadSel) { + case HashIbbl: + Status = GetSubPartitionPayloadData (IbbPayloadPtr, "IBBL", &SubPartPayload); + HashDataPtr = SubPartPayload.DataPtr; + HashDataSize = SubPartPayload.Size; + break; + + case HashIbbm: + Status = GetSubPartitionPayloadData (IbbPayloadPtr, "IBB", &SubPartPayload); + HashDataPtr = SubPartPayload.DataPtr; + HashDataSize = SubPartPayload.Size; + break; + + case HashObb: + Status = GetBpdtPayloadData (BootPart2, BpdtObb, &ObbPayloadPtr); + if (ObbPayloadPtr == NULL) { + return EFI_NOT_FOUND; + } + DirectoryHeader = (SUBPART_DIR_HEADER *)ObbPayloadPtr->DataPtr; + DirectoryEntry = (SUBPART_DIR_ENTRY *)((UINTN)DirectoryHeader + sizeof(SUBPART_DIR_HEADER)); + HashDataPtr = (VOID *)((UINTN)DirectoryHeader + DirectoryEntry->EntryOffset); + + for (Index = 1; Index < DirectoryHeader->NumOfEntries; Index++) { + DirectoryEntry++; + } + + FvTailAddr = (UINT32)DirectoryHeader + DirectoryEntry->EntryOffset + DirectoryEntry->EntrySize; + HashDataSize = FvTailAddr - (UINT32)HashDataPtr; + break; + + default: + + return EFI_INVALID_PARAMETER; + break; + } + if (EFI_ERROR (Status)) { + return Status; + } + + Status = VerifyHashBpm(PayloadSel, BpmPtr, HashDataPtr, HashDataSize); + return Status; +} + + + +/** + Computes the Hash of a given data block and compares to the one in the Boot Policy Metadata. + + @param[in] PayloadSel Which hash in BPM to compare with. + @param[in] PayloadPtr Pointer to the begining of the data to be hashed. + @param[in] DataSize Size of the data to be hashed. + @param[in] BpmPtr Pointer to the BPM structure. + + @retval EFI_SUCCESS If the function completed successfully. + @retval EFI_ABORTED If the attempt to compute the hash fails. + @retval EFI_INVALID_PARAMETER If the hash type or size in BPM is unsupported. + @retval EFI_SECURITY_VIOLATION If the has does not match the one in BPM. +**/ +EFI_STATUS +EFIAPI +VerifyHashBpm ( + IN BPM_HASH_SELECT PayloadSel, + IN BPM_DATA_FILE *BpmPtr, + IN VOID *PayloadPtr, + IN UINT32 DataSize + ) +{ + UINT8 Index; + UINT32 BpmHashAlgo; + UINT32 BpmHashSize; + UINT8 *BpmHash; + UINTN ContextSize; + VOID *HashContext; + BOOLEAN HashResult; + UINT8 Digest[SHA256_DIGEST_SIZE]; + + + ZeroMem (Digest, SHA256_DIGEST_SIZE); + ContextSize = Sha256GetContextSize (); + HashContext = AllocatePool (ContextSize); + + DEBUG((EFI_D_INFO, "VerifyFV: PayloadPtr = %x, DataSize = %x\n", PayloadPtr, DataSize)); + + switch (PayloadSel) { + case HashIbbl: + BpmHashAlgo = BpmPtr->IbblHashAlgo; + BpmHashSize = BpmPtr->IbblHashSize; + BpmHash = BpmPtr->IbblHash; + break; + + case HashIbbm: + BpmHashAlgo = BpmPtr->IbbmHashAlgo; + BpmHashSize = BpmPtr->IbbmHashSize; + BpmHash = BpmPtr->IbbmHash; + break; + + case HashObb: + default: + BpmHashAlgo = BpmPtr->ObbHashAlgo; + BpmHashSize = BpmPtr->ObbHashSize; + BpmHash = BpmPtr->ObbHash; + break; + } + + if (BpmHashAlgo != 2 || BpmHashSize != SHA256_DIGEST_SIZE) { + DEBUG((EFI_D_ERROR, "Hash length NOT correct for SHA256.\n")); + return EFI_INVALID_PARAMETER; + } + + // + //If hash entry in BPM is empty (all 0s), skip the OBB verification. + // + if (!CompareMem(BpmHash, Digest, SHA256_DIGEST_SIZE)) { + return EFI_SUCCESS; + } + + DEBUG((EFI_D_INFO, "BPM Info:\n" )); + DEBUG((EFI_D_INFO, " Payload = %d, HashAlgo = %d, HashSize = %d\n", PayloadSel, BpmHashAlgo, BpmHashSize)); + DEBUG((EFI_D_INFO, " Hash =")); + for (Index = 0; Index < BpmHashSize; Index++) { + DEBUG((EFI_D_INFO, " %02x", BpmHash[Index])); + } + DEBUG((EFI_D_INFO, "\n")); + + HashResult = Sha256Init (HashContext); + if (!HashResult) { + DEBUG((EFI_D_ERROR, "Sha256Init Failed!\n")); + return EFI_ABORTED; + } + HashResult = Sha256Update (HashContext, PayloadPtr, DataSize); + if (!HashResult) { + DEBUG((EFI_D_ERROR, "Sha256Update Failed!\n")); + return EFI_ABORTED; + } + HashResult = Sha256Final (HashContext, Digest); + if (!HashResult) { + DEBUG((EFI_D_ERROR, "Sha256Final Failed!\n")); + return EFI_ABORTED; + } + + DEBUG((EFI_D_INFO, " Hash =")); + for (Index = 0; Index < BpmHashSize; Index ++) { + DEBUG((EFI_D_INFO, " %02x", Digest[Index])); + } + DEBUG((EFI_D_INFO, "\n")); + + if (CompareMem(BpmHash, Digest, BpmHashSize)) { + DEBUG((EFI_D_ERROR, "\nHash Mis-Match. Return Security Violation\n")); + return EFI_SECURITY_VIOLATION; + } + + return EFI_SUCCESS; +} diff --git a/Silicon/BroxtonSoC/BroxtonSiPkg/Library/BpdtLib/BpdtLib.inf b/Silicon/BroxtonSoC/BroxtonSiPkg/Library/BpdtLib/BpdtLib.inf new file mode 100644 index 0000000000..c07ecb05c1 --- /dev/null +++ b/Silicon/BroxtonSoC/BroxtonSiPkg/Library/BpdtLib/BpdtLib.inf @@ -0,0 +1,49 @@ +## @file +# Boot Partition Descriptor Table library. +# +# Copyright (c) 2016 - 2017, Intel Corporation. All rights reserved.
+# +# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php. +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = BpdtLib + FILE_GUID = ec12d034-0ab8-4e9c-87f9-01a2d284ea6c + MODULE_TYPE = PEIM + VERSION_STRING = 1.0 + LIBRARY_CLASS = BpdtLib + +[Sources] + BpdtLib.c + +[Packages] + MdePkg/MdePkg.dec + CryptoPkg/CryptoPkg.dec + BroxtonSiPkg/BroxtonSiPkg.dec + +[LibraryClasses] + BaseLib + DebugLib + HobLib + BaseMemoryLib + MemoryAllocationLib + PeiServicesTablePointerLib + ScPlatformLib + PeiSpiInitLib + BaseCryptLib + +[Guids] + gEfiBpdtLibBp1DataGuid + gEfiBpdtLibBp2DataGuid + +[Ppis] + gScSpiPpiGuid + gEfiPeiVirtualBlockIoPpiGuid -- cgit v1.2.3