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 - 2014, 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 Hook point for AcpiVariableThunkPlatform for InstallAcpiS3Save.
39 InstallAcpiS3SaveThunk (
44 Hook point for AcpiVariableThunkPlatform for S3Ready.
46 @param AcpiS3Context ACPI s3 context
49 S3ReadyThunkPlatform (
50 IN ACPI_S3_CONTEXT
*AcpiS3Context
53 UINTN mLegacyRegionSize
;
55 EFI_ACPI_S3_SAVE_PROTOCOL mS3Save
= {
56 LegacyGetS3MemorySize
,
60 EFI_GUID mAcpiS3IdtrProfileGuid
= {
61 0xdea652b0, 0xd587, 0x4c54, { 0xb5, 0xb4, 0xc6, 0x82, 0xe7, 0xa0, 0xaa, 0x3d }
65 Allocate memory below 4G memory address.
67 This function allocates memory below 4G memory address.
69 @param MemoryType Memory type of memory to allocate.
70 @param Size Size of memory to allocate.
72 @return Allocated address for output.
76 AllocateMemoryBelow4G (
77 IN EFI_MEMORY_TYPE MemoryType
,
82 EFI_PHYSICAL_ADDRESS Address
;
86 Pages
= EFI_SIZE_TO_PAGES (Size
);
89 Status
= gBS
->AllocatePages (
95 ASSERT_EFI_ERROR (Status
);
97 Buffer
= (VOID
*) (UINTN
) Address
;
98 ZeroMem (Buffer
, Size
);
105 This function scan ACPI table in RSDT.
107 @param Rsdt ACPI RSDT
108 @param Signature ACPI table signature
115 IN EFI_ACPI_DESCRIPTION_HEADER
*Rsdt
,
122 EFI_ACPI_DESCRIPTION_HEADER
*Table
;
128 EntryCount
= (Rsdt
->Length
- sizeof (EFI_ACPI_DESCRIPTION_HEADER
)) / sizeof(UINT32
);
130 EntryPtr
= (UINT32
*)(Rsdt
+ 1);
131 for (Index
= 0; Index
< EntryCount
; Index
++, EntryPtr
++) {
132 Table
= (EFI_ACPI_DESCRIPTION_HEADER
*)((UINTN
)(*EntryPtr
));
133 if (Table
->Signature
== Signature
) {
143 This function scan ACPI table in XSDT.
145 @param Xsdt ACPI XSDT
146 @param Signature ACPI table signature
153 IN EFI_ACPI_DESCRIPTION_HEADER
*Xsdt
,
161 EFI_ACPI_DESCRIPTION_HEADER
*Table
;
167 EntryCount
= (Xsdt
->Length
- sizeof (EFI_ACPI_DESCRIPTION_HEADER
)) / sizeof(UINT64
);
169 BasePtr
= (UINTN
)(Xsdt
+ 1);
170 for (Index
= 0; Index
< EntryCount
; Index
++) {
171 CopyMem (&EntryPtr
, (VOID
*)(BasePtr
+ Index
* sizeof(UINT64
)), sizeof(UINT64
));
172 Table
= (EFI_ACPI_DESCRIPTION_HEADER
*)((UINTN
)(EntryPtr
));
173 if (Table
->Signature
== Signature
) {
182 To find Facs in FADT.
184 @param Fadt FADT table pointer
186 @return Facs table pointer.
188 EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*
189 FindAcpiFacsFromFadt (
190 IN EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE
*Fadt
193 EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*Facs
;
200 if (Fadt
->Header
.Revision
< EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_REVISION
) {
201 Facs
= (EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*)(UINTN
)Fadt
->FirmwareCtrl
;
203 if (Fadt
->FirmwareCtrl
!= 0) {
204 Facs
= (EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*)(UINTN
)Fadt
->FirmwareCtrl
;
206 CopyMem (&Data64
, &Fadt
->XFirmwareCtrl
, sizeof(UINT64
));
207 Facs
= (EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*)(UINTN
)Data64
;
214 To find Facs in Acpi tables.
216 To find Firmware ACPI control strutcure in Acpi Tables since the S3 waking vector is stored
219 @param AcpiTableGuid The guid used to find ACPI table in UEFI ConfigurationTable.
221 @return Facs table pointer.
223 EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*
224 FindAcpiFacsTableByAcpiGuid (
225 IN EFI_GUID
*AcpiTableGuid
228 EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER
*Rsdp
;
229 EFI_ACPI_DESCRIPTION_HEADER
*Rsdt
;
230 EFI_ACPI_DESCRIPTION_HEADER
*Xsdt
;
231 EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE
*Fadt
;
232 EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*Facs
;
237 // found ACPI table RSD_PTR from system table
239 for (Index
= 0; Index
< gST
->NumberOfTableEntries
; Index
++) {
240 if (CompareGuid (&(gST
->ConfigurationTable
[Index
].VendorGuid
), AcpiTableGuid
)) {
242 // A match was found.
244 Rsdp
= gST
->ConfigurationTable
[Index
].VendorTable
;
256 if (Rsdp
->Revision
>= EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER_REVISION
) {
257 Xsdt
= (EFI_ACPI_DESCRIPTION_HEADER
*)(UINTN
) Rsdp
->XsdtAddress
;
258 Fadt
= ScanTableInXSDT (Xsdt
, EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE
);
260 Facs
= FindAcpiFacsFromFadt (Fadt
);
270 Rsdt
= (EFI_ACPI_DESCRIPTION_HEADER
*)(UINTN
) Rsdp
->RsdtAddress
;
271 Fadt
= ScanTableInRSDT (Rsdt
, EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE
);
273 Facs
= FindAcpiFacsFromFadt (Fadt
);
283 To find Facs in Acpi tables.
285 To find Firmware ACPI control strutcure in Acpi Tables since the S3 waking vector is stored
288 @return Facs table pointer.
290 EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*
295 EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*Facs
;
297 Facs
= FindAcpiFacsTableByAcpiGuid (&gEfiAcpi20TableGuid
);
302 return FindAcpiFacsTableByAcpiGuid (&gEfiAcpi10TableGuid
);
306 Allocates and fills in the Page Directory and Page Table Entries to
307 establish a 1:1 Virtual to Physical mapping.
308 If BootScriptExector driver will run in 64-bit mode, this function will establish the 1:1
309 virtual to physical mapping page table.
310 If BootScriptExector driver will not run in 64-bit mode, this function will do nothing.
312 @return the 1:1 Virtual to Physical identity mapping page table base address.
316 S3CreateIdentityMappingPageTables (
320 if (FeaturePcdGet (PcdDxeIplSwitchToLongMode
)) {
323 UINT8 PhysicalAddressBits
;
324 UINT32 NumberOfPml4EntriesNeeded
;
325 UINT32 NumberOfPdpEntriesNeeded
;
326 EFI_PHYSICAL_ADDRESS S3NvsPageTableAddress
;
327 UINTN TotalPageTableSize
;
329 BOOLEAN Page1GSupport
;
331 S3NvsPageTableAddress
= (EFI_PHYSICAL_ADDRESS
) PcdGet64 (PcdIdentifyMappingPageTablePtr
);
332 if (S3NvsPageTableAddress
!= 0x0) {
333 return S3NvsPageTableAddress
;
335 Page1GSupport
= FALSE
;
336 if (PcdGetBool(PcdUse1GPageTable
)) {
337 AsmCpuid (0x80000000, &RegEax
, NULL
, NULL
, NULL
);
338 if (RegEax
>= 0x80000001) {
339 AsmCpuid (0x80000001, NULL
, NULL
, NULL
, &RegEdx
);
340 if ((RegEdx
& BIT26
) != 0) {
341 Page1GSupport
= TRUE
;
347 // Get physical address bits supported.
349 Hob
= GetFirstHob (EFI_HOB_TYPE_CPU
);
351 PhysicalAddressBits
= ((EFI_HOB_CPU
*) Hob
)->SizeOfMemorySpace
;
353 AsmCpuid (0x80000000, &RegEax
, NULL
, NULL
, NULL
);
354 if (RegEax
>= 0x80000008) {
355 AsmCpuid (0x80000008, &RegEax
, NULL
, NULL
, NULL
);
356 PhysicalAddressBits
= (UINT8
) RegEax
;
358 PhysicalAddressBits
= 36;
363 // IA-32e paging translates 48-bit linear addresses to 52-bit physical addresses.
365 ASSERT (PhysicalAddressBits
<= 52);
366 if (PhysicalAddressBits
> 48) {
367 PhysicalAddressBits
= 48;
371 // Calculate the table entries needed.
373 if (PhysicalAddressBits
<= 39 ) {
374 NumberOfPml4EntriesNeeded
= 1;
375 NumberOfPdpEntriesNeeded
= (UINT32
)LShiftU64 (1, (PhysicalAddressBits
- 30));
377 NumberOfPml4EntriesNeeded
= (UINT32
)LShiftU64 (1, (PhysicalAddressBits
- 39));
378 NumberOfPdpEntriesNeeded
= 512;
382 // We need calculate whole page size then allocate once, because S3 restore page table does not know each page in Nvs.
384 if (!Page1GSupport
) {
385 TotalPageTableSize
= (UINTN
)(1 + NumberOfPml4EntriesNeeded
+ NumberOfPml4EntriesNeeded
* NumberOfPdpEntriesNeeded
);
387 TotalPageTableSize
= (UINTN
)(1 + NumberOfPml4EntriesNeeded
);
389 DEBUG ((EFI_D_ERROR
, "TotalPageTableSize - %x pages\n", TotalPageTableSize
));
392 // By architecture only one PageMapLevel4 exists - so lets allocate storage for it.
394 S3NvsPageTableAddress
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)AllocateMemoryBelow4G (EfiReservedMemoryType
, EFI_PAGES_TO_SIZE(TotalPageTableSize
));
395 ASSERT (S3NvsPageTableAddress
!= 0);
396 PcdSet64 (PcdIdentifyMappingPageTablePtr
, S3NvsPageTableAddress
);
397 return S3NvsPageTableAddress
;
401 // If DXE is running 32-bit mode, no need to establish page table.
403 return (EFI_PHYSICAL_ADDRESS
) 0;
408 Gets the buffer of legacy memory below 1 MB
409 This function is to get the buffer in legacy memory below 1MB that is required during S3 resume.
411 @param This A pointer to the EFI_ACPI_S3_SAVE_PROTOCOL instance.
412 @param Size The returned size of legacy memory below 1 MB.
414 @retval EFI_SUCCESS Size is successfully returned.
415 @retval EFI_INVALID_PARAMETER The pointer Size is NULL.
420 LegacyGetS3MemorySize (
421 IN EFI_ACPI_S3_SAVE_PROTOCOL
*This
,
426 return EFI_INVALID_PARAMETER
;
429 *Size
= mLegacyRegionSize
;
434 Prepares all information that is needed in the S3 resume boot path.
436 Allocate the resources or prepare informations and save in ACPI variable set for S3 resume boot path
438 @param This A pointer to the EFI_ACPI_S3_SAVE_PROTOCOL instance.
439 @param LegacyMemoryAddress The base address of legacy memory.
441 @retval EFI_NOT_FOUND Some necessary information cannot be found.
442 @retval EFI_SUCCESS All information was saved successfully.
443 @retval EFI_OUT_OF_RESOURCES Resources were insufficient to save all the information.
444 @retval EFI_INVALID_PARAMETER The memory range is not located below 1 MB.
450 IN EFI_ACPI_S3_SAVE_PROTOCOL
*This
,
451 IN VOID
*LegacyMemoryAddress
455 EFI_PHYSICAL_ADDRESS AcpiS3ContextBuffer
;
456 ACPI_S3_CONTEXT
*AcpiS3Context
;
457 STATIC BOOLEAN AlreadyEntered
;
458 IA32_DESCRIPTOR
*Idtr
;
459 IA32_IDT_GATE_DESCRIPTOR
*IdtGate
;
461 DEBUG ((EFI_D_INFO
, "S3Ready!\n"));
464 // Platform may invoke AcpiS3Save->S3Save() before ExitPmAuth, because we need save S3 information there, while BDS ReadyToBoot may invoke it again.
465 // So if 2nd S3Save() is triggered later, we need ignore it.
467 if (AlreadyEntered
) {
470 AlreadyEntered
= TRUE
;
472 AcpiS3Context
= AllocateMemoryBelow4G (EfiReservedMemoryType
, sizeof(*AcpiS3Context
));
473 ASSERT (AcpiS3Context
!= NULL
);
474 AcpiS3ContextBuffer
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)AcpiS3Context
;
477 // Get ACPI Table because we will save its position to variable
479 AcpiS3Context
->AcpiFacsTable
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)FindAcpiFacsTable ();
480 ASSERT (AcpiS3Context
->AcpiFacsTable
!= 0);
482 IdtGate
= AllocateMemoryBelow4G (EfiReservedMemoryType
, sizeof(IA32_IDT_GATE_DESCRIPTOR
) * 0x100 + sizeof(IA32_DESCRIPTOR
));
483 Idtr
= (IA32_DESCRIPTOR
*)(IdtGate
+ 0x100);
484 Idtr
->Base
= (UINTN
)IdtGate
;
485 Idtr
->Limit
= (UINT16
)(sizeof(IA32_IDT_GATE_DESCRIPTOR
) * 0x100 - 1);
486 AcpiS3Context
->IdtrProfile
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)Idtr
;
488 Status
= SaveLockBox (
489 &mAcpiS3IdtrProfileGuid
,
491 (UINTN
)sizeof(IA32_DESCRIPTOR
)
493 ASSERT_EFI_ERROR (Status
);
495 Status
= SetLockBoxAttributes (&mAcpiS3IdtrProfileGuid
, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE
);
496 ASSERT_EFI_ERROR (Status
);
499 // Allocate page table
501 AcpiS3Context
->S3NvsPageTableAddress
= S3CreateIdentityMappingPageTables ();
506 AcpiS3Context
->BootScriptStackSize
= PcdGet32 (PcdS3BootScriptStackSize
);
507 AcpiS3Context
->BootScriptStackBase
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)AllocateMemoryBelow4G (EfiReservedMemoryType
, PcdGet32 (PcdS3BootScriptStackSize
));
508 ASSERT (AcpiS3Context
->BootScriptStackBase
!= 0);
511 // Allocate a code buffer < 4G for S3 debug to load external code, set invalid code instructions in it.
513 AcpiS3Context
->S3DebugBufferAddress
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)AllocateMemoryBelow4G (EfiReservedMemoryType
, EFI_PAGE_SIZE
);
514 SetMem ((VOID
*)(UINTN
)AcpiS3Context
->S3DebugBufferAddress
, EFI_PAGE_SIZE
, 0xff);
516 DEBUG((EFI_D_INFO
, "AcpiS3Context: AcpiFacsTable is 0x%8x\n", AcpiS3Context
->AcpiFacsTable
));
517 DEBUG((EFI_D_INFO
, "AcpiS3Context: IdtrProfile is 0x%8x\n", AcpiS3Context
->IdtrProfile
));
518 DEBUG((EFI_D_INFO
, "AcpiS3Context: S3NvsPageTableAddress is 0x%8x\n", AcpiS3Context
->S3NvsPageTableAddress
));
519 DEBUG((EFI_D_INFO
, "AcpiS3Context: S3DebugBufferAddress is 0x%8x\n", AcpiS3Context
->S3DebugBufferAddress
));
520 DEBUG((EFI_D_INFO
, "AcpiS3Context: BootScriptStackBase is 0x%8x\n", AcpiS3Context
->BootScriptStackBase
));
521 DEBUG((EFI_D_INFO
, "AcpiS3Context: BootScriptStackSize is 0x%8x\n", AcpiS3Context
->BootScriptStackSize
));
523 Status
= SaveLockBox (
524 &gEfiAcpiVariableGuid
,
525 &AcpiS3ContextBuffer
,
526 sizeof(AcpiS3ContextBuffer
)
528 ASSERT_EFI_ERROR (Status
);
530 Status
= SaveLockBox (
531 &gEfiAcpiS3ContextGuid
,
532 (VOID
*)(UINTN
)AcpiS3Context
,
533 (UINTN
)sizeof(*AcpiS3Context
)
535 ASSERT_EFI_ERROR (Status
);
537 Status
= SetLockBoxAttributes (&gEfiAcpiS3ContextGuid
, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE
);
538 ASSERT_EFI_ERROR (Status
);
540 if (FeaturePcdGet(PcdFrameworkCompatibilitySupport
)) {
541 S3ReadyThunkPlatform (AcpiS3Context
);
548 The Driver Entry Point.
550 The function is the driver Entry point which will produce AcpiS3SaveProtocol.
552 @param ImageHandle A handle for the image that is initializing this driver
553 @param SystemTable A pointer to the EFI system table
555 @retval EFI_SUCCESS: Driver initialized successfully
556 @retval EFI_LOAD_ERROR: Failed to Initialize or has been loaded
557 @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources
563 IN EFI_HANDLE ImageHandle
,
564 IN EFI_SYSTEM_TABLE
*SystemTable
569 if (!FeaturePcdGet(PcdPlatformCsmSupport
)) {
571 // More memory for no CSM tip, because GDT need relocation
573 mLegacyRegionSize
= 0x250;
575 mLegacyRegionSize
= 0x100;
578 if (FeaturePcdGet(PcdFrameworkCompatibilitySupport
)) {
579 InstallAcpiS3SaveThunk ();
582 Status
= gBS
->InstallProtocolInterface (
584 &gEfiAcpiS3SaveProtocolGuid
,
585 EFI_NATIVE_INTERFACE
,
588 ASSERT_EFI_ERROR (Status
);