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
;
87 // Mark EFI_CAPSULE_LONG_MODE_BUFFER_NAME variable to read-only if the Variable Lock protocol exists
89 Status
= gBS
->LocateProtocol (&gEdkiiVariableLockProtocolGuid
, NULL
, (VOID
**) &VariableLock
);
90 if (!EFI_ERROR (Status
)) {
91 Status
= VariableLock
->RequestToLock (VariableLock
, EFI_CAPSULE_LONG_MODE_BUFFER_NAME
, &gEfiCapsuleVendorGuid
);
92 ASSERT_EFI_ERROR (Status
);
97 1. Allocate Reserved memory for capsule PEIM to establish a 1:1 Virtual to Physical mapping.
98 2. Allocate Reserved memroy as a stack for capsule PEIM to transfer from 32-bit mdoe to 64-bit mode.
103 PrepareContextForCapsulePei (
107 UINTN ExtraPageTablePages
;
111 UINT8 PhysicalAddressBits
;
112 UINT32 NumberOfPml4EntriesNeeded
;
113 UINT32 NumberOfPdpEntriesNeeded
;
114 BOOLEAN Page1GSupport
;
115 EFI_CAPSULE_LONG_MODE_BUFFER LongModeBuffer
;
120 // Calculate the size of page table, allocate the memory.
122 Page1GSupport
= FALSE
;
123 if (PcdGetBool(PcdUse1GPageTable
)) {
124 AsmCpuid (0x80000000, &RegEax
, NULL
, NULL
, NULL
);
125 if (RegEax
>= 0x80000001) {
126 AsmCpuid (0x80000001, NULL
, NULL
, NULL
, &RegEdx
);
127 if ((RegEdx
& BIT26
) != 0) {
128 Page1GSupport
= TRUE
;
134 // Create 4G page table by default,
135 // and let PF handler to handle > 4G request.
137 PhysicalAddressBits
= 32;
138 ExtraPageTablePages
= EXTRA_PAGE_TABLE_PAGES
;
141 // Calculate the table entries needed.
143 if (PhysicalAddressBits
<= 39 ) {
144 NumberOfPml4EntriesNeeded
= 1;
145 NumberOfPdpEntriesNeeded
= (UINT32
)LShiftU64 (1, (PhysicalAddressBits
- 30));
147 NumberOfPml4EntriesNeeded
= (UINT32
)LShiftU64 (1, (PhysicalAddressBits
- 39));
148 NumberOfPdpEntriesNeeded
= 512;
151 if (!Page1GSupport
) {
152 TotalPagesNum
= (NumberOfPdpEntriesNeeded
+ 1) * NumberOfPml4EntriesNeeded
+ 1;
154 TotalPagesNum
= NumberOfPml4EntriesNeeded
+ 1;
156 TotalPagesNum
+= ExtraPageTablePages
;
157 DEBUG ((DEBUG_INFO
, "CapsuleRuntimeDxe X64 TotalPagesNum - 0x%x pages\n", TotalPagesNum
));
159 LongModeBuffer
.PageTableAddress
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)AllocateReservedMemoryBelow4G (EFI_PAGES_TO_SIZE (TotalPagesNum
));
160 ASSERT (LongModeBuffer
.PageTableAddress
!= 0);
165 LongModeBuffer
.StackSize
= PcdGet32 (PcdCapsulePeiLongModeStackSize
);
166 LongModeBuffer
.StackBaseAddress
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)AllocateReservedMemoryBelow4G (PcdGet32 (PcdCapsulePeiLongModeStackSize
));
167 ASSERT (LongModeBuffer
.StackBaseAddress
!= 0);
169 Status
= gRT
->SetVariable (
170 EFI_CAPSULE_LONG_MODE_BUFFER_NAME
,
171 &gEfiCapsuleVendorGuid
,
172 EFI_VARIABLE_NON_VOLATILE
| EFI_VARIABLE_BOOTSERVICE_ACCESS
,
173 sizeof (EFI_CAPSULE_LONG_MODE_BUFFER
),
176 if (!EFI_ERROR (Status
)) {
178 // Register callback function upon VariableLockProtocol
179 // to lock EFI_CAPSULE_LONG_MODE_BUFFER_NAME variable to avoid malicious code to update it.
181 EfiCreateProtocolNotifyEvent (
182 &gEdkiiVariableLockProtocolGuid
,
184 VariableLockCapsuleLongModeBufferVariable
,
189 DEBUG ((EFI_D_ERROR
, "FATAL ERROR: CapsuleLongModeBuffer cannot be saved: %r. Capsule in PEI may fail!\n", Status
));
190 gBS
->FreePages (LongModeBuffer
.StackBaseAddress
, EFI_SIZE_TO_PAGES (LongModeBuffer
.StackSize
));
195 Create the variable to save the base address of page table and stack
196 for transferring into long mode in IA32 capsule PEI.
199 SaveLongModeContext (
203 if ((FeaturePcdGet(PcdSupportUpdateCapsuleReset
)) && (FeaturePcdGet (PcdDxeIplSwitchToLongMode
))) {
205 // Allocate memory for Capsule IA32 PEIM, it will create page table to transfer to long mode to access capsule above 4GB.
207 PrepareContextForCapsulePei ();