2 This module produces the EFI_PEI_S3_RESUME_PPI.
3 This module works with StandAloneBootScriptExecutor to S3 resume to OS.
4 This module will excute the boot script saved during last boot and after that,
5 control is passed to OS waking up handler.
7 Copyright (c) 2006 - 2012, Intel Corporation. All rights reserved.<BR>
9 This program and the accompanying materials
10 are licensed and made available under the terms and conditions
11 of the BSD License which accompanies this distribution. The
12 full text of the license may be found at
13 http://opensource.org/licenses/bsd-license.php
15 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
16 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
22 #include <Guid/AcpiS3Context.h>
23 #include <Guid/BootScriptExecutorVariable.h>
24 #include <Guid/Performance.h>
25 #include <Ppi/ReadOnlyVariable2.h>
26 #include <Ppi/S3Resume2.h>
27 #include <Ppi/SmmAccess.h>
28 #include <Ppi/PostBootScriptTable.h>
29 #include <Ppi/EndOfPeiPhase.h>
31 #include <Library/DebugLib.h>
32 #include <Library/BaseLib.h>
33 #include <Library/TimerLib.h>
34 #include <Library/PeimEntryPoint.h>
35 #include <Library/PeiServicesLib.h>
36 #include <Library/HobLib.h>
37 #include <Library/PerformanceLib.h>
38 #include <Library/PeiServicesTablePointerLib.h>
39 #include <Library/IoLib.h>
40 #include <Library/BaseMemoryLib.h>
41 #include <Library/MemoryAllocationLib.h>
42 #include <Library/PcdLib.h>
43 #include <Library/DebugAgentLib.h>
44 #include <Library/LocalApicLib.h>
45 #include <Library/ReportStatusCodeLib.h>
46 #include <Library/PrintLib.h>
47 #include <Library/HobLib.h>
48 #include <Library/LockBoxLib.h>
49 #include <IndustryStandard/Acpi.h>
64 UINT32 DefaultSize
: 1;
65 UINT32 Granularity
: 1;
72 // Page-Map Level-4 Offset (PML4) and
73 // Page-Directory-Pointer Offset (PDPE) entries 4K & 2MB
77 UINT64 Present
:1; // 0 = Not present in memory, 1 = Present in memory
78 UINT64 ReadWrite
:1; // 0 = Read-Only, 1= Read/Write
79 UINT64 UserSupervisor
:1; // 0 = Supervisor, 1=User
80 UINT64 WriteThrough
:1; // 0 = Write-Back caching, 1=Write-Through caching
81 UINT64 CacheDisabled
:1; // 0 = Cached, 1=Non-Cached
82 UINT64 Accessed
:1; // 0 = Not accessed, 1 = Accessed (set by CPU)
83 UINT64 Reserved
:1; // Reserved
84 UINT64 MustBeZero
:2; // Must Be Zero
85 UINT64 Available
:3; // Available for use by system software
86 UINT64 PageTableBaseAddress
:40; // Page Table Base Address
87 UINT64 AvabilableHigh
:11; // Available for use by system software
88 UINT64 Nx
:1; // No Execute bit
91 } PAGE_MAP_AND_DIRECTORY_POINTER
;
94 // Page Table Entry 2MB
98 UINT64 Present
:1; // 0 = Not present in memory, 1 = Present in memory
99 UINT64 ReadWrite
:1; // 0 = Read-Only, 1= Read/Write
100 UINT64 UserSupervisor
:1; // 0 = Supervisor, 1=User
101 UINT64 WriteThrough
:1; // 0 = Write-Back caching, 1=Write-Through caching
102 UINT64 CacheDisabled
:1; // 0 = Cached, 1=Non-Cached
103 UINT64 Accessed
:1; // 0 = Not accessed, 1 = Accessed (set by CPU)
104 UINT64 Dirty
:1; // 0 = Not Dirty, 1 = written by processor on access to page
105 UINT64 MustBe1
:1; // Must be 1
106 UINT64 Global
:1; // 0 = Not global page, 1 = global page TLB not cleared on CR3 write
107 UINT64 Available
:3; // Available for use by system software
109 UINT64 MustBeZero
:8; // Must be zero;
110 UINT64 PageTableBaseAddress
:31; // Page Table Base Address
111 UINT64 AvabilableHigh
:11; // Available for use by system software
112 UINT64 Nx
:1; // 0 = Execute Code, 1 = No Code Execution
118 // Page Table Entry 1GB
122 UINT64 Present
:1; // 0 = Not present in memory, 1 = Present in memory
123 UINT64 ReadWrite
:1; // 0 = Read-Only, 1= Read/Write
124 UINT64 UserSupervisor
:1; // 0 = Supervisor, 1=User
125 UINT64 WriteThrough
:1; // 0 = Write-Back caching, 1=Write-Through caching
126 UINT64 CacheDisabled
:1; // 0 = Cached, 1=Non-Cached
127 UINT64 Accessed
:1; // 0 = Not accessed, 1 = Accessed (set by CPU)
128 UINT64 Dirty
:1; // 0 = Not Dirty, 1 = written by processor on access to page
129 UINT64 MustBe1
:1; // Must be 1
130 UINT64 Global
:1; // 0 = Not global page, 1 = global page TLB not cleared on CR3 write
131 UINT64 Available
:3; // Available for use by system software
133 UINT64 MustBeZero
:17; // Must be zero;
134 UINT64 PageTableBaseAddress
:22; // Page Table Base Address
135 UINT64 AvabilableHigh
:11; // Available for use by system software
136 UINT64 Nx
:1; // 0 = Execute Code, 1 = No Code Execution
139 } PAGE_TABLE_1G_ENTRY
;
144 // Function prototypes
147 a ASM function to transfer control to OS.
149 @param S3WakingVector The S3 waking up vector saved in ACPI Facs table
150 @param AcpiLowMemoryBase a buffer under 1M which could be used during the transfer
154 (EFIAPI
*ASM_TRANSFER_CONTROL
) (
155 IN UINT32 S3WakingVector
,
156 IN UINT32 AcpiLowMemoryBase
160 Restores the platform to its preboot configuration for an S3 resume and
161 jumps to the OS waking vector.
163 This function will restore the platform to its pre-boot configuration that was
164 pre-stored in the boot script table and transfer control to OS waking vector.
165 Upon invocation, this function is responsible for locating the following
166 information before jumping to OS waking vector:
169 - any other information that it needs
171 The S3RestoreConfig() function then executes the pre-stored boot script table
172 and transitions the platform to the pre-boot state. The boot script is recorded
173 during regular boot using the EFI_S3_SAVE_STATE_PROTOCOL.Write() and
174 EFI_S3_SMM_SAVE_STATE_PROTOCOL.Write() functions. Finally, this function
175 transfers control to the OS waking vector. If the OS supports only a real-mode
176 waking vector, this function will switch from flat mode to real mode before
177 jumping to the waking vector. If all platform pre-boot configurations are
178 successfully restored and all other necessary information is ready, this
179 function will never return and instead will directly jump to the OS waking
180 vector. If this function returns, it indicates that the attempt to resume
181 from the ACPI S3 sleep state failed.
183 @param[in] This Pointer to this instance of the PEI_S3_RESUME_PPI
185 @retval EFI_ABORTED Execution of the S3 resume boot script table failed.
186 @retval EFI_NOT_FOUND Some necessary information that is used for the S3
187 resume boot path could not be located.
193 IN EFI_PEI_S3_RESUME2_PPI
*This
197 Set data segment selectors value including DS/ES/FS/GS/SS.
199 @param[in] SelectorValue Segment selector value to be set.
204 AsmSetDataSelectors (
205 IN UINT16 SelectorValue
211 EFI_PEI_S3_RESUME2_PPI mS3ResumePpi
= { S3RestoreConfig2
};
213 EFI_PEI_PPI_DESCRIPTOR mPpiList
= {
214 (EFI_PEI_PPI_DESCRIPTOR_PPI
| EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST
),
215 &gEfiPeiS3Resume2PpiGuid
,
219 EFI_PEI_PPI_DESCRIPTOR mPpiListPostScriptTable
= {
220 (EFI_PEI_PPI_DESCRIPTOR_PPI
| EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST
),
221 &gPeiPostScriptTablePpiGuid
,
225 EFI_PEI_PPI_DESCRIPTOR mPpiListEndOfPeiTable
= {
226 (EFI_PEI_PPI_DESCRIPTOR_PPI
| EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST
),
227 &gEfiEndOfPeiSignalPpiGuid
,
232 // Global Descriptor Table (GDT)
234 GLOBAL_REMOVE_IF_UNREFERENCED IA32_GDT mGdtEntries
[] = {
235 /* selector { Global Segment Descriptor } */
236 /* 0x00 */ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
237 /* 0x08 */ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
238 /* 0x10 */ {{0xFFFF, 0, 0, 0xB, 1, 0, 1, 0xF, 0, 0, 1, 1, 0}},
239 /* 0x18 */ {{0xFFFF, 0, 0, 0x3, 1, 0, 1, 0xF, 0, 0, 1, 1, 0}},
240 /* 0x20 */ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
241 /* 0x28 */ {{0xFFFF, 0, 0, 0xB, 1, 0, 1, 0xF, 0, 0, 0, 1, 0}},
242 /* 0x30 */ {{0xFFFF, 0, 0, 0x3, 1, 0, 1, 0xF, 0, 0, 0, 1, 0}},
243 /* 0x38 */ {{0xFFFF, 0, 0, 0xB, 1, 0, 1, 0xF, 0, 1, 0, 1, 0}},
244 /* 0x40 */ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
247 #define DATA_SEGEMENT_SELECTOR 0x18
252 GLOBAL_REMOVE_IF_UNREFERENCED CONST IA32_DESCRIPTOR mGdt
= {
253 sizeof (mGdtEntries
) - 1,
258 Performance measure function to get S3 detailed performance data.
260 This function will getS3 detailed performance data and saved in pre-reserved ACPI memory.
263 WriteToOsS3PerformanceData (
268 EFI_PHYSICAL_ADDRESS mAcpiLowMemoryBase
;
269 PERF_HEADER
*PerfHeader
;
273 EFI_PEI_READ_ONLY_VARIABLE2_PPI
*VariableServices
;
287 // Retrive time stamp count as early as possilbe
289 Ticker
= GetPerformanceCounter ();
291 Freq
= GetPerformanceCounterProperties (&StartValue
, &EndValue
);
293 Freq
= DivU64x32 (Freq
, 1000);
295 Status
= PeiServicesLocatePpi (
296 &gEfiPeiReadOnlyVariable2PpiGuid
,
299 (VOID
**) &VariableServices
301 if (EFI_ERROR (Status
)) {
305 VarSize
= sizeof (EFI_PHYSICAL_ADDRESS
);
306 Status
= VariableServices
->GetVariable (
309 &gPerformanceProtocolGuid
,
314 if (EFI_ERROR (Status
)) {
315 DEBUG ((EFI_D_ERROR
, "Fail to retrieve variable to log S3 performance data \n"));
319 PerfHeader
= (PERF_HEADER
*) (UINTN
) mAcpiLowMemoryBase
;
321 if (PerfHeader
->Signiture
!= PERFORMANCE_SIGNATURE
) {
322 DEBUG ((EFI_D_ERROR
, "Performance data in ACPI memory get corrupted! \n"));
327 // Record total S3 resume time.
329 if (EndValue
>= StartValue
) {
330 PerfHeader
->S3Resume
= Ticker
- StartValue
;
333 PerfHeader
->S3Resume
= StartValue
- Ticker
;
338 // Get S3 detailed performance data
342 while ((LogEntryKey
= GetPerformanceMeasurement (
349 if (EndTicker
!= 0) {
350 PerfData
= &PerfHeader
->S3Entry
[Index
];
353 // Use File Handle to specify the different performance log for PEIM.
354 // File Handle is the base address of PEIM FFS file.
356 if ((AsciiStrnCmp (Token
, "PEIM", PEI_PERFORMANCE_STRING_SIZE
) == 0) && (Handle
!= NULL
)) {
357 AsciiSPrint (PerfData
->Token
, PERF_TOKEN_LENGTH
, "0x%11p", Handle
);
359 AsciiStrnCpy (PerfData
->Token
, Token
, PERF_TOKEN_LENGTH
);
361 if (StartTicker
== 1) {
362 StartTicker
= StartValue
;
364 if (EndTicker
== 1) {
365 EndTicker
= StartValue
;
367 Ticker
= CountUp
? (EndTicker
- StartTicker
) : (StartTicker
- EndTicker
);
368 PerfData
->Duration
= (UINT32
) DivU64x32 (Ticker
, (UINT32
) Freq
);
371 // Only Record > 1ms performance data so that more big performance can be recorded.
373 if ((Ticker
> Freq
) && (++Index
>= PERF_PEI_ENTRY_MAX_NUM
)) {
375 // Reach the maximum number of PEI performance log entries.
381 PerfHeader
->S3EntryNum
= (UINT32
) Index
;
385 The function will check if current waking vector is long mode.
387 @param AcpiS3Context a pointer to a structure of ACPI_S3_CONTEXT
389 @retval TRUE Current context need long mode waking vector.
390 @retval FALSE Current context need not long mode waking vector.
393 IsLongModeWakingVector (
394 IN ACPI_S3_CONTEXT
*AcpiS3Context
397 EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*Facs
;
399 Facs
= (EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*) ((UINTN
) (AcpiS3Context
->AcpiFacsTable
));
400 if ((Facs
== NULL
) ||
401 (Facs
->Signature
!= EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE
) ||
402 ((Facs
->FirmwareWakingVector
== 0) && (Facs
->XFirmwareWakingVector
== 0)) ) {
403 // Something wrong with FACS
406 if (Facs
->XFirmwareWakingVector
!= 0) {
407 if ((Facs
->Version
== EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_VERSION
) &&
408 ((Facs
->Flags
& EFI_ACPI_4_0_64BIT_WAKE_SUPPORTED_F
) != 0) &&
409 ((Facs
->Flags
& EFI_ACPI_4_0_OSPM_64BIT_WAKE__F
) != 0)) {
410 // Both BIOS and OS wants 64bit vector
411 if (FeaturePcdGet (PcdDxeIplSwitchToLongMode
)) {
420 Jump to OS waking vector.
421 The function will install boot script done PPI, report S3 resume status code, and then jump to OS waking vector.
423 @param AcpiS3Context a pointer to a structure of ACPI_S3_CONTEXT
424 @param PeiS3ResumeState a pointer to a structure of PEI_S3_RESUME_STATE
429 IN ACPI_S3_CONTEXT
*AcpiS3Context
,
430 IN PEI_S3_RESUME_STATE
*PeiS3ResumeState
434 EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*Facs
;
435 ASM_TRANSFER_CONTROL AsmTransferControl
;
437 UINTN TempStack
[0x10];
442 AsmWriteIdtr (&PeiS3ResumeState
->Idtr
);
444 if (PeiS3ResumeState
->ReturnStatus
!= EFI_SUCCESS
) {
446 // Report Status code that boot script execution is failed
449 EFI_ERROR_CODE
| EFI_ERROR_MINOR
,
450 (EFI_SOFTWARE_PEI_MODULE
| EFI_SW_PEI_EC_S3_BOOT_SCRIPT_ERROR
)
455 // NOTE: Because Debug Timer interrupt and system interrupts will be disabled
456 // in BootScriptExecuteDxe, the rest code in S3ResumeBootOs() cannot be halted
460 PERF_END (NULL
, "ScriptExec", NULL
, 0);
463 // Install BootScriptDonePpi
465 Status
= PeiServicesInstallPpi (&mPpiListPostScriptTable
);
466 ASSERT_EFI_ERROR (Status
);
469 // Get ACPI Table Address
471 Facs
= (EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*) ((UINTN
) (AcpiS3Context
->AcpiFacsTable
));
473 if ((Facs
== NULL
) ||
474 (Facs
->Signature
!= EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE
) ||
475 ((Facs
->FirmwareWakingVector
== 0) && (Facs
->XFirmwareWakingVector
== 0)) ) {
477 // Report Status code that no valid vector is found
480 EFI_ERROR_CODE
| EFI_ERROR_MAJOR
,
481 (EFI_SOFTWARE_PEI_MODULE
| EFI_SW_PEI_EC_S3_OS_WAKE_ERROR
)
488 // Install EndOfPeiPpi
490 Status
= PeiServicesInstallPpi (&mPpiListEndOfPeiTable
);
491 ASSERT_EFI_ERROR (Status
);
494 // report status code on S3 resume
496 REPORT_STATUS_CODE (EFI_PROGRESS_CODE
, EFI_SOFTWARE_PEI_MODULE
| EFI_SW_PEI_PC_OS_WAKE
);
499 WriteToOsS3PerformanceData ();
502 AsmTransferControl
= (ASM_TRANSFER_CONTROL
)(UINTN
)PeiS3ResumeState
->AsmTransferControl
;
503 if (Facs
->XFirmwareWakingVector
!= 0) {
505 // Switch to native waking vector
507 TempStackTop
= (UINTN
)&TempStack
+ sizeof(TempStack
);
508 if ((Facs
->Version
== EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_VERSION
) &&
509 ((Facs
->Flags
& EFI_ACPI_4_0_64BIT_WAKE_SUPPORTED_F
) != 0) &&
510 ((Facs
->Flags
& EFI_ACPI_4_0_OSPM_64BIT_WAKE__F
) != 0)) {
512 // X64 long mode waking vector
514 DEBUG (( EFI_D_ERROR
, "Transfer to 64bit OS waking vector - %x\r\n", (UINTN
)Facs
->XFirmwareWakingVector
));
515 if (FeaturePcdGet (PcdDxeIplSwitchToLongMode
)) {
518 Facs
->XFirmwareWakingVector
,
521 (UINT64
)(UINTN
)TempStackTop
525 // Report Status code that no valid waking vector is found
528 EFI_ERROR_CODE
| EFI_ERROR_MAJOR
,
529 (EFI_SOFTWARE_PEI_MODULE
| EFI_SW_PEI_EC_S3_OS_WAKE_ERROR
)
531 DEBUG (( EFI_D_ERROR
, "Unsupported for 32bit DXE transfer to 64bit OS waking vector!\r\n"));
538 // IA32 protected mode waking vector (Page disabled)
540 DEBUG (( EFI_D_ERROR
, "Transfer to 32bit OS waking vector - %x\r\n", (UINTN
)Facs
->XFirmwareWakingVector
));
542 (SWITCH_STACK_ENTRY_POINT
) (UINTN
) Facs
->XFirmwareWakingVector
,
545 (VOID
*)(UINTN
)TempStackTop
550 // 16bit Realmode waking vector
552 DEBUG (( EFI_D_ERROR
, "Transfer to 16bit OS waking vector - %x\r\n", (UINTN
)Facs
->FirmwareWakingVector
));
553 AsmTransferControl (Facs
->FirmwareWakingVector
, 0x0);
557 // Report Status code the failure of S3Resume
560 EFI_ERROR_CODE
| EFI_ERROR_MAJOR
,
561 (EFI_SOFTWARE_PEI_MODULE
| EFI_SW_PEI_EC_S3_OS_WAKE_ERROR
)
571 Restore S3 page table because we do not trust ACPINvs content.
572 If BootScriptExector driver will not run in 64-bit mode, this function will do nothing.
574 @param S3NvsPageTableAddress PageTableAddress in ACPINvs
575 @param Build4GPageTableOnly If BIOS just build 4G page table only
578 RestoreS3PageTables (
579 IN UINTN S3NvsPageTableAddress
,
580 IN BOOLEAN Build4GPageTableOnly
583 if (FeaturePcdGet (PcdDxeIplSwitchToLongMode
)) {
586 UINT8 PhysicalAddressBits
;
587 EFI_PHYSICAL_ADDRESS PageAddress
;
588 UINTN IndexOfPml4Entries
;
589 UINTN IndexOfPdpEntries
;
590 UINTN IndexOfPageDirectoryEntries
;
591 UINT32 NumberOfPml4EntriesNeeded
;
592 UINT32 NumberOfPdpEntriesNeeded
;
593 PAGE_MAP_AND_DIRECTORY_POINTER
*PageMapLevel4Entry
;
594 PAGE_MAP_AND_DIRECTORY_POINTER
*PageMap
;
595 PAGE_MAP_AND_DIRECTORY_POINTER
*PageDirectoryPointerEntry
;
596 PAGE_TABLE_ENTRY
*PageDirectoryEntry
;
598 BOOLEAN Page1GSupport
;
599 PAGE_TABLE_1G_ENTRY
*PageDirectory1GEntry
;
602 // NOTE: We have to ASSUME the page table generation format, because we do not know whole page table information.
603 // The whole page table is too large to be saved in SMRAM.
605 // The assumption is : whole page table is allocated in CONTINOUS memory and CR3 points to TOP page.
607 DEBUG ((EFI_D_ERROR
, "S3NvsPageTableAddress - %x (%x)\n", (UINTN
)S3NvsPageTableAddress
, (UINTN
)Build4GPageTableOnly
));
610 // By architecture only one PageMapLevel4 exists - so lets allocate storgage for it.
612 PageMap
= (PAGE_MAP_AND_DIRECTORY_POINTER
*)S3NvsPageTableAddress
;
613 S3NvsPageTableAddress
+= SIZE_4KB
;
615 Page1GSupport
= FALSE
;
616 if (PcdGetBool(PcdUse1GPageTable
)) {
617 AsmCpuid (0x80000000, &RegEax
, NULL
, NULL
, NULL
);
618 if (RegEax
>= 0x80000001) {
619 AsmCpuid (0x80000001, NULL
, NULL
, NULL
, &RegEdx
);
620 if ((RegEdx
& BIT26
) != 0) {
621 Page1GSupport
= TRUE
;
627 // Get physical address bits supported.
629 Hob
= GetFirstHob (EFI_HOB_TYPE_CPU
);
631 PhysicalAddressBits
= ((EFI_HOB_CPU
*) Hob
)->SizeOfMemorySpace
;
633 AsmCpuid (0x80000000, &RegEax
, NULL
, NULL
, NULL
);
634 if (RegEax
>= 0x80000008) {
635 AsmCpuid (0x80000008, &RegEax
, NULL
, NULL
, NULL
);
636 PhysicalAddressBits
= (UINT8
) RegEax
;
638 PhysicalAddressBits
= 36;
643 // IA-32e paging translates 48-bit linear addresses to 52-bit physical addresses.
645 ASSERT (PhysicalAddressBits
<= 52);
646 if (PhysicalAddressBits
> 48) {
647 PhysicalAddressBits
= 48;
651 // NOTE: In order to save time to create full page table, we just create 4G page table by default.
652 // And let PF handler in BootScript driver to create more on request.
654 if (Build4GPageTableOnly
) {
655 PhysicalAddressBits
= 32;
656 ZeroMem (PageMap
, EFI_PAGES_TO_SIZE(2));
659 // Calculate the table entries needed.
661 if (PhysicalAddressBits
<= 39) {
662 NumberOfPml4EntriesNeeded
= 1;
663 NumberOfPdpEntriesNeeded
= (UINT32
)LShiftU64 (1, (PhysicalAddressBits
- 30));
665 NumberOfPml4EntriesNeeded
= (UINT32
)LShiftU64 (1, (PhysicalAddressBits
- 39));
666 NumberOfPdpEntriesNeeded
= 512;
669 PageMapLevel4Entry
= PageMap
;
671 for (IndexOfPml4Entries
= 0; IndexOfPml4Entries
< NumberOfPml4EntriesNeeded
; IndexOfPml4Entries
++, PageMapLevel4Entry
++) {
673 // Each PML4 entry points to a page of Page Directory Pointer entires.
674 // So lets allocate space for them and fill them in in the IndexOfPdpEntries loop.
676 PageDirectoryPointerEntry
= (PAGE_MAP_AND_DIRECTORY_POINTER
*)S3NvsPageTableAddress
;
677 S3NvsPageTableAddress
+= SIZE_4KB
;
682 PageMapLevel4Entry
->Uint64
= (UINT64
)(UINTN
)PageDirectoryPointerEntry
;
683 PageMapLevel4Entry
->Bits
.ReadWrite
= 1;
684 PageMapLevel4Entry
->Bits
.Present
= 1;
687 PageDirectory1GEntry
= (VOID
*) PageDirectoryPointerEntry
;
689 for (IndexOfPageDirectoryEntries
= 0; IndexOfPageDirectoryEntries
< 512; IndexOfPageDirectoryEntries
++, PageDirectory1GEntry
++, PageAddress
+= SIZE_1GB
) {
691 // Fill in the Page Directory entries
693 PageDirectory1GEntry
->Uint64
= (UINT64
)PageAddress
;
694 PageDirectory1GEntry
->Bits
.ReadWrite
= 1;
695 PageDirectory1GEntry
->Bits
.Present
= 1;
696 PageDirectory1GEntry
->Bits
.MustBe1
= 1;
699 for (IndexOfPdpEntries
= 0; IndexOfPdpEntries
< NumberOfPdpEntriesNeeded
; IndexOfPdpEntries
++, PageDirectoryPointerEntry
++) {
701 // Each Directory Pointer entries points to a page of Page Directory entires.
702 // So allocate space for them and fill them in in the IndexOfPageDirectoryEntries loop.
704 PageDirectoryEntry
= (PAGE_TABLE_ENTRY
*)S3NvsPageTableAddress
;
705 S3NvsPageTableAddress
+= SIZE_4KB
;
708 // Fill in a Page Directory Pointer Entries
710 PageDirectoryPointerEntry
->Uint64
= (UINT64
)(UINTN
)PageDirectoryEntry
;
711 PageDirectoryPointerEntry
->Bits
.ReadWrite
= 1;
712 PageDirectoryPointerEntry
->Bits
.Present
= 1;
714 for (IndexOfPageDirectoryEntries
= 0; IndexOfPageDirectoryEntries
< 512; IndexOfPageDirectoryEntries
++, PageDirectoryEntry
++, PageAddress
+= SIZE_2MB
) {
716 // Fill in the Page Directory entries
718 PageDirectoryEntry
->Uint64
= (UINT64
)PageAddress
;
719 PageDirectoryEntry
->Bits
.ReadWrite
= 1;
720 PageDirectoryEntry
->Bits
.Present
= 1;
721 PageDirectoryEntry
->Bits
.MustBe1
= 1;
729 // If DXE is running 32-bit mode, no need to establish page table.
736 Jump to boot script executor driver.
738 The function will close and lock SMRAM and then jump to boot script execute driver to executing S3 boot script table.
740 @param AcpiS3Context a pointer to a structure of ACPI_S3_CONTEXT
741 @param EfiBootScriptExecutorVariable The function entry to executing S3 boot Script table. This function is build in
742 boot script execute driver
746 S3ResumeExecuteBootScript (
747 IN ACPI_S3_CONTEXT
*AcpiS3Context
,
748 IN BOOT_SCRIPT_EXECUTOR_VARIABLE
*EfiBootScriptExecutorVariable
752 PEI_SMM_ACCESS_PPI
*SmmAccess
;
755 IA32_DESCRIPTOR
*IdtDescriptor
;
757 PEI_S3_RESUME_STATE
*PeiS3ResumeState
;
758 BOOLEAN InterruptStatus
;
760 DEBUG ((EFI_D_ERROR
, "S3ResumeExecuteBootScript()\n"));
763 // Attempt to use content from SMRAM first
765 GuidHob
= GetFirstGuidHob (&gEfiAcpiVariableGuid
);
766 if (GuidHob
!= NULL
) {
768 // Last step for SMM - send SMI for initialization
774 SendSmiIpiAllExcludingSelf ();
778 SendSmiIpi (GetApicId ());
780 Status
= PeiServicesLocatePpi (
781 &gPeiSmmAccessPpiGuid
,
786 if (!EFI_ERROR (Status
)) {
787 DEBUG ((EFI_D_ERROR
, "Close all SMRAM regions before executing boot script\n"));
789 for (Index
= 0, Status
= EFI_SUCCESS
; !EFI_ERROR (Status
); Index
++) {
790 Status
= SmmAccess
->Close ((EFI_PEI_SERVICES
**)GetPeiServicesTablePointer (), SmmAccess
, Index
);
793 DEBUG ((EFI_D_ERROR
, "Lock all SMRAM regions before executing boot script\n"));
795 for (Index
= 0, Status
= EFI_SUCCESS
; !EFI_ERROR (Status
); Index
++) {
796 Status
= SmmAccess
->Lock ((EFI_PEI_SERVICES
**)GetPeiServicesTablePointer (), SmmAccess
, Index
);
801 if (FeaturePcdGet (PcdDxeIplSwitchToLongMode
)) {
802 AsmWriteCr3 ((UINTN
)AcpiS3Context
->S3NvsPageTableAddress
);
805 if (FeaturePcdGet (PcdFrameworkCompatibilitySupport
)) {
807 // On some platform, such as ECP, a dispatch node in boot script table may execute a 32-bit PEIM which may need PeiServices
808 // pointer. So PeiServices need preserve in (IDTBase- sizeof (UINTN)).
810 IdtDescriptor
= (IA32_DESCRIPTOR
*) (UINTN
) (AcpiS3Context
->IdtrProfile
);
812 // Make sure the newly allcated IDT align with 16-bytes
814 IdtBuffer
= AllocatePages (EFI_SIZE_TO_PAGES((IdtDescriptor
->Limit
+ 1) + 16));
815 ASSERT (IdtBuffer
!= NULL
);
817 // Additional 16 bytes allocated to save IA32 IDT descriptor and Pei Service Table Pointer
818 // IA32 IDT descriptor will be used to setup IA32 IDT table for 32-bit Framework Boot Script code
820 ZeroMem (IdtBuffer
, 16);
821 AsmReadIdtr ((IA32_DESCRIPTOR
*)IdtBuffer
);
822 CopyMem ((VOID
*)((UINT8
*)IdtBuffer
+ 16),(VOID
*)(IdtDescriptor
->Base
), (IdtDescriptor
->Limit
+ 1));
823 IdtDescriptor
->Base
= (UINTN
)((UINT8
*)IdtBuffer
+ 16);
824 *(UINTN
*)(IdtDescriptor
->Base
- sizeof(UINTN
)) = (UINTN
)GetPeiServicesTablePointer ();
827 InterruptStatus
= SaveAndDisableInterrupts ();
829 // Need to make sure the GDT is loaded with values that support long mode and real mode.
831 AsmWriteGdtr (&mGdt
);
833 // update segment selectors per the new GDT.
835 AsmSetDataSelectors (DATA_SEGEMENT_SELECTOR
);
837 // Restore interrupt state.
839 SetInterruptState (InterruptStatus
);
842 // Prepare data for return back
844 PeiS3ResumeState
= AllocatePool (sizeof(*PeiS3ResumeState
));
845 ASSERT (PeiS3ResumeState
!= NULL
);
846 DEBUG (( EFI_D_ERROR
, "PeiS3ResumeState - %x\r\n", PeiS3ResumeState
));
847 PeiS3ResumeState
->ReturnCs
= 0x10;
848 PeiS3ResumeState
->ReturnEntryPoint
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)S3ResumeBootOs
;
849 PeiS3ResumeState
->ReturnStackPointer
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)&Status
;
853 AsmReadIdtr (&PeiS3ResumeState
->Idtr
);
856 // Report Status Code to indicate S3 boot script execution
858 REPORT_STATUS_CODE (EFI_PROGRESS_CODE
, EFI_SOFTWARE_PEI_MODULE
| EFI_SW_PEI_PC_S3_BOOT_SCRIPT
);
860 PERF_START (NULL
, "ScriptExec", NULL
, 0);
862 if (FeaturePcdGet (PcdDxeIplSwitchToLongMode
)) {
866 DEBUG (( EFI_D_ERROR
, "Enable X64 and transfer control to Standalone Boot Script Executor\r\n"));
869 // Switch to long mode to complete resume.
873 EfiBootScriptExecutorVariable
->BootScriptExecutorEntrypoint
,
874 (UINT64
)(UINTN
)AcpiS3Context
,
875 (UINT64
)(UINTN
)PeiS3ResumeState
,
876 (UINT64
)(UINTN
)(AcpiS3Context
->BootScriptStackBase
+ AcpiS3Context
->BootScriptStackSize
)
882 DEBUG (( EFI_D_ERROR
, "transfer control to Standalone Boot Script Executor\r\n"));
884 (SWITCH_STACK_ENTRY_POINT
) (UINTN
) EfiBootScriptExecutorVariable
->BootScriptExecutorEntrypoint
,
885 (VOID
*)AcpiS3Context
,
886 (VOID
*)PeiS3ResumeState
,
887 (VOID
*)(UINTN
)(AcpiS3Context
->BootScriptStackBase
+ AcpiS3Context
->BootScriptStackSize
)
897 Restores the platform to its preboot configuration for an S3 resume and
898 jumps to the OS waking vector.
900 This function will restore the platform to its pre-boot configuration that was
901 pre-stored in the boot script table and transfer control to OS waking vector.
902 Upon invocation, this function is responsible for locating the following
903 information before jumping to OS waking vector:
906 - any other information that it needs
908 The S3RestoreConfig() function then executes the pre-stored boot script table
909 and transitions the platform to the pre-boot state. The boot script is recorded
910 during regular boot using the EFI_S3_SAVE_STATE_PROTOCOL.Write() and
911 EFI_S3_SMM_SAVE_STATE_PROTOCOL.Write() functions. Finally, this function
912 transfers control to the OS waking vector. If the OS supports only a real-mode
913 waking vector, this function will switch from flat mode to real mode before
914 jumping to the waking vector. If all platform pre-boot configurations are
915 successfully restored and all other necessary information is ready, this
916 function will never return and instead will directly jump to the OS waking
917 vector. If this function returns, it indicates that the attempt to resume
918 from the ACPI S3 sleep state failed.
920 @param[in] This Pointer to this instance of the PEI_S3_RESUME_PPI
922 @retval EFI_ABORTED Execution of the S3 resume boot script table failed.
923 @retval EFI_NOT_FOUND Some necessary information that is used for the S3
924 resume boot path could not be located.
930 IN EFI_PEI_S3_RESUME2_PPI
*This
934 PEI_SMM_ACCESS_PPI
*SmmAccess
;
936 ACPI_S3_CONTEXT
*AcpiS3Context
;
937 EFI_PHYSICAL_ADDRESS TempEfiBootScriptExecutorVariable
;
938 EFI_PHYSICAL_ADDRESS TempAcpiS3Context
;
939 BOOT_SCRIPT_EXECUTOR_VARIABLE
*EfiBootScriptExecutorVariable
;
941 EFI_SMRAM_DESCRIPTOR
*SmramDescriptor
;
942 SMM_S3_RESUME_STATE
*SmmS3ResumeState
;
944 BOOLEAN Build4GPageTableOnly
;
945 BOOLEAN InterruptStatus
;
947 TempAcpiS3Context
= 0;
948 TempEfiBootScriptExecutorVariable
= 0;
950 DEBUG ((EFI_D_ERROR
, "Enter S3 PEIM\r\n"));
952 VarSize
= sizeof (EFI_PHYSICAL_ADDRESS
);
953 Status
= RestoreLockBox (
954 &gEfiAcpiVariableGuid
,
958 ASSERT_EFI_ERROR (Status
);
960 Status
= RestoreLockBox (
961 &gEfiAcpiS3ContextGuid
,
965 ASSERT_EFI_ERROR (Status
);
967 AcpiS3Context
= (ACPI_S3_CONTEXT
*)(UINTN
)TempAcpiS3Context
;
968 ASSERT (AcpiS3Context
!= NULL
);
970 VarSize
= sizeof (EFI_PHYSICAL_ADDRESS
);
971 Status
= RestoreLockBox (
972 &gEfiBootScriptExecutorVariableGuid
,
973 &TempEfiBootScriptExecutorVariable
,
976 ASSERT_EFI_ERROR (Status
);
978 Status
= RestoreLockBox (
979 &gEfiBootScriptExecutorContextGuid
,
983 ASSERT_EFI_ERROR (Status
);
985 EfiBootScriptExecutorVariable
= (BOOT_SCRIPT_EXECUTOR_VARIABLE
*) (UINTN
) TempEfiBootScriptExecutorVariable
;
986 ASSERT (EfiBootScriptExecutorVariable
!= NULL
);
988 DEBUG (( EFI_D_ERROR
, "AcpiS3Context = %x\n", AcpiS3Context
));
989 DEBUG (( EFI_D_ERROR
, "Waking Vector = %x\n", ((EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*) ((UINTN
) (AcpiS3Context
->AcpiFacsTable
)))->FirmwareWakingVector
));
990 DEBUG (( EFI_D_ERROR
, "AcpiS3Context->AcpiFacsTable = %x\n", AcpiS3Context
->AcpiFacsTable
));
991 DEBUG (( EFI_D_ERROR
, "AcpiS3Context->S3NvsPageTableAddress = %x\n", AcpiS3Context
->S3NvsPageTableAddress
));
992 DEBUG (( EFI_D_ERROR
, "AcpiS3Context->S3DebugBufferAddress = %x\n", AcpiS3Context
->S3DebugBufferAddress
));
993 DEBUG (( EFI_D_ERROR
, "EfiBootScriptExecutorVariable->BootScriptExecutorEntrypoint = %x\n", EfiBootScriptExecutorVariable
->BootScriptExecutorEntrypoint
));
996 // Additional step for BootScript integrity - we only handle BootScript and BootScriptExecutor.
997 // Script dispatch image and context (parameter) are handled by platform.
998 // We just use restore all lock box in place, no need restore one by one.
1000 Status
= RestoreAllLockBoxInPlace ();
1001 ASSERT_EFI_ERROR (Status
);
1002 if (EFI_ERROR (Status
)) {
1007 if (FeaturePcdGet (PcdDxeIplSwitchToLongMode
)) {
1009 // Need reconstruct page table here, since we do not trust ACPINvs.
1011 if (IsLongModeWakingVector (AcpiS3Context
)) {
1012 Build4GPageTableOnly
= FALSE
;
1014 Build4GPageTableOnly
= TRUE
;
1016 RestoreS3PageTables ((UINTN
)AcpiS3Context
->S3NvsPageTableAddress
, Build4GPageTableOnly
);
1020 // Attempt to use content from SMRAM first
1022 GuidHob
= GetFirstGuidHob (&gEfiAcpiVariableGuid
);
1023 if (GuidHob
!= NULL
) {
1024 Status
= PeiServicesLocatePpi (
1025 &gPeiSmmAccessPpiGuid
,
1028 (VOID
**) &SmmAccess
1030 for (Index
= 0; !EFI_ERROR (Status
); Index
++) {
1031 Status
= SmmAccess
->Open ((EFI_PEI_SERVICES
**)GetPeiServicesTablePointer (), SmmAccess
, Index
);
1034 SmramDescriptor
= (EFI_SMRAM_DESCRIPTOR
*) GET_GUID_HOB_DATA (GuidHob
);
1035 SmmS3ResumeState
= (SMM_S3_RESUME_STATE
*)(UINTN
)SmramDescriptor
->CpuStart
;
1037 SmmS3ResumeState
->ReturnCs
= AsmReadCs ();
1038 SmmS3ResumeState
->ReturnEntryPoint
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)S3ResumeExecuteBootScript
;
1039 SmmS3ResumeState
->ReturnContext1
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)AcpiS3Context
;
1040 SmmS3ResumeState
->ReturnContext2
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)EfiBootScriptExecutorVariable
;
1041 SmmS3ResumeState
->ReturnStackPointer
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)&Status
;
1043 DEBUG (( EFI_D_ERROR
, "SMM S3 Signature = %x\n", SmmS3ResumeState
->Signature
));
1044 DEBUG (( EFI_D_ERROR
, "SMM S3 Stack Base = %x\n", SmmS3ResumeState
->SmmS3StackBase
));
1045 DEBUG (( EFI_D_ERROR
, "SMM S3 Stack Size = %x\n", SmmS3ResumeState
->SmmS3StackSize
));
1046 DEBUG (( EFI_D_ERROR
, "SMM S3 Resume Entry Point = %x\n", SmmS3ResumeState
->SmmS3ResumeEntryPoint
));
1047 DEBUG (( EFI_D_ERROR
, "SMM S3 CR0 = %x\n", SmmS3ResumeState
->SmmS3Cr0
));
1048 DEBUG (( EFI_D_ERROR
, "SMM S3 CR3 = %x\n", SmmS3ResumeState
->SmmS3Cr3
));
1049 DEBUG (( EFI_D_ERROR
, "SMM S3 CR4 = %x\n", SmmS3ResumeState
->SmmS3Cr4
));
1050 DEBUG (( EFI_D_ERROR
, "SMM S3 Return CS = %x\n", SmmS3ResumeState
->ReturnCs
));
1051 DEBUG (( EFI_D_ERROR
, "SMM S3 Return Entry Point = %x\n", SmmS3ResumeState
->ReturnEntryPoint
));
1052 DEBUG (( EFI_D_ERROR
, "SMM S3 Return Context1 = %x\n", SmmS3ResumeState
->ReturnContext1
));
1053 DEBUG (( EFI_D_ERROR
, "SMM S3 Return Context2 = %x\n", SmmS3ResumeState
->ReturnContext2
));
1054 DEBUG (( EFI_D_ERROR
, "SMM S3 Return Stack Pointer = %x\n", SmmS3ResumeState
->ReturnStackPointer
));
1055 DEBUG (( EFI_D_ERROR
, "SMM S3 Smst = %x\n", SmmS3ResumeState
->Smst
));
1057 if (SmmS3ResumeState
->Signature
== SMM_S3_RESUME_SMM_32
) {
1059 (SWITCH_STACK_ENTRY_POINT
)(UINTN
)SmmS3ResumeState
->SmmS3ResumeEntryPoint
,
1060 (VOID
*)AcpiS3Context
,
1062 (VOID
*)(UINTN
)(SmmS3ResumeState
->SmmS3StackBase
+ SmmS3ResumeState
->SmmS3StackSize
)
1065 if (SmmS3ResumeState
->Signature
== SMM_S3_RESUME_SMM_64
) {
1067 // Switch to long mode to complete resume.
1070 InterruptStatus
= SaveAndDisableInterrupts ();
1072 // Need to make sure the GDT is loaded with values that support long mode and real mode.
1074 AsmWriteGdtr (&mGdt
);
1076 // update segment selectors per the new GDT.
1078 AsmSetDataSelectors (DATA_SEGEMENT_SELECTOR
);
1080 // Restore interrupt state.
1082 SetInterruptState (InterruptStatus
);
1084 AsmWriteCr3 ((UINTN
)SmmS3ResumeState
->SmmS3Cr3
);
1087 // Disable interrupt of Debug timer, since IDT table cannot work in long mode.
1088 // NOTE: On x64 platforms, because DisablePaging64() will disable interrupts,
1089 // the code in S3ResumeExecuteBootScript() cannot be halted by soft debugger.
1091 SaveAndSetDebugTimerInterrupt (FALSE
);
1095 SmmS3ResumeState
->SmmS3ResumeEntryPoint
,
1096 (UINT64
)(UINTN
)AcpiS3Context
,
1098 SmmS3ResumeState
->SmmS3StackBase
+ SmmS3ResumeState
->SmmS3StackSize
1104 S3ResumeExecuteBootScript (AcpiS3Context
, EfiBootScriptExecutorVariable
);
1108 Main entry for S3 Resume PEIM.
1110 This routine is to install EFI_PEI_S3_RESUME2_PPI.
1112 @param FileHandle Handle of the file being invoked.
1113 @param PeiServices Pointer to PEI Services table.
1115 @retval EFI_SUCCESS S3Resume Ppi is installed successfully.
1120 PeimS3ResumeEntryPoint (
1121 IN EFI_PEI_FILE_HANDLE FileHandle
,
1122 IN CONST EFI_PEI_SERVICES
**PeiServices
1128 // Install S3 Resume Ppi
1130 Status
= (**PeiServices
).InstallPpi (PeiServices
, &mPpiList
);
1131 ASSERT_EFI_ERROR (Status
);