/**
* CapsulePei - Capsule PEIM
*
* Source: MdeModulePkg/Universal/CapsulePei/
* Modules:
* - Common/CapsuleCoalesce.c (capsule coalescing logic)
* - UefiCapsule.c (UEFI capsule entry / PEI phase coordination)
*
* Binary: CapsulePei.efi (32-bit PEI)
* Image size: 0x44e0
* Functions: 61 (all renamed)
*/
#include <PiPei.h>
#include <Library/DebugLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/PeiServicesLib.h>
#include <Library/HobLib.h>
#include <Library/BaseLib.h>
#include <Library/PrintLib.h>
#include <Ppi/Capsule.h>
#include <Guid/CapsuleVendor.h>
//
// Forward declarations
//
EFI_STATUS
EFIAPI
_ModuleEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
);
/*----------------------------------------------------------------------
* Base Memory Routines (from BaseMemoryLibRepStr)
*----------------------------------------------------------------------*/
/**
* Copy memory (handles overlapping src/dst by doing backward copy).
*/
VOID *
InternalCopyMem (
VOID *Destination,
CONST VOID *Source,
UINTN Length
)
{
// Copies Length bytes from Source to Destination.
// If Source < Destination and ranges overlap, copies backward.
// Otherwise uses dword-aligned copy then residual.
}
/**
* Zero-fill memory.
*/
VOID *
InternalZeroMem (
VOID *Buffer,
UINTN Length
)
{
return memset(Buffer, 0, Length);
}
/**
* Set memory to a byte value.
*/
VOID *
InternalSetMem (
VOID *Buffer,
UINTN Length,
UINT8 Value
)
{
return memset(Buffer, Value, Length);
}
/**
* Set memory 32-bit values (count of UINT32).
*/
VOID *
InternalSetMem32 (
VOID *Buffer,
UINTN Count,
UINT32 Value
)
{
// memset32-style fill
}
/**
* Wrapper for setting memory 32-bit values with 64-bit pair.
*/
INTN
SetMem32Wrapper (
INTN Base,
INTN Count,
INTN ValueLow,
INTN ValueHigh
)
{
// Uses InternalSetMem32-style write in a counted loop
}
/*----------------------------------------------------------------------
* x86 Architecture Primitives (from BaseLib)
*----------------------------------------------------------------------*/
/**
* Read IDTR register.
*/
VOID
ReadIdtr (
OUT IA32_DESCRIPTOR *Idtr
)
{
__sidt(Idtr);
}
/**
* Write GDTR register.
*/
VOID
WriteGdtr (
IN IA32_DESCRIPTOR *Gdtr
)
{
__lgdt(Gdtr);
}
/**
* CPUID instruction wrapper.
*/
UINT32
Cpuid (
IN UINT32 EaxIn,
OUT UINT32 *Eax OPTIONAL,
OUT UINT32 *Ebx OPTIONAL,
OUT UINT32 *Ecx OPTIONAL,
OUT UINT32 *Edx OPTIONAL
)
{
UINT32 EaxOut;
__asm { cpuid }
if (Eax) *Eax = EaxOut;
if (Edx) *Edx = <edx_value>;
return EaxOut;
}
/**
* 64-bit left shift (Count must be < 64).
*/
UINT64
LShiftU64 (
IN UINT64 Value,
IN UINTN Count
)
{
ASSERT (Count < 64);
if ((Count & 0x20) == 0)
return Value << (Count & 0x1F);
else
return Value << (Count & 0x1F);
}
/**
* Read unaligned 64-bit value.
*/
UINT64
ReadUnaligned64 (
IN CONST VOID *Buffer
)
{
return *(UINT64 *)Buffer;
}
/**
* SetJump implementation.
*/
UINTN
SetJump (
OUT BASE_LIBRARY_JUMP_BUFFER *JumpBuffer,
IN VOID *Context
)
{
// Saves callee-saved registers, stack pointer, and return address
JumpBuffer->Ebx = EaxIn;
JumpBuffer->Esi = EsiIn;
JumpBuffer->Edi = EdiIn;
JumpBuffer->Ebp = EbpIn;
JumpBuffer->Esp = &JumpBuffer;
return ((UINTN (*)(VOID))JumpBuffer->ReturnAddr)();
}
/**
* LongJump implementation (indirect jump via [edx+0x14]).
*/
VOID
LongJump (
IN VOID *Function,
IN UINT32 Param
)
{
__asm { jmp dword ptr [edx+0x14] }
}
/**
* Validate JumpBuffer pointer for SetJump.
*/
VOID
SetJumpValidateBuffer (
IN VOID *JumpBuffer
)
{
ASSERT (JumpBuffer != NULL);
ASSERT (((UINTN)JumpBuffer & 3) == 0);
}
/**
* Enable 64-bit paging with SSE/CR4/CR0/EFER MSR setup.
*/
UINTN
AsmEnablePaging64 (
VOID
)
{
_disable();
// Prepare return address
__writecr4(__readcr4() | 0x20); // CR4.PAE
__writemsr(0xC0000080, __readmsr(0xC0000080) | 0x100); // EFER.LME
__writecr0(__readcr0() | 0x80000000); // CR0.PG
// Jump to continuation
}
/**
* Wrapper: validate entry point/stack then call AsmEnablePaging64.
*/
UINTN
AsmEnablePaging64Wrapper (
IN UINT64 EntryPoint,
IN UINTN StackSize,
IN UINTN StackBuffer,
IN UINTN Context,
IN UINT64 NewStack
)
{
ASSERT (EntryPoint != 0);
ASSERT (NewStack != 0);
return AsmEnablePaging64();
}
/*----------------------------------------------------------------------
* Page Table / Long Mode Transition
*----------------------------------------------------------------------*/
/**
* Initialize 4-level page tables at the provided buffer.
* Sets up 512 PML4 entries, each pointing to a PDP with 512 entries
* mapping 2MB pages (0x200000 per page).
*/
VOID
PageTableInitialization (
IN VOID *PageTableBuffer,
IN UINTN ModeFlags
)
{
// If ModeFlags != 0 (x64): creates PML4[0..511] each pointing to
// a PDP table with 512 PDEs mapping 0x200000 pages.
// If ModeFlags == 0: creates 512 PML4 entries with 0x40000000 pages.
// Unused entries zeroed.
}
/**
* Switch to long mode using the provided 32→64 thunk context.
* Saves GDTR, page tables, stack pointer.
*/
EFI_STATUS
CapsuleLongModeSwitch (
IN UINT32 *ThunkContext,
IN UINT32 Param
)
{
WriteGdtr(&ThunkContext->Gdtr);
SetJumpValidateBuffer(ThunkContext);
LongJump(ThunkContext, 1);
return GetReportStatusCodeInterface(); // should not return
}
/**
* Core 32→64 thunk call: allocate page tables, switch to long mode,
* call the target function, then return.
*/
UINTN
Thunk32To64Call (
IN UINT32 *PageTables,
IN VOID *ThunkData
)
{
INTN Result;
Result = SetJump(ThunkData);
if (Result == 0) {
// First call: set up page tables and long mode
PageTableInitialization(PageTables);
WriteGdtr(&GdtDescriptor);
__writecr3((UINT32)PageTables);
DEBUG ((DEBUG_INFO, "Thunk32To64 Stack Base: 0x%lx, Stack Size: 0x%lx\n",
ThunkData->StackBase, ThunkData->StackSize));
AsmEnablePaging64Wrapper (
*(UINT64 *)ThunkData,
ThunkData,
0,
PageTables,
0,
*(UINT64 *)(ThunkData + 8) + *(UINT64 *)(ThunkData + 16)
);
}
if (ThunkData->Result)
return ThunkData->Result | 0x80000000;
return EFI_SUCCESS;
}
/*----------------------------------------------------------------------
* PEI Services Wrappers
*----------------------------------------------------------------------*/
/**
* Retrieve PEI Services pointer from the IDTR base.
*/
EFI_PEI_SERVICES **
GetPeiServices (
VOID
)
{
IA32_DESCRIPTOR Idtr;
EFI_PEI_SERVICES **PeiServices;
ReadIdtr(&Idtr);
PeiServices = *(EFI_PEI_SERVICES ***)(Idtr.Base - 4);
ASSERT (PeiServices != NULL);
return PeiServices;
}
/**
* Locate a PPI.
*/
EFI_STATUS
LocatePpi (
IN EFI_GUID *Guid,
OUT VOID **PpiInterface
)
{
return (*GetPeiServices())->LocatePpi (Guid, 0, NULL, PpiInterface);
}
/**
* Allocate boot-services pages.
*/
EFI_STATUS
AllocatePages (
IN EFI_MEMORY_TYPE MemoryType,
IN UINTN Pages,
OUT EFI_PHYSICAL_ADDRESS *Address
)
{
return (*GetPeiServices())->AllocatePages (MemoryType, Pages, Address);
}
/**
* Install a PPI.
*/
EFI_STATUS
InstallCapsulePpi (
VOID
)
{
// Installs the Capsule PPI with gEfiPeiCapsulePpiGuid
return (*GetPeiServices())->InstallPpi (gCapsulePpiGuid);
}
/**
* Get HOB list.
*/
EFI_HOB_HANDOFF_INFO_TABLE *
GetHobList (
VOID
)
{
EFI_HOB_HANDOFF_INFO_TABLE *HobList;
EFI_STATUS Status;
Status = (*GetPeiServices())->GetHobList (&HobList);
ASSERT_EFI_ERROR (Status);
ASSERT (HobList != NULL);
return HobList;
}
/**
* Get first HOB of specific type from list.
*/
VOID *
GetFirstHob (
VOID
)
{
VOID *Hob;
Hob = GetHobList();
if ((*GetPeiServices())->GetBootMode() < 0)
Hob = NULL;
return Hob;
}
/**
* Walk HOB list to find a HOB matching the requested type.
*/
VOID *
GetNextHob (
IN UINT16 HobType,
IN VOID *HobStart
)
{
ASSERT (HobStart != NULL);
while (((EFI_HOB_GENERIC_HEADER *)HobStart)->HobType != HobType) {
if (((EFI_HOB_GENERIC_HEADER *)HobStart)->HobType == EFI_HOB_TYPE_END_OF_HOB_LIST)
return NULL;
HobStart = (VOID *)((UINT8 *)HobStart + ((EFI_HOB_GENERIC_HEADER *)HobStart)->HobLength);
}
return HobStart;
}
/**
* Compare two GUIDs.
*/
BOOLEAN
CompareGuid (
IN EFI_GUID *Guid1,
IN EFI_GUID *Guid2
)
{
return (ReadUnaligned64(Guid1) == ReadUnaligned64(Guid2) &&
ReadUnaligned64(&Guid1->Data2) == ReadUnaligned64(&Guid2->Data2));
}
/**
* Get PCD database pointer.
*/
VOID *
GetPcdDb (
VOID
)
{
EFI_GUID gPcdPpiGuid = ...;
VOID *PcdDb = NULL;
LocatePpi (&gPcdPpiGuid, &PcdDb);
return PcdDb;
}
/**
* Get ReportStatusCode interface (from PCD or PPI).
*/
EFI_REPORT_STATUS_CODE_INTERFACE *
GetReportStatusCodeInterface (
VOID
)
{
EFI_GUID gStatusCodePpiGuid;
VOID *Interface = NULL;
if (LocatePpi (&gStatusCodePpiGuid, &Interface) >= 0)
return Interface;
return NULL;
}
/**
* Debug print via report status code.
*/
VOID
DebugPrint (
IN UINTN ErrorLevel,
IN CONST CHAR8 *Format,
...
)
{
EFI_REPORT_STATUS_CODE_INTERFACE *Rsc;
Rsc = GetReportStatusCodeInterface();
if (Rsc && (GetPlatformType() & ErrorLevel))
Rsc->ReportStatusCode (ErrorLevel, Format, VA_ARG_LIST);
}
/**
* ASSERT print helper.
*/
VOID
AssertPrint (
IN CONST CHAR8 *FileName,
IN UINTN LineNumber,
IN CONST CHAR8 *Expression
)
{
EFI_REPORT_STATUS_CODE_INTERFACE *Rsc;
Rsc = GetReportStatusCodeInterface();
if (Rsc)
Rsc->ReportStatusCode (EFI_ERROR_CODE, FileName, LineNumber, Expression);
}
/**
* CopyMem wrapper with boundary checking.
*/
VOID *
CopyMem (
VOID *Destination,
CONST VOID *Source,
UINTN Length
)
{
ASSERT (Length <= (UINTN)-1 - (UINTN)Destination);
ASSERT (Length <= (UINTN)-1 - (UINTN)Source);
if (Destination == Source)
return Destination;
return InternalCopyMem (Destination, Source, Length);
}
/**
* ZeroMem wrapper with boundary checking.
*/
VOID
ZeroMem (
VOID *Buffer,
UINTN Length
)
{
ASSERT (Buffer != NULL);
ASSERT (Length <= (UINTN)-1 - (UINTN)Buffer + 1);
InternalZeroMem (Buffer, Length);
}
/*----------------------------------------------------------------------
* Platform identification
*----------------------------------------------------------------------*/
/**
* Determine platform type via CMOS/RTC.
* Returns:
* 0 - Normal/Unknown
* -2147483644 - Server platform
* -2147483578 - Other known platform
*/
EFI_STATUS
GetPlatformType (
VOID
)
{
UINT8 Register;
// Read CMOS index 0x4A
__outbyte (0x70, __inbyte(0x70) & 0x80 | 0x4A);
Register = __inbyte (0x71);
if (Register <= 3) {
// Normal range - small value
} else if (Register == 0) {
Register = (*(UINT8 *)0xFDAF0490 & 2) | 1;
if (Register == 0)
return EFI_SUCCESS;
}
if (Register == (UINT8)-1)
return EFI_SUCCESS;
if (Register == 1)
return EFI_INVALID_PARAMETER; // Server
return EFI_ACCESS_DENIED; // Other known
}
/*----------------------------------------------------------------------
* String handling (CapsuleUpdateData variable)
*----------------------------------------------------------------------*/
/**
* Check if address overlaps with the "CapsuleUpdateData" string.
*/
BOOLEAN
IsOverlap (
IN UINTN Address,
IN UINTN Size
)
{
// Overlap check against L"CapsuleUpdateData" storage
}
/**
* Check if string fits within given size.
*/
BOOLEAN
StrSizeCheck (
IN UINTN Address,
IN UINTN SizeElements
)
{
return IsOverlap (Address, 2 * SizeElements) == 0;
}
/**
* Calculate length of L"CapsuleUpdateData" wide string.
*/
UINTN
StrLenCapsuleUpdateData (
VOID
)
{
// Returns length of L"CapsuleUpdateData" (15 characters)
return 15;
}
/**
* Copy "CapsuleUpdateData" wide string to destination.
*/
INTN
StrCpyCapsuleUpdateData (
OUT CHAR16 *Destination
)
{
// Copies "CapsuleUpdateData\0" to destination
}
/**
* Calculate length of a wide string.
*/
UINTN
StrLen (
IN CONST CHAR16 *String
)
{
UINTN Length = 0;
while (*String++) {
if (Length >= PcdGet32 (PcdMaximumUnicodeStringLength))
break;
Length++;
}
return Length;
}
/**
* Set a wide string filled with a single character.
*/
CHAR16 *
StrNSetChar (
OUT CHAR16 *String,
IN UINTN Size,
IN INTN Count,
IN CHAR16 Char
)
{
for (INTN i = 0; i < Count; i++) {
if ((UINTN)String >= Size)
break;
*String++ = Char;
}
return String;
}
/*----------------------------------------------------------------------
* Print support (PrintLib)
*----------------------------------------------------------------------*/
/**
* Convert UINT64 to decimal ASCII string.
*/
CHAR8 *
ConvertUint64ToDecimalString (
OUT CHAR8 *Buffer,
IN UINT64 Value
)
{
// Converts Value to decimal string, stores in Buffer
}
/**
* Unicode SPrint with alignment check.
*/
UINTN
AsciiSPrintUnicodeCheck (
OUT CHAR8 *Buffer,
IN UINTN BufferSize,
IN CONST CHAR8 *Format,
...
)
{
ASSERT (((UINTN)Buffer & 1) == 0);
return InternalPrintLibSPrint (Buffer, BufferSize, Format, ...);
}
/**
* Internal PrintLib SPrint implementation.
*/
UINTN
InternalPrintLibSPrint (
OUT CHAR8 *Buffer,
IN UINTN BufferSize,
IN CONST CHAR8 *Format,
IN VA_LIST VaListMarker
)
{
// Full format string parser with number conversion
}
/*----------------------------------------------------------------------
* PEI Capsule Entry Points (UefiCapsule.c)
*----------------------------------------------------------------------*/
/**
* Find the CapsuleX64 PE32+ image in the firmware volume.
* Walks FV to locate the x64 capsule coalesce entrypoint.
*/
EFI_STATUS
FindCapsuleX64PeImage (
IN UINTN CapsuleType,
OUT UINT16 *CapsuleImageType
)
{
EFI_STATUS Status;
EFI_PEI_SERVICES **PeiServices;
UINTN Index = 0;
EFI_FV_INFO FvInfo;
EFI_GUID *CapsuleGuid = &gEfiCapsuleVendorGuid;
PeiServices = GetPeiServices();
Status = (*PeiServices)->FfsFindNextVolume (PeiServices, 0, &FvInfo);
while (!EFI_ERROR (Status)) {
Status = (*PeiServices)->FfsFindFile (CapsuleGuid, FvInfo, &FvInfo);
if (!EFI_ERROR (Status)) {
// Found the capsule PE image
if (LocatePpi (&gPeiCapsuleThunkPpiGuid, ...) < 0) {
ASSERT_EFI_ERROR (FALSE);
}
// Locate PE32 section, get entry point
*CapsuleImageType = GetPeCoffEntryPoint (SectionData);
return EFI_SUCCESS;
}
Status = (*PeiServices)->FfsFindNextVolume (PeiServices, ++Index, &FvInfo);
}
return Status;
}
/**
* Get the "CapsuleLongModeBuffer" variable from variable services.
*/
EFI_STATUS
GetLongModeBufferVariable (
OUT CAPSULE_LONG_MODE_BUFFER *Buffer
)
{
EFI_GUID gCapsuleVendorGuid;
UINTN BufferSize = sizeof (CAPSULE_LONG_MODE_BUFFER);
if (LocatePpi (&gPeiVariablePpiGuid, &VariablePpi) < 0) {
ASSERT_EFI_ERROR (FALSE);
}
return VariablePpi->GetVariable (
L"CapsuleLongModeBuffer",
&gCapsuleVendorGuid,
0,
&BufferSize,
Buffer
);
}
/**
* Determine the physical address width (bits) from CPUID or HOB.
*/
UINT8
GetPhysicalAddressBits (
VOID
)
{
UINT8 PhysicalAddressBits;
UINTN Hob;
UINT32 Eax, Edx;
UINT32 MaxCpuId;
Hob = GetHobList();
if (GetNextHob (EFI_HOB_TYPE_CPU, Hob)) {
PhysicalAddressBits = *(UINT8 *)(Hob + 8);
} else {
Cpuid (0, &MaxCpuId, NULL, NULL, NULL);
if (MaxCpuId >= 0x80000008) {
Cpuid (0x80000008, &Eax, NULL, NULL, NULL);
PhysicalAddressBits = (UINT8)Eax;
} else if (MaxCpuId >= 0x80000000) {
Cpuid (0x80000000, &Eax, NULL, NULL, NULL);
PhysicalAddressBits = (UINT8)Eax;
} else {
PhysicalAddressBits = 36;
}
}
ASSERT (PhysicalAddressBits <= 52);
if (PhysicalAddressBits > 48)
return 48;
return PhysicalAddressBits;
}
/**
* Collect system memory resources from HOBs.
*/
EFI_STATUS
GetSystemMemoryResources (
OUT EFI_MEMORY_DESCRIPTOR **MemoryResources,
OUT UINTN *BestResourceIndex
)
{
// Walk the HOB list, collect EFI_RESOURCE_MEMORY resources.
// Returns the resource with largest descriptor.
}
/**
* Get capsule variable entries from "CapsuleUpdateData".
* Each entry is an {Address, Size} pair tracking pending capsules.
*/
EFI_STATUS
GetCapsuleVariableList (
OUT CAPSULE_VARIABLE_ENTRY *VariableEntries
)
{
// Enumerate all "CapsuleUpdateData" variable instances
// Variable is gEfiCapsuleVendorGuid + L"CapsuleUpdateData" Name
}
/**
* Get number of pending capsule variables.
*/
UINTN
GetCapsuleVariableCount (
VOID
)
{
return GetCapsuleVariableList (NULL);
}
/**
* Main capsule data coalescing entry point.
* Orchestrates the full coalescing flow:
* 1. Get system memory resources
* 2. Read long mode buffer
* 3. Find CapsuleX64 PE image
* 4. Enumerate capsule variables
* 5. Allocate/relocate capsule data
* 6. Coalesce through x64 thunk
*/
EFI_STATUS
CapsuleDataCoalesceEntry (
IN UINTN CapsuleType,
OUT VOID **CapsuleData,
OUT UINTN *CapsuleDataSize
)
{
DEBUG ((DEBUG_INFO, "Capsule variable Index = %d\n"));
DEBUG ((DEBUG_INFO, "Capsule variable count = %d\n"));
// Allocate storage for capsule variable list
CapsuleVariableCount = GetCapsuleVariableCount();
BufferSize = 8 * CapsuleVariableCount + 8;
Status = (*GetPeiServices())->AllocatePages (BufferSize, &VariableList);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "AllocatePages Failed!, Status = %x\n", Status));
return Status;
}
ZeroMem (VariableList, BufferSize);
Status = GetCapsuleVariableList (VariableList);
if (!EFI_ERROR (Status)) {
MemoryResources = GetSystemMemoryResources();
GetLongModeBufferVariable (&LongModeBuffer);
if (FindCapsuleX64PeImage (&CoalesceImageEntryPoint, &ImageType) < 0 ||
ImageType != 0x86A4) {
DEBUG ((DEBUG_ERROR, "Fail to find CapsuleX64 module in FV!\n"));
return EFI_NOT_FOUND;
}
ASSERT (CoalesceImageEntryPoint != 0);
Status = CapsuleCoalesce (
&LongModeBuffer,
(VOID *)CoalesceImageEntryPoint,
VariableList,
0,
MemoryResources,
(UINTN *)CapsuleData,
(UINTN *)CapsuleDataSize
);
}
return Status;
}
/*----------------------------------------------------------------------
* Capsule Caching & Coalescing (UefiCapsule.c)
*----------------------------------------------------------------------*/
/**
* Cache all capsule data into contiguous memory and coalesce.
*
* Walks the capsule descriptor block list, validates, allocates
* scratch pages, copies all capsule segments, then coalesces
* in place into the final capsule header format.
*/
EFI_STATUS
CapsuleCacheAndCoalesce (
IN VOID *CapsuleHeader,
IN UINTN CapsuleSize
)
{
// Validate signature = 0x50637343 ("Csc")
// Read capsule sizes and counts
// Allocate pages for coalesced data
// Copy segment by segment
// Call CapsuleDataCoalesce to build final capsule
}
/*----------------------------------------------------------------------
* Capsule Validation & Coalescing Logic (CapsuleCoalesce.c)
*----------------------------------------------------------------------*/
/**
* Validate that a memory address range is valid in system memory.
*/
BOOLEAN
ValidateMemoryAddress (
IN UINT64 Address,
IN UINT64 Size,
IN VOID *MemoryResourceList,
IN BOOLEAN ValidateResource
)
{
// Check Address + Size does not overflow
// Verify range falls within one of the memory resources
}
/**
* Validate capsule descriptor block list integrity.
*
* Walks the EFI_CAPSULE_BLOCK_DESCRIPTOR linked list:
* - Each data block points to valid memory
* - Each capsule header has valid flags/sizes
* - Capsule count and total image sizes are consistent
*/
BOOLEAN
ValidateCapsuleIntegrity (
IN EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockList
)
{
UINT64 CapsuleSizeTotal = 0;
UINT32 CapsuleCount = 0;
EFI_CAPSULE_HEADER *CapsuleHeader;
EFI_CAPSULE_BLOCK_DESCRIPTOR *Ptr = BlockList;
DEBUG ((DEBUG_INFO, "ValidateCapsuleIntegrity\n"));
while (Ptr->Length != 0 || Ptr->Union.DataBlock != 0) {
if (((UINTN)Ptr & 7) != 0) {
DEBUG ((DEBUG_ERROR, "ERROR: BlockList address failed alignment check\n"));
return FALSE;
}
if (Ptr->Length != 0) {
// Validate data block
if (!ValidateMemoryAddress (Ptr->Union.DataBlock, Ptr->Length, 0, FALSE))
return FALSE;
if (CapsuleSizeTotal == 0) {
// First capsule header
CapsuleHeader = (EFI_CAPSULE_HEADER *)(UINTN)Ptr->Union.DataBlock;
if (Ptr->Length < sizeof (EFI_CAPSULE_HEADER)) {
DEBUG ((DEBUG_ERROR, "ERROR: Ptr->Length < sizeof(EFI_CAPSULE_HEADER)\n"));
return FALSE;
}
CapsuleSizeTotal = CapsuleHeader->CapsuleImageSize;
if (CapsuleHeader->HeaderSize > CapsuleSizeTotal) {
DEBUG ((DEBUG_ERROR, "ERROR: HeaderSize > CapsuleImageSize\n"));
return FALSE;
}
// Check flags: must have CAPSULE_FLAGS_PERSIST_ACROSS_RESET
// but not INITIATE_RESET or some other exclude bit
CapsuleCount++;
}
if (CapsuleSizeTotal < Ptr->Length) {
DEBUG ((DEBUG_ERROR, "ERROR: CapsuleSize(0x%lx) < Ptr->Length(0x%lx)\n"));
return FALSE;
}
CapsuleSizeTotal -= Ptr->Length;
Ptr++;
} else {
// Continuation pointer
Ptr = (EFI_CAPSULE_BLOCK_DESCRIPTOR *)(UINTN)Ptr->Union.Continuation;
}
}
if (CapsuleCount == 0) {
DEBUG ((DEBUG_ERROR, "ERROR: CapsuleCount == 0\n"));
return FALSE;
}
if (CapsuleSizeTotal != 0) {
DEBUG ((DEBUG_ERROR, "ERROR: CapsuleSizeTotal != 0\n"));
return FALSE;
}
return TRUE;
}
/**
* Resolve overlapping memory regions during capsule relocate.
* Adjusts destination to skip regions already occupied by source.
*/
UINT64
CapsuleRelocateOverlap (
IN UINT64 SourceEnd,
IN UINT64 Dest,
IN UINT32 DestEnd,
IN UINT32 RelocateOffset
)
{
// Given a capsule descriptor chain at SourceEnd, walks
// continuation entries to compute a safe relocation
// destination that doesn't overlap source data.
}
/**
* Relocate capsule descriptors from the descriptor chain,
* copying data blocks to non-overlapping destinations.
*/
UINT64
CapsuleRelocateDescriptors (
IN EFI_CAPSULE_BLOCK_DESCRIPTOR *DescriptorChain,
IN UINT32 DescriptorCount,
IN VOID *DestBuffer,
IN UINT32 DestBufferSize
)
{
// Walks all descriptors, copies data blocks
// Handles overlap via CapsuleRelocateOverlap
}
/**
* Parse capsule descriptor chain and verify consistency.
* Returns total size, capsule count, and capsule image size.
*/
EFI_STATUS
GetCapsuleInfo (
IN EFI_CAPSULE_BLOCK_DESCRIPTOR *Desc,
OUT UINTN *TotalSize,
OUT UINTN *DescriptorCount,
OUT UINT32 *CapsuleFlags,
OUT UINT32 *CapsuleImageSize
)
{
DEBUG ((DEBUG_INFO, "GetCapsuleInfo enter\n"));
if (Desc == NULL) {
ASSERT (Desc != NULL);
return EFI_INVALID_PARAMETER;
}
// Walk descriptors, sum lengths, find capsule header
}
/**
* Check capsule data for test pattern before coalescing.
*/
BOOLEAN
CapsuleTestPatternPreCoalesce (
IN EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockList
)
{
// If the first data block has signature 0x54534554 ("TEST")
// followed by sequence 0,1,2,..., then it passes as test pattern.
DEBUG ((DEBUG_INFO, "CapsuleTestPatternPreCoalesce\n"));
}
/**
* Build a linked capsule descriptor list from a flat descriptor array.
*/
EFI_STATUS
BuildCapsuleDescriptors (
OUT EFI_CAPSULE_BLOCK_DESCRIPTOR **Descriptor
)
{
// Chains all valid capsule descriptors together
DEBUG ((DEBUG_INFO, "BuildCapsuleDescriptors enter\n"));
}
/**
* Main capsule data coalesce logic.
* Orchestrates: build descriptors → test pattern check →
* get capsule info → allocate → copy → coalesce.
*/
EFI_STATUS
CapsuleDataCoalesce (
IN VOID *CapsuleData,
OUT VOID **CoalescedData,
OUT UINTN *CoalescedDataSize
)
{
DEBUG ((DEBUG_INFO, "CapsuleDataCoalesce enter\n"));
// 1. Build capsule descriptors
// 2. Test pattern pre-coalesce check
// 3. Get capsule info (sizes, count, flags)
// 4. Allocate coalesced data buffer
// 5. Copy/cache capsule data
// 6. Relocate overlapping descriptors
// 7. Store final coalesced pointer
}
/**
* High-level capsule coalesce entry from x64 thunk.
*
* Parameters:
* a1 - CapsuleLongModeBuffer [AddressLow, AddressHigh, ...]
* a2 - CapsuleX64 PE entry point (destination for thunk)
* a3 - Capsule variable list
* a4 - Reserved/Flags
* a5 - Memory resource descriptors
* a6 - [Out] Coalesced data pointer
* a7 - [In/Out] Coalesced data size
*/
EFI_STATUS
CapsuleCoalesce (
IN UINT64 *CapsuleBuffer,
IN VOID *CoalesceEntry,
IN VOID *VariableList,
IN UINT32 Reserved,
IN VOID *MemoryResources,
OUT UINTN *CoalescedData,
IN OUT UINTN *CoalescedDataSize
)
{
UINT64 CapsuleEnd;
UINT64 CapsuleStart;
BOOLEAN IsLongMode;
EFI_STATUS Status;
UINTN TotalCapsuleSize;
UINTN CapsuleImageSize;
UINT32 MemoryAttributes;
UINT8 PlatformType;
UINT8 PhysicalAddressBits;
CAPSULE_VARIABLE_ENTRY VariableEntries[16];
ZeroMem (VariableEntries, sizeof (VariableEntries));
CapsuleStart = CoalesceData;
CapsuleEnd = *CoalescedDataSize;
// Check platform capabilities
Status = GetPcdDb();
PlatformStatus = CallPpi(4);
if (PlatformStatus) {
// Determine if long mode is available
IsLongMode = TRUE;
}
// Parse capsule buffer layout
// CapsuleBuffer[0] = low address, [1] = high address
CapsuleImageSize = *CoalescedData;
TotalCapsuleSize = *CoalescedDataSize;
// Validate capsule range against system memory
// ... complex range/overlap checking ...
// Populate descriptor context
VariableEntries[0] = VariableList;
VariableEntries[2] = CapsuleBuffer + 2; // StackBase
VariableEntries[3] = CapsuleBuffer + 3;
VariableEntries[4] = CapsuleBuffer + 4;
VariableEntries[5] = CapsuleBuffer + 5;
VariableEntries[6] = CoalesceEntry;
VariableEntries[8] = Reserved; // Reserved param
VariableEntries[9] = CapsuleBuffer; // CapsuleStart
VariableEntries[10] = MemoryResources; // Memory descriptors
VariableEntries[12] = &CapsuleEnd;
VariableEntries[14] = &CapsuleImageSize;
VariableEntries[1] = 0;
VariableEntries[11] = 0;
VariableEntries[13] = 0;
VariableEntries[15] = 0;
// SGDT for GDTR save
VariableEntries[7] = 0;
Status = Thunk32To64Call (CapsuleStart, CapsuleEnd);
if (!EFI_ERROR (Status)) {
*CoalescedData = (UINTN)VariableEntries[12];
*CoalescedDataSize = VariableEntries[14];
}
return Status;
}
/*----------------------------------------------------------------------
* PE/COFF Helpers
*----------------------------------------------------------------------*/
/**
* Get entry point from PE/COFF image.
* Handles PE32+ as well as TE image formats.
*/
UINT16
GetPeCoffEntryPoint (
IN VOID *Pe32Data
)
{
ASSERT (Pe32Data != NULL);
if (*(UINT16 *)Pe32Data == EFI_IMAGE_DOS_SIGNATURE) {
Pe32Data = (VOID *)((UINT8 *)Pe32Data + *(UINT16 *)((UINT8 *)Pe32Data + 60));
}
if (*(UINT16 *)Pe32Data == EFI_IMAGE_NT_SIGNATURE) {
// PE32+ (PE32+)
return ((EFI_IMAGE_NT_HEADERS64 *)Pe32Data)->OptionalHeader.AddressOfEntryPoint;
}
if (*(UINT32 *)Pe32Data == EFI_TE_IMAGE_HEADER_SIGNATURE) {
// TE image
return ((EFI_TE_IMAGE_HEADER *)Pe32Data)->AddressOfEntryPoint;
}
return 0;
}
/*----------------------------------------------------------------------
* Module Entry Point
*----------------------------------------------------------------------*/
/**
* Capsule PEIM entry point.
*
* Installs the Capsule PPI which UEFI capsule processing uses to
* detect and coalesce capsule updates during the PEI phase.
*
* On success: Capsule PPI is installed for DXE to use.
* On failure: ASSERT on EFI error with debug output.
*/
EFI_STATUS
EFIAPI
_ModuleEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_PEI_SERVICES **PeiServices;
EFI_STATUS Status;
PeiServices = GetPeiServices();
Status = (*PeiServices)->NotifyPpi (&gCapsuleNotifyDescriptor);
if (Status == EFI_ALREADY_STARTED) {
// Already notified - this is expected on repeat calls
} else if (EFI_ERROR (Status)) {
// Install the Capsule PPI
Status = CapsuleDataCoalesceEntry (&CoalescedCapsule, &CapsuleSize);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", Status));
ASSERT_EFI_ERROR (FALSE);
}
Status = (*PeiServices)->InstallPpi (&gCapsulePpiDescriptor);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", Status));
ASSERT_EFI_ERROR (FALSE);
}
}
return EFI_SUCCESS;
}