2 This is the implementation to save ACPI S3 Context.
4 Copyright (c) 2006 - 2022, 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
))
89 // Something wrong with FACS.
94 if ((Facs
->Version
== EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_VERSION
) &&
95 ((Facs
->Flags
& EFI_ACPI_4_0_64BIT_WAKE_SUPPORTED_F
) != 0))
98 // BIOS supports 64bit waking vector.
100 if (sizeof (UINTN
) == sizeof (UINT64
)) {
109 Allocates page table buffer.
111 @param[in] LongModeWakingVectorSupport Support long mode waking vector or not.
113 If BootScriptExecutor driver will run in 64-bit mode, this function will establish the 1:1
114 virtual to physical mapping page table when long mode waking vector is supported, otherwise
115 create 4G page table when long mode waking vector is not supported and let PF handler to
117 If BootScriptExecutor driver will not run in 64-bit mode, this function will do nothing.
119 @return Page table base address.
123 S3AllocatePageTablesBuffer (
124 IN BOOLEAN LongModeWakingVectorSupport
127 if ((FeaturePcdGet (PcdDxeIplSwitchToLongMode
)) || (sizeof (UINTN
) == sizeof (UINT64
))) {
128 UINTN ExtraPageTablePages
;
131 UINT8 PhysicalAddressBits
;
132 UINT32 NumberOfPml4EntriesNeeded
;
133 UINT32 NumberOfPdpEntriesNeeded
;
134 EFI_PHYSICAL_ADDRESS S3NvsPageTableAddress
;
135 UINTN TotalPageTableSize
;
137 BOOLEAN Page1GSupport
;
139 Page1GSupport
= FALSE
;
140 if (PcdGetBool (PcdUse1GPageTable
)) {
141 AsmCpuid (0x80000000, &RegEax
, NULL
, NULL
, NULL
);
142 if (RegEax
>= 0x80000001) {
143 AsmCpuid (0x80000001, NULL
, NULL
, NULL
, &RegEdx
);
144 if ((RegEdx
& BIT26
) != 0) {
145 Page1GSupport
= TRUE
;
151 // Get physical address bits supported.
153 Hob
= GetFirstHob (EFI_HOB_TYPE_CPU
);
155 PhysicalAddressBits
= ((EFI_HOB_CPU
*)Hob
)->SizeOfMemorySpace
;
157 AsmCpuid (0x80000000, &RegEax
, NULL
, NULL
, NULL
);
158 if (RegEax
>= 0x80000008) {
159 AsmCpuid (0x80000008, &RegEax
, NULL
, NULL
, NULL
);
160 PhysicalAddressBits
= (UINT8
)RegEax
;
162 PhysicalAddressBits
= 36;
167 // IA-32e paging translates 48-bit linear addresses to 52-bit physical addresses.
169 ASSERT (PhysicalAddressBits
<= 52);
170 if (PhysicalAddressBits
> 48) {
171 PhysicalAddressBits
= 48;
174 ExtraPageTablePages
= 0;
175 if (!LongModeWakingVectorSupport
) {
177 // Create 4G page table when BIOS does not support long mode waking vector,
178 // and let PF handler to handle > 4G request.
180 PhysicalAddressBits
= 32;
181 ExtraPageTablePages
= EXTRA_PAGE_TABLE_PAGES
;
185 // Calculate the table entries needed.
187 if (PhysicalAddressBits
<= 39 ) {
188 NumberOfPml4EntriesNeeded
= 1;
189 NumberOfPdpEntriesNeeded
= (UINT32
)LShiftU64 (1, (PhysicalAddressBits
- 30));
191 NumberOfPml4EntriesNeeded
= (UINT32
)LShiftU64 (1, (PhysicalAddressBits
- 39));
192 NumberOfPdpEntriesNeeded
= 512;
196 // We need calculate whole page size then allocate once, because S3 restore page table does not know each page in Nvs.
198 if (!Page1GSupport
) {
199 TotalPageTableSize
= 1 + NumberOfPml4EntriesNeeded
+ NumberOfPml4EntriesNeeded
* NumberOfPdpEntriesNeeded
;
201 TotalPageTableSize
= 1 + NumberOfPml4EntriesNeeded
;
204 TotalPageTableSize
+= ExtraPageTablePages
;
205 DEBUG ((DEBUG_INFO
, "AcpiS3ContextSave TotalPageTableSize - 0x%x pages\n", TotalPageTableSize
));
208 // By architecture only one PageMapLevel4 exists - so lets allocate storage for it.
210 S3NvsPageTableAddress
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)AllocateMemoryBelow4G (EfiReservedMemoryType
, EFI_PAGES_TO_SIZE (TotalPageTableSize
));
211 ASSERT (S3NvsPageTableAddress
!= 0);
212 return S3NvsPageTableAddress
;
215 // If DXE is running 32-bit mode, no need to establish page table.
217 return (EFI_PHYSICAL_ADDRESS
)0;
222 Callback function executed when the EndOfDxe event group is signaled.
224 @param[in] Event Event whose notification function is being invoked.
225 @param[in] Context The pointer to the notification function's context, which
226 is implementation-dependent.
230 AcpiS3ContextSaveOnEndOfDxe (
236 EFI_PHYSICAL_ADDRESS AcpiS3ContextBuffer
;
237 ACPI_S3_CONTEXT
*AcpiS3Context
;
238 IA32_DESCRIPTOR
*Idtr
;
239 IA32_IDT_GATE_DESCRIPTOR
*IdtGate
;
240 EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*Facs
;
243 DEBUG ((DEBUG_INFO
, "AcpiS3ContextSave!\n"));
245 Status
= gBS
->LocateProtocol (&gEfiLockBoxProtocolGuid
, NULL
, &Interface
);
246 if (EFI_ERROR (Status
)) {
247 DEBUG ((DEBUG_INFO
| DEBUG_WARN
, "ACPI S3 context can't be saved without LockBox!\n"));
251 AcpiS3Context
= AllocateMemoryBelow4G (EfiReservedMemoryType
, sizeof (*AcpiS3Context
));
252 ASSERT (AcpiS3Context
!= NULL
);
253 AcpiS3ContextBuffer
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)AcpiS3Context
;
256 // Get ACPI Table because we will save its position to variable
258 Facs
= (EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*)EfiLocateFirstAcpiTable (
259 EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE
261 AcpiS3Context
->AcpiFacsTable
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)Facs
;
262 ASSERT (AcpiS3Context
->AcpiFacsTable
!= 0);
264 IdtGate
= AllocateMemoryBelow4G (EfiReservedMemoryType
, sizeof (IA32_IDT_GATE_DESCRIPTOR
) * 0x100 + sizeof (IA32_DESCRIPTOR
));
265 Idtr
= (IA32_DESCRIPTOR
*)(IdtGate
+ 0x100);
266 Idtr
->Base
= (UINTN
)IdtGate
;
267 Idtr
->Limit
= (UINT16
)(sizeof (IA32_IDT_GATE_DESCRIPTOR
) * 0x100 - 1);
268 AcpiS3Context
->IdtrProfile
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)Idtr
;
270 Status
= SaveLockBox (
271 &mAcpiS3IdtrProfileGuid
,
273 (UINTN
)sizeof (IA32_DESCRIPTOR
)
275 ASSERT_EFI_ERROR (Status
);
277 Status
= SetLockBoxAttributes (&mAcpiS3IdtrProfileGuid
, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE
);
278 ASSERT_EFI_ERROR (Status
);
281 // Allocate page table
283 AcpiS3Context
->S3NvsPageTableAddress
= S3AllocatePageTablesBuffer (IsLongModeWakingVectorSupport (Facs
));
288 AcpiS3Context
->BootScriptStackSize
= PcdGet32 (PcdS3BootScriptStackSize
);
289 AcpiS3Context
->BootScriptStackBase
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)AllocateMemoryBelow4G (EfiReservedMemoryType
, PcdGet32 (PcdS3BootScriptStackSize
));
290 ASSERT (AcpiS3Context
->BootScriptStackBase
!= 0);
293 // Allocate a code buffer < 4G for S3 debug to load external code, set invalid code instructions in it.
295 AcpiS3Context
->S3DebugBufferAddress
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)AllocateMemoryBelow4G (EfiReservedMemoryType
, EFI_PAGE_SIZE
);
296 SetMem ((VOID
*)(UINTN
)AcpiS3Context
->S3DebugBufferAddress
, EFI_PAGE_SIZE
, 0xff);
298 DEBUG ((DEBUG_INFO
, "AcpiS3Context: AcpiFacsTable is 0x%8x\n", AcpiS3Context
->AcpiFacsTable
));
299 DEBUG ((DEBUG_INFO
, "AcpiS3Context: IdtrProfile is 0x%8x\n", AcpiS3Context
->IdtrProfile
));
300 DEBUG ((DEBUG_INFO
, "AcpiS3Context: S3NvsPageTableAddress is 0x%8x\n", AcpiS3Context
->S3NvsPageTableAddress
));
301 DEBUG ((DEBUG_INFO
, "AcpiS3Context: S3DebugBufferAddress is 0x%8x\n", AcpiS3Context
->S3DebugBufferAddress
));
302 DEBUG ((DEBUG_INFO
, "AcpiS3Context: BootScriptStackBase is 0x%8x\n", AcpiS3Context
->BootScriptStackBase
));
303 DEBUG ((DEBUG_INFO
, "AcpiS3Context: BootScriptStackSize is 0x%8x\n", AcpiS3Context
->BootScriptStackSize
));
305 Status
= SaveLockBox (
306 &gEfiAcpiVariableGuid
,
307 &AcpiS3ContextBuffer
,
308 sizeof (AcpiS3ContextBuffer
)
310 ASSERT_EFI_ERROR (Status
);
312 Status
= SaveLockBox (
313 &gEfiAcpiS3ContextGuid
,
314 (VOID
*)(UINTN
)AcpiS3Context
,
315 (UINTN
)sizeof (*AcpiS3Context
)
317 ASSERT_EFI_ERROR (Status
);
319 Status
= SetLockBoxAttributes (&gEfiAcpiS3ContextGuid
, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE
);
320 ASSERT_EFI_ERROR (Status
);
324 // Close the event, deregistering the callback and freeing resources.
326 Status
= gBS
->CloseEvent (Event
);
327 ASSERT_EFI_ERROR (Status
);