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