mirror of https://github.com/acidanthera/audk.git
1255 lines
44 KiB
C
1255 lines
44 KiB
C
/** @file
|
|
The logic to process capsule.
|
|
|
|
Caution: This module requires additional review when modified.
|
|
This driver will have external input - capsule image.
|
|
This external input must be validated carefully to avoid security issue like
|
|
buffer overflow, integer overflow.
|
|
|
|
CapsuleDataCoalesce() will do basic validation before coalesce capsule data
|
|
into memory.
|
|
|
|
(C) Copyright 2014 Hewlett-Packard Development Company, L.P.<BR>
|
|
Copyright (c) 2011 - 2014, 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 <Uefi.h>
|
|
#include <PiPei.h>
|
|
|
|
#include <Guid/CapsuleVendor.h>
|
|
|
|
#include <Library/BaseMemoryLib.h>
|
|
#include <Library/DebugLib.h>
|
|
#include <Library/PrintLib.h>
|
|
#include <Library/BaseLib.h>
|
|
|
|
#include "CommonHeader.h"
|
|
|
|
#define MIN_COALESCE_ADDR (1024 * 1024)
|
|
|
|
/**
|
|
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
|
|
);
|
|
|
|
/**
|
|
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
|
|
);
|
|
|
|
/**
|
|
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 NumDescriptors number of capsule data block descriptors, whose Length is non-zero.
|
|
@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 UINTN NumDescriptors,
|
|
IN UINT8 *MemBase,
|
|
IN UINTN MemSize
|
|
);
|
|
|
|
/**
|
|
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
|
|
);
|
|
|
|
/**
|
|
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
|
|
);
|
|
|
|
/**
|
|
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
|
|
@param NumDescriptors Optional pointer to where to return the number of capsule data descriptors, whose Length is non-zero.
|
|
@param CapsuleSize Optional pointer to where to return the capsule image size
|
|
@param CapsuleNumber Optional pointer to where to return the number of capsule
|
|
|
|
@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,
|
|
IN OUT UINTN *CapsuleNumber OPTIONAL
|
|
);
|
|
|
|
/**
|
|
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;
|
|
}
|
|
|
|
/**
|
|
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;
|
|
UINTN CapsuleCount;
|
|
EFI_CAPSULE_BLOCK_DESCRIPTOR *Ptr;
|
|
|
|
DEBUG ((EFI_D_INFO, "ValidateCapsuleIntegrity\n"));
|
|
|
|
//
|
|
// Go through the list to look for inconsistencies. Check for:
|
|
// * misaligned block descriptors.
|
|
// * The first capsule header guid
|
|
// * The first capsule header flag
|
|
// * The first capsule header HeaderSize
|
|
// * Length > MAX_ADDRESS
|
|
// * ContinuationPointer > MAX_ADDRESS
|
|
// * DataBlock + Length > MAX_ADDRESS
|
|
//
|
|
CapsuleSize = 0;
|
|
CapsuleCount = 0;
|
|
Ptr = BlockList;
|
|
|
|
DEBUG ((EFI_D_INFO, "Ptr - 0x%x\n", Ptr));
|
|
DEBUG ((EFI_D_INFO, "Ptr->Length - 0x%x\n", Ptr->Length));
|
|
DEBUG ((EFI_D_INFO, "Ptr->Union - 0x%x\n", Ptr->Union.ContinuationPointer));
|
|
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 & (sizeof(UINT64) - 1)) {
|
|
DEBUG ((EFI_D_ERROR, "ERROR: BlockList address failed alignment check\n"));
|
|
return NULL;
|
|
}
|
|
//
|
|
// Sanity Check
|
|
//
|
|
if (Ptr->Length > MAX_ADDRESS) {
|
|
DEBUG ((EFI_D_ERROR, "ERROR: Ptr->Length(0x%lx) > MAX_ADDRESS\n", Ptr->Length));
|
|
return NULL;
|
|
}
|
|
|
|
if (Ptr->Length == 0) {
|
|
//
|
|
// Sanity Check
|
|
//
|
|
if (Ptr->Union.ContinuationPointer > MAX_ADDRESS) {
|
|
DEBUG ((EFI_D_ERROR, "ERROR: Ptr->Union.ContinuationPointer(0x%lx) > MAX_ADDRESS\n", Ptr->Union.ContinuationPointer));
|
|
return NULL;
|
|
}
|
|
//
|
|
// Descriptor points to another list of block descriptors somewhere
|
|
// else.
|
|
//
|
|
Ptr = (EFI_CAPSULE_BLOCK_DESCRIPTOR *) (UINTN) Ptr->Union.ContinuationPointer;
|
|
DEBUG ((EFI_D_INFO, "Ptr(C) - 0x%x\n", Ptr));
|
|
DEBUG ((EFI_D_INFO, "Ptr->Length - 0x%x\n", Ptr->Length));
|
|
DEBUG ((EFI_D_INFO, "Ptr->Union - 0x%x\n", Ptr->Union.ContinuationPointer));
|
|
} else {
|
|
//
|
|
// Sanity Check
|
|
//
|
|
if (Ptr->Union.DataBlock > (MAX_ADDRESS - (UINTN)Ptr->Length)) {
|
|
DEBUG ((EFI_D_ERROR, "ERROR: Ptr->Union.DataBlock(0x%lx) > (MAX_ADDRESS - (UINTN)Ptr->Length(0x%lx))\n", Ptr->Union.DataBlock, Ptr->Length));
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
//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);
|
|
//
|
|
// Sanity check
|
|
//
|
|
if (Ptr->Length < sizeof(EFI_CAPSULE_HEADER)) {
|
|
DEBUG ((EFI_D_ERROR, "ERROR: Ptr->Length(0x%lx) < sizeof(EFI_CAPSULE_HEADER)\n", Ptr->Length));
|
|
return NULL;
|
|
}
|
|
//
|
|
// Make sure HeaderSize field is valid
|
|
//
|
|
if (CapsuleHeader->HeaderSize > CapsuleHeader->CapsuleImageSize) {
|
|
DEBUG ((EFI_D_ERROR, "ERROR: CapsuleHeader->HeaderSize(0x%x) > CapsuleHeader->CapsuleImageSize(0x%x)\n", CapsuleHeader->HeaderSize, CapsuleHeader->CapsuleImageSize));
|
|
return NULL;
|
|
}
|
|
if (IsCapsuleCorrupted (CapsuleHeader)) {
|
|
return NULL;
|
|
}
|
|
CapsuleCount ++;
|
|
CapsuleSize = CapsuleHeader->CapsuleImageSize;
|
|
}
|
|
|
|
if (CapsuleSize >= Ptr->Length) {
|
|
CapsuleSize = CapsuleSize - Ptr->Length;
|
|
} else {
|
|
DEBUG ((EFI_D_ERROR, "ERROR: CapsuleSize(0x%lx) < Ptr->Length(0x%lx)\n", CapsuleSize, Ptr->Length));
|
|
//
|
|
// Sanity check
|
|
//
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Move to next BLOCK descriptor
|
|
//
|
|
Ptr++;
|
|
DEBUG ((EFI_D_INFO, "Ptr(B) - 0x%x\n", Ptr));
|
|
DEBUG ((EFI_D_INFO, "Ptr->Length - 0x%x\n", Ptr->Length));
|
|
DEBUG ((EFI_D_INFO, "Ptr->Union - 0x%x\n", Ptr->Union.ContinuationPointer));
|
|
}
|
|
}
|
|
|
|
if (CapsuleCount == 0) {
|
|
//
|
|
// No any capsule is found in BlockList
|
|
//
|
|
DEBUG ((EFI_D_ERROR, "ERROR: CapsuleCount(0x%x) == 0\n", CapsuleCount));
|
|
return NULL;
|
|
}
|
|
|
|
if (CapsuleSize != 0) {
|
|
//
|
|
// Capsule data is incomplete.
|
|
//
|
|
DEBUG ((EFI_D_ERROR, "ERROR: CapsuleSize(0x%lx) != 0\n", CapsuleSize));
|
|
return NULL;
|
|
}
|
|
|
|
return Ptr;
|
|
}
|
|
|
|
/**
|
|
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 NumDescriptors number of capsule data block descriptors, whose Length is non-zero.
|
|
@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 UINTN NumDescriptors,
|
|
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 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.
|
|
// NumDescriptors is the number of legit data descriptors, so add one for
|
|
// a terminator. (Already done by caller, no check is needed.)
|
|
//
|
|
|
|
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);
|
|
DEBUG ((EFI_D_INFO, "Capsule relocate descriptors from/to/size 0x%lX 0x%lX 0x%lX\n", TempBlockDesc->Union.DataBlock, (UINT64)(UINTN)RelocBuffer, TempBlockDesc->Length));
|
|
TempBlockDesc->Union.DataBlock = (EFI_PHYSICAL_ADDRESS) (UINTN) RelocBuffer;
|
|
}
|
|
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;
|
|
}
|
|
|
|
/**
|
|
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 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
|
|
@param NumDescriptors Optional pointer to where to return the number of capsule data descriptors, whose Length is non-zero.
|
|
@param CapsuleSize Optional pointer to where to return the capsule image size
|
|
@param CapsuleNumber Optional pointer to where to return the number of capsule
|
|
|
|
@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,
|
|
IN OUT UINTN *CapsuleNumber OPTIONAL
|
|
)
|
|
{
|
|
UINTN Count;
|
|
UINTN Size;
|
|
UINTN Number;
|
|
UINTN ThisCapsuleImageSize;
|
|
EFI_CAPSULE_HEADER *CapsuleHeader;
|
|
|
|
DEBUG ((EFI_D_INFO, "GetCapsuleInfo enter\n"));
|
|
|
|
ASSERT (Desc != NULL);
|
|
|
|
Count = 0;
|
|
Size = 0;
|
|
Number = 0;
|
|
ThisCapsuleImageSize = 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 {
|
|
//
|
|
// Sanity Check
|
|
// It is needed, because ValidateCapsuleIntegrity() only validate one individual capsule Size.
|
|
// While here we need check all capsules size.
|
|
//
|
|
if (Desc->Length >= (MAX_ADDRESS - Size)) {
|
|
DEBUG ((EFI_D_ERROR, "ERROR: Desc->Length(0x%lx) >= (MAX_ADDRESS - Size(0x%x))\n", Desc->Length, Size));
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
Size += (UINTN) Desc->Length;
|
|
Count++;
|
|
|
|
//
|
|
// See if this is first capsule's header
|
|
//
|
|
if (ThisCapsuleImageSize == 0) {
|
|
CapsuleHeader = (EFI_CAPSULE_HEADER*)((UINTN)Desc->Union.DataBlock);
|
|
//
|
|
// This has been checked in ValidateCapsuleIntegrity()
|
|
//
|
|
Number ++;
|
|
ThisCapsuleImageSize = CapsuleHeader->CapsuleImageSize;
|
|
}
|
|
|
|
//
|
|
// This has been checked in ValidateCapsuleIntegrity()
|
|
//
|
|
ASSERT (ThisCapsuleImageSize >= Desc->Length);
|
|
ThisCapsuleImageSize = (UINTN)(ThisCapsuleImageSize - Desc->Length);
|
|
|
|
//
|
|
// Move to next
|
|
//
|
|
Desc++;
|
|
}
|
|
}
|
|
//
|
|
// If no descriptors, then fail
|
|
//
|
|
if (Count == 0) {
|
|
DEBUG ((EFI_D_ERROR, "ERROR: Count == 0\n"));
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
//
|
|
// checked in ValidateCapsuleIntegrity()
|
|
//
|
|
ASSERT (ThisCapsuleImageSize == 0);
|
|
|
|
if (NumDescriptors != NULL) {
|
|
*NumDescriptors = Count;
|
|
}
|
|
|
|
if (CapsuleSize != NULL) {
|
|
*CapsuleSize = Size;
|
|
}
|
|
|
|
if (CapsuleNumber != NULL) {
|
|
*CapsuleNumber = Number;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
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;
|
|
}
|
|
|
|
/**
|
|
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;
|
|
|
|
DEBUG ((EFI_D_INFO, "CapsuleTestPatternPreCoalesce\n"));
|
|
|
|
//
|
|
// 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;
|
|
//
|
|
// 0x54534554 "TEST"
|
|
//
|
|
if (*TestPtr != 0x54534554) {
|
|
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;
|
|
}
|
|
}
|
|
|
|
/**
|
|
Checks for the presence of capsule descriptors.
|
|
Get capsule descriptors from variable CapsuleUpdateData, CapsuleUpdateData1, CapsuleUpdateData2...
|
|
|
|
@param BlockListBuffer Pointer to the buffer of capsule descriptors variables
|
|
@param BlockDescriptorList Pointer to the capsule descriptors list
|
|
|
|
@retval EFI_SUCCESS a valid capsule is present
|
|
@retval EFI_NOT_FOUND if a valid capsule is not present
|
|
**/
|
|
EFI_STATUS
|
|
BuildCapsuleDescriptors (
|
|
IN EFI_PHYSICAL_ADDRESS *BlockListBuffer,
|
|
OUT EFI_CAPSULE_BLOCK_DESCRIPTOR **BlockDescriptorList
|
|
)
|
|
{
|
|
UINTN Index;
|
|
EFI_CAPSULE_BLOCK_DESCRIPTOR *LastBlock;
|
|
EFI_CAPSULE_BLOCK_DESCRIPTOR *TempBlock;
|
|
EFI_CAPSULE_BLOCK_DESCRIPTOR *HeadBlock;
|
|
|
|
DEBUG ((EFI_D_INFO, "BuildCapsuleDescriptors enter\n"));
|
|
|
|
LastBlock = NULL;
|
|
HeadBlock = NULL;
|
|
TempBlock = NULL;
|
|
Index = 0;
|
|
|
|
while (BlockListBuffer[Index] != 0) {
|
|
//
|
|
// Test integrity of descriptors.
|
|
//
|
|
if (BlockListBuffer[Index] < MAX_ADDRESS) {
|
|
TempBlock = ValidateCapsuleIntegrity ((EFI_CAPSULE_BLOCK_DESCRIPTOR *)(UINTN)BlockListBuffer[Index]);
|
|
if (TempBlock != NULL) {
|
|
if (LastBlock == NULL) {
|
|
LastBlock = TempBlock;
|
|
|
|
//
|
|
// Return the base of the block descriptors
|
|
//
|
|
HeadBlock = (EFI_CAPSULE_BLOCK_DESCRIPTOR *)(UINTN)BlockListBuffer[Index];
|
|
} else {
|
|
//
|
|
// Combine the different BlockList into single BlockList.
|
|
//
|
|
LastBlock->Union.DataBlock = (EFI_PHYSICAL_ADDRESS)(UINTN)BlockListBuffer[Index];
|
|
LastBlock->Length = 0;
|
|
LastBlock = TempBlock;
|
|
}
|
|
}
|
|
} else {
|
|
DEBUG ((EFI_D_ERROR, "ERROR: BlockListBuffer[Index](0x%lx) < MAX_ADDRESS\n", BlockListBuffer[Index]));
|
|
}
|
|
Index ++;
|
|
}
|
|
|
|
if (HeadBlock != NULL) {
|
|
*BlockDescriptorList = HeadBlock;
|
|
return EFI_SUCCESS;
|
|
}
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
/**
|
|
The function to coalesce a fragmented capsule in memory.
|
|
|
|
Memory Map for coalesced capsule:
|
|
MemBase + ---->+---------------------------+<-----------+
|
|
MemSize | ------------------------- | |
|
|
| | Capsule [Num-1] | | |
|
|
| ------------------------- | |
|
|
| | ................ | | |
|
|
| ------------------------- | |
|
|
| | Capsule [1] | | |
|
|
| ------------------------- | |
|
|
| | Capsule [0] | | |
|
|
| ------------------------- | |
|
|
| Capsule Image | |
|
|
CapsuleImageBase-->+---------------------------+
|
|
| ------------------------- | |
|
|
| | CapsuleOffset[Num-1] | | |
|
|
| ------------------------- | |
|
|
| | ................ | | CapsuleSize
|
|
| ------------------------- | |
|
|
| | CapsuleOffset[1] | | |
|
|
| ------------------------- | |
|
|
| | CapsuleOffset[0] | | |
|
|
|---------------------------| |
|
|
| | CapsuleNumber | | |
|
|
| ------------------------- | |
|
|
| | CapsuleAllImageSize | | |
|
|
| ------------------------- | |
|
|
| PrivateData | |
|
|
DestPtr ---->+---------------------------+<-----------+
|
|
| | |
|
|
| FreeMem | FreeMemSize
|
|
| | |
|
|
FreeMemBase --->+---------------------------+<-----------+
|
|
| Terminator |
|
|
+---------------------------+
|
|
| BlockDescriptor n |
|
|
+---------------------------+
|
|
| ................. |
|
|
+---------------------------+
|
|
| BlockDescriptor 1 |
|
|
+---------------------------+
|
|
| BlockDescriptor 0 |
|
|
+---------------------------+
|
|
| PrivateDataDesc 0 |
|
|
MemBase ---->+---------------------------+<----- BlockList
|
|
|
|
Caution: This function may receive untrusted input.
|
|
The capsule data is external input, so this routine will do basic validation before
|
|
coalesce capsule data into memory.
|
|
|
|
@param PeiServices General purpose services available to every PEIM.
|
|
@param BlockListBuffer Point to the buffer of Capsule Descriptor Variables.
|
|
@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 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 Processed the capsule successfully.
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
CapsuleDataCoalesce (
|
|
IN EFI_PEI_SERVICES **PeiServices,
|
|
IN EFI_PHYSICAL_ADDRESS *BlockListBuffer,
|
|
IN OUT VOID **MemoryBase,
|
|
IN OUT UINTN *MemorySize
|
|
)
|
|
{
|
|
VOID *NewCapsuleBase;
|
|
VOID *CapsuleImageBase;
|
|
UINTN CapsuleIndex;
|
|
UINT8 *FreeMemBase;
|
|
UINT8 *DestPtr;
|
|
UINTN DestLength;
|
|
UINT8 *RelocPtr;
|
|
UINTN CapsuleTimes;
|
|
UINT64 SizeLeft;
|
|
UINT64 CapsuleImageSize;
|
|
UINTN CapsuleSize;
|
|
UINTN CapsuleNumber;
|
|
UINTN DescriptorsSize;
|
|
UINTN FreeMemSize;
|
|
UINTN NumDescriptors;
|
|
BOOLEAN CapsuleBeginFlag;
|
|
EFI_STATUS Status;
|
|
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];
|
|
|
|
DEBUG ((EFI_D_INFO, "CapsuleDataCoalesce enter\n"));
|
|
|
|
CapsuleIndex = 0;
|
|
SizeLeft = 0;
|
|
CapsuleTimes = 0;
|
|
CapsuleImageSize = 0;
|
|
PrivateDataPtr = NULL;
|
|
CapsuleHeader = NULL;
|
|
CapsuleBeginFlag = TRUE;
|
|
CapsuleSize = 0;
|
|
NumDescriptors = 0;
|
|
|
|
//
|
|
// Build capsule descriptors list
|
|
//
|
|
Status = BuildCapsuleDescriptors (BlockListBuffer, &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.
|
|
//
|
|
Status = GetCapsuleInfo (BlockList, &NumDescriptors, &CapsuleSize, &CapsuleNumber);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
DEBUG ((EFI_D_INFO, "CapsuleSize - 0x%x\n", CapsuleSize));
|
|
DEBUG ((EFI_D_INFO, "CapsuleNumber - 0x%x\n", CapsuleNumber));
|
|
DEBUG ((EFI_D_INFO, "NumDescriptors - 0x%x\n", NumDescriptors));
|
|
if ((CapsuleSize == 0) || (NumDescriptors == 0) || (CapsuleNumber == 0)) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
if (CapsuleNumber - 1 >= (MAX_ADDRESS - (sizeof (EFI_CAPSULE_PEIM_PRIVATE_DATA) + sizeof(UINT64))) / sizeof(UINT64)) {
|
|
DEBUG ((EFI_D_ERROR, "ERROR: CapsuleNumber - 0x%x\n", CapsuleNumber));
|
|
return EFI_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
//
|
|
// 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.CapsuleAllImageSize = (UINT64) CapsuleSize;
|
|
PrivateData.CapsuleNumber = (UINT64) CapsuleNumber;
|
|
PrivateData.CapsuleOffset[0] = 0;
|
|
//
|
|
// NOTE: Only data in sizeof (EFI_CAPSULE_PEIM_PRIVATE_DATA) is valid, CapsuleOffset field is unitialized at this moment.
|
|
// The code sets partial length here for Descriptor.Length check, but later it will use full length to reserve those PrivateData region.
|
|
//
|
|
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;
|
|
//
|
|
// Add PrivateDataDesc[0] in beginning beginning, as it is new descriptor. PrivateDataDesc[1] is NOT needed.
|
|
// In addition, one NULL terminator is added in the end. See RelocateBlockDescriptors().
|
|
//
|
|
NumDescriptors += 2;
|
|
//
|
|
// Sandity check
|
|
//
|
|
if (CapsuleSize >= (MAX_ADDRESS - (sizeof (EFI_CAPSULE_PEIM_PRIVATE_DATA) + (CapsuleNumber - 1) * sizeof(UINT64) + sizeof(UINT64)))) {
|
|
DEBUG ((EFI_D_ERROR, "ERROR: CapsuleSize - 0x%x\n", CapsuleSize));
|
|
return EFI_BUFFER_TOO_SMALL;
|
|
}
|
|
//
|
|
// Need add sizeof(UINT64) for PrivateData alignment
|
|
//
|
|
CapsuleSize += sizeof (EFI_CAPSULE_PEIM_PRIVATE_DATA) + (CapsuleNumber - 1) * sizeof(UINT64) + sizeof(UINT64);
|
|
BlockList = PrivateDataDesc;
|
|
//
|
|
// Sandity check
|
|
//
|
|
if (NumDescriptors >= (MAX_ADDRESS / sizeof(EFI_CAPSULE_BLOCK_DESCRIPTOR))) {
|
|
DEBUG ((EFI_D_ERROR, "ERROR: NumDescriptors - 0x%x\n", NumDescriptors));
|
|
return EFI_BUFFER_TOO_SMALL;
|
|
}
|
|
DescriptorsSize = NumDescriptors * sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR);
|
|
//
|
|
// Sandity check
|
|
//
|
|
if (DescriptorsSize >= (MAX_ADDRESS - CapsuleSize)) {
|
|
DEBUG ((EFI_D_ERROR, "ERROR: DescriptorsSize - 0x%lx, CapsuleSize - 0x%lx\n", (UINT64)DescriptorsSize, (UINT64)CapsuleSize));
|
|
return EFI_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
//
|
|
// 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) {
|
|
DEBUG ((EFI_D_ERROR, "ERROR: *MemoryBase + *MemorySize - 0x%x\n", (UINTN)*MemoryBase + *MemorySize));
|
|
return EFI_BUFFER_TOO_SMALL;
|
|
} else {
|
|
*MemorySize = *MemorySize - ((UINTN) MIN_COALESCE_ADDR - (UINTN) *MemoryBase);
|
|
*MemoryBase = (VOID *) (UINTN) MIN_COALESCE_ADDR;
|
|
}
|
|
}
|
|
|
|
if (*MemorySize <= (CapsuleSize + DescriptorsSize)) {
|
|
DEBUG ((EFI_D_ERROR, "ERROR: CapsuleSize + DescriptorsSize - 0x%x\n", 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, NumDescriptors, 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. UINT64 align up.
|
|
//
|
|
DestPtr = FreeMemBase + FreeMemSize - CapsuleSize;
|
|
DestPtr = (UINT8 *) (((UINTN)DestPtr + sizeof (UINT64) - 1) & ~(sizeof (UINT64) - 1));
|
|
FreeMemBase = (UINT8 *) BlockList + DescriptorsSize;
|
|
FreeMemSize = (UINTN) DestPtr - (UINTN) FreeMemBase;
|
|
NewCapsuleBase = (VOID *) DestPtr;
|
|
CapsuleImageBase = (UINT8 *)NewCapsuleBase + sizeof(EFI_CAPSULE_PEIM_PRIVATE_DATA) + (CapsuleNumber - 1) * sizeof(UINT64);
|
|
|
|
PrivateDataPtr = (EFI_CAPSULE_PEIM_PRIVATE_DATA *) NewCapsuleBase;
|
|
|
|
//
|
|
// 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)) {
|
|
if (CapsuleTimes == 0) {
|
|
//
|
|
// The first entry is the block descriptor for EFI_CAPSULE_PEIM_PRIVATE_DATA.
|
|
// CapsuleOffset field is uninitialized at this time. No need copy it, but need to reserve for future use.
|
|
//
|
|
ASSERT (CurrentBlockDesc->Union.DataBlock == (UINT64)(UINTN)&PrivateData);
|
|
DestLength = sizeof (EFI_CAPSULE_PEIM_PRIVATE_DATA) + (CapsuleNumber - 1) * sizeof(UINT64);
|
|
} else {
|
|
DestLength = (UINTN)CurrentBlockDesc->Length;
|
|
}
|
|
//
|
|
// 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) DestLength,
|
|
(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;
|
|
|
|
//
|
|
// No more check here is needed, because IsCapsuleCorrupted() already in ValidateCapsuleIntegrity()
|
|
//
|
|
ASSERT (CapsuleIndex < CapsuleNumber);
|
|
|
|
//
|
|
// Relocate this capsule
|
|
//
|
|
CapsuleImageSize += SizeLeft;
|
|
//
|
|
// Cache the begin offset of this capsule
|
|
//
|
|
ASSERT (PrivateDataPtr->Signature == EFI_CAPSULE_PEIM_PRIVATE_DATA_SIGNATURE);
|
|
ASSERT ((UINTN)DestPtr >= (UINTN)CapsuleImageBase);
|
|
PrivateDataPtr->CapsuleOffset[CapsuleIndex++] = (UINT64)((UINTN)DestPtr - (UINTN)CapsuleImageBase);
|
|
}
|
|
|
|
//
|
|
// Below ASSERT is checked in ValidateCapsuleIntegrity()
|
|
//
|
|
ASSERT (CurrentBlockDesc->Length <= SizeLeft);
|
|
|
|
CopyMem ((VOID *) DestPtr, (VOID *) (UINTN) (CurrentBlockDesc->Union.DataBlock), (UINTN)CurrentBlockDesc->Length);
|
|
DEBUG ((EFI_D_INFO, "Capsule coalesce block no.0x%lX from 0x%lX to 0x%lX with size 0x%lX\n",(UINT64)CapsuleTimes,
|
|
CurrentBlockDesc->Union.DataBlock, (UINT64)(UINTN)DestPtr, CurrentBlockDesc->Length));
|
|
DestPtr += CurrentBlockDesc->Length;
|
|
SizeLeft -= CurrentBlockDesc->Length;
|
|
|
|
if (SizeLeft == 0) {
|
|
//
|
|
//Here is the end of the current capsule image.
|
|
//
|
|
CapsuleBeginFlag = TRUE;
|
|
}
|
|
} else {
|
|
//
|
|
// The first entry is the block descriptor for EFI_CAPSULE_PEIM_PRIVATE_DATA.
|
|
// CapsuleOffset field is uninitialized at this time. No need copy it, but need to reserve for future use.
|
|
//
|
|
ASSERT (CurrentBlockDesc->Length == sizeof (EFI_CAPSULE_PEIM_PRIVATE_DATA));
|
|
ASSERT ((UINTN)DestPtr == (UINTN)NewCapsuleBase);
|
|
CopyMem ((VOID *) DestPtr, (VOID *) (UINTN) CurrentBlockDesc->Union.DataBlock, (UINTN) CurrentBlockDesc->Length);
|
|
DestPtr += sizeof (EFI_CAPSULE_PEIM_PRIVATE_DATA) + (CapsuleNumber - 1) * sizeof(UINT64);
|
|
}
|
|
//
|
|
//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) CapsuleSize;
|
|
*MemoryBase = (VOID *) NewCapsuleBase;
|
|
|
|
ASSERT (PrivateDataPtr->Signature == EFI_CAPSULE_PEIM_PRIVATE_DATA_SIGNATURE);
|
|
ASSERT (PrivateDataPtr->CapsuleAllImageSize == CapsuleImageSize);
|
|
ASSERT (PrivateDataPtr->CapsuleNumber == CapsuleIndex);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|