]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/Library/BaseMemEncryptSevLib/X64/SnpPageStateChangeInternal.c
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[mirror_edk2.git] / OvmfPkg / Library / BaseMemEncryptSevLib / X64 / SnpPageStateChangeInternal.c
CommitLineData
ade62c18
BS
1/** @file\r
2\r
3 SEV-SNP Page Validation functions.\r
4\r
5 Copyright (c) 2021 AMD Incorporated. All rights reserved.<BR>\r
6\r
7 SPDX-License-Identifier: BSD-2-Clause-Patent\r
8\r
9**/\r
10\r
11#include <Uefi/UefiBaseType.h>\r
12#include <Library/BaseLib.h>\r
13#include <Library/BaseMemoryLib.h>\r
14#include <Library/MemEncryptSevLib.h>\r
15#include <Library/DebugLib.h>\r
a89f558d 16#include <Library/CcExitLib.h>\r
ade62c18
BS
17\r
18#include <Register/Amd/Ghcb.h>\r
19#include <Register/Amd/Msr.h>\r
20\r
21#include "SnpPageStateChange.h"\r
22\r
23#define IS_ALIGNED(x, y) ((((x) & (y - 1)) == 0))\r
24#define PAGES_PER_LARGE_ENTRY 512\r
25\r
26STATIC\r
27UINTN\r
28MemoryStateToGhcbOp (\r
29 IN SEV_SNP_PAGE_STATE State\r
30 )\r
31{\r
32 UINTN Cmd;\r
33\r
34 switch (State) {\r
35 case SevSnpPageShared: Cmd = SNP_PAGE_STATE_SHARED;\r
36 break;\r
37 case SevSnpPagePrivate: Cmd = SNP_PAGE_STATE_PRIVATE;\r
38 break;\r
39 default: ASSERT (0);\r
40 }\r
41\r
42 return Cmd;\r
43}\r
44\r
ade62c18
BS
45VOID\r
46SnpPageStateFailureTerminate (\r
47 VOID\r
48 )\r
49{\r
50 MSR_SEV_ES_GHCB_REGISTER Msr;\r
51\r
52 //\r
53 // Use the GHCB MSR Protocol to request termination by the hypervisor\r
54 //\r
55 Msr.GhcbPhysicalAddress = 0;\r
56 Msr.GhcbTerminate.Function = GHCB_INFO_TERMINATE_REQUEST;\r
57 Msr.GhcbTerminate.ReasonCodeSet = GHCB_TERMINATE_GHCB;\r
58 Msr.GhcbTerminate.ReasonCode = GHCB_TERMINATE_GHCB_GENERAL;\r
59 AsmWriteMsr64 (MSR_SEV_ES_GHCB, Msr.GhcbPhysicalAddress);\r
60\r
61 AsmVmgExit ();\r
62\r
63 ASSERT (FALSE);\r
64 CpuDeadLoop ();\r
65}\r
66\r
67/**\r
68 This function issues the PVALIDATE instruction to validate or invalidate the memory\r
69 range specified. If PVALIDATE returns size mismatch then it retry validating with\r
70 smaller page size.\r
71\r
72 */\r
73STATIC\r
74VOID\r
75PvalidateRange (\r
76 IN SNP_PAGE_STATE_CHANGE_INFO *Info,\r
77 IN UINTN StartIndex,\r
78 IN UINTN EndIndex,\r
79 IN BOOLEAN Validate\r
80 )\r
81{\r
82 UINTN Address, RmpPageSize, Ret, i;\r
83\r
84 for ( ; StartIndex <= EndIndex; StartIndex++) {\r
85 //\r
86 // Get the address and the page size from the Info.\r
87 //\r
88 Address = Info->Entry[StartIndex].GuestFrameNumber << EFI_PAGE_SHIFT;\r
89 RmpPageSize = Info->Entry[StartIndex].PageSize;\r
90\r
91 Ret = AsmPvalidate (RmpPageSize, Validate, Address);\r
92\r
93 //\r
94 // If we fail to validate due to size mismatch then try with the\r
95 // smaller page size. This senario will occur if the backing page in\r
96 // the RMP entry is 4K and we are validating it as a 2MB.\r
97 //\r
98 if ((Ret == PVALIDATE_RET_SIZE_MISMATCH) && (RmpPageSize == PvalidatePageSize2MB)) {\r
99 for (i = 0; i < PAGES_PER_LARGE_ENTRY; i++) {\r
100 Ret = AsmPvalidate (PvalidatePageSize4K, Validate, Address);\r
101 if (Ret) {\r
102 break;\r
103 }\r
104\r
105 Address = Address + EFI_PAGE_SIZE;\r
106 }\r
107 }\r
108\r
109 //\r
110 // If validation failed then do not continue.\r
111 //\r
112 if (Ret) {\r
113 DEBUG ((\r
114 DEBUG_ERROR,\r
115 "%a:%a: Failed to %a address 0x%Lx Error code %d\n",\r
116 gEfiCallerBaseName,\r
117 __FUNCTION__,\r
118 Validate ? "Validate" : "Invalidate",\r
119 Address,\r
120 Ret\r
121 ));\r
122 SnpPageStateFailureTerminate ();\r
123 }\r
124 }\r
125}\r
126\r
127STATIC\r
128EFI_PHYSICAL_ADDRESS\r
129BuildPageStateBuffer (\r
130 IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
131 IN EFI_PHYSICAL_ADDRESS EndAddress,\r
132 IN SEV_SNP_PAGE_STATE State,\r
133 IN BOOLEAN UseLargeEntry,\r
134 IN SNP_PAGE_STATE_CHANGE_INFO *Info\r
135 )\r
136{\r
137 EFI_PHYSICAL_ADDRESS NextAddress;\r
138 UINTN i, RmpPageSize;\r
139\r
140 // Clear the page state structure\r
141 SetMem (Info, sizeof (*Info), 0);\r
142\r
143 i = 0;\r
144 NextAddress = EndAddress;\r
145\r
146 //\r
147 // Populate the page state entry structure\r
148 //\r
149 while ((BaseAddress < EndAddress) && (i < SNP_PAGE_STATE_MAX_ENTRY)) {\r
150 //\r
151 // Is this a 2MB aligned page? Check if we can use the Large RMP entry.\r
152 //\r
153 if (UseLargeEntry && IS_ALIGNED (BaseAddress, SIZE_2MB) &&\r
154 ((EndAddress - BaseAddress) >= SIZE_2MB))\r
155 {\r
156 RmpPageSize = PvalidatePageSize2MB;\r
157 NextAddress = BaseAddress + SIZE_2MB;\r
158 } else {\r
159 RmpPageSize = PvalidatePageSize4K;\r
160 NextAddress = BaseAddress + EFI_PAGE_SIZE;\r
161 }\r
162\r
163 Info->Entry[i].GuestFrameNumber = BaseAddress >> EFI_PAGE_SHIFT;\r
164 Info->Entry[i].PageSize = RmpPageSize;\r
165 Info->Entry[i].Operation = MemoryStateToGhcbOp (State);\r
166 Info->Entry[i].CurrentPage = 0;\r
167 Info->Header.EndEntry = (UINT16)i;\r
168\r
169 BaseAddress = NextAddress;\r
170 i++;\r
171 }\r
172\r
173 return NextAddress;\r
174}\r
175\r
176STATIC\r
177VOID\r
178PageStateChangeVmgExit (\r
179 IN GHCB *Ghcb,\r
180 IN SNP_PAGE_STATE_CHANGE_INFO *Info\r
181 )\r
182{\r
183 EFI_STATUS Status;\r
184\r
185 //\r
186 // As per the GHCB specification, the hypervisor can resume the guest before\r
187 // processing all the entries. Checks whether all the entries are processed.\r
188 //\r
189 // The stragtegy here is to wait for the hypervisor to change the page\r
190 // state in the RMP table before guest access the memory pages. If the\r
191 // page state was not successful, then later memory access will result\r
192 // in the crash.\r
193 //\r
194 while (Info->Header.CurrentEntry <= Info->Header.EndEntry) {\r
195 Ghcb->SaveArea.SwScratch = (UINT64)Ghcb->SharedBuffer;\r
765ba5bf 196 CcExitVmgSetOffsetValid (Ghcb, GhcbSwScratch);\r
ade62c18 197\r
765ba5bf 198 Status = CcExitVmgExit (Ghcb, SVM_EXIT_SNP_PAGE_STATE_CHANGE, 0, 0);\r
ade62c18
BS
199\r
200 //\r
201 // The Page State Change VMGEXIT can pass the failure through the\r
202 // ExitInfo2. Lets check both the return value as well as ExitInfo2.\r
203 //\r
204 if ((Status != 0) || (Ghcb->SaveArea.SwExitInfo2)) {\r
205 SnpPageStateFailureTerminate ();\r
206 }\r
207 }\r
208}\r
209\r
210/**\r
211 The function is used to set the page state when SEV-SNP is active. The page state\r
212 transition consist of changing the page ownership in the RMP table, and using the\r
213 PVALIDATE instruction to update the Validated bit in RMP table.\r
214\r
215 When the UseLargeEntry is set to TRUE, then function will try to use the large RMP\r
216 entry (whevever possible).\r
217 */\r
218VOID\r
219InternalSetPageState (\r
220 IN EFI_PHYSICAL_ADDRESS BaseAddress,\r
221 IN UINTN NumPages,\r
222 IN SEV_SNP_PAGE_STATE State,\r
223 IN BOOLEAN UseLargeEntry\r
224 )\r
225{\r
226 GHCB *Ghcb;\r
227 EFI_PHYSICAL_ADDRESS NextAddress, EndAddress;\r
228 MSR_SEV_ES_GHCB_REGISTER Msr;\r
229 BOOLEAN InterruptState;\r
230 SNP_PAGE_STATE_CHANGE_INFO *Info;\r
231\r
232 Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);\r
233 Ghcb = Msr.Ghcb;\r
234\r
235 EndAddress = BaseAddress + EFI_PAGES_TO_SIZE (NumPages);\r
236\r
237 DEBUG ((\r
238 DEBUG_VERBOSE,\r
239 "%a:%a Address 0x%Lx - 0x%Lx State = %a LargeEntry = %d\n",\r
240 gEfiCallerBaseName,\r
241 __FUNCTION__,\r
242 BaseAddress,\r
243 EndAddress,\r
244 State == SevSnpPageShared ? "Shared" : "Private",\r
245 UseLargeEntry\r
246 ));\r
247\r
248 while (BaseAddress < EndAddress) {\r
249 UINTN CurrentEntry, EndEntry;\r
250\r
251 //\r
252 // Initialize the GHCB\r
253 //\r
765ba5bf 254 CcExitVmgInit (Ghcb, &InterruptState);\r
ade62c18
BS
255\r
256 //\r
257 // Build the page state structure\r
258 //\r
259 Info = (SNP_PAGE_STATE_CHANGE_INFO *)Ghcb->SharedBuffer;\r
260 NextAddress = BuildPageStateBuffer (\r
261 BaseAddress,\r
262 EndAddress,\r
263 State,\r
264 UseLargeEntry,\r
265 Info\r
266 );\r
267\r
268 //\r
269 // Save the current and end entry from the page state structure. We need\r
270 // it later.\r
271 //\r
272 CurrentEntry = Info->Header.CurrentEntry;\r
273 EndEntry = Info->Header.EndEntry;\r
274\r
275 //\r
276 // If the caller requested to change the page state to shared then\r
277 // invalidate the pages before making the page shared in the RMP table.\r
278 //\r
279 if (State == SevSnpPageShared) {\r
280 PvalidateRange (Info, CurrentEntry, EndEntry, FALSE);\r
281 }\r
282\r
283 //\r
284 // Invoke the page state change VMGEXIT.\r
285 //\r
286 PageStateChangeVmgExit (Ghcb, Info);\r
287\r
288 //\r
289 // If the caller requested to change the page state to private then\r
290 // validate the pages after it has been added in the RMP table.\r
291 //\r
292 if (State == SevSnpPagePrivate) {\r
293 PvalidateRange (Info, CurrentEntry, EndEntry, TRUE);\r
294 }\r
295\r
765ba5bf 296 CcExitVmgDone (Ghcb, InterruptState);\r
ade62c18
BS
297\r
298 BaseAddress = NextAddress;\r
299 }\r
300}\r