2 Capsule update PEIM for UEFI2.0
4 Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.<BR>
5 Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR>
7 SPDX-License-Identifier: BSD-2-Clause-Patent
13 #define DEFAULT_SG_LIST_HEADS (20)
17 // Global Descriptor Table (GDT)
19 GLOBAL_REMOVE_IF_UNREFERENCED IA32_SEGMENT_DESCRIPTOR mGdtEntries
[] = {
20 /* selector { Global Segment Descriptor } */
21 /* 0x00 */ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, //null descriptor
22 /* 0x08 */ {{0xffff, 0, 0, 0x3, 1, 0, 1, 0xf, 0, 0, 1, 1, 0}}, //linear data segment descriptor
23 /* 0x10 */ {{0xffff, 0, 0, 0xf, 1, 0, 1, 0xf, 0, 0, 1, 1, 0}}, //linear code segment descriptor
24 /* 0x18 */ {{0xffff, 0, 0, 0x3, 1, 0, 1, 0xf, 0, 0, 1, 1, 0}}, //system data segment descriptor
25 /* 0x20 */ {{0xffff, 0, 0, 0xb, 1, 0, 1, 0xf, 0, 0, 1, 1, 0}}, //system code segment descriptor
26 /* 0x28 */ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, //spare segment descriptor
27 /* 0x30 */ {{0xffff, 0, 0, 0x3, 1, 0, 1, 0xf, 0, 0, 1, 1, 0}}, //system data segment descriptor
28 /* 0x38 */ {{0xffff, 0, 0, 0xb, 1, 0, 1, 0xf, 0, 1, 0, 1, 0}}, //system code segment descriptor
29 /* 0x40 */ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, //spare segment descriptor
35 GLOBAL_REMOVE_IF_UNREFERENCED CONST IA32_DESCRIPTOR mGdt
= {
36 sizeof (mGdtEntries
) - 1,
42 The function will check if 1G page is supported.
44 @retval TRUE 1G page is supported.
45 @retval FALSE 1G page is not supported.
55 BOOLEAN Page1GSupport
;
57 Page1GSupport
= FALSE
;
58 if (PcdGetBool(PcdUse1GPageTable
)) {
59 AsmCpuid (0x80000000, &RegEax
, NULL
, NULL
, NULL
);
60 if (RegEax
>= 0x80000001) {
61 AsmCpuid (0x80000001, NULL
, NULL
, NULL
, &RegEdx
);
62 if ((RegEdx
& BIT26
) != 0) {
72 Calculate the total size of page table.
74 @param[in] Page1GSupport 1G page support or not.
76 @return The size of page table.
80 CalculatePageTableSize (
81 IN BOOLEAN Page1GSupport
84 UINTN ExtraPageTablePages
;
86 UINT8 PhysicalAddressBits
;
87 UINT32 NumberOfPml4EntriesNeeded
;
88 UINT32 NumberOfPdpEntriesNeeded
;
91 // Create 4G page table by default,
92 // and let PF handler to handle > 4G request.
94 PhysicalAddressBits
= 32;
95 ExtraPageTablePages
= EXTRA_PAGE_TABLE_PAGES
;
98 // Calculate the table entries needed.
100 if (PhysicalAddressBits
<= 39 ) {
101 NumberOfPml4EntriesNeeded
= 1;
102 NumberOfPdpEntriesNeeded
= (UINT32
)LShiftU64 (1, (PhysicalAddressBits
- 30));
104 NumberOfPml4EntriesNeeded
= (UINT32
)LShiftU64 (1, (PhysicalAddressBits
- 39));
105 NumberOfPdpEntriesNeeded
= 512;
108 if (!Page1GSupport
) {
109 TotalPagesNum
= (NumberOfPdpEntriesNeeded
+ 1) * NumberOfPml4EntriesNeeded
+ 1;
111 TotalPagesNum
= NumberOfPml4EntriesNeeded
+ 1;
113 TotalPagesNum
+= ExtraPageTablePages
;
115 return EFI_PAGES_TO_SIZE (TotalPagesNum
);
119 Allocates and fills in the Page Directory and Page Table Entries to
120 establish a 4G page table.
122 @param[in] PageTablesAddress The base address of page table.
123 @param[in] Page1GSupport 1G page support or not.
128 IN EFI_PHYSICAL_ADDRESS PageTablesAddress
,
129 IN BOOLEAN Page1GSupport
132 UINT8 PhysicalAddressBits
;
133 EFI_PHYSICAL_ADDRESS PageAddress
;
134 UINTN IndexOfPml4Entries
;
135 UINTN IndexOfPdpEntries
;
136 UINTN IndexOfPageDirectoryEntries
;
137 UINT32 NumberOfPml4EntriesNeeded
;
138 UINT32 NumberOfPdpEntriesNeeded
;
139 PAGE_MAP_AND_DIRECTORY_POINTER
*PageMapLevel4Entry
;
140 PAGE_MAP_AND_DIRECTORY_POINTER
*PageMap
;
141 PAGE_MAP_AND_DIRECTORY_POINTER
*PageDirectoryPointerEntry
;
142 PAGE_TABLE_ENTRY
*PageDirectoryEntry
;
143 UINTN BigPageAddress
;
144 PAGE_TABLE_1G_ENTRY
*PageDirectory1GEntry
;
145 UINT64 AddressEncMask
;
148 // Make sure AddressEncMask is contained to smallest supported address field.
150 AddressEncMask
= PcdGet64 (PcdPteMemoryEncryptionAddressOrMask
) & PAGING_1G_ADDRESS_MASK_64
;
153 // Create 4G page table by default,
154 // and let PF handler to handle > 4G request.
156 PhysicalAddressBits
= 32;
159 // Calculate the table entries needed.
161 if (PhysicalAddressBits
<= 39 ) {
162 NumberOfPml4EntriesNeeded
= 1;
163 NumberOfPdpEntriesNeeded
= (UINT32
)LShiftU64 (1, (PhysicalAddressBits
- 30));
165 NumberOfPml4EntriesNeeded
= (UINT32
)LShiftU64 (1, (PhysicalAddressBits
- 39));
166 NumberOfPdpEntriesNeeded
= 512;
170 // Pre-allocate big pages to avoid later allocations.
172 BigPageAddress
= (UINTN
) PageTablesAddress
;
175 // By architecture only one PageMapLevel4 exists - so lets allocate storage for it.
177 PageMap
= (VOID
*) BigPageAddress
;
178 BigPageAddress
+= SIZE_4KB
;
180 PageMapLevel4Entry
= PageMap
;
182 for (IndexOfPml4Entries
= 0; IndexOfPml4Entries
< NumberOfPml4EntriesNeeded
; IndexOfPml4Entries
++, PageMapLevel4Entry
++) {
184 // Each PML4 entry points to a page of Page Directory Pointer entires.
185 // So lets allocate space for them and fill them in in the IndexOfPdpEntries loop.
187 PageDirectoryPointerEntry
= (VOID
*) BigPageAddress
;
188 BigPageAddress
+= SIZE_4KB
;
193 PageMapLevel4Entry
->Uint64
= (UINT64
)(UINTN
)PageDirectoryPointerEntry
| AddressEncMask
;
194 PageMapLevel4Entry
->Bits
.ReadWrite
= 1;
195 PageMapLevel4Entry
->Bits
.Present
= 1;
198 PageDirectory1GEntry
= (VOID
*) PageDirectoryPointerEntry
;
200 for (IndexOfPageDirectoryEntries
= 0; IndexOfPageDirectoryEntries
< 512; IndexOfPageDirectoryEntries
++, PageDirectory1GEntry
++, PageAddress
+= SIZE_1GB
) {
202 // Fill in the Page Directory entries
204 PageDirectory1GEntry
->Uint64
= (UINT64
)PageAddress
| AddressEncMask
;
205 PageDirectory1GEntry
->Bits
.ReadWrite
= 1;
206 PageDirectory1GEntry
->Bits
.Present
= 1;
207 PageDirectory1GEntry
->Bits
.MustBe1
= 1;
210 for (IndexOfPdpEntries
= 0; IndexOfPdpEntries
< NumberOfPdpEntriesNeeded
; IndexOfPdpEntries
++, PageDirectoryPointerEntry
++) {
212 // Each Directory Pointer entries points to a page of Page Directory entires.
213 // So allocate space for them and fill them in in the IndexOfPageDirectoryEntries loop.
215 PageDirectoryEntry
= (VOID
*) BigPageAddress
;
216 BigPageAddress
+= SIZE_4KB
;
219 // Fill in a Page Directory Pointer Entries
221 PageDirectoryPointerEntry
->Uint64
= (UINT64
)(UINTN
)PageDirectoryEntry
| AddressEncMask
;
222 PageDirectoryPointerEntry
->Bits
.ReadWrite
= 1;
223 PageDirectoryPointerEntry
->Bits
.Present
= 1;
225 for (IndexOfPageDirectoryEntries
= 0; IndexOfPageDirectoryEntries
< 512; IndexOfPageDirectoryEntries
++, PageDirectoryEntry
++, PageAddress
+= SIZE_2MB
) {
227 // Fill in the Page Directory entries
229 PageDirectoryEntry
->Uint64
= (UINT64
)PageAddress
| AddressEncMask
;
230 PageDirectoryEntry
->Bits
.ReadWrite
= 1;
231 PageDirectoryEntry
->Bits
.Present
= 1;
232 PageDirectoryEntry
->Bits
.MustBe1
= 1;
236 for (; IndexOfPdpEntries
< 512; IndexOfPdpEntries
++, PageDirectoryPointerEntry
++) {
238 PageDirectoryPointerEntry
,
239 sizeof(PAGE_MAP_AND_DIRECTORY_POINTER
)
246 // For the PML4 entries we are not using fill in a null entry.
248 for (; IndexOfPml4Entries
< 512; IndexOfPml4Entries
++, PageMapLevel4Entry
++) {
251 sizeof (PAGE_MAP_AND_DIRECTORY_POINTER
)
257 Return function from long mode to 32-bit mode.
259 @param EntrypointContext Context for mode switching
260 @param ReturnContext Context for mode switching
265 SWITCH_32_TO_64_CONTEXT
*EntrypointContext
,
266 SWITCH_64_TO_32_CONTEXT
*ReturnContext
270 // Restore original GDT
272 AsmWriteGdtr (&ReturnContext
->Gdtr
);
275 // return to original caller
277 LongJump ((BASE_LIBRARY_JUMP_BUFFER
*)(UINTN
)EntrypointContext
->JumpBuffer
, 1);
286 Thunk function from 32-bit protection mode to long mode.
288 @param PageTableAddress Page table base address
289 @param Context Context for mode switching
290 @param ReturnContext Context for mode switching
292 @retval EFI_SUCCESS Function successfully executed.
297 EFI_PHYSICAL_ADDRESS PageTableAddress
,
298 SWITCH_32_TO_64_CONTEXT
*Context
,
299 SWITCH_64_TO_32_CONTEXT
*ReturnContext
306 // Save return address, LongJump will return here then
308 SetJumpFlag
= SetJump ((BASE_LIBRARY_JUMP_BUFFER
*) (UINTN
) Context
->JumpBuffer
);
310 if (SetJumpFlag
== 0) {
313 // Build 4G Page Tables.
315 Create4GPageTables (PageTableAddress
, Context
->Page1GSupport
);
320 AsmWriteGdtr (&mGdt
);
325 AsmWriteCr3 ((UINTN
) PageTableAddress
);
329 "%a() Stack Base: 0x%lx, Stack Size: 0x%lx\n",
331 Context
->StackBufferBase
,
332 Context
->StackBufferLength
336 // Disable interrupt of Debug timer, since the IDT table cannot work in long mode
338 SaveAndSetDebugTimerInterrupt (FALSE
);
340 // Transfer to long mode
344 (UINT64
) Context
->EntryPoint
,
345 (UINT64
)(UINTN
) Context
,
346 (UINT64
)(UINTN
) ReturnContext
,
347 Context
->StackBufferBase
+ Context
->StackBufferLength
352 // Convert to 32-bit Status and return
354 Status
= EFI_SUCCESS
;
355 if ((UINTN
) ReturnContext
->ReturnStatus
!= 0) {
356 Status
= ENCODE_ERROR ((UINTN
) ReturnContext
->ReturnStatus
);
363 If in 32 bit protection mode, and coalesce image is of X64, switch to long mode.
365 @param LongModeBuffer The context of long mode.
366 @param CoalesceEntry Entry of coalesce image.
367 @param BlockListAddr Address of block list.
368 @param MemoryResource Pointer to the buffer of memory resource descriptor.
369 @param MemoryBase Base of memory range.
370 @param MemorySize Size of memory range.
372 @retval EFI_SUCCESS Successfully switched to long mode and execute coalesce.
373 @retval Others Failed to execute coalesce in long mode.
378 IN EFI_CAPSULE_LONG_MODE_BUFFER
*LongModeBuffer
,
379 IN COALESCE_ENTRY CoalesceEntry
,
380 IN EFI_PHYSICAL_ADDRESS BlockListAddr
,
381 IN MEMORY_RESOURCE_DESCRIPTOR
*MemoryResource
,
382 IN OUT VOID
**MemoryBase
,
383 IN OUT UINTN
*MemorySize
387 EFI_PHYSICAL_ADDRESS MemoryBase64
;
389 EFI_PHYSICAL_ADDRESS MemoryEnd64
;
390 SWITCH_32_TO_64_CONTEXT Context
;
391 SWITCH_64_TO_32_CONTEXT ReturnContext
;
392 BASE_LIBRARY_JUMP_BUFFER JumpBuffer
;
393 EFI_PHYSICAL_ADDRESS ReservedRangeBase
;
394 EFI_PHYSICAL_ADDRESS ReservedRangeEnd
;
395 BOOLEAN Page1GSupport
;
397 ZeroMem (&Context
, sizeof (SWITCH_32_TO_64_CONTEXT
));
398 ZeroMem (&ReturnContext
, sizeof (SWITCH_64_TO_32_CONTEXT
));
400 MemoryBase64
= (UINT64
) (UINTN
) *MemoryBase
;
401 MemorySize64
= (UINT64
) (UINTN
) *MemorySize
;
402 MemoryEnd64
= MemoryBase64
+ MemorySize64
;
404 Page1GSupport
= IsPage1GSupport ();
407 // Merge memory range reserved for stack and page table
409 if (LongModeBuffer
->StackBaseAddress
< LongModeBuffer
->PageTableAddress
) {
410 ReservedRangeBase
= LongModeBuffer
->StackBaseAddress
;
411 ReservedRangeEnd
= LongModeBuffer
->PageTableAddress
+ CalculatePageTableSize (Page1GSupport
);
413 ReservedRangeBase
= LongModeBuffer
->PageTableAddress
;
414 ReservedRangeEnd
= LongModeBuffer
->StackBaseAddress
+ LongModeBuffer
->StackSize
;
418 // Check if memory range reserved is overlap with MemoryBase ~ MemoryBase + MemorySize.
419 // If they are overlapped, get a larger range to process capsule data.
421 if (ReservedRangeBase
<= MemoryBase64
) {
422 if (ReservedRangeEnd
< MemoryEnd64
) {
423 MemoryBase64
= ReservedRangeEnd
;
425 DEBUG ((DEBUG_ERROR
, "Memory is not enough to process capsule!\n"));
426 return EFI_OUT_OF_RESOURCES
;
428 } else if (ReservedRangeBase
< MemoryEnd64
) {
429 if (ReservedRangeEnd
< MemoryEnd64
&&
430 ReservedRangeBase
- MemoryBase64
< MemoryEnd64
- ReservedRangeEnd
) {
431 MemoryBase64
= ReservedRangeEnd
;
433 MemorySize64
= (UINT64
)(UINTN
)(ReservedRangeBase
- MemoryBase64
);
438 // Initialize context jumping to 64-bit enviroment
440 Context
.JumpBuffer
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)&JumpBuffer
;
441 Context
.StackBufferBase
= LongModeBuffer
->StackBaseAddress
;
442 Context
.StackBufferLength
= LongModeBuffer
->StackSize
;
443 Context
.EntryPoint
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)CoalesceEntry
;
444 Context
.BlockListAddr
= BlockListAddr
;
445 Context
.MemoryResource
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)MemoryResource
;
446 Context
.MemoryBase64Ptr
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)&MemoryBase64
;
447 Context
.MemorySize64Ptr
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)&MemorySize64
;
448 Context
.Page1GSupport
= Page1GSupport
;
449 Context
.AddressEncMask
= PcdGet64 (PcdPteMemoryEncryptionAddressOrMask
) & PAGING_1G_ADDRESS_MASK_64
;
452 // Prepare data for return back
454 ReturnContext
.ReturnCs
= 0x10;
455 ReturnContext
.ReturnEntryPoint
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)ReturnFunction
;
457 // Will save the return status of processing capsule
459 ReturnContext
.ReturnStatus
= 0;
464 AsmReadGdtr ((IA32_DESCRIPTOR
*)&ReturnContext
.Gdtr
);
466 Status
= Thunk32To64 (LongModeBuffer
->PageTableAddress
, &Context
, &ReturnContext
);
468 if (!EFI_ERROR (Status
)) {
469 *MemoryBase
= (VOID
*) (UINTN
) MemoryBase64
;
470 *MemorySize
= (UINTN
) MemorySize64
;
478 Locates the coalesce image entry point, and detects its machine type.
480 @param CoalesceImageEntryPoint Pointer to coalesce image entry point for output.
481 @param CoalesceImageMachineType Pointer to machine type of coalesce image.
483 @retval EFI_SUCCESS Coalesce image successfully located.
484 @retval Others Failed to locate the coalesce image.
488 FindCapsuleCoalesceImage (
489 OUT EFI_PHYSICAL_ADDRESS
*CoalesceImageEntryPoint
,
490 OUT UINT16
*CoalesceImageMachineType
495 EFI_PEI_LOAD_FILE_PPI
*LoadFile
;
496 EFI_PEI_FV_HANDLE VolumeHandle
;
497 EFI_PEI_FILE_HANDLE FileHandle
;
498 EFI_PHYSICAL_ADDRESS CoalesceImageAddress
;
499 UINT64 CoalesceImageSize
;
500 UINT32 AuthenticationState
;
505 Status
= PeiServicesFfsFindNextVolume (Instance
++, &VolumeHandle
);
506 if (EFI_ERROR (Status
)) {
509 Status
= PeiServicesFfsFindFileByName (PcdGetPtr(PcdCapsuleCoalesceFile
), VolumeHandle
, &FileHandle
);
510 if (!EFI_ERROR (Status
)) {
511 Status
= PeiServicesLocatePpi (&gEfiPeiLoadFilePpiGuid
, 0, NULL
, (VOID
**) &LoadFile
);
512 ASSERT_EFI_ERROR (Status
);
514 Status
= LoadFile
->LoadFile (
517 &CoalesceImageAddress
,
519 CoalesceImageEntryPoint
,
522 if (EFI_ERROR (Status
)) {
523 DEBUG ((DEBUG_ERROR
, "Unable to find PE32 section in CapsuleX64 image ffs %r!\n", Status
));
526 *CoalesceImageMachineType
= PeCoffLoaderGetMachineType ((VOID
*) (UINTN
) CoalesceImageAddress
);
537 Gets the reserved long mode buffer.
539 @param LongModeBuffer Pointer to the long mode buffer for output.
541 @retval EFI_SUCCESS Long mode buffer successfully retrieved.
542 @retval Others Variable storing long mode buffer not found.
547 OUT EFI_CAPSULE_LONG_MODE_BUFFER
*LongModeBuffer
552 EFI_PEI_READ_ONLY_VARIABLE2_PPI
*PPIVariableServices
;
554 Status
= PeiServicesLocatePpi (
555 &gEfiPeiReadOnlyVariable2PpiGuid
,
558 (VOID
**) &PPIVariableServices
560 ASSERT_EFI_ERROR (Status
);
562 Size
= sizeof (EFI_CAPSULE_LONG_MODE_BUFFER
);
563 Status
= PPIVariableServices
->GetVariable (
565 EFI_CAPSULE_LONG_MODE_BUFFER_NAME
,
566 &gEfiCapsuleVendorGuid
,
571 if (EFI_ERROR (Status
)) {
572 DEBUG (( DEBUG_ERROR
, "Error Get LongModeBuffer variable %r!\n", Status
));
578 #if defined (MDE_CPU_IA32) || defined (MDE_CPU_X64)
580 Get physical address bits.
582 @return Physical address bits.
586 GetPhysicalAddressBits (
591 UINT8 PhysicalAddressBits
;
595 // Get physical address bits supported.
597 Hob
= GetFirstHob (EFI_HOB_TYPE_CPU
);
599 PhysicalAddressBits
= ((EFI_HOB_CPU
*) Hob
)->SizeOfMemorySpace
;
601 AsmCpuid (0x80000000, &RegEax
, NULL
, NULL
, NULL
);
602 if (RegEax
>= 0x80000008) {
603 AsmCpuid (0x80000008, &RegEax
, NULL
, NULL
, NULL
);
604 PhysicalAddressBits
= (UINT8
) RegEax
;
606 PhysicalAddressBits
= 36;
611 // IA-32e paging translates 48-bit linear addresses to 52-bit physical addresses.
613 ASSERT (PhysicalAddressBits
<= 52);
614 if (PhysicalAddressBits
> 48) {
615 PhysicalAddressBits
= 48;
618 return PhysicalAddressBits
;
623 Sort memory resource entries based upon PhysicalStart, from low to high.
625 @param[in, out] MemoryResource A pointer to the memory resource entry buffer.
629 SortMemoryResourceDescriptor (
630 IN OUT MEMORY_RESOURCE_DESCRIPTOR
*MemoryResource
633 MEMORY_RESOURCE_DESCRIPTOR
*MemoryResourceEntry
;
634 MEMORY_RESOURCE_DESCRIPTOR
*NextMemoryResourceEntry
;
635 MEMORY_RESOURCE_DESCRIPTOR TempMemoryResource
;
637 MemoryResourceEntry
= MemoryResource
;
638 NextMemoryResourceEntry
= MemoryResource
+ 1;
639 while (MemoryResourceEntry
->ResourceLength
!= 0) {
640 while (NextMemoryResourceEntry
->ResourceLength
!= 0) {
641 if (MemoryResourceEntry
->PhysicalStart
> NextMemoryResourceEntry
->PhysicalStart
) {
642 CopyMem (&TempMemoryResource
, MemoryResourceEntry
, sizeof (MEMORY_RESOURCE_DESCRIPTOR
));
643 CopyMem (MemoryResourceEntry
, NextMemoryResourceEntry
, sizeof (MEMORY_RESOURCE_DESCRIPTOR
));
644 CopyMem (NextMemoryResourceEntry
, &TempMemoryResource
, sizeof (MEMORY_RESOURCE_DESCRIPTOR
));
647 NextMemoryResourceEntry
= NextMemoryResourceEntry
+ 1;
650 MemoryResourceEntry
= MemoryResourceEntry
+ 1;
651 NextMemoryResourceEntry
= MemoryResourceEntry
+ 1;
656 Merge continous memory resource entries.
658 @param[in, out] MemoryResource A pointer to the memory resource entry buffer.
662 MergeMemoryResourceDescriptor (
663 IN OUT MEMORY_RESOURCE_DESCRIPTOR
*MemoryResource
666 MEMORY_RESOURCE_DESCRIPTOR
*MemoryResourceEntry
;
667 MEMORY_RESOURCE_DESCRIPTOR
*NewMemoryResourceEntry
;
668 MEMORY_RESOURCE_DESCRIPTOR
*NextMemoryResourceEntry
;
669 MEMORY_RESOURCE_DESCRIPTOR
*MemoryResourceEnd
;
671 MemoryResourceEntry
= MemoryResource
;
672 NewMemoryResourceEntry
= MemoryResource
;
673 while (MemoryResourceEntry
->ResourceLength
!= 0) {
674 CopyMem (NewMemoryResourceEntry
, MemoryResourceEntry
, sizeof (MEMORY_RESOURCE_DESCRIPTOR
));
675 NextMemoryResourceEntry
= MemoryResourceEntry
+ 1;
677 while ((NextMemoryResourceEntry
->ResourceLength
!= 0) &&
678 (NextMemoryResourceEntry
->PhysicalStart
== (MemoryResourceEntry
->PhysicalStart
+ MemoryResourceEntry
->ResourceLength
))) {
679 MemoryResourceEntry
->ResourceLength
+= NextMemoryResourceEntry
->ResourceLength
;
680 if (NewMemoryResourceEntry
!= MemoryResourceEntry
) {
681 NewMemoryResourceEntry
->ResourceLength
+= NextMemoryResourceEntry
->ResourceLength
;
684 NextMemoryResourceEntry
= NextMemoryResourceEntry
+ 1;
687 MemoryResourceEntry
= NextMemoryResourceEntry
;
688 NewMemoryResourceEntry
= NewMemoryResourceEntry
+ 1;
692 // Set NULL terminate memory resource descriptor after merging.
694 MemoryResourceEnd
= NewMemoryResourceEntry
;
695 ZeroMem (MemoryResourceEnd
, sizeof (MEMORY_RESOURCE_DESCRIPTOR
));
699 Build memory resource descriptor from resource descriptor in HOB list.
701 @return Pointer to the buffer of memory resource descriptor.
702 NULL if no memory resource descriptor reported in HOB list
703 before capsule Coalesce.
706 MEMORY_RESOURCE_DESCRIPTOR
*
707 BuildMemoryResourceDescriptor (
711 EFI_PEI_HOB_POINTERS Hob
;
713 EFI_HOB_RESOURCE_DESCRIPTOR
*ResourceDescriptor
;
714 MEMORY_RESOURCE_DESCRIPTOR
*MemoryResource
;
718 // Get the count of memory resource descriptor.
721 Hob
.Raw
= GetFirstHob (EFI_HOB_TYPE_RESOURCE_DESCRIPTOR
);
722 while (Hob
.Raw
!= NULL
) {
723 ResourceDescriptor
= (EFI_HOB_RESOURCE_DESCRIPTOR
*) Hob
.Raw
;
724 if (ResourceDescriptor
->ResourceType
== EFI_RESOURCE_SYSTEM_MEMORY
) {
727 Hob
.Raw
= GET_NEXT_HOB (Hob
);
728 Hob
.Raw
= GetNextHob (EFI_HOB_TYPE_RESOURCE_DESCRIPTOR
, Hob
.Raw
);
732 DEBUG ((DEBUG_INFO
| DEBUG_WARN
, "No memory resource descriptor reported in HOB list before capsule Coalesce\n"));
733 #if defined (MDE_CPU_IA32) || defined (MDE_CPU_X64)
735 // Allocate memory to hold memory resource descriptor,
736 // include extra one NULL terminate memory resource descriptor.
738 Status
= PeiServicesAllocatePool ((1 + 1) * sizeof (MEMORY_RESOURCE_DESCRIPTOR
), (VOID
**) &MemoryResource
);
739 ASSERT_EFI_ERROR (Status
);
740 ZeroMem (MemoryResource
, (1 + 1) * sizeof (MEMORY_RESOURCE_DESCRIPTOR
));
742 MemoryResource
[0].PhysicalStart
= 0;
743 MemoryResource
[0].ResourceLength
= LShiftU64 (1, GetPhysicalAddressBits ());
744 DEBUG ((DEBUG_INFO
, "MemoryResource[0x0] - Start(0x%0lx) Length(0x%0lx)\n",
745 MemoryResource
[0x0].PhysicalStart
, MemoryResource
[0x0].ResourceLength
));
746 return MemoryResource
;
753 // Allocate memory to hold memory resource descriptor,
754 // include extra one NULL terminate memory resource descriptor.
756 Status
= PeiServicesAllocatePool ((Index
+ 1) * sizeof (MEMORY_RESOURCE_DESCRIPTOR
), (VOID
**) &MemoryResource
);
757 ASSERT_EFI_ERROR (Status
);
758 ZeroMem (MemoryResource
, (Index
+ 1) * sizeof (MEMORY_RESOURCE_DESCRIPTOR
));
761 // Get the content of memory resource descriptor.
764 Hob
.Raw
= GetFirstHob (EFI_HOB_TYPE_RESOURCE_DESCRIPTOR
);
765 while (Hob
.Raw
!= NULL
) {
766 ResourceDescriptor
= (EFI_HOB_RESOURCE_DESCRIPTOR
*) Hob
.Raw
;
767 if (ResourceDescriptor
->ResourceType
== EFI_RESOURCE_SYSTEM_MEMORY
) {
768 DEBUG ((DEBUG_INFO
, "MemoryResource[0x%x] - Start(0x%0lx) Length(0x%0lx)\n",
769 Index
, ResourceDescriptor
->PhysicalStart
, ResourceDescriptor
->ResourceLength
));
770 MemoryResource
[Index
].PhysicalStart
= ResourceDescriptor
->PhysicalStart
;
771 MemoryResource
[Index
].ResourceLength
= ResourceDescriptor
->ResourceLength
;
774 Hob
.Raw
= GET_NEXT_HOB (Hob
);
775 Hob
.Raw
= GetNextHob (EFI_HOB_TYPE_RESOURCE_DESCRIPTOR
, Hob
.Raw
);
778 SortMemoryResourceDescriptor (MemoryResource
);
779 MergeMemoryResourceDescriptor (MemoryResource
);
781 DEBUG ((DEBUG_INFO
, "Dump MemoryResource[] after sorted and merged\n"));
782 for (Index
= 0; MemoryResource
[Index
].ResourceLength
!= 0; Index
++) {
785 " MemoryResource[0x%x] - Start(0x%0lx) Length(0x%0lx)\n",
787 MemoryResource
[Index
].PhysicalStart
,
788 MemoryResource
[Index
].ResourceLength
792 return MemoryResource
;
796 Check if the capsules are staged.
798 @retval TRUE The capsules are staged.
799 @retval FALSE The capsules are not staged.
809 EFI_PEI_READ_ONLY_VARIABLE2_PPI
*PPIVariableServices
;
810 EFI_PHYSICAL_ADDRESS CapsuleDataPtr64
;
812 CapsuleDataPtr64
= 0;
814 Status
= PeiServicesLocatePpi(
815 &gEfiPeiReadOnlyVariable2PpiGuid
,
818 (VOID
**)&PPIVariableServices
821 if (EFI_ERROR (Status
)) {
822 DEBUG ((DEBUG_ERROR
, "Failed to find ReadOnlyVariable2PPI\n"));
827 // Check for Update capsule
829 Size
= sizeof (CapsuleDataPtr64
);
830 Status
= PPIVariableServices
->GetVariable (
832 EFI_CAPSULE_VARIABLE_NAME
,
833 &gEfiCapsuleVendorGuid
,
836 (VOID
*)&CapsuleDataPtr64
839 if (!EFI_ERROR (Status
)) {
847 Check all the variables for SG list heads and get the count and addresses.
849 @param ListLength A pointer would return the SG list length.
850 @param HeadList A ponter to the capsule SG list.
852 @retval EFI_SUCCESS a valid capsule is present
853 @retval EFI_NOT_FOUND if a valid capsule is not present
854 @retval EFI_INVALID_PARAMETER the input parameter is invalid
855 @retval EFI_OUT_OF_RESOURCES fail to allocate memory
859 GetScatterGatherHeadEntries (
860 OUT UINTN
*ListLength
,
861 OUT EFI_PHYSICAL_ADDRESS
**HeadList
870 CHAR16 CapsuleVarName
[30];
872 EFI_PHYSICAL_ADDRESS CapsuleDataPtr64
;
873 EFI_PEI_READ_ONLY_VARIABLE2_PPI
*PPIVariableServices
;
874 EFI_PHYSICAL_ADDRESS
*TempList
;
875 EFI_PHYSICAL_ADDRESS
*EnlargedTempList
;
876 UINTN TempListLength
;
880 CapsuleVarName
[0] = 0;
882 CapsuleDataPtr64
= 0;
884 if ((ListLength
== NULL
) || (HeadList
== NULL
)) {
885 DEBUG ((DEBUG_ERROR
, "%a Invalid parameters. Inputs can't be NULL\n", __FUNCTION__
));
886 ASSERT (ListLength
!= NULL
);
887 ASSERT (HeadList
!= NULL
);
888 return EFI_INVALID_PARAMETER
;
894 Status
= PeiServicesLocatePpi (
895 &gEfiPeiReadOnlyVariable2PpiGuid
,
898 (VOID
**)&PPIVariableServices
901 if (EFI_ERROR (Status
)) {
902 DEBUG ((DEBUG_ERROR
, "Failed to find ReadOnlyVariable2PPI\n"));
907 // Allocate memory for sg list head
909 TempListLength
= DEFAULT_SG_LIST_HEADS
* sizeof (EFI_PHYSICAL_ADDRESS
);
910 TempList
= AllocateZeroPool (TempListLength
);
911 if (TempList
== NULL
) {
912 DEBUG((DEBUG_ERROR
, "Failed to allocate memory\n"));
913 return EFI_OUT_OF_RESOURCES
;
917 // setup var name buffer for update capsules
919 StrCpyS (CapsuleVarName
, sizeof (CapsuleVarName
) / sizeof (CHAR16
), EFI_CAPSULE_VARIABLE_NAME
);
920 TempVarName
= CapsuleVarName
+ StrLen (CapsuleVarName
);
923 UnicodeValueToStringS (
925 (sizeof (CapsuleVarName
) - ((UINTN
)TempVarName
- (UINTN
)CapsuleVarName
)),
931 Size
= sizeof (CapsuleDataPtr64
);
932 Status
= PPIVariableServices
->GetVariable (
935 &gEfiCapsuleVendorGuid
,
938 (VOID
*)&CapsuleDataPtr64
941 if (EFI_ERROR (Status
)) {
942 if (Status
!= EFI_NOT_FOUND
) {
943 DEBUG ((DEBUG_ERROR
, "Unexpected error getting Capsule Update variable. Status = %r\n"));
949 // If this BlockList has been linked before, skip this variable
952 for (TempIndex
= 0; TempIndex
< ValidIndex
; TempIndex
++) {
953 if (TempList
[TempIndex
] == CapsuleDataPtr64
) {
964 // The TempList is full, enlarge it
966 if ((ValidIndex
+ 1) >= TempListLength
) {
967 EnlargedTempList
= AllocateZeroPool (TempListLength
* 2);
968 CopyMem (EnlargedTempList
, TempList
, TempListLength
);
970 TempList
= EnlargedTempList
;
975 // add it to the cached list
977 TempList
[ValidIndex
++] = CapsuleDataPtr64
;
981 if (ValidIndex
== 0) {
982 DEBUG ((DEBUG_ERROR
, "%a didn't find any SG lists in variables\n", __FUNCTION__
));
983 return EFI_NOT_FOUND
;
986 *HeadList
= AllocateZeroPool ((ValidIndex
+ 1) * sizeof (EFI_PHYSICAL_ADDRESS
));
987 if (*HeadList
== NULL
) {
988 DEBUG ((DEBUG_ERROR
, "Failed to allocate memory\n"));
989 return EFI_OUT_OF_RESOURCES
;
992 CopyMem (*HeadList
, TempList
, (ValidIndex
) * sizeof (EFI_PHYSICAL_ADDRESS
));
993 *ListLength
= ValidIndex
;
999 Capsule PPI service to coalesce a fragmented capsule in memory.
1001 @param PeiServices General purpose services available to every PEIM.
1002 @param MemoryBase Pointer to the base of a block of memory that we can walk
1003 all over while trying to coalesce our buffers.
1004 On output, this variable will hold the base address of
1005 a coalesced capsule.
1006 @param MemorySize Size of the memory region pointed to by MemoryBase.
1007 On output, this variable will contain the size of the
1010 @retval EFI_NOT_FOUND if we can't determine the boot mode
1011 if the boot mode is not flash-update
1012 if we could not find the capsule descriptors
1014 @retval EFI_BUFFER_TOO_SMALL
1015 if we could not coalesce the capsule in the memory
1016 region provided to us
1018 @retval EFI_SUCCESS if there's no capsule, or if we processed the
1019 capsule successfully.
1024 IN EFI_PEI_SERVICES
**PeiServices
,
1025 IN OUT VOID
**MemoryBase
,
1026 IN OUT UINTN
*MemorySize
1030 EFI_BOOT_MODE BootMode
;
1032 EFI_PHYSICAL_ADDRESS
*VariableArrayAddress
;
1033 MEMORY_RESOURCE_DESCRIPTOR
*MemoryResource
;
1035 UINT16 CoalesceImageMachineType
;
1036 EFI_PHYSICAL_ADDRESS CoalesceImageEntryPoint
;
1037 COALESCE_ENTRY CoalesceEntry
;
1038 EFI_CAPSULE_LONG_MODE_BUFFER LongModeBuffer
;
1042 VariableArrayAddress
= NULL
;
1045 // Someone should have already ascertained the boot mode. If it's not
1046 // capsule update, then return normally.
1048 Status
= PeiServicesGetBootMode (&BootMode
);
1049 if (EFI_ERROR (Status
) || (BootMode
!= BOOT_ON_FLASH_UPDATE
)) {
1050 DEBUG ((DEBUG_ERROR
, "Boot mode is not correct for capsule update path.\n"));
1051 Status
= EFI_NOT_FOUND
;
1056 // Get SG list entries
1058 Status
= GetScatterGatherHeadEntries (&ListLength
, &VariableArrayAddress
);
1059 if (EFI_ERROR (Status
)) {
1060 DEBUG ((DEBUG_ERROR
, "%a failed to get Scatter Gather List Head Entries. Status = %r\n", __FUNCTION__
, Status
));
1064 MemoryResource
= BuildMemoryResourceDescriptor ();
1067 if (FeaturePcdGet (PcdDxeIplSwitchToLongMode
)) {
1069 // Switch to 64-bit mode to process capsule data when:
1070 // 1. When DXE phase is 64-bit
1071 // 2. When the buffer for 64-bit transition exists
1072 // 3. When Capsule X64 image is built in BIOS image
1073 // In 64-bit mode, we can process capsule data above 4GB.
1075 CoalesceImageEntryPoint
= 0;
1076 Status
= GetLongModeContext (&LongModeBuffer
);
1077 if (EFI_ERROR (Status
)) {
1078 DEBUG ((DEBUG_ERROR
, "Fail to find the variable for long mode context!\n"));
1079 Status
= EFI_NOT_FOUND
;
1083 Status
= FindCapsuleCoalesceImage (&CoalesceImageEntryPoint
, &CoalesceImageMachineType
);
1084 if ((EFI_ERROR (Status
)) || (CoalesceImageMachineType
!= EFI_IMAGE_MACHINE_X64
)) {
1085 DEBUG ((DEBUG_ERROR
, "Fail to find CapsuleX64 module in FV!\n"));
1086 Status
= EFI_NOT_FOUND
;
1089 ASSERT (CoalesceImageEntryPoint
!= 0);
1090 CoalesceEntry
= (COALESCE_ENTRY
) (UINTN
) CoalesceImageEntryPoint
;
1091 Status
= ModeSwitch (&LongModeBuffer
, CoalesceEntry
, (EFI_PHYSICAL_ADDRESS
)(UINTN
)VariableArrayAddress
, MemoryResource
, MemoryBase
, MemorySize
);
1094 // Capsule is processed in IA32 mode.
1096 Status
= CapsuleDataCoalesce (PeiServices
, (EFI_PHYSICAL_ADDRESS
*)(UINTN
)VariableArrayAddress
, MemoryResource
, MemoryBase
, MemorySize
);
1100 // Process capsule directly.
1102 Status
= CapsuleDataCoalesce (PeiServices
, (EFI_PHYSICAL_ADDRESS
*)(UINTN
)VariableArrayAddress
, MemoryResource
, MemoryBase
, MemorySize
);
1105 DEBUG ((DEBUG_INFO
, "Capsule Coalesce Status = %r!\n", Status
));
1107 if (Status
== EFI_BUFFER_TOO_SMALL
) {
1108 DEBUG ((DEBUG_ERROR
, "There is not enough memory to process capsule!\n"));
1111 if (Status
== EFI_NOT_FOUND
) {
1112 DEBUG ((DEBUG_ERROR
, "Fail to parse capsule descriptor in memory!\n"));
1113 REPORT_STATUS_CODE (
1114 EFI_ERROR_CODE
| EFI_ERROR_MAJOR
,
1115 (EFI_SOFTWARE_PEI_MODULE
| EFI_SW_PEI_EC_INVALID_CAPSULE_DESCRIPTOR
)
1124 Determine if we're in capsule update boot mode.
1126 @param PeiServices PEI services table
1128 @retval EFI_SUCCESS if we have a capsule available
1129 @retval EFI_NOT_FOUND no capsule detected
1134 CheckCapsuleUpdate (
1135 IN EFI_PEI_SERVICES
**PeiServices
1138 if (AreCapsulesStaged ()) {
1141 return EFI_NOT_FOUND
;
1145 This function will look at a capsule and determine if it's a test pattern.
1146 If it is, then it will verify it and emit an error message if corruption is detected.
1148 @param PeiServices Standard pei services pointer
1149 @param CapsuleBase Base address of coalesced capsule, which is preceeded
1150 by private data. Very implementation specific.
1152 @retval TRUE Capsule image is the test image
1153 @retval FALSE Capsule image is not the test image.
1157 CapsuleTestPattern (
1158 IN EFI_PEI_SERVICES
**PeiServices
,
1159 IN VOID
*CapsuleBase
1170 // Look at the capsule data and determine if it's a test pattern. If it
1171 // is, then test it now.
1173 TestPtr
= (UINT32
*) CapsuleBase
;
1175 // 0x54534554 "TEST"
1177 if (*TestPtr
== 0x54534554) {
1179 DEBUG ((DEBUG_INFO
, "Capsule test pattern mode activated...\n"));
1180 TestSize
= TestPtr
[1] / sizeof (UINT32
);
1182 // Skip over the signature and the size fields in the pattern data header
1186 while (TestSize
> 0) {
1187 if (*TestPtr
!= TestCounter
) {
1188 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
));
1197 DEBUG ((DEBUG_INFO
, "Capsule test pattern mode SUCCESS\n"));
1204 Capsule PPI service that gets called after memory is available. The
1205 capsule coalesce function, which must be called first, returns a base
1206 address and size, which can be anything actually. Once the memory init
1207 PEIM has discovered memory, then it should call this function and pass in
1208 the base address and size returned by the coalesce function. Then this
1209 function can create a capsule HOB and return.
1211 @param PeiServices standard pei services pointer
1212 @param CapsuleBase address returned by the capsule coalesce function. Most
1213 likely this will actually be a pointer to private data.
1214 @param CapsuleSize value returned by the capsule coalesce function.
1216 @retval EFI_VOLUME_CORRUPTED CapsuleBase does not appear to point to a
1218 @retval EFI_SUCCESS if all goes well.
1223 IN EFI_PEI_SERVICES
**PeiServices
,
1224 IN VOID
*CapsuleBase
,
1225 IN UINTN CapsuleSize
1229 EFI_CAPSULE_PEIM_PRIVATE_DATA
*PrivateData
;
1231 EFI_PHYSICAL_ADDRESS NewBuffer
;
1232 UINTN CapsuleNumber
;
1234 EFI_PHYSICAL_ADDRESS BaseAddress
;
1237 PrivateData
= (EFI_CAPSULE_PEIM_PRIVATE_DATA
*) CapsuleBase
;
1238 if (PrivateData
->Signature
!= EFI_CAPSULE_PEIM_PRIVATE_DATA_SIGNATURE
) {
1239 return EFI_VOLUME_CORRUPTED
;
1241 if (PrivateData
->CapsuleAllImageSize
>= MAX_ADDRESS
) {
1242 DEBUG ((DEBUG_ERROR
, "CapsuleAllImageSize too big - 0x%lx\n", PrivateData
->CapsuleAllImageSize
));
1243 return EFI_OUT_OF_RESOURCES
;
1245 if (PrivateData
->CapsuleNumber
>= MAX_ADDRESS
) {
1246 DEBUG ((DEBUG_ERROR
, "CapsuleNumber too big - 0x%lx\n", PrivateData
->CapsuleNumber
));
1247 return EFI_OUT_OF_RESOURCES
;
1250 // Capsule Number and Capsule Offset is in the tail of Capsule data.
1252 Size
= (UINTN
)PrivateData
->CapsuleAllImageSize
;
1253 CapsuleNumber
= (UINTN
)PrivateData
->CapsuleNumber
;
1255 // Allocate the memory so that it gets preserved into DXE
1257 Status
= PeiServicesAllocatePages (
1258 EfiRuntimeServicesData
,
1259 EFI_SIZE_TO_PAGES (Size
),
1263 if (Status
!= EFI_SUCCESS
) {
1264 DEBUG ((DEBUG_ERROR
, "AllocatePages Failed!\n"));
1268 // Copy to our new buffer for DXE
1270 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
));
1271 CopyMem ((VOID
*) (UINTN
) NewBuffer
, (VOID
*) (UINTN
) ((UINT8
*)PrivateData
+ sizeof(EFI_CAPSULE_PEIM_PRIVATE_DATA
) + (CapsuleNumber
- 1) * sizeof(UINT64
)), Size
);
1273 // Check for test data pattern. If it is the test pattern, then we'll
1274 // test it and still create the HOB so that it can be used to verify
1275 // that capsules don't get corrupted all the way into BDS. BDS will
1276 // still try to turn it into a firmware volume, but will think it's
1277 // corrupted so nothing will happen.
1280 CapsuleTestPattern (PeiServices
, (VOID
*) (UINTN
) NewBuffer
);
1284 // Build the UEFI Capsule Hob for each capsule image.
1286 for (Index
= 0; Index
< CapsuleNumber
; Index
++) {
1287 BaseAddress
= NewBuffer
+ PrivateData
->CapsuleOffset
[Index
];
1288 Length
= ((EFI_CAPSULE_HEADER
*)((UINTN
) BaseAddress
))->CapsuleImageSize
;
1290 BuildCvHob (BaseAddress
, Length
);
1296 CONST EFI_PEI_CAPSULE_PPI mCapsulePpi
= {
1302 CONST EFI_PEI_PPI_DESCRIPTOR mUefiPpiListCapsule
= {
1303 (EFI_PEI_PPI_DESCRIPTOR_PPI
| EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST
),
1304 &gEfiPeiCapsulePpiGuid
,
1305 (EFI_PEI_CAPSULE_PPI
*) &mCapsulePpi
1309 Entry point function for the PEIM
1311 @param FileHandle Handle of the file being invoked.
1312 @param PeiServices Describes the list of possible PEI Services.
1314 @return EFI_SUCCESS If we installed our PPI
1320 IN EFI_PEI_FILE_HANDLE FileHandle
,
1321 IN CONST EFI_PEI_SERVICES
**PeiServices
1325 // Just produce our PPI
1327 return PeiServicesInstallPpi (&mUefiPpiListCapsule
);