2 This is an implementation of the ACPI S3 Save protocol. This is defined in
3 S3 boot path specification 0.9.
5 Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.<BR>
7 This program and the accompanying materials
8 are licensed and made available under the terms and conditions
9 of the BSD License which accompanies this distribution. The
10 full text of the license may be found at
11 http://opensource.org/licenses/bsd-license.php
13 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
14 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
19 #include <Library/BaseLib.h>
20 #include <Library/BaseMemoryLib.h>
21 #include <Library/UefiBootServicesTableLib.h>
22 #include <Library/UefiRuntimeServicesTableLib.h>
23 #include <Library/HobLib.h>
24 #include <Library/LockBoxLib.h>
25 #include <Library/PcdLib.h>
26 #include <Library/DebugLib.h>
27 #include <Guid/AcpiVariableCompatibility.h>
28 #include <Guid/AcpiS3Context.h>
29 #include <Guid/Acpi.h>
30 #include <Protocol/AcpiS3Save.h>
31 #include <IndustryStandard/Acpi.h>
33 #include "AcpiS3Save.h"
36 // 8 extra pages for PF handler.
38 #define EXTRA_PAGE_TABLE_PAGES 8
41 Hook point for AcpiVariableThunkPlatform for InstallAcpiS3Save.
44 InstallAcpiS3SaveThunk (
49 Hook point for AcpiVariableThunkPlatform for S3Ready.
51 @param AcpiS3Context ACPI s3 context
54 S3ReadyThunkPlatform (
55 IN ACPI_S3_CONTEXT
*AcpiS3Context
58 UINTN mLegacyRegionSize
;
60 EFI_ACPI_S3_SAVE_PROTOCOL mS3Save
= {
61 LegacyGetS3MemorySize
,
65 EFI_GUID mAcpiS3IdtrProfileGuid
= {
66 0xdea652b0, 0xd587, 0x4c54, { 0xb5, 0xb4, 0xc6, 0x82, 0xe7, 0xa0, 0xaa, 0x3d }
70 Allocate memory below 4G memory address.
72 This function allocates memory below 4G memory address.
74 @param MemoryType Memory type of memory to allocate.
75 @param Size Size of memory to allocate.
77 @return Allocated address for output.
81 AllocateMemoryBelow4G (
82 IN EFI_MEMORY_TYPE MemoryType
,
87 EFI_PHYSICAL_ADDRESS Address
;
91 Pages
= EFI_SIZE_TO_PAGES (Size
);
94 Status
= gBS
->AllocatePages (
100 ASSERT_EFI_ERROR (Status
);
102 Buffer
= (VOID
*) (UINTN
) Address
;
103 ZeroMem (Buffer
, Size
);
110 This function scan ACPI table in RSDT.
112 @param Rsdt ACPI RSDT
113 @param Signature ACPI table signature
120 IN EFI_ACPI_DESCRIPTION_HEADER
*Rsdt
,
127 EFI_ACPI_DESCRIPTION_HEADER
*Table
;
133 EntryCount
= (Rsdt
->Length
- sizeof (EFI_ACPI_DESCRIPTION_HEADER
)) / sizeof(UINT32
);
135 EntryPtr
= (UINT32
*)(Rsdt
+ 1);
136 for (Index
= 0; Index
< EntryCount
; Index
++, EntryPtr
++) {
137 Table
= (EFI_ACPI_DESCRIPTION_HEADER
*)((UINTN
)(*EntryPtr
));
138 if (Table
->Signature
== Signature
) {
148 This function scan ACPI table in XSDT.
150 @param Xsdt ACPI XSDT
151 @param Signature ACPI table signature
158 IN EFI_ACPI_DESCRIPTION_HEADER
*Xsdt
,
166 EFI_ACPI_DESCRIPTION_HEADER
*Table
;
172 EntryCount
= (Xsdt
->Length
- sizeof (EFI_ACPI_DESCRIPTION_HEADER
)) / sizeof(UINT64
);
174 BasePtr
= (UINTN
)(Xsdt
+ 1);
175 for (Index
= 0; Index
< EntryCount
; Index
++) {
176 CopyMem (&EntryPtr
, (VOID
*)(BasePtr
+ Index
* sizeof(UINT64
)), sizeof(UINT64
));
177 Table
= (EFI_ACPI_DESCRIPTION_HEADER
*)((UINTN
)(EntryPtr
));
178 if (Table
->Signature
== Signature
) {
187 To find Facs in FADT.
189 @param Fadt FADT table pointer
191 @return Facs table pointer.
193 EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*
194 FindAcpiFacsFromFadt (
195 IN EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE
*Fadt
198 EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*Facs
;
205 if (Fadt
->Header
.Revision
< EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_REVISION
) {
206 Facs
= (EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*)(UINTN
)Fadt
->FirmwareCtrl
;
208 if (Fadt
->FirmwareCtrl
!= 0) {
209 Facs
= (EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*)(UINTN
)Fadt
->FirmwareCtrl
;
211 CopyMem (&Data64
, &Fadt
->XFirmwareCtrl
, sizeof(UINT64
));
212 Facs
= (EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*)(UINTN
)Data64
;
219 To find Facs in Acpi tables.
221 To find Firmware ACPI control strutcure in Acpi Tables since the S3 waking vector is stored
224 @param AcpiTableGuid The guid used to find ACPI table in UEFI ConfigurationTable.
226 @return Facs table pointer.
228 EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*
229 FindAcpiFacsTableByAcpiGuid (
230 IN EFI_GUID
*AcpiTableGuid
233 EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER
*Rsdp
;
234 EFI_ACPI_DESCRIPTION_HEADER
*Rsdt
;
235 EFI_ACPI_DESCRIPTION_HEADER
*Xsdt
;
236 EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE
*Fadt
;
237 EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*Facs
;
242 // found ACPI table RSD_PTR from system table
244 for (Index
= 0; Index
< gST
->NumberOfTableEntries
; Index
++) {
245 if (CompareGuid (&(gST
->ConfigurationTable
[Index
].VendorGuid
), AcpiTableGuid
)) {
247 // A match was found.
249 Rsdp
= gST
->ConfigurationTable
[Index
].VendorTable
;
261 if (Rsdp
->Revision
>= EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER_REVISION
) {
262 Xsdt
= (EFI_ACPI_DESCRIPTION_HEADER
*)(UINTN
) Rsdp
->XsdtAddress
;
263 Fadt
= ScanTableInXSDT (Xsdt
, EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE
);
265 Facs
= FindAcpiFacsFromFadt (Fadt
);
275 Rsdt
= (EFI_ACPI_DESCRIPTION_HEADER
*)(UINTN
) Rsdp
->RsdtAddress
;
276 Fadt
= ScanTableInRSDT (Rsdt
, EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE
);
278 Facs
= FindAcpiFacsFromFadt (Fadt
);
288 To find Facs in Acpi tables.
290 To find Firmware ACPI control strutcure in Acpi Tables since the S3 waking vector is stored
293 @return Facs table pointer.
295 EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*
300 EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*Facs
;
302 Facs
= FindAcpiFacsTableByAcpiGuid (&gEfiAcpi20TableGuid
);
307 return FindAcpiFacsTableByAcpiGuid (&gEfiAcpi10TableGuid
);
311 The function will check if long mode waking vector is supported.
313 @param[in] Facs Pointer to FACS table.
315 @retval TRUE Long mode waking vector is supported.
316 @retval FALSE Long mode waking vector is not supported.
320 IsLongModeWakingVectorSupport (
321 IN EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*Facs
324 if ((Facs
== NULL
) ||
325 (Facs
->Signature
!= EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE
) ) {
327 // Something wrong with FACS.
331 if (Facs
->XFirmwareWakingVector
!= 0) {
332 if ((Facs
->Version
== EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_VERSION
) &&
333 ((Facs
->Flags
& EFI_ACPI_4_0_64BIT_WAKE_SUPPORTED_F
) != 0)) {
335 // BIOS supports 64bit waking vector.
337 if (FeaturePcdGet (PcdDxeIplSwitchToLongMode
)) {
346 Allocates page table buffer.
348 @param[in] LongModeWakingVectorSupport Support long mode waking vector or not.
350 If BootScriptExector driver will run in 64-bit mode, this function will establish the 1:1
351 virtual to physical mapping page table when long mode waking vector is supported, otherwise
352 create 4G page table when long mode waking vector is not supported and let PF handler to
354 If BootScriptExector driver will not run in 64-bit mode, this function will do nothing.
356 @return Page table base address.
360 S3AllocatePageTablesBuffer (
361 IN BOOLEAN LongModeWakingVectorSupport
364 if (FeaturePcdGet (PcdDxeIplSwitchToLongMode
)) {
365 UINTN ExtraPageTablePages
;
368 UINT8 PhysicalAddressBits
;
369 UINT32 NumberOfPml4EntriesNeeded
;
370 UINT32 NumberOfPdpEntriesNeeded
;
371 EFI_PHYSICAL_ADDRESS S3NvsPageTableAddress
;
372 UINTN TotalPageTableSize
;
374 BOOLEAN Page1GSupport
;
376 Page1GSupport
= FALSE
;
377 if (PcdGetBool(PcdUse1GPageTable
)) {
378 AsmCpuid (0x80000000, &RegEax
, NULL
, NULL
, NULL
);
379 if (RegEax
>= 0x80000001) {
380 AsmCpuid (0x80000001, NULL
, NULL
, NULL
, &RegEdx
);
381 if ((RegEdx
& BIT26
) != 0) {
382 Page1GSupport
= TRUE
;
388 // Get physical address bits supported.
390 Hob
= GetFirstHob (EFI_HOB_TYPE_CPU
);
392 PhysicalAddressBits
= ((EFI_HOB_CPU
*) Hob
)->SizeOfMemorySpace
;
394 AsmCpuid (0x80000000, &RegEax
, NULL
, NULL
, NULL
);
395 if (RegEax
>= 0x80000008) {
396 AsmCpuid (0x80000008, &RegEax
, NULL
, NULL
, NULL
);
397 PhysicalAddressBits
= (UINT8
) RegEax
;
399 PhysicalAddressBits
= 36;
404 // IA-32e paging translates 48-bit linear addresses to 52-bit physical addresses.
406 ASSERT (PhysicalAddressBits
<= 52);
407 if (PhysicalAddressBits
> 48) {
408 PhysicalAddressBits
= 48;
411 ExtraPageTablePages
= 0;
412 if (!LongModeWakingVectorSupport
) {
414 // Create 4G page table when BIOS does not support long mode waking vector,
415 // and let PF handler to handle > 4G request.
417 PhysicalAddressBits
= 32;
418 ExtraPageTablePages
= EXTRA_PAGE_TABLE_PAGES
;
422 // Calculate the table entries needed.
424 if (PhysicalAddressBits
<= 39 ) {
425 NumberOfPml4EntriesNeeded
= 1;
426 NumberOfPdpEntriesNeeded
= (UINT32
)LShiftU64 (1, (PhysicalAddressBits
- 30));
428 NumberOfPml4EntriesNeeded
= (UINT32
)LShiftU64 (1, (PhysicalAddressBits
- 39));
429 NumberOfPdpEntriesNeeded
= 512;
433 // We need calculate whole page size then allocate once, because S3 restore page table does not know each page in Nvs.
435 if (!Page1GSupport
) {
436 TotalPageTableSize
= (UINTN
)(1 + NumberOfPml4EntriesNeeded
+ NumberOfPml4EntriesNeeded
* NumberOfPdpEntriesNeeded
);
438 TotalPageTableSize
= (UINTN
)(1 + NumberOfPml4EntriesNeeded
);
441 TotalPageTableSize
+= ExtraPageTablePages
;
442 DEBUG ((EFI_D_ERROR
, "AcpiS3Save TotalPageTableSize - 0x%x pages\n", TotalPageTableSize
));
445 // By architecture only one PageMapLevel4 exists - so lets allocate storage for it.
447 S3NvsPageTableAddress
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)AllocateMemoryBelow4G (EfiReservedMemoryType
, EFI_PAGES_TO_SIZE(TotalPageTableSize
));
448 ASSERT (S3NvsPageTableAddress
!= 0);
449 return S3NvsPageTableAddress
;
452 // If DXE is running 32-bit mode, no need to establish page table.
454 return (EFI_PHYSICAL_ADDRESS
) 0;
459 Gets the buffer of legacy memory below 1 MB
460 This function is to get the buffer in legacy memory below 1MB that is required during S3 resume.
462 @param This A pointer to the EFI_ACPI_S3_SAVE_PROTOCOL instance.
463 @param Size The returned size of legacy memory below 1 MB.
465 @retval EFI_SUCCESS Size is successfully returned.
466 @retval EFI_INVALID_PARAMETER The pointer Size is NULL.
471 LegacyGetS3MemorySize (
472 IN EFI_ACPI_S3_SAVE_PROTOCOL
*This
,
477 return EFI_INVALID_PARAMETER
;
480 *Size
= mLegacyRegionSize
;
485 Prepares all information that is needed in the S3 resume boot path.
487 Allocate the resources or prepare informations and save in ACPI variable set for S3 resume boot path
489 @param This A pointer to the EFI_ACPI_S3_SAVE_PROTOCOL instance.
490 @param LegacyMemoryAddress The base address of legacy memory.
492 @retval EFI_NOT_FOUND Some necessary information cannot be found.
493 @retval EFI_SUCCESS All information was saved successfully.
494 @retval EFI_OUT_OF_RESOURCES Resources were insufficient to save all the information.
495 @retval EFI_INVALID_PARAMETER The memory range is not located below 1 MB.
501 IN EFI_ACPI_S3_SAVE_PROTOCOL
*This
,
502 IN VOID
*LegacyMemoryAddress
506 EFI_PHYSICAL_ADDRESS AcpiS3ContextBuffer
;
507 ACPI_S3_CONTEXT
*AcpiS3Context
;
508 STATIC BOOLEAN AlreadyEntered
;
509 IA32_DESCRIPTOR
*Idtr
;
510 IA32_IDT_GATE_DESCRIPTOR
*IdtGate
;
511 EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*Facs
;
513 DEBUG ((EFI_D_INFO
, "S3Ready!\n"));
516 // Platform may invoke AcpiS3Save->S3Save() before ExitPmAuth, because we need save S3 information there, while BDS ReadyToBoot may invoke it again.
517 // So if 2nd S3Save() is triggered later, we need ignore it.
519 if (AlreadyEntered
) {
522 AlreadyEntered
= TRUE
;
524 AcpiS3Context
= AllocateMemoryBelow4G (EfiReservedMemoryType
, sizeof(*AcpiS3Context
));
525 ASSERT (AcpiS3Context
!= NULL
);
526 AcpiS3ContextBuffer
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)AcpiS3Context
;
529 // Get ACPI Table because we will save its position to variable
531 Facs
= (EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*) FindAcpiFacsTable ();
532 AcpiS3Context
->AcpiFacsTable
= (EFI_PHYSICAL_ADDRESS
) (UINTN
) Facs
;
533 ASSERT (AcpiS3Context
->AcpiFacsTable
!= 0);
535 IdtGate
= AllocateMemoryBelow4G (EfiReservedMemoryType
, sizeof(IA32_IDT_GATE_DESCRIPTOR
) * 0x100 + sizeof(IA32_DESCRIPTOR
));
536 Idtr
= (IA32_DESCRIPTOR
*)(IdtGate
+ 0x100);
537 Idtr
->Base
= (UINTN
)IdtGate
;
538 Idtr
->Limit
= (UINT16
)(sizeof(IA32_IDT_GATE_DESCRIPTOR
) * 0x100 - 1);
539 AcpiS3Context
->IdtrProfile
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)Idtr
;
541 Status
= SaveLockBox (
542 &mAcpiS3IdtrProfileGuid
,
544 (UINTN
)sizeof(IA32_DESCRIPTOR
)
546 ASSERT_EFI_ERROR (Status
);
548 Status
= SetLockBoxAttributes (&mAcpiS3IdtrProfileGuid
, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE
);
549 ASSERT_EFI_ERROR (Status
);
552 // Allocate page table
554 AcpiS3Context
->S3NvsPageTableAddress
= S3AllocatePageTablesBuffer (IsLongModeWakingVectorSupport (Facs
));
559 AcpiS3Context
->BootScriptStackSize
= PcdGet32 (PcdS3BootScriptStackSize
);
560 AcpiS3Context
->BootScriptStackBase
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)AllocateMemoryBelow4G (EfiReservedMemoryType
, PcdGet32 (PcdS3BootScriptStackSize
));
561 ASSERT (AcpiS3Context
->BootScriptStackBase
!= 0);
564 // Allocate a code buffer < 4G for S3 debug to load external code, set invalid code instructions in it.
566 AcpiS3Context
->S3DebugBufferAddress
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)AllocateMemoryBelow4G (EfiReservedMemoryType
, EFI_PAGE_SIZE
);
567 SetMem ((VOID
*)(UINTN
)AcpiS3Context
->S3DebugBufferAddress
, EFI_PAGE_SIZE
, 0xff);
569 DEBUG((EFI_D_INFO
, "AcpiS3Context: AcpiFacsTable is 0x%8x\n", AcpiS3Context
->AcpiFacsTable
));
570 DEBUG((EFI_D_INFO
, "AcpiS3Context: IdtrProfile is 0x%8x\n", AcpiS3Context
->IdtrProfile
));
571 DEBUG((EFI_D_INFO
, "AcpiS3Context: S3NvsPageTableAddress is 0x%8x\n", AcpiS3Context
->S3NvsPageTableAddress
));
572 DEBUG((EFI_D_INFO
, "AcpiS3Context: S3DebugBufferAddress is 0x%8x\n", AcpiS3Context
->S3DebugBufferAddress
));
573 DEBUG((EFI_D_INFO
, "AcpiS3Context: BootScriptStackBase is 0x%8x\n", AcpiS3Context
->BootScriptStackBase
));
574 DEBUG((EFI_D_INFO
, "AcpiS3Context: BootScriptStackSize is 0x%8x\n", AcpiS3Context
->BootScriptStackSize
));
576 Status
= SaveLockBox (
577 &gEfiAcpiVariableGuid
,
578 &AcpiS3ContextBuffer
,
579 sizeof(AcpiS3ContextBuffer
)
581 ASSERT_EFI_ERROR (Status
);
583 Status
= SaveLockBox (
584 &gEfiAcpiS3ContextGuid
,
585 (VOID
*)(UINTN
)AcpiS3Context
,
586 (UINTN
)sizeof(*AcpiS3Context
)
588 ASSERT_EFI_ERROR (Status
);
590 Status
= SetLockBoxAttributes (&gEfiAcpiS3ContextGuid
, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE
);
591 ASSERT_EFI_ERROR (Status
);
593 if (FeaturePcdGet(PcdFrameworkCompatibilitySupport
)) {
594 S3ReadyThunkPlatform (AcpiS3Context
);
601 The Driver Entry Point.
603 The function is the driver Entry point which will produce AcpiS3SaveProtocol.
605 @param ImageHandle A handle for the image that is initializing this driver
606 @param SystemTable A pointer to the EFI system table
608 @retval EFI_SUCCESS Driver initialized successfully
609 @retval EFI_UNSUPPORTED Do not support ACPI S3
610 @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources
616 IN EFI_HANDLE ImageHandle
,
617 IN EFI_SYSTEM_TABLE
*SystemTable
622 if (!PcdGetBool (PcdAcpiS3Enable
)) {
623 return EFI_UNSUPPORTED
;
626 if (!FeaturePcdGet(PcdPlatformCsmSupport
)) {
628 // More memory for no CSM tip, because GDT need relocation
630 mLegacyRegionSize
= 0x250;
632 mLegacyRegionSize
= 0x100;
635 if (FeaturePcdGet(PcdFrameworkCompatibilitySupport
)) {
636 InstallAcpiS3SaveThunk ();
639 Status
= gBS
->InstallProtocolInterface (
641 &gEfiAcpiS3SaveProtocolGuid
,
642 EFI_NATIVE_INTERFACE
,
645 ASSERT_EFI_ERROR (Status
);