2 Capsule update PEIM for UEFI2.0
4 Copyright (c) 2006 - 2017, Intel Corporation. All rights reserved.<BR>
5 Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR>
7 This program and the accompanying materials
8 are licensed and made available under the terms and conditions
9 of the BSD License which accompanies this distribution. The
10 full text of the license may be found at
11 http://opensource.org/licenses/bsd-license.php
13 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
14 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
22 // Global Descriptor Table (GDT)
24 GLOBAL_REMOVE_IF_UNREFERENCED IA32_SEGMENT_DESCRIPTOR mGdtEntries
[] = {
25 /* selector { Global Segment Descriptor } */
26 /* 0x00 */ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, //null descriptor
27 /* 0x08 */ {{0xffff, 0, 0, 0x3, 1, 0, 1, 0xf, 0, 0, 1, 1, 0}}, //linear data segment descriptor
28 /* 0x10 */ {{0xffff, 0, 0, 0xf, 1, 0, 1, 0xf, 0, 0, 1, 1, 0}}, //linear code segment descriptor
29 /* 0x18 */ {{0xffff, 0, 0, 0x3, 1, 0, 1, 0xf, 0, 0, 1, 1, 0}}, //system data segment descriptor
30 /* 0x20 */ {{0xffff, 0, 0, 0xb, 1, 0, 1, 0xf, 0, 0, 1, 1, 0}}, //system code segment descriptor
31 /* 0x28 */ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, //spare segment descriptor
32 /* 0x30 */ {{0xffff, 0, 0, 0x3, 1, 0, 1, 0xf, 0, 0, 1, 1, 0}}, //system data segment descriptor
33 /* 0x38 */ {{0xffff, 0, 0, 0xb, 1, 0, 1, 0xf, 0, 1, 0, 1, 0}}, //system code segment descriptor
34 /* 0x40 */ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, //spare segment descriptor
40 GLOBAL_REMOVE_IF_UNREFERENCED CONST IA32_DESCRIPTOR mGdt
= {
41 sizeof (mGdtEntries
) - 1,
47 The function will check if 1G page is supported.
49 @retval TRUE 1G page is supported.
50 @retval FALSE 1G page is not supported.
60 BOOLEAN Page1GSupport
;
62 Page1GSupport
= FALSE
;
63 if (PcdGetBool(PcdUse1GPageTable
)) {
64 AsmCpuid (0x80000000, &RegEax
, NULL
, NULL
, NULL
);
65 if (RegEax
>= 0x80000001) {
66 AsmCpuid (0x80000001, NULL
, NULL
, NULL
, &RegEdx
);
67 if ((RegEdx
& BIT26
) != 0) {
77 Calculate the total size of page table.
79 @param[in] Page1GSupport 1G page support or not.
81 @return The size of page table.
85 CalculatePageTableSize (
86 IN BOOLEAN Page1GSupport
89 UINTN ExtraPageTablePages
;
91 UINT8 PhysicalAddressBits
;
92 UINT32 NumberOfPml4EntriesNeeded
;
93 UINT32 NumberOfPdpEntriesNeeded
;
96 // Create 4G page table by default,
97 // and let PF handler to handle > 4G request.
99 PhysicalAddressBits
= 32;
100 ExtraPageTablePages
= EXTRA_PAGE_TABLE_PAGES
;
103 // Calculate the table entries needed.
105 if (PhysicalAddressBits
<= 39 ) {
106 NumberOfPml4EntriesNeeded
= 1;
107 NumberOfPdpEntriesNeeded
= (UINT32
)LShiftU64 (1, (PhysicalAddressBits
- 30));
109 NumberOfPml4EntriesNeeded
= (UINT32
)LShiftU64 (1, (PhysicalAddressBits
- 39));
110 NumberOfPdpEntriesNeeded
= 512;
113 if (!Page1GSupport
) {
114 TotalPagesNum
= (NumberOfPdpEntriesNeeded
+ 1) * NumberOfPml4EntriesNeeded
+ 1;
116 TotalPagesNum
= NumberOfPml4EntriesNeeded
+ 1;
118 TotalPagesNum
+= ExtraPageTablePages
;
120 return EFI_PAGES_TO_SIZE (TotalPagesNum
);
124 Allocates and fills in the Page Directory and Page Table Entries to
125 establish a 4G page table.
127 @param[in] PageTablesAddress The base address of page table.
128 @param[in] Page1GSupport 1G page support or not.
133 IN EFI_PHYSICAL_ADDRESS PageTablesAddress
,
134 IN BOOLEAN Page1GSupport
137 UINT8 PhysicalAddressBits
;
138 EFI_PHYSICAL_ADDRESS PageAddress
;
139 UINTN IndexOfPml4Entries
;
140 UINTN IndexOfPdpEntries
;
141 UINTN IndexOfPageDirectoryEntries
;
142 UINT32 NumberOfPml4EntriesNeeded
;
143 UINT32 NumberOfPdpEntriesNeeded
;
144 PAGE_MAP_AND_DIRECTORY_POINTER
*PageMapLevel4Entry
;
145 PAGE_MAP_AND_DIRECTORY_POINTER
*PageMap
;
146 PAGE_MAP_AND_DIRECTORY_POINTER
*PageDirectoryPointerEntry
;
147 PAGE_TABLE_ENTRY
*PageDirectoryEntry
;
148 UINTN BigPageAddress
;
149 PAGE_TABLE_1G_ENTRY
*PageDirectory1GEntry
;
150 UINT64 AddressEncMask
;
153 // Make sure AddressEncMask is contained to smallest supported address field.
155 AddressEncMask
= PcdGet64 (PcdPteMemoryEncryptionAddressOrMask
) & PAGING_1G_ADDRESS_MASK_64
;
158 // Create 4G page table by default,
159 // and let PF handler to handle > 4G request.
161 PhysicalAddressBits
= 32;
164 // Calculate the table entries needed.
166 if (PhysicalAddressBits
<= 39 ) {
167 NumberOfPml4EntriesNeeded
= 1;
168 NumberOfPdpEntriesNeeded
= (UINT32
)LShiftU64 (1, (PhysicalAddressBits
- 30));
170 NumberOfPml4EntriesNeeded
= (UINT32
)LShiftU64 (1, (PhysicalAddressBits
- 39));
171 NumberOfPdpEntriesNeeded
= 512;
175 // Pre-allocate big pages to avoid later allocations.
177 BigPageAddress
= (UINTN
) PageTablesAddress
;
180 // By architecture only one PageMapLevel4 exists - so lets allocate storage for it.
182 PageMap
= (VOID
*) BigPageAddress
;
183 BigPageAddress
+= SIZE_4KB
;
185 PageMapLevel4Entry
= PageMap
;
187 for (IndexOfPml4Entries
= 0; IndexOfPml4Entries
< NumberOfPml4EntriesNeeded
; IndexOfPml4Entries
++, PageMapLevel4Entry
++) {
189 // Each PML4 entry points to a page of Page Directory Pointer entires.
190 // So lets allocate space for them and fill them in in the IndexOfPdpEntries loop.
192 PageDirectoryPointerEntry
= (VOID
*) BigPageAddress
;
193 BigPageAddress
+= SIZE_4KB
;
198 PageMapLevel4Entry
->Uint64
= (UINT64
)(UINTN
)PageDirectoryPointerEntry
| AddressEncMask
;
199 PageMapLevel4Entry
->Bits
.ReadWrite
= 1;
200 PageMapLevel4Entry
->Bits
.Present
= 1;
203 PageDirectory1GEntry
= (VOID
*) PageDirectoryPointerEntry
;
205 for (IndexOfPageDirectoryEntries
= 0; IndexOfPageDirectoryEntries
< 512; IndexOfPageDirectoryEntries
++, PageDirectory1GEntry
++, PageAddress
+= SIZE_1GB
) {
207 // Fill in the Page Directory entries
209 PageDirectory1GEntry
->Uint64
= (UINT64
)PageAddress
| AddressEncMask
;
210 PageDirectory1GEntry
->Bits
.ReadWrite
= 1;
211 PageDirectory1GEntry
->Bits
.Present
= 1;
212 PageDirectory1GEntry
->Bits
.MustBe1
= 1;
215 for (IndexOfPdpEntries
= 0; IndexOfPdpEntries
< NumberOfPdpEntriesNeeded
; IndexOfPdpEntries
++, PageDirectoryPointerEntry
++) {
217 // Each Directory Pointer entries points to a page of Page Directory entires.
218 // So allocate space for them and fill them in in the IndexOfPageDirectoryEntries loop.
220 PageDirectoryEntry
= (VOID
*) BigPageAddress
;
221 BigPageAddress
+= SIZE_4KB
;
224 // Fill in a Page Directory Pointer Entries
226 PageDirectoryPointerEntry
->Uint64
= (UINT64
)(UINTN
)PageDirectoryEntry
| AddressEncMask
;
227 PageDirectoryPointerEntry
->Bits
.ReadWrite
= 1;
228 PageDirectoryPointerEntry
->Bits
.Present
= 1;
230 for (IndexOfPageDirectoryEntries
= 0; IndexOfPageDirectoryEntries
< 512; IndexOfPageDirectoryEntries
++, PageDirectoryEntry
++, PageAddress
+= SIZE_2MB
) {
232 // Fill in the Page Directory entries
234 PageDirectoryEntry
->Uint64
= (UINT64
)PageAddress
| AddressEncMask
;
235 PageDirectoryEntry
->Bits
.ReadWrite
= 1;
236 PageDirectoryEntry
->Bits
.Present
= 1;
237 PageDirectoryEntry
->Bits
.MustBe1
= 1;
241 for (; IndexOfPdpEntries
< 512; IndexOfPdpEntries
++, PageDirectoryPointerEntry
++) {
243 PageDirectoryPointerEntry
,
244 sizeof(PAGE_MAP_AND_DIRECTORY_POINTER
)
251 // For the PML4 entries we are not using fill in a null entry.
253 for (; IndexOfPml4Entries
< 512; IndexOfPml4Entries
++, PageMapLevel4Entry
++) {
256 sizeof (PAGE_MAP_AND_DIRECTORY_POINTER
)
262 Return function from long mode to 32-bit mode.
264 @param EntrypointContext Context for mode switching
265 @param ReturnContext Context for mode switching
270 SWITCH_32_TO_64_CONTEXT
*EntrypointContext
,
271 SWITCH_64_TO_32_CONTEXT
*ReturnContext
275 // Restore original GDT
277 AsmWriteGdtr (&ReturnContext
->Gdtr
);
280 // return to original caller
282 LongJump ((BASE_LIBRARY_JUMP_BUFFER
*)(UINTN
)EntrypointContext
->JumpBuffer
, 1);
291 Thunk function from 32-bit protection mode to long mode.
293 @param PageTableAddress Page table base address
294 @param Context Context for mode switching
295 @param ReturnContext Context for mode switching
297 @retval EFI_SUCCESS Function successfully executed.
302 EFI_PHYSICAL_ADDRESS PageTableAddress
,
303 SWITCH_32_TO_64_CONTEXT
*Context
,
304 SWITCH_64_TO_32_CONTEXT
*ReturnContext
311 // Save return address, LongJump will return here then
313 SetJumpFlag
= SetJump ((BASE_LIBRARY_JUMP_BUFFER
*) (UINTN
) Context
->JumpBuffer
);
315 if (SetJumpFlag
== 0) {
318 // Build 4G Page Tables.
320 Create4GPageTables (PageTableAddress
, Context
->Page1GSupport
);
325 AsmWriteGdtr (&mGdt
);
330 AsmWriteCr3 ((UINTN
) PageTableAddress
);
334 "%a() Stack Base: 0x%lx, Stack Size: 0x%lx\n",
336 Context
->StackBufferBase
,
337 Context
->StackBufferLength
341 // Disable interrupt of Debug timer, since the IDT table cannot work in long mode
343 SaveAndSetDebugTimerInterrupt (FALSE
);
345 // Transfer to long mode
349 (UINT64
) Context
->EntryPoint
,
350 (UINT64
)(UINTN
) Context
,
351 (UINT64
)(UINTN
) ReturnContext
,
352 Context
->StackBufferBase
+ Context
->StackBufferLength
357 // Convert to 32-bit Status and return
359 Status
= EFI_SUCCESS
;
360 if ((UINTN
) ReturnContext
->ReturnStatus
!= 0) {
361 Status
= ENCODE_ERROR ((UINTN
) ReturnContext
->ReturnStatus
);
368 If in 32 bit protection mode, and coalesce image is of X64, switch to long mode.
370 @param LongModeBuffer The context of long mode.
371 @param CoalesceEntry Entry of coalesce image.
372 @param BlockListAddr Address of block list.
373 @param MemoryResource Pointer to the buffer of memory resource descriptor.
374 @param MemoryBase Base of memory range.
375 @param MemorySize Size of memory range.
377 @retval EFI_SUCCESS Successfully switched to long mode and execute coalesce.
378 @retval Others Failed to execute coalesce in long mode.
383 IN EFI_CAPSULE_LONG_MODE_BUFFER
*LongModeBuffer
,
384 IN COALESCE_ENTRY CoalesceEntry
,
385 IN EFI_PHYSICAL_ADDRESS BlockListAddr
,
386 IN MEMORY_RESOURCE_DESCRIPTOR
*MemoryResource
,
387 IN OUT VOID
**MemoryBase
,
388 IN OUT UINTN
*MemorySize
392 EFI_PHYSICAL_ADDRESS MemoryBase64
;
394 EFI_PHYSICAL_ADDRESS MemoryEnd64
;
395 SWITCH_32_TO_64_CONTEXT Context
;
396 SWITCH_64_TO_32_CONTEXT ReturnContext
;
397 BASE_LIBRARY_JUMP_BUFFER JumpBuffer
;
398 EFI_PHYSICAL_ADDRESS ReservedRangeBase
;
399 EFI_PHYSICAL_ADDRESS ReservedRangeEnd
;
400 BOOLEAN Page1GSupport
;
402 ZeroMem (&Context
, sizeof (SWITCH_32_TO_64_CONTEXT
));
403 ZeroMem (&ReturnContext
, sizeof (SWITCH_64_TO_32_CONTEXT
));
405 MemoryBase64
= (UINT64
) (UINTN
) *MemoryBase
;
406 MemorySize64
= (UINT64
) (UINTN
) *MemorySize
;
407 MemoryEnd64
= MemoryBase64
+ MemorySize64
;
409 Page1GSupport
= IsPage1GSupport ();
412 // Merge memory range reserved for stack and page table
414 if (LongModeBuffer
->StackBaseAddress
< LongModeBuffer
->PageTableAddress
) {
415 ReservedRangeBase
= LongModeBuffer
->StackBaseAddress
;
416 ReservedRangeEnd
= LongModeBuffer
->PageTableAddress
+ CalculatePageTableSize (Page1GSupport
);
418 ReservedRangeBase
= LongModeBuffer
->PageTableAddress
;
419 ReservedRangeEnd
= LongModeBuffer
->StackBaseAddress
+ LongModeBuffer
->StackSize
;
423 // Check if memory range reserved is overlap with MemoryBase ~ MemoryBase + MemorySize.
424 // If they are overlapped, get a larger range to process capsule data.
426 if (ReservedRangeBase
<= MemoryBase64
) {
427 if (ReservedRangeEnd
< MemoryEnd64
) {
428 MemoryBase64
= ReservedRangeEnd
;
430 DEBUG ((EFI_D_ERROR
, "Memory is not enough to process capsule!\n"));
431 return EFI_OUT_OF_RESOURCES
;
433 } else if (ReservedRangeBase
< MemoryEnd64
) {
434 if (ReservedRangeEnd
< MemoryEnd64
&&
435 ReservedRangeBase
- MemoryBase64
< MemoryEnd64
- ReservedRangeEnd
) {
436 MemoryBase64
= ReservedRangeEnd
;
438 MemorySize64
= (UINT64
)(UINTN
)(ReservedRangeBase
- MemoryBase64
);
443 // Initialize context jumping to 64-bit enviroment
445 Context
.JumpBuffer
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)&JumpBuffer
;
446 Context
.StackBufferBase
= LongModeBuffer
->StackBaseAddress
;
447 Context
.StackBufferLength
= LongModeBuffer
->StackSize
;
448 Context
.EntryPoint
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)CoalesceEntry
;
449 Context
.BlockListAddr
= BlockListAddr
;
450 Context
.MemoryResource
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)MemoryResource
;
451 Context
.MemoryBase64Ptr
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)&MemoryBase64
;
452 Context
.MemorySize64Ptr
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)&MemorySize64
;
453 Context
.Page1GSupport
= Page1GSupport
;
454 Context
.AddressEncMask
= PcdGet64 (PcdPteMemoryEncryptionAddressOrMask
) & PAGING_1G_ADDRESS_MASK_64
;
457 // Prepare data for return back
459 ReturnContext
.ReturnCs
= 0x10;
460 ReturnContext
.ReturnEntryPoint
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)ReturnFunction
;
462 // Will save the return status of processing capsule
464 ReturnContext
.ReturnStatus
= 0;
469 AsmReadGdtr ((IA32_DESCRIPTOR
*)&ReturnContext
.Gdtr
);
471 Status
= Thunk32To64 (LongModeBuffer
->PageTableAddress
, &Context
, &ReturnContext
);
473 if (!EFI_ERROR (Status
)) {
474 *MemoryBase
= (VOID
*) (UINTN
) MemoryBase64
;
475 *MemorySize
= (UINTN
) MemorySize64
;
483 Locates the coalesce image entry point, and detects its machine type.
485 @param CoalesceImageEntryPoint Pointer to coalesce image entry point for output.
486 @param CoalesceImageMachineType Pointer to machine type of coalesce image.
488 @retval EFI_SUCCESS Coalesce image successfully located.
489 @retval Others Failed to locate the coalesce image.
493 FindCapsuleCoalesceImage (
494 OUT EFI_PHYSICAL_ADDRESS
*CoalesceImageEntryPoint
,
495 OUT UINT16
*CoalesceImageMachineType
500 EFI_PEI_LOAD_FILE_PPI
*LoadFile
;
501 EFI_PEI_FV_HANDLE VolumeHandle
;
502 EFI_PEI_FILE_HANDLE FileHandle
;
503 EFI_PHYSICAL_ADDRESS CoalesceImageAddress
;
504 UINT64 CoalesceImageSize
;
505 UINT32 AuthenticationState
;
510 Status
= PeiServicesFfsFindNextVolume (Instance
++, &VolumeHandle
);
511 if (EFI_ERROR (Status
)) {
514 Status
= PeiServicesFfsFindFileByName (PcdGetPtr(PcdCapsuleCoalesceFile
), VolumeHandle
, &FileHandle
);
515 if (!EFI_ERROR (Status
)) {
516 Status
= PeiServicesLocatePpi (&gEfiPeiLoadFilePpiGuid
, 0, NULL
, (VOID
**) &LoadFile
);
517 ASSERT_EFI_ERROR (Status
);
519 Status
= LoadFile
->LoadFile (
522 &CoalesceImageAddress
,
524 CoalesceImageEntryPoint
,
527 if (EFI_ERROR (Status
)) {
528 DEBUG ((EFI_D_ERROR
, "Unable to find PE32 section in CapsuleX64 image ffs %r!\n", Status
));
531 *CoalesceImageMachineType
= PeCoffLoaderGetMachineType ((VOID
*) (UINTN
) CoalesceImageAddress
);
542 Gets the reserved long mode buffer.
544 @param LongModeBuffer Pointer to the long mode buffer for output.
546 @retval EFI_SUCCESS Long mode buffer successfully retrieved.
547 @retval Others Variable storing long mode buffer not found.
552 OUT EFI_CAPSULE_LONG_MODE_BUFFER
*LongModeBuffer
557 EFI_PEI_READ_ONLY_VARIABLE2_PPI
*PPIVariableServices
;
559 Status
= PeiServicesLocatePpi (
560 &gEfiPeiReadOnlyVariable2PpiGuid
,
563 (VOID
**) &PPIVariableServices
565 ASSERT_EFI_ERROR (Status
);
567 Size
= sizeof (EFI_CAPSULE_LONG_MODE_BUFFER
);
568 Status
= PPIVariableServices
->GetVariable (
570 EFI_CAPSULE_LONG_MODE_BUFFER_NAME
,
571 &gEfiCapsuleVendorGuid
,
576 if (EFI_ERROR (Status
)) {
577 DEBUG (( EFI_D_ERROR
, "Error Get LongModeBuffer variable %r!\n", Status
));
583 #if defined (MDE_CPU_IA32) || defined (MDE_CPU_X64)
585 Get physical address bits.
587 @return Physical address bits.
591 GetPhysicalAddressBits (
596 UINT8 PhysicalAddressBits
;
600 // Get physical address bits supported.
602 Hob
= GetFirstHob (EFI_HOB_TYPE_CPU
);
604 PhysicalAddressBits
= ((EFI_HOB_CPU
*) Hob
)->SizeOfMemorySpace
;
606 AsmCpuid (0x80000000, &RegEax
, NULL
, NULL
, NULL
);
607 if (RegEax
>= 0x80000008) {
608 AsmCpuid (0x80000008, &RegEax
, NULL
, NULL
, NULL
);
609 PhysicalAddressBits
= (UINT8
) RegEax
;
611 PhysicalAddressBits
= 36;
616 // IA-32e paging translates 48-bit linear addresses to 52-bit physical addresses.
618 ASSERT (PhysicalAddressBits
<= 52);
619 if (PhysicalAddressBits
> 48) {
620 PhysicalAddressBits
= 48;
623 return PhysicalAddressBits
;
628 Build memory resource descriptor from resource descriptor in HOB list.
630 @return Pointer to the buffer of memory resource descriptor.
631 NULL if no memory resource descriptor reported in HOB list
632 before capsule Coalesce.
635 MEMORY_RESOURCE_DESCRIPTOR
*
636 BuildMemoryResourceDescriptor (
640 EFI_PEI_HOB_POINTERS Hob
;
642 EFI_HOB_RESOURCE_DESCRIPTOR
*ResourceDescriptor
;
643 MEMORY_RESOURCE_DESCRIPTOR
*MemoryResource
;
647 // Get the count of memory resource descriptor.
650 Hob
.Raw
= GetFirstHob (EFI_HOB_TYPE_RESOURCE_DESCRIPTOR
);
651 while (Hob
.Raw
!= NULL
) {
652 ResourceDescriptor
= (EFI_HOB_RESOURCE_DESCRIPTOR
*) Hob
.Raw
;
653 if (ResourceDescriptor
->ResourceType
== EFI_RESOURCE_SYSTEM_MEMORY
) {
656 Hob
.Raw
= GET_NEXT_HOB (Hob
);
657 Hob
.Raw
= GetNextHob (EFI_HOB_TYPE_RESOURCE_DESCRIPTOR
, Hob
.Raw
);
661 DEBUG ((EFI_D_INFO
| EFI_D_WARN
, "No memory resource descriptor reported in HOB list before capsule Coalesce\n"));
662 #if defined (MDE_CPU_IA32) || defined (MDE_CPU_X64)
664 // Allocate memory to hold memory resource descriptor,
665 // include extra one NULL terminate memory resource descriptor.
667 Status
= PeiServicesAllocatePool ((1 + 1) * sizeof (MEMORY_RESOURCE_DESCRIPTOR
), (VOID
**) &MemoryResource
);
668 ASSERT_EFI_ERROR (Status
);
669 ZeroMem (MemoryResource
, (1 + 1) * sizeof (MEMORY_RESOURCE_DESCRIPTOR
));
671 MemoryResource
[0].PhysicalStart
= 0;
672 MemoryResource
[0].ResourceLength
= LShiftU64 (1, GetPhysicalAddressBits ());
673 DEBUG ((EFI_D_INFO
, "MemoryResource[0x0] - Start(0x%0lx) Length(0x%0lx)\n",
674 MemoryResource
[0x0].PhysicalStart
, MemoryResource
[0x0].ResourceLength
));
675 return MemoryResource
;
682 // Allocate memory to hold memory resource descriptor,
683 // include extra one NULL terminate memory resource descriptor.
685 Status
= PeiServicesAllocatePool ((Index
+ 1) * sizeof (MEMORY_RESOURCE_DESCRIPTOR
), (VOID
**) &MemoryResource
);
686 ASSERT_EFI_ERROR (Status
);
687 ZeroMem (MemoryResource
, (Index
+ 1) * sizeof (MEMORY_RESOURCE_DESCRIPTOR
));
690 // Get the content of memory resource descriptor.
693 Hob
.Raw
= GetFirstHob (EFI_HOB_TYPE_RESOURCE_DESCRIPTOR
);
694 while (Hob
.Raw
!= NULL
) {
695 ResourceDescriptor
= (EFI_HOB_RESOURCE_DESCRIPTOR
*) Hob
.Raw
;
696 if (ResourceDescriptor
->ResourceType
== EFI_RESOURCE_SYSTEM_MEMORY
) {
697 DEBUG ((EFI_D_INFO
, "MemoryResource[0x%x] - Start(0x%0lx) Length(0x%0lx)\n",
698 Index
, ResourceDescriptor
->PhysicalStart
, ResourceDescriptor
->ResourceLength
));
699 MemoryResource
[Index
].PhysicalStart
= ResourceDescriptor
->PhysicalStart
;
700 MemoryResource
[Index
].ResourceLength
= ResourceDescriptor
->ResourceLength
;
703 Hob
.Raw
= GET_NEXT_HOB (Hob
);
704 Hob
.Raw
= GetNextHob (EFI_HOB_TYPE_RESOURCE_DESCRIPTOR
, Hob
.Raw
);
707 return MemoryResource
;
711 Checks for the presence of capsule descriptors.
712 Get capsule descriptors from variable CapsuleUpdateData, CapsuleUpdateData1, CapsuleUpdateData2...
713 and save to DescriptorBuffer.
715 @param DescriptorBuffer Pointer to the capsule descriptors
717 @retval EFI_SUCCESS a valid capsule is present
718 @retval EFI_NOT_FOUND if a valid capsule is not present
721 GetCapsuleDescriptors (
722 IN EFI_PHYSICAL_ADDRESS
*DescriptorBuffer
731 CHAR16 CapsuleVarName
[30];
733 EFI_PHYSICAL_ADDRESS CapsuleDataPtr64
;
734 EFI_PEI_READ_ONLY_VARIABLE2_PPI
*PPIVariableServices
;
738 CapsuleVarName
[0] = 0;
740 CapsuleDataPtr64
= 0;
742 Status
= PeiServicesLocatePpi (
743 &gEfiPeiReadOnlyVariable2PpiGuid
,
746 (VOID
**) &PPIVariableServices
748 if (Status
== EFI_SUCCESS
) {
749 StrCpyS (CapsuleVarName
, sizeof(CapsuleVarName
)/sizeof(CHAR16
), EFI_CAPSULE_VARIABLE_NAME
);
750 TempVarName
= CapsuleVarName
+ StrLen (CapsuleVarName
);
751 Size
= sizeof (CapsuleDataPtr64
);
755 // For the first Capsule Image
757 Status
= PPIVariableServices
->GetVariable (
760 &gEfiCapsuleVendorGuid
,
763 (VOID
*) &CapsuleDataPtr64
765 if (EFI_ERROR (Status
)) {
766 DEBUG ((DEBUG_INFO
, "Capsule -- capsule variable not set\n"));
767 return EFI_NOT_FOUND
;
770 // We have a chicken/egg situation where the memory init code needs to
771 // know the boot mode prior to initializing memory. For this case, our
772 // validate function will fail. We can detect if this is the case if blocklist
773 // pointer is null. In that case, return success since we know that the
776 if (DescriptorBuffer
== NULL
) {
780 UnicodeValueToStringS (
782 sizeof (CapsuleVarName
) - ((UINTN
)TempVarName
- (UINTN
)CapsuleVarName
),
787 Status
= PPIVariableServices
->GetVariable (
790 &gEfiCapsuleVendorGuid
,
793 (VOID
*) &CapsuleDataPtr64
795 if (EFI_ERROR (Status
)) {
800 // If this BlockList has been linked before, skip this variable
803 for (TempIndex
= 0; TempIndex
< ValidIndex
; TempIndex
++) {
804 if (DescriptorBuffer
[TempIndex
] == CapsuleDataPtr64
) {
816 // Cache BlockList which has been processed
818 DescriptorBuffer
[ValidIndex
++] = CapsuleDataPtr64
;
827 Capsule PPI service to coalesce a fragmented capsule in memory.
829 @param PeiServices General purpose services available to every PEIM.
830 @param MemoryBase Pointer to the base of a block of memory that we can walk
831 all over while trying to coalesce our buffers.
832 On output, this variable will hold the base address of
834 @param MemorySize Size of the memory region pointed to by MemoryBase.
835 On output, this variable will contain the size of the
838 @retval EFI_NOT_FOUND if we can't determine the boot mode
839 if the boot mode is not flash-update
840 if we could not find the capsule descriptors
842 @retval EFI_BUFFER_TOO_SMALL
843 if we could not coalesce the capsule in the memory
844 region provided to us
846 @retval EFI_SUCCESS if there's no capsule, or if we processed the
847 capsule successfully.
852 IN EFI_PEI_SERVICES
**PeiServices
,
853 IN OUT VOID
**MemoryBase
,
854 IN OUT UINTN
*MemorySize
860 CHAR16 CapsuleVarName
[30];
862 EFI_PHYSICAL_ADDRESS CapsuleDataPtr64
;
864 EFI_BOOT_MODE BootMode
;
865 EFI_PEI_READ_ONLY_VARIABLE2_PPI
*PPIVariableServices
;
866 EFI_PHYSICAL_ADDRESS
*VariableArrayAddress
;
867 MEMORY_RESOURCE_DESCRIPTOR
*MemoryResource
;
869 UINT16 CoalesceImageMachineType
;
870 EFI_PHYSICAL_ADDRESS CoalesceImageEntryPoint
;
871 COALESCE_ENTRY CoalesceEntry
;
872 EFI_CAPSULE_LONG_MODE_BUFFER LongModeBuffer
;
877 CapsuleVarName
[0] = 0;
878 CapsuleDataPtr64
= 0;
881 // Someone should have already ascertained the boot mode. If it's not
882 // capsule update, then return normally.
884 Status
= PeiServicesGetBootMode (&BootMode
);
885 if (EFI_ERROR (Status
) || (BootMode
!= BOOT_ON_FLASH_UPDATE
)) {
886 DEBUG ((EFI_D_ERROR
, "Boot mode is not correct for capsule update path.\n"));
887 Status
= EFI_NOT_FOUND
;
892 // User may set the same ScatterGatherList with several different variables,
893 // so cache all ScatterGatherList for check later.
895 Status
= PeiServicesLocatePpi (
896 &gEfiPeiReadOnlyVariable2PpiGuid
,
899 (VOID
**) &PPIVariableServices
901 if (EFI_ERROR (Status
)) {
904 Size
= sizeof (CapsuleDataPtr64
);
905 StrCpyS (CapsuleVarName
, sizeof(CapsuleVarName
)/sizeof(CHAR16
), EFI_CAPSULE_VARIABLE_NAME
);
906 TempVarName
= CapsuleVarName
+ StrLen (CapsuleVarName
);
909 UnicodeValueToStringS (
911 sizeof (CapsuleVarName
) - ((UINTN
)TempVarName
- (UINTN
)CapsuleVarName
),
917 Status
= PPIVariableServices
->GetVariable (
920 &gEfiCapsuleVendorGuid
,
923 (VOID
*) &CapsuleDataPtr64
925 if (EFI_ERROR (Status
)) {
927 // There is no capsule variables, quit
929 DEBUG ((EFI_D_INFO
,"Capsule variable Index = %d\n", Index
));
936 DEBUG ((EFI_D_INFO
,"Capsule variable count = %d\n", VariableCount
));
939 // The last entry is the end flag.
941 Status
= PeiServicesAllocatePool (
942 (VariableCount
+ 1) * sizeof (EFI_PHYSICAL_ADDRESS
),
943 (VOID
**)&VariableArrayAddress
946 if (Status
!= EFI_SUCCESS
) {
947 DEBUG ((EFI_D_ERROR
, "AllocatePages Failed!, Status = %x\n", Status
));
951 ZeroMem (VariableArrayAddress
, (VariableCount
+ 1) * sizeof (EFI_PHYSICAL_ADDRESS
));
954 // Find out if we actually have a capsule.
955 // GetCapsuleDescriptors depends on variable PPI, so it should run in 32-bit environment.
957 Status
= GetCapsuleDescriptors (VariableArrayAddress
);
958 if (EFI_ERROR (Status
)) {
959 DEBUG ((EFI_D_ERROR
, "Fail to find capsule variables.\n"));
963 MemoryResource
= BuildMemoryResourceDescriptor ();
966 if (FeaturePcdGet (PcdDxeIplSwitchToLongMode
)) {
968 // Switch to 64-bit mode to process capsule data when:
969 // 1. When DXE phase is 64-bit
970 // 2. When the buffer for 64-bit transition exists
971 // 3. When Capsule X64 image is built in BIOS image
972 // In 64-bit mode, we can process capsule data above 4GB.
974 CoalesceImageEntryPoint
= 0;
975 Status
= GetLongModeContext (&LongModeBuffer
);
976 if (EFI_ERROR (Status
)) {
977 DEBUG ((EFI_D_ERROR
, "Fail to find the variable for long mode context!\n"));
978 Status
= EFI_NOT_FOUND
;
982 Status
= FindCapsuleCoalesceImage (&CoalesceImageEntryPoint
, &CoalesceImageMachineType
);
983 if ((EFI_ERROR (Status
)) || (CoalesceImageMachineType
!= EFI_IMAGE_MACHINE_X64
)) {
984 DEBUG ((EFI_D_ERROR
, "Fail to find CapsuleX64 module in FV!\n"));
985 Status
= EFI_NOT_FOUND
;
988 ASSERT (CoalesceImageEntryPoint
!= 0);
989 CoalesceEntry
= (COALESCE_ENTRY
) (UINTN
) CoalesceImageEntryPoint
;
990 Status
= ModeSwitch (&LongModeBuffer
, CoalesceEntry
, (EFI_PHYSICAL_ADDRESS
)(UINTN
)VariableArrayAddress
, MemoryResource
, MemoryBase
, MemorySize
);
993 // Capsule is processed in IA32 mode.
995 Status
= CapsuleDataCoalesce (PeiServices
, (EFI_PHYSICAL_ADDRESS
*)(UINTN
)VariableArrayAddress
, MemoryResource
, MemoryBase
, MemorySize
);
999 // Process capsule directly.
1001 Status
= CapsuleDataCoalesce (PeiServices
, (EFI_PHYSICAL_ADDRESS
*)(UINTN
)VariableArrayAddress
, MemoryResource
, MemoryBase
, MemorySize
);
1004 DEBUG ((EFI_D_INFO
, "Capsule Coalesce Status = %r!\n", Status
));
1006 if (Status
== EFI_BUFFER_TOO_SMALL
) {
1007 DEBUG ((EFI_D_ERROR
, "There is not enough memory to process capsule!\n"));
1010 if (Status
== EFI_NOT_FOUND
) {
1011 DEBUG ((EFI_D_ERROR
, "Fail to parse capsule descriptor in memory!\n"));
1012 REPORT_STATUS_CODE (
1013 EFI_ERROR_CODE
| EFI_ERROR_MAJOR
,
1014 (EFI_SOFTWARE_PEI_MODULE
| EFI_SW_PEI_EC_INVALID_CAPSULE_DESCRIPTOR
)
1023 Determine if we're in capsule update boot mode.
1025 @param PeiServices PEI services table
1027 @retval EFI_SUCCESS if we have a capsule available
1028 @retval EFI_NOT_FOUND no capsule detected
1033 CheckCapsuleUpdate (
1034 IN EFI_PEI_SERVICES
**PeiServices
1038 Status
= GetCapsuleDescriptors (NULL
);
1042 This function will look at a capsule and determine if it's a test pattern.
1043 If it is, then it will verify it and emit an error message if corruption is detected.
1045 @param PeiServices Standard pei services pointer
1046 @param CapsuleBase Base address of coalesced capsule, which is preceeded
1047 by private data. Very implementation specific.
1049 @retval TRUE Capsule image is the test image
1050 @retval FALSE Capsule image is not the test image.
1054 CapsuleTestPattern (
1055 IN EFI_PEI_SERVICES
**PeiServices
,
1056 IN VOID
*CapsuleBase
1067 // Look at the capsule data and determine if it's a test pattern. If it
1068 // is, then test it now.
1070 TestPtr
= (UINT32
*) CapsuleBase
;
1072 // 0x54534554 "TEST"
1074 if (*TestPtr
== 0x54534554) {
1076 DEBUG ((EFI_D_INFO
, "Capsule test pattern mode activated...\n"));
1077 TestSize
= TestPtr
[1] / sizeof (UINT32
);
1079 // Skip over the signature and the size fields in the pattern data header
1083 while (TestSize
> 0) {
1084 if (*TestPtr
!= TestCounter
) {
1085 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
));
1094 DEBUG ((EFI_D_INFO
, "Capsule test pattern mode SUCCESS\n"));
1101 Capsule PPI service that gets called after memory is available. The
1102 capsule coalesce function, which must be called first, returns a base
1103 address and size, which can be anything actually. Once the memory init
1104 PEIM has discovered memory, then it should call this function and pass in
1105 the base address and size returned by the coalesce function. Then this
1106 function can create a capsule HOB and return.
1108 @param PeiServices standard pei services pointer
1109 @param CapsuleBase address returned by the capsule coalesce function. Most
1110 likely this will actually be a pointer to private data.
1111 @param CapsuleSize value returned by the capsule coalesce function.
1113 @retval EFI_VOLUME_CORRUPTED CapsuleBase does not appear to point to a
1115 @retval EFI_SUCCESS if all goes well.
1120 IN EFI_PEI_SERVICES
**PeiServices
,
1121 IN VOID
*CapsuleBase
,
1122 IN UINTN CapsuleSize
1126 EFI_CAPSULE_PEIM_PRIVATE_DATA
*PrivateData
;
1128 EFI_PHYSICAL_ADDRESS NewBuffer
;
1129 UINTN CapsuleNumber
;
1131 EFI_PHYSICAL_ADDRESS BaseAddress
;
1134 PrivateData
= (EFI_CAPSULE_PEIM_PRIVATE_DATA
*) CapsuleBase
;
1135 if (PrivateData
->Signature
!= EFI_CAPSULE_PEIM_PRIVATE_DATA_SIGNATURE
) {
1136 return EFI_VOLUME_CORRUPTED
;
1138 if (PrivateData
->CapsuleAllImageSize
>= MAX_ADDRESS
) {
1139 DEBUG ((EFI_D_ERROR
, "CapsuleAllImageSize too big - 0x%lx\n", PrivateData
->CapsuleAllImageSize
));
1140 return EFI_OUT_OF_RESOURCES
;
1142 if (PrivateData
->CapsuleNumber
>= MAX_ADDRESS
) {
1143 DEBUG ((EFI_D_ERROR
, "CapsuleNumber too big - 0x%lx\n", PrivateData
->CapsuleNumber
));
1144 return EFI_OUT_OF_RESOURCES
;
1147 // Capsule Number and Capsule Offset is in the tail of Capsule data.
1149 Size
= (UINTN
)PrivateData
->CapsuleAllImageSize
;
1150 CapsuleNumber
= (UINTN
)PrivateData
->CapsuleNumber
;
1152 // Allocate the memory so that it gets preserved into DXE
1154 Status
= PeiServicesAllocatePages (
1155 EfiRuntimeServicesData
,
1156 EFI_SIZE_TO_PAGES (Size
),
1160 if (Status
!= EFI_SUCCESS
) {
1161 DEBUG ((EFI_D_ERROR
, "AllocatePages Failed!\n"));
1165 // Copy to our new buffer for DXE
1167 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
));
1168 CopyMem ((VOID
*) (UINTN
) NewBuffer
, (VOID
*) (UINTN
) ((UINT8
*)PrivateData
+ sizeof(EFI_CAPSULE_PEIM_PRIVATE_DATA
) + (CapsuleNumber
- 1) * sizeof(UINT64
)), Size
);
1170 // Check for test data pattern. If it is the test pattern, then we'll
1171 // test it and still create the HOB so that it can be used to verify
1172 // that capsules don't get corrupted all the way into BDS. BDS will
1173 // still try to turn it into a firmware volume, but will think it's
1174 // corrupted so nothing will happen.
1177 CapsuleTestPattern (PeiServices
, (VOID
*) (UINTN
) NewBuffer
);
1181 // Build the UEFI Capsule Hob for each capsule image.
1183 for (Index
= 0; Index
< CapsuleNumber
; Index
++) {
1184 BaseAddress
= NewBuffer
+ PrivateData
->CapsuleOffset
[Index
];
1185 Length
= ((EFI_CAPSULE_HEADER
*)((UINTN
) BaseAddress
))->CapsuleImageSize
;
1187 BuildCvHob (BaseAddress
, Length
);
1193 CONST EFI_PEI_CAPSULE_PPI mCapsulePpi
= {
1199 CONST EFI_PEI_PPI_DESCRIPTOR mUefiPpiListCapsule
= {
1200 (EFI_PEI_PPI_DESCRIPTOR_PPI
| EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST
),
1201 &gEfiPeiCapsulePpiGuid
,
1202 (EFI_PEI_CAPSULE_PPI
*) &mCapsulePpi
1206 Entry point function for the PEIM
1208 @param FileHandle Handle of the file being invoked.
1209 @param PeiServices Describes the list of possible PEI Services.
1211 @return EFI_SUCCESS If we installed our PPI
1217 IN EFI_PEI_FILE_HANDLE FileHandle
,
1218 IN CONST EFI_PEI_SERVICES
**PeiServices
1222 // Just produce our PPI
1224 return PeiServicesInstallPpi (&mUefiPpiListCapsule
);