2 This is the implementation to save ACPI S3 Context.
4 Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
6 This program and the accompanying materials
7 are licensed and made available under the terms and conditions
8 of the BSD License which accompanies this distribution. The
9 full text of the license may be found at
10 http://opensource.org/licenses/bsd-license.php
12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
13 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
18 #include <Library/BaseLib.h>
19 #include <Library/BaseMemoryLib.h>
20 #include <Library/UefiBootServicesTableLib.h>
21 #include <Library/HobLib.h>
22 #include <Library/LockBoxLib.h>
23 #include <Library/PcdLib.h>
24 #include <Library/DebugLib.h>
25 #include <Library/UefiLib.h>
26 #include <Guid/AcpiS3Context.h>
27 #include <IndustryStandard/Acpi.h>
28 #include <Protocol/LockBox.h>
31 // 8 extra pages for PF handler.
33 #define EXTRA_PAGE_TABLE_PAGES 8
35 EFI_GUID mAcpiS3IdtrProfileGuid
= {
36 0xdea652b0, 0xd587, 0x4c54, { 0xb5, 0xb4, 0xc6, 0x82, 0xe7, 0xa0, 0xaa, 0x3d }
40 Allocate memory below 4G memory address.
42 This function allocates memory below 4G memory address.
44 @param MemoryType Memory type of memory to allocate.
45 @param Size Size of memory to allocate.
47 @return Allocated address for output.
51 AllocateMemoryBelow4G (
52 IN EFI_MEMORY_TYPE MemoryType
,
57 EFI_PHYSICAL_ADDRESS Address
;
61 Pages
= EFI_SIZE_TO_PAGES (Size
);
64 Status
= gBS
->AllocatePages (
70 ASSERT_EFI_ERROR (Status
);
72 Buffer
= (VOID
*) (UINTN
) Address
;
73 ZeroMem (Buffer
, Size
);
79 The function will check if long mode waking vector is supported.
81 @param[in] Facs Pointer to FACS table.
83 @retval TRUE Long mode waking vector is supported.
84 @retval FALSE Long mode waking vector is not supported.
88 IsLongModeWakingVectorSupport (
89 IN EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*Facs
93 (Facs
->Signature
!= EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE
) ) {
95 // Something wrong with FACS.
99 if ((Facs
->Version
== EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_VERSION
) &&
100 ((Facs
->Flags
& EFI_ACPI_4_0_64BIT_WAKE_SUPPORTED_F
) != 0)) {
102 // BIOS supports 64bit waking vector.
104 if (FeaturePcdGet (PcdDxeIplSwitchToLongMode
)) {
112 Allocates page table buffer.
114 @param[in] LongModeWakingVectorSupport Support long mode waking vector or not.
116 If BootScriptExector driver will run in 64-bit mode, this function will establish the 1:1
117 virtual to physical mapping page table when long mode waking vector is supported, otherwise
118 create 4G page table when long mode waking vector is not supported and let PF handler to
120 If BootScriptExector driver will not run in 64-bit mode, this function will do nothing.
122 @return Page table base address.
126 S3AllocatePageTablesBuffer (
127 IN BOOLEAN LongModeWakingVectorSupport
130 if (FeaturePcdGet (PcdDxeIplSwitchToLongMode
)) {
131 UINTN ExtraPageTablePages
;
134 UINT8 PhysicalAddressBits
;
135 UINT32 NumberOfPml4EntriesNeeded
;
136 UINT32 NumberOfPdpEntriesNeeded
;
137 EFI_PHYSICAL_ADDRESS S3NvsPageTableAddress
;
138 UINTN TotalPageTableSize
;
140 BOOLEAN Page1GSupport
;
142 Page1GSupport
= FALSE
;
143 if (PcdGetBool(PcdUse1GPageTable
)) {
144 AsmCpuid (0x80000000, &RegEax
, NULL
, NULL
, NULL
);
145 if (RegEax
>= 0x80000001) {
146 AsmCpuid (0x80000001, NULL
, NULL
, NULL
, &RegEdx
);
147 if ((RegEdx
& BIT26
) != 0) {
148 Page1GSupport
= TRUE
;
154 // Get physical address bits supported.
156 Hob
= GetFirstHob (EFI_HOB_TYPE_CPU
);
158 PhysicalAddressBits
= ((EFI_HOB_CPU
*) Hob
)->SizeOfMemorySpace
;
160 AsmCpuid (0x80000000, &RegEax
, NULL
, NULL
, NULL
);
161 if (RegEax
>= 0x80000008) {
162 AsmCpuid (0x80000008, &RegEax
, NULL
, NULL
, NULL
);
163 PhysicalAddressBits
= (UINT8
) RegEax
;
165 PhysicalAddressBits
= 36;
170 // IA-32e paging translates 48-bit linear addresses to 52-bit physical addresses.
172 ASSERT (PhysicalAddressBits
<= 52);
173 if (PhysicalAddressBits
> 48) {
174 PhysicalAddressBits
= 48;
177 ExtraPageTablePages
= 0;
178 if (!LongModeWakingVectorSupport
) {
180 // Create 4G page table when BIOS does not support long mode waking vector,
181 // and let PF handler to handle > 4G request.
183 PhysicalAddressBits
= 32;
184 ExtraPageTablePages
= EXTRA_PAGE_TABLE_PAGES
;
188 // Calculate the table entries needed.
190 if (PhysicalAddressBits
<= 39 ) {
191 NumberOfPml4EntriesNeeded
= 1;
192 NumberOfPdpEntriesNeeded
= (UINT32
)LShiftU64 (1, (PhysicalAddressBits
- 30));
194 NumberOfPml4EntriesNeeded
= (UINT32
)LShiftU64 (1, (PhysicalAddressBits
- 39));
195 NumberOfPdpEntriesNeeded
= 512;
199 // We need calculate whole page size then allocate once, because S3 restore page table does not know each page in Nvs.
201 if (!Page1GSupport
) {
202 TotalPageTableSize
= 1 + NumberOfPml4EntriesNeeded
+ NumberOfPml4EntriesNeeded
* NumberOfPdpEntriesNeeded
;
204 TotalPageTableSize
= 1 + NumberOfPml4EntriesNeeded
;
207 TotalPageTableSize
+= ExtraPageTablePages
;
208 DEBUG ((DEBUG_INFO
, "AcpiS3ContextSave TotalPageTableSize - 0x%x pages\n", TotalPageTableSize
));
211 // By architecture only one PageMapLevel4 exists - so lets allocate storage for it.
213 S3NvsPageTableAddress
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)AllocateMemoryBelow4G (EfiReservedMemoryType
, EFI_PAGES_TO_SIZE(TotalPageTableSize
));
214 ASSERT (S3NvsPageTableAddress
!= 0);
215 return S3NvsPageTableAddress
;
218 // If DXE is running 32-bit mode, no need to establish page table.
220 return (EFI_PHYSICAL_ADDRESS
) 0;
225 Callback function executed when the EndOfDxe event group is signaled.
227 @param[in] Event Event whose notification function is being invoked.
228 @param[in] Context The pointer to the notification function's context, which
229 is implementation-dependent.
233 AcpiS3ContextSaveOnEndOfDxe (
239 EFI_PHYSICAL_ADDRESS AcpiS3ContextBuffer
;
240 ACPI_S3_CONTEXT
*AcpiS3Context
;
241 IA32_DESCRIPTOR
*Idtr
;
242 IA32_IDT_GATE_DESCRIPTOR
*IdtGate
;
243 EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*Facs
;
246 DEBUG ((EFI_D_INFO
, "AcpiS3ContextSave!\n"));
248 Status
= gBS
->LocateProtocol (&gEfiLockBoxProtocolGuid
, NULL
, &Interface
);
249 if (EFI_ERROR (Status
)) {
250 DEBUG ((EFI_D_INFO
| EFI_D_WARN
, "ACPI S3 context can't be saved without LockBox!\n"));
254 AcpiS3Context
= AllocateMemoryBelow4G (EfiReservedMemoryType
, sizeof(*AcpiS3Context
));
255 ASSERT (AcpiS3Context
!= NULL
);
256 AcpiS3ContextBuffer
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)AcpiS3Context
;
259 // Get ACPI Table because we will save its position to variable
261 Facs
= (EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*) EfiLocateFirstAcpiTable (
262 EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE
264 AcpiS3Context
->AcpiFacsTable
= (EFI_PHYSICAL_ADDRESS
) (UINTN
) Facs
;
265 ASSERT (AcpiS3Context
->AcpiFacsTable
!= 0);
267 IdtGate
= AllocateMemoryBelow4G (EfiReservedMemoryType
, sizeof(IA32_IDT_GATE_DESCRIPTOR
) * 0x100 + sizeof(IA32_DESCRIPTOR
));
268 Idtr
= (IA32_DESCRIPTOR
*)(IdtGate
+ 0x100);
269 Idtr
->Base
= (UINTN
)IdtGate
;
270 Idtr
->Limit
= (UINT16
)(sizeof(IA32_IDT_GATE_DESCRIPTOR
) * 0x100 - 1);
271 AcpiS3Context
->IdtrProfile
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)Idtr
;
273 Status
= SaveLockBox (
274 &mAcpiS3IdtrProfileGuid
,
276 (UINTN
)sizeof(IA32_DESCRIPTOR
)
278 ASSERT_EFI_ERROR (Status
);
280 Status
= SetLockBoxAttributes (&mAcpiS3IdtrProfileGuid
, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE
);
281 ASSERT_EFI_ERROR (Status
);
284 // Allocate page table
286 AcpiS3Context
->S3NvsPageTableAddress
= S3AllocatePageTablesBuffer (IsLongModeWakingVectorSupport (Facs
));
291 AcpiS3Context
->BootScriptStackSize
= PcdGet32 (PcdS3BootScriptStackSize
);
292 AcpiS3Context
->BootScriptStackBase
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)AllocateMemoryBelow4G (EfiReservedMemoryType
, PcdGet32 (PcdS3BootScriptStackSize
));
293 ASSERT (AcpiS3Context
->BootScriptStackBase
!= 0);
296 // Allocate a code buffer < 4G for S3 debug to load external code, set invalid code instructions in it.
298 AcpiS3Context
->S3DebugBufferAddress
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)AllocateMemoryBelow4G (EfiReservedMemoryType
, EFI_PAGE_SIZE
);
299 SetMem ((VOID
*)(UINTN
)AcpiS3Context
->S3DebugBufferAddress
, EFI_PAGE_SIZE
, 0xff);
301 DEBUG((EFI_D_INFO
, "AcpiS3Context: AcpiFacsTable is 0x%8x\n", AcpiS3Context
->AcpiFacsTable
));
302 DEBUG((EFI_D_INFO
, "AcpiS3Context: IdtrProfile is 0x%8x\n", AcpiS3Context
->IdtrProfile
));
303 DEBUG((EFI_D_INFO
, "AcpiS3Context: S3NvsPageTableAddress is 0x%8x\n", AcpiS3Context
->S3NvsPageTableAddress
));
304 DEBUG((EFI_D_INFO
, "AcpiS3Context: S3DebugBufferAddress is 0x%8x\n", AcpiS3Context
->S3DebugBufferAddress
));
305 DEBUG((EFI_D_INFO
, "AcpiS3Context: BootScriptStackBase is 0x%8x\n", AcpiS3Context
->BootScriptStackBase
));
306 DEBUG((EFI_D_INFO
, "AcpiS3Context: BootScriptStackSize is 0x%8x\n", AcpiS3Context
->BootScriptStackSize
));
308 Status
= SaveLockBox (
309 &gEfiAcpiVariableGuid
,
310 &AcpiS3ContextBuffer
,
311 sizeof(AcpiS3ContextBuffer
)
313 ASSERT_EFI_ERROR (Status
);
315 Status
= SaveLockBox (
316 &gEfiAcpiS3ContextGuid
,
317 (VOID
*)(UINTN
)AcpiS3Context
,
318 (UINTN
)sizeof(*AcpiS3Context
)
320 ASSERT_EFI_ERROR (Status
);
322 Status
= SetLockBoxAttributes (&gEfiAcpiS3ContextGuid
, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE
);
323 ASSERT_EFI_ERROR (Status
);
327 // Close the event, deregistering the callback and freeing resources.
329 Status
= gBS
->CloseEvent (Event
);
330 ASSERT_EFI_ERROR (Status
);