--- /dev/null
+/** @file\r
+ This module produces the EFI_PEI_S3_RESUME_PPI.\r
+ This module works with StandAloneBootScriptExecutor to S3 resume to OS.\r
+ This module will excute the boot script saved during last boot and after that,\r
+ control is passed to OS waking up handler.\r
+\r
+ Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.<BR>\r
+\r
+ This program and the accompanying materials\r
+ are licensed and made available under the terms and conditions\r
+ of the BSD License which accompanies this distribution. The\r
+ full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include <PiPei.h>\r
+\r
+#include <Guid/AcpiS3Context.h>\r
+#include <Guid/BootScriptExecutorVariable.h>\r
+#include <Guid/Performance.h>\r
+#include <Ppi/ReadOnlyVariable2.h>\r
+#include <Ppi/S3Resume2.h>\r
+#include <Ppi/SmmAccess.h>\r
+#include <Ppi/PostBootScriptTable.h>\r
+#include <Ppi/EndOfPeiPhase.h>\r
+\r
+#include <Library/DebugLib.h>\r
+#include <Library/BaseLib.h>\r
+#include <Library/TimerLib.h>\r
+#include <Library/PeimEntryPoint.h>\r
+#include <Library/PeiServicesLib.h>\r
+#include <Library/HobLib.h>\r
+#include <Library/PerformanceLib.h>\r
+#include <Library/PeiServicesTablePointerLib.h>\r
+#include <Library/IoLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/PcdLib.h>\r
+#include <Library/DebugAgentLib.h>\r
+#include <Library/LocalApicLib.h>\r
+#include <Library/ReportStatusCodeLib.h>\r
+#include <Library/PrintLib.h>\r
+#include <Library/LockBoxLib.h>\r
+#include <IndustryStandard/Acpi.h>\r
+\r
+#pragma pack(1)\r
+typedef union {\r
+ struct {\r
+ UINT32 LimitLow : 16;\r
+ UINT32 BaseLow : 16;\r
+ UINT32 BaseMid : 8;\r
+ UINT32 Type : 4;\r
+ UINT32 System : 1;\r
+ UINT32 Dpl : 2;\r
+ UINT32 Present : 1;\r
+ UINT32 LimitHigh : 4;\r
+ UINT32 Software : 1;\r
+ UINT32 Reserved : 1;\r
+ UINT32 DefaultSize : 1;\r
+ UINT32 Granularity : 1;\r
+ UINT32 BaseHigh : 8;\r
+ } Bits;\r
+ UINT64 Uint64;\r
+} IA32_GDT;\r
+\r
+//\r
+// Page-Map Level-4 Offset (PML4) and\r
+// Page-Directory-Pointer Offset (PDPE) entries 4K & 2MB\r
+//\r
+typedef union {\r
+ struct {\r
+ UINT64 Present:1; // 0 = Not present in memory, 1 = Present in memory\r
+ UINT64 ReadWrite:1; // 0 = Read-Only, 1= Read/Write\r
+ UINT64 UserSupervisor:1; // 0 = Supervisor, 1=User\r
+ UINT64 WriteThrough:1; // 0 = Write-Back caching, 1=Write-Through caching\r
+ UINT64 CacheDisabled:1; // 0 = Cached, 1=Non-Cached\r
+ UINT64 Accessed:1; // 0 = Not accessed, 1 = Accessed (set by CPU)\r
+ UINT64 Reserved:1; // Reserved\r
+ UINT64 MustBeZero:2; // Must Be Zero\r
+ UINT64 Available:3; // Available for use by system software\r
+ UINT64 PageTableBaseAddress:40; // Page Table Base Address\r
+ UINT64 AvabilableHigh:11; // Available for use by system software\r
+ UINT64 Nx:1; // No Execute bit\r
+ } Bits;\r
+ UINT64 Uint64;\r
+} PAGE_MAP_AND_DIRECTORY_POINTER;\r
+\r
+//\r
+// Page Table Entry 2MB\r
+//\r
+typedef union {\r
+ struct {\r
+ UINT64 Present:1; // 0 = Not present in memory, 1 = Present in memory\r
+ UINT64 ReadWrite:1; // 0 = Read-Only, 1= Read/Write\r
+ UINT64 UserSupervisor:1; // 0 = Supervisor, 1=User\r
+ UINT64 WriteThrough:1; // 0 = Write-Back caching, 1=Write-Through caching\r
+ UINT64 CacheDisabled:1; // 0 = Cached, 1=Non-Cached\r
+ UINT64 Accessed:1; // 0 = Not accessed, 1 = Accessed (set by CPU)\r
+ UINT64 Dirty:1; // 0 = Not Dirty, 1 = written by processor on access to page\r
+ UINT64 MustBe1:1; // Must be 1 \r
+ UINT64 Global:1; // 0 = Not global page, 1 = global page TLB not cleared on CR3 write\r
+ UINT64 Available:3; // Available for use by system software\r
+ UINT64 PAT:1; //\r
+ UINT64 MustBeZero:8; // Must be zero;\r
+ UINT64 PageTableBaseAddress:31; // Page Table Base Address\r
+ UINT64 AvabilableHigh:11; // Available for use by system software\r
+ UINT64 Nx:1; // 0 = Execute Code, 1 = No Code Execution\r
+ } Bits;\r
+ UINT64 Uint64;\r
+} PAGE_TABLE_ENTRY;\r
+\r
+#pragma pack()\r
+\r
+//\r
+// Function prototypes\r
+//\r
+/**\r
+ a ASM function to transfer control to OS.\r
+ \r
+ @param S3WakingVector The S3 waking up vector saved in ACPI Facs table\r
+ @param AcpiLowMemoryBase a buffer under 1M which could be used during the transfer \r
+**/\r
+typedef\r
+VOID\r
+(EFIAPI *ASM_TRANSFER_CONTROL) (\r
+ IN UINT32 S3WakingVector,\r
+ IN UINT32 AcpiLowMemoryBase\r
+ );\r
+\r
+/**\r
+ Restores the platform to its preboot configuration for an S3 resume and\r
+ jumps to the OS waking vector.\r
+\r
+ This function will restore the platform to its pre-boot configuration that was\r
+ pre-stored in the boot script table and transfer control to OS waking vector.\r
+ Upon invocation, this function is responsible for locating the following\r
+ information before jumping to OS waking vector:\r
+ - ACPI tables\r
+ - boot script table\r
+ - any other information that it needs\r
+\r
+ The S3RestoreConfig() function then executes the pre-stored boot script table\r
+ and transitions the platform to the pre-boot state. The boot script is recorded\r
+ during regular boot using the EFI_S3_SAVE_STATE_PROTOCOL.Write() and\r
+ EFI_S3_SMM_SAVE_STATE_PROTOCOL.Write() functions. Finally, this function\r
+ transfers control to the OS waking vector. If the OS supports only a real-mode\r
+ waking vector, this function will switch from flat mode to real mode before\r
+ jumping to the waking vector. If all platform pre-boot configurations are\r
+ successfully restored and all other necessary information is ready, this\r
+ function will never return and instead will directly jump to the OS waking\r
+ vector. If this function returns, it indicates that the attempt to resume\r
+ from the ACPI S3 sleep state failed.\r
+\r
+ @param[in] This Pointer to this instance of the PEI_S3_RESUME_PPI\r
+\r
+ @retval EFI_ABORTED Execution of the S3 resume boot script table failed.\r
+ @retval EFI_NOT_FOUND Some necessary information that is used for the S3\r
+ resume boot path could not be located.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+S3RestoreConfig2 (\r
+ IN EFI_PEI_S3_RESUME2_PPI *This\r
+ );\r
+\r
+//\r
+// Globals\r
+//\r
+EFI_PEI_S3_RESUME2_PPI mS3ResumePpi = { S3RestoreConfig2 };\r
+\r
+EFI_PEI_PPI_DESCRIPTOR mPpiList = {\r
+ (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),\r
+ &gEfiPeiS3Resume2PpiGuid,\r
+ &mS3ResumePpi\r
+};\r
+\r
+EFI_PEI_PPI_DESCRIPTOR mPpiListPostScriptTable = {\r
+ (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),\r
+ &gPeiPostScriptTablePpiGuid,\r
+ 0\r
+};\r
+\r
+EFI_PEI_PPI_DESCRIPTOR mPpiListEndOfPeiTable = {\r
+ (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),\r
+ &gEfiEndOfPeiSignalPpiGuid,\r
+ 0\r
+};\r
+\r
+//\r
+// Global Descriptor Table (GDT)\r
+//\r
+GLOBAL_REMOVE_IF_UNREFERENCED IA32_GDT mGdtEntries[] = {\r
+/* selector { Global Segment Descriptor } */\r
+/* 0x00 */ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},\r
+/* 0x08 */ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},\r
+/* 0x10 */ {{0xFFFF, 0, 0, 0xB, 1, 0, 1, 0xF, 0, 0, 1, 1, 0}},\r
+/* 0x18 */ {{0xFFFF, 0, 0, 0x3, 1, 0, 1, 0xF, 0, 0, 1, 1, 0}},\r
+/* 0x20 */ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},\r
+/* 0x28 */ {{0xFFFF, 0, 0, 0xB, 1, 0, 1, 0xF, 0, 0, 0, 1, 0}},\r
+/* 0x30 */ {{0xFFFF, 0, 0, 0x3, 1, 0, 1, 0xF, 0, 0, 0, 1, 0}},\r
+/* 0x38 */ {{0xFFFF, 0, 0, 0xB, 1, 0, 1, 0xF, 0, 1, 0, 1, 0}},\r
+/* 0x40 */ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},\r
+};\r
+\r
+//\r
+// IA32 Gdt register\r
+//\r
+GLOBAL_REMOVE_IF_UNREFERENCED CONST IA32_DESCRIPTOR mGdt = {\r
+ sizeof (mGdtEntries) - 1,\r
+ (UINTN) mGdtEntries\r
+ };\r
+\r
+/**\r
+ Performance measure function to get S3 detailed performance data.\r
+\r
+ This function will getS3 detailed performance data and saved in pre-reserved ACPI memory.\r
+**/\r
+VOID\r
+WriteToOsS3PerformanceData (\r
+ VOID\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_PHYSICAL_ADDRESS mAcpiLowMemoryBase;\r
+ PERF_HEADER *PerfHeader;\r
+ PERF_DATA *PerfData;\r
+ UINT64 Ticker;\r
+ UINTN Index;\r
+ EFI_PEI_READ_ONLY_VARIABLE2_PPI *VariableServices;\r
+ UINTN VarSize;\r
+ UINTN LogEntryKey;\r
+ CONST VOID *Handle;\r
+ CONST CHAR8 *Token;\r
+ CONST CHAR8 *Module;\r
+ UINT64 StartTicker;\r
+ UINT64 EndTicker;\r
+ UINT64 StartValue;\r
+ UINT64 EndValue;\r
+ BOOLEAN CountUp;\r
+ UINT64 Freq;\r
+\r
+ //\r
+ // Retrive time stamp count as early as possilbe\r
+ //\r
+ Ticker = GetPerformanceCounter ();\r
+\r
+ Freq = GetPerformanceCounterProperties (&StartValue, &EndValue);\r
+\r
+ Freq = DivU64x32 (Freq, 1000);\r
+\r
+ Status = PeiServicesLocatePpi (\r
+ &gEfiPeiReadOnlyVariable2PpiGuid,\r
+ 0,\r
+ NULL,\r
+ (VOID **) &VariableServices\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ VarSize = sizeof (EFI_PHYSICAL_ADDRESS);\r
+ Status = VariableServices->GetVariable (\r
+ VariableServices,\r
+ L"PerfDataMemAddr",\r
+ &gPerformanceProtocolGuid,\r
+ NULL,\r
+ &VarSize,\r
+ &mAcpiLowMemoryBase\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((EFI_D_ERROR, "Fail to retrieve variable to log S3 performance data \n"));\r
+ return;\r
+ }\r
+\r
+ PerfHeader = (PERF_HEADER *) (UINTN) mAcpiLowMemoryBase;\r
+\r
+ if (PerfHeader->Signiture != PERFORMANCE_SIGNATURE) {\r
+ DEBUG ((EFI_D_ERROR, "Performance data in ACPI memory get corrupted! \n"));\r
+ return;\r
+ }\r
+\r
+ //\r
+ // Record total S3 resume time.\r
+ //\r
+ if (EndValue >= StartValue) {\r
+ PerfHeader->S3Resume = Ticker - StartValue;\r
+ CountUp = TRUE;\r
+ } else {\r
+ PerfHeader->S3Resume = StartValue - Ticker;\r
+ CountUp = FALSE;\r
+ }\r
+\r
+ //\r
+ // Get S3 detailed performance data\r
+ //\r
+ Index = 0;\r
+ LogEntryKey = 0;\r
+ while ((LogEntryKey = GetPerformanceMeasurement (\r
+ LogEntryKey,\r
+ &Handle,\r
+ &Token,\r
+ &Module,\r
+ &StartTicker,\r
+ &EndTicker)) != 0) {\r
+ if (EndTicker != 0) {\r
+ PerfData = &PerfHeader->S3Entry[Index];\r
+\r
+ //\r
+ // Use File Handle to specify the different performance log for PEIM.\r
+ // File Handle is the base address of PEIM FFS file.\r
+ //\r
+ if ((AsciiStrnCmp (Token, "PEIM", PEI_PERFORMANCE_STRING_SIZE) == 0) && (Handle != NULL)) {\r
+ AsciiSPrint (PerfData->Token, PERF_TOKEN_LENGTH, "0x%11p", Handle);\r
+ } else {\r
+ AsciiStrnCpy (PerfData->Token, Token, PERF_TOKEN_LENGTH);\r
+ }\r
+ if (StartTicker == 1) {\r
+ StartTicker = StartValue;\r
+ }\r
+ if (EndTicker == 1) {\r
+ EndTicker = StartValue;\r
+ }\r
+ Ticker = CountUp? (EndTicker - StartTicker) : (StartTicker - EndTicker);\r
+ PerfData->Duration = (UINT32) DivU64x32 (Ticker, (UINT32) Freq);\r
+\r
+ //\r
+ // Only Record > 1ms performance data so that more big performance can be recorded.\r
+ //\r
+ if ((Ticker > Freq) && (++Index >= PERF_PEI_ENTRY_MAX_NUM)) {\r
+ //\r
+ // Reach the maximum number of PEI performance log entries.\r
+ //\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ PerfHeader->S3EntryNum = (UINT32) Index;\r
+}\r
+\r
+/**\r
+ Jump to OS waking vector.\r
+ The function will install boot script done PPI, report S3 resume status code, and then jump to OS waking vector.\r
+\r
+ @param AcpiS3Context a pointer to a structure of ACPI_S3_CONTEXT\r
+ @param PeiS3ResumeState a pointer to a structure of PEI_S3_RESUME_STATE\r
+**/\r
+VOID\r
+EFIAPI\r
+S3ResumeBootOs (\r
+ IN ACPI_S3_CONTEXT *AcpiS3Context,\r
+ IN PEI_S3_RESUME_STATE *PeiS3ResumeState\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *Facs;\r
+ ASM_TRANSFER_CONTROL AsmTransferControl;\r
+ UINTN TempStackTop;\r
+ UINTN TempStack[0x10];\r
+\r
+ //\r
+ // Restore IDT\r
+ //\r
+ AsmWriteIdtr (&PeiS3ResumeState->Idtr);\r
+\r
+ //\r
+ // Install BootScriptDonePpi\r
+ //\r
+ Status = PeiServicesInstallPpi (&mPpiListPostScriptTable);\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ //\r
+ // Get ACPI Table Address\r
+ //\r
+ Facs = (EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *) ((UINTN) (AcpiS3Context->AcpiFacsTable));\r
+\r
+ if ((Facs == NULL) ||\r
+ (Facs->Signature != EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE) ||\r
+ ((Facs->FirmwareWakingVector == 0) && (Facs->XFirmwareWakingVector == 0)) ) {\r
+ CpuDeadLoop ();\r
+ return ;\r
+ }\r
+\r
+ //\r
+ // report status code on S3 resume\r
+ //\r
+ REPORT_STATUS_CODE (EFI_PROGRESS_CODE, EFI_SOFTWARE_PEI_MODULE | EFI_SW_PEI_PC_OS_WAKE);\r
+\r
+ //\r
+ // Install EndOfPeiPpi\r
+ //\r
+ Status = PeiServicesInstallPpi (&mPpiListEndOfPeiTable);\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ PERF_CODE (\r
+ WriteToOsS3PerformanceData ();\r
+ );\r
+\r
+ AsmTransferControl = (ASM_TRANSFER_CONTROL)(UINTN)PeiS3ResumeState->AsmTransferControl;\r
+ if (Facs->XFirmwareWakingVector != 0) {\r
+ //\r
+ // Switch to native waking vector\r
+ //\r
+ TempStackTop = (UINTN)&TempStack + sizeof(TempStack);\r
+ if ((Facs->Version == EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_VERSION) &&\r
+ ((Facs->Flags & EFI_ACPI_4_0_64BIT_WAKE_SUPPORTED_F) != 0) &&\r
+ ((Facs->Flags & EFI_ACPI_4_0_OSPM_64BIT_WAKE__F) != 0)) {\r
+ //\r
+ // X64 long mode waking vector\r
+ //\r
+ DEBUG (( EFI_D_ERROR, "Transfer to 64bit OS waking vector - %x\r\n", (UINTN)Facs->XFirmwareWakingVector));\r
+ if (FeaturePcdGet (PcdDxeIplSwitchToLongMode)) {\r
+ AsmEnablePaging64 (\r
+ 0x38,\r
+ Facs->XFirmwareWakingVector,\r
+ 0,\r
+ 0,\r
+ (UINT64)(UINTN)TempStackTop\r
+ );\r
+ } else {\r
+ DEBUG (( EFI_D_ERROR, "Unsupported for 32bit DXE transfer to 64bit OS waking vector!\r\n"));\r
+ ASSERT (FALSE);\r
+ }\r
+ } else {\r
+ //\r
+ // IA32 protected mode waking vector (Page disabled)\r
+ //\r
+ DEBUG (( EFI_D_ERROR, "Transfer to 32bit OS waking vector - %x\r\n", (UINTN)Facs->XFirmwareWakingVector));\r
+ SwitchStack (\r
+ (SWITCH_STACK_ENTRY_POINT) (UINTN) Facs->XFirmwareWakingVector,\r
+ NULL,\r
+ NULL,\r
+ (VOID *)(UINTN)TempStackTop\r
+ );\r
+ }\r
+ } else {\r
+ //\r
+ // 16bit Realmode waking vector\r
+ //\r
+ DEBUG (( EFI_D_ERROR, "Transfer to 16bit OS waking vector - %x\r\n", (UINTN)Facs->FirmwareWakingVector));\r
+ AsmTransferControl (Facs->FirmwareWakingVector, 0x0);\r
+ }\r
+\r
+ //\r
+ // Never run to here\r
+ //\r
+ CpuDeadLoop();\r
+}\r
+\r
+/**\r
+ Restore S3 page table because we do not trust ACPINvs content.\r
+ If BootScriptExector driver will not run in 64-bit mode, this function will do nothing. \r
+\r
+ @param S3NvsPageTableAddress PageTableAddress in ACPINvs\r
+**/\r
+VOID\r
+RestoreS3PageTables (\r
+ IN UINTN S3NvsPageTableAddress\r
+ )\r
+{\r
+ if (FeaturePcdGet (PcdDxeIplSwitchToLongMode)) {\r
+ UINT32 RegEax;\r
+ UINT8 PhysicalAddressBits;\r
+ EFI_PHYSICAL_ADDRESS PageAddress;\r
+ UINTN IndexOfPml4Entries;\r
+ UINTN IndexOfPdpEntries;\r
+ UINTN IndexOfPageDirectoryEntries;\r
+ UINT64 NumberOfPml4EntriesNeeded;\r
+ UINT64 NumberOfPdpEntriesNeeded;\r
+ PAGE_MAP_AND_DIRECTORY_POINTER *PageMapLevel4Entry;\r
+ PAGE_MAP_AND_DIRECTORY_POINTER *PageMap;\r
+ PAGE_MAP_AND_DIRECTORY_POINTER *PageDirectoryPointerEntry;\r
+ PAGE_TABLE_ENTRY *PageDirectoryEntry;\r
+\r
+ //\r
+ // NOTE: We have to ASSUME the page table generation format, because we do not know whole page table information.\r
+ // The whole page table is too large to be saved in SMRAM.\r
+ //\r
+ // The assumption is : whole page table is allocated in CONTINOUS memory and CR3 points to TOP page.\r
+ //\r
+ DEBUG ((EFI_D_ERROR, "S3NvsPageTableAddress - %x\n", S3NvsPageTableAddress));\r
+\r
+ //\r
+ // By architecture only one PageMapLevel4 exists - so lets allocate storgage for it.\r
+ //\r
+ PageMap = (PAGE_MAP_AND_DIRECTORY_POINTER *)S3NvsPageTableAddress;\r
+ S3NvsPageTableAddress += SIZE_4KB;\r
+ \r
+ //\r
+ // Get physical address bits supported.\r
+ //\r
+ AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);\r
+ if (RegEax >= 0x80000008) {\r
+ AsmCpuid (0x80000008, &RegEax, NULL, NULL, NULL);\r
+ PhysicalAddressBits = (UINT8) RegEax;\r
+ } else {\r
+ PhysicalAddressBits = 36;\r
+ }\r
+ \r
+ //\r
+ // Calculate the table entries needed.\r
+ //\r
+ if (PhysicalAddressBits <= 39) {\r
+ NumberOfPml4EntriesNeeded = 1;\r
+ NumberOfPdpEntriesNeeded = LShiftU64 (1, (PhysicalAddressBits - 30));\r
+ } else {\r
+ NumberOfPml4EntriesNeeded = LShiftU64 (1, (PhysicalAddressBits - 39));\r
+ NumberOfPdpEntriesNeeded = 512;\r
+ }\r
+ \r
+ PageMapLevel4Entry = PageMap;\r
+ PageAddress = 0;\r
+ for (IndexOfPml4Entries = 0; IndexOfPml4Entries < NumberOfPml4EntriesNeeded; IndexOfPml4Entries++, PageMapLevel4Entry++) {\r
+ //\r
+ // Each PML4 entry points to a page of Page Directory Pointer entires.\r
+ // So lets allocate space for them and fill them in in the IndexOfPdpEntries loop.\r
+ //\r
+ PageDirectoryPointerEntry = (PAGE_MAP_AND_DIRECTORY_POINTER *)S3NvsPageTableAddress;\r
+ S3NvsPageTableAddress += SIZE_4KB;\r
+ \r
+ //\r
+ // Make a PML4 Entry\r
+ //\r
+ PageMapLevel4Entry->Uint64 = (UINT64)(UINTN)PageDirectoryPointerEntry;\r
+ PageMapLevel4Entry->Bits.ReadWrite = 1;\r
+ PageMapLevel4Entry->Bits.Present = 1;\r
+ \r
+ for (IndexOfPdpEntries = 0; IndexOfPdpEntries < NumberOfPdpEntriesNeeded; IndexOfPdpEntries++, PageDirectoryPointerEntry++) {\r
+ //\r
+ // Each Directory Pointer entries points to a page of Page Directory entires.\r
+ // So allocate space for them and fill them in in the IndexOfPageDirectoryEntries loop.\r
+ // \r
+ PageDirectoryEntry = (PAGE_TABLE_ENTRY *)S3NvsPageTableAddress;\r
+ S3NvsPageTableAddress += SIZE_4KB;\r
+ \r
+ //\r
+ // Fill in a Page Directory Pointer Entries\r
+ //\r
+ PageDirectoryPointerEntry->Uint64 = (UINT64)(UINTN)PageDirectoryEntry;\r
+ PageDirectoryPointerEntry->Bits.ReadWrite = 1;\r
+ PageDirectoryPointerEntry->Bits.Present = 1;\r
+ \r
+ for (IndexOfPageDirectoryEntries = 0; IndexOfPageDirectoryEntries < 512; IndexOfPageDirectoryEntries++, PageDirectoryEntry++, PageAddress += SIZE_2MB) {\r
+ //\r
+ // Fill in the Page Directory entries\r
+ //\r
+ PageDirectoryEntry->Uint64 = (UINT64)PageAddress;\r
+ PageDirectoryEntry->Bits.ReadWrite = 1;\r
+ PageDirectoryEntry->Bits.Present = 1;\r
+ PageDirectoryEntry->Bits.MustBe1 = 1;\r
+ }\r
+ }\r
+ }\r
+ return ;\r
+ } else {\r
+ //\r
+ // If DXE is running 32-bit mode, no need to establish page table.\r
+ //\r
+ return ;\r
+ }\r
+}\r
+\r
+/**\r
+ Jump to boot script executor driver.\r
+\r
+ The function will close and lock SMRAM and then jump to boot script execute driver to executing S3 boot script table.\r
+\r
+ @param AcpiS3Context a pointer to a structure of ACPI_S3_CONTEXT\r
+ @param EfiBootScriptExecutorVariable The function entry to executing S3 boot Script table. This function is build in\r
+ boot script execute driver\r
+**/\r
+VOID\r
+EFIAPI\r
+S3ResumeExecuteBootScript (\r
+ IN ACPI_S3_CONTEXT *AcpiS3Context,\r
+ IN BOOT_SCRIPT_EXECUTOR_VARIABLE *EfiBootScriptExecutorVariable\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ PEI_SMM_ACCESS_PPI *SmmAccess;\r
+ UINTN Index;\r
+ VOID *GuidHob;\r
+ IA32_DESCRIPTOR *IdtDescriptor;\r
+ VOID *IdtBuffer;\r
+ PEI_S3_RESUME_STATE *PeiS3ResumeState;\r
+\r
+ DEBUG ((EFI_D_ERROR, "S3ResumeExecuteBootScript()\n"));\r
+\r
+ //\r
+ // Attempt to use content from SMRAM first\r
+ //\r
+ GuidHob = GetFirstGuidHob (&gEfiAcpiVariableGuid);\r
+ if (GuidHob != NULL) {\r
+ //\r
+ // Last step for SMM - send SMI for initialization\r
+ //\r
+\r
+ //\r
+ // Send SMI to APs\r
+ // \r
+ SendSmiIpiAllExcludingSelf ();\r
+ //\r
+ // Send SMI to BSP\r
+ //\r
+ SendSmiIpi (GetApicId ());\r
+\r
+ Status = PeiServicesLocatePpi (\r
+ &gPeiSmmAccessPpiGuid,\r
+ 0,\r
+ NULL,\r
+ (VOID **) &SmmAccess\r
+ );\r
+\r
+ DEBUG ((EFI_D_ERROR, "Close all SMRAM regions before executing boot script\n"));\r
+\r
+ for (Index = 0, Status = EFI_SUCCESS; !EFI_ERROR (Status); Index++) {\r
+ Status = SmmAccess->Close ((EFI_PEI_SERVICES **)GetPeiServicesTablePointer (), SmmAccess, Index);\r
+ }\r
+\r
+ DEBUG ((EFI_D_ERROR, "Lock all SMRAM regions before executing boot script\n"));\r
+\r
+ for (Index = 0, Status = EFI_SUCCESS; !EFI_ERROR (Status); Index++) {\r
+ Status = SmmAccess->Lock ((EFI_PEI_SERVICES **)GetPeiServicesTablePointer (), SmmAccess, Index);\r
+ }\r
+ }\r
+\r
+ if (FeaturePcdGet (PcdDxeIplSwitchToLongMode)) {\r
+ //\r
+ // Need reconstruct page table here, since we do not trust ACPINvs.\r
+ //\r
+ RestoreS3PageTables ((UINTN)AcpiS3Context->S3NvsPageTableAddress);\r
+ AsmWriteCr3 ((UINTN)AcpiS3Context->S3NvsPageTableAddress);\r
+ }\r
+\r
+ if (FeaturePcdGet (PcdFrameworkCompatibilitySupport)) {\r
+ //\r
+ // On some platform, such as ECP, a dispatch node in boot script table may execute a 32-bit PEIM which may need PeiServices\r
+ // pointer. So PeiServices need preserve in (IDTBase- sizeof (UINTN)). \r
+ //\r
+ IdtDescriptor = (IA32_DESCRIPTOR *) (UINTN) (AcpiS3Context->IdtrProfile);\r
+ //\r
+ // Make sure the newly allcated IDT align with 16-bytes\r
+ // \r
+ IdtBuffer = AllocatePages (EFI_SIZE_TO_PAGES((IdtDescriptor->Limit + 1) + 16));\r
+ ASSERT (IdtBuffer != NULL);\r
+ CopyMem ((VOID*)((UINT8*)IdtBuffer + 16),(VOID*)(IdtDescriptor->Base), (IdtDescriptor->Limit + 1));\r
+ IdtDescriptor->Base = (UINTN)((UINT8*)IdtBuffer + 16);\r
+ *(UINTN*)(IdtDescriptor->Base - sizeof(UINTN)) = (UINTN)GetPeiServicesTablePointer ();\r
+ }\r
+\r
+ //\r
+ // Need to make sure the GDT is loaded with values that support long mode and real mode.\r
+ //\r
+ AsmWriteGdtr (&mGdt);\r
+\r
+ //\r
+ // Prepare data for return back\r
+ //\r
+ PeiS3ResumeState = AllocatePool (sizeof(*PeiS3ResumeState));\r
+ ASSERT (PeiS3ResumeState != NULL);\r
+ DEBUG (( EFI_D_ERROR, "PeiS3ResumeState - %x\r\n", PeiS3ResumeState));\r
+ PeiS3ResumeState->ReturnCs = 0x10;\r
+ PeiS3ResumeState->ReturnEntryPoint = (EFI_PHYSICAL_ADDRESS)(UINTN)S3ResumeBootOs;\r
+ PeiS3ResumeState->ReturnStackPointer = (EFI_PHYSICAL_ADDRESS)(UINTN)&Status;\r
+ //\r
+ // Save IDT\r
+ //\r
+ AsmReadIdtr (&PeiS3ResumeState->Idtr);\r
+\r
+ if (FeaturePcdGet (PcdDxeIplSwitchToLongMode)) {\r
+ //\r
+ // X64 S3 Resume\r
+ //\r
+ DEBUG (( EFI_D_ERROR, "Enable X64 and transfer control to Standalone Boot Script Executor\r\n"));\r
+\r
+ //\r
+ // Switch to long mode to complete resume.\r
+ //\r
+ AsmEnablePaging64 (\r
+ 0x38,\r
+ EfiBootScriptExecutorVariable->BootScriptExecutorEntrypoint,\r
+ (UINT64)(UINTN)AcpiS3Context,\r
+ (UINT64)(UINTN)PeiS3ResumeState,\r
+ (UINT64)(UINTN)(AcpiS3Context->BootScriptStackBase + AcpiS3Context->BootScriptStackSize)\r
+ );\r
+ } else {\r
+ //\r
+ // IA32 S3 Resume\r
+ //\r
+ DEBUG (( EFI_D_ERROR, "transfer control to Standalone Boot Script Executor\r\n"));\r
+ SwitchStack (\r
+ (SWITCH_STACK_ENTRY_POINT) (UINTN) EfiBootScriptExecutorVariable->BootScriptExecutorEntrypoint,\r
+ (VOID *)AcpiS3Context,\r
+ (VOID *)PeiS3ResumeState,\r
+ (VOID *)(UINTN)(AcpiS3Context->BootScriptStackBase + AcpiS3Context->BootScriptStackSize)\r
+ );\r
+ }\r
+\r
+ //\r
+ // Never run to here\r
+ //\r
+ CpuDeadLoop();\r
+}\r
+/**\r
+ Restores the platform to its preboot configuration for an S3 resume and\r
+ jumps to the OS waking vector.\r
+\r
+ This function will restore the platform to its pre-boot configuration that was\r
+ pre-stored in the boot script table and transfer control to OS waking vector.\r
+ Upon invocation, this function is responsible for locating the following\r
+ information before jumping to OS waking vector:\r
+ - ACPI tables\r
+ - boot script table\r
+ - any other information that it needs\r
+\r
+ The S3RestoreConfig() function then executes the pre-stored boot script table\r
+ and transitions the platform to the pre-boot state. The boot script is recorded\r
+ during regular boot using the EFI_S3_SAVE_STATE_PROTOCOL.Write() and\r
+ EFI_S3_SMM_SAVE_STATE_PROTOCOL.Write() functions. Finally, this function\r
+ transfers control to the OS waking vector. If the OS supports only a real-mode\r
+ waking vector, this function will switch from flat mode to real mode before\r
+ jumping to the waking vector. If all platform pre-boot configurations are\r
+ successfully restored and all other necessary information is ready, this\r
+ function will never return and instead will directly jump to the OS waking\r
+ vector. If this function returns, it indicates that the attempt to resume\r
+ from the ACPI S3 sleep state failed.\r
+\r
+ @param[in] This Pointer to this instance of the PEI_S3_RESUME_PPI\r
+\r
+ @retval EFI_ABORTED Execution of the S3 resume boot script table failed.\r
+ @retval EFI_NOT_FOUND Some necessary information that is used for the S3\r
+ resume boot path could not be located.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+S3RestoreConfig2 (\r
+ IN EFI_PEI_S3_RESUME2_PPI *This\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ PEI_SMM_ACCESS_PPI *SmmAccess;\r
+ UINTN Index;\r
+ ACPI_S3_CONTEXT *AcpiS3Context;\r
+ EFI_PEI_READ_ONLY_VARIABLE2_PPI *VariableServices;\r
+ EFI_PHYSICAL_ADDRESS TempEfiBootScriptExecutorVariable;\r
+ EFI_PHYSICAL_ADDRESS TempAcpiS3Context;\r
+ BOOT_SCRIPT_EXECUTOR_VARIABLE *EfiBootScriptExecutorVariable;\r
+ UINTN VarSize;\r
+ EFI_SMRAM_DESCRIPTOR *SmramDescriptor;\r
+ SMM_S3_RESUME_STATE *SmmS3ResumeState;\r
+ VOID *GuidHob;\r
+\r
+ DEBUG ((EFI_D_ERROR, "Enter S3 PEIM\r\n"));\r
+\r
+ Status = PeiServicesLocatePpi (\r
+ &gPeiSmmAccessPpiGuid,\r
+ 0,\r
+ NULL,\r
+ (VOID **) &SmmAccess\r
+ );\r
+ for (Index = 0; !EFI_ERROR (Status); Index++) {\r
+ Status = SmmAccess->Open ((EFI_PEI_SERVICES **)GetPeiServicesTablePointer (), SmmAccess, Index);\r
+ }\r
+\r
+ Status = PeiServicesLocatePpi (\r
+ &gEfiPeiReadOnlyVariable2PpiGuid,\r
+ 0,\r
+ NULL,\r
+ (VOID **) &VariableServices\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ VarSize = sizeof (EFI_PHYSICAL_ADDRESS);\r
+ Status = RestoreLockBox (\r
+ &gEfiAcpiVariableGuid,\r
+ &TempAcpiS3Context,\r
+ &VarSize\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ AcpiS3Context = (ACPI_S3_CONTEXT *)(UINTN)TempAcpiS3Context;\r
+ ASSERT (AcpiS3Context != NULL);\r
+\r
+ Status = RestoreLockBox (\r
+ &gEfiAcpiS3ContextGuid,\r
+ NULL,\r
+ NULL\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ VarSize = sizeof (TempEfiBootScriptExecutorVariable);\r
+ Status = RestoreLockBox (\r
+ &gEfiBootScriptExecutorVariableGuid,\r
+ &TempEfiBootScriptExecutorVariable,\r
+ &VarSize\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ Status = RestoreLockBox (\r
+ &gEfiBootScriptExecutorContextGuid,\r
+ NULL,\r
+ NULL\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ EfiBootScriptExecutorVariable = (BOOT_SCRIPT_EXECUTOR_VARIABLE *) (UINTN) TempEfiBootScriptExecutorVariable;\r
+\r
+ DEBUG (( EFI_D_ERROR, "AcpiS3Context = %x\n", AcpiS3Context));\r
+ DEBUG (( EFI_D_ERROR, "Waking Vector = %x\n", ((EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *) ((UINTN) (AcpiS3Context->AcpiFacsTable)))->FirmwareWakingVector));\r
+ DEBUG (( EFI_D_ERROR, "AcpiS3Context->AcpiFacsTable = %x\n", AcpiS3Context->AcpiFacsTable));\r
+ DEBUG (( EFI_D_ERROR, "AcpiS3Context->S3NvsPageTableAddress = %x\n", AcpiS3Context->S3NvsPageTableAddress));\r
+ DEBUG (( EFI_D_ERROR, "AcpiS3Context->S3DebugBufferAddress = %x\n", AcpiS3Context->S3DebugBufferAddress));\r
+ DEBUG (( EFI_D_ERROR, "EfiBootScriptExecutorVariable->BootScriptExecutorEntrypoint = %x\n", EfiBootScriptExecutorVariable->BootScriptExecutorEntrypoint));\r
+\r
+ //\r
+ // Additional step for BootScript integrity - we only handle BootScript and BootScriptExecutor.\r
+ // Script dispatch image and context (parameter) are handled by platform.\r
+ // We just use restore all lock box in place, no need restore one by one.\r
+ //\r
+ Status = RestoreAllLockBoxInPlace ();\r
+ ASSERT_EFI_ERROR (Status);\r
+ if (EFI_ERROR (Status)) {\r
+ // Something wrong\r
+ CpuDeadLoop ();\r
+ }\r
+\r
+ //\r
+ // Attempt to use content from SMRAM first\r
+ //\r
+ GuidHob = GetFirstGuidHob (&gEfiAcpiVariableGuid);\r
+ if (GuidHob != NULL) {\r
+ SmramDescriptor = (EFI_SMRAM_DESCRIPTOR *) GET_GUID_HOB_DATA (GuidHob);\r
+ SmmS3ResumeState = (SMM_S3_RESUME_STATE *)(UINTN)SmramDescriptor->CpuStart;\r
+\r
+ SmmS3ResumeState->ReturnCs = AsmReadCs ();\r
+ SmmS3ResumeState->ReturnEntryPoint = (EFI_PHYSICAL_ADDRESS)(UINTN)S3ResumeExecuteBootScript;\r
+ SmmS3ResumeState->ReturnContext1 = (EFI_PHYSICAL_ADDRESS)(UINTN)AcpiS3Context;\r
+ SmmS3ResumeState->ReturnContext2 = (EFI_PHYSICAL_ADDRESS)(UINTN)EfiBootScriptExecutorVariable;\r
+ SmmS3ResumeState->ReturnStackPointer = (EFI_PHYSICAL_ADDRESS)(UINTN)&Status;\r
+\r
+ DEBUG (( EFI_D_ERROR, "SMM S3 Signature = %x\n", SmmS3ResumeState->Signature));\r
+ DEBUG (( EFI_D_ERROR, "SMM S3 Stack Base = %x\n", SmmS3ResumeState->SmmS3StackBase));\r
+ DEBUG (( EFI_D_ERROR, "SMM S3 Stack Size = %x\n", SmmS3ResumeState->SmmS3StackSize));\r
+ DEBUG (( EFI_D_ERROR, "SMM S3 Resume Entry Point = %x\n", SmmS3ResumeState->SmmS3ResumeEntryPoint));\r
+ DEBUG (( EFI_D_ERROR, "SMM S3 CR0 = %x\n", SmmS3ResumeState->SmmS3Cr0));\r
+ DEBUG (( EFI_D_ERROR, "SMM S3 CR3 = %x\n", SmmS3ResumeState->SmmS3Cr3));\r
+ DEBUG (( EFI_D_ERROR, "SMM S3 CR4 = %x\n", SmmS3ResumeState->SmmS3Cr4));\r
+ DEBUG (( EFI_D_ERROR, "SMM S3 Return CS = %x\n", SmmS3ResumeState->ReturnCs));\r
+ DEBUG (( EFI_D_ERROR, "SMM S3 Return Entry Point = %x\n", SmmS3ResumeState->ReturnEntryPoint));\r
+ DEBUG (( EFI_D_ERROR, "SMM S3 Return Context1 = %x\n", SmmS3ResumeState->ReturnContext1));\r
+ DEBUG (( EFI_D_ERROR, "SMM S3 Return Context2 = %x\n", SmmS3ResumeState->ReturnContext2));\r
+ DEBUG (( EFI_D_ERROR, "SMM S3 Return Stack Pointer = %x\n", SmmS3ResumeState->ReturnStackPointer));\r
+ DEBUG (( EFI_D_ERROR, "SMM S3 Smst = %x\n", SmmS3ResumeState->Smst));\r
+\r
+ //\r
+ // Disable interrupt of Debug timer.\r
+ //\r
+ SaveAndSetDebugTimerInterrupt (FALSE);\r
+\r
+ if (SmmS3ResumeState->Signature == SMM_S3_RESUME_SMM_32) {\r
+ SwitchStack (\r
+ (SWITCH_STACK_ENTRY_POINT)(UINTN)SmmS3ResumeState->SmmS3ResumeEntryPoint,\r
+ (VOID *)AcpiS3Context,\r
+ 0,\r
+ (VOID *)(UINTN)(SmmS3ResumeState->SmmS3StackBase + SmmS3ResumeState->SmmS3StackSize)\r
+ );\r
+ }\r
+ if (SmmS3ResumeState->Signature == SMM_S3_RESUME_SMM_64) {\r
+ //\r
+ // Switch to long mode to complete resume.\r
+ //\r
+\r
+ //\r
+ // Need to make sure the GDT is loaded with values that support long mode and real mode.\r
+ //\r
+ AsmWriteGdtr (&mGdt);\r
+ AsmWriteCr3 ((UINTN)SmmS3ResumeState->SmmS3Cr3);\r
+ AsmEnablePaging64 (\r
+ 0x38,\r
+ SmmS3ResumeState->SmmS3ResumeEntryPoint,\r
+ (UINT64)(UINTN)AcpiS3Context,\r
+ 0,\r
+ SmmS3ResumeState->SmmS3StackBase + SmmS3ResumeState->SmmS3StackSize\r
+ );\r
+ }\r
+\r
+ }\r
+\r
+ S3ResumeExecuteBootScript (AcpiS3Context, EfiBootScriptExecutorVariable );\r
+ return EFI_SUCCESS;\r
+}\r
+/**\r
+ Main entry for S3 Resume PEIM.\r
+\r
+ This routine is to install EFI_PEI_S3_RESUME2_PPI.\r
+ \r
+ @param FileHandle Handle of the file being invoked.\r
+ @param PeiServices Pointer to PEI Services table.\r
+\r
+ @retval EFI_SUCCESS S3Resume Ppi is installed successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+PeimS3ResumeEntryPoint (\r
+ IN EFI_PEI_FILE_HANDLE FileHandle,\r
+ IN CONST EFI_PEI_SERVICES **PeiServices\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+\r
+ //\r
+ // Install S3 Resume Ppi\r
+ //\r
+ Status = (**PeiServices).InstallPpi (PeiServices, &mPpiList);\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r