2 Capsule update PEIM for UEFI2.0
4 Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
5 Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR>
7 SPDX-License-Identifier: BSD-2-Clause-Patent
15 // Global Descriptor Table (GDT)
17 GLOBAL_REMOVE_IF_UNREFERENCED IA32_SEGMENT_DESCRIPTOR mGdtEntries
[] = {
18 /* selector { Global Segment Descriptor } */
19 /* 0x00 */ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, //null descriptor
20 /* 0x08 */ {{0xffff, 0, 0, 0x3, 1, 0, 1, 0xf, 0, 0, 1, 1, 0}}, //linear data segment descriptor
21 /* 0x10 */ {{0xffff, 0, 0, 0xf, 1, 0, 1, 0xf, 0, 0, 1, 1, 0}}, //linear code segment descriptor
22 /* 0x18 */ {{0xffff, 0, 0, 0x3, 1, 0, 1, 0xf, 0, 0, 1, 1, 0}}, //system data segment descriptor
23 /* 0x20 */ {{0xffff, 0, 0, 0xb, 1, 0, 1, 0xf, 0, 0, 1, 1, 0}}, //system code segment descriptor
24 /* 0x28 */ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, //spare segment descriptor
25 /* 0x30 */ {{0xffff, 0, 0, 0x3, 1, 0, 1, 0xf, 0, 0, 1, 1, 0}}, //system data segment descriptor
26 /* 0x38 */ {{0xffff, 0, 0, 0xb, 1, 0, 1, 0xf, 0, 1, 0, 1, 0}}, //system code segment descriptor
27 /* 0x40 */ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, //spare segment descriptor
33 GLOBAL_REMOVE_IF_UNREFERENCED CONST IA32_DESCRIPTOR mGdt
= {
34 sizeof (mGdtEntries
) - 1,
40 The function will check if 1G page is supported.
42 @retval TRUE 1G page is supported.
43 @retval FALSE 1G page is not supported.
53 BOOLEAN Page1GSupport
;
55 Page1GSupport
= FALSE
;
56 if (PcdGetBool(PcdUse1GPageTable
)) {
57 AsmCpuid (0x80000000, &RegEax
, NULL
, NULL
, NULL
);
58 if (RegEax
>= 0x80000001) {
59 AsmCpuid (0x80000001, NULL
, NULL
, NULL
, &RegEdx
);
60 if ((RegEdx
& BIT26
) != 0) {
70 Calculate the total size of page table.
72 @param[in] Page1GSupport 1G page support or not.
74 @return The size of page table.
78 CalculatePageTableSize (
79 IN BOOLEAN Page1GSupport
82 UINTN ExtraPageTablePages
;
84 UINT8 PhysicalAddressBits
;
85 UINT32 NumberOfPml4EntriesNeeded
;
86 UINT32 NumberOfPdpEntriesNeeded
;
89 // Create 4G page table by default,
90 // and let PF handler to handle > 4G request.
92 PhysicalAddressBits
= 32;
93 ExtraPageTablePages
= EXTRA_PAGE_TABLE_PAGES
;
96 // Calculate the table entries needed.
98 if (PhysicalAddressBits
<= 39 ) {
99 NumberOfPml4EntriesNeeded
= 1;
100 NumberOfPdpEntriesNeeded
= (UINT32
)LShiftU64 (1, (PhysicalAddressBits
- 30));
102 NumberOfPml4EntriesNeeded
= (UINT32
)LShiftU64 (1, (PhysicalAddressBits
- 39));
103 NumberOfPdpEntriesNeeded
= 512;
106 if (!Page1GSupport
) {
107 TotalPagesNum
= (NumberOfPdpEntriesNeeded
+ 1) * NumberOfPml4EntriesNeeded
+ 1;
109 TotalPagesNum
= NumberOfPml4EntriesNeeded
+ 1;
111 TotalPagesNum
+= ExtraPageTablePages
;
113 return EFI_PAGES_TO_SIZE (TotalPagesNum
);
117 Allocates and fills in the Page Directory and Page Table Entries to
118 establish a 4G page table.
120 @param[in] PageTablesAddress The base address of page table.
121 @param[in] Page1GSupport 1G page support or not.
126 IN EFI_PHYSICAL_ADDRESS PageTablesAddress
,
127 IN BOOLEAN Page1GSupport
130 UINT8 PhysicalAddressBits
;
131 EFI_PHYSICAL_ADDRESS PageAddress
;
132 UINTN IndexOfPml4Entries
;
133 UINTN IndexOfPdpEntries
;
134 UINTN IndexOfPageDirectoryEntries
;
135 UINT32 NumberOfPml4EntriesNeeded
;
136 UINT32 NumberOfPdpEntriesNeeded
;
137 PAGE_MAP_AND_DIRECTORY_POINTER
*PageMapLevel4Entry
;
138 PAGE_MAP_AND_DIRECTORY_POINTER
*PageMap
;
139 PAGE_MAP_AND_DIRECTORY_POINTER
*PageDirectoryPointerEntry
;
140 PAGE_TABLE_ENTRY
*PageDirectoryEntry
;
141 UINTN BigPageAddress
;
142 PAGE_TABLE_1G_ENTRY
*PageDirectory1GEntry
;
143 UINT64 AddressEncMask
;
146 // Make sure AddressEncMask is contained to smallest supported address field.
148 AddressEncMask
= PcdGet64 (PcdPteMemoryEncryptionAddressOrMask
) & PAGING_1G_ADDRESS_MASK_64
;
151 // Create 4G page table by default,
152 // and let PF handler to handle > 4G request.
154 PhysicalAddressBits
= 32;
157 // Calculate the table entries needed.
159 if (PhysicalAddressBits
<= 39 ) {
160 NumberOfPml4EntriesNeeded
= 1;
161 NumberOfPdpEntriesNeeded
= (UINT32
)LShiftU64 (1, (PhysicalAddressBits
- 30));
163 NumberOfPml4EntriesNeeded
= (UINT32
)LShiftU64 (1, (PhysicalAddressBits
- 39));
164 NumberOfPdpEntriesNeeded
= 512;
168 // Pre-allocate big pages to avoid later allocations.
170 BigPageAddress
= (UINTN
) PageTablesAddress
;
173 // By architecture only one PageMapLevel4 exists - so lets allocate storage for it.
175 PageMap
= (VOID
*) BigPageAddress
;
176 BigPageAddress
+= SIZE_4KB
;
178 PageMapLevel4Entry
= PageMap
;
180 for (IndexOfPml4Entries
= 0; IndexOfPml4Entries
< NumberOfPml4EntriesNeeded
; IndexOfPml4Entries
++, PageMapLevel4Entry
++) {
182 // Each PML4 entry points to a page of Page Directory Pointer entires.
183 // So lets allocate space for them and fill them in in the IndexOfPdpEntries loop.
185 PageDirectoryPointerEntry
= (VOID
*) BigPageAddress
;
186 BigPageAddress
+= SIZE_4KB
;
191 PageMapLevel4Entry
->Uint64
= (UINT64
)(UINTN
)PageDirectoryPointerEntry
| AddressEncMask
;
192 PageMapLevel4Entry
->Bits
.ReadWrite
= 1;
193 PageMapLevel4Entry
->Bits
.Present
= 1;
196 PageDirectory1GEntry
= (VOID
*) PageDirectoryPointerEntry
;
198 for (IndexOfPageDirectoryEntries
= 0; IndexOfPageDirectoryEntries
< 512; IndexOfPageDirectoryEntries
++, PageDirectory1GEntry
++, PageAddress
+= SIZE_1GB
) {
200 // Fill in the Page Directory entries
202 PageDirectory1GEntry
->Uint64
= (UINT64
)PageAddress
| AddressEncMask
;
203 PageDirectory1GEntry
->Bits
.ReadWrite
= 1;
204 PageDirectory1GEntry
->Bits
.Present
= 1;
205 PageDirectory1GEntry
->Bits
.MustBe1
= 1;
208 for (IndexOfPdpEntries
= 0; IndexOfPdpEntries
< NumberOfPdpEntriesNeeded
; IndexOfPdpEntries
++, PageDirectoryPointerEntry
++) {
210 // Each Directory Pointer entries points to a page of Page Directory entires.
211 // So allocate space for them and fill them in in the IndexOfPageDirectoryEntries loop.
213 PageDirectoryEntry
= (VOID
*) BigPageAddress
;
214 BigPageAddress
+= SIZE_4KB
;
217 // Fill in a Page Directory Pointer Entries
219 PageDirectoryPointerEntry
->Uint64
= (UINT64
)(UINTN
)PageDirectoryEntry
| AddressEncMask
;
220 PageDirectoryPointerEntry
->Bits
.ReadWrite
= 1;
221 PageDirectoryPointerEntry
->Bits
.Present
= 1;
223 for (IndexOfPageDirectoryEntries
= 0; IndexOfPageDirectoryEntries
< 512; IndexOfPageDirectoryEntries
++, PageDirectoryEntry
++, PageAddress
+= SIZE_2MB
) {
225 // Fill in the Page Directory entries
227 PageDirectoryEntry
->Uint64
= (UINT64
)PageAddress
| AddressEncMask
;
228 PageDirectoryEntry
->Bits
.ReadWrite
= 1;
229 PageDirectoryEntry
->Bits
.Present
= 1;
230 PageDirectoryEntry
->Bits
.MustBe1
= 1;
234 for (; IndexOfPdpEntries
< 512; IndexOfPdpEntries
++, PageDirectoryPointerEntry
++) {
236 PageDirectoryPointerEntry
,
237 sizeof(PAGE_MAP_AND_DIRECTORY_POINTER
)
244 // For the PML4 entries we are not using fill in a null entry.
246 for (; IndexOfPml4Entries
< 512; IndexOfPml4Entries
++, PageMapLevel4Entry
++) {
249 sizeof (PAGE_MAP_AND_DIRECTORY_POINTER
)
255 Return function from long mode to 32-bit mode.
257 @param EntrypointContext Context for mode switching
258 @param ReturnContext Context for mode switching
263 SWITCH_32_TO_64_CONTEXT
*EntrypointContext
,
264 SWITCH_64_TO_32_CONTEXT
*ReturnContext
268 // Restore original GDT
270 AsmWriteGdtr (&ReturnContext
->Gdtr
);
273 // return to original caller
275 LongJump ((BASE_LIBRARY_JUMP_BUFFER
*)(UINTN
)EntrypointContext
->JumpBuffer
, 1);
284 Thunk function from 32-bit protection mode to long mode.
286 @param PageTableAddress Page table base address
287 @param Context Context for mode switching
288 @param ReturnContext Context for mode switching
290 @retval EFI_SUCCESS Function successfully executed.
295 EFI_PHYSICAL_ADDRESS PageTableAddress
,
296 SWITCH_32_TO_64_CONTEXT
*Context
,
297 SWITCH_64_TO_32_CONTEXT
*ReturnContext
304 // Save return address, LongJump will return here then
306 SetJumpFlag
= SetJump ((BASE_LIBRARY_JUMP_BUFFER
*) (UINTN
) Context
->JumpBuffer
);
308 if (SetJumpFlag
== 0) {
311 // Build 4G Page Tables.
313 Create4GPageTables (PageTableAddress
, Context
->Page1GSupport
);
318 AsmWriteGdtr (&mGdt
);
323 AsmWriteCr3 ((UINTN
) PageTableAddress
);
327 "%a() Stack Base: 0x%lx, Stack Size: 0x%lx\n",
329 Context
->StackBufferBase
,
330 Context
->StackBufferLength
334 // Disable interrupt of Debug timer, since the IDT table cannot work in long mode
336 SaveAndSetDebugTimerInterrupt (FALSE
);
338 // Transfer to long mode
342 (UINT64
) Context
->EntryPoint
,
343 (UINT64
)(UINTN
) Context
,
344 (UINT64
)(UINTN
) ReturnContext
,
345 Context
->StackBufferBase
+ Context
->StackBufferLength
350 // Convert to 32-bit Status and return
352 Status
= EFI_SUCCESS
;
353 if ((UINTN
) ReturnContext
->ReturnStatus
!= 0) {
354 Status
= ENCODE_ERROR ((UINTN
) ReturnContext
->ReturnStatus
);
361 If in 32 bit protection mode, and coalesce image is of X64, switch to long mode.
363 @param LongModeBuffer The context of long mode.
364 @param CoalesceEntry Entry of coalesce image.
365 @param BlockListAddr Address of block list.
366 @param MemoryResource Pointer to the buffer of memory resource descriptor.
367 @param MemoryBase Base of memory range.
368 @param MemorySize Size of memory range.
370 @retval EFI_SUCCESS Successfully switched to long mode and execute coalesce.
371 @retval Others Failed to execute coalesce in long mode.
376 IN EFI_CAPSULE_LONG_MODE_BUFFER
*LongModeBuffer
,
377 IN COALESCE_ENTRY CoalesceEntry
,
378 IN EFI_PHYSICAL_ADDRESS BlockListAddr
,
379 IN MEMORY_RESOURCE_DESCRIPTOR
*MemoryResource
,
380 IN OUT VOID
**MemoryBase
,
381 IN OUT UINTN
*MemorySize
385 EFI_PHYSICAL_ADDRESS MemoryBase64
;
387 EFI_PHYSICAL_ADDRESS MemoryEnd64
;
388 SWITCH_32_TO_64_CONTEXT Context
;
389 SWITCH_64_TO_32_CONTEXT ReturnContext
;
390 BASE_LIBRARY_JUMP_BUFFER JumpBuffer
;
391 EFI_PHYSICAL_ADDRESS ReservedRangeBase
;
392 EFI_PHYSICAL_ADDRESS ReservedRangeEnd
;
393 BOOLEAN Page1GSupport
;
395 ZeroMem (&Context
, sizeof (SWITCH_32_TO_64_CONTEXT
));
396 ZeroMem (&ReturnContext
, sizeof (SWITCH_64_TO_32_CONTEXT
));
398 MemoryBase64
= (UINT64
) (UINTN
) *MemoryBase
;
399 MemorySize64
= (UINT64
) (UINTN
) *MemorySize
;
400 MemoryEnd64
= MemoryBase64
+ MemorySize64
;
402 Page1GSupport
= IsPage1GSupport ();
405 // Merge memory range reserved for stack and page table
407 if (LongModeBuffer
->StackBaseAddress
< LongModeBuffer
->PageTableAddress
) {
408 ReservedRangeBase
= LongModeBuffer
->StackBaseAddress
;
409 ReservedRangeEnd
= LongModeBuffer
->PageTableAddress
+ CalculatePageTableSize (Page1GSupport
);
411 ReservedRangeBase
= LongModeBuffer
->PageTableAddress
;
412 ReservedRangeEnd
= LongModeBuffer
->StackBaseAddress
+ LongModeBuffer
->StackSize
;
416 // Check if memory range reserved is overlap with MemoryBase ~ MemoryBase + MemorySize.
417 // If they are overlapped, get a larger range to process capsule data.
419 if (ReservedRangeBase
<= MemoryBase64
) {
420 if (ReservedRangeEnd
< MemoryEnd64
) {
421 MemoryBase64
= ReservedRangeEnd
;
423 DEBUG ((EFI_D_ERROR
, "Memory is not enough to process capsule!\n"));
424 return EFI_OUT_OF_RESOURCES
;
426 } else if (ReservedRangeBase
< MemoryEnd64
) {
427 if (ReservedRangeEnd
< MemoryEnd64
&&
428 ReservedRangeBase
- MemoryBase64
< MemoryEnd64
- ReservedRangeEnd
) {
429 MemoryBase64
= ReservedRangeEnd
;
431 MemorySize64
= (UINT64
)(UINTN
)(ReservedRangeBase
- MemoryBase64
);
436 // Initialize context jumping to 64-bit enviroment
438 Context
.JumpBuffer
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)&JumpBuffer
;
439 Context
.StackBufferBase
= LongModeBuffer
->StackBaseAddress
;
440 Context
.StackBufferLength
= LongModeBuffer
->StackSize
;
441 Context
.EntryPoint
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)CoalesceEntry
;
442 Context
.BlockListAddr
= BlockListAddr
;
443 Context
.MemoryResource
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)MemoryResource
;
444 Context
.MemoryBase64Ptr
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)&MemoryBase64
;
445 Context
.MemorySize64Ptr
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)&MemorySize64
;
446 Context
.Page1GSupport
= Page1GSupport
;
447 Context
.AddressEncMask
= PcdGet64 (PcdPteMemoryEncryptionAddressOrMask
) & PAGING_1G_ADDRESS_MASK_64
;
450 // Prepare data for return back
452 ReturnContext
.ReturnCs
= 0x10;
453 ReturnContext
.ReturnEntryPoint
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)ReturnFunction
;
455 // Will save the return status of processing capsule
457 ReturnContext
.ReturnStatus
= 0;
462 AsmReadGdtr ((IA32_DESCRIPTOR
*)&ReturnContext
.Gdtr
);
464 Status
= Thunk32To64 (LongModeBuffer
->PageTableAddress
, &Context
, &ReturnContext
);
466 if (!EFI_ERROR (Status
)) {
467 *MemoryBase
= (VOID
*) (UINTN
) MemoryBase64
;
468 *MemorySize
= (UINTN
) MemorySize64
;
476 Locates the coalesce image entry point, and detects its machine type.
478 @param CoalesceImageEntryPoint Pointer to coalesce image entry point for output.
479 @param CoalesceImageMachineType Pointer to machine type of coalesce image.
481 @retval EFI_SUCCESS Coalesce image successfully located.
482 @retval Others Failed to locate the coalesce image.
486 FindCapsuleCoalesceImage (
487 OUT EFI_PHYSICAL_ADDRESS
*CoalesceImageEntryPoint
,
488 OUT UINT16
*CoalesceImageMachineType
493 EFI_PEI_LOAD_FILE_PPI
*LoadFile
;
494 EFI_PEI_FV_HANDLE VolumeHandle
;
495 EFI_PEI_FILE_HANDLE FileHandle
;
496 EFI_PHYSICAL_ADDRESS CoalesceImageAddress
;
497 UINT64 CoalesceImageSize
;
498 UINT32 AuthenticationState
;
503 Status
= PeiServicesFfsFindNextVolume (Instance
++, &VolumeHandle
);
504 if (EFI_ERROR (Status
)) {
507 Status
= PeiServicesFfsFindFileByName (PcdGetPtr(PcdCapsuleCoalesceFile
), VolumeHandle
, &FileHandle
);
508 if (!EFI_ERROR (Status
)) {
509 Status
= PeiServicesLocatePpi (&gEfiPeiLoadFilePpiGuid
, 0, NULL
, (VOID
**) &LoadFile
);
510 ASSERT_EFI_ERROR (Status
);
512 Status
= LoadFile
->LoadFile (
515 &CoalesceImageAddress
,
517 CoalesceImageEntryPoint
,
520 if (EFI_ERROR (Status
)) {
521 DEBUG ((EFI_D_ERROR
, "Unable to find PE32 section in CapsuleX64 image ffs %r!\n", Status
));
524 *CoalesceImageMachineType
= PeCoffLoaderGetMachineType ((VOID
*) (UINTN
) CoalesceImageAddress
);
535 Gets the reserved long mode buffer.
537 @param LongModeBuffer Pointer to the long mode buffer for output.
539 @retval EFI_SUCCESS Long mode buffer successfully retrieved.
540 @retval Others Variable storing long mode buffer not found.
545 OUT EFI_CAPSULE_LONG_MODE_BUFFER
*LongModeBuffer
550 EFI_PEI_READ_ONLY_VARIABLE2_PPI
*PPIVariableServices
;
552 Status
= PeiServicesLocatePpi (
553 &gEfiPeiReadOnlyVariable2PpiGuid
,
556 (VOID
**) &PPIVariableServices
558 ASSERT_EFI_ERROR (Status
);
560 Size
= sizeof (EFI_CAPSULE_LONG_MODE_BUFFER
);
561 Status
= PPIVariableServices
->GetVariable (
563 EFI_CAPSULE_LONG_MODE_BUFFER_NAME
,
564 &gEfiCapsuleVendorGuid
,
569 if (EFI_ERROR (Status
)) {
570 DEBUG (( EFI_D_ERROR
, "Error Get LongModeBuffer variable %r!\n", Status
));
576 #if defined (MDE_CPU_IA32) || defined (MDE_CPU_X64)
578 Get physical address bits.
580 @return Physical address bits.
584 GetPhysicalAddressBits (
589 UINT8 PhysicalAddressBits
;
593 // Get physical address bits supported.
595 Hob
= GetFirstHob (EFI_HOB_TYPE_CPU
);
597 PhysicalAddressBits
= ((EFI_HOB_CPU
*) Hob
)->SizeOfMemorySpace
;
599 AsmCpuid (0x80000000, &RegEax
, NULL
, NULL
, NULL
);
600 if (RegEax
>= 0x80000008) {
601 AsmCpuid (0x80000008, &RegEax
, NULL
, NULL
, NULL
);
602 PhysicalAddressBits
= (UINT8
) RegEax
;
604 PhysicalAddressBits
= 36;
609 // IA-32e paging translates 48-bit linear addresses to 52-bit physical addresses.
611 ASSERT (PhysicalAddressBits
<= 52);
612 if (PhysicalAddressBits
> 48) {
613 PhysicalAddressBits
= 48;
616 return PhysicalAddressBits
;
621 Sort memory resource entries based upon PhysicalStart, from low to high.
623 @param[in, out] MemoryResource A pointer to the memory resource entry buffer.
627 SortMemoryResourceDescriptor (
628 IN OUT MEMORY_RESOURCE_DESCRIPTOR
*MemoryResource
631 MEMORY_RESOURCE_DESCRIPTOR
*MemoryResourceEntry
;
632 MEMORY_RESOURCE_DESCRIPTOR
*NextMemoryResourceEntry
;
633 MEMORY_RESOURCE_DESCRIPTOR TempMemoryResource
;
635 MemoryResourceEntry
= MemoryResource
;
636 NextMemoryResourceEntry
= MemoryResource
+ 1;
637 while (MemoryResourceEntry
->ResourceLength
!= 0) {
638 while (NextMemoryResourceEntry
->ResourceLength
!= 0) {
639 if (MemoryResourceEntry
->PhysicalStart
> NextMemoryResourceEntry
->PhysicalStart
) {
640 CopyMem (&TempMemoryResource
, MemoryResourceEntry
, sizeof (MEMORY_RESOURCE_DESCRIPTOR
));
641 CopyMem (MemoryResourceEntry
, NextMemoryResourceEntry
, sizeof (MEMORY_RESOURCE_DESCRIPTOR
));
642 CopyMem (NextMemoryResourceEntry
, &TempMemoryResource
, sizeof (MEMORY_RESOURCE_DESCRIPTOR
));
645 NextMemoryResourceEntry
= NextMemoryResourceEntry
+ 1;
648 MemoryResourceEntry
= MemoryResourceEntry
+ 1;
649 NextMemoryResourceEntry
= MemoryResourceEntry
+ 1;
654 Merge continous memory resource entries.
656 @param[in, out] MemoryResource A pointer to the memory resource entry buffer.
660 MergeMemoryResourceDescriptor (
661 IN OUT MEMORY_RESOURCE_DESCRIPTOR
*MemoryResource
664 MEMORY_RESOURCE_DESCRIPTOR
*MemoryResourceEntry
;
665 MEMORY_RESOURCE_DESCRIPTOR
*NewMemoryResourceEntry
;
666 MEMORY_RESOURCE_DESCRIPTOR
*NextMemoryResourceEntry
;
667 MEMORY_RESOURCE_DESCRIPTOR
*MemoryResourceEnd
;
669 MemoryResourceEntry
= MemoryResource
;
670 NewMemoryResourceEntry
= MemoryResource
;
671 while (MemoryResourceEntry
->ResourceLength
!= 0) {
672 CopyMem (NewMemoryResourceEntry
, MemoryResourceEntry
, sizeof (MEMORY_RESOURCE_DESCRIPTOR
));
673 NextMemoryResourceEntry
= MemoryResourceEntry
+ 1;
675 while ((NextMemoryResourceEntry
->ResourceLength
!= 0) &&
676 (NextMemoryResourceEntry
->PhysicalStart
== (MemoryResourceEntry
->PhysicalStart
+ MemoryResourceEntry
->ResourceLength
))) {
677 MemoryResourceEntry
->ResourceLength
+= NextMemoryResourceEntry
->ResourceLength
;
678 if (NewMemoryResourceEntry
!= MemoryResourceEntry
) {
679 NewMemoryResourceEntry
->ResourceLength
+= NextMemoryResourceEntry
->ResourceLength
;
682 NextMemoryResourceEntry
= NextMemoryResourceEntry
+ 1;
685 MemoryResourceEntry
= NextMemoryResourceEntry
;
686 NewMemoryResourceEntry
= NewMemoryResourceEntry
+ 1;
690 // Set NULL terminate memory resource descriptor after merging.
692 MemoryResourceEnd
= NewMemoryResourceEntry
;
693 ZeroMem (MemoryResourceEnd
, sizeof (MEMORY_RESOURCE_DESCRIPTOR
));
697 Build memory resource descriptor from resource descriptor in HOB list.
699 @return Pointer to the buffer of memory resource descriptor.
700 NULL if no memory resource descriptor reported in HOB list
701 before capsule Coalesce.
704 MEMORY_RESOURCE_DESCRIPTOR
*
705 BuildMemoryResourceDescriptor (
709 EFI_PEI_HOB_POINTERS Hob
;
711 EFI_HOB_RESOURCE_DESCRIPTOR
*ResourceDescriptor
;
712 MEMORY_RESOURCE_DESCRIPTOR
*MemoryResource
;
716 // Get the count of memory resource descriptor.
719 Hob
.Raw
= GetFirstHob (EFI_HOB_TYPE_RESOURCE_DESCRIPTOR
);
720 while (Hob
.Raw
!= NULL
) {
721 ResourceDescriptor
= (EFI_HOB_RESOURCE_DESCRIPTOR
*) Hob
.Raw
;
722 if (ResourceDescriptor
->ResourceType
== EFI_RESOURCE_SYSTEM_MEMORY
) {
725 Hob
.Raw
= GET_NEXT_HOB (Hob
);
726 Hob
.Raw
= GetNextHob (EFI_HOB_TYPE_RESOURCE_DESCRIPTOR
, Hob
.Raw
);
730 DEBUG ((EFI_D_INFO
| EFI_D_WARN
, "No memory resource descriptor reported in HOB list before capsule Coalesce\n"));
731 #if defined (MDE_CPU_IA32) || defined (MDE_CPU_X64)
733 // Allocate memory to hold memory resource descriptor,
734 // include extra one NULL terminate memory resource descriptor.
736 Status
= PeiServicesAllocatePool ((1 + 1) * sizeof (MEMORY_RESOURCE_DESCRIPTOR
), (VOID
**) &MemoryResource
);
737 ASSERT_EFI_ERROR (Status
);
738 ZeroMem (MemoryResource
, (1 + 1) * sizeof (MEMORY_RESOURCE_DESCRIPTOR
));
740 MemoryResource
[0].PhysicalStart
= 0;
741 MemoryResource
[0].ResourceLength
= LShiftU64 (1, GetPhysicalAddressBits ());
742 DEBUG ((EFI_D_INFO
, "MemoryResource[0x0] - Start(0x%0lx) Length(0x%0lx)\n",
743 MemoryResource
[0x0].PhysicalStart
, MemoryResource
[0x0].ResourceLength
));
744 return MemoryResource
;
751 // Allocate memory to hold memory resource descriptor,
752 // include extra one NULL terminate memory resource descriptor.
754 Status
= PeiServicesAllocatePool ((Index
+ 1) * sizeof (MEMORY_RESOURCE_DESCRIPTOR
), (VOID
**) &MemoryResource
);
755 ASSERT_EFI_ERROR (Status
);
756 ZeroMem (MemoryResource
, (Index
+ 1) * sizeof (MEMORY_RESOURCE_DESCRIPTOR
));
759 // Get the content of memory resource descriptor.
762 Hob
.Raw
= GetFirstHob (EFI_HOB_TYPE_RESOURCE_DESCRIPTOR
);
763 while (Hob
.Raw
!= NULL
) {
764 ResourceDescriptor
= (EFI_HOB_RESOURCE_DESCRIPTOR
*) Hob
.Raw
;
765 if (ResourceDescriptor
->ResourceType
== EFI_RESOURCE_SYSTEM_MEMORY
) {
766 DEBUG ((EFI_D_INFO
, "MemoryResource[0x%x] - Start(0x%0lx) Length(0x%0lx)\n",
767 Index
, ResourceDescriptor
->PhysicalStart
, ResourceDescriptor
->ResourceLength
));
768 MemoryResource
[Index
].PhysicalStart
= ResourceDescriptor
->PhysicalStart
;
769 MemoryResource
[Index
].ResourceLength
= ResourceDescriptor
->ResourceLength
;
772 Hob
.Raw
= GET_NEXT_HOB (Hob
);
773 Hob
.Raw
= GetNextHob (EFI_HOB_TYPE_RESOURCE_DESCRIPTOR
, Hob
.Raw
);
776 SortMemoryResourceDescriptor (MemoryResource
);
777 MergeMemoryResourceDescriptor (MemoryResource
);
779 DEBUG ((DEBUG_INFO
, "Dump MemoryResource[] after sorted and merged\n"));
780 for (Index
= 0; MemoryResource
[Index
].ResourceLength
!= 0; Index
++) {
783 " MemoryResource[0x%x] - Start(0x%0lx) Length(0x%0lx)\n",
785 MemoryResource
[Index
].PhysicalStart
,
786 MemoryResource
[Index
].ResourceLength
790 return MemoryResource
;
794 Checks for the presence of capsule descriptors.
795 Get capsule descriptors from variable CapsuleUpdateData, CapsuleUpdateData1, CapsuleUpdateData2...
796 and save to DescriptorBuffer.
798 @param DescriptorBuffer Pointer to the capsule descriptors
800 @retval EFI_SUCCESS a valid capsule is present
801 @retval EFI_NOT_FOUND if a valid capsule is not present
804 GetCapsuleDescriptors (
805 IN EFI_PHYSICAL_ADDRESS
*DescriptorBuffer
814 CHAR16 CapsuleVarName
[30];
816 EFI_PHYSICAL_ADDRESS CapsuleDataPtr64
;
817 EFI_PEI_READ_ONLY_VARIABLE2_PPI
*PPIVariableServices
;
821 CapsuleVarName
[0] = 0;
823 CapsuleDataPtr64
= 0;
825 Status
= PeiServicesLocatePpi (
826 &gEfiPeiReadOnlyVariable2PpiGuid
,
829 (VOID
**) &PPIVariableServices
831 if (Status
== EFI_SUCCESS
) {
832 StrCpyS (CapsuleVarName
, sizeof(CapsuleVarName
)/sizeof(CHAR16
), EFI_CAPSULE_VARIABLE_NAME
);
833 TempVarName
= CapsuleVarName
+ StrLen (CapsuleVarName
);
834 Size
= sizeof (CapsuleDataPtr64
);
838 // For the first Capsule Image
840 Status
= PPIVariableServices
->GetVariable (
843 &gEfiCapsuleVendorGuid
,
846 (VOID
*) &CapsuleDataPtr64
848 if (EFI_ERROR (Status
)) {
849 DEBUG ((DEBUG_INFO
, "Capsule -- capsule variable not set\n"));
850 return EFI_NOT_FOUND
;
853 // We have a chicken/egg situation where the memory init code needs to
854 // know the boot mode prior to initializing memory. For this case, our
855 // validate function will fail. We can detect if this is the case if blocklist
856 // pointer is null. In that case, return success since we know that the
859 if (DescriptorBuffer
== NULL
) {
863 UnicodeValueToStringS (
865 sizeof (CapsuleVarName
) - ((UINTN
)TempVarName
- (UINTN
)CapsuleVarName
),
870 Status
= PPIVariableServices
->GetVariable (
873 &gEfiCapsuleVendorGuid
,
876 (VOID
*) &CapsuleDataPtr64
878 if (EFI_ERROR (Status
)) {
883 // If this BlockList has been linked before, skip this variable
886 for (TempIndex
= 0; TempIndex
< ValidIndex
; TempIndex
++) {
887 if (DescriptorBuffer
[TempIndex
] == CapsuleDataPtr64
) {
899 // Cache BlockList which has been processed
901 DescriptorBuffer
[ValidIndex
++] = CapsuleDataPtr64
;
910 Capsule PPI service to coalesce a fragmented capsule in memory.
912 @param PeiServices General purpose services available to every PEIM.
913 @param MemoryBase Pointer to the base of a block of memory that we can walk
914 all over while trying to coalesce our buffers.
915 On output, this variable will hold the base address of
917 @param MemorySize Size of the memory region pointed to by MemoryBase.
918 On output, this variable will contain the size of the
921 @retval EFI_NOT_FOUND if we can't determine the boot mode
922 if the boot mode is not flash-update
923 if we could not find the capsule descriptors
925 @retval EFI_BUFFER_TOO_SMALL
926 if we could not coalesce the capsule in the memory
927 region provided to us
929 @retval EFI_SUCCESS if there's no capsule, or if we processed the
930 capsule successfully.
935 IN EFI_PEI_SERVICES
**PeiServices
,
936 IN OUT VOID
**MemoryBase
,
937 IN OUT UINTN
*MemorySize
943 CHAR16 CapsuleVarName
[30];
945 EFI_PHYSICAL_ADDRESS CapsuleDataPtr64
;
947 EFI_BOOT_MODE BootMode
;
948 EFI_PEI_READ_ONLY_VARIABLE2_PPI
*PPIVariableServices
;
949 EFI_PHYSICAL_ADDRESS
*VariableArrayAddress
;
950 MEMORY_RESOURCE_DESCRIPTOR
*MemoryResource
;
952 UINT16 CoalesceImageMachineType
;
953 EFI_PHYSICAL_ADDRESS CoalesceImageEntryPoint
;
954 COALESCE_ENTRY CoalesceEntry
;
955 EFI_CAPSULE_LONG_MODE_BUFFER LongModeBuffer
;
960 CapsuleVarName
[0] = 0;
961 CapsuleDataPtr64
= 0;
964 // Someone should have already ascertained the boot mode. If it's not
965 // capsule update, then return normally.
967 Status
= PeiServicesGetBootMode (&BootMode
);
968 if (EFI_ERROR (Status
) || (BootMode
!= BOOT_ON_FLASH_UPDATE
)) {
969 DEBUG ((EFI_D_ERROR
, "Boot mode is not correct for capsule update path.\n"));
970 Status
= EFI_NOT_FOUND
;
975 // User may set the same ScatterGatherList with several different variables,
976 // so cache all ScatterGatherList for check later.
978 Status
= PeiServicesLocatePpi (
979 &gEfiPeiReadOnlyVariable2PpiGuid
,
982 (VOID
**) &PPIVariableServices
984 if (EFI_ERROR (Status
)) {
987 Size
= sizeof (CapsuleDataPtr64
);
988 StrCpyS (CapsuleVarName
, sizeof(CapsuleVarName
)/sizeof(CHAR16
), EFI_CAPSULE_VARIABLE_NAME
);
989 TempVarName
= CapsuleVarName
+ StrLen (CapsuleVarName
);
992 UnicodeValueToStringS (
994 sizeof (CapsuleVarName
) - ((UINTN
)TempVarName
- (UINTN
)CapsuleVarName
),
1000 Status
= PPIVariableServices
->GetVariable (
1001 PPIVariableServices
,
1003 &gEfiCapsuleVendorGuid
,
1006 (VOID
*) &CapsuleDataPtr64
1008 if (EFI_ERROR (Status
)) {
1010 // There is no capsule variables, quit
1012 DEBUG ((EFI_D_INFO
,"Capsule variable Index = %d\n", Index
));
1019 DEBUG ((EFI_D_INFO
,"Capsule variable count = %d\n", VariableCount
));
1022 // The last entry is the end flag.
1024 Status
= PeiServicesAllocatePool (
1025 (VariableCount
+ 1) * sizeof (EFI_PHYSICAL_ADDRESS
),
1026 (VOID
**)&VariableArrayAddress
1029 if (Status
!= EFI_SUCCESS
) {
1030 DEBUG ((EFI_D_ERROR
, "AllocatePages Failed!, Status = %x\n", Status
));
1034 ZeroMem (VariableArrayAddress
, (VariableCount
+ 1) * sizeof (EFI_PHYSICAL_ADDRESS
));
1037 // Find out if we actually have a capsule.
1038 // GetCapsuleDescriptors depends on variable PPI, so it should run in 32-bit environment.
1040 Status
= GetCapsuleDescriptors (VariableArrayAddress
);
1041 if (EFI_ERROR (Status
)) {
1042 DEBUG ((EFI_D_ERROR
, "Fail to find capsule variables.\n"));
1046 MemoryResource
= BuildMemoryResourceDescriptor ();
1049 if (FeaturePcdGet (PcdDxeIplSwitchToLongMode
)) {
1051 // Switch to 64-bit mode to process capsule data when:
1052 // 1. When DXE phase is 64-bit
1053 // 2. When the buffer for 64-bit transition exists
1054 // 3. When Capsule X64 image is built in BIOS image
1055 // In 64-bit mode, we can process capsule data above 4GB.
1057 CoalesceImageEntryPoint
= 0;
1058 Status
= GetLongModeContext (&LongModeBuffer
);
1059 if (EFI_ERROR (Status
)) {
1060 DEBUG ((EFI_D_ERROR
, "Fail to find the variable for long mode context!\n"));
1061 Status
= EFI_NOT_FOUND
;
1065 Status
= FindCapsuleCoalesceImage (&CoalesceImageEntryPoint
, &CoalesceImageMachineType
);
1066 if ((EFI_ERROR (Status
)) || (CoalesceImageMachineType
!= EFI_IMAGE_MACHINE_X64
)) {
1067 DEBUG ((EFI_D_ERROR
, "Fail to find CapsuleX64 module in FV!\n"));
1068 Status
= EFI_NOT_FOUND
;
1071 ASSERT (CoalesceImageEntryPoint
!= 0);
1072 CoalesceEntry
= (COALESCE_ENTRY
) (UINTN
) CoalesceImageEntryPoint
;
1073 Status
= ModeSwitch (&LongModeBuffer
, CoalesceEntry
, (EFI_PHYSICAL_ADDRESS
)(UINTN
)VariableArrayAddress
, MemoryResource
, MemoryBase
, MemorySize
);
1076 // Capsule is processed in IA32 mode.
1078 Status
= CapsuleDataCoalesce (PeiServices
, (EFI_PHYSICAL_ADDRESS
*)(UINTN
)VariableArrayAddress
, MemoryResource
, MemoryBase
, MemorySize
);
1082 // Process capsule directly.
1084 Status
= CapsuleDataCoalesce (PeiServices
, (EFI_PHYSICAL_ADDRESS
*)(UINTN
)VariableArrayAddress
, MemoryResource
, MemoryBase
, MemorySize
);
1087 DEBUG ((EFI_D_INFO
, "Capsule Coalesce Status = %r!\n", Status
));
1089 if (Status
== EFI_BUFFER_TOO_SMALL
) {
1090 DEBUG ((EFI_D_ERROR
, "There is not enough memory to process capsule!\n"));
1093 if (Status
== EFI_NOT_FOUND
) {
1094 DEBUG ((EFI_D_ERROR
, "Fail to parse capsule descriptor in memory!\n"));
1095 REPORT_STATUS_CODE (
1096 EFI_ERROR_CODE
| EFI_ERROR_MAJOR
,
1097 (EFI_SOFTWARE_PEI_MODULE
| EFI_SW_PEI_EC_INVALID_CAPSULE_DESCRIPTOR
)
1106 Determine if we're in capsule update boot mode.
1108 @param PeiServices PEI services table
1110 @retval EFI_SUCCESS if we have a capsule available
1111 @retval EFI_NOT_FOUND no capsule detected
1116 CheckCapsuleUpdate (
1117 IN EFI_PEI_SERVICES
**PeiServices
1121 Status
= GetCapsuleDescriptors (NULL
);
1125 This function will look at a capsule and determine if it's a test pattern.
1126 If it is, then it will verify it and emit an error message if corruption is detected.
1128 @param PeiServices Standard pei services pointer
1129 @param CapsuleBase Base address of coalesced capsule, which is preceeded
1130 by private data. Very implementation specific.
1132 @retval TRUE Capsule image is the test image
1133 @retval FALSE Capsule image is not the test image.
1137 CapsuleTestPattern (
1138 IN EFI_PEI_SERVICES
**PeiServices
,
1139 IN VOID
*CapsuleBase
1150 // Look at the capsule data and determine if it's a test pattern. If it
1151 // is, then test it now.
1153 TestPtr
= (UINT32
*) CapsuleBase
;
1155 // 0x54534554 "TEST"
1157 if (*TestPtr
== 0x54534554) {
1159 DEBUG ((EFI_D_INFO
, "Capsule test pattern mode activated...\n"));
1160 TestSize
= TestPtr
[1] / sizeof (UINT32
);
1162 // Skip over the signature and the size fields in the pattern data header
1166 while (TestSize
> 0) {
1167 if (*TestPtr
!= TestCounter
) {
1168 DEBUG ((EFI_D_INFO
, "Capsule test pattern mode FAILED: BaseAddr/FailAddr 0x%X 0x%X\n", (UINT32
)(UINTN
)(EFI_CAPSULE_PEIM_PRIVATE_DATA
*)CapsuleBase
, (UINT32
)(UINTN
)TestPtr
));
1177 DEBUG ((EFI_D_INFO
, "Capsule test pattern mode SUCCESS\n"));
1184 Capsule PPI service that gets called after memory is available. The
1185 capsule coalesce function, which must be called first, returns a base
1186 address and size, which can be anything actually. Once the memory init
1187 PEIM has discovered memory, then it should call this function and pass in
1188 the base address and size returned by the coalesce function. Then this
1189 function can create a capsule HOB and return.
1191 @param PeiServices standard pei services pointer
1192 @param CapsuleBase address returned by the capsule coalesce function. Most
1193 likely this will actually be a pointer to private data.
1194 @param CapsuleSize value returned by the capsule coalesce function.
1196 @retval EFI_VOLUME_CORRUPTED CapsuleBase does not appear to point to a
1198 @retval EFI_SUCCESS if all goes well.
1203 IN EFI_PEI_SERVICES
**PeiServices
,
1204 IN VOID
*CapsuleBase
,
1205 IN UINTN CapsuleSize
1209 EFI_CAPSULE_PEIM_PRIVATE_DATA
*PrivateData
;
1211 EFI_PHYSICAL_ADDRESS NewBuffer
;
1212 UINTN CapsuleNumber
;
1214 EFI_PHYSICAL_ADDRESS BaseAddress
;
1217 PrivateData
= (EFI_CAPSULE_PEIM_PRIVATE_DATA
*) CapsuleBase
;
1218 if (PrivateData
->Signature
!= EFI_CAPSULE_PEIM_PRIVATE_DATA_SIGNATURE
) {
1219 return EFI_VOLUME_CORRUPTED
;
1221 if (PrivateData
->CapsuleAllImageSize
>= MAX_ADDRESS
) {
1222 DEBUG ((EFI_D_ERROR
, "CapsuleAllImageSize too big - 0x%lx\n", PrivateData
->CapsuleAllImageSize
));
1223 return EFI_OUT_OF_RESOURCES
;
1225 if (PrivateData
->CapsuleNumber
>= MAX_ADDRESS
) {
1226 DEBUG ((EFI_D_ERROR
, "CapsuleNumber too big - 0x%lx\n", PrivateData
->CapsuleNumber
));
1227 return EFI_OUT_OF_RESOURCES
;
1230 // Capsule Number and Capsule Offset is in the tail of Capsule data.
1232 Size
= (UINTN
)PrivateData
->CapsuleAllImageSize
;
1233 CapsuleNumber
= (UINTN
)PrivateData
->CapsuleNumber
;
1235 // Allocate the memory so that it gets preserved into DXE
1237 Status
= PeiServicesAllocatePages (
1238 EfiRuntimeServicesData
,
1239 EFI_SIZE_TO_PAGES (Size
),
1243 if (Status
!= EFI_SUCCESS
) {
1244 DEBUG ((EFI_D_ERROR
, "AllocatePages Failed!\n"));
1248 // Copy to our new buffer for DXE
1250 DEBUG ((EFI_D_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
));
1251 CopyMem ((VOID
*) (UINTN
) NewBuffer
, (VOID
*) (UINTN
) ((UINT8
*)PrivateData
+ sizeof(EFI_CAPSULE_PEIM_PRIVATE_DATA
) + (CapsuleNumber
- 1) * sizeof(UINT64
)), Size
);
1253 // Check for test data pattern. If it is the test pattern, then we'll
1254 // test it and still create the HOB so that it can be used to verify
1255 // that capsules don't get corrupted all the way into BDS. BDS will
1256 // still try to turn it into a firmware volume, but will think it's
1257 // corrupted so nothing will happen.
1260 CapsuleTestPattern (PeiServices
, (VOID
*) (UINTN
) NewBuffer
);
1264 // Build the UEFI Capsule Hob for each capsule image.
1266 for (Index
= 0; Index
< CapsuleNumber
; Index
++) {
1267 BaseAddress
= NewBuffer
+ PrivateData
->CapsuleOffset
[Index
];
1268 Length
= ((EFI_CAPSULE_HEADER
*)((UINTN
) BaseAddress
))->CapsuleImageSize
;
1270 BuildCvHob (BaseAddress
, Length
);
1276 CONST EFI_PEI_CAPSULE_PPI mCapsulePpi
= {
1282 CONST EFI_PEI_PPI_DESCRIPTOR mUefiPpiListCapsule
= {
1283 (EFI_PEI_PPI_DESCRIPTOR_PPI
| EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST
),
1284 &gEfiPeiCapsulePpiGuid
,
1285 (EFI_PEI_CAPSULE_PPI
*) &mCapsulePpi
1289 Entry point function for the PEIM
1291 @param FileHandle Handle of the file being invoked.
1292 @param PeiServices Describes the list of possible PEI Services.
1294 @return EFI_SUCCESS If we installed our PPI
1300 IN EFI_PEI_FILE_HANDLE FileHandle
,
1301 IN CONST EFI_PEI_SERVICES
**PeiServices
1305 // Just produce our PPI
1307 return PeiServicesInstallPpi (&mUefiPpiListCapsule
);