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 - 2011, 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/LockBoxLib.h>
24 #include <Library/PcdLib.h>
25 #include <Library/DebugLib.h>
26 #include <Guid/AcpiVariableCompatibility.h>
27 #include <Guid/AcpiS3Context.h>
28 #include <Guid/Acpi.h>
29 #include <Protocol/AcpiS3Save.h>
30 #include <IndustryStandard/Acpi.h>
32 #include "AcpiS3Save.h"
35 Hook point for AcpiVariableThunkPlatform for InstallAcpiS3Save.
38 InstallAcpiS3SaveThunk (
43 Hook point for AcpiVariableThunkPlatform for S3Ready.
45 @param AcpiS3Context ACPI s3 context
48 S3ReadyThunkPlatform (
49 IN ACPI_S3_CONTEXT
*AcpiS3Context
52 UINTN mLegacyRegionSize
;
54 EFI_ACPI_S3_SAVE_PROTOCOL mS3Save
= {
55 LegacyGetS3MemorySize
,
59 EFI_GUID mAcpiS3IdtrProfileGuid
= {
60 0xdea652b0, 0xd587, 0x4c54, 0xb5, 0xb4, 0xc6, 0x82, 0xe7, 0xa0, 0xaa, 0x3d
64 Allocate EfiACPIMemoryNVS below 4G memory address.
66 This function allocates EfiACPIMemoryNVS below 4G memory address.
68 @param Size Size of memory to allocate.
70 @return Allocated address for output.
74 AllocateAcpiNvsMemoryBelow4G (
79 EFI_PHYSICAL_ADDRESS Address
;
83 Pages
= EFI_SIZE_TO_PAGES (Size
);
86 Status
= gBS
->AllocatePages (
92 ASSERT_EFI_ERROR (Status
);
94 Buffer
= (VOID
*) (UINTN
) Address
;
95 ZeroMem (Buffer
, Size
);
101 To find Facs in Acpi tables.
103 To find Firmware ACPI control strutcure in Acpi Tables since the S3 waking vector is stored
106 @param AcpiTableGuid The guid used to find ACPI table in UEFI ConfigurationTable.
108 @return Facs table pointer.
110 EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*
111 FindAcpiFacsTableByAcpiGuid (
112 IN EFI_GUID
*AcpiTableGuid
115 EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER
*Rsdp
;
116 EFI_ACPI_DESCRIPTION_HEADER
*Rsdt
;
117 EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE
*Fadt
;
118 EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*Facs
;
125 // found ACPI table RSD_PTR from system table
127 for (Index
= 0; Index
< gST
->NumberOfTableEntries
; Index
++) {
128 if (CompareGuid (&(gST
->ConfigurationTable
[Index
].VendorGuid
), AcpiTableGuid
)) {
130 // A match was found.
132 Rsdp
= gST
->ConfigurationTable
[Index
].VendorTable
;
141 Rsdt
= (EFI_ACPI_DESCRIPTION_HEADER
*)(UINTN
) Rsdp
->RsdtAddress
;
142 if (Rsdt
== NULL
|| Rsdt
->Signature
!= EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_TABLE_SIGNATURE
) {
146 for (Index
= sizeof (EFI_ACPI_DESCRIPTION_HEADER
); Index
< Rsdt
->Length
; Index
= Index
+ sizeof (UINT32
)) {
148 Data32
= *(UINT32
*) ((UINT8
*) Rsdt
+ Index
);
149 Fadt
= (EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE
*) (UINT32
*) (UINTN
) Data32
;
150 if (Fadt
->Header
.Signature
== EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE
) {
155 if (Fadt
== NULL
|| Fadt
->Header
.Signature
!= EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE
) {
159 Facs
= (EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*)(UINTN
)Fadt
->FirmwareCtrl
;
165 To find Facs in Acpi tables.
167 To find Firmware ACPI control strutcure in Acpi Tables since the S3 waking vector is stored
170 @return Facs table pointer.
172 EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*
177 EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*Facs
;
179 Facs
= FindAcpiFacsTableByAcpiGuid (&gEfiAcpi20TableGuid
);
184 return FindAcpiFacsTableByAcpiGuid (&gEfiAcpi10TableGuid
);
188 Allocates and fills in the Page Directory and Page Table Entries to
189 establish a 1:1 Virtual to Physical mapping.
190 If BootScriptExector driver will run in 64-bit mode, this function will establish the 1:1
191 virtual to physical mapping page table.
192 If BootScriptExector driver will not run in 64-bit mode, this function will do nothing.
194 @return the 1:1 Virtual to Physical identity mapping page table base address.
198 S3CreateIdentityMappingPageTables (
202 if (FeaturePcdGet (PcdDxeIplSwitchToLongMode
)) {
204 UINT8 PhysicalAddressBits
;
205 EFI_PHYSICAL_ADDRESS PageAddress
;
206 UINTN IndexOfPml4Entries
;
207 UINTN IndexOfPdpEntries
;
208 UINTN IndexOfPageDirectoryEntries
;
209 UINTN NumberOfPml4EntriesNeeded
;
210 UINTN NumberOfPdpEntriesNeeded
;
211 PAGE_MAP_AND_DIRECTORY_POINTER
*PageMapLevel4Entry
;
212 PAGE_MAP_AND_DIRECTORY_POINTER
*PageMap
;
213 PAGE_MAP_AND_DIRECTORY_POINTER
*PageDirectoryPointerEntry
;
214 PAGE_TABLE_ENTRY
*PageDirectoryEntry
;
215 EFI_PHYSICAL_ADDRESS S3NvsPageTableAddress
;
216 UINTN TotalPageTableSize
;
219 // Get physical address bits supported.
221 AsmCpuid (0x80000000, &RegEax
, NULL
, NULL
, NULL
);
222 if (RegEax
>= 0x80000008) {
223 AsmCpuid (0x80000008, &RegEax
, NULL
, NULL
, NULL
);
224 PhysicalAddressBits
= (UINT8
) RegEax
;
226 PhysicalAddressBits
= 36;
230 // Calculate the table entries needed.
232 if (PhysicalAddressBits
<= 39 ) {
233 NumberOfPml4EntriesNeeded
= 1;
234 NumberOfPdpEntriesNeeded
= (UINTN
)LShiftU64 (1, (PhysicalAddressBits
- 30));
236 NumberOfPml4EntriesNeeded
= (UINTN
)LShiftU64 (1, (PhysicalAddressBits
- 39));
237 NumberOfPdpEntriesNeeded
= 512;
241 // We need calculate whole page size then allocate once, because S3 restore page table does not know each page in Nvs.
243 TotalPageTableSize
= (UINTN
)(1 + NumberOfPml4EntriesNeeded
+ NumberOfPml4EntriesNeeded
* NumberOfPdpEntriesNeeded
);
244 DEBUG ((EFI_D_ERROR
, "TotalPageTableSize - %x pages\n", TotalPageTableSize
));
247 // By architecture only one PageMapLevel4 exists - so lets allocate storgage for it.
249 S3NvsPageTableAddress
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)AllocateAcpiNvsMemoryBelow4G (EFI_PAGES_TO_SIZE(TotalPageTableSize
));
250 ASSERT (S3NvsPageTableAddress
!= 0);
251 PageMap
= (PAGE_MAP_AND_DIRECTORY_POINTER
*)(UINTN
)S3NvsPageTableAddress
;
252 S3NvsPageTableAddress
+= SIZE_4KB
;
254 PageMapLevel4Entry
= PageMap
;
256 for (IndexOfPml4Entries
= 0; IndexOfPml4Entries
< NumberOfPml4EntriesNeeded
; IndexOfPml4Entries
++, PageMapLevel4Entry
++) {
258 // Each PML4 entry points to a page of Page Directory Pointer entires.
259 // So lets allocate space for them and fill them in in the IndexOfPdpEntries loop.
261 PageDirectoryPointerEntry
= (PAGE_MAP_AND_DIRECTORY_POINTER
*)(UINTN
)S3NvsPageTableAddress
;
262 S3NvsPageTableAddress
+= SIZE_4KB
;
266 PageMapLevel4Entry
->Uint64
= (UINT64
)(UINTN
)PageDirectoryPointerEntry
;
267 PageMapLevel4Entry
->Bits
.ReadWrite
= 1;
268 PageMapLevel4Entry
->Bits
.Present
= 1;
270 for (IndexOfPdpEntries
= 0; IndexOfPdpEntries
< NumberOfPdpEntriesNeeded
; IndexOfPdpEntries
++, PageDirectoryPointerEntry
++) {
272 // Each Directory Pointer entries points to a page of Page Directory entires.
273 // So allocate space for them and fill them in in the IndexOfPageDirectoryEntries loop.
275 PageDirectoryEntry
= (PAGE_TABLE_ENTRY
*)(UINTN
)S3NvsPageTableAddress
;
276 S3NvsPageTableAddress
+= SIZE_4KB
;
279 // Fill in a Page Directory Pointer Entries
281 PageDirectoryPointerEntry
->Uint64
= (UINT64
)(UINTN
)PageDirectoryEntry
;
282 PageDirectoryPointerEntry
->Bits
.ReadWrite
= 1;
283 PageDirectoryPointerEntry
->Bits
.Present
= 1;
285 for (IndexOfPageDirectoryEntries
= 0; IndexOfPageDirectoryEntries
< 512; IndexOfPageDirectoryEntries
++, PageDirectoryEntry
++, PageAddress
+= 0x200000) {
287 // Fill in the Page Directory entries
289 PageDirectoryEntry
->Uint64
= (UINT64
)PageAddress
;
290 PageDirectoryEntry
->Bits
.ReadWrite
= 1;
291 PageDirectoryEntry
->Bits
.Present
= 1;
292 PageDirectoryEntry
->Bits
.MustBe1
= 1;
296 return (EFI_PHYSICAL_ADDRESS
) (UINTN
) PageMap
;
299 // If DXE is running 32-bit mode, no need to establish page table.
301 return (EFI_PHYSICAL_ADDRESS
) 0;
306 Gets the buffer of legacy memory below 1 MB
307 This function is to get the buffer in legacy memory below 1MB that is required during S3 resume.
309 @param This A pointer to the EFI_ACPI_S3_SAVE_PROTOCOL instance.
310 @param Size The returned size of legacy memory below 1 MB.
312 @retval EFI_SUCCESS Size is successfully returned.
313 @retval EFI_INVALID_PARAMETER The pointer Size is NULL.
318 LegacyGetS3MemorySize (
319 IN EFI_ACPI_S3_SAVE_PROTOCOL
*This
,
324 return EFI_INVALID_PARAMETER
;
327 *Size
= mLegacyRegionSize
;
332 Prepares all information that is needed in the S3 resume boot path.
334 Allocate the resources or prepare informations and save in ACPI variable set for S3 resume boot path
336 @param This A pointer to the EFI_ACPI_S3_SAVE_PROTOCOL instance.
337 @param LegacyMemoryAddress The base address of legacy memory.
339 @retval EFI_NOT_FOUND Some necessary information cannot be found.
340 @retval EFI_SUCCESS All information was saved successfully.
341 @retval EFI_OUT_OF_RESOURCES Resources were insufficient to save all the information.
342 @retval EFI_INVALID_PARAMETER The memory range is not located below 1 MB.
348 IN EFI_ACPI_S3_SAVE_PROTOCOL
*This
,
349 IN VOID
*LegacyMemoryAddress
353 EFI_PHYSICAL_ADDRESS AcpiS3ContextBuffer
;
354 ACPI_S3_CONTEXT
*AcpiS3Context
;
355 STATIC BOOLEAN AlreadyEntered
;
356 IA32_DESCRIPTOR
*Idtr
;
357 IA32_IDT_GATE_DESCRIPTOR
*IdtGate
;
359 DEBUG ((EFI_D_INFO
, "S3Ready!\n"));
362 // Platform may invoke AcpiS3Save->S3Save() before ExitPmAuth, because we need save S3 information there, while BDS ReadyToBoot may invoke it again.
363 // So if 2nd S3Save() is triggered later, we need ignore it.
365 if (AlreadyEntered
) {
368 AlreadyEntered
= TRUE
;
370 AcpiS3Context
= AllocateAcpiNvsMemoryBelow4G (sizeof(*AcpiS3Context
));
371 ASSERT (AcpiS3Context
!= NULL
);
372 AcpiS3ContextBuffer
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)AcpiS3Context
;
375 // Get ACPI Table because we will save its position to variable
377 AcpiS3Context
->AcpiFacsTable
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)FindAcpiFacsTable ();
378 ASSERT (AcpiS3Context
->AcpiFacsTable
!= 0);
380 IdtGate
= AllocateAcpiNvsMemoryBelow4G (sizeof(IA32_IDT_GATE_DESCRIPTOR
) * 0x100 + sizeof(IA32_DESCRIPTOR
));
381 Idtr
= (IA32_DESCRIPTOR
*)(IdtGate
+ 0x100);
382 Idtr
->Base
= (UINTN
)IdtGate
;
383 Idtr
->Limit
= (UINT16
)(sizeof(IA32_IDT_GATE_DESCRIPTOR
) * 0x100 - 1);
384 AcpiS3Context
->IdtrProfile
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)Idtr
;
386 Status
= SaveLockBox (
387 &mAcpiS3IdtrProfileGuid
,
389 (UINTN
)sizeof(IA32_DESCRIPTOR
)
391 ASSERT_EFI_ERROR (Status
);
393 Status
= SetLockBoxAttributes (&mAcpiS3IdtrProfileGuid
, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE
);
394 ASSERT_EFI_ERROR (Status
);
397 // Allocate page table
399 AcpiS3Context
->S3NvsPageTableAddress
= S3CreateIdentityMappingPageTables ();
404 AcpiS3Context
->BootScriptStackSize
= PcdGet32 (PcdS3BootScriptStackSize
);
405 AcpiS3Context
->BootScriptStackBase
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)AllocateAcpiNvsMemoryBelow4G (PcdGet32 (PcdS3BootScriptStackSize
));
406 ASSERT (AcpiS3Context
->BootScriptStackBase
!= 0);
409 // Allocate a code buffer < 4G for S3 debug to load external code
411 AcpiS3Context
->S3DebugBufferAddress
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)AllocateAcpiNvsMemoryBelow4G (EFI_PAGE_SIZE
);
413 DEBUG((EFI_D_INFO
, "AcpiS3Context: AcpiFacsTable is 0x%8x\n", AcpiS3Context
->AcpiFacsTable
));
414 DEBUG((EFI_D_INFO
, "AcpiS3Context: IdtrProfile is 0x%8x\n", AcpiS3Context
->IdtrProfile
));
415 DEBUG((EFI_D_INFO
, "AcpiS3Context: S3NvsPageTableAddress is 0x%8x\n", AcpiS3Context
->S3NvsPageTableAddress
));
416 DEBUG((EFI_D_INFO
, "AcpiS3Context: S3DebugBufferAddress is 0x%8x\n", AcpiS3Context
->S3DebugBufferAddress
));
418 Status
= SaveLockBox (
419 &gEfiAcpiVariableGuid
,
420 &AcpiS3ContextBuffer
,
421 sizeof(AcpiS3ContextBuffer
)
423 ASSERT_EFI_ERROR (Status
);
425 Status
= SaveLockBox (
426 &gEfiAcpiS3ContextGuid
,
427 (VOID
*)(UINTN
)AcpiS3Context
,
428 (UINTN
)sizeof(*AcpiS3Context
)
430 ASSERT_EFI_ERROR (Status
);
432 Status
= SetLockBoxAttributes (&gEfiAcpiS3ContextGuid
, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE
);
433 ASSERT_EFI_ERROR (Status
);
435 if (FeaturePcdGet(PcdFrameworkCompatibilitySupport
)) {
436 S3ReadyThunkPlatform (AcpiS3Context
);
443 The Driver Entry Point.
445 The function is the driver Entry point which will produce AcpiS3SaveProtocol.
447 @param ImageHandle A handle for the image that is initializing this driver
448 @param SystemTable A pointer to the EFI system table
450 @retval EFI_SUCCESS: Driver initialized successfully
451 @retval EFI_LOAD_ERROR: Failed to Initialize or has been loaded
452 @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources
458 IN EFI_HANDLE ImageHandle
,
459 IN EFI_SYSTEM_TABLE
*SystemTable
464 if (!FeaturePcdGet(PcdPlatformCsmSupport
)) {
466 // More memory for no CSM tip, because GDT need relocation
468 mLegacyRegionSize
= 0x250;
470 mLegacyRegionSize
= 0x100;
473 if (FeaturePcdGet(PcdFrameworkCompatibilitySupport
)) {
474 InstallAcpiS3SaveThunk ();
477 Status
= gBS
->InstallProtocolInterface (
479 &gEfiAcpiS3SaveProtocolGuid
,
480 EFI_NATIVE_INTERFACE
,
483 ASSERT_EFI_ERROR (Status
);