2 This is the implementation to save ACPI S3 Context.
4 Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
6 SPDX-License-Identifier: BSD-2-Clause-Patent
11 #include <Library/BaseLib.h>
12 #include <Library/BaseMemoryLib.h>
13 #include <Library/UefiBootServicesTableLib.h>
14 #include <Library/HobLib.h>
15 #include <Library/LockBoxLib.h>
16 #include <Library/PcdLib.h>
17 #include <Library/DebugLib.h>
18 #include <Library/UefiLib.h>
19 #include <Guid/AcpiS3Context.h>
20 #include <IndustryStandard/Acpi.h>
21 #include <Protocol/LockBox.h>
24 // 8 extra pages for PF handler.
26 #define EXTRA_PAGE_TABLE_PAGES 8
28 EFI_GUID mAcpiS3IdtrProfileGuid
= {
29 0xdea652b0, 0xd587, 0x4c54, { 0xb5, 0xb4, 0xc6, 0x82, 0xe7, 0xa0, 0xaa, 0x3d }
33 Allocate memory below 4G memory address.
35 This function allocates memory below 4G memory address.
37 @param MemoryType Memory type of memory to allocate.
38 @param Size Size of memory to allocate.
40 @return Allocated address for output.
44 AllocateMemoryBelow4G (
45 IN EFI_MEMORY_TYPE MemoryType
,
50 EFI_PHYSICAL_ADDRESS Address
;
54 Pages
= EFI_SIZE_TO_PAGES (Size
);
57 Status
= gBS
->AllocatePages (
63 ASSERT_EFI_ERROR (Status
);
65 Buffer
= (VOID
*) (UINTN
) Address
;
66 ZeroMem (Buffer
, Size
);
72 The function will check if long mode waking vector is supported.
74 @param[in] Facs Pointer to FACS table.
76 @retval TRUE Long mode waking vector is supported.
77 @retval FALSE Long mode waking vector is not supported.
81 IsLongModeWakingVectorSupport (
82 IN EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*Facs
86 (Facs
->Signature
!= EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE
) ) {
88 // Something wrong with FACS.
92 if ((Facs
->Version
== EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_VERSION
) &&
93 ((Facs
->Flags
& EFI_ACPI_4_0_64BIT_WAKE_SUPPORTED_F
) != 0)) {
95 // BIOS supports 64bit waking vector.
97 if (FeaturePcdGet (PcdDxeIplSwitchToLongMode
)) {
105 Allocates page table buffer.
107 @param[in] LongModeWakingVectorSupport Support long mode waking vector or not.
109 If BootScriptExector driver will run in 64-bit mode, this function will establish the 1:1
110 virtual to physical mapping page table when long mode waking vector is supported, otherwise
111 create 4G page table when long mode waking vector is not supported and let PF handler to
113 If BootScriptExector driver will not run in 64-bit mode, this function will do nothing.
115 @return Page table base address.
119 S3AllocatePageTablesBuffer (
120 IN BOOLEAN LongModeWakingVectorSupport
123 if (FeaturePcdGet (PcdDxeIplSwitchToLongMode
)) {
124 UINTN ExtraPageTablePages
;
127 UINT8 PhysicalAddressBits
;
128 UINT32 NumberOfPml4EntriesNeeded
;
129 UINT32 NumberOfPdpEntriesNeeded
;
130 EFI_PHYSICAL_ADDRESS S3NvsPageTableAddress
;
131 UINTN TotalPageTableSize
;
133 BOOLEAN Page1GSupport
;
135 Page1GSupport
= FALSE
;
136 if (PcdGetBool(PcdUse1GPageTable
)) {
137 AsmCpuid (0x80000000, &RegEax
, NULL
, NULL
, NULL
);
138 if (RegEax
>= 0x80000001) {
139 AsmCpuid (0x80000001, NULL
, NULL
, NULL
, &RegEdx
);
140 if ((RegEdx
& BIT26
) != 0) {
141 Page1GSupport
= TRUE
;
147 // Get physical address bits supported.
149 Hob
= GetFirstHob (EFI_HOB_TYPE_CPU
);
151 PhysicalAddressBits
= ((EFI_HOB_CPU
*) Hob
)->SizeOfMemorySpace
;
153 AsmCpuid (0x80000000, &RegEax
, NULL
, NULL
, NULL
);
154 if (RegEax
>= 0x80000008) {
155 AsmCpuid (0x80000008, &RegEax
, NULL
, NULL
, NULL
);
156 PhysicalAddressBits
= (UINT8
) RegEax
;
158 PhysicalAddressBits
= 36;
163 // IA-32e paging translates 48-bit linear addresses to 52-bit physical addresses.
165 ASSERT (PhysicalAddressBits
<= 52);
166 if (PhysicalAddressBits
> 48) {
167 PhysicalAddressBits
= 48;
170 ExtraPageTablePages
= 0;
171 if (!LongModeWakingVectorSupport
) {
173 // Create 4G page table when BIOS does not support long mode waking vector,
174 // and let PF handler to handle > 4G request.
176 PhysicalAddressBits
= 32;
177 ExtraPageTablePages
= EXTRA_PAGE_TABLE_PAGES
;
181 // Calculate the table entries needed.
183 if (PhysicalAddressBits
<= 39 ) {
184 NumberOfPml4EntriesNeeded
= 1;
185 NumberOfPdpEntriesNeeded
= (UINT32
)LShiftU64 (1, (PhysicalAddressBits
- 30));
187 NumberOfPml4EntriesNeeded
= (UINT32
)LShiftU64 (1, (PhysicalAddressBits
- 39));
188 NumberOfPdpEntriesNeeded
= 512;
192 // We need calculate whole page size then allocate once, because S3 restore page table does not know each page in Nvs.
194 if (!Page1GSupport
) {
195 TotalPageTableSize
= 1 + NumberOfPml4EntriesNeeded
+ NumberOfPml4EntriesNeeded
* NumberOfPdpEntriesNeeded
;
197 TotalPageTableSize
= 1 + NumberOfPml4EntriesNeeded
;
200 TotalPageTableSize
+= ExtraPageTablePages
;
201 DEBUG ((DEBUG_INFO
, "AcpiS3ContextSave TotalPageTableSize - 0x%x pages\n", TotalPageTableSize
));
204 // By architecture only one PageMapLevel4 exists - so lets allocate storage for it.
206 S3NvsPageTableAddress
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)AllocateMemoryBelow4G (EfiReservedMemoryType
, EFI_PAGES_TO_SIZE(TotalPageTableSize
));
207 ASSERT (S3NvsPageTableAddress
!= 0);
208 return S3NvsPageTableAddress
;
211 // If DXE is running 32-bit mode, no need to establish page table.
213 return (EFI_PHYSICAL_ADDRESS
) 0;
218 Callback function executed when the EndOfDxe event group is signaled.
220 @param[in] Event Event whose notification function is being invoked.
221 @param[in] Context The pointer to the notification function's context, which
222 is implementation-dependent.
226 AcpiS3ContextSaveOnEndOfDxe (
232 EFI_PHYSICAL_ADDRESS AcpiS3ContextBuffer
;
233 ACPI_S3_CONTEXT
*AcpiS3Context
;
234 IA32_DESCRIPTOR
*Idtr
;
235 IA32_IDT_GATE_DESCRIPTOR
*IdtGate
;
236 EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*Facs
;
239 DEBUG ((EFI_D_INFO
, "AcpiS3ContextSave!\n"));
241 Status
= gBS
->LocateProtocol (&gEfiLockBoxProtocolGuid
, NULL
, &Interface
);
242 if (EFI_ERROR (Status
)) {
243 DEBUG ((EFI_D_INFO
| EFI_D_WARN
, "ACPI S3 context can't be saved without LockBox!\n"));
247 AcpiS3Context
= AllocateMemoryBelow4G (EfiReservedMemoryType
, sizeof(*AcpiS3Context
));
248 ASSERT (AcpiS3Context
!= NULL
);
249 AcpiS3ContextBuffer
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)AcpiS3Context
;
252 // Get ACPI Table because we will save its position to variable
254 Facs
= (EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*) EfiLocateFirstAcpiTable (
255 EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE
257 AcpiS3Context
->AcpiFacsTable
= (EFI_PHYSICAL_ADDRESS
) (UINTN
) Facs
;
258 ASSERT (AcpiS3Context
->AcpiFacsTable
!= 0);
260 IdtGate
= AllocateMemoryBelow4G (EfiReservedMemoryType
, sizeof(IA32_IDT_GATE_DESCRIPTOR
) * 0x100 + sizeof(IA32_DESCRIPTOR
));
261 Idtr
= (IA32_DESCRIPTOR
*)(IdtGate
+ 0x100);
262 Idtr
->Base
= (UINTN
)IdtGate
;
263 Idtr
->Limit
= (UINT16
)(sizeof(IA32_IDT_GATE_DESCRIPTOR
) * 0x100 - 1);
264 AcpiS3Context
->IdtrProfile
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)Idtr
;
266 Status
= SaveLockBox (
267 &mAcpiS3IdtrProfileGuid
,
269 (UINTN
)sizeof(IA32_DESCRIPTOR
)
271 ASSERT_EFI_ERROR (Status
);
273 Status
= SetLockBoxAttributes (&mAcpiS3IdtrProfileGuid
, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE
);
274 ASSERT_EFI_ERROR (Status
);
277 // Allocate page table
279 AcpiS3Context
->S3NvsPageTableAddress
= S3AllocatePageTablesBuffer (IsLongModeWakingVectorSupport (Facs
));
284 AcpiS3Context
->BootScriptStackSize
= PcdGet32 (PcdS3BootScriptStackSize
);
285 AcpiS3Context
->BootScriptStackBase
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)AllocateMemoryBelow4G (EfiReservedMemoryType
, PcdGet32 (PcdS3BootScriptStackSize
));
286 ASSERT (AcpiS3Context
->BootScriptStackBase
!= 0);
289 // Allocate a code buffer < 4G for S3 debug to load external code, set invalid code instructions in it.
291 AcpiS3Context
->S3DebugBufferAddress
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)AllocateMemoryBelow4G (EfiReservedMemoryType
, EFI_PAGE_SIZE
);
292 SetMem ((VOID
*)(UINTN
)AcpiS3Context
->S3DebugBufferAddress
, EFI_PAGE_SIZE
, 0xff);
294 DEBUG((EFI_D_INFO
, "AcpiS3Context: AcpiFacsTable is 0x%8x\n", AcpiS3Context
->AcpiFacsTable
));
295 DEBUG((EFI_D_INFO
, "AcpiS3Context: IdtrProfile is 0x%8x\n", AcpiS3Context
->IdtrProfile
));
296 DEBUG((EFI_D_INFO
, "AcpiS3Context: S3NvsPageTableAddress is 0x%8x\n", AcpiS3Context
->S3NvsPageTableAddress
));
297 DEBUG((EFI_D_INFO
, "AcpiS3Context: S3DebugBufferAddress is 0x%8x\n", AcpiS3Context
->S3DebugBufferAddress
));
298 DEBUG((EFI_D_INFO
, "AcpiS3Context: BootScriptStackBase is 0x%8x\n", AcpiS3Context
->BootScriptStackBase
));
299 DEBUG((EFI_D_INFO
, "AcpiS3Context: BootScriptStackSize is 0x%8x\n", AcpiS3Context
->BootScriptStackSize
));
301 Status
= SaveLockBox (
302 &gEfiAcpiVariableGuid
,
303 &AcpiS3ContextBuffer
,
304 sizeof(AcpiS3ContextBuffer
)
306 ASSERT_EFI_ERROR (Status
);
308 Status
= SaveLockBox (
309 &gEfiAcpiS3ContextGuid
,
310 (VOID
*)(UINTN
)AcpiS3Context
,
311 (UINTN
)sizeof(*AcpiS3Context
)
313 ASSERT_EFI_ERROR (Status
);
315 Status
= SetLockBoxAttributes (&gEfiAcpiS3ContextGuid
, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE
);
316 ASSERT_EFI_ERROR (Status
);
320 // Close the event, deregistering the callback and freeing resources.
322 Status
= gBS
->CloseEvent (Event
);
323 ASSERT_EFI_ERROR (Status
);