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) 2014-2015, Red Hat, Inc.<BR>
6 Copyright (c) 2006 - 2013, Intel Corporation. All rights reserved.<BR>
8 This program and the accompanying materials
9 are licensed and made available under the terms and conditions
10 of the BSD License which accompanies this distribution. The
11 full text of the license may be found at
12 http://opensource.org/licenses/bsd-license.php
14 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
15 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
20 #include <Library/BaseLib.h>
21 #include <Library/BaseMemoryLib.h>
22 #include <Library/UefiBootServicesTableLib.h>
23 #include <Library/UefiRuntimeServicesTableLib.h>
24 #include <Library/HobLib.h>
25 #include <Library/LockBoxLib.h>
26 #include <Library/PcdLib.h>
27 #include <Library/DebugLib.h>
28 #include <Library/QemuFwCfgLib.h>
29 #include <Guid/AcpiVariableCompatibility.h>
30 #include <Guid/AcpiS3Context.h>
31 #include <Guid/Acpi.h>
32 #include <Guid/EventGroup.h>
33 #include <Protocol/AcpiS3Save.h>
34 #include <Protocol/S3SaveState.h>
35 #include <Protocol/DxeSmmReadyToLock.h>
36 #include <Protocol/LockBox.h>
37 #include <IndustryStandard/Acpi.h>
39 #include "AcpiS3Save.h"
41 UINTN mLegacyRegionSize
;
43 EFI_ACPI_S3_SAVE_PROTOCOL mS3Save
= {
44 LegacyGetS3MemorySize
,
48 EFI_GUID mAcpiS3IdtrProfileGuid
= {
49 0xdea652b0, 0xd587, 0x4c54, { 0xb5, 0xb4, 0xc6, 0x82, 0xe7, 0xa0, 0xaa, 0x3d }
53 Allocate memory below 4G memory address.
55 This function allocates memory below 4G memory address.
57 @param MemoryType Memory type of memory to allocate.
58 @param Size Size of memory to allocate.
60 @return Allocated address for output.
64 AllocateMemoryBelow4G (
65 IN EFI_MEMORY_TYPE MemoryType
,
70 EFI_PHYSICAL_ADDRESS Address
;
74 Pages
= EFI_SIZE_TO_PAGES (Size
);
77 Status
= gBS
->AllocatePages (
83 ASSERT_EFI_ERROR (Status
);
85 Buffer
= (VOID
*) (UINTN
) Address
;
86 ZeroMem (Buffer
, Size
);
93 This function scan ACPI table in RSDT.
96 @param Signature ACPI table signature
103 IN EFI_ACPI_DESCRIPTION_HEADER
*Rsdt
,
110 EFI_ACPI_DESCRIPTION_HEADER
*Table
;
116 EntryCount
= (Rsdt
->Length
- sizeof (EFI_ACPI_DESCRIPTION_HEADER
)) / sizeof(UINT32
);
118 EntryPtr
= (UINT32
*)(Rsdt
+ 1);
119 for (Index
= 0; Index
< EntryCount
; Index
++, EntryPtr
++) {
120 Table
= (EFI_ACPI_DESCRIPTION_HEADER
*)((UINTN
)(*EntryPtr
));
121 if (Table
->Signature
== Signature
) {
131 This function scan ACPI table in XSDT.
133 @param Xsdt ACPI XSDT
134 @param Signature ACPI table signature
141 IN EFI_ACPI_DESCRIPTION_HEADER
*Xsdt
,
149 EFI_ACPI_DESCRIPTION_HEADER
*Table
;
155 EntryCount
= (Xsdt
->Length
- sizeof (EFI_ACPI_DESCRIPTION_HEADER
)) / sizeof(UINT64
);
157 BasePtr
= (UINTN
)(Xsdt
+ 1);
158 for (Index
= 0; Index
< EntryCount
; Index
++) {
159 CopyMem (&EntryPtr
, (VOID
*)(BasePtr
+ Index
* sizeof(UINT64
)), sizeof(UINT64
));
160 Table
= (EFI_ACPI_DESCRIPTION_HEADER
*)((UINTN
)(EntryPtr
));
161 if (Table
->Signature
== Signature
) {
170 To find Facs in FADT.
172 @param Fadt FADT table pointer
174 @return Facs table pointer.
176 EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*
177 FindAcpiFacsFromFadt (
178 IN EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE
*Fadt
181 EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*Facs
;
188 if (Fadt
->Header
.Revision
< EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_REVISION
) {
189 Facs
= (EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*)(UINTN
)Fadt
->FirmwareCtrl
;
191 if (Fadt
->FirmwareCtrl
!= 0) {
192 Facs
= (EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*)(UINTN
)Fadt
->FirmwareCtrl
;
194 CopyMem (&Data64
, &Fadt
->XFirmwareCtrl
, sizeof(UINT64
));
195 Facs
= (EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*)(UINTN
)Data64
;
202 To find Facs in Acpi tables.
204 To find Firmware ACPI control strutcure in Acpi Tables since the S3 waking vector is stored
207 @param AcpiTableGuid The guid used to find ACPI table in UEFI ConfigurationTable.
209 @return Facs table pointer.
211 EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*
212 FindAcpiFacsTableByAcpiGuid (
213 IN EFI_GUID
*AcpiTableGuid
216 EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER
*Rsdp
;
217 EFI_ACPI_DESCRIPTION_HEADER
*Rsdt
;
218 EFI_ACPI_DESCRIPTION_HEADER
*Xsdt
;
219 EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE
*Fadt
;
220 EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*Facs
;
225 // found ACPI table RSD_PTR from system table
227 for (Index
= 0; Index
< gST
->NumberOfTableEntries
; Index
++) {
228 if (CompareGuid (&(gST
->ConfigurationTable
[Index
].VendorGuid
), AcpiTableGuid
)) {
230 // A match was found.
232 Rsdp
= gST
->ConfigurationTable
[Index
].VendorTable
;
244 if (Rsdp
->Revision
>= EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER_REVISION
) {
245 Xsdt
= (EFI_ACPI_DESCRIPTION_HEADER
*)(UINTN
) Rsdp
->XsdtAddress
;
246 Fadt
= ScanTableInXSDT (Xsdt
, EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE
);
248 Facs
= FindAcpiFacsFromFadt (Fadt
);
258 Rsdt
= (EFI_ACPI_DESCRIPTION_HEADER
*)(UINTN
) Rsdp
->RsdtAddress
;
259 Fadt
= ScanTableInRSDT (Rsdt
, EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE
);
261 Facs
= FindAcpiFacsFromFadt (Fadt
);
271 To find Facs in Acpi tables.
273 To find Firmware ACPI control strutcure in Acpi Tables since the S3 waking vector is stored
276 @return Facs table pointer.
278 EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*
283 EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*Facs
;
285 Facs
= FindAcpiFacsTableByAcpiGuid (&gEfiAcpi20TableGuid
);
290 return FindAcpiFacsTableByAcpiGuid (&gEfiAcpi10TableGuid
);
294 Allocates and fills in the Page Directory and Page Table Entries to
295 establish a 1:1 Virtual to Physical mapping.
296 If BootScriptExector driver will run in 64-bit mode, this function will establish the 1:1
297 virtual to physical mapping page table.
298 If BootScriptExector driver will not run in 64-bit mode, this function will do nothing.
300 @return the 1:1 Virtual to Physical identity mapping page table base address.
304 S3CreateIdentityMappingPageTables (
308 if (FeaturePcdGet (PcdDxeIplSwitchToLongMode
)) {
311 UINT8 PhysicalAddressBits
;
312 UINT32 NumberOfPml4EntriesNeeded
;
313 UINT32 NumberOfPdpEntriesNeeded
;
314 EFI_PHYSICAL_ADDRESS S3NvsPageTableAddress
;
315 UINTN TotalPageTableSize
;
317 BOOLEAN Page1GSupport
;
319 Page1GSupport
= FALSE
;
320 if (PcdGetBool(PcdUse1GPageTable
)) {
321 AsmCpuid (0x80000000, &RegEax
, NULL
, NULL
, NULL
);
322 if (RegEax
>= 0x80000001) {
323 AsmCpuid (0x80000001, NULL
, NULL
, NULL
, &RegEdx
);
324 if ((RegEdx
& BIT26
) != 0) {
325 Page1GSupport
= TRUE
;
331 // Get physical address bits supported.
333 Hob
= GetFirstHob (EFI_HOB_TYPE_CPU
);
335 PhysicalAddressBits
= ((EFI_HOB_CPU
*) Hob
)->SizeOfMemorySpace
;
337 AsmCpuid (0x80000000, &RegEax
, NULL
, NULL
, NULL
);
338 if (RegEax
>= 0x80000008) {
339 AsmCpuid (0x80000008, &RegEax
, NULL
, NULL
, NULL
);
340 PhysicalAddressBits
= (UINT8
) RegEax
;
342 PhysicalAddressBits
= 36;
347 // IA-32e paging translates 48-bit linear addresses to 52-bit physical addresses.
349 ASSERT (PhysicalAddressBits
<= 52);
350 if (PhysicalAddressBits
> 48) {
351 PhysicalAddressBits
= 48;
355 // Calculate the table entries needed.
357 if (PhysicalAddressBits
<= 39 ) {
358 NumberOfPml4EntriesNeeded
= 1;
359 NumberOfPdpEntriesNeeded
= (UINT32
)LShiftU64 (1, (PhysicalAddressBits
- 30));
361 NumberOfPml4EntriesNeeded
= (UINT32
)LShiftU64 (1, (PhysicalAddressBits
- 39));
362 NumberOfPdpEntriesNeeded
= 512;
366 // We need calculate whole page size then allocate once, because S3 restore page table does not know each page in Nvs.
368 if (!Page1GSupport
) {
369 TotalPageTableSize
= (UINTN
)(1 + NumberOfPml4EntriesNeeded
+ NumberOfPml4EntriesNeeded
* NumberOfPdpEntriesNeeded
);
371 TotalPageTableSize
= (UINTN
)(1 + NumberOfPml4EntriesNeeded
);
373 DEBUG ((EFI_D_ERROR
, "TotalPageTableSize - %x pages\n", TotalPageTableSize
));
376 // By architecture only one PageMapLevel4 exists - so lets allocate storage for it.
378 S3NvsPageTableAddress
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)AllocateMemoryBelow4G (EfiReservedMemoryType
, EFI_PAGES_TO_SIZE(TotalPageTableSize
));
379 ASSERT (S3NvsPageTableAddress
!= 0);
380 return S3NvsPageTableAddress
;
383 // If DXE is running 32-bit mode, no need to establish page table.
385 return (EFI_PHYSICAL_ADDRESS
) 0;
390 Gets the buffer of legacy memory below 1 MB
391 This function is to get the buffer in legacy memory below 1MB that is required during S3 resume.
393 @param This A pointer to the EFI_ACPI_S3_SAVE_PROTOCOL instance.
394 @param Size The returned size of legacy memory below 1 MB.
396 @retval EFI_SUCCESS Size is successfully returned.
397 @retval EFI_INVALID_PARAMETER The pointer Size is NULL.
402 LegacyGetS3MemorySize (
403 IN EFI_ACPI_S3_SAVE_PROTOCOL
*This
,
410 return EFI_INVALID_PARAMETER
;
413 *Size
= mLegacyRegionSize
;
418 Save the S3 boot script.
420 Note that we trigger DxeSmmReadyToLock here -- otherwise the script wouldn't
421 be saved actually. Triggering this protocol installation event in turn locks
422 down SMM, so no further changes to LockBoxes or SMRAM are possible
433 EFI_S3_SAVE_STATE_PROTOCOL
*BootScript
;
435 STATIC CONST UINT8 Info
[] = { 0xDE, 0xAD, 0xBE, 0xEF };
437 Status
= gBS
->LocateProtocol (&gEfiS3SaveStateProtocolGuid
, NULL
,
438 (VOID
**) &BootScript
);
439 ASSERT_EFI_ERROR (Status
);
442 // Despite the opcode documentation in the PI spec, the protocol
443 // implementation embeds a deep copy of the info in the boot script, rather
444 // than storing just a pointer to runtime or NVS storage.
446 Status
= BootScript
->Write(BootScript
, EFI_BOOT_SCRIPT_INFORMATION_OPCODE
,
447 (UINT32
) sizeof Info
,
448 (EFI_PHYSICAL_ADDRESS
)(UINTN
) &Info
);
449 ASSERT_EFI_ERROR (Status
);
452 Status
= gBS
->InstallProtocolInterface (&Handle
,
453 &gEfiDxeSmmReadyToLockProtocolGuid
, EFI_NATIVE_INTERFACE
,
455 ASSERT_EFI_ERROR (Status
);
460 Prepares all information that is needed in the S3 resume boot path.
462 Allocate the resources or prepare informations and save in ACPI variable set for S3 resume boot path
464 @param This A pointer to the EFI_ACPI_S3_SAVE_PROTOCOL instance.
465 @param LegacyMemoryAddress The base address of legacy memory.
467 @retval EFI_NOT_FOUND Some necessary information cannot be found.
468 @retval EFI_SUCCESS All information was saved successfully.
469 @retval EFI_OUT_OF_RESOURCES Resources were insufficient to save all the information.
470 @retval EFI_INVALID_PARAMETER The memory range is not located below 1 MB.
476 IN EFI_ACPI_S3_SAVE_PROTOCOL
*This
,
477 IN VOID
*LegacyMemoryAddress
481 EFI_PHYSICAL_ADDRESS AcpiS3ContextBuffer
;
482 ACPI_S3_CONTEXT
*AcpiS3Context
;
483 STATIC BOOLEAN AlreadyEntered
;
484 IA32_DESCRIPTOR
*Idtr
;
485 IA32_IDT_GATE_DESCRIPTOR
*IdtGate
;
487 DEBUG ((EFI_D_INFO
, "S3Ready!\n"));
490 // Platform may invoke AcpiS3Save->S3Save() before ExitPmAuth, because we need save S3 information there, while BDS ReadyToBoot may invoke it again.
491 // So if 2nd S3Save() is triggered later, we need ignore it.
493 if (AlreadyEntered
) {
496 AlreadyEntered
= TRUE
;
498 ASSERT (LegacyMemoryAddress
== NULL
);
500 AcpiS3Context
= AllocateMemoryBelow4G (EfiReservedMemoryType
, sizeof(*AcpiS3Context
));
501 ASSERT (AcpiS3Context
!= NULL
);
502 AcpiS3ContextBuffer
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)AcpiS3Context
;
505 // Get ACPI Table because we will save its position to variable
507 AcpiS3Context
->AcpiFacsTable
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)FindAcpiFacsTable ();
508 ASSERT (AcpiS3Context
->AcpiFacsTable
!= 0);
510 IdtGate
= AllocateMemoryBelow4G (EfiReservedMemoryType
, sizeof(IA32_IDT_GATE_DESCRIPTOR
) * 0x100 + sizeof(IA32_DESCRIPTOR
));
511 Idtr
= (IA32_DESCRIPTOR
*)(IdtGate
+ 0x100);
512 Idtr
->Base
= (UINTN
)IdtGate
;
513 Idtr
->Limit
= (UINT16
)(sizeof(IA32_IDT_GATE_DESCRIPTOR
) * 0x100 - 1);
514 AcpiS3Context
->IdtrProfile
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)Idtr
;
516 Status
= SaveLockBox (
517 &mAcpiS3IdtrProfileGuid
,
519 (UINTN
)sizeof(IA32_DESCRIPTOR
)
521 ASSERT_EFI_ERROR (Status
);
523 Status
= SetLockBoxAttributes (&mAcpiS3IdtrProfileGuid
, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE
);
524 ASSERT_EFI_ERROR (Status
);
527 // Allocate page table
529 AcpiS3Context
->S3NvsPageTableAddress
= S3CreateIdentityMappingPageTables ();
534 AcpiS3Context
->BootScriptStackSize
= PcdGet32 (PcdS3BootScriptStackSize
);
535 AcpiS3Context
->BootScriptStackBase
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)AllocateMemoryBelow4G (EfiReservedMemoryType
, PcdGet32 (PcdS3BootScriptStackSize
));
536 ASSERT (AcpiS3Context
->BootScriptStackBase
!= 0);
539 // Allocate a code buffer < 4G for S3 debug to load external code, set invalid code instructions in it.
541 AcpiS3Context
->S3DebugBufferAddress
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)AllocateMemoryBelow4G (EfiReservedMemoryType
, EFI_PAGE_SIZE
);
542 SetMem ((VOID
*)(UINTN
)AcpiS3Context
->S3DebugBufferAddress
, EFI_PAGE_SIZE
, 0xff);
544 DEBUG((EFI_D_INFO
, "AcpiS3Context: AcpiFacsTable is 0x%8x\n", AcpiS3Context
->AcpiFacsTable
));
545 DEBUG((EFI_D_INFO
, "AcpiS3Context: IdtrProfile is 0x%8x\n", AcpiS3Context
->IdtrProfile
));
546 DEBUG((EFI_D_INFO
, "AcpiS3Context: S3NvsPageTableAddress is 0x%8x\n", AcpiS3Context
->S3NvsPageTableAddress
));
547 DEBUG((EFI_D_INFO
, "AcpiS3Context: S3DebugBufferAddress is 0x%8x\n", AcpiS3Context
->S3DebugBufferAddress
));
549 Status
= SaveLockBox (
550 &gEfiAcpiVariableGuid
,
551 &AcpiS3ContextBuffer
,
552 sizeof(AcpiS3ContextBuffer
)
554 ASSERT_EFI_ERROR (Status
);
556 Status
= SaveLockBox (
557 &gEfiAcpiS3ContextGuid
,
558 (VOID
*)(UINTN
)AcpiS3Context
,
559 (UINTN
)sizeof(*AcpiS3Context
)
561 ASSERT_EFI_ERROR (Status
);
563 Status
= SetLockBoxAttributes (&gEfiAcpiS3ContextGuid
, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE
);
564 ASSERT_EFI_ERROR (Status
);
567 // Save the boot script too. Note that this requires/includes emitting the
568 // DxeSmmReadyToLock event, which in turn locks down SMM.
575 Callback function executed when the EndOfDxe event group is signaled.
577 @param[in] Event Event whose notification function is being invoked.
578 @param[in] Context The pointer to the notification function's context, which
579 is implementation-dependent.
591 // Our S3Ready() function ignores both of its parameters, and always
596 NULL
// LegacyMemoryAddress
598 ASSERT_EFI_ERROR (Status
);
601 // Close the event, deregistering the callback and freeing resources.
603 Status
= gBS
->CloseEvent (Event
);
604 ASSERT_EFI_ERROR (Status
);
609 The Driver Entry Point.
611 The function is the driver Entry point which will produce AcpiS3SaveProtocol.
613 @param ImageHandle A handle for the image that is initializing this driver
614 @param SystemTable A pointer to the EFI system table
616 @retval EFI_SUCCESS: Driver initialized successfully
617 @retval EFI_LOAD_ERROR: Failed to Initialize or has been loaded
618 @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources
624 IN EFI_HANDLE ImageHandle
,
625 IN EFI_SYSTEM_TABLE
*SystemTable
629 EFI_EVENT EndOfDxeEvent
;
631 if (!QemuFwCfgS3Enabled()) {
632 return EFI_LOAD_ERROR
;
635 if (!FeaturePcdGet(PcdPlatformCsmSupport
)) {
637 // More memory for no CSM tip, because GDT need relocation
639 mLegacyRegionSize
= 0x250;
641 mLegacyRegionSize
= 0x100;
644 Status
= gBS
->InstallMultipleProtocolInterfaces (
646 &gEfiAcpiS3SaveProtocolGuid
, &mS3Save
,
647 &gEfiLockBoxProtocolGuid
, NULL
,
650 ASSERT_EFI_ERROR (Status
);
652 Status
= gBS
->CreateEventEx (
656 NULL
, /* NotifyContext */
657 &gEfiEndOfDxeEventGroupGuid
,
660 ASSERT_EFI_ERROR (Status
);