2019-09-03 11:58:42 +02:00
/** @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 1 G page is supported .
@ retval TRUE 1 G page is supported .
@ retval FALSE 1 G 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 1 G 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 4 G page table .
@ param [ in ] PageTablesAddress The base address of page table .
@ param [ in ] Page1GSupport 1 G 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 ) ;
2020-04-23 11:08:10 +02:00
if ( ! EFI_ERROR ( Status ) ) {
2019-09-03 11:58:42 +02:00
* 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 ) ;
2020-04-23 11:08:10 +02:00
if ( EFI_ERROR ( Status ) ) {
2019-09-03 11:58:42 +02:00
return Status ;
}
Status = PeiServicesFfsFindFileByName ( PcdGetPtr ( PcdCapsuleCoalesceFile ) , VolumeHandle , & FileHandle ) ;
2020-04-23 11:08:10 +02:00
if ( ! EFI_ERROR ( Status ) ) {
2019-09-03 11:58:42 +02:00
Status = PeiServicesLocatePpi ( & gEfiPeiLoadFilePpiGuid , 0 , NULL , ( VOID * * ) & LoadFile ) ;
2020-04-23 11:08:10 +02:00
ASSERT_EFI_ERROR ( Status ) ;
2019-09-03 11:58:42 +02:00
Status = LoadFile - > LoadFile (
LoadFile ,
FileHandle ,
& CoalesceImageAddress ,
& CoalesceImageSize ,
CoalesceImageEntryPoint ,
& AuthenticationState
) ;
2020-04-23 11:08:10 +02:00
if ( EFI_ERROR ( Status ) ) {
2019-09-03 11:58:42 +02:00
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
) ;
2020-04-23 11:08:10 +02:00
ASSERT_EFI_ERROR ( Status ) ;
2019-09-03 11:58:42 +02:00
Size = sizeof ( EFI_CAPSULE_LONG_MODE_BUFFER ) ;
Status = PPIVariableServices - > GetVariable (
PPIVariableServices ,
EFI_CAPSULE_LONG_MODE_BUFFER_NAME ,
& gEfiCapsuleVendorGuid ,
NULL ,
& Size ,
LongModeBuffer
) ;
2020-04-23 11:08:10 +02:00
if ( EFI_ERROR ( Status ) ) {
2019-09-03 11:58:42 +02:00
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 ) {
2020-05-01 18:26:28 +02:00
CopyMem ( & TempMemoryResource , MemoryResourceEntry , sizeof ( MEMORY_RESOURCE_DESCRIPTOR ) ) ;
CopyMem ( MemoryResourceEntry , NextMemoryResourceEntry , sizeof ( MEMORY_RESOURCE_DESCRIPTOR ) ) ;
CopyMem ( NextMemoryResourceEntry , & TempMemoryResource , sizeof ( MEMORY_RESOURCE_DESCRIPTOR ) ) ;
2019-09-03 11:58:42 +02:00
}
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 ) {
2020-05-01 18:26:28 +02:00
CopyMem ( NewMemoryResourceEntry , MemoryResourceEntry , sizeof ( MEMORY_RESOURCE_DESCRIPTOR ) ) ;
2019-09-03 11:58:42 +02:00
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 ) ;
2020-04-23 11:08:10 +02:00
ASSERT_EFI_ERROR ( Status ) ;
2019-09-03 11:58:42 +02:00
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 ) ;
2020-04-23 11:08:10 +02:00
ASSERT_EFI_ERROR ( Status ) ;
2019-09-03 11:58:42 +02:00
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
) ;
2020-04-23 11:08:10 +02:00
if ( EFI_ERROR ( Status ) ) {
2019-09-03 11:58:42 +02:00
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
) ;
2020-04-23 11:08:10 +02:00
if ( ! EFI_ERROR ( Status ) ) {
2019-09-03 11:58:42 +02:00
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
) ;
2020-04-23 11:08:10 +02:00
if ( EFI_ERROR ( Status ) ) {
2019-09-03 11:58:42 +02:00
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 ) ;
2020-04-28 12:49:24 +02:00
TempList = AllocateZeroPool ( TempListLength ) ;
2019-09-03 11:58:42 +02:00
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
) ;
2020-04-23 11:08:10 +02:00
if ( EFI_ERROR ( Status ) ) {
2019-09-03 11:58:42 +02:00
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 ) {
2020-04-28 12:49:24 +02:00
EnlargedTempList = AllocateZeroPool ( TempListLength * 2 ) ;
2019-09-03 11:58:42 +02:00
if ( EnlargedTempList = = NULL ) {
DEBUG ( ( DEBUG_ERROR , " Fail to allocate memory! \n " ) ) ;
return EFI_OUT_OF_RESOURCES ;
}
2020-05-01 18:26:28 +02:00
CopyMem ( EnlargedTempList , TempList , TempListLength ) ;
2020-04-23 11:08:10 +02:00
FreePool ( TempList ) ;
2019-09-03 11:58:42 +02:00
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 ;
}
2020-04-28 12:49:24 +02:00
* HeadList = AllocateZeroPool ( ( ValidIndex + 1 ) * sizeof ( EFI_PHYSICAL_ADDRESS ) ) ;
2019-09-03 11:58:42 +02:00
if ( * HeadList = = NULL ) {
DEBUG ( ( DEBUG_ERROR , " Failed to allocate memory \n " ) ) ;
return EFI_OUT_OF_RESOURCES ;
}
2020-05-01 18:26:28 +02:00
CopyMem ( * HeadList , TempList , ( ValidIndex ) * sizeof ( EFI_PHYSICAL_ADDRESS ) ) ;
2019-09-03 11:58:42 +02:00
* 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 ) ;
2020-04-23 11:08:10 +02:00
if ( EFI_ERROR ( Status ) | | ( BootMode ! = BOOT_ON_FLASH_UPDATE ) ) {
2019-09-03 11:58:42 +02:00
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 ) ;
2020-04-23 11:08:10 +02:00
if ( EFI_ERROR ( Status ) | | VariableArrayAddress = = NULL ) {
2019-09-03 11:58:42 +02:00
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 ) ;
2020-04-23 11:08:10 +02:00
if ( EFI_ERROR ( Status ) ) {
2019-09-03 11:58:42 +02:00
DEBUG ( ( DEBUG_ERROR , " Fail to find the variable for long mode context! \n " ) ) ;
Status = EFI_NOT_FOUND ;
goto Done ;
}
Status = FindCapsuleCoalesceImage ( & CoalesceImageEntryPoint , & CoalesceImageMachineType ) ;
2020-04-23 11:08:10 +02:00
if ( ( EFI_ERROR ( Status ) ) | | ( CoalesceImageMachineType ! = EFI_IMAGE_MACHINE_X64 ) ) {
2019-09-03 11:58:42 +02:00
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 ) ) ;
2020-05-01 18:26:28 +02:00
CopyMem ( ( VOID * ) ( UINTN ) NewBuffer , ( VOID * ) ( UINTN ) ( ( UINT8 * ) PrivateData + sizeof ( EFI_CAPSULE_PEIM_PRIVATE_DATA ) + ( CapsuleNumber - 1 ) * sizeof ( UINT64 ) ) , Size ) ;
2019-09-03 11:58:42 +02:00
//
// 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 ) ;
}