]> git.proxmox.com Git - mirror_edk2.git/commitdiff
OvmfPkg/BaseMemEncryptTdxLib: Add TDX helper library
authorMin Xu <min.m.xu@intel.com>
Wed, 22 Sep 2021 12:21:18 +0000 (20:21 +0800)
committermergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Sat, 2 Apr 2022 08:15:12 +0000 (08:15 +0000)
RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429

Add Intel Tdx helper library. The library provides the routines to:
-  set or clear Shared bit for a given memory region.
-  query whether TDX is enabled.

Please be noted, there a lot of duplicated codes for Page Table
operations. These codes should be moved to a common library
(PageTablesLib) so that it is more friendly for review and maintain.
There is a new feature requirement
https://bugzilla.tianocore.org/show_bug.cgi?id=847 which is to
implement the library. After the lib is introduced, BaseMemEncryptTdxLib
will be refactored.

Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Brijesh Singh <brijesh.singh@amd.com>
Cc: Erdem Aktas <erdemaktas@google.com>
Cc: James Bottomley <jejb@linux.ibm.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Acked-by: Gerd Hoffmann <kraxel@redhat.com>
Reviewed-by: Jiewen Yao <jiewen.yao@intel.com>
Signed-off-by: Min Xu <min.m.xu@intel.com>
OvmfPkg/Include/Library/MemEncryptTdxLib.h [new file with mode: 0644]
OvmfPkg/Library/BaseMemEncryptTdxLib/BaseMemEncryptTdxLib.inf [new file with mode: 0644]
OvmfPkg/Library/BaseMemEncryptTdxLib/BaseMemEncryptTdxLibNull.inf [new file with mode: 0644]
OvmfPkg/Library/BaseMemEncryptTdxLib/BaseMemoryEncryptionNull.c [new file with mode: 0644]
OvmfPkg/Library/BaseMemEncryptTdxLib/MemoryEncryption.c [new file with mode: 0644]
OvmfPkg/Library/BaseMemEncryptTdxLib/VirtualMemory.h [new file with mode: 0644]
OvmfPkg/OvmfPkg.dec
OvmfPkg/OvmfPkgIa32.dsc
OvmfPkg/OvmfPkgIa32X64.dsc

