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 - 2014, Intel Corporation. All rights reserved.<BR>
6 This program and the accompanying materials
7 are licensed and made available under the terms and conditions of the BSD License
8 which accompanies this distribution. The full text of the license may be found at
9 http://opensource.org/licenses/bsd-license.php
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
18 #include <Protocol/Capsule.h>
19 #include <Protocol/DxeSmmReadyToLock.h>
20 #include <Protocol/VariableLock.h>
22 #include <Guid/CapsuleVendor.h>
23 #include <Guid/AcpiS3Context.h>
25 #include <Library/DebugLib.h>
26 #include <Library/PcdLib.h>
27 #include <Library/UefiBootServicesTableLib.h>
28 #include <Library/UefiRuntimeServicesTableLib.h>
29 #include <Library/UefiRuntimeLib.h>
30 #include <Library/BaseLib.h>
31 #include <Library/LockBoxLib.h>
32 #include <Library/UefiLib.h>
33 #include <Library/BaseMemoryLib.h>
34 #include <Library/HobLib.h>
37 Allocate EfiACPIMemoryNVS below 4G memory address.
39 This function allocates EfiACPIMemoryNVS below 4G memory address.
41 @param Size Size of memory to allocate.
43 @return Allocated address for output.
47 AllocateAcpiNvsMemoryBelow4G (
52 EFI_PHYSICAL_ADDRESS Address
;
56 Pages
= EFI_SIZE_TO_PAGES (Size
);
59 Status
= gBS
->AllocatePages (
65 ASSERT_EFI_ERROR (Status
);
67 Buffer
= (VOID
*) (UINTN
) Address
;
68 ZeroMem (Buffer
, Size
);
74 Register callback function upon VariableLockProtocol
75 to lock EFI_CAPSULE_LONG_MODE_BUFFER_NAME variable to avoid malicious code to update it.
77 @param[in] Event Event whose notification function is being invoked.
78 @param[in] Context Pointer to the notification function's context.
82 VariableLockCapsuleLongModeBufferVariable (
88 EDKII_VARIABLE_LOCK_PROTOCOL
*VariableLock
;
90 // Mark EFI_CAPSULE_LONG_MODE_BUFFER_NAME variable to read-only if the Variable Lock protocol exists
92 Status
= gBS
->LocateProtocol (&gEdkiiVariableLockProtocolGuid
, NULL
, (VOID
**) &VariableLock
);
93 if (!EFI_ERROR (Status
)) {
94 Status
= VariableLock
->RequestToLock (VariableLock
, EFI_CAPSULE_LONG_MODE_BUFFER_NAME
, &gEfiCapsuleVendorGuid
);
95 ASSERT_EFI_ERROR (Status
);
100 1. Allocate NVS memory for capsule PEIM to establish a 1:1 Virtual to Physical mapping.
101 2. Allocate NVS memroy as a stack for capsule PEIM to transfer from 32-bit mdoe to 64-bit mode.
106 PrepareContextForCapsulePei (
113 UINT8 PhysicalAddressBits
;
115 UINT32 NumberOfPml4EntriesNeeded
;
116 UINT32 NumberOfPdpEntriesNeeded
;
117 BOOLEAN Page1GSupport
;
118 EFI_CAPSULE_LONG_MODE_BUFFER LongModeBuffer
;
122 LongModeBuffer
.PageTableAddress
= (EFI_PHYSICAL_ADDRESS
) PcdGet64 (PcdIdentifyMappingPageTablePtr
);
124 if (LongModeBuffer
.PageTableAddress
== 0x0) {
126 // Calculate the size of page table, allocate the memory, and set PcdIdentifyMappingPageTablePtr.
128 Page1GSupport
= FALSE
;
129 if (PcdGetBool(PcdUse1GPageTable
)) {
130 AsmCpuid (0x80000000, &RegEax
, NULL
, NULL
, NULL
);
131 if (RegEax
>= 0x80000001) {
132 AsmCpuid (0x80000001, NULL
, NULL
, NULL
, &RegEdx
);
133 if ((RegEdx
& BIT26
) != 0) {
134 Page1GSupport
= TRUE
;
140 // Get physical address bits supported.
142 Hob
= GetFirstHob (EFI_HOB_TYPE_CPU
);
144 PhysicalAddressBits
= ((EFI_HOB_CPU
*) Hob
)->SizeOfMemorySpace
;
146 AsmCpuid (0x80000000, &RegEax
, NULL
, NULL
, NULL
);
147 if (RegEax
>= 0x80000008) {
148 AsmCpuid (0x80000008, &RegEax
, NULL
, NULL
, NULL
);
149 PhysicalAddressBits
= (UINT8
) RegEax
;
151 PhysicalAddressBits
= 36;
156 // IA-32e paging translates 48-bit linear addresses to 52-bit physical addresses.
158 ASSERT (PhysicalAddressBits
<= 52);
159 if (PhysicalAddressBits
> 48) {
160 PhysicalAddressBits
= 48;
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;
174 if (!Page1GSupport
) {
175 TotalPagesNum
= (NumberOfPdpEntriesNeeded
+ 1) * NumberOfPml4EntriesNeeded
+ 1;
177 TotalPagesNum
= NumberOfPml4EntriesNeeded
+ 1;
180 LongModeBuffer
.PageTableAddress
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)AllocateAcpiNvsMemoryBelow4G (EFI_PAGES_TO_SIZE (TotalPagesNum
));
181 ASSERT (LongModeBuffer
.PageTableAddress
!= 0);
182 PcdSet64 (PcdIdentifyMappingPageTablePtr
, LongModeBuffer
.PageTableAddress
);
188 LongModeBuffer
.StackSize
= PcdGet32 (PcdCapsulePeiLongModeStackSize
);
189 LongModeBuffer
.StackBaseAddress
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)AllocateAcpiNvsMemoryBelow4G (PcdGet32 (PcdCapsulePeiLongModeStackSize
));
190 ASSERT (LongModeBuffer
.StackBaseAddress
!= 0);
192 Status
= gRT
->SetVariable (
193 EFI_CAPSULE_LONG_MODE_BUFFER_NAME
,
194 &gEfiCapsuleVendorGuid
,
195 EFI_VARIABLE_NON_VOLATILE
| EFI_VARIABLE_BOOTSERVICE_ACCESS
,
196 sizeof (EFI_CAPSULE_LONG_MODE_BUFFER
),
199 if (!EFI_ERROR (Status
)) {
201 // Register callback function upon VariableLockProtocol
202 // to lock EFI_CAPSULE_LONG_MODE_BUFFER_NAME variable to avoid malicious code to update it.
204 EfiCreateProtocolNotifyEvent (
205 &gEdkiiVariableLockProtocolGuid
,
207 VariableLockCapsuleLongModeBufferVariable
,
212 DEBUG ((EFI_D_ERROR
, "FATAL ERROR: CapsuleLongModeBuffer cannot be saved: %r. Capsule in PEI may fail!\n", Status
));
213 gBS
->FreePages (LongModeBuffer
.StackBaseAddress
, EFI_SIZE_TO_PAGES (LongModeBuffer
.StackSize
));
218 Create the variable to save the base address of page table and stack
219 for transferring into long mode in IA32 capsule PEI.
222 SaveLongModeContext (
226 if ((FeaturePcdGet(PcdSupportUpdateCapsuleReset
)) && (FeaturePcdGet (PcdDxeIplSwitchToLongMode
))) {
228 // Allocate memory for Capsule IA32 PEIM, it will create page table to transfer to long mode to access capsule above 4GB.
230 PrepareContextForCapsulePei ();