1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
|
/** @file
Unaccepted memory is a special type of private memory. In Td guest
TDCALL [TDG.MEM.PAGE.ACCEPT] is invoked to accept the unaccepted
memory before use it.
Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include <Library/BaseLib.h>
#include <Library/DebugLib.h>
#include <IndustryStandard/Tdx.h>
#include <Uefi/UefiBaseType.h>
#include <Library/TdxLib.h>
#include <Library/BaseMemoryLib.h>
UINT64 mNumberOfDuplicatedAcceptedPages;
#define TDX_ACCEPTPAGE_MAX_RETRIED 3
// PageSize is mapped to PageLevel like below:
// 4KB - 0, 2MB - 1
UINT32 mTdxAcceptPageLevelMap[2] = {
SIZE_4KB,
SIZE_2MB
};
#define INVALID_ACCEPT_PAGELEVEL ARRAY_SIZE(mTdxAcceptPageLevelMap)
/**
This function gets the PageLevel according to the input page size.
@param[in] PageSize Page size
@return UINT32 The mapped page level
**/
UINT32
GetGpaPageLevel (
UINT32 PageSize
)
{
UINT32 Index;
for (Index = 0; Index < ARRAY_SIZE (mTdxAcceptPageLevelMap); Index++) {
if (mTdxAcceptPageLevelMap[Index] == PageSize) {
break;
}
}
return Index;
}
/**
This function accept a pending private page, and initialize the page to
all-0 using the TD ephemeral private key.
Sometimes TDCALL [TDG.MEM.PAGE.ACCEPT] may return
TDX_EXIT_REASON_PAGE_SIZE_MISMATCH. It indicates the input PageLevel is
not workable. In this case we need to try to fallback to a smaller
PageLevel if possible.
@param[in] StartAddress Guest physical address of the private
page to accept. [63:52] and [11:0] must be 0.
@param[in] NumberOfPages Number of the pages to be accepted.
@param[in] PageSize GPA page size. Only accept 2M/4K size.
@return EFI_SUCCESS Accept successfully
@return others Indicate other errors
**/
EFI_STATUS
EFIAPI
TdAcceptPages (
IN UINT64 StartAddress,
IN UINT64 NumberOfPages,
IN UINT32 PageSize
)
{
EFI_STATUS Status;
UINT64 Address;
UINT64 TdxStatus;
UINT64 Index;
UINT32 GpaPageLevel;
UINT32 PageSize2;
UINTN Retried;
Retried = 0;
if ((StartAddress & ~0xFFFFFFFFFF000ULL) != 0) {
ASSERT (FALSE);
DEBUG ((DEBUG_ERROR, "Accept page address(0x%llx) is not valid. [63:52] and [11:0] must be 0\n", StartAddress));
return EFI_INVALID_PARAMETER;
}
Address = StartAddress;
GpaPageLevel = GetGpaPageLevel (PageSize);
if (GpaPageLevel == INVALID_ACCEPT_PAGELEVEL) {
ASSERT (FALSE);
DEBUG ((DEBUG_ERROR, "Accept page size must be 4K/2M. Invalid page size - 0x%llx\n", PageSize));
return EFI_INVALID_PARAMETER;
}
Status = EFI_SUCCESS;
for (Index = 0; Index < NumberOfPages; Index++) {
Retried = 0;
DoAcceptPage:
TdxStatus = TdCall (TDCALL_TDACCEPTPAGE, Address | GpaPageLevel, 0, 0, 0);
if (TdxStatus != TDX_EXIT_REASON_SUCCESS) {
if ((TdxStatus & ~0xFFFFULL) == TDX_EXIT_REASON_PAGE_ALREADY_ACCEPTED) {
//
// Already accepted
//
mNumberOfDuplicatedAcceptedPages++;
DEBUG ((DEBUG_WARN, "Page at Address (0x%llx) has already been accepted. - %d\n", Address, mNumberOfDuplicatedAcceptedPages));
} else if ((TdxStatus & ~0xFFFFULL) == TDX_EXIT_REASON_PAGE_SIZE_MISMATCH) {
//
// GpaPageLevel is mismatch, fall back to a smaller GpaPageLevel if possible
//
DEBUG ((DEBUG_VERBOSE, "Address %llx cannot be accepted in PageLevel of %d\n", Address, GpaPageLevel));
if (GpaPageLevel == 0) {
//
// Cannot fall back to smaller page level
//
DEBUG ((DEBUG_ERROR, "AcceptPage cannot fallback from PageLevel %d\n", GpaPageLevel));
Status = EFI_INVALID_PARAMETER;
break;
} else {
//
// Fall back to a smaller page size
//
PageSize2 = mTdxAcceptPageLevelMap[GpaPageLevel - 1];
Status = TdAcceptPages (Address, 512, PageSize2);
if (EFI_ERROR (Status)) {
break;
}
}
} else if ((TdxStatus & ~0xFFFFULL) == TDX_EXIT_REASON_OPERAND_BUSY) {
//
// Concurrent TDG.MEM.PAGE.ACCEPT is using the same Secure EPT entry
// So try it again. There is a max retried count. If Retried exceeds the max count,
// report the error and quit.
//
Retried += 1;
if (Retried > TDX_ACCEPTPAGE_MAX_RETRIED) {
DEBUG ((
DEBUG_ERROR,
"Address %llx (%d) failed to be accepted because of OPERAND_BUSY. Retried %d time.\n",
Address,
Index,
Retried
));
Status = EFI_INVALID_PARAMETER;
break;
} else {
goto DoAcceptPage;
}
} else {
//
// Other errors
//
DEBUG ((
DEBUG_ERROR,
"Address %llx (%d) failed to be accepted. Error = 0x%llx\n",
Address,
Index,
TdxStatus
));
Status = EFI_INVALID_PARAMETER;
break;
}
}
Address += PageSize;
}
return Status;
}
|