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;
}
|