diff --git a/OvmfPkg/Include/Library/PeilessStartupLib.h b/OvmfPkg/Include/Library/PeilessStartupLib.h
new file mode 100644
index 0000000000..8c9e3cb05a
--- /dev/null
+++ b/OvmfPkg/Include/Library/PeilessStartupLib.h
@@ -0,0 +1,34 @@
+/** @file
+
+ Copyright (c) 2021, Intel Corporation. All rights reserved.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef PEILESS_STARTUP_LIB_H_
+#define PEILESS_STARTUP_LIB_H_
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+/**
+ * This function brings up the Tdx guest from SEC phase to DXE phase.
+ * PEI phase is skipped because most of the components in PEI phase
+ * is not needed for Tdx guest, for example, MP Services, TPM etc.
+ * In this way, the attack surfaces are reduced as much as possible.
+ *
+ * @param Context The pointer to the SecCoreData
+ * @return VOID This function never returns
+ */
+VOID
+EFIAPI
+PeilessStartup (
+ IN VOID *Context
+ );
+
+#endif
diff --git a/OvmfPkg/Library/PeilessStartupLib/DxeLoad.c b/OvmfPkg/Library/PeilessStartupLib/DxeLoad.c
new file mode 100644
index 0000000000..6e79c30846
--- /dev/null
+++ b/OvmfPkg/Library/PeilessStartupLib/DxeLoad.c
@@ -0,0 +1,218 @@
+/** @file
+ Responsibility of this file is to load the DXE Core from a Firmware Volume.
+
+Copyright (c) 2016 HP Development Company, L.P.
+Copyright (c) 2006 - 2020, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "PeilessStartupInternal.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "X64/PageTables.h"
+#include
+
+#define STACK_SIZE 0x20000
+
+/**
+ Transfers control to DxeCore.
+
+ This function performs a CPU architecture specific operations to execute
+ the entry point of DxeCore
+
+ @param DxeCoreEntryPoint The entry point of DxeCore.
+
+**/
+VOID
+HandOffToDxeCore (
+ IN EFI_PHYSICAL_ADDRESS DxeCoreEntryPoint
+ )
+{
+ VOID *BaseOfStack;
+ VOID *TopOfStack;
+ UINTN PageTables;
+
+ //
+ // Clear page 0 and mark it as allocated if NULL pointer detection is enabled.
+ //
+ if (IsNullDetectionEnabled ()) {
+ ClearFirst4KPage (GetHobList ());
+ BuildMemoryAllocationHob (0, EFI_PAGES_TO_SIZE (1), EfiBootServicesData);
+ }
+
+ //
+ // Allocate 128KB for the Stack
+ //
+ BaseOfStack = AllocatePages (EFI_SIZE_TO_PAGES (STACK_SIZE));
+ ASSERT (BaseOfStack != NULL);
+
+ //
+ // Compute the top of the stack we were allocated. Pre-allocate a UINTN
+ // for safety.
+ //
+ TopOfStack = (VOID *)((UINTN)BaseOfStack + EFI_SIZE_TO_PAGES (STACK_SIZE) * EFI_PAGE_SIZE - CPU_STACK_ALIGNMENT);
+ TopOfStack = ALIGN_POINTER (TopOfStack, CPU_STACK_ALIGNMENT);
+
+ DEBUG ((DEBUG_INFO, "BaseOfStack=0x%x, TopOfStack=0x%x\n", BaseOfStack, TopOfStack));
+
+ //
+ // Create page table and save PageMapLevel4 to CR3
+ //
+ PageTables = CreateIdentityMappingPageTables (
+ (EFI_PHYSICAL_ADDRESS)(UINTN)BaseOfStack,
+ STACK_SIZE
+ );
+ if (PageTables == 0) {
+ DEBUG ((DEBUG_ERROR, "Failed to create idnetity mapping page tables.\n"));
+ CpuDeadLoop ();
+ }
+
+ AsmWriteCr3 (PageTables);
+
+ //
+ // Update the contents of BSP stack HOB to reflect the real stack info passed to DxeCore.
+ //
+ UpdateStackHob ((EFI_PHYSICAL_ADDRESS)(UINTN)BaseOfStack, STACK_SIZE);
+
+ DEBUG ((DEBUG_INFO, "SwitchStack then Jump to DxeCore\n"));
+ //
+ // Transfer the control to the entry point of DxeCore.
+ //
+ SwitchStack (
+ (SWITCH_STACK_ENTRY_POINT)(UINTN)DxeCoreEntryPoint,
+ GetHobList (),
+ NULL,
+ TopOfStack
+ );
+}
+
+/**
+ Searches DxeCore in all firmware Volumes and loads the first
+ instance that contains DxeCore.
+
+ @return FileHandle of DxeCore to load DxeCore.
+
+**/
+EFI_STATUS
+FindDxeCore (
+ IN INTN FvInstance,
+ IN OUT EFI_PEI_FILE_HANDLE *FileHandle
+ )
+{
+ EFI_STATUS Status;
+ EFI_PEI_FV_HANDLE VolumeHandle;
+
+ if (FileHandle == NULL) {
+ ASSERT (FALSE);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *FileHandle = NULL;
+
+ //
+ // Caller passed in a specific FV to try, so only try that one
+ //
+ Status = FfsFindNextVolume (FvInstance, &VolumeHandle);
+ if (!EFI_ERROR (Status)) {
+ Status = FfsFindNextFile (EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE, VolumeHandle, FileHandle);
+ if (*FileHandle) {
+ // Assume the FV that contains multiple compressed FVs.
+ // So decompress the compressed FVs
+ Status = FfsProcessFvFile (*FileHandle);
+ ASSERT_EFI_ERROR (Status);
+ Status = FfsAnyFvFindFirstFile (EFI_FV_FILETYPE_DXE_CORE, &VolumeHandle, FileHandle);
+ }
+ }
+
+ return Status;
+}
+
+/**
+ This function finds DXE Core in the firmware volume and transfer the control to
+ DXE core.
+
+ @return EFI_SUCCESS DXE core was successfully loaded.
+ @return EFI_OUT_OF_RESOURCES There are not enough resources to load DXE core.
+
+**/
+EFI_STATUS
+EFIAPI
+DxeLoadCore (
+ IN INTN FvInstance
+ )
+{
+ EFI_STATUS Status;
+ EFI_FV_FILE_INFO DxeCoreFileInfo;
+ EFI_PHYSICAL_ADDRESS DxeCoreAddress;
+ UINT64 DxeCoreSize;
+ EFI_PHYSICAL_ADDRESS DxeCoreEntryPoint;
+ EFI_PEI_FILE_HANDLE FileHandle;
+ VOID *PeCoffImage;
+
+ //
+ // Look in all the FVs present and find the DXE Core FileHandle
+ //
+ Status = FindDxeCore (FvInstance, &FileHandle);
+
+ if (EFI_ERROR (Status)) {
+ ASSERT (FALSE);
+ return Status;
+ }
+
+ //
+ // Load the DXE Core from a Firmware Volume.
+ //
+ Status = FfsFindSectionData (EFI_SECTION_PE32, FileHandle, &PeCoffImage);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = LoadPeCoffImage (PeCoffImage, &DxeCoreAddress, &DxeCoreSize, &DxeCoreEntryPoint);
+ ASSERT_EFI_ERROR (Status);
+
+ //
+ // Extract the DxeCore GUID file name.
+ //
+ Status = FfsGetFileInfo (FileHandle, &DxeCoreFileInfo);
+ ASSERT_EFI_ERROR (Status);
+
+ //
+ // Add HOB for the DXE Core
+ //
+ BuildModuleHob (
+ &DxeCoreFileInfo.FileName,
+ DxeCoreAddress,
+ ALIGN_VALUE (DxeCoreSize, EFI_PAGE_SIZE),
+ DxeCoreEntryPoint
+ );
+
+ DEBUG ((
+ DEBUG_INFO | DEBUG_LOAD,
+ "Loading DXE CORE at 0x%11p EntryPoint=0x%11p\n",
+ (VOID *)(UINTN)DxeCoreAddress,
+ FUNCTION_ENTRY_POINT (DxeCoreEntryPoint)
+ ));
+
+ // Transfer control to the DXE Core
+ // The hand off state is simply a pointer to the HOB list
+ //
+ HandOffToDxeCore (DxeCoreEntryPoint);
+
+ //
+ // If we get here, then the DXE Core returned. This is an error
+ // DxeCore should not return.
+ //
+ ASSERT (FALSE);
+ CpuDeadLoop ();
+
+ return EFI_OUT_OF_RESOURCES;
+}
diff --git a/OvmfPkg/Library/PeilessStartupLib/Hob.c b/OvmfPkg/Library/PeilessStartupLib/Hob.c
new file mode 100644
index 0000000000..3c544ca1f6
--- /dev/null
+++ b/OvmfPkg/Library/PeilessStartupLib/Hob.c
@@ -0,0 +1,135 @@
+/** @file
+ Main SEC phase code. Handles initial TDX Hob List Processing
+
+ Copyright (c) 2008, Intel Corporation. All rights reserved.
+ (C) Copyright 2016 Hewlett Packard Enterprise Development LP
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "PeilessStartupInternal.h"
+
+/**
+ * Construct the HobList in SEC phase.
+ *
+ * @return EFI_SUCCESS Successfully construct the firmware hoblist.
+ * @return EFI_NOT_FOUND Cannot find a memory region to be the fw hoblist.
+ */
+EFI_STATUS
+EFIAPI
+ConstructSecHobList (
+ )
+{
+ UINT32 LowMemorySize;
+ UINT32 LowMemoryStart;
+
+ EFI_HOB_HANDOFF_INFO_TABLE *HobList;
+ EFI_HOB_PLATFORM_INFO PlatformInfoHob;
+
+ ZeroMem (&PlatformInfoHob, sizeof (PlatformInfoHob));
+ PlatformInfoHob.HostBridgeDevId = PciRead16 (OVMF_HOSTBRIDGE_DID);
+ LowMemorySize = PlatformGetSystemMemorySizeBelow4gb (&PlatformInfoHob);
+ ASSERT (LowMemorySize != 0);
+ LowMemoryStart = FixedPcdGet32 (PcdOvmfDxeMemFvBase) + FixedPcdGet32 (PcdOvmfDxeMemFvSize);
+ LowMemorySize -= LowMemoryStart;
+
+ DEBUG ((DEBUG_INFO, "LowMemory Start and End: %x, %x\n", LowMemoryStart, LowMemoryStart + LowMemorySize));
+ HobList = HobConstructor (
+ (VOID *)(UINTN)LowMemoryStart,
+ LowMemorySize,
+ (VOID *)(UINTN)LowMemoryStart,
+ (VOID *)(UINTN)(LowMemoryStart + LowMemorySize)
+ );
+
+ SetHobList ((VOID *)(UINT64)HobList);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ * This function is to find a memory region which is the largest one below 4GB.
+ * It will be used as the firmware hoblist.
+ *
+ * @param VmmHobList Vmm passed hoblist which constains the memory information.
+ * @return EFI_SUCCESS Successfully construct the firmware hoblist.
+ */
+EFI_STATUS
+EFIAPI
+ConstructFwHobList (
+ IN CONST VOID *VmmHobList
+ )
+{
+ EFI_PEI_HOB_POINTERS Hob;
+ EFI_PHYSICAL_ADDRESS PhysicalEnd;
+ UINT64 ResourceLength;
+ EFI_PHYSICAL_ADDRESS LowMemoryStart;
+ UINT64 LowMemoryLength;
+
+ ASSERT (VmmHobList != NULL);
+
+ Hob.Raw = (UINT8 *)VmmHobList;
+
+ LowMemoryLength = 0;
+ LowMemoryStart = 0;
+
+ //
+ // Parse the HOB list until end of list or matching type is found.
+ //
+ while (!END_OF_HOB_LIST (Hob)) {
+ if (Hob.Header->HobType == EFI_HOB_TYPE_RESOURCE_DESCRIPTOR) {
+ if (Hob.ResourceDescriptor->ResourceType == EFI_RESOURCE_SYSTEM_MEMORY) {
+ PhysicalEnd = Hob.ResourceDescriptor->PhysicalStart + Hob.ResourceDescriptor->ResourceLength;
+ ResourceLength = Hob.ResourceDescriptor->ResourceLength;
+
+ if (PhysicalEnd <= BASE_4GB) {
+ if (ResourceLength > LowMemoryLength) {
+ LowMemoryStart = Hob.ResourceDescriptor->PhysicalStart;
+ LowMemoryLength = ResourceLength;
+ }
+ } else {
+ break;
+ }
+ }
+ }
+
+ Hob.Raw = GET_NEXT_HOB (Hob);
+ }
+
+ if (LowMemoryLength == 0) {
+ DEBUG ((DEBUG_ERROR, "Cannot find a memory region under 4GB for Fw hoblist.\n"));
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // HobLib doesn't like HobStart at address 0 so adjust is needed
+ //
+ if (LowMemoryStart == 0) {
+ LowMemoryStart += EFI_PAGE_SIZE;
+ LowMemoryLength -= EFI_PAGE_SIZE;
+ }
+
+ DEBUG ((DEBUG_INFO, "LowMemory Start and End: %x, %x\n", LowMemoryStart, LowMemoryStart + LowMemoryLength));
+ HobConstructor (
+ (VOID *)LowMemoryStart,
+ LowMemoryLength,
+ (VOID *)LowMemoryStart,
+ (VOID *)(LowMemoryStart + LowMemoryLength)
+ );
+
+ SetHobList ((VOID *)(UINT64)LowMemoryStart);
+
+ return EFI_SUCCESS;
+}
diff --git a/OvmfPkg/Library/PeilessStartupLib/PeilessStartup.c b/OvmfPkg/Library/PeilessStartupLib/PeilessStartup.c
new file mode 100644
index 0000000000..126eb74048
--- /dev/null
+++ b/OvmfPkg/Library/PeilessStartupLib/PeilessStartup.c
@@ -0,0 +1,223 @@
+/** @file
+
+ Copyright (c) 2021, Intel Corporation. All rights reserved.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "PeilessStartupInternal.h"
+
+#define GET_GPAW_INIT_STATE(INFO) ((UINT8) ((INFO) & 0x3f))
+
+EFI_MEMORY_TYPE_INFORMATION mDefaultMemoryTypeInformation[] = {
+ { EfiACPIMemoryNVS, 0x004 },
+ { EfiACPIReclaimMemory, 0x008 },
+ { EfiReservedMemoryType, 0x004 },
+ { EfiRuntimeServicesData, 0x024 },
+ { EfiRuntimeServicesCode, 0x030 },
+ { EfiBootServicesCode, 0x180 },
+ { EfiBootServicesData, 0xF00 },
+ { EfiMaxMemoryType, 0x000 }
+};
+
+EFI_STATUS
+EFIAPI
+InitializePlatform (
+ EFI_HOB_PLATFORM_INFO *PlatformInfoHob
+ )
+{
+ UINT32 LowerMemorySize;
+
+ DEBUG ((DEBUG_INFO, "InitializePlatform in Pei-less boot\n"));
+ PlatformDebugDumpCmos ();
+
+ PlatformInfoHob->DefaultMaxCpuNumber = 64;
+ PlatformInfoHob->PcdPciMmio64Size = 0x800000000;
+
+ PlatformInfoHob->HostBridgeDevId = PciRead16 (OVMF_HOSTBRIDGE_DID);
+ DEBUG ((DEBUG_INFO, "HostBridgeDeviceId = 0x%x\n", PlatformInfoHob->HostBridgeDevId));
+
+ PlatformAddressWidthInitialization (PlatformInfoHob);
+ DEBUG ((
+ DEBUG_INFO,
+ "PhysMemAddressWidth=0x%x, Pci64Base=0x%llx, Pci64Size=0x%llx\n",
+ PlatformInfoHob->PhysMemAddressWidth,
+ PlatformInfoHob->PcdPciMmio64Base,
+ PlatformInfoHob->PcdPciMmio64Size
+ ));
+
+ PlatformMaxCpuCountInitialization (PlatformInfoHob);
+ DEBUG ((
+ DEBUG_INFO,
+ "MaxCpuCount=%d, BootCpuCount=%d\n",
+ PlatformInfoHob->PcdCpuMaxLogicalProcessorNumber,
+ PlatformInfoHob->PcdCpuBootLogicalProcessorNumber
+ ));
+
+ LowerMemorySize = PlatformGetSystemMemorySizeBelow4gb (PlatformInfoHob);
+ PlatformQemuUc32BaseInitialization (PlatformInfoHob);
+ DEBUG ((
+ DEBUG_INFO,
+ "Uc32Base = 0x%x, Uc32Size = 0x%x, LowerMemorySize = 0x%x\n",
+ PlatformInfoHob->Uc32Base,
+ PlatformInfoHob->Uc32Size,
+ LowerMemorySize
+ ));
+
+ if (TdIsEnabled ()) {
+ PlatformTdxPublishRamRegions ();
+ } else {
+ PlatformQemuInitializeRam (PlatformInfoHob);
+ PlatformQemuInitializeRamForS3 (PlatformInfoHob);
+ }
+
+ //
+ // Create Memory Type Information HOB
+ //
+ BuildGuidDataHob (
+ &gEfiMemoryTypeInformationGuid,
+ mDefaultMemoryTypeInformation,
+ sizeof (mDefaultMemoryTypeInformation)
+ );
+
+ PlatformMemMapInitialization (PlatformInfoHob);
+
+ PlatformNoexecDxeInitialization (PlatformInfoHob);
+
+ if (TdIsEnabled ()) {
+ PlatformInfoHob->PcdConfidentialComputingGuestAttr = CCAttrIntelTdx;
+ PlatformInfoHob->PcdTdxSharedBitMask = TdSharedPageMask ();
+ PlatformInfoHob->PcdSetNxForStack = TRUE;
+ }
+
+ PlatformMiscInitialization (PlatformInfoHob);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ * This function brings up the Tdx guest from SEC phase to DXE phase.
+ * PEI phase is skipped because most of the components in PEI phase
+ * is not needed for Tdx guest, for example, MP Services, TPM etc.
+ * In this way, the attack surfaces are reduced as much as possible.
+ *
+ * @param Context The pointer to the SecCoreData
+ * @return VOID This function never returns
+ */
+VOID
+EFIAPI
+PeilessStartup (
+ IN VOID *Context
+ )
+{
+ EFI_SEC_PEI_HAND_OFF *SecCoreData;
+ EFI_FIRMWARE_VOLUME_HEADER *BootFv;
+ EFI_STATUS Status;
+ EFI_HOB_PLATFORM_INFO PlatformInfoHob;
+ UINT32 DxeCodeBase;
+ UINT32 DxeCodeSize;
+ TD_RETURN_DATA TdReturnData;
+ VOID *VmmHobList;
+
+ Status = EFI_SUCCESS;
+ BootFv = NULL;
+ VmmHobList = NULL;
+ SecCoreData = (EFI_SEC_PEI_HAND_OFF *)Context;
+
+ ZeroMem (&PlatformInfoHob, sizeof (PlatformInfoHob));
+
+ if (TdIsEnabled ()) {
+ VmmHobList = (VOID *)(UINTN)FixedPcdGet32 (PcdOvmfSecGhcbBase);
+ Status = TdCall (TDCALL_TDINFO, 0, 0, 0, &TdReturnData);
+ ASSERT (Status == EFI_SUCCESS);
+
+ DEBUG ((
+ DEBUG_INFO,
+ "Tdx started with(Hob: 0x%x, Gpaw: 0x%x, Cpus: %d)\n",
+ (UINT32)(UINTN)VmmHobList,
+ GET_GPAW_INIT_STATE (TdReturnData.TdInfo.Gpaw),
+ TdReturnData.TdInfo.NumVcpus
+ ));
+
+ Status = ConstructFwHobList (VmmHobList);
+ } else {
+ DEBUG ((DEBUG_INFO, "Ovmf started\n"));
+ Status = ConstructSecHobList ();
+ }
+
+ if (EFI_ERROR (Status)) {
+ ASSERT (FALSE);
+ CpuDeadLoop ();
+ }
+
+ DEBUG ((DEBUG_INFO, "HobList: %p\n", GetHobList ()));
+
+ //
+ // Initialize the Platform
+ //
+ Status = InitializePlatform (&PlatformInfoHob);
+ if (EFI_ERROR (Status)) {
+ ASSERT (FALSE);
+ CpuDeadLoop ();
+ }
+
+ BuildGuidDataHob (&gUefiOvmfPkgPlatformInfoGuid, &PlatformInfoHob, sizeof (EFI_HOB_PLATFORM_INFO));
+
+ //
+ // SecFV
+ //
+ BootFv = (EFI_FIRMWARE_VOLUME_HEADER *)SecCoreData->BootFirmwareVolumeBase;
+ BuildFvHob ((UINTN)BootFv, BootFv->FvLength);
+
+ //
+ // DxeFV
+ //
+ DxeCodeBase = PcdGet32 (PcdBfvBase);
+ DxeCodeSize = PcdGet32 (PcdBfvRawDataSize) - (UINT32)BootFv->FvLength;
+ BuildFvHob (DxeCodeBase, DxeCodeSize);
+
+ DEBUG ((DEBUG_INFO, "SecFv : %p, 0x%x\n", BootFv, BootFv->FvLength));
+ DEBUG ((DEBUG_INFO, "DxeFv : %x, 0x%x\n", DxeCodeBase, DxeCodeSize));
+
+ BuildStackHob ((UINTN)SecCoreData->StackBase, SecCoreData->StackSize <<= 1);
+
+ BuildResourceDescriptorHob (
+ EFI_RESOURCE_SYSTEM_MEMORY,
+ EFI_RESOURCE_ATTRIBUTE_PRESENT |
+ EFI_RESOURCE_ATTRIBUTE_INITIALIZED |
+ EFI_RESOURCE_ATTRIBUTE_UNCACHEABLE |
+ EFI_RESOURCE_ATTRIBUTE_WRITE_COMBINEABLE |
+ EFI_RESOURCE_ATTRIBUTE_WRITE_THROUGH_CACHEABLE |
+ EFI_RESOURCE_ATTRIBUTE_WRITE_BACK_CACHEABLE |
+ EFI_RESOURCE_ATTRIBUTE_TESTED,
+ (UINT64)SecCoreData->TemporaryRamBase,
+ (UINT64)SecCoreData->TemporaryRamSize
+ );
+
+ //
+ // Load the DXE Core and transfer control to it.
+ // Only DxeFV is in the compressed section.
+ //
+ Status = DxeLoadCore (1);
+
+ //
+ // Never arrive here.
+ //
+ ASSERT (FALSE);
+ CpuDeadLoop ();
+}
diff --git a/OvmfPkg/Library/PeilessStartupLib/PeilessStartupInternal.h b/OvmfPkg/Library/PeilessStartupLib/PeilessStartupInternal.h
new file mode 100644
index 0000000000..23e9e0be53
--- /dev/null
+++ b/OvmfPkg/Library/PeilessStartupLib/PeilessStartupInternal.h
@@ -0,0 +1,55 @@
+/** @file
+
+ Copyright (c) 2021, Intel Corporation. All rights reserved.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef PEILESS_STARTUP_INTERNAL_LIB_H_
+#define PEILESS_STARTUP_INTERNAL_LIB_H_
+
+#include
+#include
+#include
+#include
+#include
+
+EFI_STATUS
+EFIAPI
+DxeLoadCore (
+ IN INTN FvInstance
+ );
+
+VOID
+EFIAPI
+TransferHobList (
+ IN CONST VOID *HobStart
+ );
+
+/**
+ * This function is to find a memory region which is the largest one below 4GB.
+ * It will be used as the firmware hoblist.
+ *
+ * @param VmmHobList Vmm passed hoblist which constains the memory information.
+ * @return EFI_SUCCESS Successfully construct the firmware hoblist.
+ * @return EFI_NOT_FOUND Cannot find a memory region to be the fw hoblist.
+ */
+EFI_STATUS
+EFIAPI
+ConstructFwHobList (
+ IN CONST VOID *VmmHobList
+ );
+
+/**
+ * Construct the HobList in SEC phase.
+ *
+ * @return EFI_SUCCESS Successfully construct the firmware hoblist.
+ * @return EFI_NOT_FOUND Cannot find a memory region to be the fw hoblist.
+ */
+EFI_STATUS
+EFIAPI
+ConstructSecHobList (
+ );
+
+#endif
diff --git a/OvmfPkg/Library/PeilessStartupLib/PeilessStartupLib.inf b/OvmfPkg/Library/PeilessStartupLib/PeilessStartupLib.inf
new file mode 100644
index 0000000000..8791984586
--- /dev/null
+++ b/OvmfPkg/Library/PeilessStartupLib/PeilessStartupLib.inf
@@ -0,0 +1,86 @@
+#/** @file
+# Component description file for TDX Pre PI Library
+#
+# LIbrary helps you build a platform that skips PEI and loads DXE Core
+# directly. Helps building HOBs, reading data from the FV, and doing
+# decompression.
+#
+# Copyright (c) 2018, Intel Corporation. All rights reserved.
+# Copyright (c) 2008, Apple Inc. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+#
+#**/
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = PeilessStartupLib
+ FILE_GUID = 8FA74135-F841-40A4-86C8-69C923D2E85F
+ MODULE_TYPE = BASE
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = PeilessStartupLib|SEC
+
+#
+# VALID_ARCHITECTURES = X64
+#
+
+[Sources]
+ PeilessStartup.c
+ Hob.c
+ DxeLoad.c
+
+[Sources.X64]
+ X64/VirtualMemory.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ UefiCpuPkg/UefiCpuPkg.dec
+ OvmfPkg/OvmfPkg.dec
+ EmbeddedPkg/EmbeddedPkg.dec
+
+[LibraryClasses]
+ BaseLib
+ DebugLib
+ BaseMemoryLib
+ PcdLib
+ UefiCpuLib
+ DebugAgentLib
+ IoLib
+ LocalApicLib
+ SynchronizationLib
+ HobLib
+ TdxLib
+ MemoryAllocationLib
+ PrePiLib
+ QemuFwCfgLib
+ PlatformInitLib
+
+[Guids]
+ gEfiHobMemoryAllocModuleGuid
+ gEfiHobMemoryAllocStackGuid
+ gUefiOvmfPkgPlatformInfoGuid
+ gEfiMemoryTypeInformationGuid
+ gPcdDataBaseHobGuid
+
+[Pcd]
+ gUefiOvmfPkgTokenSpaceGuid.PcdCfvBase
+ gUefiOvmfPkgTokenSpaceGuid.PcdCfvRawDataOffset
+ gUefiOvmfPkgTokenSpaceGuid.PcdCfvRawDataSize
+ gUefiOvmfPkgTokenSpaceGuid.PcdBfvBase
+ gUefiOvmfPkgTokenSpaceGuid.PcdBfvRawDataOffset
+ gUefiOvmfPkgTokenSpaceGuid.PcdBfvRawDataSize
+ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBackupBase
+ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBackupSize
+ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbSize
+ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBase
+ gEfiMdeModulePkgTokenSpaceGuid.PcdDxeIplBuildPageTables ## CONSUMES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdCpuStackGuard ## CONSUMES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdUse1GPageTable ## SOMETIMES_CONSUMES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdDxeNxMemoryProtectionPolicy ## SOMETIMES_CONSUMES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdImageProtectionPolicy ## SOMETIMES_CONSUMES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdPteMemoryEncryptionAddressOrMask ## CONSUMES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdNullPointerDetectionPropertyMask ## CONSUMES
+ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfDxeMemFvBase
+ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfDxeMemFvSize
diff --git a/OvmfPkg/Library/PeilessStartupLib/X64/PageTables.h b/OvmfPkg/Library/PeilessStartupLib/X64/PageTables.h
new file mode 100644
index 0000000000..a0d0d3547b
--- /dev/null
+++ b/OvmfPkg/Library/PeilessStartupLib/X64/PageTables.h
@@ -0,0 +1,206 @@
+/** @file
+ x64 Long Mode Virtual Memory Management Definitions
+
+ References:
+ 1) IA-32 Intel(R) Architecture Software Developer's Manual Volume 1:Basic Architecture, Intel
+ 2) IA-32 Intel(R) Architecture Software Developer's Manual Volume 2:Instruction Set Reference, Intel
+ 3) IA-32 Intel(R) Architecture Software Developer's Manual Volume 3:System Programmer's Guide, Intel
+ 4) AMD64 Architecture Programmer's Manual Volume 2: System Programming
+
+Copyright (c) 2006 - 2020, Intel Corporation. All rights reserved.
+Copyright (c) 2017, AMD Incorporated. All rights reserved.
+
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef PAGE_TABLES_H_
+#define PAGE_TABLES_H_
+
+#define SYS_CODE64_SEL 0x38
+
+#pragma pack(1)
+
+typedef union {
+ struct {
+ UINT32 LimitLow : 16;
+ UINT32 BaseLow : 16;
+ UINT32 BaseMid : 8;
+ UINT32 Type : 4;
+ UINT32 System : 1;
+ UINT32 Dpl : 2;
+ UINT32 Present : 1;
+ UINT32 LimitHigh : 4;
+ UINT32 Software : 1;
+ UINT32 Reserved : 1;
+ UINT32 DefaultSize : 1;
+ UINT32 Granularity : 1;
+ UINT32 BaseHigh : 8;
+ } Bits;
+ UINT64 Uint64;
+} IA32_GDT;
+
+typedef struct {
+ IA32_IDT_GATE_DESCRIPTOR Ia32IdtEntry;
+ UINT32 Offset32To63;
+ UINT32 Reserved;
+} X64_IDT_GATE_DESCRIPTOR;
+
+//
+// Page-Map Level-4 Offset (PML4) and
+// Page-Directory-Pointer Offset (PDPE) entries 4K & 2MB
+//
+
+typedef union {
+ struct {
+ UINT64 Present : 1; // 0 = Not present in memory, 1 = Present in memory
+ UINT64 ReadWrite : 1; // 0 = Read-Only, 1= Read/Write
+ UINT64 UserSupervisor : 1; // 0 = Supervisor, 1=User
+ UINT64 WriteThrough : 1; // 0 = Write-Back caching, 1=Write-Through caching
+ UINT64 CacheDisabled : 1; // 0 = Cached, 1=Non-Cached
+ UINT64 Accessed : 1; // 0 = Not accessed, 1 = Accessed (set by CPU)
+ UINT64 Reserved : 1; // Reserved
+ UINT64 MustBeZero : 2; // Must Be Zero
+ UINT64 Available : 3; // Available for use by system software
+ UINT64 PageTableBaseAddress : 40; // Page Table Base Address
+ UINT64 AvabilableHigh : 11; // Available for use by system software
+ UINT64 Nx : 1; // No Execute bit
+ } Bits;
+ UINT64 Uint64;
+} PAGE_MAP_AND_DIRECTORY_POINTER;
+
+//
+// Page Table Entry 4KB
+//
+typedef union {
+ struct {
+ UINT64 Present : 1; // 0 = Not present in memory, 1 = Present in memory
+ UINT64 ReadWrite : 1; // 0 = Read-Only, 1= Read/Write
+ UINT64 UserSupervisor : 1; // 0 = Supervisor, 1=User
+ UINT64 WriteThrough : 1; // 0 = Write-Back caching, 1=Write-Through caching
+ UINT64 CacheDisabled : 1; // 0 = Cached, 1=Non-Cached
+ UINT64 Accessed : 1; // 0 = Not accessed, 1 = Accessed (set by CPU)
+ UINT64 Dirty : 1; // 0 = Not Dirty, 1 = written by processor on access to page
+ UINT64 PAT : 1; //
+ UINT64 Global : 1; // 0 = Not global page, 1 = global page TLB not cleared on CR3 write
+ UINT64 Available : 3; // Available for use by system software
+ UINT64 PageTableBaseAddress : 40; // Page Table Base Address
+ UINT64 AvabilableHigh : 11; // Available for use by system software
+ UINT64 Nx : 1; // 0 = Execute Code, 1 = No Code Execution
+ } Bits;
+ UINT64 Uint64;
+} PAGE_TABLE_4K_ENTRY;
+
+//
+// Page Table Entry 2MB
+//
+typedef union {
+ struct {
+ UINT64 Present : 1; // 0 = Not present in memory, 1 = Present in memory
+ UINT64 ReadWrite : 1; // 0 = Read-Only, 1= Read/Write
+ UINT64 UserSupervisor : 1; // 0 = Supervisor, 1=User
+ UINT64 WriteThrough : 1; // 0 = Write-Back caching, 1=Write-Through caching
+ UINT64 CacheDisabled : 1; // 0 = Cached, 1=Non-Cached
+ UINT64 Accessed : 1; // 0 = Not accessed, 1 = Accessed (set by CPU)
+ UINT64 Dirty : 1; // 0 = Not Dirty, 1 = written by processor on access to page
+ UINT64 MustBe1 : 1; // Must be 1
+ UINT64 Global : 1; // 0 = Not global page, 1 = global page TLB not cleared on CR3 write
+ UINT64 Available : 3; // Available for use by system software
+ UINT64 PAT : 1; //
+ UINT64 MustBeZero : 8; // Must be zero;
+ UINT64 PageTableBaseAddress : 31; // Page Table Base Address
+ UINT64 AvabilableHigh : 11; // Available for use by system software
+ UINT64 Nx : 1; // 0 = Execute Code, 1 = No Code Execution
+ } Bits;
+ UINT64 Uint64;
+} PAGE_TABLE_ENTRY;
+
+//
+// Page Table Entry 1GB
+//
+typedef union {
+ struct {
+ UINT64 Present : 1; // 0 = Not present in memory, 1 = Present in memory
+ UINT64 ReadWrite : 1; // 0 = Read-Only, 1= Read/Write
+ UINT64 UserSupervisor : 1; // 0 = Supervisor, 1=User
+ UINT64 WriteThrough : 1; // 0 = Write-Back caching, 1=Write-Through caching
+ UINT64 CacheDisabled : 1; // 0 = Cached, 1=Non-Cached
+ UINT64 Accessed : 1; // 0 = Not accessed, 1 = Accessed (set by CPU)
+ UINT64 Dirty : 1; // 0 = Not Dirty, 1 = written by processor on access to page
+ UINT64 MustBe1 : 1; // Must be 1
+ UINT64 Global : 1; // 0 = Not global page, 1 = global page TLB not cleared on CR3 write
+ UINT64 Available : 3; // Available for use by system software
+ UINT64 PAT : 1; //
+ UINT64 MustBeZero : 17; // Must be zero;
+ UINT64 PageTableBaseAddress : 22; // Page Table Base Address
+ UINT64 AvabilableHigh : 11; // Available for use by system software
+ UINT64 Nx : 1; // 0 = Execute Code, 1 = No Code Execution
+ } Bits;
+ UINT64 Uint64;
+} PAGE_TABLE_1G_ENTRY;
+
+#pragma pack()
+
+#define CR0_WP BIT16
+
+#define IA32_PG_P BIT0
+#define IA32_PG_RW BIT1
+#define IA32_PG_PS BIT7
+
+#define PAGING_PAE_INDEX_MASK 0x1FF
+
+#define PAGING_4K_ADDRESS_MASK_64 0x000FFFFFFFFFF000ull
+#define PAGING_2M_ADDRESS_MASK_64 0x000FFFFFFFE00000ull
+#define PAGING_1G_ADDRESS_MASK_64 0x000FFFFFC0000000ull
+
+#define PAGING_L1_ADDRESS_SHIFT 12
+#define PAGING_L2_ADDRESS_SHIFT 21
+#define PAGING_L3_ADDRESS_SHIFT 30
+#define PAGING_L4_ADDRESS_SHIFT 39
+
+#define PAGING_PML4E_NUMBER 4
+
+#define PAGE_TABLE_POOL_ALIGNMENT BASE_2MB
+#define PAGE_TABLE_POOL_UNIT_SIZE SIZE_2MB
+#define PAGE_TABLE_POOL_UNIT_PAGES EFI_SIZE_TO_PAGES (PAGE_TABLE_POOL_UNIT_SIZE)
+#define PAGE_TABLE_POOL_ALIGN_MASK \
+ (~(EFI_PHYSICAL_ADDRESS)(PAGE_TABLE_POOL_ALIGNMENT - 1))
+
+typedef struct {
+ VOID *NextPool;
+ UINTN Offset;
+ UINTN FreePages;
+} PAGE_TABLE_POOL;
+
+UINTN
+CreateIdentityMappingPageTables (
+ IN EFI_PHYSICAL_ADDRESS StackBase,
+ IN UINTN StackSize
+ );
+
+/**
+ Clear legacy memory located at the first 4K-page.
+
+ This function traverses the whole HOB list to check if memory from 0 to 4095
+ exists and has not been allocated, and then clear it if so.
+
+ @param HobStart The start of HobList passed to DxeCore.
+
+**/
+VOID
+ClearFirst4KPage (
+ IN VOID *HobStart
+ );
+
+/**
+ Return configure status of NULL pointer detection feature.
+
+ @return TRUE NULL pointer detection feature is enabled
+ @return FALSE NULL pointer detection feature is disabled
+**/
+BOOLEAN
+IsNullDetectionEnabled (
+ VOID
+ );
+
+#endif
diff --git a/OvmfPkg/Library/PeilessStartupLib/X64/VirtualMemory.c b/OvmfPkg/Library/PeilessStartupLib/X64/VirtualMemory.c
new file mode 100644
index 0000000000..8da06da74e
--- /dev/null
+++ b/OvmfPkg/Library/PeilessStartupLib/X64/VirtualMemory.c
@@ -0,0 +1,935 @@
+/** @file
+ x64-specifc functionality for Page Table Setup.
+
+Copyright (c) 2006 - 2020, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "PageTables.h"
+
+//
+// Global variable to keep track current available memory used as page table.
+//
+PAGE_TABLE_POOL *mPageTablePool = NULL;
+
+UINTN mLevelShift[5] = {
+ 0,
+ PAGING_L1_ADDRESS_SHIFT,
+ PAGING_L2_ADDRESS_SHIFT,
+ PAGING_L3_ADDRESS_SHIFT,
+ PAGING_L4_ADDRESS_SHIFT
+};
+
+UINT64 mLevelMask[5] = {
+ 0,
+ PAGING_4K_ADDRESS_MASK_64,
+ PAGING_2M_ADDRESS_MASK_64,
+ PAGING_1G_ADDRESS_MASK_64,
+ PAGING_1G_ADDRESS_MASK_64
+};
+
+UINT64 mLevelSize[5] = {
+ 0,
+ SIZE_4KB,
+ SIZE_2MB,
+ SIZE_1GB,
+ SIZE_512GB
+};
+
+BOOLEAN
+IsSetNxForStack (
+ VOID
+ )
+{
+ EFI_HOB_GUID_TYPE *GuidHob;
+ EFI_HOB_PLATFORM_INFO *PlatformInfo;
+
+ GuidHob = GetFirstGuidHob (&gUefiOvmfPkgPlatformInfoGuid);
+ if (GuidHob == NULL) {
+ ASSERT (FALSE);
+ return FALSE;
+ }
+
+ PlatformInfo = (EFI_HOB_PLATFORM_INFO *)GET_GUID_HOB_DATA (GuidHob);
+
+ return PlatformInfo->PcdSetNxForStack;
+}
+
+/**
+ Clear legacy memory located at the first 4K-page, if available.
+
+ This function traverses the whole HOB list to check if memory from 0 to 4095
+ exists and has not been allocated, and then clear it if so.
+
+ @param HobStart The start of HobList passed to DxeCore.
+
+**/
+VOID
+ClearFirst4KPage (
+ IN VOID *HobStart
+ )
+{
+ EFI_PEI_HOB_POINTERS RscHob;
+ EFI_PEI_HOB_POINTERS MemHob;
+ BOOLEAN DoClear;
+
+ RscHob.Raw = HobStart;
+ MemHob.Raw = HobStart;
+ DoClear = FALSE;
+
+ //
+ // Check if page 0 exists and free
+ //
+ while ((RscHob.Raw = GetNextHob (
+ EFI_HOB_TYPE_RESOURCE_DESCRIPTOR,
+ RscHob.Raw
+ )) != NULL)
+ {
+ if ((RscHob.ResourceDescriptor->ResourceType == EFI_RESOURCE_SYSTEM_MEMORY) &&
+ (RscHob.ResourceDescriptor->PhysicalStart == 0))
+ {
+ DoClear = TRUE;
+ //
+ // Make sure memory at 0-4095 has not been allocated.
+ //
+ while ((MemHob.Raw = GetNextHob (
+ EFI_HOB_TYPE_MEMORY_ALLOCATION,
+ MemHob.Raw
+ )) != NULL)
+ {
+ if (MemHob.MemoryAllocation->AllocDescriptor.MemoryBaseAddress
+ < EFI_PAGE_SIZE)
+ {
+ DoClear = FALSE;
+ break;
+ }
+
+ MemHob.Raw = GET_NEXT_HOB (MemHob);
+ }
+
+ break;
+ }
+
+ RscHob.Raw = GET_NEXT_HOB (RscHob);
+ }
+
+ if (DoClear) {
+ DEBUG ((DEBUG_INFO, "Clearing first 4K-page!\r\n"));
+ SetMem (NULL, EFI_PAGE_SIZE, 0);
+ }
+
+ return;
+}
+
+/**
+ Return configure status of NULL pointer detection feature.
+
+ @return TRUE NULL pointer detection feature is enabled
+ @return FALSE NULL pointer detection feature is disabled
+
+**/
+BOOLEAN
+IsNullDetectionEnabled (
+ VOID
+ )
+{
+ return ((PcdGet8 (PcdNullPointerDetectionPropertyMask) & BIT0) != 0);
+}
+
+/**
+ The function will check if Execute Disable Bit is available.
+
+ @retval TRUE Execute Disable Bit is available.
+ @retval FALSE Execute Disable Bit is not available.
+
+**/
+BOOLEAN
+IsExecuteDisableBitAvailable (
+ VOID
+ )
+{
+ UINT32 RegEax;
+ UINT32 RegEdx;
+ BOOLEAN Available;
+
+ Available = FALSE;
+ AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
+ if (RegEax >= 0x80000001) {
+ AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx);
+ if ((RegEdx & BIT20) != 0) {
+ //
+ // Bit 20: Execute Disable Bit available.
+ //
+ Available = TRUE;
+ }
+ }
+
+ return Available;
+}
+
+/**
+ Check if Execute Disable Bit (IA32_EFER.NXE) should be enabled or not.
+
+ @retval TRUE IA32_EFER.NXE should be enabled.
+ @retval FALSE IA32_EFER.NXE should not be enabled.
+
+**/
+BOOLEAN
+IsEnableNonExecNeeded (
+ VOID
+ )
+{
+ if (!IsExecuteDisableBitAvailable ()) {
+ return FALSE;
+ }
+
+ //
+ // XD flag (BIT63) in page table entry is only valid if IA32_EFER.NXE is set.
+ // Features controlled by Following PCDs need this feature to be enabled.
+ //
+ return (IsSetNxForStack () ||
+ FixedPcdGet64 (PcdDxeNxMemoryProtectionPolicy) != 0 ||
+ PcdGet32 (PcdImageProtectionPolicy) != 0);
+}
+
+/**
+ Enable Execute Disable Bit.
+
+**/
+VOID
+EnableExecuteDisableBit (
+ VOID
+ )
+{
+ UINT64 MsrRegisters;
+
+ MsrRegisters = AsmReadMsr64 (0xC0000080);
+ MsrRegisters |= BIT11;
+ AsmWriteMsr64 (0xC0000080, MsrRegisters);
+}
+
+/**
+ The function will check if page table entry should be splitted to smaller
+ granularity.
+
+ @param Address Physical memory address.
+ @param Size Size of the given physical memory.
+ @param StackBase Base address of stack.
+ @param StackSize Size of stack.
+
+ @retval TRUE Page table should be split.
+ @retval FALSE Page table should not be split.
+**/
+BOOLEAN
+ToSplitPageTable (
+ IN EFI_PHYSICAL_ADDRESS Address,
+ IN UINTN Size,
+ IN EFI_PHYSICAL_ADDRESS StackBase,
+ IN UINTN StackSize
+ )
+{
+ if (IsNullDetectionEnabled () && (Address == 0)) {
+ return TRUE;
+ }
+
+ if (FixedPcdGetBool (PcdCpuStackGuard)) {
+ if ((StackBase >= Address) && (StackBase < (Address + Size))) {
+ return TRUE;
+ }
+ }
+
+ if (IsSetNxForStack ()) {
+ if ((Address < StackBase + StackSize) && ((Address + Size) > StackBase)) {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/**
+ Initialize a buffer pool for page table use only.
+
+ To reduce the potential split operation on page table, the pages reserved for
+ page table should be allocated in the times of PAGE_TABLE_POOL_UNIT_PAGES and
+ at the boundary of PAGE_TABLE_POOL_ALIGNMENT. So the page pool is always
+ initialized with number of pages greater than or equal to the given PoolPages.
+
+ Once the pages in the pool are used up, this method should be called again to
+ reserve at least another PAGE_TABLE_POOL_UNIT_PAGES. But usually this won't
+ happen in practice.
+
+ @param PoolPages The least page number of the pool to be created.
+
+ @retval TRUE The pool is initialized successfully.
+ @retval FALSE The memory is out of resource.
+**/
+BOOLEAN
+InitializePageTablePool (
+ IN UINTN PoolPages
+ )
+{
+ VOID *Buffer;
+
+ DEBUG ((DEBUG_INFO, "InitializePageTablePool PoolPages=%d\n", PoolPages));
+
+ //
+ // Always reserve at least PAGE_TABLE_POOL_UNIT_PAGES, including one page for
+ // header.
+ //
+ PoolPages += 1; // Add one page for header.
+ PoolPages = ((PoolPages - 1) / PAGE_TABLE_POOL_UNIT_PAGES + 1) *
+ PAGE_TABLE_POOL_UNIT_PAGES;
+ Buffer = AllocateAlignedPages (PoolPages, PAGE_TABLE_POOL_ALIGNMENT);
+ if (Buffer == NULL) {
+ DEBUG ((DEBUG_ERROR, "ERROR: Out of aligned pages\r\n"));
+ return FALSE;
+ }
+
+ //
+ // Link all pools into a list for easier track later.
+ //
+ if (mPageTablePool == NULL) {
+ mPageTablePool = Buffer;
+ mPageTablePool->NextPool = mPageTablePool;
+ } else {
+ ((PAGE_TABLE_POOL *)Buffer)->NextPool = mPageTablePool->NextPool;
+ mPageTablePool->NextPool = Buffer;
+ mPageTablePool = Buffer;
+ }
+
+ //
+ // Reserve one page for pool header.
+ //
+ mPageTablePool->FreePages = PoolPages - 1;
+ mPageTablePool->Offset = EFI_PAGES_TO_SIZE (1);
+
+ return TRUE;
+}
+
+/**
+ This API provides a way to allocate memory for page table.
+
+ This API can be called more than once to allocate memory for page tables.
+
+ Allocates the number of 4KB pages and returns a pointer to the allocated
+ buffer. The buffer returned is aligned on a 4KB boundary.
+
+ If Pages is 0, then NULL is returned.
+ If there is not enough memory remaining to satisfy the request, then NULL is
+ returned.
+
+ @param Pages The number of 4 KB pages to allocate.
+
+ @return A pointer to the allocated buffer or NULL if allocation fails.
+
+**/
+VOID *
+AllocatePageTableMemory (
+ IN UINTN Pages
+ )
+{
+ VOID *Buffer;
+
+ if (Pages == 0) {
+ return NULL;
+ }
+
+ DEBUG ((DEBUG_INFO, "AllocatePageTableMemory. mPageTablePool=%p, Pages=%d\n", mPageTablePool, Pages));
+ //
+ // Renew the pool if necessary.
+ //
+ if ((mPageTablePool == NULL) ||
+ (Pages > mPageTablePool->FreePages))
+ {
+ if (!InitializePageTablePool (Pages)) {
+ return NULL;
+ }
+ }
+
+ Buffer = (UINT8 *)mPageTablePool + mPageTablePool->Offset;
+
+ mPageTablePool->Offset += EFI_PAGES_TO_SIZE (Pages);
+ mPageTablePool->FreePages -= Pages;
+
+ DEBUG ((
+ DEBUG_INFO,
+ "%a:%a: Buffer=0x%Lx Pages=%ld\n",
+ gEfiCallerBaseName,
+ __FUNCTION__,
+ Buffer,
+ Pages
+ ));
+
+ return Buffer;
+}
+
+/**
+ Split 2M page to 4K.
+
+ @param[in] PhysicalAddress Start physical address the 2M page covered.
+ @param[in, out] PageEntry2M Pointer to 2M page entry.
+ @param[in] StackBase Stack base address.
+ @param[in] StackSize Stack size.
+
+**/
+VOID
+Split2MPageTo4K (
+ IN EFI_PHYSICAL_ADDRESS PhysicalAddress,
+ IN OUT UINT64 *PageEntry2M,
+ IN EFI_PHYSICAL_ADDRESS StackBase,
+ IN UINTN StackSize
+ )
+{
+ EFI_PHYSICAL_ADDRESS PhysicalAddress4K;
+ UINTN IndexOfPageTableEntries;
+ PAGE_TABLE_4K_ENTRY *PageTableEntry;
+
+ DEBUG ((DEBUG_INFO, "Split2MPageTo4K\n"));
+
+ PageTableEntry = AllocatePageTableMemory (1);
+
+ if (PageTableEntry == NULL) {
+ ASSERT (FALSE);
+ return;
+ }
+
+ //
+ // Fill in 2M page entry.
+ //
+ *PageEntry2M = (UINT64)(UINTN)PageTableEntry | IA32_PG_P | IA32_PG_RW;
+
+ PhysicalAddress4K = PhysicalAddress;
+ for (IndexOfPageTableEntries = 0; IndexOfPageTableEntries < 512; IndexOfPageTableEntries++, PageTableEntry++, PhysicalAddress4K += SIZE_4KB) {
+ //
+ // Fill in the Page Table entries
+ //
+ PageTableEntry->Uint64 = (UINT64)PhysicalAddress4K;
+ PageTableEntry->Bits.ReadWrite = 1;
+
+ if ((IsNullDetectionEnabled () && (PhysicalAddress4K == 0)) ||
+ (FixedPcdGetBool (PcdCpuStackGuard) && (PhysicalAddress4K == StackBase)))
+ {
+ PageTableEntry->Bits.Present = 0;
+ } else {
+ PageTableEntry->Bits.Present = 1;
+ }
+
+ if ( IsSetNxForStack ()
+ && (PhysicalAddress4K >= StackBase)
+ && (PhysicalAddress4K < StackBase + StackSize))
+ {
+ //
+ // Set Nx bit for stack.
+ //
+ PageTableEntry->Bits.Nx = 1;
+ }
+ }
+}
+
+/**
+ Split 1G page to 2M.
+
+ @param[in] PhysicalAddress Start physical address the 1G page covered.
+ @param[in, out] PageEntry1G Pointer to 1G page entry.
+ @param[in] StackBase Stack base address.
+ @param[in] StackSize Stack size.
+
+**/
+VOID
+Split1GPageTo2M (
+ IN EFI_PHYSICAL_ADDRESS PhysicalAddress,
+ IN OUT UINT64 *PageEntry1G,
+ IN EFI_PHYSICAL_ADDRESS StackBase,
+ IN UINTN StackSize
+ )
+{
+ EFI_PHYSICAL_ADDRESS PhysicalAddress2M;
+ UINTN IndexOfPageDirectoryEntries;
+ PAGE_TABLE_ENTRY *PageDirectoryEntry;
+
+ PageDirectoryEntry = AllocatePageTableMemory (1);
+
+ if (PageDirectoryEntry == NULL) {
+ ASSERT (FALSE);
+ return;
+ }
+
+ //
+ // Fill in 1G page entry.
+ //
+ *PageEntry1G = (UINT64)(UINTN)PageDirectoryEntry | IA32_PG_P | IA32_PG_RW;
+
+ PhysicalAddress2M = PhysicalAddress;
+ for (IndexOfPageDirectoryEntries = 0; IndexOfPageDirectoryEntries < 512; IndexOfPageDirectoryEntries++, PageDirectoryEntry++, PhysicalAddress2M += SIZE_2MB) {
+ if (ToSplitPageTable (PhysicalAddress2M, SIZE_2MB, StackBase, StackSize)) {
+ //
+ // Need to split this 2M page that covers NULL or stack range.
+ //
+ Split2MPageTo4K (PhysicalAddress2M, (UINT64 *)PageDirectoryEntry, StackBase, StackSize);
+ } else {
+ //
+ // Fill in the Page Directory entries
+ //
+ PageDirectoryEntry->Uint64 = (UINT64)PhysicalAddress2M;
+ PageDirectoryEntry->Bits.ReadWrite = 1;
+ PageDirectoryEntry->Bits.Present = 1;
+ PageDirectoryEntry->Bits.MustBe1 = 1;
+ }
+ }
+}
+
+/**
+ Set one page of page table pool memory to be read-only.
+
+ @param[in] PageTableBase Base address of page table (CR3).
+ @param[in] Address Start address of a page to be set as read-only.
+ @param[in] Level4Paging Level 4 paging flag.
+
+**/
+VOID
+SetPageTablePoolReadOnly (
+ IN UINTN PageTableBase,
+ IN EFI_PHYSICAL_ADDRESS Address,
+ IN BOOLEAN Level4Paging
+ )
+{
+ UINTN Index;
+ UINTN EntryIndex;
+ EFI_PHYSICAL_ADDRESS PhysicalAddress;
+ UINT64 *PageTable;
+ UINT64 *NewPageTable;
+ UINT64 PageAttr;
+ UINTN Level;
+ UINT64 PoolUnitSize;
+
+ if (PageTableBase == 0) {
+ ASSERT (FALSE);
+ return;
+ }
+
+ //
+ // Since the page table is always from page table pool, which is always
+ // located at the boundary of PcdPageTablePoolAlignment, we just need to
+ // set the whole pool unit to be read-only.
+ //
+ Address = Address & PAGE_TABLE_POOL_ALIGN_MASK;
+
+ PageTable = (UINT64 *)(UINTN)PageTableBase;
+ PoolUnitSize = PAGE_TABLE_POOL_UNIT_SIZE;
+
+ for (Level = (Level4Paging) ? 4 : 3; Level > 0; --Level) {
+ Index = ((UINTN)RShiftU64 (Address, mLevelShift[Level]));
+ Index &= PAGING_PAE_INDEX_MASK;
+
+ PageAttr = PageTable[Index];
+ if ((PageAttr & IA32_PG_PS) == 0) {
+ //
+ // Go to next level of table.
+ //
+ PageTable = (UINT64 *)(UINTN)(PageAttr & PAGING_4K_ADDRESS_MASK_64);
+ continue;
+ }
+
+ if (PoolUnitSize >= mLevelSize[Level]) {
+ //
+ // Clear R/W bit if current page granularity is not larger than pool unit
+ // size.
+ //
+ if ((PageAttr & IA32_PG_RW) != 0) {
+ while (PoolUnitSize > 0) {
+ //
+ // PAGE_TABLE_POOL_UNIT_SIZE and PAGE_TABLE_POOL_ALIGNMENT are fit in
+ // one page (2MB). Then we don't need to update attributes for pages
+ // crossing page directory. ASSERT below is for that purpose.
+ //
+ ASSERT (Index < EFI_PAGE_SIZE/sizeof (UINT64));
+
+ PageTable[Index] &= ~(UINT64)IA32_PG_RW;
+ PoolUnitSize -= mLevelSize[Level];
+
+ ++Index;
+ }
+ }
+
+ break;
+ } else {
+ //
+ // The smaller granularity of page must be needed.
+ //
+ ASSERT (Level > 1);
+
+ NewPageTable = AllocatePageTableMemory (1);
+
+ if (NewPageTable == NULL) {
+ ASSERT (FALSE);
+ return;
+ }
+
+ PhysicalAddress = PageAttr & mLevelMask[Level];
+ for (EntryIndex = 0;
+ EntryIndex < EFI_PAGE_SIZE/sizeof (UINT64);
+ ++EntryIndex)
+ {
+ NewPageTable[EntryIndex] = PhysicalAddress |
+ IA32_PG_P | IA32_PG_RW;
+ if (Level > 2) {
+ NewPageTable[EntryIndex] |= IA32_PG_PS;
+ }
+
+ PhysicalAddress += mLevelSize[Level - 1];
+ }
+
+ PageTable[Index] = (UINT64)(UINTN)NewPageTable |
+ IA32_PG_P | IA32_PG_RW;
+ PageTable = NewPageTable;
+ }
+ }
+}
+
+/**
+ Prevent the memory pages used for page table from been overwritten.
+
+ @param[in] PageTableBase Base address of page table (CR3).
+ @param[in] Level4Paging Level 4 paging flag.
+
+**/
+VOID
+EnablePageTableProtection (
+ IN UINTN PageTableBase,
+ IN BOOLEAN Level4Paging
+ )
+{
+ PAGE_TABLE_POOL *HeadPool;
+ PAGE_TABLE_POOL *Pool;
+ UINT64 PoolSize;
+ EFI_PHYSICAL_ADDRESS Address;
+
+ DEBUG ((DEBUG_INFO, "EnablePageTableProtection\n"));
+
+ if (mPageTablePool == NULL) {
+ return;
+ }
+
+ //
+ // Disable write protection, because we need to mark page table to be write
+ // protected.
+ //
+ AsmWriteCr0 (AsmReadCr0 () & ~CR0_WP);
+
+ //
+ // SetPageTablePoolReadOnly might update mPageTablePool. It's safer to
+ // remember original one in advance.
+ //
+ HeadPool = mPageTablePool;
+ Pool = HeadPool;
+ do {
+ Address = (EFI_PHYSICAL_ADDRESS)(UINTN)Pool;
+ PoolSize = Pool->Offset + EFI_PAGES_TO_SIZE (Pool->FreePages);
+
+ //
+ // The size of one pool must be multiple of PAGE_TABLE_POOL_UNIT_SIZE, which
+ // is one of page size of the processor (2MB by default). Let's apply the
+ // protection to them one by one.
+ //
+ while (PoolSize > 0) {
+ SetPageTablePoolReadOnly (PageTableBase, Address, Level4Paging);
+ Address += PAGE_TABLE_POOL_UNIT_SIZE;
+ PoolSize -= PAGE_TABLE_POOL_UNIT_SIZE;
+ }
+
+ Pool = Pool->NextPool;
+ } while (Pool != HeadPool);
+
+ //
+ // Enable write protection, after page table attribute updated.
+ //
+ AsmWriteCr0 (AsmReadCr0 () | CR0_WP);
+}
+
+/**
+ Allocates and fills in the Page Directory and Page Table Entries to
+ establish a 1:1 Virtual to Physical mapping.
+
+ @param[in] StackBase Stack base address.
+ @param[in] StackSize Stack size.
+
+ @return The address of 4 level page map.
+
+**/
+UINTN
+CreateIdentityMappingPageTables (
+ IN EFI_PHYSICAL_ADDRESS StackBase,
+ IN UINTN StackSize
+ )
+{
+ UINT32 RegEax;
+ UINT32 RegEdx;
+ UINT8 PhysicalAddressBits;
+ EFI_PHYSICAL_ADDRESS PageAddress;
+ UINTN IndexOfPml5Entries;
+ UINTN IndexOfPml4Entries;
+ UINTN IndexOfPdpEntries;
+ UINTN IndexOfPageDirectoryEntries;
+ UINT32 NumberOfPml5EntriesNeeded;
+ UINT32 NumberOfPml4EntriesNeeded;
+ UINT32 NumberOfPdpEntriesNeeded;
+ PAGE_MAP_AND_DIRECTORY_POINTER *PageMapLevel5Entry;
+ PAGE_MAP_AND_DIRECTORY_POINTER *PageMapLevel4Entry;
+ PAGE_MAP_AND_DIRECTORY_POINTER *PageMap;
+ PAGE_MAP_AND_DIRECTORY_POINTER *PageDirectoryPointerEntry;
+ PAGE_TABLE_ENTRY *PageDirectoryEntry;
+ UINTN TotalPagesNum;
+ UINTN BigPageAddress;
+ VOID *Hob;
+ BOOLEAN Page5LevelSupport;
+ BOOLEAN Page1GSupport;
+ PAGE_TABLE_1G_ENTRY *PageDirectory1GEntry;
+ IA32_CR4 Cr4;
+
+ //
+ // Set PageMapLevel5Entry to suppress incorrect compiler/analyzer warnings
+ //
+ PageMapLevel5Entry = NULL;
+
+ Page1GSupport = FALSE;
+ if (FixedPcdGetBool (PcdUse1GPageTable)) {
+ AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
+ if (RegEax >= 0x80000001) {
+ AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx);
+ if ((RegEdx & BIT26) != 0) {
+ Page1GSupport = TRUE;
+ }
+ }
+ }
+
+ //
+ // Get physical address bits supported.
+ //
+ Hob = GetFirstHob (EFI_HOB_TYPE_CPU);
+ if (Hob == NULL) {
+ ASSERT (FALSE);
+ return 0;
+ }
+
+ PhysicalAddressBits = ((EFI_HOB_CPU *)Hob)->SizeOfMemorySpace;
+
+ //
+ // CPU will already have LA57 enabled so just check CR4
+ Cr4.UintN = AsmReadCr4 ();
+ Page5LevelSupport = (Cr4.Bits.LA57 ? TRUE : FALSE);
+
+ DEBUG ((
+ DEBUG_INFO,
+ "AddressBits=%u 5LevelPaging=%u 1GPage=%u \n",
+ PhysicalAddressBits,
+ Page5LevelSupport,
+ Page1GSupport
+ ));
+
+ //
+ // Calculate the table entries needed.
+ //
+ NumberOfPml5EntriesNeeded = 1;
+ if (PhysicalAddressBits > 48) {
+ NumberOfPml5EntriesNeeded = (UINT32)LShiftU64 (1, PhysicalAddressBits - 48);
+ PhysicalAddressBits = 48;
+ }
+
+ NumberOfPml4EntriesNeeded = 1;
+ if (PhysicalAddressBits > 39) {
+ NumberOfPml4EntriesNeeded = (UINT32)LShiftU64 (1, PhysicalAddressBits - 39);
+ PhysicalAddressBits = 39;
+ }
+
+ NumberOfPdpEntriesNeeded = 1;
+ ASSERT (PhysicalAddressBits > 30);
+ NumberOfPdpEntriesNeeded = (UINT32)LShiftU64 (1, PhysicalAddressBits - 30);
+
+ //
+ // Pre-allocate big pages to avoid later allocations.
+ //
+ if (!Page1GSupport) {
+ TotalPagesNum = ((NumberOfPdpEntriesNeeded + 1) * NumberOfPml4EntriesNeeded + 1) * NumberOfPml5EntriesNeeded + 1;
+ } else {
+ TotalPagesNum = (NumberOfPml4EntriesNeeded + 1) * NumberOfPml5EntriesNeeded + 1;
+ }
+
+ //
+ // Substract the one page occupied by PML5 entries if 5-Level Paging is disabled.
+ //
+ if (!Page5LevelSupport) {
+ TotalPagesNum--;
+ }
+
+ DEBUG ((
+ DEBUG_INFO,
+ "Pml5=%u Pml4=%u Pdp=%u TotalPage=%Lu\n",
+ NumberOfPml5EntriesNeeded,
+ NumberOfPml4EntriesNeeded,
+ NumberOfPdpEntriesNeeded,
+ (UINT64)TotalPagesNum
+ ));
+
+ BigPageAddress = (UINTN)AllocatePageTableMemory (TotalPagesNum);
+ if (BigPageAddress == 0) {
+ ASSERT (FALSE);
+ return 0;
+ }
+
+ DEBUG ((DEBUG_INFO, "BigPageAddress = 0x%llx\n", BigPageAddress));
+
+ //
+ // By architecture only one PageMapLevel4 exists - so lets allocate storage for it.
+ //
+ PageMap = (VOID *)BigPageAddress;
+ if (Page5LevelSupport) {
+ //
+ // By architecture only one PageMapLevel5 exists - so lets allocate storage for it.
+ //
+ PageMapLevel5Entry = PageMap;
+ BigPageAddress += SIZE_4KB;
+ }
+
+ PageAddress = 0;
+
+ for ( IndexOfPml5Entries = 0
+ ; IndexOfPml5Entries < NumberOfPml5EntriesNeeded
+ ; IndexOfPml5Entries++)
+ {
+ //
+ // Each PML5 entry points to a page of PML4 entires.
+ // So lets allocate space for them and fill them in in the IndexOfPml4Entries loop.
+ // When 5-Level Paging is disabled, below allocation happens only once.
+ //
+ PageMapLevel4Entry = (VOID *)BigPageAddress;
+ BigPageAddress += SIZE_4KB;
+
+ if (Page5LevelSupport) {
+ //
+ // Make a PML5 Entry
+ //
+ PageMapLevel5Entry->Uint64 = (UINT64)(UINTN)PageMapLevel4Entry;
+ PageMapLevel5Entry->Bits.ReadWrite = 1;
+ PageMapLevel5Entry->Bits.Present = 1;
+ PageMapLevel5Entry++;
+ }
+
+ for ( IndexOfPml4Entries = 0
+ ; IndexOfPml4Entries < (NumberOfPml5EntriesNeeded == 1 ? NumberOfPml4EntriesNeeded : 512)
+ ; IndexOfPml4Entries++, PageMapLevel4Entry++)
+ {
+ //
+ // Each PML4 entry points to a page of Page Directory Pointer entires.
+ // So lets allocate space for them and fill them in in the IndexOfPdpEntries loop.
+ //
+ PageDirectoryPointerEntry = (VOID *)BigPageAddress;
+ BigPageAddress += SIZE_4KB;
+
+ //
+ // Make a PML4 Entry
+ //
+ PageMapLevel4Entry->Uint64 = (UINT64)(UINTN)PageDirectoryPointerEntry;
+ PageMapLevel4Entry->Bits.ReadWrite = 1;
+ PageMapLevel4Entry->Bits.Present = 1;
+
+ if (Page1GSupport) {
+ PageDirectory1GEntry = (VOID *)PageDirectoryPointerEntry;
+
+ for (IndexOfPageDirectoryEntries = 0; IndexOfPageDirectoryEntries < 512; IndexOfPageDirectoryEntries++, PageDirectory1GEntry++, PageAddress += SIZE_1GB) {
+ if (ToSplitPageTable (PageAddress, SIZE_1GB, StackBase, StackSize)) {
+ Split1GPageTo2M (
+ PageAddress,
+ (UINT64 *)PageDirectory1GEntry,
+ StackBase,
+ StackSize
+ );
+ } else {
+ //
+ // Fill in the Page Directory entries
+ //
+ PageDirectory1GEntry->Uint64 = (UINT64)PageAddress;
+ PageDirectory1GEntry->Bits.ReadWrite = 1;
+ PageDirectory1GEntry->Bits.Present = 1;
+ PageDirectory1GEntry->Bits.MustBe1 = 1;
+ }
+ }
+ } else {
+ for ( IndexOfPdpEntries = 0
+ ; IndexOfPdpEntries < (NumberOfPml4EntriesNeeded == 1 ? NumberOfPdpEntriesNeeded : 512)
+ ; IndexOfPdpEntries++, PageDirectoryPointerEntry++)
+ {
+ //
+ // Each Directory Pointer entries points to a page of Page Directory entires.
+ // So allocate space for them and fill them in in the IndexOfPageDirectoryEntries loop.
+ //
+ PageDirectoryEntry = (VOID *)BigPageAddress;
+ BigPageAddress += SIZE_4KB;
+
+ //
+ // Fill in a Page Directory Pointer Entries
+ //
+ PageDirectoryPointerEntry->Uint64 = (UINT64)(UINTN)PageDirectoryEntry;
+ PageDirectoryPointerEntry->Bits.ReadWrite = 1;
+ PageDirectoryPointerEntry->Bits.Present = 1;
+
+ for (IndexOfPageDirectoryEntries = 0; IndexOfPageDirectoryEntries < 512; IndexOfPageDirectoryEntries++, PageDirectoryEntry++, PageAddress += SIZE_2MB) {
+ if (ToSplitPageTable (PageAddress, SIZE_2MB, StackBase, StackSize)) {
+ //
+ // Need to split this 2M page that covers NULL or stack range.
+ //
+ Split2MPageTo4K (PageAddress, (UINT64 *)PageDirectoryEntry, StackBase, StackSize);
+ } else {
+ //
+ // Fill in the Page Directory entries
+ //
+ PageDirectoryEntry->Uint64 = (UINT64)PageAddress;
+ PageDirectoryEntry->Bits.ReadWrite = 1;
+ PageDirectoryEntry->Bits.Present = 1;
+ PageDirectoryEntry->Bits.MustBe1 = 1;
+ }
+ }
+ }
+
+ //
+ // Fill with null entry for unused PDPTE
+ //
+ ZeroMem (PageDirectoryPointerEntry, (512 - IndexOfPdpEntries) * sizeof (PAGE_MAP_AND_DIRECTORY_POINTER));
+ }
+ }
+
+ //
+ // For the PML4 entries we are not using fill in a null entry.
+ //
+ ZeroMem (PageMapLevel4Entry, (512 - IndexOfPml4Entries) * sizeof (PAGE_MAP_AND_DIRECTORY_POINTER));
+ }
+
+ if (Page5LevelSupport) {
+ //
+ // For the PML5 entries we are not using fill in a null entry.
+ //
+ ZeroMem (PageMapLevel5Entry, (512 - IndexOfPml5Entries) * sizeof (PAGE_MAP_AND_DIRECTORY_POINTER));
+ }
+
+ //
+ // Protect the page table by marking the memory used for page table to be
+ // read-only.
+ //
+ EnablePageTableProtection ((UINTN)PageMap, TRUE);
+
+ return (UINTN)PageMap;
+}
diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec
index a8adbe0e6e..b9ca441202 100644
--- a/OvmfPkg/OvmfPkg.dec
+++ b/OvmfPkg/OvmfPkg.dec
@@ -121,6 +121,10 @@
#
PlatformInitLib|Include/Library/PlatformInitLib.h
+ ## @libraryclass PeilessStartupLib
+ #
+ PeilessStartupLib|Include/Library/PeilessStartupLib.h
+
[Guids]
gUefiOvmfPkgTokenSpaceGuid = {0x93bb96af, 0xb9f2, 0x4eb8, {0x94, 0x62, 0xe0, 0xba, 0x74, 0x56, 0x42, 0x36}}
gEfiXenInfoGuid = {0xd3b46f3b, 0xd441, 0x1244, {0x9a, 0x12, 0x0, 0x12, 0x27, 0x3f, 0xc1, 0x4d}}