VirtualBox

source: vbox/trunk/src/VBox/Devices/EFI/FirmwareNew/OvmfPkg/Library/AmdSvsmLib/AmdSvsmLib.c

Last change on this file was 105670, checked in by vboxsync, 9 months ago

Devices/EFI/FirmwareNew: Merge edk2-stable-202405 and make it build on aarch64, bugref:4643

  • Property svn:eol-style set to native
File size: 13.2 KB
Line 
1/** @file
2 SVSM Support Library.
3
4 Copyright (C) 2024, Advanced Micro Devices, Inc. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
6
7**/
8
9#include <Base.h>
10#include <Uefi.h>
11#include <Library/BaseMemoryLib.h>
12#include <Library/AmdSvsmLib.h>
13#include <Register/Amd/Msr.h>
14#include <Register/Amd/Svsm.h>
15
16#define PAGES_PER_2MB_ENTRY 512
17
18/**
19 Issue a GHCB termination request for termination.
20
21 Request termination using the GHCB MSR protocol.
22
23**/
24STATIC
25VOID
26SnpTerminate (
27 VOID
28 )
29{
30 MSR_SEV_ES_GHCB_REGISTER Msr;
31
32 //
33 // Use the GHCB MSR Protocol to request termination by the hypervisor
34 //
35 Msr.Uint64 = 0;
36 Msr.GhcbTerminate.Function = GHCB_INFO_TERMINATE_REQUEST;
37 Msr.GhcbTerminate.ReasonCodeSet = GHCB_TERMINATE_GHCB;
38 Msr.GhcbTerminate.ReasonCode = GHCB_TERMINATE_GHCB_GENERAL;
39 AsmWriteMsr64 (MSR_SEV_ES_GHCB, Msr.Uint64);
40
41 AsmVmgExit ();
42
43 ASSERT (FALSE);
44 CpuDeadLoop ();
45}
46
47/**
48 Issue an SVSM request.
49
50 Invokes the SVSM to process a request on behalf of the guest.
51
52 @param[in,out] SvsmCallData Pointer to the SVSM call data
53
54 @return Contents of RAX upon return from VMGEXIT
55**/
56STATIC
57UINTN
58SvsmMsrProtocol (
59 IN OUT SVSM_CALL_DATA *SvsmCallData
60 )
61{
62 MSR_SEV_ES_GHCB_REGISTER Msr;
63 UINT64 CurrentMsr;
64 UINT8 Pending;
65 BOOLEAN InterruptState;
66 UINTN Ret;
67
68 do {
69 //
70 // Be sure that an interrupt can't cause a #VC while the GHCB MSR protocol
71 // is being used (#VC handler will ASSERT if lower 12-bits are not zero).
72 //
73 InterruptState = GetInterruptState ();
74 if (InterruptState) {
75 DisableInterrupts ();
76 }
77
78 Pending = 0;
79 SvsmCallData->CallPending = &Pending;
80
81 CurrentMsr = AsmReadMsr64 (MSR_SEV_ES_GHCB);
82
83 Msr.Uint64 = 0;
84 Msr.SnpVmplRequest.Function = GHCB_INFO_SNP_VMPL_REQUEST;
85 Msr.SnpVmplRequest.Vmpl = 0;
86 AsmWriteMsr64 (MSR_SEV_ES_GHCB, Msr.Uint64);
87
88 //
89 // Guest memory is used for the guest-SVSM communication, so fence the
90 // invocation of the VMGEXIT instruction to ensure VMSA accesses are
91 // synchronized properly.
92 //
93 MemoryFence ();
94 Ret = AsmVmgExitSvsm (SvsmCallData);
95 MemoryFence ();
96
97 Msr.Uint64 = AsmReadMsr64 (MSR_SEV_ES_GHCB);
98
99 AsmWriteMsr64 (MSR_SEV_ES_GHCB, CurrentMsr);
100
101 if (InterruptState) {
102 EnableInterrupts ();
103 }
104
105 if (Pending != 0) {
106 SnpTerminate ();
107 }
108
109 if ((Msr.SnpVmplResponse.Function != GHCB_INFO_SNP_VMPL_RESPONSE) ||
110 (Msr.SnpVmplResponse.ErrorCode != 0))
111 {
112 SnpTerminate ();
113 }
114 } while (Ret == SVSM_ERR_INCOMPLETE || Ret == SVSM_ERR_BUSY);
115
116 return Ret;
117}
118
119/**
120 Report the presence of an Secure Virtual Services Module (SVSM).
121
122 Determines the presence of an SVSM.
123
124 @retval TRUE An SVSM is present
125 @retval FALSE An SVSM is not present
126
127**/
128BOOLEAN
129EFIAPI
130AmdSvsmIsSvsmPresent (
131 VOID
132 )
133{
134 SVSM_INFORMATION *SvsmInfo;
135
136 SvsmInfo = (SVSM_INFORMATION *)(UINTN)PcdGet32 (PcdOvmfSnpSecretsBase);
137
138 return (SvsmInfo != NULL && SvsmInfo->SvsmSize != 0);
139}
140
141/**
142 Report the VMPL level at which the SEV-SNP guest is running.
143
144 Determines the VMPL level at which the guest is running. If an SVSM is
145 not present, then it must be VMPL0, otherwise return what is reported
146 by the SVSM.
147
148 @return The VMPL level
149
150**/
151UINT8
152EFIAPI
153AmdSvsmSnpGetVmpl (
154 VOID
155 )
156{
157 SVSM_INFORMATION *SvsmInfo;
158
159 SvsmInfo = (SVSM_INFORMATION *)(UINTN)PcdGet32 (PcdOvmfSnpSecretsBase);
160
161 return AmdSvsmIsSvsmPresent () ? SvsmInfo->SvsmGuestVmpl : 0;
162}
163
164/**
165 Report the Calling Area address (CAA) for the BSP of the SEV-SNP guest.
166
167 If an SVSM is present, the CAA for the BSP is returned.
168
169 @return The CAA
170
171**/
172UINT64
173EFIAPI
174AmdSvsmSnpGetCaa (
175 VOID
176 )
177{
178 SVSM_INFORMATION *SvsmInfo;
179
180 SvsmInfo = (SVSM_INFORMATION *)(UINTN)PcdGet32 (PcdOvmfSnpSecretsBase);
181
182 return AmdSvsmIsSvsmPresent () ? SvsmInfo->SvsmCaa : 0;
183}
184
185/**
186 Issue an SVSM request to perform the PVALIDATE instruction.
187
188 Invokes the SVSM to process the PVALIDATE instruction on behalf of the
189 guest to validate or invalidate the memory range specified.
190
191 @param[in] Info Pointer to a page state change structure
192
193**/
194STATIC
195VOID
196SvsmPvalidate (
197 IN SNP_PAGE_STATE_CHANGE_INFO *Info
198 )
199{
200 SVSM_CALL_DATA SvsmCallData;
201 SVSM_CAA *Caa;
202 SVSM_PVALIDATE_REQUEST *Request;
203 SVSM_FUNCTION Function;
204 BOOLEAN Validate;
205 UINTN Entry;
206 UINTN EntryLimit;
207 UINTN Index;
208 UINTN EndIndex;
209 UINT64 Gfn;
210 UINT64 GfnEnd;
211 UINTN Ret;
212
213 Caa = (SVSM_CAA *)AmdSvsmSnpGetCaa ();
214 ZeroMem (Caa->SvsmBuffer, sizeof (Caa->SvsmBuffer));
215
216 Function.Id.Protocol = 0;
217 Function.Id.CallId = 1;
218
219 Request = (SVSM_PVALIDATE_REQUEST *)Caa->SvsmBuffer;
220 EntryLimit = ((sizeof (Caa->SvsmBuffer) - sizeof (*Request)) /
221 sizeof (Request->Entry[0])) - 1;
222
223 SvsmCallData.Caa = Caa;
224 SvsmCallData.RaxIn = Function.Uint64;
225 SvsmCallData.RcxIn = (UINT64)(UINTN)Request;
226
227 Entry = 0;
228 Index = Info->Header.CurrentEntry;
229 EndIndex = Info->Header.EndEntry;
230
231 while (Index <= EndIndex) {
232 Validate = Info->Entry[Index].Operation == SNP_PAGE_STATE_PRIVATE;
233
234 Request->Header.Entries++;
235 Request->Entry[Entry].Bits.PageSize = Info->Entry[Index].PageSize;
236 Request->Entry[Entry].Bits.Action = (Validate == TRUE) ? 1 : 0;
237 Request->Entry[Entry].Bits.IgnoreCf = 0;
238 Request->Entry[Entry].Bits.Address = Info->Entry[Index].GuestFrameNumber;
239
240 Entry++;
241 if ((Entry > EntryLimit) || (Index == EndIndex)) {
242 Ret = SvsmMsrProtocol (&SvsmCallData);
243 if ((Ret == SVSM_ERR_PVALIDATE_FAIL_SIZE_MISMATCH) &&
244 (Request->Entry[Request->Header.Next].Bits.PageSize != 0))
245 {
246 // Calculate the Index of the entry after the entry that failed
247 // before clearing the buffer so that processing can continue
248 // from that point
249 Index = Index - (Entry - Request->Header.Next) + 2;
250
251 // Obtain the failing GFN before clearing the buffer
252 Gfn = Request->Entry[Request->Header.Next].Bits.Address;
253
254 // Clear the buffer in prep for creating all new entries
255 ZeroMem (Caa->SvsmBuffer, sizeof (Caa->SvsmBuffer));
256 Entry = 0;
257
258 GfnEnd = Gfn + PAGES_PER_2MB_ENTRY - 1;
259 for ( ; Gfn <= GfnEnd; Gfn++) {
260 Request->Header.Entries++;
261 Request->Entry[Entry].Bits.PageSize = 0;
262 Request->Entry[Entry].Bits.Action = (Validate == TRUE) ? 1 : 0;
263 Request->Entry[Entry].Bits.IgnoreCf = 0;
264 Request->Entry[Entry].Bits.Address = Gfn;
265
266 Entry++;
267 if ((Entry > EntryLimit) || (Gfn == GfnEnd)) {
268 Ret = SvsmMsrProtocol (&SvsmCallData);
269 if (Ret != 0) {
270 SnpTerminate ();
271 }
272
273 ZeroMem (Caa->SvsmBuffer, sizeof (Caa->SvsmBuffer));
274 Entry = 0;
275 }
276 }
277
278 continue;
279 }
280
281 if (Ret != 0) {
282 SnpTerminate ();
283 }
284
285 ZeroMem (Caa->SvsmBuffer, sizeof (Caa->SvsmBuffer));
286 Entry = 0;
287 }
288
289 Index++;
290 }
291}
292
293/**
294 Perform a native PVALIDATE operation for the page ranges specified.
295
296 Validate or rescind the validation of the specified pages.
297
298 @param[in] Info Pointer to a page state change structure
299
300**/
301STATIC
302VOID
303BasePvalidate (
304 IN SNP_PAGE_STATE_CHANGE_INFO *Info
305 )
306{
307 UINTN RmpPageSize;
308 UINTN StartIndex;
309 UINTN EndIndex;
310 UINTN Index;
311 UINTN Ret;
312 EFI_PHYSICAL_ADDRESS Address;
313 BOOLEAN Validate;
314
315 StartIndex = Info->Header.CurrentEntry;
316 EndIndex = Info->Header.EndEntry;
317
318 for ( ; StartIndex <= EndIndex; StartIndex++) {
319 //
320 // Get the address and the page size from the Info.
321 //
322 Address = ((EFI_PHYSICAL_ADDRESS)Info->Entry[StartIndex].GuestFrameNumber) << EFI_PAGE_SHIFT;
323 RmpPageSize = Info->Entry[StartIndex].PageSize;
324 Validate = Info->Entry[StartIndex].Operation == SNP_PAGE_STATE_PRIVATE;
325
326 Ret = AsmPvalidate (RmpPageSize, Validate, Address);
327
328 //
329 // If we fail to validate due to size mismatch then try with the
330 // smaller page size. This senario will occur if the backing page in
331 // the RMP entry is 4K and we are validating it as a 2MB.
332 //
333 if ((Ret == PVALIDATE_RET_SIZE_MISMATCH) && (RmpPageSize == PvalidatePageSize2MB)) {
334 for (Index = 0; Index < PAGES_PER_2MB_ENTRY; Index++) {
335 Ret = AsmPvalidate (PvalidatePageSize4K, Validate, Address);
336 if (Ret) {
337 break;
338 }
339
340 Address = Address + EFI_PAGE_SIZE;
341 }
342 }
343
344 //
345 // If validation failed then do not continue.
346 //
347 if (Ret) {
348 DEBUG ((
349 DEBUG_ERROR,
350 "%a:%a: Failed to %a address 0x%Lx Error code %d\n",
351 gEfiCallerBaseName,
352 __func__,
353 Validate ? "Validate" : "Invalidate",
354 Address,
355 Ret
356 ));
357
358 SnpTerminate ();
359 }
360 }
361}
362
363/**
364 Perform a PVALIDATE operation for the page ranges specified.
365
366 Validate or rescind the validation of the specified pages.
367
368 @param[in] Info Pointer to a page state change structure
369
370**/
371VOID
372EFIAPI
373AmdSvsmSnpPvalidate (
374 IN SNP_PAGE_STATE_CHANGE_INFO *Info
375 )
376{
377 AmdSvsmIsSvsmPresent () ? SvsmPvalidate (Info) : BasePvalidate (Info);
378}
379
380/**
381 Perform an RMPADJUST operation to alter the VMSA setting of a page.
382
383 Add or remove the VMSA attribute for a page.
384
385 @param[in] Vmsa Pointer to an SEV-ES save area page
386 @param[in] ApicId APIC ID associated with the VMSA
387 @param[in] SetVmsa Boolean indicator as to whether to set or
388 or clear the VMSA setting for the page
389
390 @retval EFI_SUCCESS RMPADJUST operation successful
391 @retval EFI_UNSUPPORTED Operation is not supported
392 @retval EFI_INVALID_PARAMETER RMPADJUST operation failed, an invalid
393 parameter was supplied
394
395**/
396STATIC
397EFI_STATUS
398SvsmVmsaRmpAdjust (
399 IN SEV_ES_SAVE_AREA *Vmsa,
400 IN UINT32 ApicId,
401 IN BOOLEAN SetVmsa
402 )
403{
404 SVSM_CALL_DATA SvsmCallData;
405 SVSM_FUNCTION Function;
406 UINTN Ret;
407
408 SvsmCallData.Caa = (SVSM_CAA *)AmdSvsmSnpGetCaa ();
409
410 Function.Id.Protocol = 0;
411
412 if (SetVmsa) {
413 Function.Id.CallId = 2;
414
415 SvsmCallData.RaxIn = Function.Uint64;
416 SvsmCallData.RcxIn = (UINT64)(UINTN)Vmsa;
417 SvsmCallData.RdxIn = (UINT64)(UINTN)Vmsa + SIZE_4KB;
418 SvsmCallData.R8In = ApicId;
419 } else {
420 Function.Id.CallId = 3;
421
422 SvsmCallData.RaxIn = Function.Uint64;
423 SvsmCallData.RcxIn = (UINT64)(UINTN)Vmsa;
424 }
425
426 Ret = SvsmMsrProtocol (&SvsmCallData);
427
428 return (Ret == 0) ? EFI_SUCCESS : EFI_INVALID_PARAMETER;
429}
430
431/**
432 Perform a native RMPADJUST operation to alter the VMSA setting of a page.
433
434 Add or remove the VMSA attribute for a page.
435
436 @param[in] Vmsa Pointer to an SEV-ES save area page
437 @param[in] SetVmsa Boolean indicator as to whether to set or
438 or clear the VMSA setting for the page
439
440 @retval EFI_SUCCESS RMPADJUST operation successful
441 @retval EFI_INVALID_PARAMETER RMPADJUST operation failed, an invalid
442 parameter was supplied
443
444**/
445STATIC
446EFI_STATUS
447BaseVmsaRmpAdjust (
448 IN SEV_ES_SAVE_AREA *Vmsa,
449 IN BOOLEAN SetVmsa
450 )
451{
452 UINT64 Rdx;
453 UINT32 Ret;
454
455 //
456 // The RMPADJUST instruction is used to set or clear the VMSA bit for a
457 // page. The VMSA change is only made when running at VMPL0 and is ignored
458 // otherwise. If too low a target VMPL is specified, the instruction can
459 // succeed without changing the VMSA bit when not running at VMPL0. Using a
460 // target VMPL level of 1, RMPADJUST will return a FAIL_PERMISSION error if
461 // not running at VMPL0, thus ensuring that the VMSA bit is set appropriately
462 // when no error is returned.
463 //
464 Rdx = 1;
465 if (SetVmsa) {
466 Rdx |= RMPADJUST_VMSA_PAGE_BIT;
467 }
468
469 Ret = AsmRmpAdjust ((UINT64)(UINTN)Vmsa, 0, Rdx);
470
471 return (Ret == 0) ? EFI_SUCCESS : EFI_INVALID_PARAMETER;
472}
473
474/**
475 Perform an RMPADJUST operation to alter the VMSA setting of a page.
476
477 Add or remove the VMSA attribute for a page.
478
479 @param[in] Vmsa Pointer to an SEV-ES save area page
480 @param[in] ApicId APIC ID associated with the VMSA
481 @param[in] SetVmsa Boolean indicator as to whether to set or
482 or clear the VMSA setting for the page
483
484 @retval EFI_SUCCESS RMPADJUST operation successful
485 @retval EFI_UNSUPPORTED Operation is not supported
486 @retval EFI_INVALID_PARAMETER RMPADJUST operation failed, an invalid
487 parameter was supplied
488
489**/
490EFI_STATUS
491EFIAPI
492AmdSvsmSnpVmsaRmpAdjust (
493 IN SEV_ES_SAVE_AREA *Vmsa,
494 IN UINT32 ApicId,
495 IN BOOLEAN SetVmsa
496 )
497{
498 return AmdSvsmIsSvsmPresent () ? SvsmVmsaRmpAdjust (Vmsa, ApicId, SetVmsa)
499 : BaseVmsaRmpAdjust (Vmsa, SetVmsa);
500}
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette