2 This is a replacement for the ACPI S3 Save protocol.
4 The ACPI S3 Save protocol used to be defined in the S3 boot path
5 specification 0.9. Instead, the same functionality is now hooked to the
8 Copyright (c) 2014-2015, Red Hat, Inc.<BR>
9 Copyright (c) 2006 - 2013, Intel Corporation. All rights reserved.<BR>
11 This program and the accompanying materials
12 are licensed and made available under the terms and conditions
13 of the BSD License which accompanies this distribution. The
14 full text of the license may be found at
15 http://opensource.org/licenses/bsd-license.php
17 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
18 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
23 #include <Library/BaseLib.h>
24 #include <Library/BaseMemoryLib.h>
25 #include <Library/UefiBootServicesTableLib.h>
26 #include <Library/UefiRuntimeServicesTableLib.h>
27 #include <Library/HobLib.h>
28 #include <Library/LockBoxLib.h>
29 #include <Library/PcdLib.h>
30 #include <Library/DebugLib.h>
31 #include <Library/QemuFwCfgLib.h>
32 #include <Guid/AcpiVariableCompatibility.h>
33 #include <Guid/AcpiS3Context.h>
34 #include <Guid/Acpi.h>
35 #include <Guid/EventGroup.h>
36 #include <Protocol/LockBox.h>
37 #include <IndustryStandard/Acpi.h>
39 EFI_GUID mAcpiS3IdtrProfileGuid
= {
40 0xdea652b0, 0xd587, 0x4c54, { 0xb5, 0xb4, 0xc6, 0x82, 0xe7, 0xa0, 0xaa, 0x3d }
44 Allocate memory below 4G memory address.
46 This function allocates memory below 4G memory address.
48 @param MemoryType Memory type of memory to allocate.
49 @param Size Size of memory to allocate.
51 @return Allocated address for output.
55 AllocateMemoryBelow4G (
56 IN EFI_MEMORY_TYPE MemoryType
,
61 EFI_PHYSICAL_ADDRESS Address
;
65 Pages
= EFI_SIZE_TO_PAGES (Size
);
68 Status
= gBS
->AllocatePages (
74 ASSERT_EFI_ERROR (Status
);
76 Buffer
= (VOID
*) (UINTN
) Address
;
77 ZeroMem (Buffer
, Size
);
84 This function scan ACPI table in RSDT.
87 @param Signature ACPI table signature
94 IN EFI_ACPI_DESCRIPTION_HEADER
*Rsdt
,
101 EFI_ACPI_DESCRIPTION_HEADER
*Table
;
107 EntryCount
= (Rsdt
->Length
- sizeof (EFI_ACPI_DESCRIPTION_HEADER
)) / sizeof(UINT32
);
109 EntryPtr
= (UINT32
*)(Rsdt
+ 1);
110 for (Index
= 0; Index
< EntryCount
; Index
++, EntryPtr
++) {
111 Table
= (EFI_ACPI_DESCRIPTION_HEADER
*)((UINTN
)(*EntryPtr
));
112 if (Table
->Signature
== Signature
) {
122 This function scan ACPI table in XSDT.
124 @param Xsdt ACPI XSDT
125 @param Signature ACPI table signature
132 IN EFI_ACPI_DESCRIPTION_HEADER
*Xsdt
,
140 EFI_ACPI_DESCRIPTION_HEADER
*Table
;
146 EntryCount
= (Xsdt
->Length
- sizeof (EFI_ACPI_DESCRIPTION_HEADER
)) / sizeof(UINT64
);
148 BasePtr
= (UINTN
)(Xsdt
+ 1);
149 for (Index
= 0; Index
< EntryCount
; Index
++) {
150 CopyMem (&EntryPtr
, (VOID
*)(BasePtr
+ Index
* sizeof(UINT64
)), sizeof(UINT64
));
151 Table
= (EFI_ACPI_DESCRIPTION_HEADER
*)((UINTN
)(EntryPtr
));
152 if (Table
->Signature
== Signature
) {
161 To find Facs in FADT.
163 @param Fadt FADT table pointer
165 @return Facs table pointer.
167 EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*
168 FindAcpiFacsFromFadt (
169 IN EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE
*Fadt
172 EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*Facs
;
179 if (Fadt
->Header
.Revision
< EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_REVISION
) {
180 Facs
= (EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*)(UINTN
)Fadt
->FirmwareCtrl
;
182 if (Fadt
->FirmwareCtrl
!= 0) {
183 Facs
= (EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*)(UINTN
)Fadt
->FirmwareCtrl
;
185 CopyMem (&Data64
, &Fadt
->XFirmwareCtrl
, sizeof(UINT64
));
186 Facs
= (EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*)(UINTN
)Data64
;
193 To find Facs in Acpi tables.
195 To find Firmware ACPI control strutcure in Acpi Tables since the S3 waking vector is stored
198 @param AcpiTableGuid The guid used to find ACPI table in UEFI ConfigurationTable.
200 @return Facs table pointer.
202 EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*
203 FindAcpiFacsTableByAcpiGuid (
204 IN EFI_GUID
*AcpiTableGuid
207 EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER
*Rsdp
;
208 EFI_ACPI_DESCRIPTION_HEADER
*Rsdt
;
209 EFI_ACPI_DESCRIPTION_HEADER
*Xsdt
;
210 EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE
*Fadt
;
211 EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*Facs
;
216 // found ACPI table RSD_PTR from system table
218 for (Index
= 0; Index
< gST
->NumberOfTableEntries
; Index
++) {
219 if (CompareGuid (&(gST
->ConfigurationTable
[Index
].VendorGuid
), AcpiTableGuid
)) {
221 // A match was found.
223 Rsdp
= gST
->ConfigurationTable
[Index
].VendorTable
;
235 if (Rsdp
->Revision
>= EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER_REVISION
) {
236 Xsdt
= (EFI_ACPI_DESCRIPTION_HEADER
*)(UINTN
) Rsdp
->XsdtAddress
;
237 Fadt
= ScanTableInXSDT (Xsdt
, EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE
);
239 Facs
= FindAcpiFacsFromFadt (Fadt
);
249 Rsdt
= (EFI_ACPI_DESCRIPTION_HEADER
*)(UINTN
) Rsdp
->RsdtAddress
;
250 Fadt
= ScanTableInRSDT (Rsdt
, EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE
);
252 Facs
= FindAcpiFacsFromFadt (Fadt
);
262 To find Facs in Acpi tables.
264 To find Firmware ACPI control strutcure in Acpi Tables since the S3 waking vector is stored
267 @return Facs table pointer.
269 EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*
274 EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*Facs
;
276 Facs
= FindAcpiFacsTableByAcpiGuid (&gEfiAcpi20TableGuid
);
281 return FindAcpiFacsTableByAcpiGuid (&gEfiAcpi10TableGuid
);
285 Allocates and fills in the Page Directory and Page Table Entries to
286 establish a 1:1 Virtual to Physical mapping.
287 If BootScriptExector driver will run in 64-bit mode, this function will establish the 1:1
288 virtual to physical mapping page table.
289 If BootScriptExector driver will not run in 64-bit mode, this function will do nothing.
291 @return the 1:1 Virtual to Physical identity mapping page table base address.
295 S3CreateIdentityMappingPageTables (
299 if (FeaturePcdGet (PcdDxeIplSwitchToLongMode
)) {
302 UINT8 PhysicalAddressBits
;
303 UINT32 NumberOfPml4EntriesNeeded
;
304 UINT32 NumberOfPdpEntriesNeeded
;
305 EFI_PHYSICAL_ADDRESS S3NvsPageTableAddress
;
306 UINTN TotalPageTableSize
;
308 BOOLEAN Page1GSupport
;
310 Page1GSupport
= FALSE
;
311 if (PcdGetBool(PcdUse1GPageTable
)) {
312 AsmCpuid (0x80000000, &RegEax
, NULL
, NULL
, NULL
);
313 if (RegEax
>= 0x80000001) {
314 AsmCpuid (0x80000001, NULL
, NULL
, NULL
, &RegEdx
);
315 if ((RegEdx
& BIT26
) != 0) {
316 Page1GSupport
= TRUE
;
322 // Get physical address bits supported.
324 Hob
= GetFirstHob (EFI_HOB_TYPE_CPU
);
326 PhysicalAddressBits
= ((EFI_HOB_CPU
*) Hob
)->SizeOfMemorySpace
;
328 AsmCpuid (0x80000000, &RegEax
, NULL
, NULL
, NULL
);
329 if (RegEax
>= 0x80000008) {
330 AsmCpuid (0x80000008, &RegEax
, NULL
, NULL
, NULL
);
331 PhysicalAddressBits
= (UINT8
) RegEax
;
333 PhysicalAddressBits
= 36;
338 // IA-32e paging translates 48-bit linear addresses to 52-bit physical addresses.
340 ASSERT (PhysicalAddressBits
<= 52);
341 if (PhysicalAddressBits
> 48) {
342 PhysicalAddressBits
= 48;
346 // Calculate the table entries needed.
348 if (PhysicalAddressBits
<= 39 ) {
349 NumberOfPml4EntriesNeeded
= 1;
350 NumberOfPdpEntriesNeeded
= (UINT32
)LShiftU64 (1, (PhysicalAddressBits
- 30));
352 NumberOfPml4EntriesNeeded
= (UINT32
)LShiftU64 (1, (PhysicalAddressBits
- 39));
353 NumberOfPdpEntriesNeeded
= 512;
357 // We need calculate whole page size then allocate once, because S3 restore page table does not know each page in Nvs.
359 if (!Page1GSupport
) {
360 TotalPageTableSize
= (UINTN
)(1 + NumberOfPml4EntriesNeeded
+ NumberOfPml4EntriesNeeded
* NumberOfPdpEntriesNeeded
);
362 TotalPageTableSize
= (UINTN
)(1 + NumberOfPml4EntriesNeeded
);
364 DEBUG ((EFI_D_ERROR
, "TotalPageTableSize - %Lx pages\n",
365 (UINT64
)TotalPageTableSize
));
368 // By architecture only one PageMapLevel4 exists - so lets allocate storage for it.
370 S3NvsPageTableAddress
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)AllocateMemoryBelow4G (EfiReservedMemoryType
, EFI_PAGES_TO_SIZE(TotalPageTableSize
));
371 ASSERT (S3NvsPageTableAddress
!= 0);
372 return S3NvsPageTableAddress
;
375 // If DXE is running 32-bit mode, no need to establish page table.
377 return (EFI_PHYSICAL_ADDRESS
) 0;
382 Prepares all information that is needed in the S3 resume boot path.
384 Allocate the resources or prepare informations and save in ACPI variable set for S3 resume boot path
386 @retval EFI_SUCCESS All information was saved successfully.
396 EFI_PHYSICAL_ADDRESS AcpiS3ContextBuffer
;
397 ACPI_S3_CONTEXT
*AcpiS3Context
;
398 STATIC BOOLEAN AlreadyEntered
;
399 IA32_DESCRIPTOR
*Idtr
;
400 IA32_IDT_GATE_DESCRIPTOR
*IdtGate
;
402 DEBUG ((EFI_D_INFO
, "S3Ready!\n"));
404 ASSERT (!AlreadyEntered
);
405 if (AlreadyEntered
) {
408 AlreadyEntered
= TRUE
;
410 AcpiS3Context
= AllocateMemoryBelow4G (EfiReservedMemoryType
, sizeof(*AcpiS3Context
));
411 ASSERT (AcpiS3Context
!= NULL
);
412 AcpiS3ContextBuffer
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)AcpiS3Context
;
415 // Get ACPI Table because we will save its position to variable
417 AcpiS3Context
->AcpiFacsTable
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)FindAcpiFacsTable ();
418 ASSERT (AcpiS3Context
->AcpiFacsTable
!= 0);
420 IdtGate
= AllocateMemoryBelow4G (EfiReservedMemoryType
, sizeof(IA32_IDT_GATE_DESCRIPTOR
) * 0x100 + sizeof(IA32_DESCRIPTOR
));
421 Idtr
= (IA32_DESCRIPTOR
*)(IdtGate
+ 0x100);
422 Idtr
->Base
= (UINTN
)IdtGate
;
423 Idtr
->Limit
= (UINT16
)(sizeof(IA32_IDT_GATE_DESCRIPTOR
) * 0x100 - 1);
424 AcpiS3Context
->IdtrProfile
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)Idtr
;
426 Status
= SaveLockBox (
427 &mAcpiS3IdtrProfileGuid
,
429 (UINTN
)sizeof(IA32_DESCRIPTOR
)
431 ASSERT_EFI_ERROR (Status
);
433 Status
= SetLockBoxAttributes (&mAcpiS3IdtrProfileGuid
, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE
);
434 ASSERT_EFI_ERROR (Status
);
437 // Allocate page table
439 AcpiS3Context
->S3NvsPageTableAddress
= S3CreateIdentityMappingPageTables ();
444 AcpiS3Context
->BootScriptStackSize
= PcdGet32 (PcdS3BootScriptStackSize
);
445 AcpiS3Context
->BootScriptStackBase
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)AllocateMemoryBelow4G (EfiReservedMemoryType
, PcdGet32 (PcdS3BootScriptStackSize
));
446 ASSERT (AcpiS3Context
->BootScriptStackBase
!= 0);
449 // Allocate a code buffer < 4G for S3 debug to load external code, set invalid code instructions in it.
451 AcpiS3Context
->S3DebugBufferAddress
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)AllocateMemoryBelow4G (EfiReservedMemoryType
, EFI_PAGE_SIZE
);
452 SetMem ((VOID
*)(UINTN
)AcpiS3Context
->S3DebugBufferAddress
, EFI_PAGE_SIZE
, 0xff);
454 DEBUG ((EFI_D_INFO
, "AcpiS3Context: AcpiFacsTable is 0x%8Lx\n",
455 AcpiS3Context
->AcpiFacsTable
));
456 DEBUG ((EFI_D_INFO
, "AcpiS3Context: IdtrProfile is 0x%8Lx\n",
457 AcpiS3Context
->IdtrProfile
));
458 DEBUG ((EFI_D_INFO
, "AcpiS3Context: S3NvsPageTableAddress is 0x%8Lx\n",
459 AcpiS3Context
->S3NvsPageTableAddress
));
460 DEBUG ((EFI_D_INFO
, "AcpiS3Context: S3DebugBufferAddress is 0x%8Lx\n",
461 AcpiS3Context
->S3DebugBufferAddress
));
463 Status
= SaveLockBox (
464 &gEfiAcpiVariableGuid
,
465 &AcpiS3ContextBuffer
,
466 sizeof(AcpiS3ContextBuffer
)
468 ASSERT_EFI_ERROR (Status
);
470 Status
= SaveLockBox (
471 &gEfiAcpiS3ContextGuid
,
472 (VOID
*)(UINTN
)AcpiS3Context
,
473 (UINTN
)sizeof(*AcpiS3Context
)
475 ASSERT_EFI_ERROR (Status
);
477 Status
= SetLockBoxAttributes (&gEfiAcpiS3ContextGuid
, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE
);
478 ASSERT_EFI_ERROR (Status
);
484 Callback function executed when the EndOfDxe event group is signaled.
486 @param[in] Event Event whose notification function is being invoked.
487 @param[in] Context The pointer to the notification function's context, which
488 is implementation-dependent.
500 // Our S3Ready() function always succeeds.
503 ASSERT_EFI_ERROR (Status
);
506 // Close the event, deregistering the callback and freeing resources.
508 Status
= gBS
->CloseEvent (Event
);
509 ASSERT_EFI_ERROR (Status
);
514 The Driver Entry Point.
516 The function is the driver Entry point that will register the End-of-Dxe
519 @param ImageHandle A handle for the image that is initializing this driver
520 @param SystemTable A pointer to the EFI system table
522 @retval EFI_SUCCESS: Driver initialized successfully
523 @retval EFI_LOAD_ERROR: Failed to Initialize or has been loaded
524 @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources
529 InstallEndOfDxeCallback (
530 IN EFI_HANDLE ImageHandle
,
531 IN EFI_SYSTEM_TABLE
*SystemTable
535 EFI_EVENT EndOfDxeEvent
;
537 if (!QemuFwCfgS3Enabled()) {
538 return EFI_LOAD_ERROR
;
541 Status
= gBS
->InstallMultipleProtocolInterfaces (
543 &gEfiLockBoxProtocolGuid
, NULL
,
546 ASSERT_EFI_ERROR (Status
);
548 Status
= gBS
->CreateEventEx (
552 NULL
, /* NotifyContext */
553 &gEfiEndOfDxeEventGroupGuid
,
556 ASSERT_EFI_ERROR (Status
);