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 <Guid/AcpiS3Context.h>
26 #include <Guid/Acpi.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
);
80 This function scan ACPI table in RSDT.
83 @param Signature ACPI table signature
90 IN EFI_ACPI_DESCRIPTION_HEADER
*Rsdt
,
97 EFI_ACPI_DESCRIPTION_HEADER
*Table
;
103 EntryCount
= (Rsdt
->Length
- sizeof (EFI_ACPI_DESCRIPTION_HEADER
)) / sizeof(UINT32
);
105 EntryPtr
= (UINT32
*)(Rsdt
+ 1);
106 for (Index
= 0; Index
< EntryCount
; Index
++, EntryPtr
++) {
107 Table
= (EFI_ACPI_DESCRIPTION_HEADER
*)((UINTN
)(*EntryPtr
));
108 if (Table
->Signature
== Signature
) {
118 This function scan ACPI table in XSDT.
120 @param Xsdt ACPI XSDT
121 @param Signature ACPI table signature
128 IN EFI_ACPI_DESCRIPTION_HEADER
*Xsdt
,
136 EFI_ACPI_DESCRIPTION_HEADER
*Table
;
142 EntryCount
= (Xsdt
->Length
- sizeof (EFI_ACPI_DESCRIPTION_HEADER
)) / sizeof(UINT64
);
144 BasePtr
= (UINTN
)(Xsdt
+ 1);
145 for (Index
= 0; Index
< EntryCount
; Index
++) {
146 CopyMem (&EntryPtr
, (VOID
*)(BasePtr
+ Index
* sizeof(UINT64
)), sizeof(UINT64
));
147 Table
= (EFI_ACPI_DESCRIPTION_HEADER
*)((UINTN
)(EntryPtr
));
148 if (Table
->Signature
== Signature
) {
157 To find Facs in FADT.
159 @param Fadt FADT table pointer
161 @return Facs table pointer.
163 EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*
164 FindAcpiFacsFromFadt (
165 IN EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE
*Fadt
168 EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*Facs
;
175 if (Fadt
->Header
.Revision
< EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_REVISION
) {
176 Facs
= (EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*)(UINTN
)Fadt
->FirmwareCtrl
;
178 if (Fadt
->FirmwareCtrl
!= 0) {
179 Facs
= (EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*)(UINTN
)Fadt
->FirmwareCtrl
;
181 CopyMem (&Data64
, &Fadt
->XFirmwareCtrl
, sizeof(UINT64
));
182 Facs
= (EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*)(UINTN
)Data64
;
189 To find Facs in Acpi tables.
191 To find Firmware ACPI control strutcure in Acpi Tables since the S3 waking vector is stored
194 @param AcpiTableGuid The guid used to find ACPI table in UEFI ConfigurationTable.
196 @return Facs table pointer.
198 EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*
199 FindAcpiFacsTableByAcpiGuid (
200 IN EFI_GUID
*AcpiTableGuid
203 EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER
*Rsdp
;
204 EFI_ACPI_DESCRIPTION_HEADER
*Rsdt
;
205 EFI_ACPI_DESCRIPTION_HEADER
*Xsdt
;
206 EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE
*Fadt
;
207 EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*Facs
;
212 // found ACPI table RSD_PTR from system table
214 for (Index
= 0; Index
< gST
->NumberOfTableEntries
; Index
++) {
215 if (CompareGuid (&(gST
->ConfigurationTable
[Index
].VendorGuid
), AcpiTableGuid
)) {
217 // A match was found.
219 Rsdp
= gST
->ConfigurationTable
[Index
].VendorTable
;
231 if (Rsdp
->Revision
>= EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER_REVISION
) {
232 Xsdt
= (EFI_ACPI_DESCRIPTION_HEADER
*)(UINTN
) Rsdp
->XsdtAddress
;
233 Fadt
= ScanTableInXSDT (Xsdt
, EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE
);
235 Facs
= FindAcpiFacsFromFadt (Fadt
);
245 Rsdt
= (EFI_ACPI_DESCRIPTION_HEADER
*)(UINTN
) Rsdp
->RsdtAddress
;
246 Fadt
= ScanTableInRSDT (Rsdt
, EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE
);
248 Facs
= FindAcpiFacsFromFadt (Fadt
);
258 To find Facs in Acpi tables.
260 To find Firmware ACPI control strutcure in Acpi Tables since the S3 waking vector is stored
263 @return Facs table pointer.
265 EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*
270 EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*Facs
;
272 Facs
= FindAcpiFacsTableByAcpiGuid (&gEfiAcpi20TableGuid
);
277 return FindAcpiFacsTableByAcpiGuid (&gEfiAcpi10TableGuid
);
281 The function will check if long mode waking vector is supported.
283 @param[in] Facs Pointer to FACS table.
285 @retval TRUE Long mode waking vector is supported.
286 @retval FALSE Long mode waking vector is not supported.
290 IsLongModeWakingVectorSupport (
291 IN EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*Facs
294 if ((Facs
== NULL
) ||
295 (Facs
->Signature
!= EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE
) ) {
297 // Something wrong with FACS.
301 if ((Facs
->Version
== EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_VERSION
) &&
302 ((Facs
->Flags
& EFI_ACPI_4_0_64BIT_WAKE_SUPPORTED_F
) != 0)) {
304 // BIOS supports 64bit waking vector.
306 if (FeaturePcdGet (PcdDxeIplSwitchToLongMode
)) {
314 Allocates page table buffer.
316 @param[in] LongModeWakingVectorSupport Support long mode waking vector or not.
318 If BootScriptExector driver will run in 64-bit mode, this function will establish the 1:1
319 virtual to physical mapping page table when long mode waking vector is supported, otherwise
320 create 4G page table when long mode waking vector is not supported and let PF handler to
322 If BootScriptExector driver will not run in 64-bit mode, this function will do nothing.
324 @return Page table base address.
328 S3AllocatePageTablesBuffer (
329 IN BOOLEAN LongModeWakingVectorSupport
332 if (FeaturePcdGet (PcdDxeIplSwitchToLongMode
)) {
333 UINTN ExtraPageTablePages
;
336 UINT8 PhysicalAddressBits
;
337 UINT32 NumberOfPml4EntriesNeeded
;
338 UINT32 NumberOfPdpEntriesNeeded
;
339 EFI_PHYSICAL_ADDRESS S3NvsPageTableAddress
;
340 UINTN TotalPageTableSize
;
342 BOOLEAN Page1GSupport
;
344 Page1GSupport
= FALSE
;
345 if (PcdGetBool(PcdUse1GPageTable
)) {
346 AsmCpuid (0x80000000, &RegEax
, NULL
, NULL
, NULL
);
347 if (RegEax
>= 0x80000001) {
348 AsmCpuid (0x80000001, NULL
, NULL
, NULL
, &RegEdx
);
349 if ((RegEdx
& BIT26
) != 0) {
350 Page1GSupport
= TRUE
;
356 // Get physical address bits supported.
358 Hob
= GetFirstHob (EFI_HOB_TYPE_CPU
);
360 PhysicalAddressBits
= ((EFI_HOB_CPU
*) Hob
)->SizeOfMemorySpace
;
362 AsmCpuid (0x80000000, &RegEax
, NULL
, NULL
, NULL
);
363 if (RegEax
>= 0x80000008) {
364 AsmCpuid (0x80000008, &RegEax
, NULL
, NULL
, NULL
);
365 PhysicalAddressBits
= (UINT8
) RegEax
;
367 PhysicalAddressBits
= 36;
372 // IA-32e paging translates 48-bit linear addresses to 52-bit physical addresses.
374 ASSERT (PhysicalAddressBits
<= 52);
375 if (PhysicalAddressBits
> 48) {
376 PhysicalAddressBits
= 48;
379 ExtraPageTablePages
= 0;
380 if (!LongModeWakingVectorSupport
) {
382 // Create 4G page table when BIOS does not support long mode waking vector,
383 // and let PF handler to handle > 4G request.
385 PhysicalAddressBits
= 32;
386 ExtraPageTablePages
= EXTRA_PAGE_TABLE_PAGES
;
390 // Calculate the table entries needed.
392 if (PhysicalAddressBits
<= 39 ) {
393 NumberOfPml4EntriesNeeded
= 1;
394 NumberOfPdpEntriesNeeded
= (UINT32
)LShiftU64 (1, (PhysicalAddressBits
- 30));
396 NumberOfPml4EntriesNeeded
= (UINT32
)LShiftU64 (1, (PhysicalAddressBits
- 39));
397 NumberOfPdpEntriesNeeded
= 512;
401 // We need calculate whole page size then allocate once, because S3 restore page table does not know each page in Nvs.
403 if (!Page1GSupport
) {
404 TotalPageTableSize
= 1 + NumberOfPml4EntriesNeeded
+ NumberOfPml4EntriesNeeded
* NumberOfPdpEntriesNeeded
;
406 TotalPageTableSize
= 1 + NumberOfPml4EntriesNeeded
;
409 TotalPageTableSize
+= ExtraPageTablePages
;
410 DEBUG ((DEBUG_INFO
, "AcpiS3ContextSave TotalPageTableSize - 0x%x pages\n", TotalPageTableSize
));
413 // By architecture only one PageMapLevel4 exists - so lets allocate storage for it.
415 S3NvsPageTableAddress
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)AllocateMemoryBelow4G (EfiReservedMemoryType
, EFI_PAGES_TO_SIZE(TotalPageTableSize
));
416 ASSERT (S3NvsPageTableAddress
!= 0);
417 return S3NvsPageTableAddress
;
420 // If DXE is running 32-bit mode, no need to establish page table.
422 return (EFI_PHYSICAL_ADDRESS
) 0;
427 Callback function executed when the EndOfDxe event group is signaled.
429 @param[in] Event Event whose notification function is being invoked.
430 @param[in] Context The pointer to the notification function's context, which
431 is implementation-dependent.
435 AcpiS3ContextSaveOnEndOfDxe (
441 EFI_PHYSICAL_ADDRESS AcpiS3ContextBuffer
;
442 ACPI_S3_CONTEXT
*AcpiS3Context
;
443 IA32_DESCRIPTOR
*Idtr
;
444 IA32_IDT_GATE_DESCRIPTOR
*IdtGate
;
445 EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*Facs
;
448 DEBUG ((EFI_D_INFO
, "AcpiS3ContextSave!\n"));
450 Status
= gBS
->LocateProtocol (&gEfiLockBoxProtocolGuid
, NULL
, &Interface
);
451 if (EFI_ERROR (Status
)) {
452 DEBUG ((EFI_D_INFO
| EFI_D_WARN
, "ACPI S3 context can't be saved without LockBox!\n"));
456 AcpiS3Context
= AllocateMemoryBelow4G (EfiReservedMemoryType
, sizeof(*AcpiS3Context
));
457 ASSERT (AcpiS3Context
!= NULL
);
458 AcpiS3ContextBuffer
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)AcpiS3Context
;
461 // Get ACPI Table because we will save its position to variable
463 Facs
= (EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*) FindAcpiFacsTable ();
464 AcpiS3Context
->AcpiFacsTable
= (EFI_PHYSICAL_ADDRESS
) (UINTN
) Facs
;
465 ASSERT (AcpiS3Context
->AcpiFacsTable
!= 0);
467 IdtGate
= AllocateMemoryBelow4G (EfiReservedMemoryType
, sizeof(IA32_IDT_GATE_DESCRIPTOR
) * 0x100 + sizeof(IA32_DESCRIPTOR
));
468 Idtr
= (IA32_DESCRIPTOR
*)(IdtGate
+ 0x100);
469 Idtr
->Base
= (UINTN
)IdtGate
;
470 Idtr
->Limit
= (UINT16
)(sizeof(IA32_IDT_GATE_DESCRIPTOR
) * 0x100 - 1);
471 AcpiS3Context
->IdtrProfile
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)Idtr
;
473 Status
= SaveLockBox (
474 &mAcpiS3IdtrProfileGuid
,
476 (UINTN
)sizeof(IA32_DESCRIPTOR
)
478 ASSERT_EFI_ERROR (Status
);
480 Status
= SetLockBoxAttributes (&mAcpiS3IdtrProfileGuid
, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE
);
481 ASSERT_EFI_ERROR (Status
);
484 // Allocate page table
486 AcpiS3Context
->S3NvsPageTableAddress
= S3AllocatePageTablesBuffer (IsLongModeWakingVectorSupport (Facs
));
491 AcpiS3Context
->BootScriptStackSize
= PcdGet32 (PcdS3BootScriptStackSize
);
492 AcpiS3Context
->BootScriptStackBase
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)AllocateMemoryBelow4G (EfiReservedMemoryType
, PcdGet32 (PcdS3BootScriptStackSize
));
493 ASSERT (AcpiS3Context
->BootScriptStackBase
!= 0);
496 // Allocate a code buffer < 4G for S3 debug to load external code, set invalid code instructions in it.
498 AcpiS3Context
->S3DebugBufferAddress
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)AllocateMemoryBelow4G (EfiReservedMemoryType
, EFI_PAGE_SIZE
);
499 SetMem ((VOID
*)(UINTN
)AcpiS3Context
->S3DebugBufferAddress
, EFI_PAGE_SIZE
, 0xff);
501 DEBUG((EFI_D_INFO
, "AcpiS3Context: AcpiFacsTable is 0x%8x\n", AcpiS3Context
->AcpiFacsTable
));
502 DEBUG((EFI_D_INFO
, "AcpiS3Context: IdtrProfile is 0x%8x\n", AcpiS3Context
->IdtrProfile
));
503 DEBUG((EFI_D_INFO
, "AcpiS3Context: S3NvsPageTableAddress is 0x%8x\n", AcpiS3Context
->S3NvsPageTableAddress
));
504 DEBUG((EFI_D_INFO
, "AcpiS3Context: S3DebugBufferAddress is 0x%8x\n", AcpiS3Context
->S3DebugBufferAddress
));
505 DEBUG((EFI_D_INFO
, "AcpiS3Context: BootScriptStackBase is 0x%8x\n", AcpiS3Context
->BootScriptStackBase
));
506 DEBUG((EFI_D_INFO
, "AcpiS3Context: BootScriptStackSize is 0x%8x\n", AcpiS3Context
->BootScriptStackSize
));
508 Status
= SaveLockBox (
509 &gEfiAcpiVariableGuid
,
510 &AcpiS3ContextBuffer
,
511 sizeof(AcpiS3ContextBuffer
)
513 ASSERT_EFI_ERROR (Status
);
515 Status
= SaveLockBox (
516 &gEfiAcpiS3ContextGuid
,
517 (VOID
*)(UINTN
)AcpiS3Context
,
518 (UINTN
)sizeof(*AcpiS3Context
)
520 ASSERT_EFI_ERROR (Status
);
522 Status
= SetLockBoxAttributes (&gEfiAcpiS3ContextGuid
, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE
);
523 ASSERT_EFI_ERROR (Status
);
527 // Close the event, deregistering the callback and freeing resources.
529 Status
= gBS
->CloseEvent (Event
);
530 ASSERT_EFI_ERROR (Status
);