514 lines
16 KiB
C
514 lines
16 KiB
C
/** @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
|
|
INTEL CONFIDENTIAL
|
|
Copyright 2014 - 2018 Intel Corporation.
|
|
|
|
The source code contained or described herein and all documents related to the
|
|
source code ("Material") are owned by Intel Corporation or its suppliers or
|
|
licensors. Title to the Material remains with Intel Corporation or its suppliers
|
|
and licensors. The Material may contain trade secrets and proprietary and
|
|
confidential information of Intel Corporation and its suppliers and licensors,
|
|
and is protected by worldwide copyright and trade secret laws and treaty
|
|
provisions. No part of the Material may be used, copied, reproduced, modified,
|
|
published, uploaded, posted, transmitted, distributed, or disclosed in any way
|
|
without Intel's prior express written permission.
|
|
|
|
No license under any patent, copyright, trade secret or other intellectual
|
|
property right is granted to or conferred upon you by disclosure or delivery
|
|
of the Materials, either expressly, by implication, inducement, estoppel or
|
|
otherwise. Any license under such intellectual property rights must be
|
|
express and approved by Intel in writing.
|
|
|
|
Unless otherwise agreed by Intel in writing, you may not remove or alter
|
|
this notice or any other notice embedded in Materials by Intel or
|
|
Intel's suppliers or licensors in any way.
|
|
|
|
This file contains an 'Intel Peripheral Driver' and is uniquely identified as
|
|
"Intel Reference Module" and is licensed for Intel CPUs and chipsets under
|
|
the terms of your license agreement with Intel or your vendor. This file may
|
|
be modified by the user, subject to additional terms of the license agreement.
|
|
|
|
@par Specification
|
|
**/
|
|
#include <Uefi.h>
|
|
#include <PiPei.h>
|
|
#include <Ppi/BlockIo.h>
|
|
#include <Library/BaseLib.h>
|
|
#include <Library/DebugLib.h>
|
|
#include <Library/HobLib.h>
|
|
#include <Library/BaseMemoryLib.h>
|
|
#include <Library/MemoryAllocationLib.h>
|
|
#include <Library/PeiServicesTablePointerLib.h>
|
|
#include <Library/PeiServicesLib.h>
|
|
#include <Library/BpdtLib.h>
|
|
|
|
#define UFS_BOOT_LUN 1
|
|
|
|
/**
|
|
Print FV name based on GUID
|
|
**/
|
|
VOID
|
|
PrintFvName (
|
|
EFI_GUID *FvName
|
|
)
|
|
{
|
|
if (CompareGuid (FvName, &gEfiAuthenticatedVariableGuid)) {
|
|
DEBUG ((DEBUG_INFO, "gEfiAuthenticatedVariableGuid\n"));
|
|
} else if (CompareGuid (FvName, &gFspMemoryFvGuid)) {
|
|
DEBUG ((DEBUG_INFO, "gFspMemoryFvGuid\n"));
|
|
} else if (CompareGuid (FvName, &gFspTempRamFvGuid)) {
|
|
DEBUG ((DEBUG_INFO, "gFspTempRamFvGuid\n"));
|
|
} else if (CompareGuid (FvName, &gFspSiliconFvGuid)) {
|
|
DEBUG ((DEBUG_INFO, "gFspSiliconFvGuid\n"));
|
|
} else if (CompareGuid (FvName, &gFspSiliconRebasedFvGuid)) {
|
|
DEBUG ((DEBUG_INFO, "gFspSiliconRebasedFvGuid\n"));
|
|
} else if (CompareGuid (FvName, &gFvRecoveryFvGuid)) {
|
|
DEBUG ((DEBUG_INFO, "gFvRecoveryFvGuid\n"));
|
|
} else if (CompareGuid (FvName, &gFvRecovery2FvGuid)) {
|
|
DEBUG ((DEBUG_INFO, "gFvRecovery2FvGuid\n"));
|
|
} else if (CompareGuid (FvName, &gFvRecovery3FvGuid)) {
|
|
DEBUG ((DEBUG_INFO, "gFvRecovery3FvGuid\n"));
|
|
} else if (CompareGuid (FvName, &gFvRecovery3CompactFvGuid)) {
|
|
DEBUG ((DEBUG_INFO, "gFvRecovery3CompactFvGuid\n"));
|
|
} else if (CompareGuid (FvName, &gFwBinariesFvGuid)) {
|
|
DEBUG ((DEBUG_INFO, "gFwBinariesFvGuid\n"));
|
|
} else if (CompareGuid (FvName, &gFvMainFvGuid)) {
|
|
DEBUG ((DEBUG_INFO, "gFvMainFvGuid\n"));
|
|
} else if (CompareGuid (FvName, &gFvMainCompactFvGuid)) {
|
|
DEBUG ((DEBUG_INFO, "gFvMainCompactFvGuid\n"));
|
|
} else if (CompareGuid (FvName, &gFvMain2FvGuid)) {
|
|
DEBUG ((DEBUG_INFO, "gFvMain2FvGuid\n"));
|
|
} else if (CompareGuid (FvName, &gFvMain2CompactFvGuid)) {
|
|
DEBUG ((DEBUG_INFO, "gFvMain2CompactFvGuid\n"));
|
|
} else if (CompareGuid (FvName, &gMicrocodeFvGuid)) {
|
|
DEBUG ((DEBUG_INFO, "gMicrocodeFvGuid\n"));
|
|
} else if (CompareGuid (FvName, &gFvTestMenuFvGuid)) {
|
|
DEBUG ((DEBUG_INFO, "gFvTestMenuFvGuid\n"));
|
|
}
|
|
}
|
|
|
|
/**
|
|
Copy Length bytes from Source to Destination using SSE4.
|
|
|
|
@param[out] Dst The target of the copy request.
|
|
@param[in] Src The place to copy from.
|
|
@param[in] SizeInBytes The number of bytes to copy.
|
|
**/
|
|
VOID
|
|
CopyMemSse4 (
|
|
OUT VOID *Dst,
|
|
IN VOID *Src,
|
|
IN UINTN SizeInBytes
|
|
)
|
|
{
|
|
#if defined __GNUC__ // GCC compiler
|
|
__asm__ __volatile__ (
|
|
// Initialize pointers to start of the USWC memory
|
|
"\n\t mov %1, %%esi" //mov esi, Src
|
|
"\n\t mov %1, %%edx" //mov edx, Src
|
|
|
|
// Initialize pointer to end of the USWC memory
|
|
"\n\t add %2, %%edx" //add edx, SizeInBytes
|
|
|
|
// Initialize pointer to start of the cacheable WB buffer
|
|
"\n\t mov %0, %%edi" //mov edi, Dst
|
|
|
|
// save xmm0 ~ xmm3 to stack
|
|
"\n\t sub $0x40, %%esp"
|
|
"\n\t movdqu %%xmm0, 0x00(%%esp)"
|
|
"\n\t movdqu %%xmm1, 0x10(%%esp)"
|
|
"\n\t movdqu %%xmm2, 0x20(%%esp)"
|
|
"\n\t movdqu %%xmm3, 0x30(%%esp)"
|
|
|
|
// Start of Bulk Load loop
|
|
"\n\t inner_start:"
|
|
// Load data from USWC Memory using Streaming Load
|
|
"\n\t MOVNTDQA 0x00(%%esi), %%xmm0"
|
|
"\n\t MOVNTDQA 0x10(%%esi), %%xmm1"
|
|
"\n\t MOVNTDQA 0x20(%%esi), %%xmm2"
|
|
"\n\t MOVNTDQA 0x30(%%esi), %%xmm3"
|
|
|
|
// Copy data to buffer
|
|
"\n\t MOVDQA %%xmm0, 0x00(%%edi)"
|
|
"\n\t MOVDQA %%xmm1, 0x10(%%edi)"
|
|
"\n\t MOVDQA %%xmm2, 0x20(%%edi)"
|
|
"\n\t MOVDQA %%xmm3, 0x30(%%edi)"
|
|
|
|
// Increment pointers by cache line size and test for end of loop
|
|
"\n\t add $0x40, %%esi"
|
|
"\n\t add $0x40, %%edi"
|
|
"\n\t cmp %%edx, %%esi"
|
|
"\n\t jne inner_start"
|
|
|
|
// restore xmm0 ~ xmm3
|
|
"\n\t mfence"
|
|
"\n\t movdqu 0x00(%%esp), %%xmm0"
|
|
"\n\t movdqu 0x10(%%esp), %%xmm1"
|
|
"\n\t movdqu 0x20(%%esp), %%xmm2"
|
|
"\n\t movdqu 0x30(%%esp), %%xmm3"
|
|
"\n\t add $0x40, %%esp" // stack cleanup
|
|
::"a"(Dst),"b"(Src),"c"(SizeInBytes)
|
|
:"%esi", "%edi", "%edx"
|
|
);
|
|
#else //MSFT compiler
|
|
_asm {
|
|
// Initialize pointers to start of the USWC memory
|
|
mov esi, Src
|
|
mov edx, Src
|
|
|
|
// Initialize pointer to end of the USWC memory
|
|
add edx, SizeInBytes
|
|
|
|
// Initialize pointer to start of the cacheable WB buffer
|
|
mov edi, Dst
|
|
|
|
// save xmm0 ~ xmm3 to stack
|
|
sub esp, 040h
|
|
movdqu [esp], xmm0
|
|
movdqu [esp + 16], xmm1
|
|
movdqu [esp + 32], xmm2
|
|
movdqu [esp + 48], xmm3
|
|
|
|
// Start of Bulk Load loop
|
|
inner_start:
|
|
// Load data from USWC Memory using Streaming Load
|
|
MOVNTDQA xmm0, xmmword ptr [esi]
|
|
MOVNTDQA xmm1, xmmword ptr [esi + 16]
|
|
MOVNTDQA xmm2, xmmword ptr [esi + 32]
|
|
MOVNTDQA xmm3, xmmword ptr [esi + 48]
|
|
|
|
// Copy data to buffer
|
|
MOVDQA xmmword ptr [edi], xmm0
|
|
MOVDQA xmmword ptr [edi + 16], xmm1
|
|
MOVDQA xmmword ptr [edi + 32], xmm2
|
|
MOVDQA xmmword ptr [edi + 48], xmm3
|
|
|
|
// Increment pointers by cache line size and test for end of loop
|
|
add esi, 040h
|
|
add edi, 040h
|
|
cmp esi, edx
|
|
jne inner_start
|
|
|
|
// restore xmm0 ~ xmm3
|
|
mfence
|
|
movdqu xmm0, [esp]
|
|
movdqu xmm1, [esp + 16]
|
|
movdqu xmm2, [esp + 32]
|
|
movdqu xmm3, [esp + 48]
|
|
add esp, 040h // stack cleanup
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
Read data from UFS into Memory.
|
|
The caller is responsible for calling FreePages on DataBuffer if necessary.
|
|
|
|
@param DataBuffer A pointer to the buffer address
|
|
@param Offset Offset of the data to read (4k aligned)
|
|
@param DataSize Size of the data to read
|
|
|
|
@retval EFI_SUCCESS The operation completed successfully
|
|
@retval EFI_INVALID_PARAMETER DataBuffer was NULL
|
|
@retval EFI_NOT_FOUND Problems reading from UFS
|
|
@retval EFI_OUT_OF_RESOURCES Couldn't allocate the buffer
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
ReadFromUfs (
|
|
IN OUT VOID **DataBuffer,
|
|
IN UINT32 Offset,
|
|
IN UINTN DataSize
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_PEI_SERVICES **PeiServices;
|
|
EFI_PEI_RECOVERY_BLOCK_IO_PPI *BlockIoPpi;
|
|
EFI_PEI_BLOCK_IO_MEDIA Media;
|
|
EFI_PEI_LBA StartBlock;
|
|
UINTN UfsBufferSize;
|
|
VOID *UfsBuffer;
|
|
|
|
if (DataBuffer == NULL) {
|
|
DEBUG ((DEBUG_ERROR, "DataBuffer pointer is NULL!\n"));
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
if (Offset % SIZE_4KB) {
|
|
DEBUG ((DEBUG_ERROR, "Offset is not 4k aligned!\n"));
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
*DataBuffer = NULL;
|
|
if (DataSize == 0) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
PeiServices = (EFI_PEI_SERVICES **) GetPeiServicesTablePointer();
|
|
|
|
//Find UFS Block IO PPI
|
|
Status = PeiServicesLocatePpi (
|
|
&gEfiPeiVirtualBlockIoPpiGuid,
|
|
0,
|
|
NULL,
|
|
(VOID **) &BlockIoPpi
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "Virtual Block IO PPI not found!\n"));
|
|
ASSERT_EFI_ERROR (Status);
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
// Check if boot media is present
|
|
ZeroMem (&Media, sizeof(Media));
|
|
Status = BlockIoPpi->GetBlockDeviceMediaInfo (
|
|
PeiServices,
|
|
BlockIoPpi,
|
|
UFS_BOOT_LUN,
|
|
&Media
|
|
);
|
|
if (EFI_ERROR (Status) || !Media.MediaPresent) {
|
|
DEBUG ((DEBUG_ERROR, "Fail to get MediaInfo or boot media not present\n"));
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
//Execute the read operation
|
|
StartBlock = (EFI_PEI_LBA) EFI_SIZE_TO_PAGES (Offset);
|
|
UfsBufferSize = EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (DataSize));
|
|
UfsBuffer = AllocatePages (EFI_SIZE_TO_PAGES (DataSize));
|
|
if (UfsBuffer == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
Status = BlockIoPpi->ReadBlocks (
|
|
PeiServices,
|
|
BlockIoPpi,
|
|
UFS_BOOT_LUN,
|
|
StartBlock,
|
|
UfsBufferSize,
|
|
UfsBuffer
|
|
);
|
|
|
|
*DataBuffer = UfsBuffer;
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Provides a pointer to the CSE Layout Pointers structure, which is stored in a HOB.
|
|
If the HOB does not exist, the HOB is created by reading from UFS.
|
|
|
|
@param[out] CseLayoutPointers Pointer to the CSE Layout Pointers HOB data.
|
|
|
|
@retval EFI_SUCCESS The CSE Layout Pointers structure was retrieved.
|
|
@retval EFI_OUT_OF_RESOURCES Could not create a HOB to store the data.
|
|
@retval Others An error occurred reading from UFS.
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
GetCseLayoutPointers (
|
|
OUT CSE_LAYOUT_POINTERS **CseLayoutPointers
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
VOID *UfsBuffer;
|
|
EFI_HOB_GUID_TYPE *GuidHobPtr;
|
|
|
|
//Default to NULL to allow failure-checking from caller
|
|
*CseLayoutPointers = NULL;
|
|
Status = EFI_SUCCESS;
|
|
|
|
//Check if data has already been retrieved
|
|
GuidHobPtr = GetFirstGuidHob (&gIfwiCseLayoutPointersHobGuid);
|
|
|
|
if (GuidHobPtr != NULL) {
|
|
//Retrieve a pointer to the data
|
|
*CseLayoutPointers = (CSE_LAYOUT_POINTERS *) GET_GUID_HOB_DATA (GuidHobPtr);
|
|
} else {
|
|
//Get the data from UFS
|
|
Status = ReadFromUfs (&UfsBuffer, 0, sizeof (CSE_LAYOUT_POINTERS));
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
//Store the data in a HOB
|
|
*CseLayoutPointers = (CSE_LAYOUT_POINTERS *) BuildGuidDataHob (
|
|
&gIfwiCseLayoutPointersHobGuid,
|
|
UfsBuffer,
|
|
sizeof (CSE_LAYOUT_POINTERS)
|
|
);
|
|
//Cleanup
|
|
FreePages(UfsBuffer, EFI_SIZE_TO_PAGES (sizeof (CSE_LAYOUT_POINTERS)));
|
|
if (*CseLayoutPointers == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Provides a pointer to the BIOS region of the IFWI (BPDT 4).
|
|
The BIOS region is stored in memory.
|
|
The pointer is stored in a HOB.
|
|
If the HOB does not exist, the HOB is created by reading from UFS.
|
|
|
|
@param[out] BiosRegion Pointer to the BIOS region stored in memory.
|
|
|
|
@retval EFI_SUCCESS The BIOS region was retrieved.
|
|
@retval EFI_OUT_OF_RESOURCES Could not create a HOB to store the pointer.
|
|
@retval Others An error occurred retrieving CSE Layout pointers,
|
|
or reading from UFS.
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
GetBiosRegion (
|
|
IN OUT VOID **BiosRegion
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
VOID *GuidHobPtr;
|
|
CSE_LAYOUT_POINTERS *CseLayoutPointers;
|
|
|
|
if (BiosRegion == NULL) {
|
|
DEBUG ((DEBUG_ERROR, "BiosRegion pointer is NULL!\n"));
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
//Default to NULL to allow failure-checking from caller
|
|
*BiosRegion = NULL;
|
|
Status = EFI_SUCCESS;
|
|
|
|
//Check if data has already been retrieved
|
|
GuidHobPtr = GetFirstGuidHob (&gIfwiBiosRegionPointerHobGuid);
|
|
|
|
if (GuidHobPtr != NULL) {
|
|
//Retrieve a pointer to the data
|
|
*BiosRegion = *((VOID **) GET_GUID_HOB_DATA (GuidHobPtr));
|
|
} else {
|
|
//Retrieve CSE Layout Pointers to find BIOS region location
|
|
Status = GetCseLayoutPointers(&CseLayoutPointers);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
//Read the BIOS region from UFS
|
|
DEBUG ((DEBUG_INFO, "Lbp4Offset = 0x%08X\n", CseLayoutPointers->Lbp4Offset));
|
|
DEBUG ((DEBUG_INFO, "Lbp4Size = 0x%08X\n", CseLayoutPointers->Lbp4Size));
|
|
Status = ReadFromUfs (
|
|
BiosRegion,
|
|
CseLayoutPointers->Lbp4Offset,
|
|
CseLayoutPointers->Lbp4Size
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//Store the pointer in a HOB
|
|
GuidHobPtr = BuildGuidDataHob (
|
|
&gIfwiBiosRegionPointerHobGuid,
|
|
BiosRegion,
|
|
sizeof (VOID *)
|
|
);
|
|
if (GuidHobPtr == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
if (*BiosRegion != *((VOID **) GuidHobPtr)) {
|
|
DEBUG ((DEBUG_ERROR, "BIOS REGION POINTER MISMATCH!\n"));
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Returns an Entry of a specific Type from the provided BPDT.
|
|
|
|
@param[in] Type Entry type to return.
|
|
@param[in] Bpdt Pointer to a BPDT.
|
|
@param[out] BpdtEntry Pointer to a BPDT Entry.
|
|
|
|
@retval EFI_SUCCESS The BPDT Entry was found.
|
|
@retval EFI_NOT_FOUND The BPDT Entry was not found.
|
|
@retval Others An error occurred.
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
GetEntryFromBpdt (
|
|
IN BPDT_ENTRY_TYPE Type,
|
|
IN BPDT_HEADER *Bpdt,
|
|
OUT BPDT_ENTRY **Entry
|
|
)
|
|
{
|
|
UINTN Index;
|
|
BPDT_ENTRY *EntryList;
|
|
|
|
if (Type >= BpdtMaxType) {
|
|
DEBUG((DEBUG_ERROR, "BPDT Entry Type %d is greater than max type %d!\n", Type, BpdtMaxType-1));
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if ((Bpdt == NULL) || (Entry == NULL)) {
|
|
DEBUG((DEBUG_ERROR, "Parameter is NULL!\n"));
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
//Default to NULL to allow failure-checking from caller
|
|
*Entry = NULL;
|
|
|
|
if (Bpdt->Signature != BPDT_SIGN_GREEN) {
|
|
DEBUG ((DEBUG_INFO, "Signature of BPDT Header is invalid - stop parsing\n"));
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
//Walk BPDT Entry List
|
|
EntryList = (BPDT_ENTRY *) (Bpdt + 1);
|
|
for (Index = 0; Index < Bpdt->DescriptorCount; Index++) {
|
|
*Entry = &EntryList[Index];
|
|
if ((BPDT_ENTRY_TYPE) (*Entry)->Type == Type) {
|
|
DEBUG ((DEBUG_INFO, "Entry Type %d Offset = 0x%08X\n", Type, (*Entry)->SubPartitionOffset));
|
|
DEBUG ((DEBUG_INFO, "Entry Type %d Size = 0x%08X\n", Type, (*Entry)->SubPartitionSize));
|
|
return EFI_SUCCESS;
|
|
}
|
|
}
|
|
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
/**
|
|
Populates a SUB_PARTITION_DATA struct from the provided BPDT & EntryType.
|
|
|
|
@param[in] Type Entry type to return.
|
|
@param[in] Bpdt Pointer to a BPDT.
|
|
@param[in, out] PayloadData Pointer to SUB_PARTITION_DATA struct.
|
|
|
|
@retval EFI_SUCCESS The BPDT Entry was found.
|
|
@retval EFI_NOT_FOUND The BPDT Entry was not found.
|
|
@retval Others An error occurred.
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
GetSubPartitionData (
|
|
IN BPDT_ENTRY_TYPE Type,
|
|
IN BPDT_HEADER *Bpdt,
|
|
IN OUT SUB_PARTITION_DATA *PayloadData
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
BPDT_ENTRY *BpdtEntry;
|
|
|
|
if ((Bpdt == NULL) || (PayloadData == NULL)) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
Status = GetEntryFromBpdt (Type, Bpdt, &BpdtEntry);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
PayloadData->Address = (VOID *) ((UINT32) Bpdt + BpdtEntry->SubPartitionOffset);
|
|
PayloadData->Size = BpdtEntry->SubPartitionSize;
|
|
|
|
return Status;
|
|
}
|
|
|