diff --git a/OvmfPkg/Include/Library/MemEncryptTdxLib.h b/OvmfPkg/Include/Library/MemEncryptTdxLib.h
new file mode 100644 (file)
index 0000000..2350dd4
--- /dev/null
@@ -0,0 +1,81 @@
+/** @file\r
+\r
+  Define Memory Encrypted Virtualization base library helper function\r
+\r
+  Copyright (c) 2020, Intel Corporation. All rights reserved.<BR>\r
+  Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR>\r
+\r
+  SPDX-License-Identifier: BSD-2-Clause-Patent\r
+\r
+**/\r
+\r
+#ifndef MEM_ENCRYPT_TDX_LIB_H_\r
+#define MEM_ENCRYPT_TDX_LIB_H_\r
+\r
+#include <Base.h>\r
+\r
+/**\r
+  Returns boolean to indicate whether to indicate which, if any, memory encryption is enabled\r
+\r
+  @param[in]  Type          Bitmask of encryption technologies to check is enabled\r
+\r
+  @retval TRUE              The encryption type(s) are enabled\r
+  @retval FALSE             The encryption type(s) are not enabled\r
+**/\r
+BOOLEAN\r
+EFIAPI\r
+MemEncryptTdxIsEnabled (\r
+  VOID\r
+  );\r
+\r
+/**\r
+  This function clears memory encryption bit for the memory region specified by\r
+  BaseAddress and NumPages from the current page table context.\r
+\r
+  @param[in]  Cr3BaseAddress          Cr3 Base Address (if zero then use\r
+                                      current CR3)\r
+  @param[in]  BaseAddress             The physical address that is the start\r
+                                      address of a memory region.\r
+  @param[in]  NumPages                The number of pages from start memory\r
+                                      region.\r
+\r
+  @retval RETURN_SUCCESS              The attributes were cleared for the\r
+                                      memory region.\r
+  @retval RETURN_INVALID_PARAMETER    Number of pages is zero.\r
+  @retval RETURN_UNSUPPORTED          Clearing the memory encryption attribute\r
+                                      is not supported\r
+**/\r
+RETURN_STATUS\r
+EFIAPI\r
+MemEncryptTdxSetPageSharedBit (\r
+  IN PHYSICAL_ADDRESS  Cr3BaseAddress,\r
+  IN PHYSICAL_ADDRESS  BaseAddress,\r
+  IN UINTN             NumPages\r
+  );\r
+\r
+/**\r
+  This function sets memory encryption bit for the memory region specified by\r
+  BaseAddress and NumPages from the current page table context.\r
+\r
+  @param[in]  Cr3BaseAddress          Cr3 Base Address (if zero then use\r
+                                      current CR3)\r
+  @param[in]  BaseAddress             The physical address that is the start\r
+                                      address of a memory region.\r
+  @param[in]  NumPages                The number of pages from start memory\r
+                                      region.\r
+\r
+  @retval RETURN_SUCCESS              The attributes were set for the memory\r
+                                      region.\r
+  @retval RETURN_INVALID_PARAMETER    Number of pages is zero.\r
+  @retval RETURN_UNSUPPORTED          Setting the memory encryption attribute\r
+                                      is not supported\r
+**/\r
+RETURN_STATUS\r
+EFIAPI\r
+MemEncryptTdxClearPageSharedBit (\r
+  IN PHYSICAL_ADDRESS  Cr3BaseAddress,\r
+  IN PHYSICAL_ADDRESS  BaseAddress,\r
+  IN UINTN             NumPages\r
+  );\r
+\r
+#endif // _MEM_ENCRYPT_TDX_LIB_H_\r
diff --git a/OvmfPkg/Library/BaseMemEncryptTdxLib/BaseMemEncryptTdxLib.inf b/OvmfPkg/Library/BaseMemEncryptTdxLib/BaseMemEncryptTdxLib.inf
new file mode 100644 (file)
index 0000000..a8abfec
--- /dev/null
@@ -0,0 +1,44 @@
+## @file\r
+#  Library for TDX Memory Encryption\r
+#\r
+#  Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>\r
+#  SPDX-License-Identifier: BSD-2-Clause-Patent\r
+#\r
+#\r
+##\r
+\r
+[Defines]\r
+  INF_VERSION                    = 1.25\r
+  BASE_NAME                      = MemEncryptTdxLib\r
+  FILE_GUID                      = 7E6651B2-B775-4593-A410-FC05B8C61993\r
+  MODULE_TYPE                    = BASE\r
+  VERSION_STRING                 = 1.0\r
+  LIBRARY_CLASS                  = MemEncryptTdxLib|PEIM DXE_DRIVER DXE_RUNTIME_DRIVER DXE_SMM_DRIVER UEFI_DRIVER\r
+\r
+#\r
+# The following information is for reference only and not required by the build\r
+# tools.\r
+#\r
+# VALID_ARCHITECTURES           = X64\r
+#\r
+\r
+[Packages]\r
+  MdeModulePkg/MdeModulePkg.dec\r
+  MdePkg/MdePkg.dec\r
+  OvmfPkg/OvmfPkg.dec\r
+\r
+[Sources]\r
+  VirtualMemory.h\r
+  MemoryEncryption.c\r
+\r
+[LibraryClasses]\r
+  BaseLib\r
+  CacheMaintenanceLib\r
+  CpuLib\r
+  DebugLib\r
+  MemoryAllocationLib\r
+  PcdLib\r
+  TdxLib\r
+\r
+[Pcd]\r
+  gEfiMdePkgTokenSpaceGuid.PcdConfidentialComputingGuestAttr\r
diff --git a/OvmfPkg/Library/BaseMemEncryptTdxLib/BaseMemEncryptTdxLibNull.inf b/OvmfPkg/Library/BaseMemEncryptTdxLib/BaseMemEncryptTdxLibNull.inf
new file mode 100644 (file)
index 0000000..a050edb
--- /dev/null
@@ -0,0 +1,35 @@
+## @file\r
+#  Library for Memory Encryption\r
+#\r
+# Copyright (c) 2020, Intel Corporation. All rights reserved.<BR>\r
+# Copyright (c) 2017 Advanced Micro Devices. All rights reserved.<BR>\r
+#\r
+#  SPDX-License-Identifier: BSD-2-Clause-Patent\r
+#\r
+#\r
+##\r
+\r
+[Defines]\r
+  INF_VERSION                    = 1.25\r
+  BASE_NAME                      = MemEncryptTdxLibNull\r
+  FILE_GUID                      = 3C69C4CA-DE46-44D7-8AA5-6EE51A4E3EA7\r
+  MODULE_TYPE                    = BASE\r
+  VERSION_STRING                 = 1.0\r
+  LIBRARY_CLASS                  = MemEncryptTdxLib|PEIM DXE_DRIVER DXE_RUNTIME_DRIVER DXE_SMM_DRIVER UEFI_DRIVER\r
+\r
+#\r
+# The following information is for reference only and not required by the build\r
+# tools.\r
+#\r
+# VALID_ARCHITECTURES           = X64 IA32\r
+#\r
+\r
+[Packages]\r
+  MdePkg/MdePkg.dec\r
+  OvmfPkg/OvmfPkg.dec\r
+\r
+[Sources]\r
+  BaseMemoryEncryptionNull.c\r
+\r
+[LibraryClasses]\r
+  BaseLib\r
diff --git a/OvmfPkg/Library/BaseMemEncryptTdxLib/BaseMemoryEncryptionNull.c b/OvmfPkg/Library/BaseMemEncryptTdxLib/BaseMemoryEncryptionNull.c
new file mode 100644 (file)
index 0000000..137d467
--- /dev/null
@@ -0,0 +1,90 @@
+/** @file\r
+\r
+  Virtual Memory Management Services to set or clear the memory encryption\r
+\r
+  Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>\r
+  Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR>\r
+\r
+  SPDX-License-Identifier: BSD-2-Clause-Patent\r
+\r
+  Code is derived from MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.c\r
+\r
+**/\r
+\r
+#include <Uefi.h>\r
+#include <Uefi/UefiBaseType.h>\r
+#include <Library/BaseLib.h>\r
+#include <Library/MemEncryptTdxLib.h>\r
+\r
+/**\r
+  Returns boolean to indicate whether to indicate which, if any, memory encryption is enabled\r
+\r
+  @param[in]  Type          Bitmask of encryption technologies to check is enabled\r
+\r
+  @retval TRUE              The encryption type(s) are enabled\r
+  @retval FALSE             The encryption type(s) are not enabled\r
+**/\r
+BOOLEAN\r
+EFIAPI\r
+MemEncryptTdxIsEnabled (\r
+  VOID\r
+  )\r
+{\r
+  return FALSE;\r
+}\r
+\r
+/**\r
+  This function clears memory encryption bit for the memory region specified by\r
+  BaseAddress and NumPages from the current page table context.\r
+\r
+  @param[in]  Cr3BaseAddress          Cr3 Base Address (if zero then use\r
+                                      current CR3)\r
+  @param[in]  BaseAddress             The physical address that is the start\r
+                                      address of a memory region.\r
+  @param[in]  NumPages                The number of pages from start memory\r
+                                      region.\r
+\r
+  @retval RETURN_SUCCESS              The attributes were cleared for the\r
+                                      memory region.\r
+  @retval RETURN_INVALID_PARAMETER    Number of pages is zero.\r
+  @retval RETURN_UNSUPPORTED          Clearing the memory encryption attribute\r
+                                      is not supported\r
+**/\r
+RETURN_STATUS\r
+EFIAPI\r
+MemEncryptTdxSetPageSharedBit (\r
+  IN PHYSICAL_ADDRESS  Cr3BaseAddress,\r
+  IN PHYSICAL_ADDRESS  BaseAddress,\r
+  IN UINTN             NumPages\r
+  )\r
+{\r
+  return EFI_UNSUPPORTED;\r
+}\r
+\r
+/**\r
+  This function sets memory encryption bit for the memory region specified by\r
+  BaseAddress and NumPages from the current page table context.\r
+\r
+  @param[in]  Cr3BaseAddress          Cr3 Base Address (if zero then use\r
+                                      current CR3)\r
+  @param[in]  BaseAddress             The physical address that is the start\r
+                                      address of a memory region.\r
+  @param[in]  NumPages                The number of pages from start memory\r
+                                      region.\r
+\r
+  @retval RETURN_SUCCESS              The attributes were set for the memory\r
+                                      region.\r
+  @retval RETURN_INVALID_PARAMETER    Number of pages is zero.\r
+  @retval RETURN_UNSUPPORTED          Setting the memory encryption attribute\r
+                                      is not supported\r
+**/\r
+RETURN_STATUS\r
+EFIAPI\r
+MemEncryptTdxClearPageSharedBit (\r
+  IN PHYSICAL_ADDRESS  Cr3BaseAddress,\r
+  IN PHYSICAL_ADDRESS  BaseAddress,\r
+  IN UINTN             NumPages\r
+  )\r
+{\r
+  return EFI_UNSUPPORTED;\r
+}\r
diff --git a/OvmfPkg/Library/BaseMemEncryptTdxLib/MemoryEncryption.c b/OvmfPkg/Library/BaseMemEncryptTdxLib/MemoryEncryption.c
new file mode 100644 (file)
index 0000000..9d11c6e
--- /dev/null
@@ -0,0 +1,948 @@
+/** @file\r
+\r
+  Virtual Memory Management Services to set or clear the memory encryption.\r
+\r
+  Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>\r
+  Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR>\r
+\r
+  SPDX-License-Identifier: BSD-2-Clause-Patent\r
+\r
+  Code is derived from MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.c\r
+\r
+  Note:\r
+  There a lot of duplicated codes for Page Table operations. These\r
+  codes should be moved to a common library (PageTablesLib) so that it is\r
+  more friendly for review and maintain. There is a new feature requirement\r
+  https://bugzilla.tianocore.org/show_bug.cgi?id=847 which is to implement\r
+  the library. After the lib is introduced, this file will be refactored.\r
+\r
+**/\r
+\r
+#include <Uefi.h>\r
+#include <Uefi/UefiBaseType.h>\r
+#include <Library/CpuLib.h>\r
+#include <Library/BaseLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/MemEncryptTdxLib.h>\r
+#include "VirtualMemory.h"\r
+#include <IndustryStandard/Tdx.h>\r
+#include <Library/TdxLib.h>\r
+#include <ConfidentialComputingGuestAttr.h>\r
+\r
+typedef enum {\r
+  SetSharedBit,\r
+  ClearSharedBit\r
+} TDX_PAGETABLE_MODE;\r
+\r
+STATIC PAGE_TABLE_POOL  *mPageTablePool = NULL;\r
+\r
+/**\r
+  Returns boolean to indicate whether to indicate which, if any, memory encryption is enabled\r
+\r
+  @param[in]  Type          Bitmask of encryption technologies to check is enabled\r
+\r
+  @retval TRUE              The encryption type(s) are enabled\r
+  @retval FALSE             The encryption type(s) are not enabled\r
+**/\r
+BOOLEAN\r
+EFIAPI\r
+MemEncryptTdxIsEnabled (\r
+  VOID\r
+  )\r
+{\r
+  return CC_GUEST_IS_TDX (PcdGet64 (PcdConfidentialComputingGuestAttr));\r
+}\r
+\r
+/**\r
+  Get the memory encryption mask\r
+\r
+  @param[out]      EncryptionMask        contains the pte mask.\r
+\r
+**/\r
+STATIC\r
+UINT64\r
+GetMemEncryptionAddressMask (\r
+  VOID\r
+  )\r
+{\r
+  return TdSharedPageMask ();\r
+}\r
+\r
+/**\r
+  Initialize a buffer pool for page table use only.\r
+\r
+  To reduce the potential split operation on page table, the pages reserved for\r
+  page table should be allocated in the times of PAGE_TABLE_POOL_UNIT_PAGES and\r
+  at the boundary of PAGE_TABLE_POOL_ALIGNMENT. So the page pool is always\r
+  initialized with number of pages greater than or equal to the given\r
+  PoolPages.\r
+\r
+  Once the pages in the pool are used up, this method should be called again to\r
+  reserve at least another PAGE_TABLE_POOL_UNIT_PAGES. Usually this won't\r
+  happen often in practice.\r
+\r
+  @param[in] PoolPages      The least page number of the pool to be created.\r
+\r
+  @retval TRUE    The pool is initialized successfully.\r
+  @retval FALSE   The memory is out of resource.\r
+**/\r
+STATIC\r
+BOOLEAN\r
+InitializePageTablePool (\r
+  IN  UINTN  PoolPages\r
+  )\r
+{\r
+  VOID  *Buffer;\r
+\r
+  //\r
+  // Always reserve at least PAGE_TABLE_POOL_UNIT_PAGES, including one page for\r
+  // header.\r
+  //\r
+  PoolPages += 1;   // Add one page for header.\r
+  PoolPages  = ((PoolPages - 1) / PAGE_TABLE_POOL_UNIT_PAGES + 1) *\r
+               PAGE_TABLE_POOL_UNIT_PAGES;\r
+  Buffer = AllocateAlignedPages (PoolPages, PAGE_TABLE_POOL_ALIGNMENT);\r
+  if (Buffer == NULL) {\r
+    DEBUG ((DEBUG_ERROR, "ERROR: Out of aligned pages\r\n"));\r
+    return FALSE;\r
+  }\r
+\r
+  //\r
+  // Link all pools into a list for easier track later.\r
+  //\r
+  if (mPageTablePool == NULL) {\r
+    mPageTablePool           = Buffer;\r
+    mPageTablePool->NextPool = mPageTablePool;\r
+  } else {\r
+    ((PAGE_TABLE_POOL *)Buffer)->NextPool = mPageTablePool->NextPool;\r
+    mPageTablePool->NextPool              = Buffer;\r
+    mPageTablePool                        = Buffer;\r
+  }\r
+\r
+  //\r
+  // Reserve one page for pool header.\r
+  //\r
+  mPageTablePool->FreePages = PoolPages - 1;\r
+  mPageTablePool->Offset    = EFI_PAGES_TO_SIZE (1);\r
+\r
+  return TRUE;\r
+}\r
+\r
+/**\r
+  This API provides a way to allocate memory for page table.\r
+\r
+  This API can be called more than once to allocate memory for page tables.\r
+\r
+  Allocates the number of 4KB pages and returns a pointer to the allocated\r
+  buffer. The buffer returned is aligned on a 4KB boundary.\r
+\r
+  If Pages is 0, then NULL is returned.\r
+  If there is not enough memory remaining to satisfy the request, then NULL is\r
+  returned.\r
+\r
+  @param  Pages                 The number of 4 KB pages to allocate.\r
+\r
+  @return A pointer to the allocated buffer or NULL if allocation fails.\r
+\r
+**/\r
+STATIC\r
+VOID *\r
+EFIAPI\r
+AllocatePageTableMemory (\r
+  IN UINTN  Pages\r
+  )\r
+{\r
+  VOID  *Buffer;\r
+\r
+  if (Pages == 0) {\r
+    return NULL;\r
+  }\r
+\r
+  //\r
+  // Renew the pool if necessary.\r
+  //\r
+  if ((mPageTablePool == NULL) ||\r
+      (Pages > mPageTablePool->FreePages))\r
+  {\r
+    if (!InitializePageTablePool (Pages)) {\r
+      return NULL;\r
+    }\r
+  }\r
+\r
+  Buffer = (UINT8 *)mPageTablePool + mPageTablePool->Offset;\r
+\r
+  mPageTablePool->Offset    += EFI_PAGES_TO_SIZE (Pages);\r
+  mPageTablePool->FreePages -= Pages;\r
+\r
+  DEBUG ((\r
+    DEBUG_VERBOSE,\r
+    "%a:%a: Buffer=0x%Lx Pages=%ld\n",\r
+    gEfiCallerBaseName,\r
+    __FUNCTION__,\r
+    Buffer,\r
+    Pages\r
+    ));\r
+\r
+  return Buffer;\r
+}\r
+\r
+/**\r
+  Split 2M page to 4K.\r
+\r
+  @param[in]      PhysicalAddress       Start physical address the 2M page\r
+                                        covered.\r
+  @param[in, out] PageEntry2M           Pointer to 2M page entry.\r
+  @param[in]      StackBase             Stack base address.\r
+  @param[in]      StackSize             Stack size.\r
+\r
+**/\r
+STATIC\r
+VOID\r
+Split2MPageTo4K (\r
+  IN        PHYSICAL_ADDRESS  PhysicalAddress,\r
+  IN  OUT   UINT64            *PageEntry2M,\r
+  IN        PHYSICAL_ADDRESS  StackBase,\r
+  IN        UINTN             StackSize,\r
+  IN        UINT64            AddressEncMask\r
+  )\r
+{\r
+  PHYSICAL_ADDRESS     PhysicalAddress4K;\r
+  UINTN                IndexOfPageTableEntries;\r
+  PAGE_TABLE_4K_ENTRY  *PageTableEntry, *PageTableEntry1;\r
+\r
+  PageTableEntry = AllocatePageTableMemory (1);\r
+\r
+  PageTableEntry1 = PageTableEntry;\r
+\r
+  if (PageTableEntry == NULL) {\r
+    ASSERT (FALSE);\r
+    return;\r
+  }\r
+\r
+  PhysicalAddress4K = PhysicalAddress;\r
+  for (IndexOfPageTableEntries = 0;\r
+       IndexOfPageTableEntries < 512;\r
+       (IndexOfPageTableEntries++,\r
+        PageTableEntry++,\r
+        PhysicalAddress4K += SIZE_4KB))\r
+  {\r
+    //\r
+    // Fill in the Page Table entries\r
+    //\r
+    PageTableEntry->Uint64         = (UINT64)PhysicalAddress4K | AddressEncMask;\r
+    PageTableEntry->Bits.ReadWrite = 1;\r
+    PageTableEntry->Bits.Present   = 1;\r
+    if ((PhysicalAddress4K >= StackBase) &&\r
+        (PhysicalAddress4K < StackBase + StackSize))\r
+    {\r
+      //\r
+      // Set Nx bit for stack.\r
+      //\r
+      PageTableEntry->Bits.Nx = 1;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Fill in 2M page entry.\r
+  //\r
+  *PageEntry2M = ((UINT64)(UINTN)PageTableEntry1 |\r
+                  IA32_PG_P | IA32_PG_RW | AddressEncMask);\r
+}\r
+\r
+/**\r
+  Set one page of page table pool memory to be read-only.\r
+\r
+  @param[in] PageTableBase    Base address of page table (CR3).\r
+  @param[in] Address          Start address of a page to be set as read-only.\r
+  @param[in] Level4Paging     Level 4 paging flag.\r
+\r
+**/\r
+STATIC\r
+VOID\r
+SetPageTablePoolReadOnly (\r
+  IN  UINTN                 PageTableBase,\r
+  IN  EFI_PHYSICAL_ADDRESS  Address,\r
+  IN  BOOLEAN               Level4Paging\r
+  )\r
+{\r
+  UINTN                 Index;\r
+  UINTN                 EntryIndex;\r
+  UINT64                AddressEncMask;\r
+  UINT64                ActiveAddressEncMask;\r
+  EFI_PHYSICAL_ADDRESS  PhysicalAddress;\r
+  UINT64                *PageTable;\r
+  UINT64                *NewPageTable;\r
+  UINT64                PageAttr;\r
+  UINT64                LevelSize[5];\r
+  UINT64                LevelMask[5];\r
+  UINTN                 LevelShift[5];\r
+  UINTN                 Level;\r
+  UINT64                PoolUnitSize;\r
+\r
+  if (PageTableBase == 0) {\r
+    ASSERT (FALSE);\r
+    return;\r
+  }\r
+\r
+  //\r
+  // Since the page table is always from page table pool, which is always\r
+  // located at the boundary of PcdPageTablePoolAlignment, we just need to\r
+  // set the whole pool unit to be read-only.\r
+  //\r
+  Address = Address & PAGE_TABLE_POOL_ALIGN_MASK;\r
+\r
+  LevelShift[1] = PAGING_L1_ADDRESS_SHIFT;\r
+  LevelShift[2] = PAGING_L2_ADDRESS_SHIFT;\r
+  LevelShift[3] = PAGING_L3_ADDRESS_SHIFT;\r
+  LevelShift[4] = PAGING_L4_ADDRESS_SHIFT;\r
+\r
+  LevelMask[1] = PAGING_4K_ADDRESS_MASK_64;\r
+  LevelMask[2] = PAGING_2M_ADDRESS_MASK_64;\r
+  LevelMask[3] = PAGING_1G_ADDRESS_MASK_64;\r
+  LevelMask[4] = PAGING_1G_ADDRESS_MASK_64;\r
+\r
+  LevelSize[1] = SIZE_4KB;\r
+  LevelSize[2] = SIZE_2MB;\r
+  LevelSize[3] = SIZE_1GB;\r
+  LevelSize[4] = SIZE_512GB;\r
+\r
+  AddressEncMask = GetMemEncryptionAddressMask () &\r
+                   PAGING_1G_ADDRESS_MASK_64;\r
+  PageTable    = (UINT64 *)(UINTN)PageTableBase;\r
+  PoolUnitSize = PAGE_TABLE_POOL_UNIT_SIZE;\r
+\r
+  for (Level = (Level4Paging) ? 4 : 3; Level > 0; --Level) {\r
+    Index  = ((UINTN)RShiftU64 (Address, LevelShift[Level]));\r
+    Index &= PAGING_PAE_INDEX_MASK;\r
+\r
+    PageAttr             = PageTable[Index];\r
+    ActiveAddressEncMask = GetMemEncryptionAddressMask () & PageAttr;\r
+\r
+    if ((PageAttr & IA32_PG_PS) == 0) {\r
+      //\r
+      // Go to next level of table.\r
+      //\r
+      PageTable = (UINT64 *)(UINTN)(PageAttr & ~AddressEncMask &\r
+                                    PAGING_4K_ADDRESS_MASK_64);\r
+      continue;\r
+    }\r
+\r
+    if (PoolUnitSize >= LevelSize[Level]) {\r
+      //\r
+      // Clear R/W bit if current page granularity is not larger than pool unit\r
+      // size.\r
+      //\r
+      if ((PageAttr & IA32_PG_RW) != 0) {\r
+        while (PoolUnitSize > 0) {\r
+          //\r
+          // PAGE_TABLE_POOL_UNIT_SIZE and PAGE_TABLE_POOL_ALIGNMENT are fit in\r
+          // one page (2MB). Then we don't need to update attributes for pages\r
+          // crossing page directory. ASSERT below is for that purpose.\r
+          //\r
+          ASSERT (Index < EFI_PAGE_SIZE/sizeof (UINT64));\r
+\r
+          PageTable[Index] &= ~(UINT64)IA32_PG_RW;\r
+          PoolUnitSize     -= LevelSize[Level];\r
+\r
+          ++Index;\r
+        }\r
+      }\r
+\r
+      break;\r
+    } else {\r
+      //\r
+      // The smaller granularity of page must be needed.\r
+      //\r
+      ASSERT (Level > 1);\r
+\r
+      NewPageTable = AllocatePageTableMemory (1);\r
+      if (NewPageTable == NULL) {\r
+        ASSERT (FALSE);\r
+        return;\r
+      }\r
+\r
+      PhysicalAddress = PageAttr & LevelMask[Level];\r
+      for (EntryIndex = 0;\r
+           EntryIndex < EFI_PAGE_SIZE/sizeof (UINT64);\r
+           ++EntryIndex)\r
+      {\r
+        NewPageTable[EntryIndex] = PhysicalAddress  | ActiveAddressEncMask |\r
+                                   IA32_PG_P | IA32_PG_RW;\r
+        if (Level > 2) {\r
+          NewPageTable[EntryIndex] |= IA32_PG_PS;\r
+        }\r
+\r
+        PhysicalAddress += LevelSize[Level - 1];\r
+      }\r
+\r
+      PageTable[Index] = (UINT64)(UINTN)NewPageTable | ActiveAddressEncMask |\r
+                         IA32_PG_P | IA32_PG_RW;\r
+      PageTable = NewPageTable;\r
+    }\r
+  }\r
+}\r
+\r
+/**\r
+  Prevent the memory pages used for page table from been overwritten.\r
+\r
+  @param[in] PageTableBase    Base address of page table (CR3).\r
+  @param[in] Level4Paging     Level 4 paging flag.\r
+\r
+**/\r
+STATIC\r
+VOID\r
+EnablePageTableProtection (\r
+  IN  UINTN    PageTableBase,\r
+  IN  BOOLEAN  Level4Paging\r
+  )\r
+{\r
+  PAGE_TABLE_POOL       *HeadPool;\r
+  PAGE_TABLE_POOL       *Pool;\r
+  UINT64                PoolSize;\r
+  EFI_PHYSICAL_ADDRESS  Address;\r
+\r
+  if (mPageTablePool == NULL) {\r
+    return;\r
+  }\r
+\r
+  //\r
+  // SetPageTablePoolReadOnly might update mPageTablePool. It's safer to\r
+  // remember original one in advance.\r
+  //\r
+  HeadPool = mPageTablePool;\r
+  Pool     = HeadPool;\r
+  do {\r
+    Address  = (EFI_PHYSICAL_ADDRESS)(UINTN)Pool;\r
+    PoolSize = Pool->Offset + EFI_PAGES_TO_SIZE (Pool->FreePages);\r
+\r
+    //\r
+    // The size of one pool must be multiple of PAGE_TABLE_POOL_UNIT_SIZE,\r
+    // which is one of page size of the processor (2MB by default). Let's apply\r
+    // the protection to them one by one.\r
+    //\r
+    while (PoolSize > 0) {\r
+      SetPageTablePoolReadOnly (PageTableBase, Address, Level4Paging);\r
+      Address  += PAGE_TABLE_POOL_UNIT_SIZE;\r
+      PoolSize -= PAGE_TABLE_POOL_UNIT_SIZE;\r
+    }\r
+\r
+    Pool = Pool->NextPool;\r
+  } while (Pool != HeadPool);\r
+}\r
+\r
+/**\r
+  Split 1G page to 2M.\r
+\r
+  @param[in]      PhysicalAddress       Start physical address the 1G page\r
+                                        covered.\r
+  @param[in, out] PageEntry1G           Pointer to 1G page entry.\r
+  @param[in]      StackBase             Stack base address.\r
+  @param[in]      StackSize             Stack size.\r
+\r
+**/\r
+STATIC\r
+VOID\r
+Split1GPageTo2M (\r
+  IN          PHYSICAL_ADDRESS  PhysicalAddress,\r
+  IN  OUT     UINT64            *PageEntry1G,\r
+  IN          PHYSICAL_ADDRESS  StackBase,\r
+  IN          UINTN             StackSize\r
+  )\r
+{\r
+  PHYSICAL_ADDRESS  PhysicalAddress2M;\r
+  UINTN             IndexOfPageDirectoryEntries;\r
+  PAGE_TABLE_ENTRY  *PageDirectoryEntry;\r
+  UINT64            AddressEncMask;\r
+  UINT64            ActiveAddressEncMask;\r
+\r
+  PageDirectoryEntry = AllocatePageTableMemory (1);\r
+  if (PageDirectoryEntry == NULL) {\r
+    return;\r
+  }\r
+\r
+  AddressEncMask = GetMemEncryptionAddressMask ();\r
+  ASSERT (PageDirectoryEntry != NULL);\r
+\r
+  ActiveAddressEncMask = *PageEntry1G & AddressEncMask;\r
+  //\r
+  // Fill in 1G page entry.\r
+  //\r
+  *PageEntry1G = ((UINT64)(UINTN)PageDirectoryEntry |\r
+                  IA32_PG_P | IA32_PG_RW | ActiveAddressEncMask);\r
+\r
+  PhysicalAddress2M = PhysicalAddress;\r
+  for (IndexOfPageDirectoryEntries = 0;\r
+       IndexOfPageDirectoryEntries < 512;\r
+       (IndexOfPageDirectoryEntries++,\r
+        PageDirectoryEntry++,\r
+        PhysicalAddress2M += SIZE_2MB))\r
+  {\r
+    if ((PhysicalAddress2M < StackBase + StackSize) &&\r
+        ((PhysicalAddress2M + SIZE_2MB) > StackBase))\r
+    {\r
+      //\r
+      // Need to split this 2M page that covers stack range.\r
+      //\r
+      Split2MPageTo4K (\r
+        PhysicalAddress2M,\r
+        (UINT64 *)PageDirectoryEntry,\r
+        StackBase,\r
+        StackSize,\r
+        ActiveAddressEncMask\r
+        );\r
+    } else {\r
+      //\r
+      // Fill in the Page Directory entries\r
+      //\r
+      PageDirectoryEntry->Uint64         = (UINT64)PhysicalAddress2M | ActiveAddressEncMask;\r
+      PageDirectoryEntry->Bits.ReadWrite = 1;\r
+      PageDirectoryEntry->Bits.Present   = 1;\r
+      PageDirectoryEntry->Bits.MustBe1   = 1;\r
+    }\r
+  }\r
+}\r
+\r
+/**\r
+  Set or Clear the memory shared bit\r
+\r
+  @param[in]      PagetablePoint        Page table entry pointer (PTE).\r
+  @param[in]      Mode                  Set or Clear shared bit\r
+\r
+**/\r
+STATIC VOID\r
+SetOrClearSharedBit (\r
+  IN   OUT     UINT64              *PageTablePointer,\r
+  IN           TDX_PAGETABLE_MODE  Mode,\r
+  IN           PHYSICAL_ADDRESS    PhysicalAddress,\r
+  IN           UINT64              Length\r
+  )\r
+{\r
+  UINT64  AddressEncMask;\r
+  UINT64  Status;\r
+\r
+  AddressEncMask = GetMemEncryptionAddressMask ();\r
+\r
+  //\r
+  // Set or clear page table entry. Also, set shared bit in physical address, before calling MapGPA\r
+  //\r
+  if (Mode == SetSharedBit) {\r
+    *PageTablePointer |= AddressEncMask;\r
+    PhysicalAddress   |= AddressEncMask;\r
+  } else {\r
+    *PageTablePointer &= ~AddressEncMask;\r
+    PhysicalAddress   &= ~AddressEncMask;\r
+  }\r
+\r
+  Status = TdVmCall (TDVMCALL_MAPGPA, PhysicalAddress, Length, 0, 0, NULL);\r
+\r
+  //\r
+  // If changing shared to private, must accept-page again\r
+  //\r
+  if (Mode == ClearSharedBit) {\r
+    TdAcceptPages (PhysicalAddress, Length / EFI_PAGE_SIZE, EFI_PAGE_SIZE);\r
+  }\r
+\r
+  DEBUG ((\r
+    DEBUG_VERBOSE,\r
+    "%a:%a: pte=0x%Lx AddressEncMask=0x%Lx Mode=0x%x MapGPA Status=0x%x\n",\r
+    gEfiCallerBaseName,\r
+    __FUNCTION__,\r
+    *PageTablePointer,\r
+    AddressEncMask,\r
+    Mode,\r
+    Status\r
+    ));\r
+}\r
+\r
+/**\r
+ Check the WP status in CR0 register. This bit is used to lock or unlock write\r
+ access to pages marked as read-only.\r
+\r
+  @retval TRUE    Write protection is enabled.\r
+  @retval FALSE   Write protection is disabled.\r
+**/\r
+STATIC\r
+BOOLEAN\r
+IsReadOnlyPageWriteProtected (\r
+  VOID\r
+  )\r
+{\r
+  return ((AsmReadCr0 () & BIT16) != 0);\r
+}\r
+\r
+/**\r
+ Disable Write Protect on pages marked as read-only.\r
+**/\r
+STATIC\r
+VOID\r
+DisableReadOnlyPageWriteProtect (\r
+  VOID\r
+  )\r
+{\r
+  AsmWriteCr0 (AsmReadCr0 () & ~BIT16);\r
+}\r
+\r
+/**\r
+ Enable Write Protect on pages marked as read-only.\r
+**/\r
+VOID\r
+EnableReadOnlyPageWriteProtect (\r
+  VOID\r
+  )\r
+{\r
+  AsmWriteCr0 (AsmReadCr0 () | BIT16);\r
+}\r
+\r
+/**\r
+  This function either sets or clears memory encryption for the memory\r
+  region specified by PhysicalAddress and Length from the current page table\r
+  context.\r
+\r
+  The function iterates through the PhysicalAddress one page at a time, and set\r
+  or clears the memory encryption in the page table. If it encounters\r
+  that a given physical address range is part of large page then it attempts to\r
+  change the attribute at one go (based on size), otherwise it splits the\r
+  large pages into smaller (e.g 2M page into 4K pages) and then try to set or\r
+  clear the shared bit on the smallest page size.\r
+\r
+  @param[in]  Cr3BaseAddress          Cr3 Base Address (if zero then use\r
+                                      current CR3)\r
+  @param[in]  PhysicalAddress         The physical address that is the start\r
+                                      address of a memory region.\r
+  @param[in]  Length                  The length of memory region\r
+  @param[in]  Mode                    Set or Clear mode\r
+\r
+  @retval RETURN_SUCCESS              The attributes were cleared for the\r
+                                      memory region.\r
+  @retval RETURN_INVALID_PARAMETER    Number of pages is zero.\r
+  @retval RETURN_UNSUPPORTED          Setting the memory encyrption attribute\r
+                                      is not supported\r
+**/\r
+STATIC\r
+RETURN_STATUS\r
+EFIAPI\r
+SetMemorySharedOrPrivate (\r
+  IN    PHYSICAL_ADDRESS    Cr3BaseAddress,\r
+  IN    PHYSICAL_ADDRESS    PhysicalAddress,\r
+  IN    UINTN               Length,\r
+  IN    TDX_PAGETABLE_MODE  Mode\r
+  )\r
+{\r
+  PAGE_MAP_AND_DIRECTORY_POINTER  *PageMapLevel4Entry;\r
+  PAGE_MAP_AND_DIRECTORY_POINTER  *PageUpperDirectoryPointerEntry;\r
+  PAGE_MAP_AND_DIRECTORY_POINTER  *PageDirectoryPointerEntry;\r
+  PAGE_TABLE_1G_ENTRY             *PageDirectory1GEntry;\r
+  PAGE_TABLE_ENTRY                *PageDirectory2MEntry;\r
+  PAGE_TABLE_4K_ENTRY             *PageTableEntry;\r
+  UINT64                          PgTableMask;\r
+  UINT64                          AddressEncMask;\r
+  UINT64                          ActiveEncMask;\r
+  BOOLEAN                         IsWpEnabled;\r
+  RETURN_STATUS                   Status;\r
+  IA32_CR4                        Cr4;\r
+  BOOLEAN                         Page5LevelSupport;\r
+\r
+  //\r
+  // Set PageMapLevel4Entry to suppress incorrect compiler/analyzer warnings.\r
+  //\r
+  PageMapLevel4Entry = NULL;\r
+\r
+  DEBUG ((\r
+    DEBUG_VERBOSE,\r
+    "%a:%a: Cr3Base=0x%Lx Physical=0x%Lx Length=0x%Lx Mode=%a\n",\r
+    gEfiCallerBaseName,\r
+    __FUNCTION__,\r
+    Cr3BaseAddress,\r
+    PhysicalAddress,\r
+    (UINT64)Length,\r
+    (Mode == SetSharedBit) ? "Shared" : "Private"\r
+    ));\r
+\r
+  //\r
+  // Check if we have a valid memory encryption mask\r
+  //\r
+  AddressEncMask = GetMemEncryptionAddressMask ();\r
+\r
+  PgTableMask = AddressEncMask | EFI_PAGE_MASK;\r
+\r
+  if (Length == 0) {\r
+    return RETURN_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // Make sure that the page table is changeable.\r
+  //\r
+  IsWpEnabled = IsReadOnlyPageWriteProtected ();\r
+  if (IsWpEnabled) {\r
+    DisableReadOnlyPageWriteProtect ();\r
+  }\r
+\r
+  //\r
+  // If Cr3BaseAddress is not specified then read the current CR3\r
+  //\r
+  if (Cr3BaseAddress == 0) {\r
+    Cr3BaseAddress = AsmReadCr3 ();\r
+  }\r
+\r
+  //\r
+  // CPU will already have LA57 enabled so just check CR4\r
+  //\r
+  Cr4.UintN = AsmReadCr4 ();\r
+\r
+  Page5LevelSupport = (Cr4.Bits.LA57 ? TRUE : FALSE);\r
+  //\r
+  // If 5-level pages, adjust Cr3BaseAddress to point to first 4-level page directory,\r
+  // we will only have 1\r
+  //\r
+  if (Page5LevelSupport) {\r
+    Cr3BaseAddress = *(UINT64 *)Cr3BaseAddress & ~PgTableMask;\r
+  }\r
+\r
+  Status = EFI_SUCCESS;\r
+\r
+  while (Length) {\r
+    PageMapLevel4Entry  = (VOID *)(Cr3BaseAddress & ~PgTableMask);\r
+    PageMapLevel4Entry += PML4_OFFSET (PhysicalAddress);\r
+    if (!PageMapLevel4Entry->Bits.Present) {\r
+      DEBUG ((\r
+        DEBUG_ERROR,\r
+        "%a:%a: bad PML4 for Physical=0x%Lx\n",\r
+        gEfiCallerBaseName,\r
+        __FUNCTION__,\r
+        PhysicalAddress\r
+        ));\r
+      Status = RETURN_NO_MAPPING;\r
+      goto Done;\r
+    }\r
+\r
+    PageDirectory1GEntry = (VOID *)(\r
+                                    (PageMapLevel4Entry->Bits.PageTableBaseAddress <<\r
+                                     12) & ~PgTableMask\r
+                                    );\r
+    PageDirectory1GEntry += PDP_OFFSET (PhysicalAddress);\r
+    if (!PageDirectory1GEntry->Bits.Present) {\r
+      DEBUG ((\r
+        DEBUG_ERROR,\r
+        "%a:%a: bad PDPE for Physical=0x%Lx\n",\r
+        gEfiCallerBaseName,\r
+        __FUNCTION__,\r
+        PhysicalAddress\r
+        ));\r
+      Status = RETURN_NO_MAPPING;\r
+      goto Done;\r
+    }\r
+\r
+    //\r
+    // If the MustBe1 bit is not 1, it's not actually a 1GB entry\r
+    //\r
+    if (PageDirectory1GEntry->Bits.MustBe1) {\r
+      //\r
+      // Valid 1GB page\r
+      // If we have at least 1GB to go, we can just update this entry\r
+      //\r
+      if (!(PhysicalAddress & (BIT30 - 1)) && (Length >= BIT30)) {\r
+        SetOrClearSharedBit (&PageDirectory1GEntry->Uint64, Mode, PhysicalAddress, BIT30);\r
+        DEBUG ((\r
+          DEBUG_VERBOSE,\r
+          "%a:%a: updated 1GB entry for Physical=0x%Lx\n",\r
+          gEfiCallerBaseName,\r
+          __FUNCTION__,\r
+          PhysicalAddress\r
+          ));\r
+        PhysicalAddress += BIT30;\r
+        Length          -= BIT30;\r
+      } else {\r
+        //\r
+        // We must split the page\r
+        //\r
+        DEBUG ((\r
+          DEBUG_VERBOSE,\r
+          "%a:%a: splitting 1GB page for Physical=0x%Lx\n",\r
+          gEfiCallerBaseName,\r
+          __FUNCTION__,\r
+          PhysicalAddress\r
+          ));\r
+        Split1GPageTo2M (\r
+          (UINT64)PageDirectory1GEntry->Bits.PageTableBaseAddress << 30,\r
+          (UINT64 *)PageDirectory1GEntry,\r
+          0,\r
+          0\r
+          );\r
+        continue;\r
+      }\r
+    } else {\r
+      //\r
+      // Actually a PDP\r
+      //\r
+      PageUpperDirectoryPointerEntry =\r
+        (PAGE_MAP_AND_DIRECTORY_POINTER *)PageDirectory1GEntry;\r
+      PageDirectory2MEntry =\r
+        (VOID *)(\r
+                 (PageUpperDirectoryPointerEntry->Bits.PageTableBaseAddress <<\r
+                  12) & ~PgTableMask\r
+                 );\r
+      PageDirectory2MEntry += PDE_OFFSET (PhysicalAddress);\r
+      if (!PageDirectory2MEntry->Bits.Present) {\r
+        DEBUG ((\r
+          DEBUG_ERROR,\r
+          "%a:%a: bad PDE for Physical=0x%Lx\n",\r
+          gEfiCallerBaseName,\r
+          __FUNCTION__,\r
+          PhysicalAddress\r
+          ));\r
+        Status = RETURN_NO_MAPPING;\r
+        goto Done;\r
+      }\r
+\r
+      //\r
+      // If the MustBe1 bit is not a 1, it's not a 2MB entry\r
+      //\r
+      if (PageDirectory2MEntry->Bits.MustBe1) {\r
+        //\r
+        // Valid 2MB page\r
+        // If we have at least 2MB left to go, we can just update this entry\r
+        //\r
+        if (!(PhysicalAddress & (BIT21-1)) && (Length >= BIT21)) {\r
+          SetOrClearSharedBit (&PageDirectory2MEntry->Uint64, Mode, PhysicalAddress, BIT21);\r
+          PhysicalAddress += BIT21;\r
+          Length          -= BIT21;\r
+        } else {\r
+          //\r
+          // We must split up this page into 4K pages\r
+          //\r
+          DEBUG ((\r
+            DEBUG_VERBOSE,\r
+            "%a:%a: splitting 2MB page for Physical=0x%Lx\n",\r
+            gEfiCallerBaseName,\r
+            __FUNCTION__,\r
+            PhysicalAddress\r
+            ));\r
+\r
+          ActiveEncMask = PageDirectory2MEntry->Uint64 & AddressEncMask;\r
+\r
+          Split2MPageTo4K (\r
+            (UINT64)PageDirectory2MEntry->Bits.PageTableBaseAddress << 21,\r
+            (UINT64 *)PageDirectory2MEntry,\r
+            0,\r
+            0,\r
+            ActiveEncMask\r
+            );\r
+          continue;\r
+        }\r
+      } else {\r
+        PageDirectoryPointerEntry =\r
+          (PAGE_MAP_AND_DIRECTORY_POINTER *)PageDirectory2MEntry;\r
+        PageTableEntry =\r
+          (VOID *)(\r
+                   (PageDirectoryPointerEntry->Bits.PageTableBaseAddress <<\r
+                    12) & ~PgTableMask\r
+                   );\r
+        PageTableEntry += PTE_OFFSET (PhysicalAddress);\r
+        if (!PageTableEntry->Bits.Present) {\r
+          DEBUG ((\r
+            DEBUG_ERROR,\r
+            "%a:%a: bad PTE for Physical=0x%Lx\n",\r
+            gEfiCallerBaseName,\r
+            __FUNCTION__,\r
+            PhysicalAddress\r
+            ));\r
+          Status = RETURN_NO_MAPPING;\r
+          goto Done;\r
+        }\r
+\r
+        SetOrClearSharedBit (&PageTableEntry->Uint64, Mode, PhysicalAddress, EFI_PAGE_SIZE);\r
+        PhysicalAddress += EFI_PAGE_SIZE;\r
+        Length          -= EFI_PAGE_SIZE;\r
+      }\r
+    }\r
+  }\r
+\r
+  //\r
+  // Protect the page table by marking the memory used for page table to be\r
+  // read-only.\r
+  //\r
+  if (IsWpEnabled) {\r
+    EnablePageTableProtection ((UINTN)PageMapLevel4Entry, TRUE);\r
+  }\r
+\r
+  //\r
+  // Flush TLB\r
+  //\r
+  CpuFlushTlb ();\r
+\r
+Done:\r
+  //\r
+  // Restore page table write protection, if any.\r
+  //\r
+  if (IsWpEnabled) {\r
+    EnableReadOnlyPageWriteProtect ();\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  This function clears memory shared bit for the memory region specified by\r
+  BaseAddress and NumPages from the current page table context.\r
+\r
+  @param[in]  Cr3BaseAddress          Cr3 Base Address (if zero then use\r
+                                      current CR3)\r
+  @param[in]  BaseAddress             The physical address that is the start\r
+                                      address of a memory region.\r
+  @param[in]  NumPages                The number of pages from start memory\r
+                                      region.\r
+\r
+  @retval RETURN_SUCCESS              The attributes were cleared for the\r
+                                      memory region.\r
+  @retval RETURN_INVALID_PARAMETER    Number of pages is zero.\r
+  @retval RETURN_UNSUPPORTED          Clearing the memory encryption attribute\r
+                                      is not supported\r
+**/\r
+RETURN_STATUS\r
+EFIAPI\r
+MemEncryptTdxSetPageSharedBit (\r
+  IN PHYSICAL_ADDRESS  Cr3BaseAddress,\r
+  IN PHYSICAL_ADDRESS  BaseAddress,\r
+  IN UINTN             NumPages\r
+  )\r
+{\r
+  return SetMemorySharedOrPrivate (\r
+           Cr3BaseAddress,\r
+           BaseAddress,\r
+           EFI_PAGES_TO_SIZE (NumPages),\r
+           SetSharedBit\r
+           );\r
+}\r
+\r
+/**\r
+  This function sets memory shared bit for the memory region specified by\r
+  BaseAddress and NumPages from the current page table context.\r
+\r
+  @param[in]  Cr3BaseAddress          Cr3 Base Address (if zero then use\r
+                                      current CR3)\r
+  @param[in]  BaseAddress             The physical address that is the start\r
+                                      address of a memory region.\r
+  @param[in]  NumPages                The number of pages from start memory\r
+                                      region.\r
+\r
+  @retval RETURN_SUCCESS              The attributes were set for the memory\r
+                                      region.\r
+  @retval RETURN_INVALID_PARAMETER    Number of pages is zero.\r
+  @retval RETURN_UNSUPPORTED          Setting the memory encryption attribute\r
+                                      is not supported\r
+**/\r
+RETURN_STATUS\r
+EFIAPI\r
+MemEncryptTdxClearPageSharedBit (\r
+  IN PHYSICAL_ADDRESS  Cr3BaseAddress,\r
+  IN PHYSICAL_ADDRESS  BaseAddress,\r
+  IN UINTN             NumPages\r
+  )\r
+{\r
+  return SetMemorySharedOrPrivate (\r
+           Cr3BaseAddress,\r
+           BaseAddress,\r
+           EFI_PAGES_TO_SIZE (NumPages),\r
+           ClearSharedBit\r
+           );\r
+}\r
diff --git a/OvmfPkg/Library/BaseMemEncryptTdxLib/VirtualMemory.h b/OvmfPkg/Library/BaseMemEncryptTdxLib/VirtualMemory.h
new file mode 100644 (file)
index 0000000..e940379
--- /dev/null
@@ -0,0 +1,181 @@
+/** @file\r
+\r
+  Virtual Memory Management Services to set or clear the memory encryption bit\r
+\r
+  Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>\r
+  Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR>\r
+\r
+  SPDX-License-Identifier: BSD-2-Clause-Patent\r
+\r
+  Code is derived from MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.h\r
+\r
+**/\r
+\r
+#ifndef TDX_VIRTUAL_MEMORY_\r
+#define TDX_VIRTUAL_MEMORY_\r
+\r
+#include <Library/BaseLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/CacheMaintenanceLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Uefi.h>\r
+\r
+#define SYS_CODE64_SEL  0x38\r
+\r
+#pragma pack(1)\r
+\r
+//\r
+// Page-Map Level-4 Offset (PML4) and\r
+// Page-Directory-Pointer Offset (PDPE) entries 4K & 2MB\r
+//\r
+\r
+typedef union {\r
+  struct {\r
+    UINT64    Present              : 1;  // 0 = Not present in memory,\r
+                                         //   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,\r
+                                         //   1 = Write-Through caching\r
+    UINT64    CacheDisabled        : 1;  // 0 = Cached, 1=Non-Cached\r
+    UINT64    Accessed             : 1;  // 0 = Not accessed,\r
+                                         //   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 4KB\r
+//\r
+typedef union {\r
+  struct {\r
+    UINT64    Present              : 1;  // 0 = Not present in memory,\r
+                                         //   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,\r
+                                         //   1 = Write-Through caching\r
+    UINT64    CacheDisabled        : 1;  // 0 = Cached, 1=Non-Cached\r
+    UINT64    Accessed             : 1;  // 0 = Not accessed,\r
+                                         //   1 = Accessed (set by CPU)\r
+    UINT64    Dirty                : 1;  // 0 = Not Dirty, 1 = written by\r
+                                         //   processor on access to page\r
+    UINT64    PAT                  : 1;  //\r
+    UINT64    Global               : 1;  // 0 = Not global page, 1 = global page\r
+                                         //   TLB not cleared on CR3 write\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;  // 0 = Execute Code,\r
+                                         //   1 = No Code Execution\r
+  } Bits;\r
+  UINT64    Uint64;\r
+} PAGE_TABLE_4K_ENTRY;\r
+\r
+//\r
+// Page Table Entry 2MB\r
+//\r
+typedef union {\r
+  struct {\r
+    UINT64    Present              : 1;  // 0 = Not present in memory,\r
+                                         //   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,\r
+                                         //   1=Write-Through caching\r
+    UINT64    CacheDisabled        : 1;  // 0 = Cached, 1=Non-Cached\r
+    UINT64    Accessed             : 1;  // 0 = Not accessed,\r
+                                         //   1 = Accessed (set by CPU)\r
+    UINT64    Dirty                : 1;  // 0 = Not Dirty, 1 = written by\r
+                                         //   processor on access to page\r
+    UINT64    MustBe1              : 1;  // Must be 1\r
+    UINT64    Global               : 1;  // 0 = Not global page, 1 = global page\r
+                                         //   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,\r
+                                         //   1 = No Code Execution\r
+  } Bits;\r
+  UINT64    Uint64;\r
+} PAGE_TABLE_ENTRY;\r
+\r
+//\r
+// Page Table Entry 1GB\r
+//\r
+typedef union {\r
+  struct {\r
+    UINT64    Present              : 1;  // 0 = Not present in memory,\r
+                                         //   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,\r
+                                         //   1 = Write-Through caching\r
+    UINT64    CacheDisabled        : 1;  // 0 = Cached, 1=Non-Cached\r
+    UINT64    Accessed             : 1;  // 0 = Not accessed,\r
+                                         //   1 = Accessed (set by CPU)\r
+    UINT64    Dirty                : 1;  // 0 = Not Dirty, 1 = written by\r
+                                         //   processor on access to page\r
+    UINT64    MustBe1              : 1;  // Must be 1\r
+    UINT64    Global               : 1;  // 0 = Not global page, 1 = global page\r
+                                         //   TLB not cleared on CR3 write\r
+    UINT64    Available            : 3;  // Available for use by system software\r
+    UINT64    PAT                  : 1;  //\r
+    UINT64    MustBeZero           : 17; // Must be zero;\r
+    UINT64    PageTableBaseAddress : 22; // Page Table Base Address\r
+    UINT64    AvabilableHigh       : 11; // Available for use by system software\r
+    UINT64    Nx                   : 1;  // 0 = Execute Code,\r
+                                         //   1 = No Code Execution\r
+  } Bits;\r
+  UINT64    Uint64;\r
+} PAGE_TABLE_1G_ENTRY;\r
+\r
+#pragma pack()\r
+\r
+#define IA32_PG_P   BIT0\r
+#define IA32_PG_RW  BIT1\r
+#define IA32_PG_PS  BIT7\r
+\r
+#define PAGING_PAE_INDEX_MASK  0x1FF\r
+\r
+#define PAGING_4K_ADDRESS_MASK_64  0x000FFFFFFFFFF000ull\r
+#define PAGING_2M_ADDRESS_MASK_64  0x000FFFFFFFE00000ull\r
+#define PAGING_1G_ADDRESS_MASK_64  0x000FFFFFC0000000ull\r
+\r
+#define PAGING_L1_ADDRESS_SHIFT  12\r
+#define PAGING_L2_ADDRESS_SHIFT  21\r
+#define PAGING_L3_ADDRESS_SHIFT  30\r
+#define PAGING_L4_ADDRESS_SHIFT  39\r
+\r
+#define PAGING_PML4E_NUMBER  4\r
+\r
+#define PAGETABLE_ENTRY_MASK  ((1UL << 9) - 1)\r
+#define PML4_OFFSET(x)  ( (x >> 39) & PAGETABLE_ENTRY_MASK)\r
+#define PDP_OFFSET(x)   ( (x >> 30) & PAGETABLE_ENTRY_MASK)\r
+#define PDE_OFFSET(x)   ( (x >> 21) & PAGETABLE_ENTRY_MASK)\r
+#define PTE_OFFSET(x)   ( (x >> 12) & PAGETABLE_ENTRY_MASK)\r
+#define PAGING_1G_ADDRESS_MASK_64  0x000FFFFFC0000000ull\r
+\r
+#define PAGE_TABLE_POOL_ALIGNMENT  BASE_2MB\r
+#define PAGE_TABLE_POOL_UNIT_SIZE  SIZE_2MB\r
+#define PAGE_TABLE_POOL_UNIT_PAGES  \\r
+  EFI_SIZE_TO_PAGES (PAGE_TABLE_POOL_UNIT_SIZE)\r
+#define PAGE_TABLE_POOL_ALIGN_MASK  \\r
+  (~(EFI_PHYSICAL_ADDRESS)(PAGE_TABLE_POOL_ALIGNMENT - 1))\r
+\r
+typedef struct {\r
+  VOID     *NextPool;\r
+  UINTN    Offset;\r
+  UINTN    FreePages;\r
+} PAGE_TABLE_POOL;\r
+\r
+#endif\r
index 746050d64ba789b29e078ac0966e1efb338ff454..72db336e321e38f60e5cb2667aafadf02abd28b2 100644 (file)
   #                  Virtualization (SEV) guests.\r
   MemEncryptSevLib|Include/Library/MemEncryptSevLib.h\r
 \r
+  ##  @libraryclass  Declares helper functions for TDX guests.\r
+  #\r
+  MemEncryptTdxLib|Include/Library/MemEncryptTdxLib.h\r
+\r
   ##  @libraryclass  Save and restore variables using a file\r
   #\r
   NvVarsFileLib|Include/Library/NvVarsFileLib.h\r
index 01a26c234a88f0273cf0eb4a7309aa0d2905766c..c64d469aeec46b58e72b5744c70bae537cbcfa25 100644 (file)
   VirtioLib|OvmfPkg/Library/VirtioLib/VirtioLib.inf\r
   LoadLinuxLib|OvmfPkg/Library/LoadLinuxLib/LoadLinuxLib.inf\r
   MemEncryptSevLib|OvmfPkg/Library/BaseMemEncryptSevLib/DxeMemEncryptSevLib.inf\r
+  MemEncryptTdxLib|OvmfPkg/Library/BaseMemEncryptTdxLib/BaseMemEncryptTdxLibNull.inf\r
 !if $(SMM_REQUIRE) == FALSE\r
   LockBoxLib|OvmfPkg/Library/LockBoxLib/LockBoxBaseLib.inf\r
 !endif\r
index bf08e893e0538af7811a8a5fe58fcf463e869737..f16becd028f7dbad92b9b997f05160c6d30a7554 100644 (file)
   VirtioLib|OvmfPkg/Library/VirtioLib/VirtioLib.inf\r
   LoadLinuxLib|OvmfPkg/Library/LoadLinuxLib/LoadLinuxLib.inf\r
   MemEncryptSevLib|OvmfPkg/Library/BaseMemEncryptSevLib/DxeMemEncryptSevLib.inf\r
+  MemEncryptTdxLib|OvmfPkg/Library/BaseMemEncryptTdxLib/BaseMemEncryptTdxLibNull.inf\r
 !if $(SMM_REQUIRE) == FALSE\r
   LockBoxLib|OvmfPkg/Library/LockBoxLib/LockBoxBaseLib.inf\r
 !endif\r