2 Create the variable to save the base address of page table and stack
3 for transferring into long mode in IA32 capsule PEI.
5 Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR>
6 SPDX-License-Identifier: BSD-2-Clause-Patent
12 #include <Protocol/Capsule.h>
13 #include <Protocol/DxeSmmReadyToLock.h>
14 #include <Protocol/VariableLock.h>
16 #include <Guid/CapsuleVendor.h>
17 #include <Guid/AcpiS3Context.h>
19 #include <Library/DebugLib.h>
20 #include <Library/PcdLib.h>
21 #include <Library/UefiBootServicesTableLib.h>
22 #include <Library/UefiRuntimeServicesTableLib.h>
23 #include <Library/UefiRuntimeLib.h>
24 #include <Library/BaseLib.h>
25 #include <Library/UefiLib.h>
26 #include <Library/BaseMemoryLib.h>
29 // 8 extra pages for PF handler.
31 #define EXTRA_PAGE_TABLE_PAGES 8
34 Allocate EfiReservedMemoryType below 4G memory address.
36 This function allocates EfiReservedMemoryType below 4G memory address.
38 @param Size Size of memory to allocate.
40 @return Allocated Address for output.
44 AllocateReservedMemoryBelow4G (
49 EFI_PHYSICAL_ADDRESS Address
;
53 Pages
= EFI_SIZE_TO_PAGES (Size
);
56 Status
= gBS
->AllocatePages (
58 EfiReservedMemoryType
,
62 ASSERT_EFI_ERROR (Status
);
64 Buffer
= (VOID
*)(UINTN
)Address
;
65 ZeroMem (Buffer
, Size
);
71 Register callback function upon VariableLockProtocol
72 to lock EFI_CAPSULE_LONG_MODE_BUFFER_NAME variable to avoid malicious code to update it.
74 @param[in] Event Event whose notification function is being invoked.
75 @param[in] Context Pointer to the notification function's context.
79 VariableLockCapsuleLongModeBufferVariable (
85 EDKII_VARIABLE_LOCK_PROTOCOL
*VariableLock
;
88 // Mark EFI_CAPSULE_LONG_MODE_BUFFER_NAME variable to read-only if the Variable Lock protocol exists
90 Status
= gBS
->LocateProtocol (&gEdkiiVariableLockProtocolGuid
, NULL
, (VOID
**)&VariableLock
);
91 if (!EFI_ERROR (Status
)) {
92 Status
= VariableLock
->RequestToLock (VariableLock
, EFI_CAPSULE_LONG_MODE_BUFFER_NAME
, &gEfiCapsuleVendorGuid
);
93 ASSERT_EFI_ERROR (Status
);
98 1. Allocate Reserved memory for capsule PEIM to establish a 1:1 Virtual to Physical mapping.
99 2. Allocate Reserved memroy as a stack for capsule PEIM to transfer from 32-bit mdoe to 64-bit mode.
104 PrepareContextForCapsulePei (
108 UINTN ExtraPageTablePages
;
112 UINT8 PhysicalAddressBits
;
113 UINT32 NumberOfPml4EntriesNeeded
;
114 UINT32 NumberOfPdpEntriesNeeded
;
115 BOOLEAN Page1GSupport
;
116 EFI_CAPSULE_LONG_MODE_BUFFER LongModeBuffer
;
121 // Calculate the size of page table, allocate the memory.
123 Page1GSupport
= FALSE
;
124 if (PcdGetBool (PcdUse1GPageTable
)) {
125 AsmCpuid (0x80000000, &RegEax
, NULL
, NULL
, NULL
);
126 if (RegEax
>= 0x80000001) {
127 AsmCpuid (0x80000001, NULL
, NULL
, NULL
, &RegEdx
);
128 if ((RegEdx
& BIT26
) != 0) {
129 Page1GSupport
= TRUE
;
135 // Create 4G page table by default,
136 // and let PF handler to handle > 4G request.
138 PhysicalAddressBits
= 32;
139 ExtraPageTablePages
= EXTRA_PAGE_TABLE_PAGES
;
142 // Calculate the table entries needed.
144 if (PhysicalAddressBits
<= 39 ) {
145 NumberOfPml4EntriesNeeded
= 1;
146 NumberOfPdpEntriesNeeded
= (UINT32
)LShiftU64 (1, (PhysicalAddressBits
- 30));
148 NumberOfPml4EntriesNeeded
= (UINT32
)LShiftU64 (1, (PhysicalAddressBits
- 39));
149 NumberOfPdpEntriesNeeded
= 512;
152 if (!Page1GSupport
) {
153 TotalPagesNum
= (NumberOfPdpEntriesNeeded
+ 1) * NumberOfPml4EntriesNeeded
+ 1;
155 TotalPagesNum
= NumberOfPml4EntriesNeeded
+ 1;
158 TotalPagesNum
+= ExtraPageTablePages
;
159 DEBUG ((DEBUG_INFO
, "CapsuleRuntimeDxe X64 TotalPagesNum - 0x%x pages\n", TotalPagesNum
));
161 LongModeBuffer
.PageTableAddress
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)AllocateReservedMemoryBelow4G (EFI_PAGES_TO_SIZE (TotalPagesNum
));
162 ASSERT (LongModeBuffer
.PageTableAddress
!= 0);
167 LongModeBuffer
.StackSize
= PcdGet32 (PcdCapsulePeiLongModeStackSize
);
168 LongModeBuffer
.StackBaseAddress
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)AllocateReservedMemoryBelow4G (PcdGet32 (PcdCapsulePeiLongModeStackSize
));
169 ASSERT (LongModeBuffer
.StackBaseAddress
!= 0);
171 Status
= gRT
->SetVariable (
172 EFI_CAPSULE_LONG_MODE_BUFFER_NAME
,
173 &gEfiCapsuleVendorGuid
,
174 EFI_VARIABLE_NON_VOLATILE
| EFI_VARIABLE_BOOTSERVICE_ACCESS
,
175 sizeof (EFI_CAPSULE_LONG_MODE_BUFFER
),
178 if (!EFI_ERROR (Status
)) {
180 // Register callback function upon VariableLockProtocol
181 // to lock EFI_CAPSULE_LONG_MODE_BUFFER_NAME variable to avoid malicious code to update it.
183 EfiCreateProtocolNotifyEvent (
184 &gEdkiiVariableLockProtocolGuid
,
186 VariableLockCapsuleLongModeBufferVariable
,
191 DEBUG ((DEBUG_ERROR
, "FATAL ERROR: CapsuleLongModeBuffer cannot be saved: %r. Capsule in PEI may fail!\n", Status
));
192 gBS
->FreePages (LongModeBuffer
.StackBaseAddress
, EFI_SIZE_TO_PAGES (LongModeBuffer
.StackSize
));
197 Create the variable to save the base address of page table and stack
198 for transferring into long mode in IA32 capsule PEI.
201 SaveLongModeContext (
205 if ((FeaturePcdGet (PcdSupportUpdateCapsuleReset
)) && (FeaturePcdGet (PcdDxeIplSwitchToLongMode
))) {
207 // Allocate memory for Capsule IA32 PEIM, it will create page table to transfer to long mode to access capsule above 4GB.
209 PrepareContextForCapsulePei ();