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 PERF_END (NULL
, "ScriptExec", NULL
, 0);
447 // Install BootScriptDonePpi
449 Status
= PeiServicesInstallPpi (&mPpiListPostScriptTable
);
450 ASSERT_EFI_ERROR (Status
);
453 // Get ACPI Table Address
455 Facs
= (EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*) ((UINTN
) (AcpiS3Context
->AcpiFacsTable
));
457 if ((Facs
== NULL
) ||
458 (Facs
->Signature
!= EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE
) ||
459 ((Facs
->FirmwareWakingVector
== 0) && (Facs
->XFirmwareWakingVector
== 0)) ) {
465 // Install EndOfPeiPpi
467 Status
= PeiServicesInstallPpi (&mPpiListEndOfPeiTable
);
468 ASSERT_EFI_ERROR (Status
);
471 // report status code on S3 resume
473 REPORT_STATUS_CODE (EFI_PROGRESS_CODE
, EFI_SOFTWARE_PEI_MODULE
| EFI_SW_PEI_PC_OS_WAKE
);
476 WriteToOsS3PerformanceData ();
479 AsmTransferControl
= (ASM_TRANSFER_CONTROL
)(UINTN
)PeiS3ResumeState
->AsmTransferControl
;
480 if (Facs
->XFirmwareWakingVector
!= 0) {
482 // Switch to native waking vector
484 TempStackTop
= (UINTN
)&TempStack
+ sizeof(TempStack
);
485 if ((Facs
->Version
== EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_VERSION
) &&
486 ((Facs
->Flags
& EFI_ACPI_4_0_64BIT_WAKE_SUPPORTED_F
) != 0) &&
487 ((Facs
->Flags
& EFI_ACPI_4_0_OSPM_64BIT_WAKE__F
) != 0)) {
489 // X64 long mode waking vector
491 DEBUG (( EFI_D_ERROR
, "Transfer to 64bit OS waking vector - %x\r\n", (UINTN
)Facs
->XFirmwareWakingVector
));
492 if (FeaturePcdGet (PcdDxeIplSwitchToLongMode
)) {
495 Facs
->XFirmwareWakingVector
,
498 (UINT64
)(UINTN
)TempStackTop
501 DEBUG (( EFI_D_ERROR
, "Unsupported for 32bit DXE transfer to 64bit OS waking vector!\r\n"));
506 // IA32 protected mode waking vector (Page disabled)
508 DEBUG (( EFI_D_ERROR
, "Transfer to 32bit OS waking vector - %x\r\n", (UINTN
)Facs
->XFirmwareWakingVector
));
510 (SWITCH_STACK_ENTRY_POINT
) (UINTN
) Facs
->XFirmwareWakingVector
,
513 (VOID
*)(UINTN
)TempStackTop
518 // 16bit Realmode waking vector
520 DEBUG (( EFI_D_ERROR
, "Transfer to 16bit OS waking vector - %x\r\n", (UINTN
)Facs
->FirmwareWakingVector
));
521 AsmTransferControl (Facs
->FirmwareWakingVector
, 0x0);
531 Restore S3 page table because we do not trust ACPINvs content.
532 If BootScriptExector driver will not run in 64-bit mode, this function will do nothing.
534 @param S3NvsPageTableAddress PageTableAddress in ACPINvs
535 @param Build4GPageTableOnly If BIOS just build 4G page table only
538 RestoreS3PageTables (
539 IN UINTN S3NvsPageTableAddress
,
540 IN BOOLEAN Build4GPageTableOnly
543 if (FeaturePcdGet (PcdDxeIplSwitchToLongMode
)) {
546 UINT8 PhysicalAddressBits
;
547 EFI_PHYSICAL_ADDRESS PageAddress
;
548 UINTN IndexOfPml4Entries
;
549 UINTN IndexOfPdpEntries
;
550 UINTN IndexOfPageDirectoryEntries
;
551 UINT32 NumberOfPml4EntriesNeeded
;
552 UINT32 NumberOfPdpEntriesNeeded
;
553 PAGE_MAP_AND_DIRECTORY_POINTER
*PageMapLevel4Entry
;
554 PAGE_MAP_AND_DIRECTORY_POINTER
*PageMap
;
555 PAGE_MAP_AND_DIRECTORY_POINTER
*PageDirectoryPointerEntry
;
556 PAGE_TABLE_ENTRY
*PageDirectoryEntry
;
558 BOOLEAN Page1GSupport
;
559 PAGE_TABLE_1G_ENTRY
*PageDirectory1GEntry
;
562 // NOTE: We have to ASSUME the page table generation format, because we do not know whole page table information.
563 // The whole page table is too large to be saved in SMRAM.
565 // The assumption is : whole page table is allocated in CONTINOUS memory and CR3 points to TOP page.
567 DEBUG ((EFI_D_ERROR
, "S3NvsPageTableAddress - %x (%x)\n", (UINTN
)S3NvsPageTableAddress
, (UINTN
)Build4GPageTableOnly
));
570 // By architecture only one PageMapLevel4 exists - so lets allocate storgage for it.
572 PageMap
= (PAGE_MAP_AND_DIRECTORY_POINTER
*)S3NvsPageTableAddress
;
573 S3NvsPageTableAddress
+= SIZE_4KB
;
575 Page1GSupport
= FALSE
;
576 if (PcdGetBool(PcdUse1GPageTable
)) {
577 AsmCpuid (0x80000000, &RegEax
, NULL
, NULL
, NULL
);
578 if (RegEax
>= 0x80000001) {
579 AsmCpuid (0x80000001, NULL
, NULL
, NULL
, &RegEdx
);
580 if ((RegEdx
& BIT26
) != 0) {
581 Page1GSupport
= TRUE
;
587 // Get physical address bits supported.
589 Hob
= GetFirstHob (EFI_HOB_TYPE_CPU
);
591 PhysicalAddressBits
= ((EFI_HOB_CPU
*) Hob
)->SizeOfMemorySpace
;
593 AsmCpuid (0x80000000, &RegEax
, NULL
, NULL
, NULL
);
594 if (RegEax
>= 0x80000008) {
595 AsmCpuid (0x80000008, &RegEax
, NULL
, NULL
, NULL
);
596 PhysicalAddressBits
= (UINT8
) RegEax
;
598 PhysicalAddressBits
= 36;
603 // IA-32e paging translates 48-bit linear addresses to 52-bit physical addresses.
605 ASSERT (PhysicalAddressBits
<= 52);
606 if (PhysicalAddressBits
> 48) {
607 PhysicalAddressBits
= 48;
611 // NOTE: In order to save time to create full page table, we just create 4G page table by default.
612 // And let PF handler in BootScript driver to create more on request.
614 if (Build4GPageTableOnly
) {
615 PhysicalAddressBits
= 32;
616 ZeroMem (PageMap
, EFI_PAGES_TO_SIZE(2));
619 // Calculate the table entries needed.
621 if (PhysicalAddressBits
<= 39) {
622 NumberOfPml4EntriesNeeded
= 1;
623 NumberOfPdpEntriesNeeded
= (UINT32
)LShiftU64 (1, (PhysicalAddressBits
- 30));
625 NumberOfPml4EntriesNeeded
= (UINT32
)LShiftU64 (1, (PhysicalAddressBits
- 39));
626 NumberOfPdpEntriesNeeded
= 512;
629 PageMapLevel4Entry
= PageMap
;
631 for (IndexOfPml4Entries
= 0; IndexOfPml4Entries
< NumberOfPml4EntriesNeeded
; IndexOfPml4Entries
++, PageMapLevel4Entry
++) {
633 // Each PML4 entry points to a page of Page Directory Pointer entires.
634 // So lets allocate space for them and fill them in in the IndexOfPdpEntries loop.
636 PageDirectoryPointerEntry
= (PAGE_MAP_AND_DIRECTORY_POINTER
*)S3NvsPageTableAddress
;
637 S3NvsPageTableAddress
+= SIZE_4KB
;
642 PageMapLevel4Entry
->Uint64
= (UINT64
)(UINTN
)PageDirectoryPointerEntry
;
643 PageMapLevel4Entry
->Bits
.ReadWrite
= 1;
644 PageMapLevel4Entry
->Bits
.Present
= 1;
647 PageDirectory1GEntry
= (VOID
*) PageDirectoryPointerEntry
;
649 for (IndexOfPageDirectoryEntries
= 0; IndexOfPageDirectoryEntries
< 512; IndexOfPageDirectoryEntries
++, PageDirectory1GEntry
++, PageAddress
+= SIZE_1GB
) {
651 // Fill in the Page Directory entries
653 PageDirectory1GEntry
->Uint64
= (UINT64
)PageAddress
;
654 PageDirectory1GEntry
->Bits
.ReadWrite
= 1;
655 PageDirectory1GEntry
->Bits
.Present
= 1;
656 PageDirectory1GEntry
->Bits
.MustBe1
= 1;
659 for (IndexOfPdpEntries
= 0; IndexOfPdpEntries
< NumberOfPdpEntriesNeeded
; IndexOfPdpEntries
++, PageDirectoryPointerEntry
++) {
661 // Each Directory Pointer entries points to a page of Page Directory entires.
662 // So allocate space for them and fill them in in the IndexOfPageDirectoryEntries loop.
664 PageDirectoryEntry
= (PAGE_TABLE_ENTRY
*)S3NvsPageTableAddress
;
665 S3NvsPageTableAddress
+= SIZE_4KB
;
668 // Fill in a Page Directory Pointer Entries
670 PageDirectoryPointerEntry
->Uint64
= (UINT64
)(UINTN
)PageDirectoryEntry
;
671 PageDirectoryPointerEntry
->Bits
.ReadWrite
= 1;
672 PageDirectoryPointerEntry
->Bits
.Present
= 1;
674 for (IndexOfPageDirectoryEntries
= 0; IndexOfPageDirectoryEntries
< 512; IndexOfPageDirectoryEntries
++, PageDirectoryEntry
++, PageAddress
+= SIZE_2MB
) {
676 // Fill in the Page Directory entries
678 PageDirectoryEntry
->Uint64
= (UINT64
)PageAddress
;
679 PageDirectoryEntry
->Bits
.ReadWrite
= 1;
680 PageDirectoryEntry
->Bits
.Present
= 1;
681 PageDirectoryEntry
->Bits
.MustBe1
= 1;
689 // If DXE is running 32-bit mode, no need to establish page table.
696 Jump to boot script executor driver.
698 The function will close and lock SMRAM and then jump to boot script execute driver to executing S3 boot script table.
700 @param AcpiS3Context a pointer to a structure of ACPI_S3_CONTEXT
701 @param EfiBootScriptExecutorVariable The function entry to executing S3 boot Script table. This function is build in
702 boot script execute driver
706 S3ResumeExecuteBootScript (
707 IN ACPI_S3_CONTEXT
*AcpiS3Context
,
708 IN BOOT_SCRIPT_EXECUTOR_VARIABLE
*EfiBootScriptExecutorVariable
712 PEI_SMM_ACCESS_PPI
*SmmAccess
;
715 IA32_DESCRIPTOR
*IdtDescriptor
;
717 PEI_S3_RESUME_STATE
*PeiS3ResumeState
;
718 BOOLEAN InterruptStatus
;
720 DEBUG ((EFI_D_ERROR
, "S3ResumeExecuteBootScript()\n"));
723 // Attempt to use content from SMRAM first
725 GuidHob
= GetFirstGuidHob (&gEfiAcpiVariableGuid
);
726 if (GuidHob
!= NULL
) {
728 // Last step for SMM - send SMI for initialization
734 SendSmiIpiAllExcludingSelf ();
738 SendSmiIpi (GetApicId ());
740 Status
= PeiServicesLocatePpi (
741 &gPeiSmmAccessPpiGuid
,
746 if (!EFI_ERROR (Status
)) {
747 DEBUG ((EFI_D_ERROR
, "Close all SMRAM regions before executing boot script\n"));
749 for (Index
= 0, Status
= EFI_SUCCESS
; !EFI_ERROR (Status
); Index
++) {
750 Status
= SmmAccess
->Close ((EFI_PEI_SERVICES
**)GetPeiServicesTablePointer (), SmmAccess
, Index
);
753 DEBUG ((EFI_D_ERROR
, "Lock all SMRAM regions before executing boot script\n"));
755 for (Index
= 0, Status
= EFI_SUCCESS
; !EFI_ERROR (Status
); Index
++) {
756 Status
= SmmAccess
->Lock ((EFI_PEI_SERVICES
**)GetPeiServicesTablePointer (), SmmAccess
, Index
);
761 if (FeaturePcdGet (PcdDxeIplSwitchToLongMode
)) {
762 AsmWriteCr3 ((UINTN
)AcpiS3Context
->S3NvsPageTableAddress
);
765 if (FeaturePcdGet (PcdFrameworkCompatibilitySupport
)) {
767 // On some platform, such as ECP, a dispatch node in boot script table may execute a 32-bit PEIM which may need PeiServices
768 // pointer. So PeiServices need preserve in (IDTBase- sizeof (UINTN)).
770 IdtDescriptor
= (IA32_DESCRIPTOR
*) (UINTN
) (AcpiS3Context
->IdtrProfile
);
772 // Make sure the newly allcated IDT align with 16-bytes
774 IdtBuffer
= AllocatePages (EFI_SIZE_TO_PAGES((IdtDescriptor
->Limit
+ 1) + 16));
775 ASSERT (IdtBuffer
!= NULL
);
777 // Additional 16 bytes allocated to save IA32 IDT descriptor and Pei Service Table Pointer
778 // IA32 IDT descriptor will be used to setup IA32 IDT table for 32-bit Framework Boot Script code
780 ZeroMem (IdtBuffer
, 16);
781 AsmReadIdtr ((IA32_DESCRIPTOR
*)IdtBuffer
);
782 CopyMem ((VOID
*)((UINT8
*)IdtBuffer
+ 16),(VOID
*)(IdtDescriptor
->Base
), (IdtDescriptor
->Limit
+ 1));
783 IdtDescriptor
->Base
= (UINTN
)((UINT8
*)IdtBuffer
+ 16);
784 *(UINTN
*)(IdtDescriptor
->Base
- sizeof(UINTN
)) = (UINTN
)GetPeiServicesTablePointer ();
787 InterruptStatus
= SaveAndDisableInterrupts ();
789 // Need to make sure the GDT is loaded with values that support long mode and real mode.
791 AsmWriteGdtr (&mGdt
);
793 // update segment selectors per the new GDT.
795 AsmSetDataSelectors (DATA_SEGEMENT_SELECTOR
);
797 // Restore interrupt state.
799 SetInterruptState (InterruptStatus
);
802 // Prepare data for return back
804 PeiS3ResumeState
= AllocatePool (sizeof(*PeiS3ResumeState
));
805 ASSERT (PeiS3ResumeState
!= NULL
);
806 DEBUG (( EFI_D_ERROR
, "PeiS3ResumeState - %x\r\n", PeiS3ResumeState
));
807 PeiS3ResumeState
->ReturnCs
= 0x10;
808 PeiS3ResumeState
->ReturnEntryPoint
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)S3ResumeBootOs
;
809 PeiS3ResumeState
->ReturnStackPointer
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)&Status
;
813 AsmReadIdtr (&PeiS3ResumeState
->Idtr
);
815 PERF_START (NULL
, "ScriptExec", NULL
, 0);
817 if (FeaturePcdGet (PcdDxeIplSwitchToLongMode
)) {
821 DEBUG (( EFI_D_ERROR
, "Enable X64 and transfer control to Standalone Boot Script Executor\r\n"));
824 // Switch to long mode to complete resume.
828 EfiBootScriptExecutorVariable
->BootScriptExecutorEntrypoint
,
829 (UINT64
)(UINTN
)AcpiS3Context
,
830 (UINT64
)(UINTN
)PeiS3ResumeState
,
831 (UINT64
)(UINTN
)(AcpiS3Context
->BootScriptStackBase
+ AcpiS3Context
->BootScriptStackSize
)
837 DEBUG (( EFI_D_ERROR
, "transfer control to Standalone Boot Script Executor\r\n"));
839 (SWITCH_STACK_ENTRY_POINT
) (UINTN
) EfiBootScriptExecutorVariable
->BootScriptExecutorEntrypoint
,
840 (VOID
*)AcpiS3Context
,
841 (VOID
*)PeiS3ResumeState
,
842 (VOID
*)(UINTN
)(AcpiS3Context
->BootScriptStackBase
+ AcpiS3Context
->BootScriptStackSize
)
852 Restores the platform to its preboot configuration for an S3 resume and
853 jumps to the OS waking vector.
855 This function will restore the platform to its pre-boot configuration that was
856 pre-stored in the boot script table and transfer control to OS waking vector.
857 Upon invocation, this function is responsible for locating the following
858 information before jumping to OS waking vector:
861 - any other information that it needs
863 The S3RestoreConfig() function then executes the pre-stored boot script table
864 and transitions the platform to the pre-boot state. The boot script is recorded
865 during regular boot using the EFI_S3_SAVE_STATE_PROTOCOL.Write() and
866 EFI_S3_SMM_SAVE_STATE_PROTOCOL.Write() functions. Finally, this function
867 transfers control to the OS waking vector. If the OS supports only a real-mode
868 waking vector, this function will switch from flat mode to real mode before
869 jumping to the waking vector. If all platform pre-boot configurations are
870 successfully restored and all other necessary information is ready, this
871 function will never return and instead will directly jump to the OS waking
872 vector. If this function returns, it indicates that the attempt to resume
873 from the ACPI S3 sleep state failed.
875 @param[in] This Pointer to this instance of the PEI_S3_RESUME_PPI
877 @retval EFI_ABORTED Execution of the S3 resume boot script table failed.
878 @retval EFI_NOT_FOUND Some necessary information that is used for the S3
879 resume boot path could not be located.
885 IN EFI_PEI_S3_RESUME2_PPI
*This
889 PEI_SMM_ACCESS_PPI
*SmmAccess
;
891 ACPI_S3_CONTEXT
*AcpiS3Context
;
892 EFI_PHYSICAL_ADDRESS TempEfiBootScriptExecutorVariable
;
893 EFI_PHYSICAL_ADDRESS TempAcpiS3Context
;
894 BOOT_SCRIPT_EXECUTOR_VARIABLE
*EfiBootScriptExecutorVariable
;
896 EFI_SMRAM_DESCRIPTOR
*SmramDescriptor
;
897 SMM_S3_RESUME_STATE
*SmmS3ResumeState
;
899 BOOLEAN Build4GPageTableOnly
;
900 BOOLEAN InterruptStatus
;
902 TempAcpiS3Context
= 0;
903 TempEfiBootScriptExecutorVariable
= 0;
905 DEBUG ((EFI_D_ERROR
, "Enter S3 PEIM\r\n"));
907 VarSize
= sizeof (EFI_PHYSICAL_ADDRESS
);
908 Status
= RestoreLockBox (
909 &gEfiAcpiVariableGuid
,
913 ASSERT_EFI_ERROR (Status
);
915 Status
= RestoreLockBox (
916 &gEfiAcpiS3ContextGuid
,
920 ASSERT_EFI_ERROR (Status
);
922 AcpiS3Context
= (ACPI_S3_CONTEXT
*)(UINTN
)TempAcpiS3Context
;
923 ASSERT (AcpiS3Context
!= NULL
);
925 VarSize
= sizeof (EFI_PHYSICAL_ADDRESS
);
926 Status
= RestoreLockBox (
927 &gEfiBootScriptExecutorVariableGuid
,
928 &TempEfiBootScriptExecutorVariable
,
931 ASSERT_EFI_ERROR (Status
);
933 Status
= RestoreLockBox (
934 &gEfiBootScriptExecutorContextGuid
,
938 ASSERT_EFI_ERROR (Status
);
940 EfiBootScriptExecutorVariable
= (BOOT_SCRIPT_EXECUTOR_VARIABLE
*) (UINTN
) TempEfiBootScriptExecutorVariable
;
941 ASSERT (EfiBootScriptExecutorVariable
!= NULL
);
943 DEBUG (( EFI_D_ERROR
, "AcpiS3Context = %x\n", AcpiS3Context
));
944 DEBUG (( EFI_D_ERROR
, "Waking Vector = %x\n", ((EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*) ((UINTN
) (AcpiS3Context
->AcpiFacsTable
)))->FirmwareWakingVector
));
945 DEBUG (( EFI_D_ERROR
, "AcpiS3Context->AcpiFacsTable = %x\n", AcpiS3Context
->AcpiFacsTable
));
946 DEBUG (( EFI_D_ERROR
, "AcpiS3Context->S3NvsPageTableAddress = %x\n", AcpiS3Context
->S3NvsPageTableAddress
));
947 DEBUG (( EFI_D_ERROR
, "AcpiS3Context->S3DebugBufferAddress = %x\n", AcpiS3Context
->S3DebugBufferAddress
));
948 DEBUG (( EFI_D_ERROR
, "EfiBootScriptExecutorVariable->BootScriptExecutorEntrypoint = %x\n", EfiBootScriptExecutorVariable
->BootScriptExecutorEntrypoint
));
951 // Additional step for BootScript integrity - we only handle BootScript and BootScriptExecutor.
952 // Script dispatch image and context (parameter) are handled by platform.
953 // We just use restore all lock box in place, no need restore one by one.
955 Status
= RestoreAllLockBoxInPlace ();
956 ASSERT_EFI_ERROR (Status
);
957 if (EFI_ERROR (Status
)) {
962 if (FeaturePcdGet (PcdDxeIplSwitchToLongMode
)) {
964 // Need reconstruct page table here, since we do not trust ACPINvs.
966 if (IsLongModeWakingVector (AcpiS3Context
)) {
967 Build4GPageTableOnly
= FALSE
;
969 Build4GPageTableOnly
= TRUE
;
971 RestoreS3PageTables ((UINTN
)AcpiS3Context
->S3NvsPageTableAddress
, Build4GPageTableOnly
);
975 // Attempt to use content from SMRAM first
977 GuidHob
= GetFirstGuidHob (&gEfiAcpiVariableGuid
);
978 if (GuidHob
!= NULL
) {
979 Status
= PeiServicesLocatePpi (
980 &gPeiSmmAccessPpiGuid
,
985 for (Index
= 0; !EFI_ERROR (Status
); Index
++) {
986 Status
= SmmAccess
->Open ((EFI_PEI_SERVICES
**)GetPeiServicesTablePointer (), SmmAccess
, Index
);
989 SmramDescriptor
= (EFI_SMRAM_DESCRIPTOR
*) GET_GUID_HOB_DATA (GuidHob
);
990 SmmS3ResumeState
= (SMM_S3_RESUME_STATE
*)(UINTN
)SmramDescriptor
->CpuStart
;
992 SmmS3ResumeState
->ReturnCs
= AsmReadCs ();
993 SmmS3ResumeState
->ReturnEntryPoint
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)S3ResumeExecuteBootScript
;
994 SmmS3ResumeState
->ReturnContext1
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)AcpiS3Context
;
995 SmmS3ResumeState
->ReturnContext2
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)EfiBootScriptExecutorVariable
;
996 SmmS3ResumeState
->ReturnStackPointer
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)&Status
;
998 DEBUG (( EFI_D_ERROR
, "SMM S3 Signature = %x\n", SmmS3ResumeState
->Signature
));
999 DEBUG (( EFI_D_ERROR
, "SMM S3 Stack Base = %x\n", SmmS3ResumeState
->SmmS3StackBase
));
1000 DEBUG (( EFI_D_ERROR
, "SMM S3 Stack Size = %x\n", SmmS3ResumeState
->SmmS3StackSize
));
1001 DEBUG (( EFI_D_ERROR
, "SMM S3 Resume Entry Point = %x\n", SmmS3ResumeState
->SmmS3ResumeEntryPoint
));
1002 DEBUG (( EFI_D_ERROR
, "SMM S3 CR0 = %x\n", SmmS3ResumeState
->SmmS3Cr0
));
1003 DEBUG (( EFI_D_ERROR
, "SMM S3 CR3 = %x\n", SmmS3ResumeState
->SmmS3Cr3
));
1004 DEBUG (( EFI_D_ERROR
, "SMM S3 CR4 = %x\n", SmmS3ResumeState
->SmmS3Cr4
));
1005 DEBUG (( EFI_D_ERROR
, "SMM S3 Return CS = %x\n", SmmS3ResumeState
->ReturnCs
));
1006 DEBUG (( EFI_D_ERROR
, "SMM S3 Return Entry Point = %x\n", SmmS3ResumeState
->ReturnEntryPoint
));
1007 DEBUG (( EFI_D_ERROR
, "SMM S3 Return Context1 = %x\n", SmmS3ResumeState
->ReturnContext1
));
1008 DEBUG (( EFI_D_ERROR
, "SMM S3 Return Context2 = %x\n", SmmS3ResumeState
->ReturnContext2
));
1009 DEBUG (( EFI_D_ERROR
, "SMM S3 Return Stack Pointer = %x\n", SmmS3ResumeState
->ReturnStackPointer
));
1010 DEBUG (( EFI_D_ERROR
, "SMM S3 Smst = %x\n", SmmS3ResumeState
->Smst
));
1013 // Disable interrupt of Debug timer.
1015 SaveAndSetDebugTimerInterrupt (FALSE
);
1017 if (SmmS3ResumeState
->Signature
== SMM_S3_RESUME_SMM_32
) {
1019 (SWITCH_STACK_ENTRY_POINT
)(UINTN
)SmmS3ResumeState
->SmmS3ResumeEntryPoint
,
1020 (VOID
*)AcpiS3Context
,
1022 (VOID
*)(UINTN
)(SmmS3ResumeState
->SmmS3StackBase
+ SmmS3ResumeState
->SmmS3StackSize
)
1025 if (SmmS3ResumeState
->Signature
== SMM_S3_RESUME_SMM_64
) {
1027 // Switch to long mode to complete resume.
1030 InterruptStatus
= SaveAndDisableInterrupts ();
1032 // Need to make sure the GDT is loaded with values that support long mode and real mode.
1034 AsmWriteGdtr (&mGdt
);
1036 // update segment selectors per the new GDT.
1038 AsmSetDataSelectors (DATA_SEGEMENT_SELECTOR
);
1040 // Restore interrupt state.
1042 SetInterruptState (InterruptStatus
);
1044 AsmWriteCr3 ((UINTN
)SmmS3ResumeState
->SmmS3Cr3
);
1047 SmmS3ResumeState
->SmmS3ResumeEntryPoint
,
1048 (UINT64
)(UINTN
)AcpiS3Context
,
1050 SmmS3ResumeState
->SmmS3StackBase
+ SmmS3ResumeState
->SmmS3StackSize
1056 S3ResumeExecuteBootScript (AcpiS3Context
, EfiBootScriptExecutorVariable
);
1060 Main entry for S3 Resume PEIM.
1062 This routine is to install EFI_PEI_S3_RESUME2_PPI.
1064 @param FileHandle Handle of the file being invoked.
1065 @param PeiServices Pointer to PEI Services table.
1067 @retval EFI_SUCCESS S3Resume Ppi is installed successfully.
1072 PeimS3ResumeEntryPoint (
1073 IN EFI_PEI_FILE_HANDLE FileHandle
,
1074 IN CONST EFI_PEI_SERVICES
**PeiServices
1080 // Install S3 Resume Ppi
1082 Status
= (**PeiServices
).InstallPpi (PeiServices
, &mPpiList
);
1083 ASSERT_EFI_ERROR (Status
);