2 This module produces the EFI_PEI_S3_RESUME2_PPI.
3 This module works with StandAloneBootScriptExecutor to S3 resume to OS.
4 This module will execute the boot script saved during last boot and after that,
5 control is passed to OS waking up handler.
7 Copyright (c) 2006 - 2022, Intel Corporation. All rights reserved.<BR>
8 Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR>
10 SPDX-License-Identifier: BSD-2-Clause-Patent
16 #include <Guid/AcpiS3Context.h>
17 #include <Guid/BootScriptExecutorVariable.h>
18 #include <Guid/ExtendedFirmwarePerformance.h>
19 #include <Guid/EndOfS3Resume.h>
20 #include <Guid/S3SmmInitDone.h>
21 #include <Ppi/S3Resume2.h>
22 #include <Ppi/SmmAccess.h>
23 #include <Ppi/PostBootScriptTable.h>
24 #include <Ppi/EndOfPeiPhase.h>
25 #include <Ppi/SmmCommunication.h>
27 #include <Library/DebugLib.h>
28 #include <Library/BaseLib.h>
29 #include <Library/PeimEntryPoint.h>
30 #include <Library/PeiServicesLib.h>
31 #include <Library/HobLib.h>
32 #include <Library/PerformanceLib.h>
33 #include <Library/PeiServicesTablePointerLib.h>
34 #include <Library/IoLib.h>
35 #include <Library/BaseMemoryLib.h>
36 #include <Library/MemoryAllocationLib.h>
37 #include <Library/PcdLib.h>
38 #include <Library/DebugAgentLib.h>
39 #include <Library/LocalApicLib.h>
40 #include <Library/ReportStatusCodeLib.h>
42 #include <Library/HobLib.h>
43 #include <Library/LockBoxLib.h>
44 #include <IndustryStandard/Acpi.h>
47 This macro aligns the address of a variable with auto storage
48 duration down to CPU_STACK_ALIGNMENT.
50 Since the stack grows downward, the result preserves more of the
51 stack than the original address (or the same amount), not less.
53 #define STACK_ALIGN_DOWN(Ptr) \
54 ((UINTN)(Ptr) & ~(UINTN)(CPU_STACK_ALIGNMENT - 1))
56 #define PAGING_1G_ADDRESS_MASK_64 0x000FFFFFC0000000ull
71 UINT32 DefaultSize
: 1;
72 UINT32 Granularity
: 1;
79 // Page-Map Level-4 Offset (PML4) and
80 // Page-Directory-Pointer Offset (PDPE) entries 4K & 2MB
84 UINT64 Present
: 1; // 0 = Not present in memory, 1 = Present in memory
85 UINT64 ReadWrite
: 1; // 0 = Read-Only, 1= Read/Write
86 UINT64 UserSupervisor
: 1; // 0 = Supervisor, 1=User
87 UINT64 WriteThrough
: 1; // 0 = Write-Back caching, 1=Write-Through caching
88 UINT64 CacheDisabled
: 1; // 0 = Cached, 1=Non-Cached
89 UINT64 Accessed
: 1; // 0 = Not accessed, 1 = Accessed (set by CPU)
90 UINT64 Reserved
: 1; // Reserved
91 UINT64 MustBeZero
: 2; // Must Be Zero
92 UINT64 Available
: 3; // Available for use by system software
93 UINT64 PageTableBaseAddress
: 40; // Page Table Base Address
94 UINT64 AvabilableHigh
: 11; // Available for use by system software
95 UINT64 Nx
: 1; // No Execute bit
98 } PAGE_MAP_AND_DIRECTORY_POINTER
;
101 // Page Table Entry 2MB
105 UINT64 Present
: 1; // 0 = Not present in memory, 1 = Present in memory
106 UINT64 ReadWrite
: 1; // 0 = Read-Only, 1= Read/Write
107 UINT64 UserSupervisor
: 1; // 0 = Supervisor, 1=User
108 UINT64 WriteThrough
: 1; // 0 = Write-Back caching, 1=Write-Through caching
109 UINT64 CacheDisabled
: 1; // 0 = Cached, 1=Non-Cached
110 UINT64 Accessed
: 1; // 0 = Not accessed, 1 = Accessed (set by CPU)
111 UINT64 Dirty
: 1; // 0 = Not Dirty, 1 = written by processor on access to page
112 UINT64 MustBe1
: 1; // Must be 1
113 UINT64 Global
: 1; // 0 = Not global page, 1 = global page TLB not cleared on CR3 write
114 UINT64 Available
: 3; // Available for use by system software
116 UINT64 MustBeZero
: 8; // Must be zero;
117 UINT64 PageTableBaseAddress
: 31; // Page Table Base Address
118 UINT64 AvabilableHigh
: 11; // Available for use by system software
119 UINT64 Nx
: 1; // 0 = Execute Code, 1 = No Code Execution
125 // Page Table Entry 1GB
129 UINT64 Present
: 1; // 0 = Not present in memory, 1 = Present in memory
130 UINT64 ReadWrite
: 1; // 0 = Read-Only, 1= Read/Write
131 UINT64 UserSupervisor
: 1; // 0 = Supervisor, 1=User
132 UINT64 WriteThrough
: 1; // 0 = Write-Back caching, 1=Write-Through caching
133 UINT64 CacheDisabled
: 1; // 0 = Cached, 1=Non-Cached
134 UINT64 Accessed
: 1; // 0 = Not accessed, 1 = Accessed (set by CPU)
135 UINT64 Dirty
: 1; // 0 = Not Dirty, 1 = written by processor on access to page
136 UINT64 MustBe1
: 1; // Must be 1
137 UINT64 Global
: 1; // 0 = Not global page, 1 = global page TLB not cleared on CR3 write
138 UINT64 Available
: 3; // Available for use by system software
140 UINT64 MustBeZero
: 17; // Must be zero;
141 UINT64 PageTableBaseAddress
: 22; // Page Table Base Address
142 UINT64 AvabilableHigh
: 11; // Available for use by system software
143 UINT64 Nx
: 1; // 0 = Execute Code, 1 = No Code Execution
146 } PAGE_TABLE_1G_ENTRY
;
149 // Define two type of smm communicate headers.
150 // One for 32 bits PEI + 64 bits DXE, the other for 32 bits PEI + 32 bits DXE case.
154 UINT32 MessageLength
;
156 } SMM_COMMUNICATE_HEADER_32
;
160 UINT64 MessageLength
;
162 } SMM_COMMUNICATE_HEADER_64
;
167 // Function prototypes
171 a ASM function to transfer control to OS.
173 @param S3WakingVector The S3 waking up vector saved in ACPI Facs table
174 @param AcpiLowMemoryBase a buffer under 1M which could be used during the transfer
178 (EFIAPI
*ASM_TRANSFER_CONTROL
)(
179 IN UINT32 S3WakingVector
,
180 IN UINT32 AcpiLowMemoryBase
184 Restores the platform to its preboot configuration for an S3 resume and
185 jumps to the OS waking vector.
187 This function will restore the platform to its pre-boot configuration that was
188 pre-stored in the boot script table and transfer control to OS waking vector.
189 Upon invocation, this function is responsible for locating the following
190 information before jumping to OS waking vector:
193 - any other information that it needs
195 The S3RestoreConfig() function then executes the pre-stored boot script table
196 and transitions the platform to the pre-boot state. The boot script is recorded
197 during regular boot using the EFI_S3_SAVE_STATE_PROTOCOL.Write() and
198 EFI_S3_SMM_SAVE_STATE_PROTOCOL.Write() functions. Finally, this function
199 transfers control to the OS waking vector. If the OS supports only a real-mode
200 waking vector, this function will switch from flat mode to real mode before
201 jumping to the waking vector. If all platform pre-boot configurations are
202 successfully restored and all other necessary information is ready, this
203 function will never return and instead will directly jump to the OS waking
204 vector. If this function returns, it indicates that the attempt to resume
205 from the ACPI S3 sleep state failed.
207 @param[in] This Pointer to this instance of the PEI_S3_RESUME_PPI
209 @retval EFI_ABORTED Execution of the S3 resume boot script table failed.
210 @retval EFI_NOT_FOUND Some necessary information that is used for the S3
211 resume boot path could not be located.
217 IN EFI_PEI_S3_RESUME2_PPI
*This
221 Set data segment selectors value including DS/ES/FS/GS/SS.
223 @param[in] SelectorValue Segment selector value to be set.
228 AsmSetDataSelectors (
229 IN UINT16 SelectorValue
235 EFI_PEI_S3_RESUME2_PPI mS3ResumePpi
= { S3RestoreConfig2
};
237 EFI_PEI_PPI_DESCRIPTOR mPpiList
= {
238 (EFI_PEI_PPI_DESCRIPTOR_PPI
| EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST
),
239 &gEfiPeiS3Resume2PpiGuid
,
243 EFI_PEI_PPI_DESCRIPTOR mPpiListPostScriptTable
= {
244 (EFI_PEI_PPI_DESCRIPTOR_PPI
| EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST
),
245 &gPeiPostScriptTablePpiGuid
,
249 EFI_PEI_PPI_DESCRIPTOR mPpiListEndOfPeiTable
= {
250 (EFI_PEI_PPI_DESCRIPTOR_PPI
| EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST
),
251 &gEfiEndOfPeiSignalPpiGuid
,
255 EFI_PEI_PPI_DESCRIPTOR mPpiListS3SmmInitDoneTable
= {
256 (EFI_PEI_PPI_DESCRIPTOR_PPI
| EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST
),
257 &gEdkiiS3SmmInitDoneGuid
,
262 // Global Descriptor Table (GDT)
264 GLOBAL_REMOVE_IF_UNREFERENCED IA32_GDT mGdtEntries
[] = {
265 /* selector { Global Segment Descriptor } */
267 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
270 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
273 { 0xFFFF, 0, 0, 0xB, 1, 0, 1, 0xF, 0, 0, 1, 1, 0 }
276 { 0xFFFF, 0, 0, 0x3, 1, 0, 1, 0xF, 0, 0, 1, 1, 0 }
279 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
282 { 0xFFFF, 0, 0, 0xB, 1, 0, 1, 0xF, 0, 0, 0, 1, 0 }
285 { 0xFFFF, 0, 0, 0x3, 1, 0, 1, 0xF, 0, 0, 0, 1, 0 }
288 { 0xFFFF, 0, 0, 0xB, 1, 0, 1, 0xF, 0, 1, 0, 1, 0 }
291 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
295 #define DATA_SEGEMENT_SELECTOR 0x18
300 GLOBAL_REMOVE_IF_UNREFERENCED CONST IA32_DESCRIPTOR mGdt
= {
301 sizeof (mGdtEntries
) - 1,
306 The function will check if current waking vector is long mode.
308 @param AcpiS3Context a pointer to a structure of ACPI_S3_CONTEXT
310 @retval TRUE Current context need long mode waking vector.
311 @retval FALSE Current context need not long mode waking vector.
314 IsLongModeWakingVector (
315 IN ACPI_S3_CONTEXT
*AcpiS3Context
318 EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*Facs
;
320 Facs
= (EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*)((UINTN
)(AcpiS3Context
->AcpiFacsTable
));
321 if ((Facs
== NULL
) ||
322 (Facs
->Signature
!= EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE
) ||
323 ((Facs
->FirmwareWakingVector
== 0) && (Facs
->XFirmwareWakingVector
== 0)))
325 // Something wrong with FACS
329 if (Facs
->XFirmwareWakingVector
!= 0) {
330 if ((Facs
->Version
== EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_VERSION
) &&
331 ((Facs
->Flags
& EFI_ACPI_4_0_64BIT_WAKE_SUPPORTED_F
) != 0) &&
332 ((Facs
->OspmFlags
& EFI_ACPI_4_0_OSPM_64BIT_WAKE__F
) != 0))
334 // Both BIOS and OS wants 64bit vector
335 ASSERT ((FeaturePcdGet (PcdDxeIplSwitchToLongMode
)) || (sizeof (UINTN
) == sizeof (UINT64
)));
344 Signal to SMM through communication buffer way.
346 @param[in] HandlerType SMI handler type to be signaled.
350 SignalToSmmByCommunication (
351 IN EFI_GUID
*HandlerType
355 EFI_PEI_SMM_COMMUNICATION_PPI
*SmmCommunicationPpi
;
357 SMM_COMMUNICATE_HEADER_32 Header32
;
358 SMM_COMMUNICATE_HEADER_64 Header64
;
361 DEBUG ((DEBUG_INFO
, "Signal %g to SMM - Enter\n", HandlerType
));
364 // This buffer consumed in DXE phase, so base on DXE mode to prepare communicate buffer.
365 // Detect whether DXE is 64 bits mode.
366 // if (sizeof(UINTN) == sizeof(UINT64), PEI already 64 bits, assume DXE also 64 bits.
367 // or (FeaturePcdGet (PcdDxeIplSwitchToLongMode)), DXE will switch to 64 bits.
369 if ((sizeof (UINTN
) == sizeof (UINT64
)) || (FeaturePcdGet (PcdDxeIplSwitchToLongMode
))) {
370 CommBuffer
= &Header64
;
371 Header64
.MessageLength
= 0;
372 CommSize
= OFFSET_OF (SMM_COMMUNICATE_HEADER_64
, Data
);
374 CommBuffer
= &Header32
;
375 Header32
.MessageLength
= 0;
376 CommSize
= OFFSET_OF (SMM_COMMUNICATE_HEADER_32
, Data
);
379 CopyGuid (CommBuffer
, HandlerType
);
381 Status
= PeiServicesLocatePpi (
382 &gEfiPeiSmmCommunicationPpiGuid
,
385 (VOID
**)&SmmCommunicationPpi
387 if (EFI_ERROR (Status
)) {
388 DEBUG ((DEBUG_ERROR
, "Locate Smm Communicate Ppi failed (%r)!\n", Status
));
392 Status
= SmmCommunicationPpi
->Communicate (
397 if (EFI_ERROR (Status
)) {
398 DEBUG ((DEBUG_ERROR
, "SmmCommunicationPpi->Communicate return failure (%r)!\n", Status
));
401 DEBUG ((DEBUG_INFO
, "Signal %g to SMM - Exit (%r)\n", HandlerType
, Status
));
406 Jump to OS waking vector.
407 The function will install boot script done PPI, report S3 resume status code, and then jump to OS waking vector.
409 @param AcpiS3Context a pointer to a structure of ACPI_S3_CONTEXT
410 @param PeiS3ResumeState a pointer to a structure of PEI_S3_RESUME_STATE
415 IN ACPI_S3_CONTEXT
*AcpiS3Context
,
416 IN PEI_S3_RESUME_STATE
*PeiS3ResumeState
420 EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*Facs
;
421 ASM_TRANSFER_CONTROL AsmTransferControl
;
423 UINTN TempStack
[0x10];
428 AsmWriteIdtr (&PeiS3ResumeState
->Idtr
);
430 if (PeiS3ResumeState
->ReturnStatus
!= EFI_SUCCESS
) {
432 // Report Status code that boot script execution is failed
435 EFI_ERROR_CODE
| EFI_ERROR_MINOR
,
436 (EFI_SOFTWARE_PEI_MODULE
| EFI_SW_PEI_EC_S3_BOOT_SCRIPT_ERROR
)
441 // NOTE: Because Debug Timer interrupt and system interrupts will be disabled
442 // in BootScriptExecuteDxe, the rest code in S3ResumeBootOs() cannot be halted
446 PERF_INMODULE_END ("ScriptExec");
449 // Install BootScriptDonePpi
451 PERF_INMODULE_BEGIN ("BootScriptDonePpi");
453 Status
= PeiServicesInstallPpi (&mPpiListPostScriptTable
);
454 ASSERT_EFI_ERROR (Status
);
456 PERF_INMODULE_END ("BootScriptDonePpi");
459 // Get ACPI Table Address
461 Facs
= (EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*)((UINTN
)(AcpiS3Context
->AcpiFacsTable
));
463 if ((Facs
== NULL
) ||
464 (Facs
->Signature
!= EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE
) ||
465 ((Facs
->FirmwareWakingVector
== 0) && (Facs
->XFirmwareWakingVector
== 0)))
468 // Report Status code that no valid vector is found
471 EFI_ERROR_CODE
| EFI_ERROR_MAJOR
,
472 (EFI_SOFTWARE_PEI_MODULE
| EFI_SW_PEI_EC_S3_OS_WAKE_ERROR
)
479 // Install EndOfPeiPpi
481 PERF_INMODULE_BEGIN ("EndOfPeiPpi");
483 Status
= PeiServicesInstallPpi (&mPpiListEndOfPeiTable
);
484 ASSERT_EFI_ERROR (Status
);
486 PERF_INMODULE_END ("EndOfPeiPpi");
488 PERF_INMODULE_BEGIN ("EndOfS3Resume");
490 DEBUG ((DEBUG_INFO
, "Signal EndOfS3Resume\n"));
492 // Signal EndOfS3Resume to SMM.
494 SignalToSmmByCommunication (&gEdkiiEndOfS3ResumeGuid
);
496 PERF_INMODULE_END ("EndOfS3Resume");
499 // report status code on S3 resume
501 REPORT_STATUS_CODE (EFI_PROGRESS_CODE
, EFI_SOFTWARE_PEI_MODULE
| EFI_SW_PEI_PC_OS_WAKE
);
503 AsmTransferControl
= (ASM_TRANSFER_CONTROL
)(UINTN
)PeiS3ResumeState
->AsmTransferControl
;
504 if (Facs
->XFirmwareWakingVector
!= 0) {
506 // Switch to native waking vector
508 TempStackTop
= (UINTN
)&TempStack
+ sizeof (TempStack
);
511 "%a() Stack Base: 0x%x, Stack Size: 0x%x\n",
516 if ((Facs
->Version
== EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_VERSION
) &&
517 ((Facs
->Flags
& EFI_ACPI_4_0_64BIT_WAKE_SUPPORTED_F
) != 0) &&
518 ((Facs
->OspmFlags
& EFI_ACPI_4_0_OSPM_64BIT_WAKE__F
) != 0))
521 // X64 long mode waking vector
523 DEBUG ((DEBUG_INFO
, "Transfer from PEI to 64bit OS waking vector - %x\r\n", (UINTN
)Facs
->XFirmwareWakingVector
));
524 if (FeaturePcdGet (PcdDxeIplSwitchToLongMode
)) {
526 // 32bit PEI calls to 64bit OS S3 waking vector
530 Facs
->XFirmwareWakingVector
,
533 (UINT64
)(UINTN
)TempStackTop
536 if (sizeof (UINTN
) == sizeof (UINT64
)) {
538 // 64bit PEI calls to 64bit OS S3 waking vector
541 (SWITCH_STACK_ENTRY_POINT
)(UINTN
)Facs
->XFirmwareWakingVector
,
544 (VOID
*)(UINTN
)TempStackTop
548 // Report Status code that no valid waking vector is found.
549 // Note: 32bit PEI + 32bit DXE firmware calling to 64bit OS S3 waking vector is an invalid configuration.
552 EFI_ERROR_CODE
| EFI_ERROR_MAJOR
,
553 (EFI_SOFTWARE_PEI_MODULE
| EFI_SW_PEI_EC_S3_OS_WAKE_ERROR
)
555 DEBUG ((DEBUG_ERROR
, "Unsupported for 32bit DXE transfer to 64bit OS waking vector!\r\n"));
563 // IA32 protected mode waking vector (Page disabled)
565 DEBUG ((DEBUG_INFO
, "Transfer to 32bit OS waking vector - %x\r\n", (UINTN
)Facs
->XFirmwareWakingVector
));
566 if (sizeof (UINTN
) == sizeof (UINT64
)) {
568 // 64bit PEI calls to 32bit OS S3 waking vector
572 (UINT32
)Facs
->XFirmwareWakingVector
,
579 // 32bit PEI calls to 32bit OS S3 waking vector
582 (SWITCH_STACK_ENTRY_POINT
)(UINTN
)Facs
->XFirmwareWakingVector
,
585 (VOID
*)(UINTN
)TempStackTop
591 // 16bit Realmode waking vector
593 DEBUG ((DEBUG_INFO
, "Transfer to 16bit OS waking vector - %x\r\n", (UINTN
)Facs
->FirmwareWakingVector
));
594 AsmTransferControl (Facs
->FirmwareWakingVector
, 0x0);
598 // Report Status code the failure of S3Resume
601 EFI_ERROR_CODE
| EFI_ERROR_MAJOR
,
602 (EFI_SOFTWARE_PEI_MODULE
| EFI_SW_PEI_EC_S3_OS_WAKE_ERROR
)
612 Restore S3 page table because we do not trust ACPINvs content.
613 If BootScriptExecutor driver will not run in 64-bit mode, this function will do nothing.
615 @param S3NvsPageTableAddress PageTableAddress in ACPINvs
616 @param Build4GPageTableOnly If BIOS just build 4G page table only
619 RestoreS3PageTables (
620 IN UINTN S3NvsPageTableAddress
,
621 IN BOOLEAN Build4GPageTableOnly
624 if ((FeaturePcdGet (PcdDxeIplSwitchToLongMode
)) || (sizeof (UINTN
) == sizeof (UINT64
))) {
627 UINT8 PhysicalAddressBits
;
628 EFI_PHYSICAL_ADDRESS PageAddress
;
629 UINTN IndexOfPml4Entries
;
630 UINTN IndexOfPdpEntries
;
631 UINTN IndexOfPageDirectoryEntries
;
632 UINT32 NumberOfPml4EntriesNeeded
;
633 UINT32 NumberOfPdpEntriesNeeded
;
634 PAGE_MAP_AND_DIRECTORY_POINTER
*PageMapLevel4Entry
;
635 PAGE_MAP_AND_DIRECTORY_POINTER
*PageMap
;
636 PAGE_MAP_AND_DIRECTORY_POINTER
*PageDirectoryPointerEntry
;
637 PAGE_TABLE_ENTRY
*PageDirectoryEntry
;
639 BOOLEAN Page1GSupport
;
640 PAGE_TABLE_1G_ENTRY
*PageDirectory1GEntry
;
641 UINT64 AddressEncMask
;
644 // Make sure AddressEncMask is contained to smallest supported address field
646 AddressEncMask
= PcdGet64 (PcdPteMemoryEncryptionAddressOrMask
) & PAGING_1G_ADDRESS_MASK_64
;
649 // NOTE: We have to ASSUME the page table generation format, because we do not know whole page table information.
650 // The whole page table is too large to be saved in SMRAM.
652 // The assumption is : whole page table is allocated in CONTINUOUS memory and CR3 points to TOP page.
654 DEBUG ((DEBUG_INFO
, "S3NvsPageTableAddress - %x (%x)\n", (UINTN
)S3NvsPageTableAddress
, (UINTN
)Build4GPageTableOnly
));
657 // By architecture only one PageMapLevel4 exists - so lets allocate storage for it.
659 PageMap
= (PAGE_MAP_AND_DIRECTORY_POINTER
*)S3NvsPageTableAddress
;
660 S3NvsPageTableAddress
+= SIZE_4KB
;
662 Page1GSupport
= FALSE
;
663 if (PcdGetBool (PcdUse1GPageTable
)) {
664 AsmCpuid (0x80000000, &RegEax
, NULL
, NULL
, NULL
);
665 if (RegEax
>= 0x80000001) {
666 AsmCpuid (0x80000001, NULL
, NULL
, NULL
, &RegEdx
);
667 if ((RegEdx
& BIT26
) != 0) {
668 Page1GSupport
= TRUE
;
674 // Get physical address bits supported.
676 Hob
= GetFirstHob (EFI_HOB_TYPE_CPU
);
678 PhysicalAddressBits
= ((EFI_HOB_CPU
*)Hob
)->SizeOfMemorySpace
;
680 AsmCpuid (0x80000000, &RegEax
, NULL
, NULL
, NULL
);
681 if (RegEax
>= 0x80000008) {
682 AsmCpuid (0x80000008, &RegEax
, NULL
, NULL
, NULL
);
683 PhysicalAddressBits
= (UINT8
)RegEax
;
685 PhysicalAddressBits
= 36;
690 // IA-32e paging translates 48-bit linear addresses to 52-bit physical addresses.
692 ASSERT (PhysicalAddressBits
<= 52);
693 if (PhysicalAddressBits
> 48) {
694 PhysicalAddressBits
= 48;
698 // NOTE: In order to save time to create full page table, we just create 4G page table by default.
699 // And let PF handler in BootScript driver to create more on request.
701 if (Build4GPageTableOnly
) {
702 PhysicalAddressBits
= 32;
703 ZeroMem (PageMap
, EFI_PAGES_TO_SIZE (2));
707 // Calculate the table entries needed.
709 if (PhysicalAddressBits
<= 39) {
710 NumberOfPml4EntriesNeeded
= 1;
711 NumberOfPdpEntriesNeeded
= (UINT32
)LShiftU64 (1, (PhysicalAddressBits
- 30));
713 NumberOfPml4EntriesNeeded
= (UINT32
)LShiftU64 (1, (PhysicalAddressBits
- 39));
714 NumberOfPdpEntriesNeeded
= 512;
717 PageMapLevel4Entry
= PageMap
;
719 for (IndexOfPml4Entries
= 0; IndexOfPml4Entries
< NumberOfPml4EntriesNeeded
; IndexOfPml4Entries
++, PageMapLevel4Entry
++) {
721 // Each PML4 entry points to a page of Page Directory Pointer entires.
722 // So lets allocate space for them and fill them in in the IndexOfPdpEntries loop.
724 PageDirectoryPointerEntry
= (PAGE_MAP_AND_DIRECTORY_POINTER
*)S3NvsPageTableAddress
;
725 S3NvsPageTableAddress
+= SIZE_4KB
;
730 PageMapLevel4Entry
->Uint64
= (UINT64
)(UINTN
)PageDirectoryPointerEntry
| AddressEncMask
;
731 PageMapLevel4Entry
->Bits
.ReadWrite
= 1;
732 PageMapLevel4Entry
->Bits
.Present
= 1;
735 PageDirectory1GEntry
= (VOID
*)PageDirectoryPointerEntry
;
737 for (IndexOfPageDirectoryEntries
= 0; IndexOfPageDirectoryEntries
< 512; IndexOfPageDirectoryEntries
++, PageDirectory1GEntry
++, PageAddress
+= SIZE_1GB
) {
739 // Fill in the Page Directory entries
741 PageDirectory1GEntry
->Uint64
= (UINT64
)PageAddress
| AddressEncMask
;
742 PageDirectory1GEntry
->Bits
.ReadWrite
= 1;
743 PageDirectory1GEntry
->Bits
.Present
= 1;
744 PageDirectory1GEntry
->Bits
.MustBe1
= 1;
747 for (IndexOfPdpEntries
= 0; IndexOfPdpEntries
< NumberOfPdpEntriesNeeded
; IndexOfPdpEntries
++, PageDirectoryPointerEntry
++) {
749 // Each Directory Pointer entries points to a page of Page Directory entires.
750 // So allocate space for them and fill them in in the IndexOfPageDirectoryEntries loop.
752 PageDirectoryEntry
= (PAGE_TABLE_ENTRY
*)S3NvsPageTableAddress
;
753 S3NvsPageTableAddress
+= SIZE_4KB
;
756 // Fill in a Page Directory Pointer Entries
758 PageDirectoryPointerEntry
->Uint64
= (UINT64
)(UINTN
)PageDirectoryEntry
| AddressEncMask
;
759 PageDirectoryPointerEntry
->Bits
.ReadWrite
= 1;
760 PageDirectoryPointerEntry
->Bits
.Present
= 1;
762 for (IndexOfPageDirectoryEntries
= 0; IndexOfPageDirectoryEntries
< 512; IndexOfPageDirectoryEntries
++, PageDirectoryEntry
++, PageAddress
+= SIZE_2MB
) {
764 // Fill in the Page Directory entries
766 PageDirectoryEntry
->Uint64
= (UINT64
)PageAddress
| AddressEncMask
;
767 PageDirectoryEntry
->Bits
.ReadWrite
= 1;
768 PageDirectoryEntry
->Bits
.Present
= 1;
769 PageDirectoryEntry
->Bits
.MustBe1
= 1;
778 // If DXE is running 32-bit mode, no need to establish page table.
785 Jump to boot script executor driver.
787 The function will close and lock SMRAM and then jump to boot script execute driver to executing S3 boot script table.
789 @param AcpiS3Context a pointer to a structure of ACPI_S3_CONTEXT
790 @param EfiBootScriptExecutorVariable The function entry to executing S3 boot Script table. This function is build in
791 boot script execute driver
795 S3ResumeExecuteBootScript (
796 IN ACPI_S3_CONTEXT
*AcpiS3Context
,
797 IN BOOT_SCRIPT_EXECUTOR_VARIABLE
*EfiBootScriptExecutorVariable
801 PEI_SMM_ACCESS_PPI
*SmmAccess
;
804 PEI_S3_RESUME_STATE
*PeiS3ResumeState
;
805 BOOLEAN InterruptStatus
;
807 DEBUG ((DEBUG_INFO
, "S3ResumeExecuteBootScript()\n"));
810 // Attempt to use content from SMRAM first
812 GuidHob
= GetFirstGuidHob (&gEfiAcpiVariableGuid
);
813 if (GuidHob
!= NULL
) {
815 // Last step for SMM - send SMI for initialization
821 SendSmiIpiAllExcludingSelf ();
825 SendSmiIpi (GetApicId ());
827 Status
= PeiServicesLocatePpi (
828 &gPeiSmmAccessPpiGuid
,
833 if (!EFI_ERROR (Status
)) {
834 DEBUG ((DEBUG_INFO
, "Close all SMRAM regions before executing boot script\n"));
836 for (Index
= 0, Status
= EFI_SUCCESS
; !EFI_ERROR (Status
); Index
++) {
837 Status
= SmmAccess
->Close ((EFI_PEI_SERVICES
**)GetPeiServicesTablePointer (), SmmAccess
, Index
);
840 DEBUG ((DEBUG_INFO
, "Lock all SMRAM regions before executing boot script\n"));
842 for (Index
= 0, Status
= EFI_SUCCESS
; !EFI_ERROR (Status
); Index
++) {
843 Status
= SmmAccess
->Lock ((EFI_PEI_SERVICES
**)GetPeiServicesTablePointer (), SmmAccess
, Index
);
847 DEBUG ((DEBUG_INFO
, "Signal S3SmmInitDone\n"));
849 // Install S3SmmInitDone PPI.
851 Status
= PeiServicesInstallPpi (&mPpiListS3SmmInitDoneTable
);
852 ASSERT_EFI_ERROR (Status
);
854 // Signal S3SmmInitDone to SMM.
856 SignalToSmmByCommunication (&gEdkiiS3SmmInitDoneGuid
);
859 if ((FeaturePcdGet (PcdDxeIplSwitchToLongMode
)) || (sizeof (UINTN
) == sizeof (UINT64
))) {
860 AsmWriteCr3 ((UINTN
)AcpiS3Context
->S3NvsPageTableAddress
);
863 InterruptStatus
= SaveAndDisableInterrupts ();
865 // Need to make sure the GDT is loaded with values that support long mode and real mode.
867 AsmWriteGdtr (&mGdt
);
869 // update segment selectors per the new GDT.
871 AsmSetDataSelectors (DATA_SEGEMENT_SELECTOR
);
873 // Restore interrupt state.
875 SetInterruptState (InterruptStatus
);
878 // Prepare data for return back
880 PeiS3ResumeState
= AllocatePool (sizeof (*PeiS3ResumeState
));
881 if (PeiS3ResumeState
== NULL
) {
883 EFI_ERROR_CODE
| EFI_ERROR_MAJOR
,
884 (EFI_SOFTWARE_PEI_MODULE
| EFI_SW_PEI_EC_S3_RESUME_FAILED
)
889 DEBUG ((DEBUG_INFO
, "PeiS3ResumeState - %x\r\n", PeiS3ResumeState
));
890 PeiS3ResumeState
->ReturnCs
= 0x10;
891 PeiS3ResumeState
->ReturnEntryPoint
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)S3ResumeBootOs
;
892 PeiS3ResumeState
->ReturnStackPointer
= (EFI_PHYSICAL_ADDRESS
)STACK_ALIGN_DOWN (&Status
);
896 AsmReadIdtr (&PeiS3ResumeState
->Idtr
);
899 // Report Status Code to indicate S3 boot script execution
901 REPORT_STATUS_CODE (EFI_PROGRESS_CODE
, EFI_SOFTWARE_PEI_MODULE
| EFI_SW_PEI_PC_S3_BOOT_SCRIPT
);
903 PERF_INMODULE_BEGIN ("ScriptExec");
905 if (FeaturePcdGet (PcdDxeIplSwitchToLongMode
)) {
909 DEBUG ((DEBUG_INFO
, "Enable X64 and transfer control to Standalone Boot Script Executor\r\n"));
912 // Switch to long mode to complete resume.
916 EfiBootScriptExecutorVariable
->BootScriptExecutorEntrypoint
,
917 (UINT64
)(UINTN
)AcpiS3Context
,
918 (UINT64
)(UINTN
)PeiS3ResumeState
,
919 (UINT64
)(UINTN
)(AcpiS3Context
->BootScriptStackBase
+ AcpiS3Context
->BootScriptStackSize
)
925 DEBUG ((DEBUG_INFO
, "transfer control to Standalone Boot Script Executor\r\n"));
927 (SWITCH_STACK_ENTRY_POINT
)(UINTN
)EfiBootScriptExecutorVariable
->BootScriptExecutorEntrypoint
,
928 (VOID
*)AcpiS3Context
,
929 (VOID
*)PeiS3ResumeState
,
930 (VOID
*)(UINTN
)(AcpiS3Context
->BootScriptStackBase
+ AcpiS3Context
->BootScriptStackSize
)
941 Restores the platform to its preboot configuration for an S3 resume and
942 jumps to the OS waking vector.
944 This function will restore the platform to its pre-boot configuration that was
945 pre-stored in the boot script table and transfer control to OS waking vector.
946 Upon invocation, this function is responsible for locating the following
947 information before jumping to OS waking vector:
950 - any other information that it needs
952 The S3RestoreConfig() function then executes the pre-stored boot script table
953 and transitions the platform to the pre-boot state. The boot script is recorded
954 during regular boot using the EFI_S3_SAVE_STATE_PROTOCOL.Write() and
955 EFI_S3_SMM_SAVE_STATE_PROTOCOL.Write() functions. Finally, this function
956 transfers control to the OS waking vector. If the OS supports only a real-mode
957 waking vector, this function will switch from flat mode to real mode before
958 jumping to the waking vector. If all platform pre-boot configurations are
959 successfully restored and all other necessary information is ready, this
960 function will never return and instead will directly jump to the OS waking
961 vector. If this function returns, it indicates that the attempt to resume
962 from the ACPI S3 sleep state failed.
964 @param[in] This Pointer to this instance of the PEI_S3_RESUME_PPI
966 @retval EFI_ABORTED Execution of the S3 resume boot script table failed.
967 @retval EFI_NOT_FOUND Some necessary information that is used for the S3
968 resume boot path could not be located.
974 IN EFI_PEI_S3_RESUME2_PPI
*This
978 PEI_SMM_ACCESS_PPI
*SmmAccess
;
980 ACPI_S3_CONTEXT
*AcpiS3Context
;
981 EFI_PHYSICAL_ADDRESS TempEfiBootScriptExecutorVariable
;
982 EFI_PHYSICAL_ADDRESS TempAcpiS3Context
;
983 BOOT_SCRIPT_EXECUTOR_VARIABLE
*EfiBootScriptExecutorVariable
;
985 EFI_SMRAM_DESCRIPTOR
*SmramDescriptor
;
986 SMM_S3_RESUME_STATE
*SmmS3ResumeState
;
988 BOOLEAN Build4GPageTableOnly
;
989 BOOLEAN InterruptStatus
;
992 TempAcpiS3Context
= 0;
993 TempEfiBootScriptExecutorVariable
= 0;
995 DEBUG ((DEBUG_INFO
, "Enter S3 PEIM\r\n"));
997 VarSize
= sizeof (EFI_PHYSICAL_ADDRESS
);
998 Status
= RestoreLockBox (
999 &gEfiAcpiVariableGuid
,
1003 ASSERT_EFI_ERROR (Status
);
1005 Status
= RestoreLockBox (
1006 &gEfiAcpiS3ContextGuid
,
1010 ASSERT_EFI_ERROR (Status
);
1012 AcpiS3Context
= (ACPI_S3_CONTEXT
*)(UINTN
)TempAcpiS3Context
;
1013 ASSERT (AcpiS3Context
!= NULL
);
1015 VarSize
= sizeof (EFI_PHYSICAL_ADDRESS
);
1016 Status
= RestoreLockBox (
1017 &gEfiBootScriptExecutorVariableGuid
,
1018 &TempEfiBootScriptExecutorVariable
,
1021 ASSERT_EFI_ERROR (Status
);
1023 Status
= RestoreLockBox (
1024 &gEfiBootScriptExecutorContextGuid
,
1028 ASSERT_EFI_ERROR (Status
);
1030 EfiBootScriptExecutorVariable
= (BOOT_SCRIPT_EXECUTOR_VARIABLE
*)(UINTN
)TempEfiBootScriptExecutorVariable
;
1031 ASSERT (EfiBootScriptExecutorVariable
!= NULL
);
1033 DEBUG ((DEBUG_INFO
, "AcpiS3Context = %x\n", AcpiS3Context
));
1034 DEBUG ((DEBUG_INFO
, "Waking Vector = %x\n", ((EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*)((UINTN
)(AcpiS3Context
->AcpiFacsTable
)))->FirmwareWakingVector
));
1035 DEBUG ((DEBUG_INFO
, "AcpiS3Context->AcpiFacsTable = %x\n", AcpiS3Context
->AcpiFacsTable
));
1036 DEBUG ((DEBUG_INFO
, "AcpiS3Context->IdtrProfile = %x\n", AcpiS3Context
->IdtrProfile
));
1037 DEBUG ((DEBUG_INFO
, "AcpiS3Context->S3NvsPageTableAddress = %x\n", AcpiS3Context
->S3NvsPageTableAddress
));
1038 DEBUG ((DEBUG_INFO
, "AcpiS3Context->S3DebugBufferAddress = %x\n", AcpiS3Context
->S3DebugBufferAddress
));
1039 DEBUG ((DEBUG_INFO
, "AcpiS3Context->BootScriptStackBase = %x\n", AcpiS3Context
->BootScriptStackBase
));
1040 DEBUG ((DEBUG_INFO
, "AcpiS3Context->BootScriptStackSize = %x\n", AcpiS3Context
->BootScriptStackSize
));
1041 DEBUG ((DEBUG_INFO
, "EfiBootScriptExecutorVariable->BootScriptExecutorEntrypoint = %x\n", EfiBootScriptExecutorVariable
->BootScriptExecutorEntrypoint
));
1044 // Additional step for BootScript integrity - we only handle BootScript and BootScriptExecutor.
1045 // Script dispatch image and context (parameter) are handled by platform.
1046 // We just use restore all lock box in place, no need restore one by one.
1048 Status
= RestoreAllLockBoxInPlace ();
1049 ASSERT_EFI_ERROR (Status
);
1050 if (EFI_ERROR (Status
)) {
1055 if ((FeaturePcdGet (PcdDxeIplSwitchToLongMode
)) || (sizeof (UINTN
) == sizeof (UINT64
))) {
1057 // Need reconstruct page table here, since we do not trust ACPINvs.
1059 if (IsLongModeWakingVector (AcpiS3Context
)) {
1060 Build4GPageTableOnly
= FALSE
;
1062 Build4GPageTableOnly
= TRUE
;
1065 RestoreS3PageTables ((UINTN
)AcpiS3Context
->S3NvsPageTableAddress
, Build4GPageTableOnly
);
1069 // Attempt to use content from SMRAM first
1071 GuidHob
= GetFirstGuidHob (&gEfiAcpiVariableGuid
);
1072 if (GuidHob
!= NULL
) {
1073 Status
= PeiServicesLocatePpi (
1074 &gPeiSmmAccessPpiGuid
,
1079 for (Index
= 0; !EFI_ERROR (Status
); Index
++) {
1080 Status
= SmmAccess
->Open ((EFI_PEI_SERVICES
**)GetPeiServicesTablePointer (), SmmAccess
, Index
);
1083 SmramDescriptor
= (EFI_SMRAM_DESCRIPTOR
*)GET_GUID_HOB_DATA (GuidHob
);
1084 SmmS3ResumeState
= (SMM_S3_RESUME_STATE
*)(UINTN
)SmramDescriptor
->CpuStart
;
1086 SmmS3ResumeState
->ReturnCs
= AsmReadCs ();
1087 SmmS3ResumeState
->ReturnEntryPoint
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)S3ResumeExecuteBootScript
;
1088 SmmS3ResumeState
->ReturnContext1
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)AcpiS3Context
;
1089 SmmS3ResumeState
->ReturnContext2
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)EfiBootScriptExecutorVariable
;
1090 SmmS3ResumeState
->ReturnStackPointer
= (EFI_PHYSICAL_ADDRESS
)STACK_ALIGN_DOWN (&Status
);
1092 DEBUG ((DEBUG_INFO
, "SMM S3 Signature = %x\n", SmmS3ResumeState
->Signature
));
1093 DEBUG ((DEBUG_INFO
, "SMM S3 Stack Base = %x\n", SmmS3ResumeState
->SmmS3StackBase
));
1094 DEBUG ((DEBUG_INFO
, "SMM S3 Stack Size = %x\n", SmmS3ResumeState
->SmmS3StackSize
));
1095 DEBUG ((DEBUG_INFO
, "SMM S3 Resume Entry Point = %x\n", SmmS3ResumeState
->SmmS3ResumeEntryPoint
));
1096 DEBUG ((DEBUG_INFO
, "SMM S3 CR0 = %x\n", SmmS3ResumeState
->SmmS3Cr0
));
1097 DEBUG ((DEBUG_INFO
, "SMM S3 CR3 = %x\n", SmmS3ResumeState
->SmmS3Cr3
));
1098 DEBUG ((DEBUG_INFO
, "SMM S3 CR4 = %x\n", SmmS3ResumeState
->SmmS3Cr4
));
1099 DEBUG ((DEBUG_INFO
, "SMM S3 Return CS = %x\n", SmmS3ResumeState
->ReturnCs
));
1100 DEBUG ((DEBUG_INFO
, "SMM S3 Return Entry Point = %x\n", SmmS3ResumeState
->ReturnEntryPoint
));
1101 DEBUG ((DEBUG_INFO
, "SMM S3 Return Context1 = %x\n", SmmS3ResumeState
->ReturnContext1
));
1102 DEBUG ((DEBUG_INFO
, "SMM S3 Return Context2 = %x\n", SmmS3ResumeState
->ReturnContext2
));
1103 DEBUG ((DEBUG_INFO
, "SMM S3 Return Stack Pointer = %x\n", SmmS3ResumeState
->ReturnStackPointer
));
1104 DEBUG ((DEBUG_INFO
, "SMM S3 Smst = %x\n", SmmS3ResumeState
->Smst
));
1107 // Directly do the switch stack when PEI and SMM env run in the same execution mode.
1109 if (((SmmS3ResumeState
->Signature
== SMM_S3_RESUME_SMM_32
) && (sizeof (UINTN
) == sizeof (UINT32
))) ||
1110 ((SmmS3ResumeState
->Signature
== SMM_S3_RESUME_SMM_64
) && (sizeof (UINTN
) == sizeof (UINT64
))))
1113 (SWITCH_STACK_ENTRY_POINT
)(UINTN
)SmmS3ResumeState
->SmmS3ResumeEntryPoint
,
1114 (VOID
*)AcpiS3Context
,
1116 (VOID
*)(UINTN
)(SmmS3ResumeState
->SmmS3StackBase
+ SmmS3ResumeState
->SmmS3StackSize
)
1120 if (SmmS3ResumeState
->Signature
== SMM_S3_RESUME_SMM_64
) {
1122 // Switch to long mode to complete resume.
1125 InterruptStatus
= SaveAndDisableInterrupts ();
1127 // Need to make sure the GDT is loaded with values that support long mode and real mode.
1129 AsmWriteGdtr (&mGdt
);
1131 // update segment selectors per the new GDT.
1133 AsmSetDataSelectors (DATA_SEGEMENT_SELECTOR
);
1135 // Restore interrupt state.
1137 SetInterruptState (InterruptStatus
);
1139 Cr0
.UintN
= AsmReadCr0 ();
1140 if (Cr0
.Bits
.PG
!= 0) {
1142 // We're in 32-bit mode, with paging enabled. We can't set CR3 to
1143 // the 64-bit page tables without first disabling paging.
1146 AsmWriteCr0 (Cr0
.UintN
);
1149 AsmWriteCr3 ((UINTN
)SmmS3ResumeState
->SmmS3Cr3
);
1152 // Disable interrupt of Debug timer, since IDT table cannot work in long mode.
1153 // NOTE: On x64 platforms, because DisablePaging64() will disable interrupts,
1154 // the code in S3ResumeExecuteBootScript() cannot be halted by soft debugger.
1156 SaveAndSetDebugTimerInterrupt (FALSE
);
1160 SmmS3ResumeState
->SmmS3ResumeEntryPoint
,
1161 (UINT64
)(UINTN
)AcpiS3Context
,
1163 SmmS3ResumeState
->SmmS3StackBase
+ SmmS3ResumeState
->SmmS3StackSize
1168 S3ResumeExecuteBootScript (AcpiS3Context
, EfiBootScriptExecutorVariable
);
1173 Main entry for S3 Resume PEIM.
1175 This routine is to install EFI_PEI_S3_RESUME2_PPI.
1177 @param FileHandle Handle of the file being invoked.
1178 @param PeiServices Pointer to PEI Services table.
1180 @retval EFI_SUCCESS S3Resume Ppi is installed successfully.
1185 PeimS3ResumeEntryPoint (
1186 IN EFI_PEI_FILE_HANDLE FileHandle
,
1187 IN CONST EFI_PEI_SERVICES
**PeiServices
1193 // Install S3 Resume Ppi
1195 Status
= (**PeiServices
).InstallPpi (PeiServices
, &mPpiList
);
1196 ASSERT_EFI_ERROR (Status
);