mirror of https://github.com/acidanthera/audk.git
1320 lines
45 KiB
C
1320 lines
45 KiB
C
|
/** @file
|
||
|
Capsule update PEIM for UEFI2.0
|
||
|
|
||
|
Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.<BR>
|
||
|
|
||
|
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 "Capsule.h"
|
||
|
|
||
|
EFI_PHYSICAL_ADDRESS *mBufferAddress;
|
||
|
|
||
|
/**
|
||
|
Check every capsule header.
|
||
|
|
||
|
@param CapsuleHeader The pointer to EFI_CAPSULE_HEADER
|
||
|
|
||
|
@retval FALSE Capsule is OK
|
||
|
@retval TRUE Capsule is corrupted
|
||
|
|
||
|
**/
|
||
|
BOOLEAN
|
||
|
IsCapsuleCorrupted (
|
||
|
IN EFI_CAPSULE_HEADER *CapsuleHeader
|
||
|
)
|
||
|
{
|
||
|
//
|
||
|
//A capsule to be updated across a system reset should contain CAPSULE_FLAGS_PERSIST_ACROSS_RESET.
|
||
|
//
|
||
|
if ((CapsuleHeader->Flags & CAPSULE_FLAGS_PERSIST_ACROSS_RESET) == 0) {
|
||
|
return TRUE;
|
||
|
}
|
||
|
//
|
||
|
//Make sure the flags combination is supported by the platform.
|
||
|
//
|
||
|
if ((CapsuleHeader->Flags & (CAPSULE_FLAGS_PERSIST_ACROSS_RESET | CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE)) == CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE) {
|
||
|
return TRUE;
|
||
|
}
|
||
|
if ((CapsuleHeader->Flags & (CAPSULE_FLAGS_PERSIST_ACROSS_RESET | CAPSULE_FLAGS_INITIATE_RESET)) == CAPSULE_FLAGS_INITIATE_RESET) {
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Check the integrity of the capsule descriptors.
|
||
|
|
||
|
@param BlockList Pointer to the capsule descriptors
|
||
|
|
||
|
@retval NULL BlockList is not valid.
|
||
|
@retval LastBlockDesc Last one Block in BlockList
|
||
|
|
||
|
**/
|
||
|
EFI_CAPSULE_BLOCK_DESCRIPTOR *
|
||
|
ValidateCapsuleIntegrity (
|
||
|
IN EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockList
|
||
|
)
|
||
|
{
|
||
|
EFI_CAPSULE_HEADER *CapsuleHeader;
|
||
|
UINT64 CapsuleSize;
|
||
|
UINT32 CapsuleCount;
|
||
|
EFI_CAPSULE_BLOCK_DESCRIPTOR *Ptr;
|
||
|
|
||
|
//
|
||
|
// Go through the list to look for inconsistencies. Check for:
|
||
|
// * misaligned block descriptors.
|
||
|
// * The first capsule header guid
|
||
|
// * The first capsule header flag
|
||
|
// * Data + Length < Data (wrap)
|
||
|
CapsuleSize = 0;
|
||
|
CapsuleCount = 0;
|
||
|
Ptr = BlockList;
|
||
|
while ((Ptr->Length != 0) || (Ptr->Union.ContinuationPointer != (EFI_PHYSICAL_ADDRESS) (UINTN) NULL)) {
|
||
|
//
|
||
|
// Make sure the descriptor is aligned at UINT64 in memory
|
||
|
//
|
||
|
if ((UINTN) Ptr & 0x07) {
|
||
|
DEBUG ((EFI_D_ERROR, "BlockList address failed alignment check\n"));
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
if (Ptr->Length == 0) {
|
||
|
//
|
||
|
// Descriptor points to another list of block descriptors somewhere
|
||
|
// else.
|
||
|
//
|
||
|
Ptr = (EFI_CAPSULE_BLOCK_DESCRIPTOR *) (UINTN) Ptr->Union.ContinuationPointer;
|
||
|
} else {
|
||
|
//
|
||
|
//To enhance the reliability of check-up, the first capsule's header is checked here.
|
||
|
//More reliabilities check-up will do later.
|
||
|
//
|
||
|
if (CapsuleSize == 0) {
|
||
|
//
|
||
|
//Move to the first capsule to check its header.
|
||
|
//
|
||
|
CapsuleHeader = (EFI_CAPSULE_HEADER*)((UINTN)Ptr->Union.DataBlock);
|
||
|
if (IsCapsuleCorrupted (CapsuleHeader)) {
|
||
|
return NULL;
|
||
|
}
|
||
|
CapsuleCount ++;
|
||
|
CapsuleSize = CapsuleHeader->CapsuleImageSize;
|
||
|
} else {
|
||
|
if (CapsuleSize >= Ptr->Length) {
|
||
|
CapsuleSize = CapsuleSize - Ptr->Length;
|
||
|
} else {
|
||
|
CapsuleSize = 0;
|
||
|
}
|
||
|
}
|
||
|
//
|
||
|
// Move to next BLOCK descriptor
|
||
|
//
|
||
|
Ptr++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (CapsuleCount == 0) {
|
||
|
//
|
||
|
// No any capsule is found in BlockList.
|
||
|
//
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
return Ptr;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
Checks for the presence of capsule descriptors.
|
||
|
Get capsule descriptors from variable CapsuleUpdateData, CapsuleUpdateData1, CapsuleUpdateData2...
|
||
|
|
||
|
@param BlockList Pointer to the capsule descriptors
|
||
|
|
||
|
@retval EFI_SUCCESS a valid capsule is present
|
||
|
@retval EFI_NOT_FOUND if a valid capsule is not present
|
||
|
**/
|
||
|
EFI_STATUS
|
||
|
GetCapsuleDescriptors (
|
||
|
IN OUT EFI_CAPSULE_BLOCK_DESCRIPTOR **BlockList OPTIONAL
|
||
|
)
|
||
|
{
|
||
|
EFI_STATUS Status;
|
||
|
UINTN Size;
|
||
|
UINTN Index;
|
||
|
UINTN TempIndex;
|
||
|
UINTN ValidIndex;
|
||
|
BOOLEAN Flag;
|
||
|
CHAR16 CapsuleVarName[30];
|
||
|
CHAR16 *TempVarName;
|
||
|
EFI_PHYSICAL_ADDRESS CapsuleDataPtr64;
|
||
|
EFI_CAPSULE_BLOCK_DESCRIPTOR *LastBlock;
|
||
|
EFI_CAPSULE_BLOCK_DESCRIPTOR *TempBlock;
|
||
|
EFI_CAPSULE_BLOCK_DESCRIPTOR *HeadBlock;
|
||
|
EFI_PEI_READ_ONLY_VARIABLE2_PPI *PPIVariableServices;
|
||
|
|
||
|
LastBlock = NULL;
|
||
|
HeadBlock = NULL;
|
||
|
TempBlock = NULL;
|
||
|
Index = 0;
|
||
|
TempVarName = NULL;
|
||
|
CapsuleVarName[0] = 0;
|
||
|
ValidIndex = 0;
|
||
|
|
||
|
Status = PeiServicesLocatePpi (
|
||
|
&gEfiPeiReadOnlyVariable2PpiGuid,
|
||
|
0,
|
||
|
NULL,
|
||
|
(VOID **) &PPIVariableServices
|
||
|
);
|
||
|
if (Status == EFI_SUCCESS) {
|
||
|
StrCpy (CapsuleVarName, EFI_CAPSULE_VARIABLE_NAME);
|
||
|
TempVarName = CapsuleVarName + StrLen (CapsuleVarName);
|
||
|
Size = sizeof (CapsuleDataPtr64);
|
||
|
while (1) {
|
||
|
if (Index == 0) {
|
||
|
//
|
||
|
// For the first Capsule Image
|
||
|
//
|
||
|
Status = PPIVariableServices->GetVariable (
|
||
|
PPIVariableServices,
|
||
|
CapsuleVarName,
|
||
|
&gEfiCapsuleVendorGuid,
|
||
|
NULL,
|
||
|
&Size,
|
||
|
(VOID *) &CapsuleDataPtr64
|
||
|
);
|
||
|
if (EFI_ERROR (Status)) {
|
||
|
DEBUG ((EFI_D_ERROR, "Capsule -- capsule variable not set\n"));
|
||
|
return EFI_NOT_FOUND;
|
||
|
}
|
||
|
//
|
||
|
// We have a chicken/egg situation where the memory init code needs to
|
||
|
// know the boot mode prior to initializing memory. For this case, our
|
||
|
// validate function will fail. We can detect if this is the case if blocklist
|
||
|
// pointer is null. In that case, return success since we know that the
|
||
|
// variable is set.
|
||
|
//
|
||
|
if (BlockList == NULL) {
|
||
|
return EFI_SUCCESS;
|
||
|
}
|
||
|
//
|
||
|
// Test integrity of descriptors.
|
||
|
//
|
||
|
LastBlock = ValidateCapsuleIntegrity ((EFI_CAPSULE_BLOCK_DESCRIPTOR *)(UINTN)CapsuleDataPtr64);
|
||
|
if (LastBlock == NULL) {
|
||
|
return EFI_NOT_FOUND;
|
||
|
}
|
||
|
//
|
||
|
// Return the base of the block descriptors
|
||
|
//
|
||
|
HeadBlock = (EFI_CAPSULE_BLOCK_DESCRIPTOR *)(UINTN)CapsuleDataPtr64;
|
||
|
} else {
|
||
|
UnicodeValueToString (TempVarName, 0, Index, 0);
|
||
|
Status = PPIVariableServices->GetVariable (
|
||
|
PPIVariableServices,
|
||
|
CapsuleVarName,
|
||
|
&gEfiCapsuleVendorGuid,
|
||
|
NULL,
|
||
|
&Size,
|
||
|
(VOID *) &CapsuleDataPtr64
|
||
|
);
|
||
|
if (EFI_ERROR (Status)) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If this BlockList has been linked before, skip this variable
|
||
|
//
|
||
|
Flag = FALSE;
|
||
|
for (TempIndex = 0; TempIndex < ValidIndex; TempIndex++) {
|
||
|
if (mBufferAddress[TempIndex] == CapsuleDataPtr64) {
|
||
|
Flag = TRUE;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (Flag) {
|
||
|
Index ++;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Test integrity of descriptors.
|
||
|
//
|
||
|
TempBlock = ValidateCapsuleIntegrity ((EFI_CAPSULE_BLOCK_DESCRIPTOR *)(UINTN)CapsuleDataPtr64);
|
||
|
if (TempBlock == NULL) {
|
||
|
return EFI_NOT_FOUND;
|
||
|
}
|
||
|
//
|
||
|
// Combine the different BlockList into single BlockList.
|
||
|
//
|
||
|
LastBlock->Union.DataBlock = CapsuleDataPtr64;
|
||
|
LastBlock->Length = 0;
|
||
|
LastBlock = TempBlock;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Cache BlockList which has been processed
|
||
|
//
|
||
|
mBufferAddress[ValidIndex++] = CapsuleDataPtr64;
|
||
|
Index ++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (HeadBlock != NULL) {
|
||
|
*BlockList = HeadBlock;
|
||
|
return EFI_SUCCESS;
|
||
|
}
|
||
|
return EFI_NOT_FOUND;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Given a pointer to a capsule block descriptor, traverse the list to figure
|
||
|
out how many legitimate descriptors there are, and how big the capsule it
|
||
|
refers to is.
|
||
|
|
||
|
@param Desc Pointer to the capsule block descriptors
|
||
|
NumDescriptors - optional pointer to where to return the number of descriptors
|
||
|
CapsuleSize - optional pointer to where to return the capsule size
|
||
|
@param NumDescriptors Optional pointer to where to return the number of descriptors
|
||
|
@param CapsuleSize Optional pointer to where to return the capsule size
|
||
|
|
||
|
@retval EFI_NOT_FOUND No descriptors containing data in the list
|
||
|
@retval EFI_SUCCESS Return data is valid
|
||
|
**/
|
||
|
EFI_STATUS
|
||
|
GetCapsuleInfo (
|
||
|
IN EFI_CAPSULE_BLOCK_DESCRIPTOR *Desc,
|
||
|
IN OUT UINTN *NumDescriptors OPTIONAL,
|
||
|
IN OUT UINTN *CapsuleSize OPTIONAL
|
||
|
)
|
||
|
{
|
||
|
UINTN Count;
|
||
|
UINTN Size;
|
||
|
|
||
|
ASSERT (Desc != NULL);
|
||
|
|
||
|
Count = 0;
|
||
|
Size = 0;
|
||
|
|
||
|
while (Desc->Union.ContinuationPointer != (EFI_PHYSICAL_ADDRESS) (UINTN) NULL) {
|
||
|
if (Desc->Length == 0) {
|
||
|
//
|
||
|
// Descriptor points to another list of block descriptors somewhere
|
||
|
//
|
||
|
Desc = (EFI_CAPSULE_BLOCK_DESCRIPTOR *) (UINTN) Desc->Union.ContinuationPointer;
|
||
|
} else {
|
||
|
Size += (UINTN) Desc->Length;
|
||
|
Count++;
|
||
|
Desc++;
|
||
|
}
|
||
|
}
|
||
|
//
|
||
|
// If no descriptors, then fail
|
||
|
//
|
||
|
if (Count == 0) {
|
||
|
return EFI_NOT_FOUND;
|
||
|
}
|
||
|
|
||
|
if (NumDescriptors != NULL) {
|
||
|
*NumDescriptors = Count;
|
||
|
}
|
||
|
|
||
|
if (CapsuleSize != NULL) {
|
||
|
*CapsuleSize = Size;
|
||
|
}
|
||
|
|
||
|
return EFI_SUCCESS;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
Try to verify the integrity of a capsule test pattern before the
|
||
|
capsule gets coalesced. This can be useful in narrowing down
|
||
|
where capsule data corruption occurs.
|
||
|
|
||
|
The test pattern mode fills in memory with a counting UINT32 value.
|
||
|
If the capsule is not divided up in a multiple of 4-byte blocks, then
|
||
|
things get messy doing the check. Therefore there are some cases
|
||
|
here where we just give up and skip the pre-coalesce check.
|
||
|
|
||
|
@param PeiServices PEI services table
|
||
|
@param Desc Pointer to capsule descriptors
|
||
|
**/
|
||
|
VOID
|
||
|
CapsuleTestPatternPreCoalesce (
|
||
|
IN EFI_PEI_SERVICES **PeiServices,
|
||
|
IN EFI_CAPSULE_BLOCK_DESCRIPTOR *Desc
|
||
|
)
|
||
|
{
|
||
|
UINT32 *TestPtr;
|
||
|
UINT32 TestCounter;
|
||
|
UINT32 TestSize;
|
||
|
//
|
||
|
// Find first data descriptor
|
||
|
//
|
||
|
while ((Desc->Length == 0) && (Desc->Union.ContinuationPointer != (EFI_PHYSICAL_ADDRESS) (UINTN) NULL)) {
|
||
|
Desc = (EFI_CAPSULE_BLOCK_DESCRIPTOR *) (UINTN) Desc->Union.ContinuationPointer;
|
||
|
}
|
||
|
|
||
|
if (Desc->Union.ContinuationPointer == 0) {
|
||
|
return ;
|
||
|
}
|
||
|
//
|
||
|
// First one better be long enough to at least hold the test signature
|
||
|
//
|
||
|
if (Desc->Length < sizeof (UINT32)) {
|
||
|
DEBUG ((EFI_D_INFO, "Capsule test pattern pre-coalesce punted #1\n"));
|
||
|
return ;
|
||
|
}
|
||
|
|
||
|
TestPtr = (UINT32 *) (UINTN) Desc->Union.DataBlock;
|
||
|
if (*TestPtr != CAPSULE_TEST_SIGNATURE) {
|
||
|
return ;
|
||
|
}
|
||
|
|
||
|
TestCounter = 0;
|
||
|
TestSize = (UINT32) Desc->Length - 2 * sizeof (UINT32);
|
||
|
//
|
||
|
// Skip over the signature and the size fields in the pattern data header
|
||
|
//
|
||
|
TestPtr += 2;
|
||
|
while (1) {
|
||
|
if ((TestSize & 0x03) != 0) {
|
||
|
DEBUG ((EFI_D_INFO, "Capsule test pattern pre-coalesce punted #2\n"));
|
||
|
return ;
|
||
|
}
|
||
|
|
||
|
while (TestSize > 0) {
|
||
|
if (*TestPtr != TestCounter) {
|
||
|
DEBUG ((EFI_D_INFO, "Capsule test pattern pre-coalesce failed data corruption check\n"));
|
||
|
return ;
|
||
|
}
|
||
|
|
||
|
TestSize -= sizeof (UINT32);
|
||
|
TestCounter++;
|
||
|
TestPtr++;
|
||
|
}
|
||
|
Desc++;
|
||
|
while ((Desc->Length == 0) && (Desc->Union.ContinuationPointer != (EFI_PHYSICAL_ADDRESS) (UINTN) NULL)) {
|
||
|
Desc = (EFI_CAPSULE_BLOCK_DESCRIPTOR *) (UINTN) Desc->Union.ContinuationPointer;
|
||
|
}
|
||
|
|
||
|
if (Desc->Union.ContinuationPointer == (EFI_PHYSICAL_ADDRESS) (UINTN) NULL) {
|
||
|
return ;
|
||
|
}
|
||
|
TestSize = (UINT32) Desc->Length;
|
||
|
TestPtr = (UINT32 *) (UINTN) Desc->Union.DataBlock;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
Determine if two buffers overlap in memory.
|
||
|
|
||
|
@param Buff1 pointer to first buffer
|
||
|
@param Size1 size of Buff1
|
||
|
@param Buff2 pointer to second buffer
|
||
|
@param Size2 size of Buff2
|
||
|
|
||
|
@retval TRUE Buffers overlap in memory.
|
||
|
@retval FALSE Buffer doesn't overlap.
|
||
|
**/
|
||
|
BOOLEAN
|
||
|
IsOverlapped (
|
||
|
UINT8 *Buff1,
|
||
|
UINTN Size1,
|
||
|
UINT8 *Buff2,
|
||
|
UINTN Size2
|
||
|
)
|
||
|
{
|
||
|
//
|
||
|
// If buff1's end is less than the start of buff2, then it's ok.
|
||
|
// Also, if buff1's start is beyond buff2's end, then it's ok.
|
||
|
//
|
||
|
if (((Buff1 + Size1) <= Buff2) || (Buff1 >= (Buff2 + Size2))) {
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Given a pointer to the capsule block list, info on the available system
|
||
|
memory, and the size of a buffer, find a free block of memory where a
|
||
|
buffer of the given size can be copied to safely.
|
||
|
|
||
|
@param BlockList Pointer to head of capsule block descriptors
|
||
|
@param MemBase Pointer to the base of memory in which we want to find free space
|
||
|
@param MemSize The size of the block of memory pointed to by MemBase
|
||
|
@param DataSize How big a free block we want to find
|
||
|
|
||
|
@return A pointer to a memory block of at least DataSize that lies somewhere
|
||
|
between MemBase and (MemBase + MemSize). The memory pointed to does not
|
||
|
contain any of the capsule block descriptors or capsule blocks pointed to
|
||
|
by the BlockList.
|
||
|
**/
|
||
|
UINT8 *
|
||
|
FindFreeMem (
|
||
|
EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockList,
|
||
|
UINT8 *MemBase,
|
||
|
UINTN MemSize,
|
||
|
UINTN DataSize
|
||
|
)
|
||
|
{
|
||
|
UINTN Size;
|
||
|
EFI_CAPSULE_BLOCK_DESCRIPTOR *CurrDesc;
|
||
|
EFI_CAPSULE_BLOCK_DESCRIPTOR *TempDesc;
|
||
|
UINT8 *MemEnd;
|
||
|
BOOLEAN Failed;
|
||
|
|
||
|
//
|
||
|
// Need at least enough to copy the data to at the end of the buffer, so
|
||
|
// say the end is less the data size for easy comparisons here.
|
||
|
//
|
||
|
MemEnd = MemBase + MemSize - DataSize;
|
||
|
CurrDesc = BlockList;
|
||
|
//
|
||
|
// Go through all the descriptor blocks and see if any obstruct the range
|
||
|
//
|
||
|
while (CurrDesc != NULL) {
|
||
|
//
|
||
|
// Get the size of this block list and see if it's in the way
|
||
|
//
|
||
|
Failed = FALSE;
|
||
|
TempDesc = CurrDesc;
|
||
|
Size = sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR);
|
||
|
while (TempDesc->Length != 0) {
|
||
|
Size += sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR);
|
||
|
TempDesc++;
|
||
|
}
|
||
|
|
||
|
if (IsOverlapped (MemBase, DataSize, (UINT8 *) CurrDesc, Size)) {
|
||
|
//
|
||
|
// Set our new base to the end of this block list and start all over
|
||
|
//
|
||
|
MemBase = (UINT8 *) CurrDesc + Size;
|
||
|
CurrDesc = BlockList;
|
||
|
if (MemBase > MemEnd) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
Failed = TRUE;
|
||
|
}
|
||
|
//
|
||
|
// Now go through all the blocks and make sure none are in the way
|
||
|
//
|
||
|
while ((CurrDesc->Length != 0) && (!Failed)) {
|
||
|
if (IsOverlapped (MemBase, DataSize, (UINT8 *) (UINTN) CurrDesc->Union.DataBlock, (UINTN) CurrDesc->Length)) {
|
||
|
//
|
||
|
// Set our new base to the end of this block and start all over
|
||
|
//
|
||
|
Failed = TRUE;
|
||
|
MemBase = (UINT8 *) ((UINTN) CurrDesc->Union.DataBlock) + CurrDesc->Length;
|
||
|
CurrDesc = BlockList;
|
||
|
if (MemBase > MemEnd) {
|
||
|
return NULL;
|
||
|
}
|
||
|
}
|
||
|
CurrDesc++;
|
||
|
}
|
||
|
//
|
||
|
// Normal continuation -- jump to next block descriptor list
|
||
|
//
|
||
|
if (!Failed) {
|
||
|
CurrDesc = (EFI_CAPSULE_BLOCK_DESCRIPTOR *) (UINTN) CurrDesc->Union.ContinuationPointer;
|
||
|
}
|
||
|
}
|
||
|
return MemBase;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
The capsule block descriptors may be fragmented and spread all over memory.
|
||
|
To simplify the coalescing of capsule blocks, first coalesce all the
|
||
|
capsule block descriptors low in memory.
|
||
|
|
||
|
The descriptors passed in can be fragmented throughout memory. Here
|
||
|
they are relocated into memory to turn them into a contiguous (null
|
||
|
terminated) array.
|
||
|
|
||
|
@param PeiServices pointer to PEI services table
|
||
|
@param BlockList pointer to the capsule block descriptors
|
||
|
@param MemBase base of system memory in which we can work
|
||
|
@param MemSize size of the system memory pointed to by MemBase
|
||
|
|
||
|
@retval NULL could not relocate the descriptors
|
||
|
@retval Pointer to the base of the successfully-relocated block descriptors.
|
||
|
**/
|
||
|
EFI_CAPSULE_BLOCK_DESCRIPTOR *
|
||
|
RelocateBlockDescriptors (
|
||
|
IN EFI_PEI_SERVICES **PeiServices,
|
||
|
IN EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockList,
|
||
|
IN UINT8 *MemBase,
|
||
|
IN UINTN MemSize
|
||
|
)
|
||
|
{
|
||
|
EFI_CAPSULE_BLOCK_DESCRIPTOR *NewBlockList;
|
||
|
EFI_CAPSULE_BLOCK_DESCRIPTOR *CurrBlockDescHead;
|
||
|
EFI_CAPSULE_BLOCK_DESCRIPTOR *TempBlockDesc;
|
||
|
EFI_CAPSULE_BLOCK_DESCRIPTOR *PrevBlockDescTail;
|
||
|
UINTN NumDescriptors;
|
||
|
UINTN BufferSize;
|
||
|
UINT8 *RelocBuffer;
|
||
|
UINTN BlockListSize;
|
||
|
//
|
||
|
// Get the info on the blocks and descriptors. Since we're going to move
|
||
|
// the descriptors low in memory, adjust the base/size values accordingly here.
|
||
|
// GetCapsuleInfo() returns the number of legit descriptors, so add one for
|
||
|
// a terminator.
|
||
|
//
|
||
|
if (GetCapsuleInfo (BlockList, &NumDescriptors, NULL) != EFI_SUCCESS) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
NumDescriptors++;
|
||
|
BufferSize = NumDescriptors * sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR);
|
||
|
NewBlockList = (EFI_CAPSULE_BLOCK_DESCRIPTOR *) MemBase;
|
||
|
if (MemSize < BufferSize) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
MemSize -= BufferSize;
|
||
|
MemBase += BufferSize;
|
||
|
//
|
||
|
// Go through all the blocks and make sure none are in the way
|
||
|
//
|
||
|
TempBlockDesc = BlockList;
|
||
|
while (TempBlockDesc->Union.ContinuationPointer != (EFI_PHYSICAL_ADDRESS) (UINTN) NULL) {
|
||
|
if (TempBlockDesc->Length == 0) {
|
||
|
//
|
||
|
// Next block of descriptors
|
||
|
//
|
||
|
TempBlockDesc = (EFI_CAPSULE_BLOCK_DESCRIPTOR *) (UINTN) TempBlockDesc->Union.ContinuationPointer;
|
||
|
} else {
|
||
|
//
|
||
|
// If the capsule data pointed to by this descriptor is in the way,
|
||
|
// move it.
|
||
|
//
|
||
|
if (IsOverlapped (
|
||
|
(UINT8 *) NewBlockList,
|
||
|
BufferSize,
|
||
|
(UINT8 *) (UINTN) TempBlockDesc->Union.DataBlock,
|
||
|
(UINTN) TempBlockDesc->Length
|
||
|
)) {
|
||
|
//
|
||
|
// Relocate the block
|
||
|
//
|
||
|
RelocBuffer = FindFreeMem (BlockList, MemBase, MemSize, (UINTN) TempBlockDesc->Length);
|
||
|
if (RelocBuffer == NULL) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
CopyMem ((VOID *) RelocBuffer, (VOID *) (UINTN) TempBlockDesc->Union.DataBlock, (UINTN) TempBlockDesc->Length);
|
||
|
TempBlockDesc->Union.DataBlock = (EFI_PHYSICAL_ADDRESS) (UINTN) RelocBuffer;
|
||
|
|
||
|
DEBUG ((EFI_D_INFO, "Capsule relocate descriptors from/to/size 0x%X 0x%X 0x%X\n", (UINT32)(UINTN)TempBlockDesc->Union.DataBlock, (UINT32)(UINTN)RelocBuffer, (UINT32)(UINTN)TempBlockDesc->Length));
|
||
|
}
|
||
|
}
|
||
|
TempBlockDesc++;
|
||
|
}
|
||
|
//
|
||
|
// Now go through all the block descriptors to make sure that they're not
|
||
|
// in the memory region we want to copy them to.
|
||
|
//
|
||
|
CurrBlockDescHead = BlockList;
|
||
|
PrevBlockDescTail = NULL;
|
||
|
while ((CurrBlockDescHead != NULL) && (CurrBlockDescHead->Union.ContinuationPointer != (EFI_PHYSICAL_ADDRESS) (UINTN) NULL)) {
|
||
|
//
|
||
|
// Get the size of this list then see if it overlaps our low region
|
||
|
//
|
||
|
TempBlockDesc = CurrBlockDescHead;
|
||
|
BlockListSize = sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR);
|
||
|
while (TempBlockDesc->Length != 0) {
|
||
|
BlockListSize += sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR);
|
||
|
TempBlockDesc++;
|
||
|
}
|
||
|
|
||
|
if (IsOverlapped (
|
||
|
(UINT8 *) NewBlockList,
|
||
|
BufferSize,
|
||
|
(UINT8 *) CurrBlockDescHead,
|
||
|
BlockListSize
|
||
|
)) {
|
||
|
//
|
||
|
// Overlaps, so move it out of the way
|
||
|
//
|
||
|
RelocBuffer = FindFreeMem (BlockList, MemBase, MemSize, BlockListSize);
|
||
|
if (RelocBuffer == NULL) {
|
||
|
return NULL;
|
||
|
}
|
||
|
CopyMem ((VOID *) RelocBuffer, (VOID *) CurrBlockDescHead, BlockListSize);
|
||
|
DEBUG ((EFI_D_INFO, "Capsule reloc descriptor block #2\n"));
|
||
|
//
|
||
|
// Point the previous block's next point to this copied version. If
|
||
|
// the tail pointer is null, then this is the first descriptor block.
|
||
|
//
|
||
|
if (PrevBlockDescTail == NULL) {
|
||
|
BlockList = (EFI_CAPSULE_BLOCK_DESCRIPTOR *) RelocBuffer;
|
||
|
} else {
|
||
|
PrevBlockDescTail->Union.DataBlock = (EFI_PHYSICAL_ADDRESS) (UINTN) RelocBuffer;
|
||
|
}
|
||
|
}
|
||
|
//
|
||
|
// Save our new tail and jump to the next block list
|
||
|
//
|
||
|
PrevBlockDescTail = TempBlockDesc;
|
||
|
CurrBlockDescHead = (EFI_CAPSULE_BLOCK_DESCRIPTOR *) (UINTN) TempBlockDesc->Union.ContinuationPointer;
|
||
|
}
|
||
|
//
|
||
|
// Cleared out low memory. Now copy the descriptors down there.
|
||
|
//
|
||
|
TempBlockDesc = BlockList;
|
||
|
CurrBlockDescHead = NewBlockList;
|
||
|
while ((TempBlockDesc != NULL) && (TempBlockDesc->Union.ContinuationPointer != (EFI_PHYSICAL_ADDRESS) (UINTN) NULL)) {
|
||
|
if (TempBlockDesc->Length != 0) {
|
||
|
CurrBlockDescHead->Union.DataBlock = TempBlockDesc->Union.DataBlock;
|
||
|
CurrBlockDescHead->Length = TempBlockDesc->Length;
|
||
|
CurrBlockDescHead++;
|
||
|
TempBlockDesc++;
|
||
|
} else {
|
||
|
TempBlockDesc = (EFI_CAPSULE_BLOCK_DESCRIPTOR *) (UINTN) TempBlockDesc->Union.ContinuationPointer;
|
||
|
}
|
||
|
}
|
||
|
//
|
||
|
// Null terminate
|
||
|
//
|
||
|
CurrBlockDescHead->Union.ContinuationPointer = (EFI_PHYSICAL_ADDRESS) (UINTN) NULL;
|
||
|
CurrBlockDescHead->Length = 0;
|
||
|
return NewBlockList;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Capsule PPI service to coalesce a fragmented capsule in memory.
|
||
|
|
||
|
Memory Map for coalesced capsule:
|
||
|
MemBase + ---->+---------------------------+<-----------+
|
||
|
MemSize | CapsuleOffset[49] | |
|
||
|
+---------------------------+ |
|
||
|
| ................ | |
|
||
|
+---------------------------+ |
|
||
|
| CapsuleOffset[2] | |
|
||
|
+---------------------------+ |
|
||
|
| CapsuleOffset[1] | |
|
||
|
+---------------------------+ |
|
||
|
| CapsuleOffset[0] | CapsuleSize
|
||
|
+---------------------------+ |
|
||
|
| CapsuleNumber | |
|
||
|
+---------------------------+ |
|
||
|
| | |
|
||
|
| | |
|
||
|
| Capsule Image | |
|
||
|
| | |
|
||
|
| | |
|
||
|
+---------------------------+ |
|
||
|
| PrivateData | |
|
||
|
DestPtr ----> +---------------------------+<-----------+
|
||
|
| | |
|
||
|
| FreeMem | FreeMemSize
|
||
|
| | |
|
||
|
FreeMemBase --->+---------------------------+<-----------+
|
||
|
| Terminator |
|
||
|
+---------------------------+
|
||
|
| BlockDescriptor n |
|
||
|
+---------------------------+
|
||
|
| ................. |
|
||
|
+---------------------------+
|
||
|
| BlockDescriptor 1 |
|
||
|
+---------------------------+
|
||
|
| BlockDescriptor 0 |
|
||
|
+---------------------------+
|
||
|
| PrivateDataDesc 0 |
|
||
|
MemBase ---->+---------------------------+<----- BlockList
|
||
|
|
||
|
|
||
|
@param PeiServices General purpose services available to every PEIM.
|
||
|
@param MemoryBase Pointer to the base of a block of memory that we can walk
|
||
|
all over while trying to coalesce our buffers.
|
||
|
On output, this variable will hold the base address of
|
||
|
a coalesced capsule.
|
||
|
@param MemorySize Size of the memory region pointed to by MemoryBase.
|
||
|
On output, this variable will contain the size of the
|
||
|
coalesced capsule.
|
||
|
|
||
|
@retval EFI_NOT_FOUND if we can't determine the boot mode
|
||
|
if the boot mode is not flash-update
|
||
|
if we could not find the capsule descriptors
|
||
|
|
||
|
@retval EFI_BUFFER_TOO_SMALL
|
||
|
if we could not coalesce the capsule in the memory
|
||
|
region provided to us
|
||
|
|
||
|
@retval EFI_SUCCESS if there's no capsule, or if we processed the
|
||
|
capsule successfully.
|
||
|
**/
|
||
|
EFI_STATUS
|
||
|
EFIAPI
|
||
|
CapsuleCoalesce (
|
||
|
IN EFI_PEI_SERVICES **PeiServices,
|
||
|
IN OUT VOID **MemoryBase,
|
||
|
IN OUT UINTN *MemorySize
|
||
|
)
|
||
|
{
|
||
|
VOID *NewCapsuleBase;
|
||
|
VOID *DataPtr;
|
||
|
UINT8 CapsuleIndex;
|
||
|
UINT8 *FreeMemBase;
|
||
|
UINT8 *DestPtr;
|
||
|
UINT8 *RelocPtr;
|
||
|
UINT32 CapsuleOffset[MAX_SUPPORT_CAPSULE_NUM];
|
||
|
UINT32 *AddDataPtr;
|
||
|
UINT32 CapsuleTimes;
|
||
|
UINT64 SizeLeft;
|
||
|
UINT64 CapsuleImageSize;
|
||
|
UINTN CapsuleSize;
|
||
|
UINTN DescriptorsSize;
|
||
|
UINTN FreeMemSize;
|
||
|
UINTN NumDescriptors;
|
||
|
UINTN Index;
|
||
|
UINTN Size;
|
||
|
UINTN VariableCount;
|
||
|
CHAR16 CapsuleVarName[30];
|
||
|
CHAR16 *TempVarName;
|
||
|
EFI_PHYSICAL_ADDRESS CapsuleDataPtr64;
|
||
|
BOOLEAN IsCorrupted;
|
||
|
BOOLEAN CapsuleBeginFlag;
|
||
|
EFI_STATUS Status;
|
||
|
EFI_BOOT_MODE BootMode;
|
||
|
EFI_CAPSULE_HEADER *CapsuleHeader;
|
||
|
EFI_CAPSULE_PEIM_PRIVATE_DATA PrivateData;
|
||
|
EFI_CAPSULE_PEIM_PRIVATE_DATA *PrivateDataPtr;
|
||
|
EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockList;
|
||
|
EFI_CAPSULE_BLOCK_DESCRIPTOR *CurrentBlockDesc;
|
||
|
EFI_CAPSULE_BLOCK_DESCRIPTOR *TempBlockDesc;
|
||
|
EFI_CAPSULE_BLOCK_DESCRIPTOR PrivateDataDesc[2];
|
||
|
EFI_PEI_READ_ONLY_VARIABLE2_PPI *PPIVariableServices;
|
||
|
|
||
|
CapsuleIndex = 0;
|
||
|
SizeLeft = 0;
|
||
|
CapsuleTimes = 0;
|
||
|
CapsuleImageSize = 0;
|
||
|
PrivateDataPtr = NULL;
|
||
|
AddDataPtr = NULL;
|
||
|
CapsuleHeader = NULL;
|
||
|
CapsuleBeginFlag = TRUE;
|
||
|
IsCorrupted = TRUE;
|
||
|
CapsuleSize = 0;
|
||
|
NumDescriptors = 0;
|
||
|
Index = 0;
|
||
|
VariableCount = 0;
|
||
|
CapsuleVarName[0] = 0;
|
||
|
|
||
|
//
|
||
|
// Someone should have already ascertained the boot mode. If it's not
|
||
|
// capsule update, then return normally.
|
||
|
//
|
||
|
Status = PeiServicesGetBootMode (&BootMode);
|
||
|
if (EFI_ERROR (Status) || (BootMode != BOOT_ON_FLASH_UPDATE)) {
|
||
|
return EFI_NOT_FOUND;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// User may set the same ScatterGatherList with several different variables,
|
||
|
// so cache all ScatterGatherList for check later.
|
||
|
//
|
||
|
Status = PeiServicesLocatePpi (
|
||
|
&gEfiPeiReadOnlyVariable2PpiGuid,
|
||
|
0,
|
||
|
NULL,
|
||
|
(VOID **) &PPIVariableServices
|
||
|
);
|
||
|
if (EFI_ERROR (Status)) {
|
||
|
return Status;
|
||
|
}
|
||
|
Size = sizeof (CapsuleDataPtr64);
|
||
|
StrCpy (CapsuleVarName, EFI_CAPSULE_VARIABLE_NAME);
|
||
|
TempVarName = CapsuleVarName + StrLen (CapsuleVarName);
|
||
|
while (TRUE) {
|
||
|
if (Index > 0) {
|
||
|
UnicodeValueToString (TempVarName, 0, Index, 0);
|
||
|
}
|
||
|
Status = PPIVariableServices->GetVariable (
|
||
|
PPIVariableServices,
|
||
|
CapsuleVarName,
|
||
|
&gEfiCapsuleVendorGuid,
|
||
|
NULL,
|
||
|
&Size,
|
||
|
(VOID *) &CapsuleDataPtr64
|
||
|
);
|
||
|
if (EFI_ERROR (Status)) {
|
||
|
//
|
||
|
// There is no capsule variables, quit
|
||
|
//
|
||
|
DEBUG ((EFI_D_ERROR,"Capsule variable Index = %d\n", Index));
|
||
|
break;
|
||
|
}
|
||
|
VariableCount++;
|
||
|
Index++;
|
||
|
}
|
||
|
|
||
|
DEBUG ((EFI_D_ERROR,"Capsule variable count = %d\n", VariableCount));
|
||
|
|
||
|
Status = PeiServicesAllocatePool (
|
||
|
VariableCount * sizeof (EFI_PHYSICAL_ADDRESS),
|
||
|
(VOID **)&mBufferAddress
|
||
|
);
|
||
|
|
||
|
if (Status != EFI_SUCCESS) {
|
||
|
DEBUG ((EFI_D_ERROR, "AllocatePages Failed!, Status = %x\n", Status));
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Find out if we actually have a capsule.
|
||
|
//
|
||
|
Status = GetCapsuleDescriptors (&BlockList);
|
||
|
if (EFI_ERROR (Status)) {
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
DEBUG_CODE (
|
||
|
CapsuleTestPatternPreCoalesce (PeiServices, BlockList);
|
||
|
);
|
||
|
|
||
|
//
|
||
|
// Get the size of our descriptors and the capsule size. GetCapsuleInfo()
|
||
|
// returns the number of descriptors that actually point to data, so add
|
||
|
// one for a terminator. Do that below.
|
||
|
//
|
||
|
GetCapsuleInfo (BlockList, &NumDescriptors, &CapsuleSize);
|
||
|
if ((CapsuleSize == 0) || (NumDescriptors == 0)) {
|
||
|
return EFI_NOT_FOUND;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Initialize our local copy of private data. When we're done, we'll create a
|
||
|
// descriptor for it as well so that it can be put into free memory without
|
||
|
// trashing anything.
|
||
|
//
|
||
|
PrivateData.Signature = EFI_CAPSULE_PEIM_PRIVATE_DATA_SIGNATURE;
|
||
|
PrivateData.CapsuleSize = CapsuleSize;
|
||
|
PrivateDataDesc[0].Union.DataBlock = (EFI_PHYSICAL_ADDRESS) (UINTN) &PrivateData;
|
||
|
PrivateDataDesc[0].Length = sizeof (EFI_CAPSULE_PEIM_PRIVATE_DATA);
|
||
|
PrivateDataDesc[1].Union.DataBlock = (EFI_PHYSICAL_ADDRESS) (UINTN) BlockList;
|
||
|
PrivateDataDesc[1].Length = 0;
|
||
|
//
|
||
|
// In addition to PrivateDataDesc[1:0], one terminator is added
|
||
|
// See below RelocateBlockDescriptors()
|
||
|
//
|
||
|
NumDescriptors += 3;
|
||
|
CapsuleSize += sizeof (EFI_CAPSULE_PEIM_PRIVATE_DATA) + sizeof(CapsuleOffset) + sizeof(UINT32);
|
||
|
BlockList = PrivateDataDesc;
|
||
|
DescriptorsSize = NumDescriptors * sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR);
|
||
|
|
||
|
//
|
||
|
// Don't go below some min address. If the base is below it,
|
||
|
// then move it up and adjust the size accordingly.
|
||
|
//
|
||
|
DEBUG ((EFI_D_INFO, "Capsule Memory range from 0x%8X to 0x%8X\n", (UINTN) *MemoryBase, (UINTN)*MemoryBase + *MemorySize));
|
||
|
if ((UINTN)*MemoryBase < (UINTN) MIN_COALESCE_ADDR) {
|
||
|
if (((UINTN)*MemoryBase + *MemorySize) < (UINTN) MIN_COALESCE_ADDR) {
|
||
|
return EFI_BUFFER_TOO_SMALL;
|
||
|
} else {
|
||
|
*MemorySize = *MemorySize - ((UINTN) MIN_COALESCE_ADDR - (UINTN) *MemoryBase);
|
||
|
*MemoryBase = (VOID *) (UINTN) MIN_COALESCE_ADDR;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (*MemorySize <= (CapsuleSize + DescriptorsSize)) {
|
||
|
return EFI_BUFFER_TOO_SMALL;
|
||
|
}
|
||
|
|
||
|
FreeMemBase = *MemoryBase;
|
||
|
FreeMemSize = *MemorySize;
|
||
|
DEBUG ((EFI_D_INFO, "Capsule Free Memory from 0x%8X to 0x%8X\n", (UINTN) FreeMemBase, (UINTN) FreeMemBase + FreeMemSize));
|
||
|
|
||
|
//
|
||
|
// Relocate all the block descriptors to low memory to make further
|
||
|
// processing easier.
|
||
|
//
|
||
|
BlockList = RelocateBlockDescriptors (PeiServices, BlockList, FreeMemBase, FreeMemSize);
|
||
|
if (BlockList == NULL) {
|
||
|
//
|
||
|
// Not enough room to relocate the descriptors
|
||
|
//
|
||
|
return EFI_BUFFER_TOO_SMALL;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Take the top of memory for the capsule. Naturally align.
|
||
|
//
|
||
|
DestPtr = FreeMemBase + FreeMemSize - CapsuleSize;
|
||
|
DestPtr = (UINT8 *) ((UINTN) DestPtr &~ (UINTN) (sizeof (UINTN) - 1));
|
||
|
FreeMemBase = (UINT8 *) BlockList + DescriptorsSize;
|
||
|
FreeMemSize = FreeMemSize - DescriptorsSize - CapsuleSize;
|
||
|
NewCapsuleBase = (VOID *) DestPtr;
|
||
|
|
||
|
//
|
||
|
// Move all the blocks to the top (high) of memory.
|
||
|
// Relocate all the obstructing blocks. Note that the block descriptors
|
||
|
// were coalesced when they were relocated, so we can just ++ the pointer.
|
||
|
//
|
||
|
CurrentBlockDesc = BlockList;
|
||
|
while ((CurrentBlockDesc->Length != 0) || (CurrentBlockDesc->Union.ContinuationPointer != (EFI_PHYSICAL_ADDRESS) (UINTN) NULL)) {
|
||
|
//
|
||
|
// See if any of the remaining capsule blocks are in the way
|
||
|
//
|
||
|
TempBlockDesc = CurrentBlockDesc;
|
||
|
while (TempBlockDesc->Length != 0) {
|
||
|
//
|
||
|
// Is this block in the way of where we want to copy the current descriptor to?
|
||
|
//
|
||
|
if (IsOverlapped (
|
||
|
(UINT8 *) DestPtr,
|
||
|
(UINTN) CurrentBlockDesc->Length,
|
||
|
(UINT8 *) (UINTN) TempBlockDesc->Union.DataBlock,
|
||
|
(UINTN) TempBlockDesc->Length
|
||
|
)) {
|
||
|
//
|
||
|
// Relocate the block
|
||
|
//
|
||
|
RelocPtr = FindFreeMem (BlockList, FreeMemBase, FreeMemSize, (UINTN) TempBlockDesc->Length);
|
||
|
if (RelocPtr == NULL) {
|
||
|
return EFI_BUFFER_TOO_SMALL;
|
||
|
}
|
||
|
|
||
|
CopyMem ((VOID *) RelocPtr, (VOID *) (UINTN) TempBlockDesc->Union.DataBlock, (UINTN) TempBlockDesc->Length);
|
||
|
DEBUG ((EFI_D_INFO, "Capsule reloc data block from 0x%8X to 0x%8X with size 0x%8X\n",
|
||
|
(UINTN) TempBlockDesc->Union.DataBlock, (UINTN) RelocPtr, (UINTN) TempBlockDesc->Length));
|
||
|
|
||
|
TempBlockDesc->Union.DataBlock = (EFI_PHYSICAL_ADDRESS) (UINTN) RelocPtr;
|
||
|
}
|
||
|
//
|
||
|
// Next descriptor
|
||
|
//
|
||
|
TempBlockDesc++;
|
||
|
}
|
||
|
//
|
||
|
// Ok, we made it through. Copy the block.
|
||
|
// we just support greping one capsule from the lists of block descs list.
|
||
|
//
|
||
|
CapsuleTimes ++;
|
||
|
//
|
||
|
//Skip the first block descriptor that filled with EFI_CAPSULE_PEIM_PRIVATE_DATA
|
||
|
//
|
||
|
if (CapsuleTimes > 1) {
|
||
|
//
|
||
|
//For every capsule entry point, check its header to determine whether to relocate it.
|
||
|
//If it is invalid, skip it and move on to the next capsule. If it is valid, relocate it.
|
||
|
//
|
||
|
if (CapsuleBeginFlag) {
|
||
|
CapsuleBeginFlag = FALSE;
|
||
|
CapsuleHeader = (EFI_CAPSULE_HEADER*)(UINTN)CurrentBlockDesc->Union.DataBlock;
|
||
|
SizeLeft = CapsuleHeader->CapsuleImageSize;
|
||
|
if (!IsCapsuleCorrupted (CapsuleHeader)) {
|
||
|
|
||
|
if (CapsuleIndex > (MAX_SUPPORT_CAPSULE_NUM - 1)) {
|
||
|
DEBUG ((EFI_D_ERROR, "Capsule number exceeds the max number of %d!\n", MAX_SUPPORT_CAPSULE_NUM));
|
||
|
return EFI_BUFFER_TOO_SMALL;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Relocate this valid capsule
|
||
|
//
|
||
|
IsCorrupted = FALSE;
|
||
|
CapsuleImageSize += SizeLeft;
|
||
|
CopyMem ((VOID *) DestPtr, (VOID *) (UINTN) CurrentBlockDesc->Union.DataBlock, (UINTN) CurrentBlockDesc->Length);
|
||
|
DEBUG ((EFI_D_INFO, "Capsule coalesce block no.0x%8X from 0x%8X to 0x%8X with size 0x%8X\n",CapsuleTimes,
|
||
|
(UINTN)CurrentBlockDesc->Union.DataBlock, (UINTN)DestPtr, (UINTN)CurrentBlockDesc->Length));
|
||
|
//
|
||
|
// Cache the begin offset of this capsule
|
||
|
//
|
||
|
CapsuleOffset[CapsuleIndex++] = (UINT32) (UINTN) DestPtr - (UINT32)(UINTN)NewCapsuleBase - (UINT32)sizeof(EFI_CAPSULE_PEIM_PRIVATE_DATA);
|
||
|
DestPtr += CurrentBlockDesc->Length;
|
||
|
}
|
||
|
//
|
||
|
// If the current block length is greater than or equal to SizeLeft, this is the
|
||
|
// start of the next capsule
|
||
|
//
|
||
|
if (CurrentBlockDesc->Length < SizeLeft) {
|
||
|
SizeLeft -= CurrentBlockDesc->Length;
|
||
|
} else {
|
||
|
//
|
||
|
// Start the next cycle
|
||
|
//
|
||
|
SizeLeft = 0;
|
||
|
IsCorrupted = TRUE;
|
||
|
CapsuleBeginFlag = TRUE;
|
||
|
}
|
||
|
} else {
|
||
|
//
|
||
|
//Go on relocating the current capule image.
|
||
|
//
|
||
|
if (CurrentBlockDesc->Length < SizeLeft) {
|
||
|
if (!IsCorrupted) {
|
||
|
CopyMem ((VOID *) DestPtr, (VOID *) (UINTN) (CurrentBlockDesc->Union.DataBlock), (UINTN)CurrentBlockDesc->Length);
|
||
|
DEBUG ((EFI_D_INFO, "Capsule coalesce block no.0x%8X from 0x%8X to 0x%8X with size 0x%8X\n",CapsuleTimes,
|
||
|
(UINTN)CurrentBlockDesc->Union.DataBlock, (UINTN)DestPtr, (UINTN)CurrentBlockDesc->Length));
|
||
|
DestPtr += CurrentBlockDesc->Length;
|
||
|
}
|
||
|
SizeLeft -= CurrentBlockDesc->Length;
|
||
|
} else {
|
||
|
//
|
||
|
//Here is the end of the current capsule image.
|
||
|
//
|
||
|
if (!IsCorrupted) {
|
||
|
CopyMem ((VOID *) DestPtr, (VOID *)(UINTN)(CurrentBlockDesc->Union.DataBlock), (UINTN)CurrentBlockDesc->Length);
|
||
|
DEBUG ((EFI_D_INFO, "Capsule coalesce block no.0x%8X from 0x%8X to 0x%8X with size 0x%8X\n",CapsuleTimes,
|
||
|
(UINTN)CurrentBlockDesc->Union.DataBlock, (UINTN)DestPtr, (UINTN)CurrentBlockDesc->Length));
|
||
|
DestPtr += CurrentBlockDesc->Length;
|
||
|
}
|
||
|
//
|
||
|
// Start the next cycle
|
||
|
//
|
||
|
SizeLeft = 0;
|
||
|
IsCorrupted = TRUE;
|
||
|
CapsuleBeginFlag = TRUE;
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
//
|
||
|
//The first entry is the block descriptor for EFI_CAPSULE_PEIM_PRIVATE_DATA.
|
||
|
//
|
||
|
CopyMem ((VOID *) DestPtr, (VOID *) (UINTN) CurrentBlockDesc->Union.DataBlock, (UINTN) CurrentBlockDesc->Length);
|
||
|
DestPtr += CurrentBlockDesc->Length;
|
||
|
}
|
||
|
//
|
||
|
//Walk through the block descriptor list.
|
||
|
//
|
||
|
CurrentBlockDesc++;
|
||
|
}
|
||
|
//
|
||
|
// We return the base of memory we want reserved, and the size.
|
||
|
// The memory peim should handle it appropriately from there.
|
||
|
//
|
||
|
*MemorySize = (UINTN) CapsuleImageSize;
|
||
|
*MemoryBase = (VOID *) NewCapsuleBase;
|
||
|
|
||
|
//
|
||
|
//Append the offsets of mutiply capsules to the continous buffer
|
||
|
//
|
||
|
DataPtr = (VOID*)((UINTN)NewCapsuleBase + sizeof(EFI_CAPSULE_PEIM_PRIVATE_DATA) + (UINTN)CapsuleImageSize);
|
||
|
AddDataPtr = (UINT32*)(((UINTN) DataPtr + sizeof(UINT32) - 1) &~ (UINT32) (sizeof (UINT32) - 1));
|
||
|
|
||
|
*AddDataPtr++ = CapsuleIndex;
|
||
|
|
||
|
CopyMem (AddDataPtr, &CapsuleOffset[0], sizeof (UINT32) * CapsuleIndex);
|
||
|
|
||
|
PrivateDataPtr = (EFI_CAPSULE_PEIM_PRIVATE_DATA *) NewCapsuleBase;
|
||
|
PrivateDataPtr->CapsuleSize = (UINTN)CapsuleImageSize;
|
||
|
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Determine if we're in capsule update boot mode.
|
||
|
|
||
|
@param PeiServices PEI services table
|
||
|
|
||
|
@retval EFI_SUCCESS if we have a capsule available
|
||
|
@retval EFI_NOT_FOUND no capsule detected
|
||
|
|
||
|
**/
|
||
|
EFI_STATUS
|
||
|
EFIAPI
|
||
|
CheckCapsuleUpdate (
|
||
|
IN EFI_PEI_SERVICES **PeiServices
|
||
|
)
|
||
|
{
|
||
|
EFI_STATUS Status;
|
||
|
Status = GetCapsuleDescriptors (NULL);
|
||
|
return Status;
|
||
|
}
|
||
|
/**
|
||
|
This function will look at a capsule and determine if it's a test pattern.
|
||
|
If it is, then it will verify it and emit an error message if corruption is detected.
|
||
|
|
||
|
@param PeiServices Standard pei services pointer
|
||
|
@param CapsuleBase Base address of coalesced capsule, which is preceeded
|
||
|
by private data. Very implementation specific.
|
||
|
|
||
|
@retval TRUE Capsule image is the test image
|
||
|
@retval FALSE Capsule image is not the test image.
|
||
|
|
||
|
**/
|
||
|
BOOLEAN
|
||
|
CapsuleTestPattern (
|
||
|
IN EFI_PEI_SERVICES **PeiServices,
|
||
|
IN VOID *CapsuleBase
|
||
|
)
|
||
|
{
|
||
|
UINT32 *TestPtr;
|
||
|
UINT32 TestCounter;
|
||
|
UINT32 TestSize;
|
||
|
BOOLEAN RetValue;
|
||
|
|
||
|
RetValue = FALSE;
|
||
|
|
||
|
//
|
||
|
// Look at the capsule data and determine if it's a test pattern. If it
|
||
|
// is, then test it now.
|
||
|
//
|
||
|
TestPtr = (UINT32 *) CapsuleBase;
|
||
|
if (*TestPtr == CAPSULE_TEST_SIGNATURE) {
|
||
|
RetValue = TRUE;
|
||
|
DEBUG ((EFI_D_INFO, "Capsule test pattern mode activated...\n"));
|
||
|
TestSize = TestPtr[1] / sizeof (UINT32);
|
||
|
//
|
||
|
// Skip over the signature and the size fields in the pattern data header
|
||
|
//
|
||
|
TestPtr += 2;
|
||
|
TestCounter = 0;
|
||
|
while (TestSize > 0) {
|
||
|
if (*TestPtr != TestCounter) {
|
||
|
DEBUG ((EFI_D_INFO, "Capsule test pattern mode FAILED: BaseAddr/FailAddr 0x%X 0x%X\n", (UINT32)(UINTN)(EFI_CAPSULE_PEIM_PRIVATE_DATA *)CapsuleBase, (UINT32)(UINTN)TestPtr));
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
TestPtr++;
|
||
|
TestCounter++;
|
||
|
TestSize--;
|
||
|
}
|
||
|
|
||
|
DEBUG ((EFI_D_INFO, "Capsule test pattern mode SUCCESS\n"));
|
||
|
}
|
||
|
|
||
|
return RetValue;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Capsule PPI service that gets called after memory is available. The
|
||
|
capsule coalesce function, which must be called first, returns a base
|
||
|
address and size, which can be anything actually. Once the memory init
|
||
|
PEIM has discovered memory, then it should call this function and pass in
|
||
|
the base address and size returned by the coalesce function. Then this
|
||
|
function can create a capsule HOB and return.
|
||
|
|
||
|
@param PeiServices standard pei services pointer
|
||
|
@param CapsuleBase address returned by the capsule coalesce function. Most
|
||
|
likely this will actually be a pointer to private data.
|
||
|
@param CapsuleSize value returned by the capsule coalesce function.
|
||
|
|
||
|
@retval EFI_VOLUME_CORRUPTED CapsuleBase does not appear to point to a
|
||
|
coalesced capsule
|
||
|
@retval EFI_SUCCESS if all goes well.
|
||
|
**/
|
||
|
EFI_STATUS
|
||
|
EFIAPI
|
||
|
CreateState (
|
||
|
IN EFI_PEI_SERVICES **PeiServices,
|
||
|
IN VOID *CapsuleBase,
|
||
|
IN UINTN CapsuleSize
|
||
|
)
|
||
|
{
|
||
|
EFI_STATUS Status;
|
||
|
EFI_CAPSULE_PEIM_PRIVATE_DATA *PrivateData;
|
||
|
UINTN Size;
|
||
|
EFI_PHYSICAL_ADDRESS NewBuffer;
|
||
|
UINT32 *DataPtr;
|
||
|
UINT32 CapsuleNumber;
|
||
|
UINT32 Index;
|
||
|
EFI_PHYSICAL_ADDRESS BaseAddress;
|
||
|
UINT64 Length;
|
||
|
|
||
|
DataPtr = NULL;
|
||
|
CapsuleNumber = 0;
|
||
|
PrivateData = (EFI_CAPSULE_PEIM_PRIVATE_DATA *) CapsuleBase;
|
||
|
if (PrivateData->Signature != EFI_CAPSULE_PEIM_PRIVATE_DATA_SIGNATURE) {
|
||
|
return EFI_VOLUME_CORRUPTED;
|
||
|
}
|
||
|
//
|
||
|
// Capsule Number and Capsule Offset is in the tail of Capsule data.
|
||
|
//
|
||
|
Size = (UINTN) PrivateData->CapsuleSize;
|
||
|
DataPtr = (UINT32*)((UINTN)CapsuleBase + (UINTN)sizeof(EFI_CAPSULE_PEIM_PRIVATE_DATA)+ Size);
|
||
|
DataPtr = (UINT32*)(((UINTN) DataPtr + sizeof(UINT32) - 1) & ~(sizeof (UINT32) - 1));
|
||
|
CapsuleNumber = *DataPtr++;
|
||
|
//
|
||
|
// Allocate the memory so that it gets preserved into DXE
|
||
|
//
|
||
|
Status = PeiServicesAllocatePages (
|
||
|
EfiRuntimeServicesData,
|
||
|
EFI_SIZE_TO_PAGES (Size),
|
||
|
&NewBuffer
|
||
|
);
|
||
|
|
||
|
if (Status != EFI_SUCCESS) {
|
||
|
DEBUG ((EFI_D_ERROR, "AllocatePages Failed!\n"));
|
||
|
return Status;
|
||
|
}
|
||
|
//
|
||
|
// Copy to our new buffer for DXE
|
||
|
//
|
||
|
DEBUG ((EFI_D_INFO, "Capsule copy from 0x%8X to 0x%8X with size 0x%8X\n", (UINTN) (PrivateData + 1), (UINTN) NewBuffer, Size));
|
||
|
CopyMem ((VOID *) (UINTN) NewBuffer, (VOID *) (UINTN) (PrivateData + 1), Size);
|
||
|
//
|
||
|
// Check for test data pattern. If it is the test pattern, then we'll
|
||
|
// test it ans still create the HOB so that it can be used to verify
|
||
|
// that capsules don't get corrupted all the way into BDS. BDS will
|
||
|
// still try to turn it into a firmware volume, but will think it's
|
||
|
// corrupted so nothing will happen.
|
||
|
//
|
||
|
DEBUG_CODE (
|
||
|
CapsuleTestPattern (PeiServices, (VOID *) (UINTN) NewBuffer);
|
||
|
);
|
||
|
|
||
|
//
|
||
|
// Build the UEFI Capsule Hob for each capsule image.
|
||
|
//
|
||
|
for (Index = 0; Index < CapsuleNumber; Index ++) {
|
||
|
BaseAddress = NewBuffer + DataPtr[Index];
|
||
|
Length = ((EFI_CAPSULE_HEADER *)((UINTN) BaseAddress))->CapsuleImageSize;
|
||
|
|
||
|
BuildCvHob (BaseAddress, Length);
|
||
|
}
|
||
|
|
||
|
return EFI_SUCCESS;
|
||
|
}
|
||
|
|
||
|
CONST PEI_CAPSULE_PPI mCapsulePpi = {
|
||
|
CapsuleCoalesce,
|
||
|
CheckCapsuleUpdate,
|
||
|
CreateState
|
||
|
};
|
||
|
|
||
|
CONST EFI_PEI_PPI_DESCRIPTOR mUefiPpiListCapsule = {
|
||
|
(EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
|
||
|
&gPeiCapsulePpiGuid,
|
||
|
(PEI_CAPSULE_PPI *) &mCapsulePpi
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
Entry point function for the PEIM
|
||
|
|
||
|
@param FileHandle Handle of the file being invoked.
|
||
|
@param PeiServices Describes the list of possible PEI Services.
|
||
|
|
||
|
@return EFI_SUCCESS If we installed our PPI
|
||
|
|
||
|
**/
|
||
|
EFI_STATUS
|
||
|
EFIAPI
|
||
|
CapsuleMain (
|
||
|
IN EFI_PEI_FILE_HANDLE FileHandle,
|
||
|
IN CONST EFI_PEI_SERVICES **PeiServices
|
||
|
)
|
||
|
{
|
||
|
//
|
||
|
// Just produce our PPI
|
||
|
//
|
||
|
return PeiServicesInstallPpi (&mUefiPpiListCapsule);
|
||
|
}
|