]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdePkg/Library/TdxLib/AcceptPages.c
MdePkg: Add TdxLib to wrap Tdx operations
[mirror_edk2.git] / MdePkg / Library / TdxLib / AcceptPages.c
diff --git a/MdePkg/Library/TdxLib/AcceptPages.c b/MdePkg/Library/TdxLib/AcceptPages.c
new file mode 100644 (file)
index 0000000..3a2182e
--- /dev/null
@@ -0,0 +1,181 @@
+/** @file\r
+\r
+  Unaccepted memory is a special type of private memory. In Td guest\r
+  TDCALL [TDG.MEM.PAGE.ACCEPT] is invoked to accept the unaccepted\r
+  memory before use it.\r
+\r
+  Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.<BR>\r
+  SPDX-License-Identifier: BSD-2-Clause-Patent\r
+\r
+**/\r
+\r
+#include <Library/BaseLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <IndustryStandard/Tdx.h>\r
+#include <Uefi/UefiBaseType.h>\r
+#include <Library/TdxLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+\r
+UINT64  mNumberOfDuplicatedAcceptedPages;\r
+\r
+#define TDX_ACCEPTPAGE_MAX_RETRIED  3\r
+\r
+// PageSize is mapped to PageLevel like below:\r
+// 4KB - 0, 2MB - 1\r
+UINT32  mTdxAcceptPageLevelMap[2] = {\r
+  SIZE_4KB,\r
+  SIZE_2MB\r
+};\r
+\r
+#define INVALID_ACCEPT_PAGELEVEL  ARRAY_SIZE(mTdxAcceptPageLevelMap)\r
+\r
+/**\r
+  This function gets the PageLevel according to the input page size.\r
+\r
+  @param[in]  PageSize    Page size\r
+\r
+  @return UINT32          The mapped page level\r
+**/\r
+UINT32\r
+GetGpaPageLevel (\r
+  UINT32  PageSize\r
+  )\r
+{\r
+  UINT32  Index;\r
+\r
+  for (Index = 0; Index < ARRAY_SIZE (mTdxAcceptPageLevelMap); Index++) {\r
+    if (mTdxAcceptPageLevelMap[Index] == PageSize) {\r
+      break;\r
+    }\r
+  }\r
+\r
+  return Index;\r
+}\r
+\r
+/**\r
+  This function accept a pending private page, and initialize the page to\r
+  all-0 using the TD ephemeral private key.\r
+\r
+  Sometimes TDCALL [TDG.MEM.PAGE.ACCEPT] may return\r
+  TDX_EXIT_REASON_PAGE_SIZE_MISMATCH. It indicates the input PageLevel is\r
+  not workable. In this case we need to try to fallback to a smaller\r
+  PageLevel if possible.\r
+\r
+  @param[in]  StartAddress      Guest physical address of the private\r
+                                page to accept. [63:52] and [11:0] must be 0.\r
+  @param[in]  NumberOfPages     Number of the pages to be accepted.\r
+  @param[in]  PageSize          GPA page size. Only accept 2M/4K size.\r
+\r
+  @return EFI_SUCCESS           Accept successfully\r
+  @return others                Indicate other errors\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+TdAcceptPages (\r
+  IN UINT64  StartAddress,\r
+  IN UINT64  NumberOfPages,\r
+  IN UINT32  PageSize\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+  UINT64      Address;\r
+  UINT64      TdxStatus;\r
+  UINT64      Index;\r
+  UINT32      GpaPageLevel;\r
+  UINT32      PageSize2;\r
+  UINTN       Retried;\r
+\r
+  Retried = 0;\r
+\r
+  if ((StartAddress & ~0xFFFFFFFFFF000ULL) != 0) {\r
+    ASSERT (FALSE);\r
+    DEBUG ((DEBUG_ERROR, "Accept page address(0x%llx) is not valid. [63:52] and [11:0] must be 0\n", StartAddress));\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Address = StartAddress;\r
+\r
+  GpaPageLevel = GetGpaPageLevel (PageSize);\r
+  if (GpaPageLevel == INVALID_ACCEPT_PAGELEVEL) {\r
+    ASSERT (FALSE);\r
+    DEBUG ((DEBUG_ERROR, "Accept page size must be 4K/2M. Invalid page size - 0x%llx\n", PageSize));\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Status = EFI_SUCCESS;\r
+  for (Index = 0; Index < NumberOfPages; Index++) {\r
+    Retried = 0;\r
+\r
+DoAcceptPage:\r
+    TdxStatus = TdCall (TDCALL_TDACCEPTPAGE, Address | GpaPageLevel, 0, 0, 0);\r
+    if (TdxStatus != TDX_EXIT_REASON_SUCCESS) {\r
+      if ((TdxStatus & ~0xFFFFULL) == TDX_EXIT_REASON_PAGE_ALREADY_ACCEPTED) {\r
+        //\r
+        // Already accepted\r
+        //\r
+        mNumberOfDuplicatedAcceptedPages++;\r
+        DEBUG ((DEBUG_WARN, "Page at Address (0x%llx) has already been accepted. - %d\n", Address, mNumberOfDuplicatedAcceptedPages));\r
+      } else if ((TdxStatus & ~0xFFFFULL) == TDX_EXIT_REASON_PAGE_SIZE_MISMATCH) {\r
+        //\r
+        // GpaPageLevel is mismatch, fall back to a smaller GpaPageLevel if possible\r
+        //\r
+        DEBUG ((DEBUG_VERBOSE, "Address %llx cannot be accepted in PageLevel of %d\n", Address, GpaPageLevel));\r
+\r
+        if (GpaPageLevel == 0) {\r
+          //\r
+          // Cannot fall back to smaller page level\r
+          //\r
+          DEBUG ((DEBUG_ERROR, "AcceptPage cannot fallback from PageLevel %d\n", GpaPageLevel));\r
+          Status = EFI_INVALID_PARAMETER;\r
+          break;\r
+        } else {\r
+          //\r
+          // Fall back to a smaller page size\r
+          //\r
+          PageSize2 = mTdxAcceptPageLevelMap[GpaPageLevel - 1];\r
+          Status    = TdAcceptPages (Address, 512, PageSize2);\r
+          if (EFI_ERROR (Status)) {\r
+            break;\r
+          }\r
+        }\r
+      } else if ((TdxStatus & ~0xFFFFULL) == TDX_EXIT_REASON_OPERAND_BUSY) {\r
+        //\r
+        // Concurrent TDG.MEM.PAGE.ACCEPT is using the same Secure EPT entry\r
+        // So try it again. There is a max retried count. If Retried exceeds the max count,\r
+        // report the error and quit.\r
+        //\r
+        Retried += 1;\r
+        if (Retried > TDX_ACCEPTPAGE_MAX_RETRIED) {\r
+          DEBUG ((\r
+            DEBUG_ERROR,\r
+            "Address %llx (%d) failed to be accepted because of OPERAND_BUSY. Retried %d time.\n",\r
+            Address,\r
+            Index,\r
+            Retried\r
+            ));\r
+          Status = EFI_INVALID_PARAMETER;\r
+          break;\r
+        } else {\r
+          goto DoAcceptPage;\r
+        }\r
+      } else {\r
+        //\r
+        // Other errors\r
+        //\r
+        DEBUG ((\r
+          DEBUG_ERROR,\r
+          "Address %llx (%d) failed to be accepted. Error = 0x%llx\n",\r
+          Address,\r
+          Index,\r
+          TdxStatus\r
+          ));\r
+        Status = EFI_INVALID_PARAMETER;\r
+        break;\r
+      }\r
+    }\r
+\r
+    Address += PageSize;\r
+  }\r
+\r
+  return Status;\r
+}\r