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 - 2018, Intel Corporation. All rights reserved.<BR>
8 Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR>
10 This program and the accompanying materials
11 are licensed and made available under the terms and conditions
12 of the BSD License which accompanies this distribution. The
13 full text of the license may be found at
14 http://opensource.org/licenses/bsd-license.php
16 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
17 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
23 #include <Guid/AcpiS3Context.h>
24 #include <Guid/BootScriptExecutorVariable.h>
25 #include <Guid/ExtendedFirmwarePerformance.h>
26 #include <Guid/EndOfS3Resume.h>
27 #include <Ppi/ReadOnlyVariable2.h>
28 #include <Ppi/S3Resume2.h>
29 #include <Ppi/SmmAccess.h>
30 #include <Ppi/PostBootScriptTable.h>
31 #include <Ppi/EndOfPeiPhase.h>
32 #include <Ppi/SmmCommunication.h>
34 #include <Library/DebugLib.h>
35 #include <Library/BaseLib.h>
36 #include <Library/TimerLib.h>
37 #include <Library/PeimEntryPoint.h>
38 #include <Library/PeiServicesLib.h>
39 #include <Library/HobLib.h>
40 #include <Library/PerformanceLib.h>
41 #include <Library/PeiServicesTablePointerLib.h>
42 #include <Library/IoLib.h>
43 #include <Library/BaseMemoryLib.h>
44 #include <Library/MemoryAllocationLib.h>
45 #include <Library/PcdLib.h>
46 #include <Library/DebugAgentLib.h>
47 #include <Library/LocalApicLib.h>
48 #include <Library/ReportStatusCodeLib.h>
49 #include <Library/PrintLib.h>
50 #include <Library/HobLib.h>
51 #include <Library/LockBoxLib.h>
52 #include <IndustryStandard/Acpi.h>
55 This macro aligns the address of a variable with auto storage
56 duration down to CPU_STACK_ALIGNMENT.
58 Since the stack grows downward, the result preserves more of the
59 stack than the original address (or the same amount), not less.
61 #define STACK_ALIGN_DOWN(Ptr) \
62 ((UINTN)(Ptr) & ~(UINTN)(CPU_STACK_ALIGNMENT - 1))
64 #define PAGING_1G_ADDRESS_MASK_64 0x000FFFFFC0000000ull
79 UINT32 DefaultSize
: 1;
80 UINT32 Granularity
: 1;
87 // Page-Map Level-4 Offset (PML4) and
88 // Page-Directory-Pointer Offset (PDPE) entries 4K & 2MB
92 UINT64 Present
:1; // 0 = Not present in memory, 1 = Present in memory
93 UINT64 ReadWrite
:1; // 0 = Read-Only, 1= Read/Write
94 UINT64 UserSupervisor
:1; // 0 = Supervisor, 1=User
95 UINT64 WriteThrough
:1; // 0 = Write-Back caching, 1=Write-Through caching
96 UINT64 CacheDisabled
:1; // 0 = Cached, 1=Non-Cached
97 UINT64 Accessed
:1; // 0 = Not accessed, 1 = Accessed (set by CPU)
98 UINT64 Reserved
:1; // Reserved
99 UINT64 MustBeZero
:2; // Must Be Zero
100 UINT64 Available
:3; // Available for use by system software
101 UINT64 PageTableBaseAddress
:40; // Page Table Base Address
102 UINT64 AvabilableHigh
:11; // Available for use by system software
103 UINT64 Nx
:1; // No Execute bit
106 } PAGE_MAP_AND_DIRECTORY_POINTER
;
109 // Page Table Entry 2MB
113 UINT64 Present
:1; // 0 = Not present in memory, 1 = Present in memory
114 UINT64 ReadWrite
:1; // 0 = Read-Only, 1= Read/Write
115 UINT64 UserSupervisor
:1; // 0 = Supervisor, 1=User
116 UINT64 WriteThrough
:1; // 0 = Write-Back caching, 1=Write-Through caching
117 UINT64 CacheDisabled
:1; // 0 = Cached, 1=Non-Cached
118 UINT64 Accessed
:1; // 0 = Not accessed, 1 = Accessed (set by CPU)
119 UINT64 Dirty
:1; // 0 = Not Dirty, 1 = written by processor on access to page
120 UINT64 MustBe1
:1; // Must be 1
121 UINT64 Global
:1; // 0 = Not global page, 1 = global page TLB not cleared on CR3 write
122 UINT64 Available
:3; // Available for use by system software
124 UINT64 MustBeZero
:8; // Must be zero;
125 UINT64 PageTableBaseAddress
:31; // Page Table Base Address
126 UINT64 AvabilableHigh
:11; // Available for use by system software
127 UINT64 Nx
:1; // 0 = Execute Code, 1 = No Code Execution
133 // Page Table Entry 1GB
137 UINT64 Present
:1; // 0 = Not present in memory, 1 = Present in memory
138 UINT64 ReadWrite
:1; // 0 = Read-Only, 1= Read/Write
139 UINT64 UserSupervisor
:1; // 0 = Supervisor, 1=User
140 UINT64 WriteThrough
:1; // 0 = Write-Back caching, 1=Write-Through caching
141 UINT64 CacheDisabled
:1; // 0 = Cached, 1=Non-Cached
142 UINT64 Accessed
:1; // 0 = Not accessed, 1 = Accessed (set by CPU)
143 UINT64 Dirty
:1; // 0 = Not Dirty, 1 = written by processor on access to page
144 UINT64 MustBe1
:1; // Must be 1
145 UINT64 Global
:1; // 0 = Not global page, 1 = global page TLB not cleared on CR3 write
146 UINT64 Available
:3; // Available for use by system software
148 UINT64 MustBeZero
:17; // Must be zero;
149 UINT64 PageTableBaseAddress
:22; // Page Table Base Address
150 UINT64 AvabilableHigh
:11; // Available for use by system software
151 UINT64 Nx
:1; // 0 = Execute Code, 1 = No Code Execution
154 } PAGE_TABLE_1G_ENTRY
;
157 // Define two type of smm communicate headers.
158 // One for 32 bits PEI + 64 bits DXE, the other for 32 bits PEI + 32 bits DXE case.
162 UINT32 MessageLength
;
164 } SMM_COMMUNICATE_HEADER_32
;
168 UINT64 MessageLength
;
170 } SMM_COMMUNICATE_HEADER_64
;
175 // Function prototypes
178 a ASM function to transfer control to OS.
180 @param S3WakingVector The S3 waking up vector saved in ACPI Facs table
181 @param AcpiLowMemoryBase a buffer under 1M which could be used during the transfer
185 (EFIAPI
*ASM_TRANSFER_CONTROL
) (
186 IN UINT32 S3WakingVector
,
187 IN UINT32 AcpiLowMemoryBase
191 Restores the platform to its preboot configuration for an S3 resume and
192 jumps to the OS waking vector.
194 This function will restore the platform to its pre-boot configuration that was
195 pre-stored in the boot script table and transfer control to OS waking vector.
196 Upon invocation, this function is responsible for locating the following
197 information before jumping to OS waking vector:
200 - any other information that it needs
202 The S3RestoreConfig() function then executes the pre-stored boot script table
203 and transitions the platform to the pre-boot state. The boot script is recorded
204 during regular boot using the EFI_S3_SAVE_STATE_PROTOCOL.Write() and
205 EFI_S3_SMM_SAVE_STATE_PROTOCOL.Write() functions. Finally, this function
206 transfers control to the OS waking vector. If the OS supports only a real-mode
207 waking vector, this function will switch from flat mode to real mode before
208 jumping to the waking vector. If all platform pre-boot configurations are
209 successfully restored and all other necessary information is ready, this
210 function will never return and instead will directly jump to the OS waking
211 vector. If this function returns, it indicates that the attempt to resume
212 from the ACPI S3 sleep state failed.
214 @param[in] This Pointer to this instance of the PEI_S3_RESUME_PPI
216 @retval EFI_ABORTED Execution of the S3 resume boot script table failed.
217 @retval EFI_NOT_FOUND Some necessary information that is used for the S3
218 resume boot path could not be located.
224 IN EFI_PEI_S3_RESUME2_PPI
*This
228 Set data segment selectors value including DS/ES/FS/GS/SS.
230 @param[in] SelectorValue Segment selector value to be set.
235 AsmSetDataSelectors (
236 IN UINT16 SelectorValue
242 EFI_PEI_S3_RESUME2_PPI mS3ResumePpi
= { S3RestoreConfig2
};
244 EFI_PEI_PPI_DESCRIPTOR mPpiList
= {
245 (EFI_PEI_PPI_DESCRIPTOR_PPI
| EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST
),
246 &gEfiPeiS3Resume2PpiGuid
,
250 EFI_PEI_PPI_DESCRIPTOR mPpiListPostScriptTable
= {
251 (EFI_PEI_PPI_DESCRIPTOR_PPI
| EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST
),
252 &gPeiPostScriptTablePpiGuid
,
256 EFI_PEI_PPI_DESCRIPTOR mPpiListEndOfPeiTable
= {
257 (EFI_PEI_PPI_DESCRIPTOR_PPI
| EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST
),
258 &gEfiEndOfPeiSignalPpiGuid
,
263 // Global Descriptor Table (GDT)
265 GLOBAL_REMOVE_IF_UNREFERENCED IA32_GDT mGdtEntries
[] = {
266 /* selector { Global Segment Descriptor } */
267 /* 0x00 */ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
268 /* 0x08 */ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
269 /* 0x10 */ {{0xFFFF, 0, 0, 0xB, 1, 0, 1, 0xF, 0, 0, 1, 1, 0}},
270 /* 0x18 */ {{0xFFFF, 0, 0, 0x3, 1, 0, 1, 0xF, 0, 0, 1, 1, 0}},
271 /* 0x20 */ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
272 /* 0x28 */ {{0xFFFF, 0, 0, 0xB, 1, 0, 1, 0xF, 0, 0, 0, 1, 0}},
273 /* 0x30 */ {{0xFFFF, 0, 0, 0x3, 1, 0, 1, 0xF, 0, 0, 0, 1, 0}},
274 /* 0x38 */ {{0xFFFF, 0, 0, 0xB, 1, 0, 1, 0xF, 0, 1, 0, 1, 0}},
275 /* 0x40 */ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
278 #define DATA_SEGEMENT_SELECTOR 0x18
283 GLOBAL_REMOVE_IF_UNREFERENCED CONST IA32_DESCRIPTOR mGdt
= {
284 sizeof (mGdtEntries
) - 1,
290 The function will check if current waking vector is long mode.
292 @param AcpiS3Context a pointer to a structure of ACPI_S3_CONTEXT
294 @retval TRUE Current context need long mode waking vector.
295 @retval FALSE Current context need not long mode waking vector.
298 IsLongModeWakingVector (
299 IN ACPI_S3_CONTEXT
*AcpiS3Context
302 EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*Facs
;
304 Facs
= (EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*) ((UINTN
) (AcpiS3Context
->AcpiFacsTable
));
305 if ((Facs
== NULL
) ||
306 (Facs
->Signature
!= EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE
) ||
307 ((Facs
->FirmwareWakingVector
== 0) && (Facs
->XFirmwareWakingVector
== 0)) ) {
308 // Something wrong with FACS
311 if (Facs
->XFirmwareWakingVector
!= 0) {
312 if ((Facs
->Version
== EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_VERSION
) &&
313 ((Facs
->Flags
& EFI_ACPI_4_0_64BIT_WAKE_SUPPORTED_F
) != 0) &&
314 ((Facs
->Flags
& EFI_ACPI_4_0_OSPM_64BIT_WAKE__F
) != 0)) {
315 // Both BIOS and OS wants 64bit vector
316 if (FeaturePcdGet (PcdDxeIplSwitchToLongMode
)) {
325 Send EndOfS3Resume event to SmmCore through communication buffer way.
327 @retval EFI_SUCCESS Return send the event success.
330 SignalEndOfS3Resume (
335 EFI_PEI_SMM_COMMUNICATION_PPI
*SmmCommunicationPpi
;
337 SMM_COMMUNICATE_HEADER_32 Header32
;
338 SMM_COMMUNICATE_HEADER_64 Header64
;
341 DEBUG ((DEBUG_INFO
, "SignalEndOfS3Resume - Enter\n"));
344 // This buffer consumed in DXE phase, so base on DXE mode to prepare communicate buffer.
345 // Detect whether DXE is 64 bits mode.
346 // if (sizeof(UINTN) == sizeof(UINT64), PEI already 64 bits, assume DXE also 64 bits.
347 // or (FeaturePcdGet (PcdDxeIplSwitchToLongMode)), DXE will switch to 64 bits.
349 if ((sizeof(UINTN
) == sizeof(UINT64
)) || (FeaturePcdGet (PcdDxeIplSwitchToLongMode
))) {
350 CommBuffer
= &Header64
;
351 Header64
.MessageLength
= 0;
352 CommSize
= OFFSET_OF (SMM_COMMUNICATE_HEADER_64
, Data
);
354 CommBuffer
= &Header32
;
355 Header32
.MessageLength
= 0;
356 CommSize
= OFFSET_OF (SMM_COMMUNICATE_HEADER_32
, Data
);
358 CopyGuid (CommBuffer
, &gEdkiiEndOfS3ResumeGuid
);
360 Status
= PeiServicesLocatePpi (
361 &gEfiPeiSmmCommunicationPpiGuid
,
364 (VOID
**)&SmmCommunicationPpi
366 if (EFI_ERROR (Status
)) {
367 DEBUG ((DEBUG_ERROR
, "Locate Smm Communicate Ppi failed (%r)!\n", Status
));
371 Status
= SmmCommunicationPpi
->Communicate (
376 if (EFI_ERROR (Status
)) {
377 DEBUG ((DEBUG_ERROR
, "SmmCommunicationPpi->Communicate return failure (%r)!\n", Status
));
380 DEBUG ((DEBUG_INFO
, "SignalEndOfS3Resume - Exit (%r)\n", Status
));
385 Jump to OS waking vector.
386 The function will install boot script done PPI, report S3 resume status code, and then jump to OS waking vector.
388 @param AcpiS3Context a pointer to a structure of ACPI_S3_CONTEXT
389 @param PeiS3ResumeState a pointer to a structure of PEI_S3_RESUME_STATE
394 IN ACPI_S3_CONTEXT
*AcpiS3Context
,
395 IN PEI_S3_RESUME_STATE
*PeiS3ResumeState
399 EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*Facs
;
400 ASM_TRANSFER_CONTROL AsmTransferControl
;
402 UINTN TempStack
[0x10];
407 AsmWriteIdtr (&PeiS3ResumeState
->Idtr
);
409 if (PeiS3ResumeState
->ReturnStatus
!= EFI_SUCCESS
) {
411 // Report Status code that boot script execution is failed
414 EFI_ERROR_CODE
| EFI_ERROR_MINOR
,
415 (EFI_SOFTWARE_PEI_MODULE
| EFI_SW_PEI_EC_S3_BOOT_SCRIPT_ERROR
)
420 // NOTE: Because Debug Timer interrupt and system interrupts will be disabled
421 // in BootScriptExecuteDxe, the rest code in S3ResumeBootOs() cannot be halted
425 PERF_END (NULL
, "ScriptExec", NULL
, 0);
428 // Install BootScriptDonePpi
430 PERF_START_EX (NULL
, "BootScriptDonePpi", NULL
, 0, PERF_INMODULE_START_ID
);
432 Status
= PeiServicesInstallPpi (&mPpiListPostScriptTable
);
433 ASSERT_EFI_ERROR (Status
);
435 PERF_END_EX (NULL
, "BootScriptDonePpi", NULL
, 0, PERF_INMODULE_END_ID
);
438 // Get ACPI Table Address
440 Facs
= (EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*) ((UINTN
) (AcpiS3Context
->AcpiFacsTable
));
442 if ((Facs
== NULL
) ||
443 (Facs
->Signature
!= EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE
) ||
444 ((Facs
->FirmwareWakingVector
== 0) && (Facs
->XFirmwareWakingVector
== 0)) ) {
446 // Report Status code that no valid vector is found
449 EFI_ERROR_CODE
| EFI_ERROR_MAJOR
,
450 (EFI_SOFTWARE_PEI_MODULE
| EFI_SW_PEI_EC_S3_OS_WAKE_ERROR
)
457 // Install EndOfPeiPpi
459 PERF_START_EX (NULL
, "EndOfPeiPpi", NULL
, 0, PERF_INMODULE_START_ID
);
461 Status
= PeiServicesInstallPpi (&mPpiListEndOfPeiTable
);
462 ASSERT_EFI_ERROR (Status
);
464 PERF_END_EX (NULL
, "EndOfPeiPpi", NULL
, 0, PERF_INMODULE_END_ID
);
467 // Signal EndOfS3Resume event.
469 PERF_START_EX (NULL
, "EndOfS3Resume", NULL
, 0, PERF_INMODULE_START_ID
);
471 SignalEndOfS3Resume ();
473 PERF_END_EX (NULL
, "EndOfS3Resume", NULL
, 0, PERF_INMODULE_END_ID
);
476 // report status code on S3 resume
478 REPORT_STATUS_CODE (EFI_PROGRESS_CODE
, EFI_SOFTWARE_PEI_MODULE
| EFI_SW_PEI_PC_OS_WAKE
);
480 AsmTransferControl
= (ASM_TRANSFER_CONTROL
)(UINTN
)PeiS3ResumeState
->AsmTransferControl
;
481 if (Facs
->XFirmwareWakingVector
!= 0) {
483 // Switch to native waking vector
485 TempStackTop
= (UINTN
)&TempStack
+ sizeof(TempStack
);
488 "%a() Stack Base: 0x%x, Stack Size: 0x%x\n",
493 if ((Facs
->Version
== EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_VERSION
) &&
494 ((Facs
->Flags
& EFI_ACPI_4_0_64BIT_WAKE_SUPPORTED_F
) != 0) &&
495 ((Facs
->Flags
& EFI_ACPI_4_0_OSPM_64BIT_WAKE__F
) != 0)) {
497 // X64 long mode waking vector
499 DEBUG ((DEBUG_INFO
, "Transfer to 64bit OS waking vector - %x\r\n", (UINTN
)Facs
->XFirmwareWakingVector
));
500 if (FeaturePcdGet (PcdDxeIplSwitchToLongMode
)) {
503 Facs
->XFirmwareWakingVector
,
506 (UINT64
)(UINTN
)TempStackTop
510 // Report Status code that no valid waking vector is found
513 EFI_ERROR_CODE
| EFI_ERROR_MAJOR
,
514 (EFI_SOFTWARE_PEI_MODULE
| EFI_SW_PEI_EC_S3_OS_WAKE_ERROR
)
516 DEBUG (( EFI_D_ERROR
, "Unsupported for 32bit DXE transfer to 64bit OS waking vector!\r\n"));
523 // IA32 protected mode waking vector (Page disabled)
525 DEBUG ((DEBUG_INFO
, "Transfer to 32bit OS waking vector - %x\r\n", (UINTN
)Facs
->XFirmwareWakingVector
));
527 (SWITCH_STACK_ENTRY_POINT
) (UINTN
) Facs
->XFirmwareWakingVector
,
530 (VOID
*)(UINTN
)TempStackTop
535 // 16bit Realmode waking vector
537 DEBUG ((DEBUG_INFO
, "Transfer to 16bit OS waking vector - %x\r\n", (UINTN
)Facs
->FirmwareWakingVector
));
538 AsmTransferControl (Facs
->FirmwareWakingVector
, 0x0);
542 // Report Status code the failure of S3Resume
545 EFI_ERROR_CODE
| EFI_ERROR_MAJOR
,
546 (EFI_SOFTWARE_PEI_MODULE
| EFI_SW_PEI_EC_S3_OS_WAKE_ERROR
)
556 Restore S3 page table because we do not trust ACPINvs content.
557 If BootScriptExector driver will not run in 64-bit mode, this function will do nothing.
559 @param S3NvsPageTableAddress PageTableAddress in ACPINvs
560 @param Build4GPageTableOnly If BIOS just build 4G page table only
563 RestoreS3PageTables (
564 IN UINTN S3NvsPageTableAddress
,
565 IN BOOLEAN Build4GPageTableOnly
568 if (FeaturePcdGet (PcdDxeIplSwitchToLongMode
)) {
571 UINT8 PhysicalAddressBits
;
572 EFI_PHYSICAL_ADDRESS PageAddress
;
573 UINTN IndexOfPml4Entries
;
574 UINTN IndexOfPdpEntries
;
575 UINTN IndexOfPageDirectoryEntries
;
576 UINT32 NumberOfPml4EntriesNeeded
;
577 UINT32 NumberOfPdpEntriesNeeded
;
578 PAGE_MAP_AND_DIRECTORY_POINTER
*PageMapLevel4Entry
;
579 PAGE_MAP_AND_DIRECTORY_POINTER
*PageMap
;
580 PAGE_MAP_AND_DIRECTORY_POINTER
*PageDirectoryPointerEntry
;
581 PAGE_TABLE_ENTRY
*PageDirectoryEntry
;
583 BOOLEAN Page1GSupport
;
584 PAGE_TABLE_1G_ENTRY
*PageDirectory1GEntry
;
585 UINT64 AddressEncMask
;
588 // Make sure AddressEncMask is contained to smallest supported address field
590 AddressEncMask
= PcdGet64 (PcdPteMemoryEncryptionAddressOrMask
) & PAGING_1G_ADDRESS_MASK_64
;
593 // NOTE: We have to ASSUME the page table generation format, because we do not know whole page table information.
594 // The whole page table is too large to be saved in SMRAM.
596 // The assumption is : whole page table is allocated in CONTINUOUS memory and CR3 points to TOP page.
598 DEBUG ((DEBUG_INFO
, "S3NvsPageTableAddress - %x (%x)\n", (UINTN
)S3NvsPageTableAddress
, (UINTN
)Build4GPageTableOnly
));
601 // By architecture only one PageMapLevel4 exists - so lets allocate storage for it.
603 PageMap
= (PAGE_MAP_AND_DIRECTORY_POINTER
*)S3NvsPageTableAddress
;
604 S3NvsPageTableAddress
+= SIZE_4KB
;
606 Page1GSupport
= FALSE
;
607 if (PcdGetBool(PcdUse1GPageTable
)) {
608 AsmCpuid (0x80000000, &RegEax
, NULL
, NULL
, NULL
);
609 if (RegEax
>= 0x80000001) {
610 AsmCpuid (0x80000001, NULL
, NULL
, NULL
, &RegEdx
);
611 if ((RegEdx
& BIT26
) != 0) {
612 Page1GSupport
= TRUE
;
618 // Get physical address bits supported.
620 Hob
= GetFirstHob (EFI_HOB_TYPE_CPU
);
622 PhysicalAddressBits
= ((EFI_HOB_CPU
*) Hob
)->SizeOfMemorySpace
;
624 AsmCpuid (0x80000000, &RegEax
, NULL
, NULL
, NULL
);
625 if (RegEax
>= 0x80000008) {
626 AsmCpuid (0x80000008, &RegEax
, NULL
, NULL
, NULL
);
627 PhysicalAddressBits
= (UINT8
) RegEax
;
629 PhysicalAddressBits
= 36;
634 // IA-32e paging translates 48-bit linear addresses to 52-bit physical addresses.
636 ASSERT (PhysicalAddressBits
<= 52);
637 if (PhysicalAddressBits
> 48) {
638 PhysicalAddressBits
= 48;
642 // NOTE: In order to save time to create full page table, we just create 4G page table by default.
643 // And let PF handler in BootScript driver to create more on request.
645 if (Build4GPageTableOnly
) {
646 PhysicalAddressBits
= 32;
647 ZeroMem (PageMap
, EFI_PAGES_TO_SIZE(2));
650 // Calculate the table entries needed.
652 if (PhysicalAddressBits
<= 39) {
653 NumberOfPml4EntriesNeeded
= 1;
654 NumberOfPdpEntriesNeeded
= (UINT32
)LShiftU64 (1, (PhysicalAddressBits
- 30));
656 NumberOfPml4EntriesNeeded
= (UINT32
)LShiftU64 (1, (PhysicalAddressBits
- 39));
657 NumberOfPdpEntriesNeeded
= 512;
660 PageMapLevel4Entry
= PageMap
;
662 for (IndexOfPml4Entries
= 0; IndexOfPml4Entries
< NumberOfPml4EntriesNeeded
; IndexOfPml4Entries
++, PageMapLevel4Entry
++) {
664 // Each PML4 entry points to a page of Page Directory Pointer entires.
665 // So lets allocate space for them and fill them in in the IndexOfPdpEntries loop.
667 PageDirectoryPointerEntry
= (PAGE_MAP_AND_DIRECTORY_POINTER
*)S3NvsPageTableAddress
;
668 S3NvsPageTableAddress
+= SIZE_4KB
;
673 PageMapLevel4Entry
->Uint64
= (UINT64
)(UINTN
)PageDirectoryPointerEntry
| AddressEncMask
;
674 PageMapLevel4Entry
->Bits
.ReadWrite
= 1;
675 PageMapLevel4Entry
->Bits
.Present
= 1;
678 PageDirectory1GEntry
= (VOID
*) PageDirectoryPointerEntry
;
680 for (IndexOfPageDirectoryEntries
= 0; IndexOfPageDirectoryEntries
< 512; IndexOfPageDirectoryEntries
++, PageDirectory1GEntry
++, PageAddress
+= SIZE_1GB
) {
682 // Fill in the Page Directory entries
684 PageDirectory1GEntry
->Uint64
= (UINT64
)PageAddress
| AddressEncMask
;
685 PageDirectory1GEntry
->Bits
.ReadWrite
= 1;
686 PageDirectory1GEntry
->Bits
.Present
= 1;
687 PageDirectory1GEntry
->Bits
.MustBe1
= 1;
690 for (IndexOfPdpEntries
= 0; IndexOfPdpEntries
< NumberOfPdpEntriesNeeded
; IndexOfPdpEntries
++, PageDirectoryPointerEntry
++) {
692 // Each Directory Pointer entries points to a page of Page Directory entires.
693 // So allocate space for them and fill them in in the IndexOfPageDirectoryEntries loop.
695 PageDirectoryEntry
= (PAGE_TABLE_ENTRY
*)S3NvsPageTableAddress
;
696 S3NvsPageTableAddress
+= SIZE_4KB
;
699 // Fill in a Page Directory Pointer Entries
701 PageDirectoryPointerEntry
->Uint64
= (UINT64
)(UINTN
)PageDirectoryEntry
| AddressEncMask
;
702 PageDirectoryPointerEntry
->Bits
.ReadWrite
= 1;
703 PageDirectoryPointerEntry
->Bits
.Present
= 1;
705 for (IndexOfPageDirectoryEntries
= 0; IndexOfPageDirectoryEntries
< 512; IndexOfPageDirectoryEntries
++, PageDirectoryEntry
++, PageAddress
+= SIZE_2MB
) {
707 // Fill in the Page Directory entries
709 PageDirectoryEntry
->Uint64
= (UINT64
)PageAddress
| AddressEncMask
;
710 PageDirectoryEntry
->Bits
.ReadWrite
= 1;
711 PageDirectoryEntry
->Bits
.Present
= 1;
712 PageDirectoryEntry
->Bits
.MustBe1
= 1;
720 // If DXE is running 32-bit mode, no need to establish page table.
727 Jump to boot script executor driver.
729 The function will close and lock SMRAM and then jump to boot script execute driver to executing S3 boot script table.
731 @param AcpiS3Context a pointer to a structure of ACPI_S3_CONTEXT
732 @param EfiBootScriptExecutorVariable The function entry to executing S3 boot Script table. This function is build in
733 boot script execute driver
737 S3ResumeExecuteBootScript (
738 IN ACPI_S3_CONTEXT
*AcpiS3Context
,
739 IN BOOT_SCRIPT_EXECUTOR_VARIABLE
*EfiBootScriptExecutorVariable
743 PEI_SMM_ACCESS_PPI
*SmmAccess
;
746 IA32_DESCRIPTOR
*IdtDescriptor
;
748 PEI_S3_RESUME_STATE
*PeiS3ResumeState
;
749 BOOLEAN InterruptStatus
;
751 DEBUG ((DEBUG_INFO
, "S3ResumeExecuteBootScript()\n"));
754 // Attempt to use content from SMRAM first
756 GuidHob
= GetFirstGuidHob (&gEfiAcpiVariableGuid
);
757 if (GuidHob
!= NULL
) {
759 // Last step for SMM - send SMI for initialization
765 SendSmiIpiAllExcludingSelf ();
769 SendSmiIpi (GetApicId ());
771 Status
= PeiServicesLocatePpi (
772 &gPeiSmmAccessPpiGuid
,
777 if (!EFI_ERROR (Status
)) {
778 DEBUG ((DEBUG_INFO
, "Close all SMRAM regions before executing boot script\n"));
780 for (Index
= 0, Status
= EFI_SUCCESS
; !EFI_ERROR (Status
); Index
++) {
781 Status
= SmmAccess
->Close ((EFI_PEI_SERVICES
**)GetPeiServicesTablePointer (), SmmAccess
, Index
);
784 DEBUG ((DEBUG_INFO
, "Lock all SMRAM regions before executing boot script\n"));
786 for (Index
= 0, Status
= EFI_SUCCESS
; !EFI_ERROR (Status
); Index
++) {
787 Status
= SmmAccess
->Lock ((EFI_PEI_SERVICES
**)GetPeiServicesTablePointer (), SmmAccess
, Index
);
792 if (FeaturePcdGet (PcdDxeIplSwitchToLongMode
)) {
793 AsmWriteCr3 ((UINTN
)AcpiS3Context
->S3NvsPageTableAddress
);
796 if (FeaturePcdGet (PcdFrameworkCompatibilitySupport
)) {
798 // On some platform, such as ECP, a dispatch node in boot script table may execute a 32-bit PEIM which may need PeiServices
799 // pointer. So PeiServices need preserve in (IDTBase- sizeof (UINTN)).
801 IdtDescriptor
= (IA32_DESCRIPTOR
*) (UINTN
) (AcpiS3Context
->IdtrProfile
);
803 // Make sure the newly allocated IDT align with 16-bytes
805 IdtBuffer
= AllocatePages (EFI_SIZE_TO_PAGES((IdtDescriptor
->Limit
+ 1) + 16));
806 if (IdtBuffer
== NULL
) {
808 EFI_ERROR_CODE
| EFI_ERROR_MAJOR
,
809 (EFI_SOFTWARE_PEI_MODULE
| EFI_SW_PEI_EC_S3_RESUME_FAILED
)
814 // Additional 16 bytes allocated to save IA32 IDT descriptor and Pei Service Table Pointer
815 // IA32 IDT descriptor will be used to setup IA32 IDT table for 32-bit Framework Boot Script code
817 ZeroMem (IdtBuffer
, 16);
818 AsmReadIdtr ((IA32_DESCRIPTOR
*)IdtBuffer
);
819 CopyMem ((VOID
*)((UINT8
*)IdtBuffer
+ 16),(VOID
*)(IdtDescriptor
->Base
), (IdtDescriptor
->Limit
+ 1));
820 IdtDescriptor
->Base
= (UINTN
)((UINT8
*)IdtBuffer
+ 16);
821 *(UINTN
*)(IdtDescriptor
->Base
- sizeof(UINTN
)) = (UINTN
)GetPeiServicesTablePointer ();
824 InterruptStatus
= SaveAndDisableInterrupts ();
826 // Need to make sure the GDT is loaded with values that support long mode and real mode.
828 AsmWriteGdtr (&mGdt
);
830 // update segment selectors per the new GDT.
832 AsmSetDataSelectors (DATA_SEGEMENT_SELECTOR
);
834 // Restore interrupt state.
836 SetInterruptState (InterruptStatus
);
839 // Prepare data for return back
841 PeiS3ResumeState
= AllocatePool (sizeof(*PeiS3ResumeState
));
842 if (PeiS3ResumeState
== NULL
) {
844 EFI_ERROR_CODE
| EFI_ERROR_MAJOR
,
845 (EFI_SOFTWARE_PEI_MODULE
| EFI_SW_PEI_EC_S3_RESUME_FAILED
)
849 DEBUG ((DEBUG_INFO
, "PeiS3ResumeState - %x\r\n", PeiS3ResumeState
));
850 PeiS3ResumeState
->ReturnCs
= 0x10;
851 PeiS3ResumeState
->ReturnEntryPoint
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)S3ResumeBootOs
;
852 PeiS3ResumeState
->ReturnStackPointer
= (EFI_PHYSICAL_ADDRESS
)STACK_ALIGN_DOWN (&Status
);
856 AsmReadIdtr (&PeiS3ResumeState
->Idtr
);
859 // Report Status Code to indicate S3 boot script execution
861 REPORT_STATUS_CODE (EFI_PROGRESS_CODE
, EFI_SOFTWARE_PEI_MODULE
| EFI_SW_PEI_PC_S3_BOOT_SCRIPT
);
863 PERF_START (NULL
, "ScriptExec", NULL
, 0);
865 if (FeaturePcdGet (PcdDxeIplSwitchToLongMode
)) {
869 DEBUG ((DEBUG_INFO
, "Enable X64 and transfer control to Standalone Boot Script Executor\r\n"));
872 // Switch to long mode to complete resume.
876 EfiBootScriptExecutorVariable
->BootScriptExecutorEntrypoint
,
877 (UINT64
)(UINTN
)AcpiS3Context
,
878 (UINT64
)(UINTN
)PeiS3ResumeState
,
879 (UINT64
)(UINTN
)(AcpiS3Context
->BootScriptStackBase
+ AcpiS3Context
->BootScriptStackSize
)
885 DEBUG ((DEBUG_INFO
, "transfer control to Standalone Boot Script Executor\r\n"));
887 (SWITCH_STACK_ENTRY_POINT
) (UINTN
) EfiBootScriptExecutorVariable
->BootScriptExecutorEntrypoint
,
888 (VOID
*)AcpiS3Context
,
889 (VOID
*)PeiS3ResumeState
,
890 (VOID
*)(UINTN
)(AcpiS3Context
->BootScriptStackBase
+ AcpiS3Context
->BootScriptStackSize
)
900 Restores the platform to its preboot configuration for an S3 resume and
901 jumps to the OS waking vector.
903 This function will restore the platform to its pre-boot configuration that was
904 pre-stored in the boot script table and transfer control to OS waking vector.
905 Upon invocation, this function is responsible for locating the following
906 information before jumping to OS waking vector:
909 - any other information that it needs
911 The S3RestoreConfig() function then executes the pre-stored boot script table
912 and transitions the platform to the pre-boot state. The boot script is recorded
913 during regular boot using the EFI_S3_SAVE_STATE_PROTOCOL.Write() and
914 EFI_S3_SMM_SAVE_STATE_PROTOCOL.Write() functions. Finally, this function
915 transfers control to the OS waking vector. If the OS supports only a real-mode
916 waking vector, this function will switch from flat mode to real mode before
917 jumping to the waking vector. If all platform pre-boot configurations are
918 successfully restored and all other necessary information is ready, this
919 function will never return and instead will directly jump to the OS waking
920 vector. If this function returns, it indicates that the attempt to resume
921 from the ACPI S3 sleep state failed.
923 @param[in] This Pointer to this instance of the PEI_S3_RESUME_PPI
925 @retval EFI_ABORTED Execution of the S3 resume boot script table failed.
926 @retval EFI_NOT_FOUND Some necessary information that is used for the S3
927 resume boot path could not be located.
933 IN EFI_PEI_S3_RESUME2_PPI
*This
937 PEI_SMM_ACCESS_PPI
*SmmAccess
;
939 ACPI_S3_CONTEXT
*AcpiS3Context
;
940 EFI_PHYSICAL_ADDRESS TempEfiBootScriptExecutorVariable
;
941 EFI_PHYSICAL_ADDRESS TempAcpiS3Context
;
942 BOOT_SCRIPT_EXECUTOR_VARIABLE
*EfiBootScriptExecutorVariable
;
944 EFI_SMRAM_DESCRIPTOR
*SmramDescriptor
;
945 SMM_S3_RESUME_STATE
*SmmS3ResumeState
;
947 BOOLEAN Build4GPageTableOnly
;
948 BOOLEAN InterruptStatus
;
950 TempAcpiS3Context
= 0;
951 TempEfiBootScriptExecutorVariable
= 0;
953 DEBUG ((DEBUG_INFO
, "Enter S3 PEIM\r\n"));
955 VarSize
= sizeof (EFI_PHYSICAL_ADDRESS
);
956 Status
= RestoreLockBox (
957 &gEfiAcpiVariableGuid
,
961 ASSERT_EFI_ERROR (Status
);
963 Status
= RestoreLockBox (
964 &gEfiAcpiS3ContextGuid
,
968 ASSERT_EFI_ERROR (Status
);
970 AcpiS3Context
= (ACPI_S3_CONTEXT
*)(UINTN
)TempAcpiS3Context
;
971 ASSERT (AcpiS3Context
!= NULL
);
973 VarSize
= sizeof (EFI_PHYSICAL_ADDRESS
);
974 Status
= RestoreLockBox (
975 &gEfiBootScriptExecutorVariableGuid
,
976 &TempEfiBootScriptExecutorVariable
,
979 ASSERT_EFI_ERROR (Status
);
981 Status
= RestoreLockBox (
982 &gEfiBootScriptExecutorContextGuid
,
986 ASSERT_EFI_ERROR (Status
);
988 EfiBootScriptExecutorVariable
= (BOOT_SCRIPT_EXECUTOR_VARIABLE
*) (UINTN
) TempEfiBootScriptExecutorVariable
;
989 ASSERT (EfiBootScriptExecutorVariable
!= NULL
);
991 DEBUG (( DEBUG_INFO
, "AcpiS3Context = %x\n", AcpiS3Context
));
992 DEBUG (( DEBUG_INFO
, "Waking Vector = %x\n", ((EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*) ((UINTN
) (AcpiS3Context
->AcpiFacsTable
)))->FirmwareWakingVector
));
993 DEBUG (( DEBUG_INFO
, "AcpiS3Context->AcpiFacsTable = %x\n", AcpiS3Context
->AcpiFacsTable
));
994 DEBUG (( DEBUG_INFO
, "AcpiS3Context->IdtrProfile = %x\n", AcpiS3Context
->IdtrProfile
));
995 DEBUG (( DEBUG_INFO
, "AcpiS3Context->S3NvsPageTableAddress = %x\n", AcpiS3Context
->S3NvsPageTableAddress
));
996 DEBUG (( DEBUG_INFO
, "AcpiS3Context->S3DebugBufferAddress = %x\n", AcpiS3Context
->S3DebugBufferAddress
));
997 DEBUG (( DEBUG_INFO
, "AcpiS3Context->BootScriptStackBase = %x\n", AcpiS3Context
->BootScriptStackBase
));
998 DEBUG (( DEBUG_INFO
, "AcpiS3Context->BootScriptStackSize = %x\n", AcpiS3Context
->BootScriptStackSize
));
999 DEBUG (( DEBUG_INFO
, "EfiBootScriptExecutorVariable->BootScriptExecutorEntrypoint = %x\n", EfiBootScriptExecutorVariable
->BootScriptExecutorEntrypoint
));
1002 // Additional step for BootScript integrity - we only handle BootScript and BootScriptExecutor.
1003 // Script dispatch image and context (parameter) are handled by platform.
1004 // We just use restore all lock box in place, no need restore one by one.
1006 Status
= RestoreAllLockBoxInPlace ();
1007 ASSERT_EFI_ERROR (Status
);
1008 if (EFI_ERROR (Status
)) {
1013 if (FeaturePcdGet (PcdDxeIplSwitchToLongMode
)) {
1015 // Need reconstruct page table here, since we do not trust ACPINvs.
1017 if (IsLongModeWakingVector (AcpiS3Context
)) {
1018 Build4GPageTableOnly
= FALSE
;
1020 Build4GPageTableOnly
= TRUE
;
1022 RestoreS3PageTables ((UINTN
)AcpiS3Context
->S3NvsPageTableAddress
, Build4GPageTableOnly
);
1026 // Attempt to use content from SMRAM first
1028 GuidHob
= GetFirstGuidHob (&gEfiAcpiVariableGuid
);
1029 if (GuidHob
!= NULL
) {
1030 Status
= PeiServicesLocatePpi (
1031 &gPeiSmmAccessPpiGuid
,
1034 (VOID
**) &SmmAccess
1036 for (Index
= 0; !EFI_ERROR (Status
); Index
++) {
1037 Status
= SmmAccess
->Open ((EFI_PEI_SERVICES
**)GetPeiServicesTablePointer (), SmmAccess
, Index
);
1040 SmramDescriptor
= (EFI_SMRAM_DESCRIPTOR
*) GET_GUID_HOB_DATA (GuidHob
);
1041 SmmS3ResumeState
= (SMM_S3_RESUME_STATE
*)(UINTN
)SmramDescriptor
->CpuStart
;
1043 SmmS3ResumeState
->ReturnCs
= AsmReadCs ();
1044 SmmS3ResumeState
->ReturnEntryPoint
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)S3ResumeExecuteBootScript
;
1045 SmmS3ResumeState
->ReturnContext1
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)AcpiS3Context
;
1046 SmmS3ResumeState
->ReturnContext2
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)EfiBootScriptExecutorVariable
;
1047 SmmS3ResumeState
->ReturnStackPointer
= (EFI_PHYSICAL_ADDRESS
)STACK_ALIGN_DOWN (&Status
);
1049 DEBUG (( DEBUG_INFO
, "SMM S3 Signature = %x\n", SmmS3ResumeState
->Signature
));
1050 DEBUG (( DEBUG_INFO
, "SMM S3 Stack Base = %x\n", SmmS3ResumeState
->SmmS3StackBase
));
1051 DEBUG (( DEBUG_INFO
, "SMM S3 Stack Size = %x\n", SmmS3ResumeState
->SmmS3StackSize
));
1052 DEBUG (( DEBUG_INFO
, "SMM S3 Resume Entry Point = %x\n", SmmS3ResumeState
->SmmS3ResumeEntryPoint
));
1053 DEBUG (( DEBUG_INFO
, "SMM S3 CR0 = %x\n", SmmS3ResumeState
->SmmS3Cr0
));
1054 DEBUG (( DEBUG_INFO
, "SMM S3 CR3 = %x\n", SmmS3ResumeState
->SmmS3Cr3
));
1055 DEBUG (( DEBUG_INFO
, "SMM S3 CR4 = %x\n", SmmS3ResumeState
->SmmS3Cr4
));
1056 DEBUG (( DEBUG_INFO
, "SMM S3 Return CS = %x\n", SmmS3ResumeState
->ReturnCs
));
1057 DEBUG (( DEBUG_INFO
, "SMM S3 Return Entry Point = %x\n", SmmS3ResumeState
->ReturnEntryPoint
));
1058 DEBUG (( DEBUG_INFO
, "SMM S3 Return Context1 = %x\n", SmmS3ResumeState
->ReturnContext1
));
1059 DEBUG (( DEBUG_INFO
, "SMM S3 Return Context2 = %x\n", SmmS3ResumeState
->ReturnContext2
));
1060 DEBUG (( DEBUG_INFO
, "SMM S3 Return Stack Pointer = %x\n", SmmS3ResumeState
->ReturnStackPointer
));
1061 DEBUG (( DEBUG_INFO
, "SMM S3 Smst = %x\n", SmmS3ResumeState
->Smst
));
1063 if (SmmS3ResumeState
->Signature
== SMM_S3_RESUME_SMM_32
) {
1065 (SWITCH_STACK_ENTRY_POINT
)(UINTN
)SmmS3ResumeState
->SmmS3ResumeEntryPoint
,
1066 (VOID
*)AcpiS3Context
,
1068 (VOID
*)(UINTN
)(SmmS3ResumeState
->SmmS3StackBase
+ SmmS3ResumeState
->SmmS3StackSize
)
1071 if (SmmS3ResumeState
->Signature
== SMM_S3_RESUME_SMM_64
) {
1073 // Switch to long mode to complete resume.
1076 InterruptStatus
= SaveAndDisableInterrupts ();
1078 // Need to make sure the GDT is loaded with values that support long mode and real mode.
1080 AsmWriteGdtr (&mGdt
);
1082 // update segment selectors per the new GDT.
1084 AsmSetDataSelectors (DATA_SEGEMENT_SELECTOR
);
1086 // Restore interrupt state.
1088 SetInterruptState (InterruptStatus
);
1090 AsmWriteCr3 ((UINTN
)SmmS3ResumeState
->SmmS3Cr3
);
1093 // Disable interrupt of Debug timer, since IDT table cannot work in long mode.
1094 // NOTE: On x64 platforms, because DisablePaging64() will disable interrupts,
1095 // the code in S3ResumeExecuteBootScript() cannot be halted by soft debugger.
1097 SaveAndSetDebugTimerInterrupt (FALSE
);
1101 SmmS3ResumeState
->SmmS3ResumeEntryPoint
,
1102 (UINT64
)(UINTN
)AcpiS3Context
,
1104 SmmS3ResumeState
->SmmS3StackBase
+ SmmS3ResumeState
->SmmS3StackSize
1110 S3ResumeExecuteBootScript (AcpiS3Context
, EfiBootScriptExecutorVariable
);
1114 Main entry for S3 Resume PEIM.
1116 This routine is to install EFI_PEI_S3_RESUME2_PPI.
1118 @param FileHandle Handle of the file being invoked.
1119 @param PeiServices Pointer to PEI Services table.
1121 @retval EFI_SUCCESS S3Resume Ppi is installed successfully.
1126 PeimS3ResumeEntryPoint (
1127 IN EFI_PEI_FILE_HANDLE FileHandle
,
1128 IN CONST EFI_PEI_SERVICES
**PeiServices
1134 // Install S3 Resume Ppi
1136 Status
= (**PeiServices
).InstallPpi (PeiServices
, &mPpiList
);
1137 ASSERT_EFI_ERROR (Status
);