aboutsummaryrefslogtreecommitdiffstats
path: root/roms/edk2/MdeModulePkg/Universal/CapsulePei/UefiCapsule.c
diff options
context:
space:
mode:
Diffstat (limited to 'roms/edk2/MdeModulePkg/Universal/CapsulePei/UefiCapsule.c')
-rw-r--r--roms/edk2/MdeModulePkg/Universal/CapsulePei/UefiCapsule.c1332
1 files changed, 1332 insertions, 0 deletions
diff --git a/roms/edk2/MdeModulePkg/Universal/CapsulePei/UefiCapsule.c b/roms/edk2/MdeModulePkg/Universal/CapsulePei/UefiCapsule.c
new file mode 100644
index 000000000..51afab7b0
--- /dev/null
+++ b/roms/edk2/MdeModulePkg/Universal/CapsulePei/UefiCapsule.c
@@ -0,0 +1,1332 @@
+/** @file
+ Capsule update PEIM for UEFI2.0
+
+Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.<BR>
+Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR>
+
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "Capsule.h"
+
+#define DEFAULT_SG_LIST_HEADS (20)
+
+#ifdef MDE_CPU_IA32
+//
+// 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
+};
+
+//
+// IA32 Gdt register
+//
+GLOBAL_REMOVE_IF_UNREFERENCED CONST IA32_DESCRIPTOR mGdt = {
+ sizeof (mGdtEntries) - 1,
+ (UINTN) mGdtEntries
+ };
+
+
+/**
+ The function will check if 1G page is supported.
+
+ @retval TRUE 1G page is supported.
+ @retval FALSE 1G page is not supported.
+
+**/
+BOOLEAN
+IsPage1GSupport (
+ VOID
+ )
+{
+ UINT32 RegEax;
+ UINT32 RegEdx;
+ BOOLEAN Page1GSupport;
+
+ Page1GSupport = FALSE;
+ if (PcdGetBool(PcdUse1GPageTable)) {
+ AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
+ if (RegEax >= 0x80000001) {
+ AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx);
+ if ((RegEdx & BIT26) != 0) {
+ Page1GSupport = TRUE;
+ }
+ }
+ }
+
+ return Page1GSupport;
+}
+
+/**
+ Calculate the total size of page table.
+
+ @param[in] Page1GSupport 1G page support or not.
+
+ @return The size of page table.
+
+**/
+UINTN
+CalculatePageTableSize (
+ IN BOOLEAN Page1GSupport
+ )
+{
+ UINTN ExtraPageTablePages;
+ UINTN TotalPagesNum;
+ UINT8 PhysicalAddressBits;
+ UINT32 NumberOfPml4EntriesNeeded;
+ UINT32 NumberOfPdpEntriesNeeded;
+
+ //
+ // Create 4G page table by default,
+ // and let PF handler to handle > 4G request.
+ //
+ PhysicalAddressBits = 32;
+ ExtraPageTablePages = EXTRA_PAGE_TABLE_PAGES;
+
+ //
+ // Calculate the table entries needed.
+ //
+ if (PhysicalAddressBits <= 39 ) {
+ NumberOfPml4EntriesNeeded = 1;
+ NumberOfPdpEntriesNeeded = (UINT32)LShiftU64 (1, (PhysicalAddressBits - 30));
+ } else {
+ NumberOfPml4EntriesNeeded = (UINT32)LShiftU64 (1, (PhysicalAddressBits - 39));
+ NumberOfPdpEntriesNeeded = 512;
+ }
+
+ if (!Page1GSupport) {
+ TotalPagesNum = (NumberOfPdpEntriesNeeded + 1) * NumberOfPml4EntriesNeeded + 1;
+ } else {
+ TotalPagesNum = NumberOfPml4EntriesNeeded + 1;
+ }
+ TotalPagesNum += ExtraPageTablePages;
+
+ return EFI_PAGES_TO_SIZE (TotalPagesNum);
+}
+
+/**
+ Allocates and fills in the Page Directory and Page Table Entries to
+ establish a 4G page table.
+
+ @param[in] PageTablesAddress The base address of page table.
+ @param[in] Page1GSupport 1G page support or not.
+
+**/
+VOID
+Create4GPageTables (
+ IN EFI_PHYSICAL_ADDRESS PageTablesAddress,
+ IN BOOLEAN Page1GSupport
+ )
+{
+ 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;
+ PAGE_TABLE_1G_ENTRY *PageDirectory1GEntry;
+ UINT64 AddressEncMask;
+
+ //
+ // Make sure AddressEncMask is contained to smallest supported address field.
+ //
+ AddressEncMask = PcdGet64 (PcdPteMemoryEncryptionAddressOrMask) & PAGING_1G_ADDRESS_MASK_64;
+
+ //
+ // Create 4G page table by default,
+ // and let PF handler to handle > 4G request.
+ //
+ PhysicalAddressBits = 32;
+
+ //
+ // Calculate the table entries needed.
+ //
+ if (PhysicalAddressBits <= 39 ) {
+ NumberOfPml4EntriesNeeded = 1;
+ NumberOfPdpEntriesNeeded = (UINT32)LShiftU64 (1, (PhysicalAddressBits - 30));
+ } else {
+ NumberOfPml4EntriesNeeded = (UINT32)LShiftU64 (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 += SIZE_4KB;
+
+ PageMapLevel4Entry = PageMap;
+ PageAddress = 0;
+ for (IndexOfPml4Entries = 0; IndexOfPml4Entries < NumberOfPml4EntriesNeeded; IndexOfPml4Entries++, PageMapLevel4Entry++) {
+ //
+ // 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.
+ //
+ PageDirectoryPointerEntry = (VOID *) BigPageAddress;
+ BigPageAddress += SIZE_4KB;
+
+ //
+ // Make a PML4 Entry
+ //
+ PageMapLevel4Entry->Uint64 = (UINT64)(UINTN)PageDirectoryPointerEntry | AddressEncMask;
+ PageMapLevel4Entry->Bits.ReadWrite = 1;
+ PageMapLevel4Entry->Bits.Present = 1;
+
+ if (Page1GSupport) {
+ PageDirectory1GEntry = (VOID *) PageDirectoryPointerEntry;
+
+ for (IndexOfPageDirectoryEntries = 0; IndexOfPageDirectoryEntries < 512; IndexOfPageDirectoryEntries++, PageDirectory1GEntry++, PageAddress += SIZE_1GB) {
+ //
+ // Fill in the Page Directory entries
+ //
+ PageDirectory1GEntry->Uint64 = (UINT64)PageAddress | AddressEncMask;
+ PageDirectory1GEntry->Bits.ReadWrite = 1;
+ PageDirectory1GEntry->Bits.Present = 1;
+ PageDirectory1GEntry->Bits.MustBe1 = 1;
+ }
+ } else {
+ for (IndexOfPdpEntries = 0; IndexOfPdpEntries < NumberOfPdpEntriesNeeded; IndexOfPdpEntries++, PageDirectoryPointerEntry++) {
+ //
+ // 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 += SIZE_4KB;
+
+ //
+ // Fill in a Page Directory Pointer Entries
+ //
+ PageDirectoryPointerEntry->Uint64 = (UINT64)(UINTN)PageDirectoryEntry | AddressEncMask;
+ PageDirectoryPointerEntry->Bits.ReadWrite = 1;
+ PageDirectoryPointerEntry->Bits.Present = 1;
+
+ for (IndexOfPageDirectoryEntries = 0; IndexOfPageDirectoryEntries < 512; IndexOfPageDirectoryEntries++, PageDirectoryEntry++, PageAddress += SIZE_2MB) {
+ //
+ // Fill in the Page Directory entries
+ //
+ PageDirectoryEntry->Uint64 = (UINT64)PageAddress | AddressEncMask;
+ PageDirectoryEntry->Bits.ReadWrite = 1;
+ PageDirectoryEntry->Bits.Present = 1;
+ PageDirectoryEntry->Bits.MustBe1 = 1;
+ }
+ }
+
+ for (; IndexOfPdpEntries < 512; IndexOfPdpEntries++, PageDirectoryPointerEntry++) {
+ ZeroMem (
+ PageDirectoryPointerEntry,
+ sizeof(PAGE_MAP_AND_DIRECTORY_POINTER)
+ );
+ }
+ }
+ }
+
+ //
+ // For the PML4 entries we are not using fill in a null entry.
+ //
+ for (; IndexOfPml4Entries < 512; IndexOfPml4Entries++, PageMapLevel4Entry++) {
+ ZeroMem (
+ PageMapLevel4Entry,
+ 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) {
+
+ //
+ // Build 4G Page Tables.
+ //
+ Create4GPageTables (PageTableAddress, Context->Page1GSupport);
+
+ //
+ // Create 64-bit GDT
+ //
+ AsmWriteGdtr (&mGdt);
+
+ //
+ // Write CR3
+ //
+ AsmWriteCr3 ((UINTN) PageTableAddress);
+
+ DEBUG ((
+ DEBUG_INFO,
+ "%a() Stack Base: 0x%lx, Stack Size: 0x%lx\n",
+ __FUNCTION__,
+ Context->StackBufferBase,
+ Context->StackBufferLength
+ ));
+
+ //
+ // Disable interrupt of Debug timer, since the IDT table cannot work in long mode
+ //
+ SaveAndSetDebugTimerInterrupt (FALSE);
+ //
+ // 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 MemoryResource Pointer to the buffer of memory resource descriptor.
+ @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 MEMORY_RESOURCE_DESCRIPTOR *MemoryResource,
+ 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;
+ BOOLEAN Page1GSupport;
+
+ 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;
+
+ Page1GSupport = IsPage1GSupport ();
+
+ //
+ // Merge memory range reserved for stack and page table
+ //
+ if (LongModeBuffer->StackBaseAddress < LongModeBuffer->PageTableAddress) {
+ ReservedRangeBase = LongModeBuffer->StackBaseAddress;
+ ReservedRangeEnd = LongModeBuffer->PageTableAddress + CalculatePageTableSize (Page1GSupport);
+ } 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 ((DEBUG_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.MemoryResource = (EFI_PHYSICAL_ADDRESS)(UINTN)MemoryResource;
+ Context.MemoryBase64Ptr = (EFI_PHYSICAL_ADDRESS)(UINTN)&MemoryBase64;
+ Context.MemorySize64Ptr = (EFI_PHYSICAL_ADDRESS)(UINTN)&MemorySize64;
+ Context.Page1GSupport = Page1GSupport;
+ Context.AddressEncMask = PcdGet64 (PcdPteMemoryEncryptionAddressOrMask) & PAGING_1G_ADDRESS_MASK_64;
+
+ //
+ // 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;
+
+}
+
+/**
+ Locates the coalesce image entry point, and detects its machine type.
+
+ @param CoalesceImageEntryPoint Pointer to coalesce image entry point for output.
+ @param CoalesceImageMachineType Pointer to machine type of coalesce image.
+
+ @retval EFI_SUCCESS Coalesce image successfully located.
+ @retval Others Failed to locate the coalesce image.
+
+**/
+EFI_STATUS
+FindCapsuleCoalesceImage (
+ OUT EFI_PHYSICAL_ADDRESS *CoalesceImageEntryPoint,
+ OUT UINT16 *CoalesceImageMachineType
+ )
+{
+ 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;
+
+ Instance = 0;
+
+ while (TRUE) {
+ Status = PeiServicesFfsFindNextVolume (Instance++, &VolumeHandle);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ 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 ((DEBUG_ERROR, "Unable to find PE32 section in CapsuleX64 image ffs %r!\n", Status));
+ return Status;
+ }
+ *CoalesceImageMachineType = PeCoffLoaderGetMachineType ((VOID *) (UINTN) CoalesceImageAddress);
+ break;
+ } else {
+ continue;
+ }
+ }
+
+ return Status;
+}
+
+/**
+ Gets the reserved long mode buffer.
+
+ @param LongModeBuffer Pointer to the long mode buffer for output.
+
+ @retval EFI_SUCCESS Long mode buffer successfully retrieved.
+ @retval Others Variable storing long mode buffer not found.
+
+**/
+EFI_STATUS
+GetLongModeContext (
+ OUT EFI_CAPSULE_LONG_MODE_BUFFER *LongModeBuffer
+ )
+{
+ EFI_STATUS Status;
+ UINTN Size;
+ EFI_PEI_READ_ONLY_VARIABLE2_PPI *PPIVariableServices;
+
+ 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 (( DEBUG_ERROR, "Error Get LongModeBuffer variable %r!\n", Status));
+ }
+ return Status;
+}
+#endif
+
+#if defined (MDE_CPU_IA32) || defined (MDE_CPU_X64)
+/**
+ Get physical address bits.
+
+ @return Physical address bits.
+
+**/
+UINT8
+GetPhysicalAddressBits (
+ VOID
+ )
+{
+ UINT32 RegEax;
+ UINT8 PhysicalAddressBits;
+ VOID *Hob;
+
+ //
+ // Get physical address bits supported.
+ //
+ Hob = GetFirstHob (EFI_HOB_TYPE_CPU);
+ if (Hob != NULL) {
+ PhysicalAddressBits = ((EFI_HOB_CPU *) Hob)->SizeOfMemorySpace;
+ } else {
+ AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
+ if (RegEax >= 0x80000008) {
+ AsmCpuid (0x80000008, &RegEax, NULL, NULL, NULL);
+ PhysicalAddressBits = (UINT8) RegEax;
+ } else {
+ PhysicalAddressBits = 36;
+ }
+ }
+
+ //
+ // IA-32e paging translates 48-bit linear addresses to 52-bit physical addresses.
+ //
+ ASSERT (PhysicalAddressBits <= 52);
+ if (PhysicalAddressBits > 48) {
+ PhysicalAddressBits = 48;
+ }
+
+ return PhysicalAddressBits;
+}
+#endif
+
+/**
+ Sort memory resource entries based upon PhysicalStart, from low to high.
+
+ @param[in, out] MemoryResource A pointer to the memory resource entry buffer.
+
+**/
+VOID
+SortMemoryResourceDescriptor (
+ IN OUT MEMORY_RESOURCE_DESCRIPTOR *MemoryResource
+ )
+{
+ MEMORY_RESOURCE_DESCRIPTOR *MemoryResourceEntry;
+ MEMORY_RESOURCE_DESCRIPTOR *NextMemoryResourceEntry;
+ MEMORY_RESOURCE_DESCRIPTOR TempMemoryResource;
+
+ MemoryResourceEntry = MemoryResource;
+ NextMemoryResourceEntry = MemoryResource + 1;
+ while (MemoryResourceEntry->ResourceLength != 0) {
+ while (NextMemoryResourceEntry->ResourceLength != 0) {
+ if (MemoryResourceEntry->PhysicalStart > NextMemoryResourceEntry->PhysicalStart) {
+ CopyMem (&TempMemoryResource, MemoryResourceEntry, sizeof (MEMORY_RESOURCE_DESCRIPTOR));
+ CopyMem (MemoryResourceEntry, NextMemoryResourceEntry, sizeof (MEMORY_RESOURCE_DESCRIPTOR));
+ CopyMem (NextMemoryResourceEntry, &TempMemoryResource, sizeof (MEMORY_RESOURCE_DESCRIPTOR));
+ }
+
+ NextMemoryResourceEntry = NextMemoryResourceEntry + 1;
+ }
+
+ MemoryResourceEntry = MemoryResourceEntry + 1;
+ NextMemoryResourceEntry = MemoryResourceEntry + 1;
+ }
+}
+
+/**
+ Merge continous memory resource entries.
+
+ @param[in, out] MemoryResource A pointer to the memory resource entry buffer.
+
+**/
+VOID
+MergeMemoryResourceDescriptor (
+ IN OUT MEMORY_RESOURCE_DESCRIPTOR *MemoryResource
+ )
+{
+ MEMORY_RESOURCE_DESCRIPTOR *MemoryResourceEntry;
+ MEMORY_RESOURCE_DESCRIPTOR *NewMemoryResourceEntry;
+ MEMORY_RESOURCE_DESCRIPTOR *NextMemoryResourceEntry;
+ MEMORY_RESOURCE_DESCRIPTOR *MemoryResourceEnd;
+
+ MemoryResourceEntry = MemoryResource;
+ NewMemoryResourceEntry = MemoryResource;
+ while (MemoryResourceEntry->ResourceLength != 0) {
+ CopyMem (NewMemoryResourceEntry, MemoryResourceEntry, sizeof (MEMORY_RESOURCE_DESCRIPTOR));
+ NextMemoryResourceEntry = MemoryResourceEntry + 1;
+
+ while ((NextMemoryResourceEntry->ResourceLength != 0) &&
+ (NextMemoryResourceEntry->PhysicalStart == (MemoryResourceEntry->PhysicalStart + MemoryResourceEntry->ResourceLength))) {
+ MemoryResourceEntry->ResourceLength += NextMemoryResourceEntry->ResourceLength;
+ if (NewMemoryResourceEntry != MemoryResourceEntry) {
+ NewMemoryResourceEntry->ResourceLength += NextMemoryResourceEntry->ResourceLength;
+ }
+
+ NextMemoryResourceEntry = NextMemoryResourceEntry + 1;
+ }
+
+ MemoryResourceEntry = NextMemoryResourceEntry;
+ NewMemoryResourceEntry = NewMemoryResourceEntry + 1;
+ }
+
+ //
+ // Set NULL terminate memory resource descriptor after merging.
+ //
+ MemoryResourceEnd = NewMemoryResourceEntry;
+ ZeroMem (MemoryResourceEnd, sizeof (MEMORY_RESOURCE_DESCRIPTOR));
+}
+
+/**
+ Build memory resource descriptor from resource descriptor in HOB list.
+
+ @return Pointer to the buffer of memory resource descriptor.
+ NULL if no memory resource descriptor reported in HOB list
+ before capsule Coalesce.
+
+**/
+MEMORY_RESOURCE_DESCRIPTOR *
+BuildMemoryResourceDescriptor (
+ VOID
+ )
+{
+ EFI_PEI_HOB_POINTERS Hob;
+ UINTN Index;
+ EFI_HOB_RESOURCE_DESCRIPTOR *ResourceDescriptor;
+ MEMORY_RESOURCE_DESCRIPTOR *MemoryResource;
+ EFI_STATUS Status;
+
+ //
+ // Get the count of memory resource descriptor.
+ //
+ Index = 0;
+ Hob.Raw = GetFirstHob (EFI_HOB_TYPE_RESOURCE_DESCRIPTOR);
+ while (Hob.Raw != NULL) {
+ ResourceDescriptor = (EFI_HOB_RESOURCE_DESCRIPTOR *) Hob.Raw;
+ if (ResourceDescriptor->ResourceType == EFI_RESOURCE_SYSTEM_MEMORY) {
+ Index++;
+ }
+ Hob.Raw = GET_NEXT_HOB (Hob);
+ Hob.Raw = GetNextHob (EFI_HOB_TYPE_RESOURCE_DESCRIPTOR, Hob.Raw);
+ }
+
+ if (Index == 0) {
+ DEBUG ((DEBUG_INFO | DEBUG_WARN, "No memory resource descriptor reported in HOB list before capsule Coalesce\n"));
+#if defined (MDE_CPU_IA32) || defined (MDE_CPU_X64)
+ //
+ // Allocate memory to hold memory resource descriptor,
+ // include extra one NULL terminate memory resource descriptor.
+ //
+ Status = PeiServicesAllocatePool ((1 + 1) * sizeof (MEMORY_RESOURCE_DESCRIPTOR), (VOID **) &MemoryResource);
+ ASSERT_EFI_ERROR (Status);
+ ZeroMem (MemoryResource, (1 + 1) * sizeof (MEMORY_RESOURCE_DESCRIPTOR));
+
+ MemoryResource[0].PhysicalStart = 0;
+ MemoryResource[0].ResourceLength = LShiftU64 (1, GetPhysicalAddressBits ());
+ DEBUG ((DEBUG_INFO, "MemoryResource[0x0] - Start(0x%0lx) Length(0x%0lx)\n",
+ MemoryResource[0x0].PhysicalStart, MemoryResource[0x0].ResourceLength));
+ return MemoryResource;
+#else
+ return NULL;
+#endif
+ }
+
+ //
+ // Allocate memory to hold memory resource descriptor,
+ // include extra one NULL terminate memory resource descriptor.
+ //
+ Status = PeiServicesAllocatePool ((Index + 1) * sizeof (MEMORY_RESOURCE_DESCRIPTOR), (VOID **) &MemoryResource);
+ ASSERT_EFI_ERROR (Status);
+ ZeroMem (MemoryResource, (Index + 1) * sizeof (MEMORY_RESOURCE_DESCRIPTOR));
+
+ //
+ // Get the content of memory resource descriptor.
+ //
+ Index = 0;
+ Hob.Raw = GetFirstHob (EFI_HOB_TYPE_RESOURCE_DESCRIPTOR);
+ while (Hob.Raw != NULL) {
+ ResourceDescriptor = (EFI_HOB_RESOURCE_DESCRIPTOR *) Hob.Raw;
+ if (ResourceDescriptor->ResourceType == EFI_RESOURCE_SYSTEM_MEMORY) {
+ DEBUG ((DEBUG_INFO, "MemoryResource[0x%x] - Start(0x%0lx) Length(0x%0lx)\n",
+ Index, ResourceDescriptor->PhysicalStart, ResourceDescriptor->ResourceLength));
+ MemoryResource[Index].PhysicalStart = ResourceDescriptor->PhysicalStart;
+ MemoryResource[Index].ResourceLength = ResourceDescriptor->ResourceLength;
+ Index++;
+ }
+ Hob.Raw = GET_NEXT_HOB (Hob);
+ Hob.Raw = GetNextHob (EFI_HOB_TYPE_RESOURCE_DESCRIPTOR, Hob.Raw);
+ }
+
+ SortMemoryResourceDescriptor (MemoryResource);
+ MergeMemoryResourceDescriptor (MemoryResource);
+
+ DEBUG ((DEBUG_INFO, "Dump MemoryResource[] after sorted and merged\n"));
+ for (Index = 0; MemoryResource[Index].ResourceLength != 0; Index++) {
+ DEBUG ((
+ DEBUG_INFO,
+ " MemoryResource[0x%x] - Start(0x%0lx) Length(0x%0lx)\n",
+ Index,
+ MemoryResource[Index].PhysicalStart,
+ MemoryResource[Index].ResourceLength
+ ));
+ }
+
+ return MemoryResource;
+}
+
+/**
+ Check if the capsules are staged.
+
+ @retval TRUE The capsules are staged.
+ @retval FALSE The capsules are not staged.
+
+**/
+BOOLEAN
+AreCapsulesStaged (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ UINTN Size;
+ EFI_PEI_READ_ONLY_VARIABLE2_PPI *PPIVariableServices;
+ EFI_PHYSICAL_ADDRESS CapsuleDataPtr64;
+
+ CapsuleDataPtr64 = 0;
+
+ Status = PeiServicesLocatePpi(
+ &gEfiPeiReadOnlyVariable2PpiGuid,
+ 0,
+ NULL,
+ (VOID **)&PPIVariableServices
+ );
+
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Failed to find ReadOnlyVariable2PPI\n"));
+ return FALSE;
+ }
+
+ //
+ // Check for Update capsule
+ //
+ Size = sizeof (CapsuleDataPtr64);
+ Status = PPIVariableServices->GetVariable (
+ PPIVariableServices,
+ EFI_CAPSULE_VARIABLE_NAME,
+ &gEfiCapsuleVendorGuid,
+ NULL,
+ &Size,
+ (VOID *)&CapsuleDataPtr64
+ );
+
+ if (!EFI_ERROR (Status)) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ Check all the variables for SG list heads and get the count and addresses.
+
+ @param ListLength A pointer would return the SG list length.
+ @param HeadList A ponter to the capsule SG list.
+
+ @retval EFI_SUCCESS a valid capsule is present
+ @retval EFI_NOT_FOUND if a valid capsule is not present
+ @retval EFI_INVALID_PARAMETER the input parameter is invalid
+ @retval EFI_OUT_OF_RESOURCES fail to allocate memory
+
+**/
+EFI_STATUS
+GetScatterGatherHeadEntries (
+ OUT UINTN *ListLength,
+ OUT EFI_PHYSICAL_ADDRESS **HeadList
+ )
+{
+ EFI_STATUS Status;
+ UINTN Size;
+ UINTN Index;
+ UINTN TempIndex;
+ UINTN ValidIndex;
+ BOOLEAN Flag;
+ CHAR16 CapsuleVarName[30];
+ CHAR16 *TempVarName;
+ EFI_PHYSICAL_ADDRESS CapsuleDataPtr64;
+ EFI_PEI_READ_ONLY_VARIABLE2_PPI *PPIVariableServices;
+ EFI_PHYSICAL_ADDRESS *TempList;
+ EFI_PHYSICAL_ADDRESS *EnlargedTempList;
+ UINTN TempListLength;
+
+ Index = 0;
+ TempVarName = NULL;
+ CapsuleVarName[0] = 0;
+ ValidIndex = 0;
+ CapsuleDataPtr64 = 0;
+
+ if ((ListLength == NULL) || (HeadList == NULL)) {
+ DEBUG ((DEBUG_ERROR, "%a Invalid parameters. Inputs can't be NULL\n", __FUNCTION__));
+ ASSERT (ListLength != NULL);
+ ASSERT (HeadList != NULL);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *ListLength = 0;
+ *HeadList = NULL;
+
+ Status = PeiServicesLocatePpi (
+ &gEfiPeiReadOnlyVariable2PpiGuid,
+ 0,
+ NULL,
+ (VOID **)&PPIVariableServices
+ );
+
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Failed to find ReadOnlyVariable2PPI\n"));
+ return Status;
+ }
+
+ //
+ // Allocate memory for sg list head
+ //
+ TempListLength = DEFAULT_SG_LIST_HEADS * sizeof (EFI_PHYSICAL_ADDRESS);
+ TempList = AllocateZeroPool (TempListLength);
+ if (TempList == NULL) {
+ DEBUG((DEBUG_ERROR, "Failed to allocate memory\n"));
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // setup var name buffer for update capsules
+ //
+ StrCpyS (CapsuleVarName, sizeof (CapsuleVarName) / sizeof (CHAR16), EFI_CAPSULE_VARIABLE_NAME);
+ TempVarName = CapsuleVarName + StrLen (CapsuleVarName);
+ while (TRUE) {
+ if (Index != 0) {
+ UnicodeValueToStringS (
+ TempVarName,
+ (sizeof (CapsuleVarName) - ((UINTN)TempVarName - (UINTN)CapsuleVarName)),
+ 0,
+ Index,
+ 0
+ );
+ }
+ Size = sizeof (CapsuleDataPtr64);
+ Status = PPIVariableServices->GetVariable (
+ PPIVariableServices,
+ CapsuleVarName,
+ &gEfiCapsuleVendorGuid,
+ NULL,
+ &Size,
+ (VOID *)&CapsuleDataPtr64
+ );
+
+ if (EFI_ERROR (Status)) {
+ if (Status != EFI_NOT_FOUND) {
+ DEBUG ((DEBUG_ERROR, "Unexpected error getting Capsule Update variable. Status = %r\n"));
+ }
+ break;
+ }
+
+ //
+ // If this BlockList has been linked before, skip this variable
+ //
+ Flag = FALSE;
+ for (TempIndex = 0; TempIndex < ValidIndex; TempIndex++) {
+ if (TempList[TempIndex] == CapsuleDataPtr64) {
+ Flag = TRUE;
+ break;
+ }
+ }
+ if (Flag) {
+ Index++;
+ continue;
+ }
+
+ //
+ // The TempList is full, enlarge it
+ //
+ if ((ValidIndex + 1) >= TempListLength) {
+ EnlargedTempList = AllocateZeroPool (TempListLength * 2);
+ if (EnlargedTempList == NULL) {
+ DEBUG ((DEBUG_ERROR, "Fail to allocate memory!\n"));
+ return EFI_OUT_OF_RESOURCES;
+ }
+ CopyMem (EnlargedTempList, TempList, TempListLength);
+ FreePool (TempList);
+ TempList = EnlargedTempList;
+ TempListLength *= 2;
+ }
+
+ //
+ // add it to the cached list
+ //
+ TempList[ValidIndex++] = CapsuleDataPtr64;
+ Index++;
+ }
+
+ if (ValidIndex == 0) {
+ DEBUG ((DEBUG_ERROR, "%a didn't find any SG lists in variables\n", __FUNCTION__));
+ return EFI_NOT_FOUND;
+ }
+
+ *HeadList = AllocateZeroPool ((ValidIndex + 1) * sizeof (EFI_PHYSICAL_ADDRESS));
+ if (*HeadList == NULL) {
+ DEBUG ((DEBUG_ERROR, "Failed to allocate memory\n"));
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ CopyMem (*HeadList, TempList, (ValidIndex) * sizeof (EFI_PHYSICAL_ADDRESS));
+ *ListLength = ValidIndex;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Capsule PPI service to coalesce a fragmented capsule in memory.
+
+ @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
+ )
+{
+ EFI_STATUS Status;
+ EFI_BOOT_MODE BootMode;
+ UINTN ListLength;
+ EFI_PHYSICAL_ADDRESS *VariableArrayAddress;
+ MEMORY_RESOURCE_DESCRIPTOR *MemoryResource;
+#ifdef MDE_CPU_IA32
+ UINT16 CoalesceImageMachineType;
+ EFI_PHYSICAL_ADDRESS CoalesceImageEntryPoint;
+ COALESCE_ENTRY CoalesceEntry;
+ EFI_CAPSULE_LONG_MODE_BUFFER LongModeBuffer;
+#endif
+
+ ListLength = 0;
+ VariableArrayAddress = NULL;
+
+ //
+ // 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)) {
+ DEBUG ((DEBUG_ERROR, "Boot mode is not correct for capsule update path.\n"));
+ Status = EFI_NOT_FOUND;
+ goto Done;
+ }
+
+ //
+ // Get SG list entries
+ //
+ Status = GetScatterGatherHeadEntries (&ListLength, &VariableArrayAddress);
+ if (EFI_ERROR (Status) || VariableArrayAddress == NULL) {
+ DEBUG ((DEBUG_ERROR, "%a failed to get Scatter Gather List Head Entries. Status = %r\n", __FUNCTION__, Status));
+ goto Done;
+ }
+
+ MemoryResource = BuildMemoryResourceDescriptor ();
+
+#ifdef MDE_CPU_IA32
+ if (FeaturePcdGet (PcdDxeIplSwitchToLongMode)) {
+ //
+ // 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.
+ //
+ CoalesceImageEntryPoint = 0;
+ Status = GetLongModeContext (&LongModeBuffer);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Fail to find the variable for long mode context!\n"));
+ Status = EFI_NOT_FOUND;
+ goto Done;
+ }
+
+ Status = FindCapsuleCoalesceImage (&CoalesceImageEntryPoint, &CoalesceImageMachineType);
+ if ((EFI_ERROR (Status)) || (CoalesceImageMachineType != EFI_IMAGE_MACHINE_X64)) {
+ DEBUG ((DEBUG_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, MemoryResource, MemoryBase, MemorySize);
+ } else {
+ //
+ // Capsule is processed in IA32 mode.
+ //
+ Status = CapsuleDataCoalesce (PeiServices, (EFI_PHYSICAL_ADDRESS *)(UINTN)VariableArrayAddress, MemoryResource, MemoryBase, MemorySize);
+ }
+#else
+ //
+ // Process capsule directly.
+ //
+ Status = CapsuleDataCoalesce (PeiServices, (EFI_PHYSICAL_ADDRESS *)(UINTN)VariableArrayAddress, MemoryResource, MemoryBase, MemorySize);
+#endif
+
+ DEBUG ((DEBUG_INFO, "Capsule Coalesce Status = %r!\n", Status));
+
+ if (Status == EFI_BUFFER_TOO_SMALL) {
+ DEBUG ((DEBUG_ERROR, "There is not enough memory to process capsule!\n"));
+ }
+
+ if (Status == EFI_NOT_FOUND) {
+ DEBUG ((DEBUG_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;
+}
+
+/**
+ 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
+ )
+{
+ if (AreCapsulesStaged ()) {
+ return EFI_SUCCESS;
+ } else {
+ return EFI_NOT_FOUND;
+ }
+}
+/**
+ 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;
+ //
+ // 0x54534554 "TEST"
+ //
+ if (*TestPtr == 0x54534554) {
+ RetValue = TRUE;
+ DEBUG ((DEBUG_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 ((DEBUG_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 ((DEBUG_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;
+ UINTN CapsuleNumber;
+ UINT32 Index;
+ EFI_PHYSICAL_ADDRESS BaseAddress;
+ UINT64 Length;
+
+ PrivateData = (EFI_CAPSULE_PEIM_PRIVATE_DATA *) CapsuleBase;
+ if (PrivateData->Signature != EFI_CAPSULE_PEIM_PRIVATE_DATA_SIGNATURE) {
+ return EFI_VOLUME_CORRUPTED;
+ }
+ if (PrivateData->CapsuleAllImageSize >= MAX_ADDRESS) {
+ DEBUG ((DEBUG_ERROR, "CapsuleAllImageSize too big - 0x%lx\n", PrivateData->CapsuleAllImageSize));
+ return EFI_OUT_OF_RESOURCES;
+ }
+ if (PrivateData->CapsuleNumber >= MAX_ADDRESS) {
+ DEBUG ((DEBUG_ERROR, "CapsuleNumber too big - 0x%lx\n", PrivateData->CapsuleNumber));
+ return EFI_OUT_OF_RESOURCES;
+ }
+ //
+ // Capsule Number and Capsule Offset is in the tail of Capsule data.
+ //
+ Size = (UINTN)PrivateData->CapsuleAllImageSize;
+ CapsuleNumber = (UINTN)PrivateData->CapsuleNumber;
+ //
+ // Allocate the memory so that it gets preserved into DXE
+ //
+ Status = PeiServicesAllocatePages (
+ EfiRuntimeServicesData,
+ EFI_SIZE_TO_PAGES (Size),
+ &NewBuffer
+ );
+
+ if (Status != EFI_SUCCESS) {
+ DEBUG ((DEBUG_ERROR, "AllocatePages Failed!\n"));
+ return Status;
+ }
+ //
+ // Copy to our new buffer for DXE
+ //
+ DEBUG ((DEBUG_INFO, "Capsule copy from 0x%8X to 0x%8X with size 0x%8X\n", (UINTN)((UINT8 *)PrivateData + sizeof(EFI_CAPSULE_PEIM_PRIVATE_DATA) + (CapsuleNumber - 1) * sizeof(UINT64)), (UINTN) NewBuffer, Size));
+ CopyMem ((VOID *) (UINTN) NewBuffer, (VOID *) (UINTN) ((UINT8 *)PrivateData + sizeof(EFI_CAPSULE_PEIM_PRIVATE_DATA) + (CapsuleNumber - 1) * sizeof(UINT64)), Size);
+ //
+ // Check for test data pattern. If it is the test pattern, then we'll
+ // test it and 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 + PrivateData->CapsuleOffset[Index];
+ Length = ((EFI_CAPSULE_HEADER *)((UINTN) BaseAddress))->CapsuleImageSize;
+
+ BuildCvHob (BaseAddress, Length);
+ }
+
+ return EFI_SUCCESS;
+}
+
+CONST EFI_PEI_CAPSULE_PPI mCapsulePpi = {
+ CapsuleCoalesce,
+ CheckCapsuleUpdate,
+ CreateState
+};
+
+CONST EFI_PEI_PPI_DESCRIPTOR mUefiPpiListCapsule = {
+ (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
+ &gEfiPeiCapsulePpiGuid,
+ (EFI_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);
+}