VirtualBox

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

Last change on this file was 108794, checked in by vboxsync, 4 weeks ago

Devices/EFI/FirmwareNew: Merge edk2-stable202502 from the vendor branch and make it build for the important platforms, bugref:4643

  • Property svn:eol-style set to native
File size: 49.0 KB
Line 
1/** @file
2 X64 #VC Exception Handler functon.
3
4 Copyright (C) 2020 - 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/LocalApicLib.h>
13#include <Library/MemEncryptSevLib.h>
14#include <Library/CcExitLib.h>
15#include <Library/AmdSvsmLib.h>
16#include <Register/Amd/Msr.h>
17#include <Register/Intel/Cpuid.h>
18#include <IndustryStandard/InstructionParsing.h>
19
20#include "CcExitVcHandler.h"
21#include "CcInstruction.h"
22
23//
24// Non-automatic Exit function prototype
25//
26typedef
27UINT64
28(*NAE_EXIT) (
29 GHCB *Ghcb,
30 EFI_SYSTEM_CONTEXT_X64 *Regs,
31 CC_INSTRUCTION_DATA *InstructionData
32 );
33
34//
35// SEV-SNP Cpuid table entry/function
36//
37typedef PACKED struct {
38 UINT32 EaxIn;
39 UINT32 EcxIn;
40 UINT64 Unused;
41 UINT64 Unused2;
42 UINT32 Eax;
43 UINT32 Ebx;
44 UINT32 Ecx;
45 UINT32 Edx;
46 UINT64 Reserved;
47} SEV_SNP_CPUID_FUNCTION;
48
49//
50// SEV-SNP Cpuid page format
51//
52typedef PACKED struct {
53 UINT32 Count;
54 UINT32 Reserved1;
55 UINT64 Reserved2;
56 SEV_SNP_CPUID_FUNCTION function[0];
57} SEV_SNP_CPUID_INFO;
58
59/**
60 Report an unsupported event to the hypervisor
61
62 Use the VMGEXIT support to report an unsupported event to the hypervisor.
63
64 @param[in] Ghcb Pointer to the Guest-Hypervisor Communication
65 Block
66 @param[in] Regs x64 processor context
67 @param[in] InstructionData Instruction parsing context
68
69 @return New exception value to propagate
70
71**/
72STATIC
73UINT64
74UnsupportedExit (
75 IN GHCB *Ghcb,
76 IN EFI_SYSTEM_CONTEXT_X64 *Regs,
77 IN CC_INSTRUCTION_DATA *InstructionData
78 )
79{
80 UINT64 Status;
81
82 Status = CcExitVmgExit (Ghcb, SVM_EXIT_UNSUPPORTED, Regs->ExceptionData, 0);
83 if (Status == 0) {
84 GHCB_EVENT_INJECTION Event;
85
86 Event.Uint64 = 0;
87 Event.Elements.Vector = GP_EXCEPTION;
88 Event.Elements.Type = GHCB_EVENT_INJECTION_TYPE_EXCEPTION;
89 Event.Elements.Valid = 1;
90
91 Status = Event.Uint64;
92 }
93
94 return Status;
95}
96
97/**
98 Validate that the MMIO memory access is not to encrypted memory.
99
100 Examine the pagetable entry for the memory specified. MMIO should not be
101 performed against encrypted memory.
102
103 @param[in] Ghcb Pointer to the Guest-Hypervisor Communication Block
104 @param[in] MemoryAddress Memory address to validate
105 @param[in] MemoryLength Memory length to validate
106
107 @retval 0 Memory is not encrypted
108 @return New exception value to propogate
109
110**/
111STATIC
112UINT64
113ValidateMmioMemory (
114 IN GHCB *Ghcb,
115 IN UINTN MemoryAddress,
116 IN UINTN MemoryLength
117 )
118{
119 MEM_ENCRYPT_SEV_ADDRESS_RANGE_STATE State;
120 GHCB_EVENT_INJECTION GpEvent;
121
122 State = MemEncryptSevGetAddressRangeState (
123 0,
124 MemoryAddress,
125 MemoryLength
126 );
127 if (State == MemEncryptSevAddressRangeUnencrypted) {
128 return 0;
129 }
130
131 //
132 // Any state other than unencrypted is an error, issue a #GP.
133 //
134 DEBUG ((
135 DEBUG_ERROR,
136 "MMIO using encrypted memory: %lx\n",
137 (UINT64)MemoryAddress
138 ));
139 GpEvent.Uint64 = 0;
140 GpEvent.Elements.Vector = GP_EXCEPTION;
141 GpEvent.Elements.Type = GHCB_EVENT_INJECTION_TYPE_EXCEPTION;
142 GpEvent.Elements.Valid = 1;
143
144 return GpEvent.Uint64;
145}
146
147/**
148 Handle an MMIO event.
149
150 Use the VMGEXIT instruction to handle either an MMIO read or an MMIO write.
151
152 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
153 Block
154 @param[in, out] Regs x64 processor context
155 @param[in, out] InstructionData Instruction parsing context
156
157 @retval 0 Event handled successfully
158 @return New exception value to propagate
159
160**/
161STATIC
162UINT64
163MmioExit (
164 IN OUT GHCB *Ghcb,
165 IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,
166 IN OUT CC_INSTRUCTION_DATA *InstructionData
167 )
168{
169 UINT64 ExitInfo1, ExitInfo2, Status;
170 UINTN Bytes;
171 UINT64 *Register;
172 UINT8 OpCode, SignByte;
173 UINTN Address;
174
175 Bytes = 0;
176
177 OpCode = *(InstructionData->OpCodes);
178 if (OpCode == TWO_BYTE_OPCODE_ESCAPE) {
179 OpCode = *(InstructionData->OpCodes + 1);
180 }
181
182 switch (OpCode) {
183 //
184 // MMIO write (MOV reg/memX, regX)
185 //
186 case 0x88:
187 Bytes = 1;
188 //
189 // fall through
190 //
191 case 0x89:
192 CcDecodeModRm (Regs, InstructionData);
193 Bytes = ((Bytes != 0) ? Bytes :
194 (InstructionData->DataSize == Size16Bits) ? 2 :
195 (InstructionData->DataSize == Size32Bits) ? 4 :
196 (InstructionData->DataSize == Size64Bits) ? 8 :
197 0);
198
199 if (InstructionData->Ext.ModRm.Mod == 3) {
200 //
201 // NPF on two register operands???
202 //
203 return UnsupportedExit (Ghcb, Regs, InstructionData);
204 }
205
206 Status = ValidateMmioMemory (Ghcb, InstructionData->Ext.RmData, Bytes);
207 if (Status != 0) {
208 return Status;
209 }
210
211 ExitInfo1 = InstructionData->Ext.RmData;
212 ExitInfo2 = Bytes;
213 CopyMem (Ghcb->SharedBuffer, &InstructionData->Ext.RegData, Bytes);
214
215 Ghcb->SaveArea.SwScratch = (UINT64)Ghcb->SharedBuffer;
216 CcExitVmgSetOffsetValid (Ghcb, GhcbSwScratch);
217 Status = CcExitVmgExit (Ghcb, SVM_EXIT_MMIO_WRITE, ExitInfo1, ExitInfo2);
218 if (Status != 0) {
219 return Status;
220 }
221
222 break;
223
224 //
225 // MMIO write (MOV moffsetX, aX)
226 //
227 case 0xA2:
228 Bytes = 1;
229 //
230 // fall through
231 //
232 case 0xA3:
233 Bytes = ((Bytes != 0) ? Bytes :
234 (InstructionData->DataSize == Size16Bits) ? 2 :
235 (InstructionData->DataSize == Size32Bits) ? 4 :
236 (InstructionData->DataSize == Size64Bits) ? 8 :
237 0);
238
239 InstructionData->ImmediateSize = (UINTN)(1 << InstructionData->AddrSize);
240 InstructionData->End += InstructionData->ImmediateSize;
241
242 //
243 // This code is X64 only, so a possible 8-byte copy to a UINTN is ok.
244 // Use a STATIC_ASSERT to be certain the code is being built as X64.
245 //
246 STATIC_ASSERT (
247 sizeof (UINTN) == sizeof (UINT64),
248 "sizeof (UINTN) != sizeof (UINT64), this file must be built as X64"
249 );
250
251 Address = 0;
252 CopyMem (
253 &Address,
254 InstructionData->Immediate,
255 InstructionData->ImmediateSize
256 );
257
258 Status = ValidateMmioMemory (Ghcb, Address, Bytes);
259 if (Status != 0) {
260 return Status;
261 }
262
263 ExitInfo1 = Address;
264 ExitInfo2 = Bytes;
265 CopyMem (Ghcb->SharedBuffer, &Regs->Rax, Bytes);
266
267 Ghcb->SaveArea.SwScratch = (UINT64)Ghcb->SharedBuffer;
268 CcExitVmgSetOffsetValid (Ghcb, GhcbSwScratch);
269 Status = CcExitVmgExit (Ghcb, SVM_EXIT_MMIO_WRITE, ExitInfo1, ExitInfo2);
270 if (Status != 0) {
271 return Status;
272 }
273
274 break;
275
276 //
277 // MMIO write (MOV reg/memX, immX)
278 //
279 case 0xC6:
280 Bytes = 1;
281 //
282 // fall through
283 //
284 case 0xC7:
285 CcDecodeModRm (Regs, InstructionData);
286 Bytes = ((Bytes != 0) ? Bytes :
287 (InstructionData->DataSize == Size16Bits) ? 2 :
288 (InstructionData->DataSize == Size32Bits) ? 4 :
289 0);
290
291 InstructionData->ImmediateSize = Bytes;
292 InstructionData->End += Bytes;
293
294 Status = ValidateMmioMemory (Ghcb, InstructionData->Ext.RmData, Bytes);
295 if (Status != 0) {
296 return Status;
297 }
298
299 ExitInfo1 = InstructionData->Ext.RmData;
300 ExitInfo2 = Bytes;
301 CopyMem (Ghcb->SharedBuffer, InstructionData->Immediate, Bytes);
302
303 Ghcb->SaveArea.SwScratch = (UINT64)Ghcb->SharedBuffer;
304 CcExitVmgSetOffsetValid (Ghcb, GhcbSwScratch);
305 Status = CcExitVmgExit (Ghcb, SVM_EXIT_MMIO_WRITE, ExitInfo1, ExitInfo2);
306 if (Status != 0) {
307 return Status;
308 }
309
310 break;
311
312 //
313 // MMIO read (MOV regX, reg/memX)
314 //
315 case 0x8A:
316 Bytes = 1;
317 //
318 // fall through
319 //
320 case 0x8B:
321 CcDecodeModRm (Regs, InstructionData);
322 Bytes = ((Bytes != 0) ? Bytes :
323 (InstructionData->DataSize == Size16Bits) ? 2 :
324 (InstructionData->DataSize == Size32Bits) ? 4 :
325 (InstructionData->DataSize == Size64Bits) ? 8 :
326 0);
327 if (InstructionData->Ext.ModRm.Mod == 3) {
328 //
329 // NPF on two register operands???
330 //
331 return UnsupportedExit (Ghcb, Regs, InstructionData);
332 }
333
334 Status = ValidateMmioMemory (Ghcb, InstructionData->Ext.RmData, Bytes);
335 if (Status != 0) {
336 return Status;
337 }
338
339 ExitInfo1 = InstructionData->Ext.RmData;
340 ExitInfo2 = Bytes;
341
342 Ghcb->SaveArea.SwScratch = (UINT64)Ghcb->SharedBuffer;
343 CcExitVmgSetOffsetValid (Ghcb, GhcbSwScratch);
344 Status = CcExitVmgExit (Ghcb, SVM_EXIT_MMIO_READ, ExitInfo1, ExitInfo2);
345 if (Status != 0) {
346 return Status;
347 }
348
349 Register = CcGetRegisterPointer (Regs, InstructionData->Ext.ModRm.Reg);
350 if (Bytes == 4) {
351 //
352 // Zero-extend for 32-bit operation
353 //
354 *Register = 0;
355 }
356
357 CopyMem (Register, Ghcb->SharedBuffer, Bytes);
358 break;
359
360 //
361 // MMIO read (MOV aX, moffsetX)
362 //
363 case 0xA0:
364 Bytes = 1;
365 //
366 // fall through
367 //
368 case 0xA1:
369 Bytes = ((Bytes != 0) ? Bytes :
370 (InstructionData->DataSize == Size16Bits) ? 2 :
371 (InstructionData->DataSize == Size32Bits) ? 4 :
372 (InstructionData->DataSize == Size64Bits) ? 8 :
373 0);
374
375 InstructionData->ImmediateSize = (UINTN)(1 << InstructionData->AddrSize);
376 InstructionData->End += InstructionData->ImmediateSize;
377
378 //
379 // This code is X64 only, so a possible 8-byte copy to a UINTN is ok.
380 // Use a STATIC_ASSERT to be certain the code is being built as X64.
381 //
382 STATIC_ASSERT (
383 sizeof (UINTN) == sizeof (UINT64),
384 "sizeof (UINTN) != sizeof (UINT64), this file must be built as X64"
385 );
386
387 Address = 0;
388 CopyMem (
389 &Address,
390 InstructionData->Immediate,
391 InstructionData->ImmediateSize
392 );
393
394 Status = ValidateMmioMemory (Ghcb, Address, Bytes);
395 if (Status != 0) {
396 return Status;
397 }
398
399 ExitInfo1 = Address;
400 ExitInfo2 = Bytes;
401
402 Ghcb->SaveArea.SwScratch = (UINT64)Ghcb->SharedBuffer;
403 CcExitVmgSetOffsetValid (Ghcb, GhcbSwScratch);
404 Status = CcExitVmgExit (Ghcb, SVM_EXIT_MMIO_READ, ExitInfo1, ExitInfo2);
405 if (Status != 0) {
406 return Status;
407 }
408
409 if (Bytes == 4) {
410 //
411 // Zero-extend for 32-bit operation
412 //
413 Regs->Rax = 0;
414 }
415
416 CopyMem (&Regs->Rax, Ghcb->SharedBuffer, Bytes);
417 break;
418
419 //
420 // MMIO read w/ zero-extension ((MOVZX regX, reg/memX)
421 //
422 case 0xB6:
423 Bytes = 1;
424 //
425 // fall through
426 //
427 case 0xB7:
428 CcDecodeModRm (Regs, InstructionData);
429 Bytes = (Bytes != 0) ? Bytes : 2;
430
431 Status = ValidateMmioMemory (Ghcb, InstructionData->Ext.RmData, Bytes);
432 if (Status != 0) {
433 return Status;
434 }
435
436 ExitInfo1 = InstructionData->Ext.RmData;
437 ExitInfo2 = Bytes;
438
439 Ghcb->SaveArea.SwScratch = (UINT64)Ghcb->SharedBuffer;
440 CcExitVmgSetOffsetValid (Ghcb, GhcbSwScratch);
441 Status = CcExitVmgExit (Ghcb, SVM_EXIT_MMIO_READ, ExitInfo1, ExitInfo2);
442 if (Status != 0) {
443 return Status;
444 }
445
446 Register = CcGetRegisterPointer (Regs, InstructionData->Ext.ModRm.Reg);
447 SetMem (Register, (UINTN)(1 << InstructionData->DataSize), 0);
448 CopyMem (Register, Ghcb->SharedBuffer, Bytes);
449 break;
450
451 //
452 // MMIO read w/ sign-extension (MOVSX regX, reg/memX)
453 //
454 case 0xBE:
455 Bytes = 1;
456 //
457 // fall through
458 //
459 case 0xBF:
460 CcDecodeModRm (Regs, InstructionData);
461 Bytes = (Bytes != 0) ? Bytes : 2;
462
463 Status = ValidateMmioMemory (Ghcb, InstructionData->Ext.RmData, Bytes);
464 if (Status != 0) {
465 return Status;
466 }
467
468 ExitInfo1 = InstructionData->Ext.RmData;
469 ExitInfo2 = Bytes;
470
471 Ghcb->SaveArea.SwScratch = (UINT64)Ghcb->SharedBuffer;
472 CcExitVmgSetOffsetValid (Ghcb, GhcbSwScratch);
473 Status = CcExitVmgExit (Ghcb, SVM_EXIT_MMIO_READ, ExitInfo1, ExitInfo2);
474 if (Status != 0) {
475 return Status;
476 }
477
478 if (Bytes == 1) {
479 UINT8 *Data;
480
481 Data = (UINT8 *)Ghcb->SharedBuffer;
482 SignByte = ((*Data & BIT7) != 0) ? 0xFF : 0x00;
483 } else {
484 UINT16 *Data;
485
486 Data = (UINT16 *)Ghcb->SharedBuffer;
487 SignByte = ((*Data & BIT15) != 0) ? 0xFF : 0x00;
488 }
489
490 Register = CcGetRegisterPointer (Regs, InstructionData->Ext.ModRm.Reg);
491 SetMem (Register, (UINTN)(1 << InstructionData->DataSize), SignByte);
492 CopyMem (Register, Ghcb->SharedBuffer, Bytes);
493 break;
494
495 default:
496 DEBUG ((DEBUG_ERROR, "Invalid MMIO opcode (%x)\n", OpCode));
497 Status = GP_EXCEPTION;
498 ASSERT (FALSE);
499 }
500
501 return Status;
502}
503
504/**
505 Handle a MWAIT event.
506
507 Use the VMGEXIT instruction to handle a MWAIT event.
508
509 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
510 Block
511 @param[in, out] Regs x64 processor context
512 @param[in] InstructionData Instruction parsing context
513
514 @retval 0 Event handled successfully
515 @return New exception value to propagate
516
517**/
518STATIC
519UINT64
520MwaitExit (
521 IN OUT GHCB *Ghcb,
522 IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,
523 IN CC_INSTRUCTION_DATA *InstructionData
524 )
525{
526 Ghcb->SaveArea.Rax = Regs->Rax;
527 CcExitVmgSetOffsetValid (Ghcb, GhcbRax);
528 Ghcb->SaveArea.Rcx = Regs->Rcx;
529 CcExitVmgSetOffsetValid (Ghcb, GhcbRcx);
530
531 return CcExitVmgExit (Ghcb, SVM_EXIT_MWAIT, 0, 0);
532}
533
534/**
535 Handle a MONITOR event.
536
537 Use the VMGEXIT instruction to handle a MONITOR event.
538
539 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
540 Block
541 @param[in, out] Regs x64 processor context
542 @param[in] InstructionData Instruction parsing context
543
544 @retval 0 Event handled successfully
545 @return New exception value to propagate
546
547**/
548STATIC
549UINT64
550MonitorExit (
551 IN OUT GHCB *Ghcb,
552 IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,
553 IN CC_INSTRUCTION_DATA *InstructionData
554 )
555{
556 Ghcb->SaveArea.Rax = Regs->Rax; // Identity mapped, so VA = PA
557 CcExitVmgSetOffsetValid (Ghcb, GhcbRax);
558 Ghcb->SaveArea.Rcx = Regs->Rcx;
559 CcExitVmgSetOffsetValid (Ghcb, GhcbRcx);
560 Ghcb->SaveArea.Rdx = Regs->Rdx;
561 CcExitVmgSetOffsetValid (Ghcb, GhcbRdx);
562
563 return CcExitVmgExit (Ghcb, SVM_EXIT_MONITOR, 0, 0);
564}
565
566/**
567 Handle a WBINVD event.
568
569 Use the VMGEXIT instruction to handle a WBINVD event.
570
571 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
572 Block
573 @param[in, out] Regs x64 processor context
574 @param[in] InstructionData Instruction parsing context
575
576 @retval 0 Event handled successfully
577 @return New exception value to propagate
578
579**/
580STATIC
581UINT64
582WbinvdExit (
583 IN OUT GHCB *Ghcb,
584 IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,
585 IN CC_INSTRUCTION_DATA *InstructionData
586 )
587{
588 return CcExitVmgExit (Ghcb, SVM_EXIT_WBINVD, 0, 0);
589}
590
591/**
592 Handle a RDTSCP event.
593
594 Use the VMGEXIT instruction to handle a RDTSCP event.
595
596 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
597 Block
598 @param[in, out] Regs x64 processor context
599 @param[in] InstructionData Instruction parsing context
600
601 @retval 0 Event handled successfully
602 @return New exception value to propagate
603
604**/
605STATIC
606UINT64
607RdtscpExit (
608 IN OUT GHCB *Ghcb,
609 IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,
610 IN CC_INSTRUCTION_DATA *InstructionData
611 )
612{
613 UINT64 Status;
614
615 CcDecodeModRm (Regs, InstructionData);
616
617 Status = CcExitVmgExit (Ghcb, SVM_EXIT_RDTSCP, 0, 0);
618 if (Status != 0) {
619 return Status;
620 }
621
622 if (!CcExitVmgIsOffsetValid (Ghcb, GhcbRax) ||
623 !CcExitVmgIsOffsetValid (Ghcb, GhcbRcx) ||
624 !CcExitVmgIsOffsetValid (Ghcb, GhcbRdx))
625 {
626 return UnsupportedExit (Ghcb, Regs, InstructionData);
627 }
628
629 Regs->Rax = Ghcb->SaveArea.Rax;
630 Regs->Rcx = Ghcb->SaveArea.Rcx;
631 Regs->Rdx = Ghcb->SaveArea.Rdx;
632
633 return 0;
634}
635
636/**
637 Handle a VMMCALL event.
638
639 Use the VMGEXIT instruction to handle a VMMCALL event.
640
641 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
642 Block
643 @param[in, out] Regs x64 processor context
644 @param[in] InstructionData Instruction parsing context
645
646 @retval 0 Event handled successfully
647 @return New exception value to propagate
648
649**/
650STATIC
651UINT64
652VmmCallExit (
653 IN OUT GHCB *Ghcb,
654 IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,
655 IN CC_INSTRUCTION_DATA *InstructionData
656 )
657{
658 UINT64 Status;
659
660 Ghcb->SaveArea.Rax = Regs->Rax;
661 CcExitVmgSetOffsetValid (Ghcb, GhcbRax);
662 Ghcb->SaveArea.Cpl = (UINT8)(Regs->Cs & 0x3);
663 CcExitVmgSetOffsetValid (Ghcb, GhcbCpl);
664
665 Status = CcExitVmgExit (Ghcb, SVM_EXIT_VMMCALL, 0, 0);
666 if (Status != 0) {
667 return Status;
668 }
669
670 if (!CcExitVmgIsOffsetValid (Ghcb, GhcbRax)) {
671 return UnsupportedExit (Ghcb, Regs, InstructionData);
672 }
673
674 Regs->Rax = Ghcb->SaveArea.Rax;
675
676 return 0;
677}
678
679/**
680 Handle an MSR event.
681
682 Use the VMGEXIT instruction to handle either a RDMSR or WRMSR event.
683
684 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
685 Block
686 @param[in, out] Regs x64 processor context
687 @param[in] InstructionData Instruction parsing context
688
689 @retval 0 Event handled successfully
690 @return New exception value to propagate
691
692**/
693STATIC
694UINT64
695MsrExit (
696 IN OUT GHCB *Ghcb,
697 IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,
698 IN CC_INSTRUCTION_DATA *InstructionData
699 )
700{
701 MSR_SVSM_CAA_REGISTER Msr;
702 UINT64 ExitInfo1;
703 UINT64 Status;
704
705 ExitInfo1 = 0;
706
707 //
708 // The SVSM CAA MSR is a software implemented MSR and not supported
709 // by the hardware, handle it directly.
710 //
711 if (Regs->Rax == MSR_SVSM_CAA) {
712 // Writes to the SVSM CAA MSR are ignored
713 if (*(InstructionData->OpCodes + 1) == 0x30) {
714 return 0;
715 }
716
717 Msr.Uint64 = AmdSvsmSnpGetCaa ();
718 Regs->Rax = Msr.Bits.Lower32Bits;
719 Regs->Rdx = Msr.Bits.Upper32Bits;
720
721 return 0;
722 }
723
724 switch (*(InstructionData->OpCodes + 1)) {
725 case 0x30: // WRMSR
726 ExitInfo1 = 1;
727 Ghcb->SaveArea.Rax = Regs->Rax;
728 CcExitVmgSetOffsetValid (Ghcb, GhcbRax);
729 Ghcb->SaveArea.Rdx = Regs->Rdx;
730 CcExitVmgSetOffsetValid (Ghcb, GhcbRdx);
731 //
732 // fall through
733 //
734 case 0x32: // RDMSR
735 Ghcb->SaveArea.Rcx = Regs->Rcx;
736 CcExitVmgSetOffsetValid (Ghcb, GhcbRcx);
737 break;
738 default:
739 return UnsupportedExit (Ghcb, Regs, InstructionData);
740 }
741
742 Status = CcExitVmgExit (Ghcb, SVM_EXIT_MSR, ExitInfo1, 0);
743 if (Status != 0) {
744 return Status;
745 }
746
747 if (ExitInfo1 == 0) {
748 if (!CcExitVmgIsOffsetValid (Ghcb, GhcbRax) ||
749 !CcExitVmgIsOffsetValid (Ghcb, GhcbRdx))
750 {
751 return UnsupportedExit (Ghcb, Regs, InstructionData);
752 }
753
754 Regs->Rax = Ghcb->SaveArea.Rax;
755 Regs->Rdx = Ghcb->SaveArea.Rdx;
756 }
757
758 return 0;
759}
760
761/**
762 Build the IOIO event information.
763
764 The IOIO event information identifies the type of IO operation to be performed
765 by the hypervisor. Build this information based on the instruction data.
766
767 @param[in] Regs x64 processor context
768 @param[in, out] InstructionData Instruction parsing context
769
770 @return IOIO event information value
771
772**/
773STATIC
774UINT64
775IoioExitInfo (
776 IN EFI_SYSTEM_CONTEXT_X64 *Regs,
777 IN OUT CC_INSTRUCTION_DATA *InstructionData
778 )
779{
780 UINT64 ExitInfo;
781
782 ExitInfo = 0;
783
784 switch (*(InstructionData->OpCodes)) {
785 //
786 // INS opcodes
787 //
788 case 0x6C:
789 case 0x6D:
790 ExitInfo |= IOIO_TYPE_INS;
791 ExitInfo |= IOIO_SEG_ES;
792 ExitInfo |= ((Regs->Rdx & 0xffff) << 16);
793 break;
794
795 //
796 // OUTS opcodes
797 //
798 case 0x6E:
799 case 0x6F:
800 ExitInfo |= IOIO_TYPE_OUTS;
801 ExitInfo |= IOIO_SEG_DS;
802 ExitInfo |= ((Regs->Rdx & 0xffff) << 16);
803 break;
804
805 //
806 // IN immediate opcodes
807 //
808 case 0xE4:
809 case 0xE5:
810 InstructionData->ImmediateSize = 1;
811 InstructionData->End++;
812 ExitInfo |= IOIO_TYPE_IN;
813 ExitInfo |= ((*(InstructionData->OpCodes + 1)) << 16);
814 break;
815
816 //
817 // OUT immediate opcodes
818 //
819 case 0xE6:
820 case 0xE7:
821 InstructionData->ImmediateSize = 1;
822 InstructionData->End++;
823 ExitInfo |= IOIO_TYPE_OUT;
824 ExitInfo |= ((*(InstructionData->OpCodes + 1)) << 16) | IOIO_TYPE_OUT;
825 break;
826
827 //
828 // IN register opcodes
829 //
830 case 0xEC:
831 case 0xED:
832 ExitInfo |= IOIO_TYPE_IN;
833 ExitInfo |= ((Regs->Rdx & 0xffff) << 16);
834 break;
835
836 //
837 // OUT register opcodes
838 //
839 case 0xEE:
840 case 0xEF:
841 ExitInfo |= IOIO_TYPE_OUT;
842 ExitInfo |= ((Regs->Rdx & 0xffff) << 16);
843 break;
844
845 default:
846 return 0;
847 }
848
849 switch (*(InstructionData->OpCodes)) {
850 //
851 // Single-byte opcodes
852 //
853 case 0x6C:
854 case 0x6E:
855 case 0xE4:
856 case 0xE6:
857 case 0xEC:
858 case 0xEE:
859 ExitInfo |= IOIO_DATA_8;
860 break;
861
862 //
863 // Length determined by instruction parsing
864 //
865 default:
866 ExitInfo |= (InstructionData->DataSize == Size16Bits) ? IOIO_DATA_16
867 : IOIO_DATA_32;
868 }
869
870 switch (InstructionData->AddrSize) {
871 case Size16Bits:
872 ExitInfo |= IOIO_ADDR_16;
873 break;
874
875 case Size32Bits:
876 ExitInfo |= IOIO_ADDR_32;
877 break;
878
879 case Size64Bits:
880 ExitInfo |= IOIO_ADDR_64;
881 break;
882
883 default:
884 break;
885 }
886
887 if (InstructionData->RepMode != 0) {
888 ExitInfo |= IOIO_REP;
889 }
890
891 return ExitInfo;
892}
893
894/**
895 Handle an IOIO event.
896
897 Use the VMGEXIT instruction to handle an IOIO event.
898
899 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
900 Block
901 @param[in, out] Regs x64 processor context
902 @param[in] InstructionData Instruction parsing context
903
904 @retval 0 Event handled successfully
905 @return New exception value to propagate
906
907**/
908STATIC
909UINT64
910IoioExit (
911 IN OUT GHCB *Ghcb,
912 IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,
913 IN CC_INSTRUCTION_DATA *InstructionData
914 )
915{
916 UINT64 ExitInfo1, ExitInfo2, Status;
917 BOOLEAN IsString;
918
919 ExitInfo1 = IoioExitInfo (Regs, InstructionData);
920 if (ExitInfo1 == 0) {
921 return UnsupportedExit (Ghcb, Regs, InstructionData);
922 }
923
924 IsString = ((ExitInfo1 & IOIO_TYPE_STR) != 0) ? TRUE : FALSE;
925 if (IsString) {
926 UINTN IoBytes, VmgExitBytes;
927 UINTN GhcbCount, OpCount;
928
929 Status = 0;
930
931 IoBytes = IOIO_DATA_BYTES (ExitInfo1);
932 GhcbCount = sizeof (Ghcb->SharedBuffer) / IoBytes;
933
934 OpCount = ((ExitInfo1 & IOIO_REP) != 0) ? Regs->Rcx : 1;
935 while (OpCount != 0) {
936 ExitInfo2 = MIN (OpCount, GhcbCount);
937 VmgExitBytes = ExitInfo2 * IoBytes;
938
939 if ((ExitInfo1 & IOIO_TYPE_IN) == 0) {
940 CopyMem (Ghcb->SharedBuffer, (VOID *)Regs->Rsi, VmgExitBytes);
941 Regs->Rsi += VmgExitBytes;
942 }
943
944 Ghcb->SaveArea.SwScratch = (UINT64)Ghcb->SharedBuffer;
945 CcExitVmgSetOffsetValid (Ghcb, GhcbSwScratch);
946 Status = CcExitVmgExit (Ghcb, SVM_EXIT_IOIO_PROT, ExitInfo1, ExitInfo2);
947 if (Status != 0) {
948 return Status;
949 }
950
951 if ((ExitInfo1 & IOIO_TYPE_IN) != 0) {
952 CopyMem ((VOID *)Regs->Rdi, Ghcb->SharedBuffer, VmgExitBytes);
953 Regs->Rdi += VmgExitBytes;
954 }
955
956 if ((ExitInfo1 & IOIO_REP) != 0) {
957 Regs->Rcx -= ExitInfo2;
958 }
959
960 OpCount -= ExitInfo2;
961 }
962 } else {
963 if ((ExitInfo1 & IOIO_TYPE_IN) != 0) {
964 Ghcb->SaveArea.Rax = 0;
965 } else {
966 CopyMem (&Ghcb->SaveArea.Rax, &Regs->Rax, IOIO_DATA_BYTES (ExitInfo1));
967 }
968
969 CcExitVmgSetOffsetValid (Ghcb, GhcbRax);
970
971 Status = CcExitVmgExit (Ghcb, SVM_EXIT_IOIO_PROT, ExitInfo1, 0);
972 if (Status != 0) {
973 return Status;
974 }
975
976 if ((ExitInfo1 & IOIO_TYPE_IN) != 0) {
977 if (!CcExitVmgIsOffsetValid (Ghcb, GhcbRax)) {
978 return UnsupportedExit (Ghcb, Regs, InstructionData);
979 }
980
981 CopyMem (&Regs->Rax, &Ghcb->SaveArea.Rax, IOIO_DATA_BYTES (ExitInfo1));
982 }
983 }
984
985 return 0;
986}
987
988/**
989 Handle a INVD event.
990
991 Use the VMGEXIT instruction to handle a INVD event.
992
993 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
994 Block
995 @param[in, out] Regs x64 processor context
996 @param[in] InstructionData Instruction parsing context
997
998 @retval 0 Event handled successfully
999 @return New exception value to propagate
1000
1001**/
1002STATIC
1003UINT64
1004InvdExit (
1005 IN OUT GHCB *Ghcb,
1006 IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,
1007 IN CC_INSTRUCTION_DATA *InstructionData
1008 )
1009{
1010 return CcExitVmgExit (Ghcb, SVM_EXIT_INVD, 0, 0);
1011}
1012
1013/**
1014 Fetch CPUID leaf/function via hypervisor/VMGEXIT.
1015
1016 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1017 Block
1018 @param[in] EaxIn EAX input for cpuid instruction
1019 @param[in] EcxIn ECX input for cpuid instruction
1020 @param[in] Xcr0In XCR0 at time of cpuid instruction
1021 @param[in, out] Eax Pointer to store leaf's EAX value
1022 @param[in, out] Ebx Pointer to store leaf's EBX value
1023 @param[in, out] Ecx Pointer to store leaf's ECX value
1024 @param[in, out] Edx Pointer to store leaf's EDX value
1025 @param[in, out] Status Pointer to store status from VMGEXIT (always 0
1026 unless return value indicates failure)
1027 @param[in, out] Unsupported Pointer to store indication of unsupported
1028 VMGEXIT (always false unless return value
1029 indicates failure)
1030
1031 @retval TRUE CPUID leaf fetch successfully.
1032 @retval FALSE Error occurred while fetching CPUID leaf. Callers
1033 should Status and Unsupported and handle
1034 accordingly if they indicate a more precise
1035 error condition.
1036
1037**/
1038STATIC
1039BOOLEAN
1040GetCpuidHyp (
1041 IN OUT GHCB *Ghcb,
1042 IN UINT32 EaxIn,
1043 IN UINT32 EcxIn,
1044 IN UINT64 XCr0,
1045 IN OUT UINT32 *Eax,
1046 IN OUT UINT32 *Ebx,
1047 IN OUT UINT32 *Ecx,
1048 IN OUT UINT32 *Edx,
1049 IN OUT UINT64 *Status,
1050 IN OUT BOOLEAN *UnsupportedExit
1051 )
1052{
1053 *UnsupportedExit = FALSE;
1054 Ghcb->SaveArea.Rax = EaxIn;
1055 CcExitVmgSetOffsetValid (Ghcb, GhcbRax);
1056 Ghcb->SaveArea.Rcx = EcxIn;
1057 CcExitVmgSetOffsetValid (Ghcb, GhcbRcx);
1058 if (EaxIn == CPUID_EXTENDED_STATE) {
1059 Ghcb->SaveArea.XCr0 = XCr0;
1060 CcExitVmgSetOffsetValid (Ghcb, GhcbXCr0);
1061 }
1062
1063 *Status = CcExitVmgExit (Ghcb, SVM_EXIT_CPUID, 0, 0);
1064 if (*Status != 0) {
1065 return FALSE;
1066 }
1067
1068 if (!CcExitVmgIsOffsetValid (Ghcb, GhcbRax) ||
1069 !CcExitVmgIsOffsetValid (Ghcb, GhcbRbx) ||
1070 !CcExitVmgIsOffsetValid (Ghcb, GhcbRcx) ||
1071 !CcExitVmgIsOffsetValid (Ghcb, GhcbRdx))
1072 {
1073 *UnsupportedExit = TRUE;
1074 return FALSE;
1075 }
1076
1077 if (Eax) {
1078 *Eax = (UINT32)(UINTN)Ghcb->SaveArea.Rax;
1079 }
1080
1081 if (Ebx) {
1082 *Ebx = (UINT32)(UINTN)Ghcb->SaveArea.Rbx;
1083 }
1084
1085 if (Ecx) {
1086 *Ecx = (UINT32)(UINTN)Ghcb->SaveArea.Rcx;
1087 }
1088
1089 if (Edx) {
1090 *Edx = (UINT32)(UINTN)Ghcb->SaveArea.Rdx;
1091 }
1092
1093 return TRUE;
1094}
1095
1096/**
1097 Check if SEV-SNP enabled.
1098
1099 @retval TRUE SEV-SNP is enabled.
1100 @retval FALSE SEV-SNP is disabled.
1101
1102**/
1103STATIC
1104BOOLEAN
1105SnpEnabled (
1106 VOID
1107 )
1108{
1109 MSR_SEV_STATUS_REGISTER Msr;
1110
1111 Msr.Uint32 = AsmReadMsr32 (MSR_SEV_STATUS);
1112
1113 return !!Msr.Bits.SevSnpBit;
1114}
1115
1116/**
1117 Calculate the total XSAVE area size for enabled XSAVE areas
1118
1119 @param[in] XFeaturesEnabled Bit-mask of enabled XSAVE features/areas as
1120 indicated by XCR0/MSR_IA32_XSS bits
1121 @param[in, out] XSaveSize Pointer to storage for calculated XSAVE area
1122 size
1123 @param[in] Compacted Whether or not the calculation is for the
1124 normal XSAVE area size (leaf 0xD,0x0,EBX) or
1125 compacted XSAVE area size (leaf 0xD,0x1,EBX)
1126
1127
1128 @retval TRUE XSAVE size calculation was successful.
1129 @retval FALSE XSAVE size calculation was unsuccessful.
1130**/
1131STATIC
1132BOOLEAN
1133GetCpuidXSaveSize (
1134 IN UINT64 XFeaturesEnabled,
1135 IN OUT UINT32 *XSaveSize,
1136 IN BOOLEAN Compacted
1137 )
1138{
1139 SEV_SNP_CPUID_INFO *CpuidInfo;
1140 UINT64 XFeaturesFound = 0;
1141 UINT32 Idx;
1142
1143 //
1144 // The base/legacy XSave size is documented to be 0x240 in the APM.
1145 //
1146 *XSaveSize = 0x240;
1147 CpuidInfo = (SEV_SNP_CPUID_INFO *)(UINT64)PcdGet32 (PcdOvmfCpuidBase);
1148
1149 for (Idx = 0; Idx < CpuidInfo->Count; Idx++) {
1150 SEV_SNP_CPUID_FUNCTION *CpuidFn = &CpuidInfo->function[Idx];
1151
1152 if (!((CpuidFn->EaxIn == 0xD) && (CpuidFn->EcxIn > 1))) {
1153 continue;
1154 }
1155
1156 if (XFeaturesFound & (1ULL << CpuidFn->EcxIn) ||
1157 !(XFeaturesEnabled & (1ULL << CpuidFn->EcxIn)))
1158 {
1159 continue;
1160 }
1161
1162 XFeaturesFound |= (1ULL << CpuidFn->EcxIn);
1163 if (Compacted) {
1164 *XSaveSize += CpuidFn->Eax;
1165 } else {
1166 *XSaveSize = MAX (*XSaveSize, CpuidFn->Eax + CpuidFn->Ebx);
1167 }
1168 }
1169
1170 /*
1171 * Either the guest set unsupported XCR0/XSS bits, or the corresponding
1172 * entries in the CPUID table were not present. This is an invalid state.
1173 */
1174 if (XFeaturesFound != (XFeaturesEnabled & ~3UL)) {
1175 return FALSE;
1176 }
1177
1178 return TRUE;
1179}
1180
1181/**
1182 Check if a CPUID leaf/function is indexed via ECX sub-leaf/sub-function
1183
1184 @param[in] EaxIn EAX input for cpuid instruction
1185
1186 @retval FALSE cpuid leaf/function is not indexed by ECX input
1187 @retval TRUE cpuid leaf/function is indexed by ECX input
1188
1189**/
1190STATIC
1191BOOLEAN
1192IsFunctionIndexed (
1193 IN UINT32 EaxIn
1194 )
1195{
1196 switch (EaxIn) {
1197 case CPUID_CACHE_PARAMS:
1198 case CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS:
1199 case CPUID_EXTENDED_TOPOLOGY:
1200 case CPUID_EXTENDED_STATE:
1201 case CPUID_INTEL_RDT_MONITORING:
1202 case CPUID_INTEL_RDT_ALLOCATION:
1203 case CPUID_INTEL_SGX:
1204 case CPUID_INTEL_PROCESSOR_TRACE:
1205 case CPUID_DETERMINISTIC_ADDRESS_TRANSLATION_PARAMETERS:
1206 case CPUID_V2_EXTENDED_TOPOLOGY:
1207 case 0x8000001D: /* Cache Topology Information */
1208 return TRUE;
1209 }
1210
1211 return FALSE;
1212}
1213
1214/**
1215 Fetch CPUID leaf/function via SEV-SNP CPUID table.
1216
1217 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1218 Block
1219 @param[in] EaxIn EAX input for cpuid instruction
1220 @param[in] EcxIn ECX input for cpuid instruction
1221 @param[in] Xcr0In XCR0 at time of cpuid instruction
1222 @param[in, out] Eax Pointer to store leaf's EAX value
1223 @param[in, out] Ebx Pointer to store leaf's EBX value
1224 @param[in, out] Ecx Pointer to store leaf's ECX value
1225 @param[in, out] Edx Pointer to store leaf's EDX value
1226 @param[in, out] Status Pointer to store status from VMGEXIT (always 0
1227 unless return value indicates failure)
1228 @param[in, out] Unsupported Pointer to store indication of unsupported
1229 VMGEXIT (always false unless return value
1230 indicates failure)
1231
1232 @retval TRUE CPUID leaf fetch successfully.
1233 @retval FALSE Error occurred while fetching CPUID leaf. Callers
1234 should Status and Unsupported and handle
1235 accordingly if they indicate a more precise
1236 error condition.
1237
1238**/
1239STATIC
1240BOOLEAN
1241GetCpuidFw (
1242 IN OUT GHCB *Ghcb,
1243 IN UINT32 EaxIn,
1244 IN UINT32 EcxIn,
1245 IN UINT64 XCr0,
1246 IN OUT UINT32 *Eax,
1247 IN OUT UINT32 *Ebx,
1248 IN OUT UINT32 *Ecx,
1249 IN OUT UINT32 *Edx,
1250 IN OUT UINT64 *Status,
1251 IN OUT BOOLEAN *Unsupported
1252 )
1253{
1254 SEV_SNP_CPUID_INFO *CpuidInfo;
1255 BOOLEAN Found;
1256 UINT32 Idx;
1257
1258 CpuidInfo = (SEV_SNP_CPUID_INFO *)(UINT64)PcdGet32 (PcdOvmfCpuidBase);
1259 Found = FALSE;
1260
1261 for (Idx = 0; Idx < CpuidInfo->Count; Idx++) {
1262 SEV_SNP_CPUID_FUNCTION *CpuidFn = &CpuidInfo->function[Idx];
1263
1264 if (CpuidFn->EaxIn != EaxIn) {
1265 continue;
1266 }
1267
1268 if (IsFunctionIndexed (CpuidFn->EaxIn) && (CpuidFn->EcxIn != EcxIn)) {
1269 continue;
1270 }
1271
1272 *Eax = CpuidFn->Eax;
1273 *Ebx = CpuidFn->Ebx;
1274 *Ecx = CpuidFn->Ecx;
1275 *Edx = CpuidFn->Edx;
1276
1277 Found = TRUE;
1278 break;
1279 }
1280
1281 if (!Found) {
1282 *Eax = *Ebx = *Ecx = *Edx = 0;
1283 goto Out;
1284 }
1285
1286 if (EaxIn == CPUID_VERSION_INFO) {
1287 IA32_CR4 Cr4;
1288 UINT32 Ebx2;
1289 UINT32 Edx2;
1290
1291 if (!GetCpuidHyp (
1292 Ghcb,
1293 EaxIn,
1294 EcxIn,
1295 XCr0,
1296 NULL,
1297 &Ebx2,
1298 NULL,
1299 &Edx2,
1300 Status,
1301 Unsupported
1302 ))
1303 {
1304 return FALSE;
1305 }
1306
1307 /* initial APIC ID */
1308 *Ebx = (*Ebx & 0x00FFFFFF) | (Ebx2 & 0xFF000000);
1309 /* APIC enabled bit */
1310 *Edx = (*Edx & ~BIT9) | (Edx2 & BIT9);
1311 /* OSXSAVE enabled bit */
1312 Cr4.UintN = AsmReadCr4 ();
1313 *Ecx = (Cr4.Bits.OSXSAVE) ? (*Ecx & ~BIT27) | (*Ecx & BIT27)
1314 : (*Ecx & ~BIT27);
1315 } else if (EaxIn == CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS) {
1316 IA32_CR4 Cr4;
1317
1318 Cr4.UintN = AsmReadCr4 ();
1319 /* OSPKE enabled bit */
1320 *Ecx = (Cr4.Bits.PKE) ? (*Ecx | BIT4) : (*Ecx & ~BIT4);
1321 } else if (EaxIn == CPUID_EXTENDED_TOPOLOGY) {
1322 if (!GetCpuidHyp (
1323 Ghcb,
1324 EaxIn,
1325 EcxIn,
1326 XCr0,
1327 NULL,
1328 NULL,
1329 NULL,
1330 Edx,
1331 Status,
1332 Unsupported
1333 ))
1334 {
1335 return FALSE;
1336 }
1337 } else if ((EaxIn == CPUID_EXTENDED_STATE) && ((EcxIn == 0) || (EcxIn == 1))) {
1338 MSR_IA32_XSS_REGISTER XssMsr;
1339 BOOLEAN Compacted;
1340 UINT32 XSaveSize;
1341
1342 XssMsr.Uint64 = 0;
1343 Compacted = FALSE;
1344 if (EcxIn == 1) {
1345 /*
1346 * The PPR and APM aren't clear on what size should be encoded in
1347 * 0xD:0x1:EBX when compaction is not enabled by either XSAVEC or
1348 * XSAVES, as these are generally fixed to 1 on real CPUs. Report
1349 * this undefined case as an error.
1350 */
1351 if (!(*Eax & (BIT3 | BIT1))) {
1352 /* (XSAVES | XSAVEC) */
1353 return FALSE;
1354 }
1355
1356 Compacted = TRUE;
1357 XssMsr.Uint64 = AsmReadMsr64 (MSR_IA32_XSS);
1358 }
1359
1360 if (!GetCpuidXSaveSize (
1361 XCr0 | XssMsr.Uint64,
1362 &XSaveSize,
1363 Compacted
1364 ))
1365 {
1366 return FALSE;
1367 }
1368
1369 *Ebx = XSaveSize;
1370 } else if (EaxIn == 0x8000001E) {
1371 UINT32 Ebx2;
1372 UINT32 Ecx2;
1373
1374 /* extended APIC ID */
1375 if (!GetCpuidHyp (
1376 Ghcb,
1377 EaxIn,
1378 EcxIn,
1379 XCr0,
1380 Eax,
1381 &Ebx2,
1382 &Ecx2,
1383 NULL,
1384 Status,
1385 Unsupported
1386 ))
1387 {
1388 return FALSE;
1389 }
1390
1391 /* compute ID */
1392 *Ebx = (*Ebx & 0xFFFFFF00) | (Ebx2 & 0x000000FF);
1393 /* node ID */
1394 *Ecx = (*Ecx & 0xFFFFFF00) | (Ecx2 & 0x000000FF);
1395 } else if (EaxIn == 0x8000001F) {
1396 /* Set the SVSM feature bit if running under an SVSM */
1397 if (AmdSvsmIsSvsmPresent ()) {
1398 *Eax |= BIT28;
1399 }
1400 }
1401
1402Out:
1403 *Status = 0;
1404 *Unsupported = FALSE;
1405 return TRUE;
1406}
1407
1408/**
1409 Handle a CPUID event.
1410
1411 Use VMGEXIT instruction or CPUID table to handle a CPUID event.
1412
1413 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1414 Block
1415 @param[in, out] Regs x64 processor context
1416 @param[in] InstructionData Instruction parsing context
1417
1418 @retval 0 Event handled successfully
1419 @return New exception value to propagate
1420
1421**/
1422STATIC
1423UINT64
1424CpuidExit (
1425 IN OUT GHCB *Ghcb,
1426 IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,
1427 IN CC_INSTRUCTION_DATA *InstructionData
1428 )
1429{
1430 BOOLEAN Unsupported;
1431 UINT64 Status;
1432 UINT32 EaxIn;
1433 UINT32 EcxIn;
1434 UINT64 XCr0;
1435 UINT32 Eax;
1436 UINT32 Ebx;
1437 UINT32 Ecx;
1438 UINT32 Edx;
1439
1440 EaxIn = (UINT32)(UINTN)Regs->Rax;
1441 EcxIn = (UINT32)(UINTN)Regs->Rcx;
1442
1443 if (EaxIn == CPUID_EXTENDED_STATE) {
1444 IA32_CR4 Cr4;
1445
1446 Cr4.UintN = AsmReadCr4 ();
1447 Ghcb->SaveArea.XCr0 = (Cr4.Bits.OSXSAVE == 1) ? AsmXGetBv (0) : 1;
1448 XCr0 = (Cr4.Bits.OSXSAVE == 1) ? AsmXGetBv (0) : 1;
1449 }
1450
1451 if (SnpEnabled ()) {
1452 if (!GetCpuidFw (
1453 Ghcb,
1454 EaxIn,
1455 EcxIn,
1456 XCr0,
1457 &Eax,
1458 &Ebx,
1459 &Ecx,
1460 &Edx,
1461 &Status,
1462 &Unsupported
1463 ))
1464 {
1465 goto CpuidFail;
1466 }
1467 } else {
1468 if (!GetCpuidHyp (
1469 Ghcb,
1470 EaxIn,
1471 EcxIn,
1472 XCr0,
1473 &Eax,
1474 &Ebx,
1475 &Ecx,
1476 &Edx,
1477 &Status,
1478 &Unsupported
1479 ))
1480 {
1481 goto CpuidFail;
1482 }
1483 }
1484
1485 Regs->Rax = Eax;
1486 Regs->Rbx = Ebx;
1487 Regs->Rcx = Ecx;
1488 Regs->Rdx = Edx;
1489
1490 return 0;
1491
1492CpuidFail:
1493 if (Unsupported) {
1494 return UnsupportedExit (Ghcb, Regs, InstructionData);
1495 }
1496
1497 return Status;
1498}
1499
1500/**
1501 Handle a RDPMC event.
1502
1503 Use the VMGEXIT instruction to handle a RDPMC event.
1504
1505 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1506 Block
1507 @param[in, out] Regs x64 processor context
1508 @param[in] InstructionData Instruction parsing context
1509
1510 @retval 0 Event handled successfully
1511 @return New exception value to propagate
1512
1513**/
1514STATIC
1515UINT64
1516RdpmcExit (
1517 IN OUT GHCB *Ghcb,
1518 IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,
1519 IN CC_INSTRUCTION_DATA *InstructionData
1520 )
1521{
1522 UINT64 Status;
1523
1524 Ghcb->SaveArea.Rcx = Regs->Rcx;
1525 CcExitVmgSetOffsetValid (Ghcb, GhcbRcx);
1526
1527 Status = CcExitVmgExit (Ghcb, SVM_EXIT_RDPMC, 0, 0);
1528 if (Status != 0) {
1529 return Status;
1530 }
1531
1532 if (!CcExitVmgIsOffsetValid (Ghcb, GhcbRax) ||
1533 !CcExitVmgIsOffsetValid (Ghcb, GhcbRdx))
1534 {
1535 return UnsupportedExit (Ghcb, Regs, InstructionData);
1536 }
1537
1538 Regs->Rax = Ghcb->SaveArea.Rax;
1539 Regs->Rdx = Ghcb->SaveArea.Rdx;
1540
1541 return 0;
1542}
1543
1544/**
1545 Handle a RDTSC event.
1546
1547 Use the VMGEXIT instruction to handle a RDTSC event.
1548
1549 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1550 Block
1551 @param[in, out] Regs x64 processor context
1552 @param[in] InstructionData Instruction parsing context
1553
1554 @retval 0 Event handled successfully
1555 @return New exception value to propagate
1556
1557**/
1558STATIC
1559UINT64
1560RdtscExit (
1561 IN OUT GHCB *Ghcb,
1562 IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,
1563 IN CC_INSTRUCTION_DATA *InstructionData
1564 )
1565{
1566 UINT64 Status;
1567
1568 Status = CcExitVmgExit (Ghcb, SVM_EXIT_RDTSC, 0, 0);
1569 if (Status != 0) {
1570 return Status;
1571 }
1572
1573 if (!CcExitVmgIsOffsetValid (Ghcb, GhcbRax) ||
1574 !CcExitVmgIsOffsetValid (Ghcb, GhcbRdx))
1575 {
1576 return UnsupportedExit (Ghcb, Regs, InstructionData);
1577 }
1578
1579 Regs->Rax = Ghcb->SaveArea.Rax;
1580 Regs->Rdx = Ghcb->SaveArea.Rdx;
1581
1582 return 0;
1583}
1584
1585/**
1586 Handle a DR7 register write event.
1587
1588 Use the VMGEXIT instruction to handle a DR7 write event.
1589
1590 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1591 Block
1592 @param[in, out] Regs x64 processor context
1593 @param[in] InstructionData Instruction parsing context
1594
1595 @retval 0 Event handled successfully
1596 @return New exception value to propagate
1597
1598**/
1599STATIC
1600UINT64
1601Dr7WriteExit (
1602 IN OUT GHCB *Ghcb,
1603 IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,
1604 IN CC_INSTRUCTION_DATA *InstructionData
1605 )
1606{
1607 CC_INSTRUCTION_OPCODE_EXT *Ext;
1608 SEV_ES_PER_CPU_DATA *SevEsData;
1609 UINT64 *Register;
1610 UINT64 Status;
1611
1612 if (MemEncryptSevEsDebugVirtualizationIsEnabled ()) {
1613 return UnsupportedExit (Ghcb, Regs, InstructionData);
1614 }
1615
1616 Ext = &InstructionData->Ext;
1617 SevEsData = (SEV_ES_PER_CPU_DATA *)(Ghcb + 1);
1618
1619 //
1620 // MOV DRn always treats MOD == 3 no matter how encoded
1621 //
1622 Register = CcGetRegisterPointer (Regs, Ext->ModRm.Rm);
1623
1624 //
1625 // Using a value of 0 for ExitInfo1 means RAX holds the value
1626 //
1627 Ghcb->SaveArea.Rax = *Register;
1628 CcExitVmgSetOffsetValid (Ghcb, GhcbRax);
1629
1630 Status = CcExitVmgExit (Ghcb, SVM_EXIT_DR7_WRITE, 0, 0);
1631 if (Status != 0) {
1632 return Status;
1633 }
1634
1635 SevEsData->Dr7 = *Register;
1636 SevEsData->Dr7Cached = 1;
1637
1638 return 0;
1639}
1640
1641/**
1642 Handle a DR7 register read event.
1643
1644 Use the VMGEXIT instruction to handle a DR7 read event.
1645
1646 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1647 Block
1648 @param[in, out] Regs x64 processor context
1649 @param[in] InstructionData Instruction parsing context
1650
1651 @retval 0 Event handled successfully
1652
1653**/
1654STATIC
1655UINT64
1656Dr7ReadExit (
1657 IN OUT GHCB *Ghcb,
1658 IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,
1659 IN CC_INSTRUCTION_DATA *InstructionData
1660 )
1661{
1662 CC_INSTRUCTION_OPCODE_EXT *Ext;
1663 SEV_ES_PER_CPU_DATA *SevEsData;
1664 UINT64 *Register;
1665
1666 if (MemEncryptSevEsDebugVirtualizationIsEnabled ()) {
1667 return UnsupportedExit (Ghcb, Regs, InstructionData);
1668 }
1669
1670 Ext = &InstructionData->Ext;
1671 SevEsData = (SEV_ES_PER_CPU_DATA *)(Ghcb + 1);
1672
1673 //
1674 // MOV DRn always treats MOD == 3 no matter how encoded
1675 //
1676 Register = CcGetRegisterPointer (Regs, Ext->ModRm.Rm);
1677
1678 //
1679 // If there is a cached valued for DR7, return that. Otherwise return the
1680 // DR7 standard reset value of 0x400 (no debug breakpoints set).
1681 //
1682 *Register = (SevEsData->Dr7Cached == 1) ? SevEsData->Dr7 : 0x400;
1683
1684 return 0;
1685}
1686
1687/**
1688 Check that the opcode matches the exit code for a #VC.
1689
1690 Each exit code should only be raised while executing certain instructions.
1691 Verify that rIP points to a correct instruction based on the exit code to
1692 protect against maliciously injected interrupts via the hypervisor. If it does
1693 not, report an unsupported event to the hypervisor.
1694
1695 Decodes the ModRm byte into InstructionData if necessary.
1696
1697 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
1698 Block
1699 @param[in, out] Regs x64 processor context
1700 @param[in, out] InstructionData Instruction parsing context
1701 @param[in] ExitCode Exit code given by #VC.
1702
1703 @retval 0 No problems detected.
1704 @return New exception value to propagate
1705
1706
1707**/
1708STATIC
1709UINT64
1710VcCheckOpcodeBytes (
1711 IN OUT GHCB *Ghcb,
1712 IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,
1713 IN OUT CC_INSTRUCTION_DATA *InstructionData,
1714 IN UINT64 ExitCode
1715 )
1716{
1717 UINT8 OpCode;
1718
1719 //
1720 // Expected opcodes are either 1 or 2 bytes. If they are 2 bytes, they always
1721 // start with TWO_BYTE_OPCODE_ESCAPE (0x0f), so skip over that.
1722 //
1723 OpCode = *(InstructionData->OpCodes);
1724 if (OpCode == TWO_BYTE_OPCODE_ESCAPE) {
1725 OpCode = *(InstructionData->OpCodes + 1);
1726 }
1727
1728 switch (ExitCode) {
1729 case SVM_EXIT_IOIO_PROT:
1730 case SVM_EXIT_NPF:
1731 /* handled separately */
1732 return 0;
1733
1734 case SVM_EXIT_CPUID:
1735 if (OpCode == 0xa2) {
1736 return 0;
1737 }
1738
1739 break;
1740
1741 case SVM_EXIT_INVD:
1742 if (OpCode == 0x08) {
1743 return 0;
1744 }
1745
1746 break;
1747
1748 case SVM_EXIT_MONITOR:
1749 CcDecodeModRm (Regs, InstructionData);
1750
1751 if ((OpCode == 0x01) &&
1752 ( (InstructionData->ModRm.Uint8 == 0xc8) /* MONITOR */
1753 || (InstructionData->ModRm.Uint8 == 0xfa))) /* MONITORX */
1754 {
1755 return 0;
1756 }
1757
1758 break;
1759
1760 case SVM_EXIT_MWAIT:
1761 CcDecodeModRm (Regs, InstructionData);
1762
1763 if ((OpCode == 0x01) &&
1764 ( (InstructionData->ModRm.Uint8 == 0xc9) /* MWAIT */
1765 || (InstructionData->ModRm.Uint8 == 0xfb))) /* MWAITX */
1766 {
1767 return 0;
1768 }
1769
1770 break;
1771
1772 case SVM_EXIT_MSR:
1773 /* RDMSR */
1774 if ((OpCode == 0x32) ||
1775 /* WRMSR */
1776 (OpCode == 0x30))
1777 {
1778 return 0;
1779 }
1780
1781 break;
1782
1783 case SVM_EXIT_RDPMC:
1784 if (OpCode == 0x33) {
1785 return 0;
1786 }
1787
1788 break;
1789
1790 case SVM_EXIT_RDTSC:
1791 if (OpCode == 0x31) {
1792 return 0;
1793 }
1794
1795 break;
1796
1797 case SVM_EXIT_RDTSCP:
1798 CcDecodeModRm (Regs, InstructionData);
1799
1800 if ((OpCode == 0x01) && (InstructionData->ModRm.Uint8 == 0xf9)) {
1801 return 0;
1802 }
1803
1804 break;
1805
1806 case SVM_EXIT_DR7_READ:
1807 CcDecodeModRm (Regs, InstructionData);
1808
1809 if ((OpCode == 0x21) &&
1810 (InstructionData->Ext.ModRm.Reg == 7))
1811 {
1812 return 0;
1813 }
1814
1815 break;
1816
1817 case SVM_EXIT_VMMCALL:
1818 CcDecodeModRm (Regs, InstructionData);
1819
1820 if ((OpCode == 0x01) && (InstructionData->ModRm.Uint8 == 0xd9)) {
1821 return 0;
1822 }
1823
1824 break;
1825
1826 case SVM_EXIT_DR7_WRITE:
1827 CcDecodeModRm (Regs, InstructionData);
1828
1829 if ((OpCode == 0x23) &&
1830 (InstructionData->Ext.ModRm.Reg == 7))
1831 {
1832 return 0;
1833 }
1834
1835 break;
1836
1837 case SVM_EXIT_WBINVD:
1838 if (OpCode == 0x9) {
1839 return 0;
1840 }
1841
1842 break;
1843
1844 default:
1845 break;
1846 }
1847
1848 return UnsupportedExit (Ghcb, Regs, InstructionData);
1849}
1850
1851/**
1852 Handle a #VC exception.
1853
1854 Performs the necessary processing to handle a #VC exception.
1855
1856 @param[in, out] Ghcb Pointer to the GHCB
1857 @param[in, out] ExceptionType Pointer to an EFI_EXCEPTION_TYPE to be set
1858 as value to use on error.
1859 @param[in, out] SystemContext Pointer to EFI_SYSTEM_CONTEXT
1860
1861 @retval EFI_SUCCESS Exception handled
1862 @retval EFI_UNSUPPORTED #VC not supported, (new) exception value to
1863 propagate provided
1864 @retval EFI_PROTOCOL_ERROR #VC handling failed, (new) exception value to
1865 propagate provided
1866
1867**/
1868EFI_STATUS
1869EFIAPI
1870InternalVmgExitHandleVc (
1871 IN OUT GHCB *Ghcb,
1872 IN OUT EFI_EXCEPTION_TYPE *ExceptionType,
1873 IN OUT EFI_SYSTEM_CONTEXT SystemContext
1874 )
1875{
1876 EFI_SYSTEM_CONTEXT_X64 *Regs;
1877 NAE_EXIT NaeExit;
1878 CC_INSTRUCTION_DATA InstructionData;
1879 UINT64 ExitCode, Status;
1880 EFI_STATUS VcRet;
1881 BOOLEAN InterruptState;
1882
1883 VcRet = EFI_SUCCESS;
1884
1885 Regs = SystemContext.SystemContextX64;
1886
1887 CcExitVmgInit (Ghcb, &InterruptState);
1888
1889 ExitCode = Regs->ExceptionData;
1890 switch (ExitCode) {
1891 case SVM_EXIT_DR7_READ:
1892 NaeExit = Dr7ReadExit;
1893 break;
1894
1895 case SVM_EXIT_DR7_WRITE:
1896 NaeExit = Dr7WriteExit;
1897 break;
1898
1899 case SVM_EXIT_RDTSC:
1900 NaeExit = RdtscExit;
1901 break;
1902
1903 case SVM_EXIT_RDPMC:
1904 NaeExit = RdpmcExit;
1905 break;
1906
1907 case SVM_EXIT_CPUID:
1908 NaeExit = CpuidExit;
1909 break;
1910
1911 case SVM_EXIT_INVD:
1912 NaeExit = InvdExit;
1913 break;
1914
1915 case SVM_EXIT_IOIO_PROT:
1916 NaeExit = IoioExit;
1917 break;
1918
1919 case SVM_EXIT_MSR:
1920 NaeExit = MsrExit;
1921 break;
1922
1923 case SVM_EXIT_VMMCALL:
1924 NaeExit = VmmCallExit;
1925 break;
1926
1927 case SVM_EXIT_RDTSCP:
1928 NaeExit = RdtscpExit;
1929 break;
1930
1931 case SVM_EXIT_WBINVD:
1932 NaeExit = WbinvdExit;
1933 break;
1934
1935 case SVM_EXIT_MONITOR:
1936 NaeExit = MonitorExit;
1937 break;
1938
1939 case SVM_EXIT_MWAIT:
1940 NaeExit = MwaitExit;
1941 break;
1942
1943 case SVM_EXIT_NPF:
1944 NaeExit = MmioExit;
1945 break;
1946
1947 default:
1948 NaeExit = UnsupportedExit;
1949 }
1950
1951 CcInitInstructionData (&InstructionData, Ghcb, Regs);
1952
1953 Status = VcCheckOpcodeBytes (Ghcb, Regs, &InstructionData, ExitCode);
1954
1955 //
1956 // If the opcode does not match the exit code, do not process the exception
1957 //
1958 if (Status == 0) {
1959 Status = NaeExit (Ghcb, Regs, &InstructionData);
1960 }
1961
1962 if (Status == 0) {
1963 Regs->Rip += CcInstructionLength (&InstructionData);
1964 } else {
1965 GHCB_EVENT_INJECTION Event;
1966
1967 Event.Uint64 = Status;
1968 if (Event.Elements.ErrorCodeValid != 0) {
1969 Regs->ExceptionData = Event.Elements.ErrorCode;
1970 } else {
1971 Regs->ExceptionData = 0;
1972 }
1973
1974 *ExceptionType = Event.Elements.Vector;
1975
1976 VcRet = EFI_PROTOCOL_ERROR;
1977 }
1978
1979 CcExitVmgDone (Ghcb, InterruptState);
1980
1981 return VcRet;
1982}
1983
1984/**
1985 Routine to allow ASSERT from within #VC.
1986
1987 @param[in, out] SevEsData Pointer to the per-CPU data
1988
1989**/
1990VOID
1991EFIAPI
1992VmgExitIssueAssert (
1993 IN OUT SEV_ES_PER_CPU_DATA *SevEsData
1994 )
1995{
1996 //
1997 // Progress will be halted, so set VcCount to allow for ASSERT output
1998 // to be seen.
1999 //
2000 SevEsData->VcCount = 0;
2001
2002 ASSERT (FALSE);
2003 CpuDeadLoop ();
2004}
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