--- /dev/null
+/** @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