/** @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.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include
#include
#include
#include
#include
#include
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;
}