summaryrefslogtreecommitdiff
path: root/MdeModulePkg/Universal/CapsulePei/UefiCapsule.c
diff options
context:
space:
mode:
Diffstat (limited to 'MdeModulePkg/Universal/CapsulePei/UefiCapsule.c')
-rw-r--r--MdeModulePkg/Universal/CapsulePei/UefiCapsule.c1360
1 files changed, 528 insertions, 832 deletions
diff --git a/MdeModulePkg/Universal/CapsulePei/UefiCapsule.c b/MdeModulePkg/Universal/CapsulePei/UefiCapsule.c
index 7c10a3c568..5598755d46 100644
--- a/MdeModulePkg/Universal/CapsulePei/UefiCapsule.c
+++ b/MdeModulePkg/Universal/CapsulePei/UefiCapsule.c
@@ -16,136 +16,420 @@ 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
+//
+// Global Descriptor Table (GDT)
+//
+GLOBAL_REMOVE_IF_UNREFERENCED IA32_SEGMENT_DESCRIPTOR mGdtEntries[] = {
+/* selector { Global Segment Descriptor } */
+/* 0x00 */ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, //null descriptor
+/* 0x08 */ {{0xffff, 0, 0, 0x3, 1, 0, 1, 0xf, 0, 0, 1, 1, 0}}, //linear data segment descriptor
+/* 0x10 */ {{0xffff, 0, 0, 0xf, 1, 0, 1, 0xf, 0, 0, 1, 1, 0}}, //linear code segment descriptor
+/* 0x18 */ {{0xffff, 0, 0, 0x3, 1, 0, 1, 0xf, 0, 0, 1, 1, 0}}, //system data segment descriptor
+/* 0x20 */ {{0xffff, 0, 0, 0xb, 1, 0, 1, 0xf, 0, 0, 1, 1, 0}}, //system code segment descriptor
+/* 0x28 */ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, //spare segment descriptor
+/* 0x30 */ {{0xffff, 0, 0, 0x3, 1, 0, 1, 0xf, 0, 0, 1, 1, 0}}, //system data segment descriptor
+/* 0x38 */ {{0xffff, 0, 0, 0xb, 1, 0, 1, 0xf, 0, 1, 0, 1, 0}}, //system code segment descriptor
+/* 0x40 */ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, //spare segment descriptor
+};
- @retval FALSE Capsule is OK
- @retval TRUE Capsule is corrupted
+//
+// IA32 Gdt register
+//
+GLOBAL_REMOVE_IF_UNREFERENCED CONST IA32_DESCRIPTOR mGdt = {
+ sizeof (mGdtEntries) - 1,
+ (UINTN) mGdtEntries
+ };
+/**
+ Calculate the total size of page table.
+
+ @return The size of page table.
+
+
**/
-BOOLEAN
-IsCapsuleCorrupted (
- IN EFI_CAPSULE_HEADER *CapsuleHeader
+UINTN
+CalculatePageTableSize (
+ VOID
)
{
+ UINTN TotalPagesNum;
+ UINT8 PhysicalAddressBits;
+ VOID *Hob;
+ UINT32 NumberOfPml4EntriesNeeded;
+ UINT32 NumberOfPdpEntriesNeeded;
+
//
- //A capsule to be updated across a system reset should contain CAPSULE_FLAGS_PERSIST_ACROSS_RESET.
+ // Get physical address bits supported from CPU HOB.
//
- if ((CapsuleHeader->Flags & CAPSULE_FLAGS_PERSIST_ACROSS_RESET) == 0) {
- return TRUE;
+ PhysicalAddressBits = 36;
+
+ Hob = GetFirstHob (EFI_HOB_TYPE_CPU);
+ if (Hob != NULL) {
+ PhysicalAddressBits = ((EFI_HOB_CPU *) Hob)->SizeOfMemorySpace;
}
+
//
- //Make sure the flags combination is supported by the platform.
+ // IA-32e paging translates 48-bit linear addresses to 52-bit physical addresses.
//
- if ((CapsuleHeader->Flags & (CAPSULE_FLAGS_PERSIST_ACROSS_RESET | CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE)) == CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE) {
- return TRUE;
+ ASSERT (PhysicalAddressBits <= 52);
+ if (PhysicalAddressBits > 48) {
+ PhysicalAddressBits = 48;
}
- if ((CapsuleHeader->Flags & (CAPSULE_FLAGS_PERSIST_ACROSS_RESET | CAPSULE_FLAGS_INITIATE_RESET)) == CAPSULE_FLAGS_INITIATE_RESET) {
- return TRUE;
+
+ //
+ // Calculate the table entries needed.
+ //
+ if (PhysicalAddressBits <= 39 ) {
+ NumberOfPml4EntriesNeeded = 1;
+ NumberOfPdpEntriesNeeded = 1 << (PhysicalAddressBits - 30);
+ } else {
+ NumberOfPml4EntriesNeeded = 1 << (PhysicalAddressBits - 39);
+ NumberOfPdpEntriesNeeded = 512;
}
- return FALSE;
+ TotalPagesNum = (NumberOfPdpEntriesNeeded + 1) * NumberOfPml4EntriesNeeded + 1;
+
+ return EFI_PAGES_TO_SIZE (TotalPagesNum);
}
/**
- Check the integrity of the capsule descriptors.
-
- @param BlockList Pointer to the capsule descriptors
+ Allocates and fills in the Page Directory and Page Table Entries to
+ establish a 1:1 Virtual to Physical mapping.
- @retval NULL BlockList is not valid.
- @retval LastBlockDesc Last one Block in BlockList
+ @param[in] PageTablesAddress The base address of page table.
**/
-EFI_CAPSULE_BLOCK_DESCRIPTOR *
-ValidateCapsuleIntegrity (
- IN EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockList
+VOID
+CreateIdentityMappingPageTables (
+ IN EFI_PHYSICAL_ADDRESS PageTablesAddress
)
-{
- 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)) {
+{
+ UINT8 PhysicalAddressBits;
+ EFI_PHYSICAL_ADDRESS PageAddress;
+ UINTN IndexOfPml4Entries;
+ UINTN IndexOfPdpEntries;
+ UINTN IndexOfPageDirectoryEntries;
+ UINT32 NumberOfPml4EntriesNeeded;
+ UINT32 NumberOfPdpEntriesNeeded;
+ PAGE_MAP_AND_DIRECTORY_POINTER *PageMapLevel4Entry;
+ PAGE_MAP_AND_DIRECTORY_POINTER *PageMap;
+ PAGE_MAP_AND_DIRECTORY_POINTER *PageDirectoryPointerEntry;
+ PAGE_TABLE_ENTRY *PageDirectoryEntry;
+ UINTN BigPageAddress;
+ VOID *Hob;
+
+ //
+ // Get physical address bits supported from CPU HOB.
+ //
+ PhysicalAddressBits = 36;
+
+ Hob = GetFirstHob (EFI_HOB_TYPE_CPU);
+ if (Hob != NULL) {
+ PhysicalAddressBits = ((EFI_HOB_CPU *) Hob)->SizeOfMemorySpace;
+ }
+
+ //
+ // IA-32e paging translates 48-bit linear addresses to 52-bit physical addresses.
+ //
+ ASSERT (PhysicalAddressBits <= 52);
+ if (PhysicalAddressBits > 48) {
+ PhysicalAddressBits = 48;
+ }
+
+ //
+ // Calculate the table entries needed.
+ //
+ if (PhysicalAddressBits <= 39 ) {
+ NumberOfPml4EntriesNeeded = 1;
+ NumberOfPdpEntriesNeeded = 1 << (PhysicalAddressBits - 30);
+ } else {
+ NumberOfPml4EntriesNeeded = 1 << (PhysicalAddressBits - 39);
+ NumberOfPdpEntriesNeeded = 512;
+ }
+
+ //
+ // Pre-allocate big pages to avoid later allocations.
+ //
+ BigPageAddress = (UINTN) PageTablesAddress;
+
+ //
+ // By architecture only one PageMapLevel4 exists - so lets allocate storage for it.
+ //
+ PageMap = (VOID *) BigPageAddress;
+ BigPageAddress += EFI_PAGE_SIZE;
+
+ PageMapLevel4Entry = PageMap;
+ PageAddress = 0;
+ for (IndexOfPml4Entries = 0; IndexOfPml4Entries < NumberOfPml4EntriesNeeded; IndexOfPml4Entries++, PageMapLevel4Entry++) {
//
- // Make sure the descriptor is aligned at UINT64 in memory
+ // Each PML4 entry points to a page of Page Directory Pointer entires.
+ // So lets allocate space for them and fill them in in the IndexOfPdpEntries loop.
//
- if ((UINTN) Ptr & 0x07) {
- DEBUG ((EFI_D_ERROR, "BlockList address failed alignment check\n"));
- return NULL;
- }
+ PageDirectoryPointerEntry = (VOID *) BigPageAddress;
+ BigPageAddress += EFI_PAGE_SIZE;
- if (Ptr->Length == 0) {
- //
- // Descriptor points to another list of block descriptors somewhere
- // else.
+ //
+ // Make a PML4 Entry
+ //
+ PageMapLevel4Entry->Uint64 = (UINT64)(UINTN)PageDirectoryPointerEntry;
+ PageMapLevel4Entry->Bits.ReadWrite = 1;
+ PageMapLevel4Entry->Bits.Present = 1;
+
+ for (IndexOfPdpEntries = 0; IndexOfPdpEntries < NumberOfPdpEntriesNeeded; IndexOfPdpEntries++, PageDirectoryPointerEntry++) {
//
- Ptr = (EFI_CAPSULE_BLOCK_DESCRIPTOR *) (UINTN) Ptr->Union.ContinuationPointer;
- } else {
+ // Each Directory Pointer entries points to a page of Page Directory entires.
+ // So allocate space for them and fill them in in the IndexOfPageDirectoryEntries loop.
+ //
+ PageDirectoryEntry = (VOID *) BigPageAddress;
+ BigPageAddress += EFI_PAGE_SIZE;
+
//
- //To enhance the reliability of check-up, the first capsule's header is checked here.
- //More reliabilities check-up will do later.
+ // Fill in a Page Directory Pointer Entries
//
- if (CapsuleSize == 0) {
+ PageDirectoryPointerEntry->Uint64 = (UINT64)(UINTN)PageDirectoryEntry;
+ PageDirectoryPointerEntry->Bits.ReadWrite = 1;
+ PageDirectoryPointerEntry->Bits.Present = 1;
+
+ for (IndexOfPageDirectoryEntries = 0; IndexOfPageDirectoryEntries < 512; IndexOfPageDirectoryEntries++, PageDirectoryEntry++, PageAddress += 0x200000) {
//
- //Move to the first capsule to check its header.
+ // Fill in the Page Directory entries
//
- 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;
- }
+ PageDirectoryEntry->Uint64 = (UINT64)PageAddress;
+ PageDirectoryEntry->Bits.ReadWrite = 1;
+ PageDirectoryEntry->Bits.Present = 1;
+ PageDirectoryEntry->Bits.MustBe1 = 1;
+
}
- //
- // Move to next BLOCK descriptor
- //
- Ptr++;
}
}
- if (CapsuleCount == 0) {
+ //
+ // For the PML4 entries we are not using fill in a null entry.
+ // For now we just copy the first entry.
+ //
+ for (; IndexOfPml4Entries < 512; IndexOfPml4Entries++, PageMapLevel4Entry++) {
+ CopyMem (
+ PageMapLevel4Entry,
+ PageMap,
+ sizeof (PAGE_MAP_AND_DIRECTORY_POINTER)
+ );
+ }
+}
+
+/**
+ Return function from long mode to 32-bit mode.
+
+ @param EntrypointContext Context for mode switching
+ @param ReturnContext Context for mode switching
+
+**/
+VOID
+ReturnFunction (
+ SWITCH_32_TO_64_CONTEXT *EntrypointContext,
+ SWITCH_64_TO_32_CONTEXT *ReturnContext
+ )
+{
+ //
+ // Restore original GDT
+ //
+ AsmWriteGdtr (&ReturnContext->Gdtr);
+
+ //
+ // return to original caller
+ //
+ LongJump ((BASE_LIBRARY_JUMP_BUFFER *)(UINTN)EntrypointContext->JumpBuffer, 1);
+
+ //
+ // never be here
+ //
+ ASSERT (FALSE);
+}
+
+/**
+ Thunk function from 32-bit protection mode to long mode.
+
+ @param PageTableAddress Page table base address
+ @param Context Context for mode switching
+ @param ReturnContext Context for mode switching
+
+ @retval EFI_SUCCESS Function successfully executed.
+
+**/
+EFI_STATUS
+Thunk32To64 (
+ EFI_PHYSICAL_ADDRESS PageTableAddress,
+ SWITCH_32_TO_64_CONTEXT *Context,
+ SWITCH_64_TO_32_CONTEXT *ReturnContext
+ )
+{
+ UINTN SetJumpFlag;
+ EFI_STATUS Status;
+
+ //
+ // Save return address, LongJump will return here then
+ //
+ SetJumpFlag = SetJump ((BASE_LIBRARY_JUMP_BUFFER *) (UINTN) Context->JumpBuffer);
+
+ if (SetJumpFlag == 0) {
+
//
- // No any capsule is found in BlockList.
+ // Build Page Tables for all physical memory processor supports
//
- return NULL;
- }
+ CreateIdentityMappingPageTables (PageTableAddress);
+
+ //
+ // Create 64-bit GDT
+ //
+ AsmWriteGdtr (&mGdt);
+
+ //
+ // Write CR3
+ //
+ AsmWriteCr3 ((UINTN) PageTableAddress);
- return Ptr;
+ //
+ // Transfer to long mode
+ //
+ AsmEnablePaging64 (
+ 0x38,
+ (UINT64) Context->EntryPoint,
+ (UINT64)(UINTN) Context,
+ (UINT64)(UINTN) ReturnContext,
+ Context->StackBufferBase + Context->StackBufferLength
+ );
+ }
+
+ //
+ // Convert to 32-bit Status and return
+ //
+ Status = EFI_SUCCESS;
+ if ((UINTN) ReturnContext->ReturnStatus != 0) {
+ Status = ENCODE_ERROR ((UINTN) ReturnContext->ReturnStatus);
+ }
+
+ return Status;
}
+/**
+ If in 32 bit protection mode, and coalesce image is of X64, switch to long mode.
+
+ @param LongModeBuffer The context of long mode.
+ @param CoalesceEntry Entry of coalesce image.
+ @param BlockListAddr Address of block list.
+ @param MemoryBase Base of memory range.
+ @param MemorySize Size of memory range.
+
+ @retval EFI_SUCCESS Successfully switched to long mode and execute coalesce.
+ @retval Others Failed to execute coalesce in long mode.
+
+**/
+EFI_STATUS
+ModeSwitch (
+ IN EFI_CAPSULE_LONG_MODE_BUFFER *LongModeBuffer,
+ IN COALESCE_ENTRY CoalesceEntry,
+ IN EFI_PHYSICAL_ADDRESS BlockListAddr,
+ IN OUT VOID **MemoryBase,
+ IN OUT UINTN *MemorySize
+ )
+{
+ EFI_STATUS Status;
+ EFI_PHYSICAL_ADDRESS MemoryBase64;
+ UINT64 MemorySize64;
+ EFI_PHYSICAL_ADDRESS MemoryEnd64;
+ SWITCH_32_TO_64_CONTEXT Context;
+ SWITCH_64_TO_32_CONTEXT ReturnContext;
+ BASE_LIBRARY_JUMP_BUFFER JumpBuffer;
+ EFI_PHYSICAL_ADDRESS ReservedRangeBase;
+ EFI_PHYSICAL_ADDRESS ReservedRangeEnd;
+
+ ZeroMem (&Context, sizeof (SWITCH_32_TO_64_CONTEXT));
+ ZeroMem (&ReturnContext, sizeof (SWITCH_64_TO_32_CONTEXT));
+
+ MemoryBase64 = (UINT64) (UINTN) *MemoryBase;
+ MemorySize64 = (UINT64) (UINTN) *MemorySize;
+ MemoryEnd64 = MemoryBase64 + MemorySize64;
+
+ //
+ // Merge memory range reserved for stack and page table
+ //
+ if (LongModeBuffer->StackBaseAddress < LongModeBuffer->PageTableAddress) {
+ ReservedRangeBase = LongModeBuffer->StackBaseAddress;
+ ReservedRangeEnd = LongModeBuffer->PageTableAddress + CalculatePageTableSize ();
+ } else {
+ ReservedRangeBase = LongModeBuffer->PageTableAddress;
+ ReservedRangeEnd = LongModeBuffer->StackBaseAddress + LongModeBuffer->StackSize;
+ }
+
+ //
+ // Check if memory range reserved is overlap with MemoryBase ~ MemoryBase + MemorySize.
+ // If they are overlapped, get a larger range to process capsule data.
+ //
+ if (ReservedRangeBase <= MemoryBase64) {
+ if (ReservedRangeEnd < MemoryEnd64) {
+ MemoryBase64 = ReservedRangeEnd;
+ } else {
+ DEBUG ((EFI_D_ERROR, "Memory is not enough to process capsule!\n"));
+ return EFI_OUT_OF_RESOURCES;
+ }
+ } else if (ReservedRangeBase < MemoryEnd64) {
+ if (ReservedRangeEnd < MemoryEnd64 &&
+ ReservedRangeBase - MemoryBase64 < MemoryEnd64 - ReservedRangeEnd) {
+ MemoryBase64 = ReservedRangeEnd;
+ } else {
+ MemorySize64 = (UINT64)(UINTN)(ReservedRangeBase - MemoryBase64);
+ }
+ }
+
+ //
+ // Initialize context jumping to 64-bit enviroment
+ //
+ Context.JumpBuffer = (EFI_PHYSICAL_ADDRESS)(UINTN)&JumpBuffer;
+ Context.StackBufferBase = LongModeBuffer->StackBaseAddress;
+ Context.StackBufferLength = LongModeBuffer->StackSize;
+ Context.EntryPoint = (EFI_PHYSICAL_ADDRESS)(UINTN)CoalesceEntry;
+ Context.BlockListAddr = BlockListAddr;
+ Context.MemoryBase64Ptr = (EFI_PHYSICAL_ADDRESS)(UINTN)&MemoryBase64;
+ Context.MemorySize64Ptr = (EFI_PHYSICAL_ADDRESS)(UINTN)&MemorySize64;
+
+ //
+ // Prepare data for return back
+ //
+ ReturnContext.ReturnCs = 0x10;
+ ReturnContext.ReturnEntryPoint = (EFI_PHYSICAL_ADDRESS)(UINTN)ReturnFunction;
+ //
+ // Will save the return status of processing capsule
+ //
+ ReturnContext.ReturnStatus = 0;
+
+ //
+ // Save original GDT
+ //
+ AsmReadGdtr ((IA32_DESCRIPTOR *)&ReturnContext.Gdtr);
+
+ Status = Thunk32To64 (LongModeBuffer->PageTableAddress, &Context, &ReturnContext);
+
+ if (!EFI_ERROR (Status)) {
+ *MemoryBase = (VOID *) (UINTN) MemoryBase64;
+ *MemorySize = (UINTN) MemorySize64;
+ }
+
+ return Status;
+
+}
/**
Checks for the presence of capsule descriptors.
Get capsule descriptors from variable CapsuleUpdateData, CapsuleUpdateData1, CapsuleUpdateData2...
+ and save to DescriptorBuffer.
- @param BlockList Pointer to the capsule descriptors
+ @param DescriptorBuffer 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
+ IN EFI_PHYSICAL_ADDRESS *DescriptorBuffer
)
{
EFI_STATUS Status;
@@ -157,14 +441,8 @@ GetCapsuleDescriptors (
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;
@@ -204,20 +482,9 @@ GetCapsuleDescriptors (
// pointer is null. In that case, return success since we know that the
// variable is set.
//
- if (BlockList == NULL) {
+ if (DescriptorBuffer == 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 (
@@ -237,7 +504,7 @@ GetCapsuleDescriptors (
//
Flag = FALSE;
for (TempIndex = 0; TempIndex < ValidIndex; TempIndex++) {
- if (mBufferAddress[TempIndex] == CapsuleDataPtr64) {
+ if (DescriptorBuffer[TempIndex] == CapsuleDataPtr64) {
Flag = TRUE;
break;
}
@@ -246,500 +513,122 @@ GetCapsuleDescriptors (
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;
+ DescriptorBuffer[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
+ Locates the coalesce image entry point, and detects its machine type.
- @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;
- }
+ @param CoalesceImageEntryPoint Pointer to coalesce image entry point for output.
+ @param CoalesceImageMachineType Pointer to machine type of coalesce image.
- return TRUE;
-}
+ @retval EFI_SUCCESS Coalesce image successfully located.
+ @retval Others Failed to locate the coalesce image.
-/**
- 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
+EFI_STATUS
+FindCapsuleCoalesceImage (
+ OUT EFI_PHYSICAL_ADDRESS *CoalesceImageEntryPoint,
+ OUT UINT16 *CoalesceImageMachineType
)
{
- 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++;
- }
+ EFI_STATUS Status;
+ UINTN Instance;
+ EFI_PEI_LOAD_FILE_PPI *LoadFile;
+ EFI_PEI_FV_HANDLE VolumeHandle;
+ EFI_PEI_FILE_HANDLE FileHandle;
+ EFI_PHYSICAL_ADDRESS CoalesceImageAddress;
+ UINT64 CoalesceImageSize;
+ UINT32 AuthenticationState;
- 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;
- }
+ Instance = 0;
- Failed = TRUE;
+ while (TRUE) {
+ Status = PeiServicesFfsFindNextVolume (Instance++, &VolumeHandle);
+ if (EFI_ERROR (Status)) {
+ return Status;
}
- //
- // 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;
- }
+ Status = PeiServicesFfsFindFileByName (PcdGetPtr(PcdCapsuleCoalesceFile), VolumeHandle, &FileHandle);
+ if (!EFI_ERROR (Status)) {
+ Status = PeiServicesLocatePpi (&gEfiPeiLoadFilePpiGuid, 0, NULL, (VOID **) &LoadFile);
+ ASSERT_EFI_ERROR (Status);
+
+ Status = LoadFile->LoadFile (
+ LoadFile,
+ FileHandle,
+ &CoalesceImageAddress,
+ &CoalesceImageSize,
+ CoalesceImageEntryPoint,
+ &AuthenticationState
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "Unable to find PE32 section in CapsuleRelocate image ffs %r!\n", Status));
+ return Status;
}
- CurrDesc++;
- }
- //
- // Normal continuation -- jump to next block descriptor list
- //
- if (!Failed) {
- CurrDesc = (EFI_CAPSULE_BLOCK_DESCRIPTOR *) (UINTN) CurrDesc->Union.ContinuationPointer;
+ *CoalesceImageMachineType = PeCoffLoaderGetMachineType ((VOID *) (UINTN) CoalesceImageAddress);
+ break;
+ } else {
+ continue;
}
}
- return MemBase;
+
+ return Status;
}
/**
- 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.
+ Gets the reserved long mode buffer.
- 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 LongModeBuffer Pointer to the long mode buffer for output.
- @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 EFI_SUCCESS Long mode buffer successfully retrieved.
+ @retval Others Variable storing long mode buffer not found.
- @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_STATUS
+GetLongModeContext (
+ OUT EFI_CAPSULE_LONG_MODE_BUFFER *LongModeBuffer
)
{
- 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++;
- }
+ EFI_STATUS Status;
+ UINTN Size;
+ EFI_PEI_READ_ONLY_VARIABLE2_PPI *PPIVariableServices;
- 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;
- }
+ Status = PeiServicesLocatePpi (
+ &gEfiPeiReadOnlyVariable2PpiGuid,
+ 0,
+ NULL,
+ (VOID **) &PPIVariableServices
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ Size = sizeof (EFI_CAPSULE_LONG_MODE_BUFFER);
+ Status = PPIVariableServices->GetVariable (
+ PPIVariableServices,
+ EFI_CAPSULE_LONG_MODE_BUFFER_NAME,
+ &gEfiCapsuleVendorGuid,
+ NULL,
+ &Size,
+ LongModeBuffer
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG (( EFI_D_ERROR, "Error Get LongModeBuffer variable %r!\n", Status));
}
- //
- // Null terminate
- //
- CurrBlockDescHead->Union.ContinuationPointer = (EFI_PHYSICAL_ADDRESS) (UINTN) NULL;
- CurrBlockDescHead->Length = 0;
- return NewBlockList;
+ return Status;
}
/**
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.
@@ -768,54 +657,26 @@ CapsuleCoalesce (
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;
+ UINTN Index;
+ UINTN Size;
+ UINTN VariableCount;
+ CHAR16 CapsuleVarName[30];
+ CHAR16 *TempVarName;
+ EFI_PHYSICAL_ADDRESS CapsuleDataPtr64;
+ EFI_STATUS Status;
+ EFI_BOOT_MODE BootMode;
+ EFI_PEI_READ_ONLY_VARIABLE2_PPI *PPIVariableServices;
+ EFI_PHYSICAL_ADDRESS *VariableArrayAddress;
+#ifdef MDE_CPU_IA32
+ UINT16 CoalesceImageMachineType;
+ EFI_PHYSICAL_ADDRESS CoalesceImageEntryPoint;
+ COALESCE_ENTRY CoalesceEntry;
+ EFI_CAPSULE_LONG_MODE_BUFFER LongModeBuffer;
+#endif
+
+ Index = 0;
+ VariableCount = 0;
+ CapsuleVarName[0] = 0;
//
// Someone should have already ascertained the boot mode. If it's not
@@ -823,7 +684,9 @@ CapsuleCoalesce (
//
Status = PeiServicesGetBootMode (&BootMode);
if (EFI_ERROR (Status) || (BootMode != BOOT_ON_FLASH_UPDATE)) {
- return EFI_NOT_FOUND;
+ DEBUG ((EFI_D_ERROR, "Boot mode is not correct for capsule update path.\n"));
+ Status = EFI_NOT_FOUND;
+ goto Done;
}
//
@@ -837,7 +700,7 @@ CapsuleCoalesce (
(VOID **) &PPIVariableServices
);
if (EFI_ERROR (Status)) {
- return Status;
+ goto Done;
}
Size = sizeof (CapsuleDataPtr64);
StrCpy (CapsuleVarName, EFI_CAPSULE_VARIABLE_NAME);
@@ -858,264 +721,94 @@ CapsuleCoalesce (
//
// There is no capsule variables, quit
//
- DEBUG ((EFI_D_ERROR,"Capsule variable Index = %d\n", Index));
+ DEBUG ((EFI_D_INFO,"Capsule variable Index = %d\n", Index));
break;
}
VariableCount++;
Index++;
}
- DEBUG ((EFI_D_ERROR,"Capsule variable count = %d\n", VariableCount));
+ DEBUG ((EFI_D_INFO,"Capsule variable count = %d\n", VariableCount));
+ //
+ // The last entry is the end flag.
+ //
Status = PeiServicesAllocatePool (
- VariableCount * sizeof (EFI_PHYSICAL_ADDRESS),
- (VOID **)&mBufferAddress
+ (VariableCount + 1) * sizeof (EFI_PHYSICAL_ADDRESS),
+ (VOID **)&VariableArrayAddress
);
if (Status != EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR, "AllocatePages Failed!, Status = %x\n", Status));
- return Status;
+ goto Done;
}
+ ZeroMem (VariableArrayAddress, (VariableCount + 1) * sizeof (EFI_PHYSICAL_ADDRESS));
+
//
// Find out if we actually have a capsule.
+ // GetCapsuleDescriptors depends on variable PPI, so it should run in 32-bit environment.
//
- Status = GetCapsuleDescriptors (&BlockList);
+ Status = GetCapsuleDescriptors (VariableArrayAddress);
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;
+ DEBUG ((EFI_D_ERROR, "Fail to find capsule variables.\n"));
+ goto Done;
}
- 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) {
+#ifdef MDE_CPU_IA32
+ if (FeaturePcdGet (PcdDxeIplSwitchToLongMode)) {
//
- // Not enough room to relocate the descriptors
+ // Switch to 64-bit mode to process capsule data when:
+ // 1. When DXE phase is 64-bit
+ // 2. When the buffer for 64-bit transition exists
+ // 3. When Capsule X64 image is built in BIOS image
+ // In 64-bit mode, we can process capsule data above 4GB.
//
- 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++;
+ CoalesceImageEntryPoint = 0;
+ Status = GetLongModeContext (&LongModeBuffer);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "Fail to find the variables for long mode context!\n"));
+ Status = EFI_NOT_FOUND;
+ goto Done;
}
- //
- // 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;
+
+ Status = FindCapsuleCoalesceImage (&CoalesceImageEntryPoint, &CoalesceImageMachineType);
+ if ((EFI_ERROR (Status)) || (CoalesceImageMachineType != EFI_IMAGE_MACHINE_X64)) {
+ DEBUG ((EFI_D_ERROR, "Fail to find CapsuleX64 module in FV!\n"));
+ Status = EFI_NOT_FOUND;
+ goto Done;
}
+ ASSERT (CoalesceImageEntryPoint != 0);
+ CoalesceEntry = (COALESCE_ENTRY) (UINTN) CoalesceImageEntryPoint;
+ Status = ModeSwitch (&LongModeBuffer, CoalesceEntry, (EFI_PHYSICAL_ADDRESS)(UINTN)VariableArrayAddress, MemoryBase, MemorySize);
+ } else {
//
- //Walk through the block descriptor list.
+ // Capsule is processed in IA32 mode.
//
- CurrentBlockDesc++;
+ Status = CapsuleDataCoalesce (PeiServices, (EFI_PHYSICAL_ADDRESS *)(UINTN)VariableArrayAddress, MemoryBase, MemorySize);
}
+#else
//
- // We return the base of memory we want reserved, and the size.
- // The memory peim should handle it appropriately from there.
+ // Process capsule directly.
//
- *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);
+ Status = CapsuleDataCoalesce (PeiServices, (EFI_PHYSICAL_ADDRESS *)(UINTN)VariableArrayAddress, MemoryBase, MemorySize);
+#endif
+
+ DEBUG ((EFI_D_INFO, "Capsule Coalesce Status = %r!\n", Status));
- PrivateDataPtr = (EFI_CAPSULE_PEIM_PRIVATE_DATA *) NewCapsuleBase;
- PrivateDataPtr->CapsuleSize = (UINTN)CapsuleImageSize;
+ if (Status == EFI_BUFFER_TOO_SMALL) {
+ DEBUG ((EFI_D_ERROR, "There is not enough memory to process capsule!\n"));
+ }
+
+ if (Status == EFI_NOT_FOUND) {
+ DEBUG ((EFI_D_ERROR, "Fail to parse capsule descriptor in memory!\n"));
+ REPORT_STATUS_CODE (
+ EFI_ERROR_CODE | EFI_ERROR_MAJOR,
+ (EFI_SOFTWARE_PEI_MODULE | EFI_SW_PEI_EC_INVALID_CAPSULE_DESCRIPTOR)
+ );
+ }
+Done:
return Status;
}
@@ -1168,7 +861,10 @@ CapsuleTestPattern (
// is, then test it now.
//
TestPtr = (UINT32 *) CapsuleBase;
- if (*TestPtr == CAPSULE_TEST_SIGNATURE) {
+ //
+ // 0x54534554 "TEST"
+ //
+ if (*TestPtr == 0x54534554) {
RetValue = TRUE;
DEBUG ((EFI_D_INFO, "Capsule test pattern mode activated...\n"));
TestSize = TestPtr[1] / sizeof (UINT32);