]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/Library/VmgExitLib/VmgExitVcHandler.c
OvmfPkg/VmgExitLib: Add support for MSR_PROT NAE events
[mirror_edk2.git] / OvmfPkg / Library / VmgExitLib / VmgExitVcHandler.c
1 /** @file
2 X64 #VC Exception Handler functon.
3
4 Copyright (C) 2020, 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/VmgExitLib.h>
13 #include <Register/Amd/Msr.h>
14 #include <Register/Intel/Cpuid.h>
15 #include <IndustryStandard/InstructionParsing.h>
16
17 //
18 // Instruction execution mode definition
19 //
20 typedef enum {
21 LongMode64Bit = 0,
22 LongModeCompat32Bit,
23 LongModeCompat16Bit,
24 } SEV_ES_INSTRUCTION_MODE;
25
26 //
27 // Instruction size definition (for operand and address)
28 //
29 typedef enum {
30 Size8Bits = 0,
31 Size16Bits,
32 Size32Bits,
33 Size64Bits,
34 } SEV_ES_INSTRUCTION_SIZE;
35
36 //
37 // Intruction segment definition
38 //
39 typedef enum {
40 SegmentEs = 0,
41 SegmentCs,
42 SegmentSs,
43 SegmentDs,
44 SegmentFs,
45 SegmentGs,
46 } SEV_ES_INSTRUCTION_SEGMENT;
47
48 //
49 // Instruction rep function definition
50 //
51 typedef enum {
52 RepNone = 0,
53 RepZ,
54 RepNZ,
55 } SEV_ES_INSTRUCTION_REP;
56
57 typedef struct {
58 UINT8 Rm;
59 UINT8 Reg;
60 UINT8 Mod;
61 } SEV_ES_INSTRUCTION_MODRM_EXT;
62
63 typedef struct {
64 UINT8 Base;
65 UINT8 Index;
66 UINT8 Scale;
67 } SEV_ES_INSTRUCTION_SIB_EXT;
68
69 //
70 // Instruction opcode definition
71 //
72 typedef struct {
73 SEV_ES_INSTRUCTION_MODRM_EXT ModRm;
74
75 SEV_ES_INSTRUCTION_SIB_EXT Sib;
76
77 UINTN RegData;
78 UINTN RmData;
79 } SEV_ES_INSTRUCTION_OPCODE_EXT;
80
81 //
82 // Instruction parsing context definition
83 //
84 typedef struct {
85 GHCB *Ghcb;
86
87 SEV_ES_INSTRUCTION_MODE Mode;
88 SEV_ES_INSTRUCTION_SIZE DataSize;
89 SEV_ES_INSTRUCTION_SIZE AddrSize;
90 BOOLEAN SegmentSpecified;
91 SEV_ES_INSTRUCTION_SEGMENT Segment;
92 SEV_ES_INSTRUCTION_REP RepMode;
93
94 UINT8 *Begin;
95 UINT8 *End;
96
97 UINT8 *Prefixes;
98 UINT8 *OpCodes;
99 UINT8 *Displacement;
100 UINT8 *Immediate;
101
102 INSTRUCTION_REX_PREFIX RexPrefix;
103
104 BOOLEAN ModRmPresent;
105 INSTRUCTION_MODRM ModRm;
106
107 BOOLEAN SibPresent;
108 INSTRUCTION_SIB Sib;
109
110 UINTN PrefixSize;
111 UINTN OpCodeSize;
112 UINTN DisplacementSize;
113 UINTN ImmediateSize;
114
115 SEV_ES_INSTRUCTION_OPCODE_EXT Ext;
116 } SEV_ES_INSTRUCTION_DATA;
117
118 //
119 // Non-automatic Exit function prototype
120 //
121 typedef
122 UINT64
123 (*NAE_EXIT) (
124 GHCB *Ghcb,
125 EFI_SYSTEM_CONTEXT_X64 *Regs,
126 SEV_ES_INSTRUCTION_DATA *InstructionData
127 );
128
129
130 /**
131 Checks the GHCB to determine if the specified register has been marked valid.
132
133 The ValidBitmap area represents the areas of the GHCB that have been marked
134 valid. Return an indication of whether the area of the GHCB that holds the
135 specified register has been marked valid.
136
137 @param[in] Ghcb Pointer to the Guest-Hypervisor Communication Block
138 @param[in] Reg Offset in the GHCB of the register to check
139
140 @retval TRUE Register has been marked vald in the GHCB
141 @retval FALSE Register has not been marked valid in the GHCB
142
143 **/
144 STATIC
145 BOOLEAN
146 GhcbIsRegValid (
147 IN GHCB *Ghcb,
148 IN GHCB_REGISTER Reg
149 )
150 {
151 UINT32 RegIndex;
152 UINT32 RegBit;
153
154 RegIndex = Reg / 8;
155 RegBit = Reg & 0x07;
156
157 return ((Ghcb->SaveArea.ValidBitmap[RegIndex] & (1 << RegBit)) != 0);
158 }
159
160 /**
161 Marks a register as valid in the GHCB.
162
163 The ValidBitmap area represents the areas of the GHCB that have been marked
164 valid. Set the area of the GHCB that holds the specified register as valid.
165
166 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication Block
167 @param[in] Reg Offset in the GHCB of the register to mark valid
168
169 **/
170 STATIC
171 VOID
172 GhcbSetRegValid (
173 IN OUT GHCB *Ghcb,
174 IN GHCB_REGISTER Reg
175 )
176 {
177 UINT32 RegIndex;
178 UINT32 RegBit;
179
180 RegIndex = Reg / 8;
181 RegBit = Reg & 0x07;
182
183 Ghcb->SaveArea.ValidBitmap[RegIndex] |= (1 << RegBit);
184 }
185
186 /**
187 Decode instruction prefixes.
188
189 Parse the instruction data to track the instruction prefixes that have
190 been used.
191
192 @param[in] Regs x64 processor context
193 @param[in, out] InstructionData Instruction parsing context
194
195 **/
196 STATIC
197 VOID
198 DecodePrefixes (
199 IN EFI_SYSTEM_CONTEXT_X64 *Regs,
200 IN OUT SEV_ES_INSTRUCTION_DATA *InstructionData
201 )
202 {
203 SEV_ES_INSTRUCTION_MODE Mode;
204 SEV_ES_INSTRUCTION_SIZE ModeDataSize;
205 SEV_ES_INSTRUCTION_SIZE ModeAddrSize;
206 UINT8 *Byte;
207
208 //
209 // Always in 64-bit mode
210 //
211 Mode = LongMode64Bit;
212 ModeDataSize = Size32Bits;
213 ModeAddrSize = Size64Bits;
214
215 InstructionData->Mode = Mode;
216 InstructionData->DataSize = ModeDataSize;
217 InstructionData->AddrSize = ModeAddrSize;
218
219 InstructionData->Prefixes = InstructionData->Begin;
220
221 Byte = InstructionData->Prefixes;
222 for ( ; ; Byte++, InstructionData->PrefixSize++) {
223 //
224 // Check the 0x40 to 0x4F range using an if statement here since some
225 // compilers don't like the "case 0x40 ... 0x4F:" syntax. This avoids
226 // 16 case statements below.
227 //
228 if ((*Byte >= REX_PREFIX_START) && (*Byte <= REX_PREFIX_STOP)) {
229 InstructionData->RexPrefix.Uint8 = *Byte;
230 if ((*Byte & REX_64BIT_OPERAND_SIZE_MASK) != 0) {
231 InstructionData->DataSize = Size64Bits;
232 }
233 continue;
234 }
235
236 switch (*Byte) {
237 case OVERRIDE_SEGMENT_CS:
238 case OVERRIDE_SEGMENT_DS:
239 case OVERRIDE_SEGMENT_ES:
240 case OVERRIDE_SEGMENT_SS:
241 if (Mode != LongMode64Bit) {
242 InstructionData->SegmentSpecified = TRUE;
243 InstructionData->Segment = (*Byte >> 3) & 3;
244 }
245 break;
246
247 case OVERRIDE_SEGMENT_FS:
248 case OVERRIDE_SEGMENT_GS:
249 InstructionData->SegmentSpecified = TRUE;
250 InstructionData->Segment = *Byte & 7;
251 break;
252
253 case OVERRIDE_OPERAND_SIZE:
254 if (InstructionData->RexPrefix.Uint8 == 0) {
255 InstructionData->DataSize =
256 (Mode == LongMode64Bit) ? Size16Bits :
257 (Mode == LongModeCompat32Bit) ? Size16Bits :
258 (Mode == LongModeCompat16Bit) ? Size32Bits : 0;
259 }
260 break;
261
262 case OVERRIDE_ADDRESS_SIZE:
263 InstructionData->AddrSize =
264 (Mode == LongMode64Bit) ? Size32Bits :
265 (Mode == LongModeCompat32Bit) ? Size16Bits :
266 (Mode == LongModeCompat16Bit) ? Size32Bits : 0;
267 break;
268
269 case LOCK_PREFIX:
270 break;
271
272 case REPZ_PREFIX:
273 InstructionData->RepMode = RepZ;
274 break;
275
276 case REPNZ_PREFIX:
277 InstructionData->RepMode = RepNZ;
278 break;
279
280 default:
281 InstructionData->OpCodes = Byte;
282 InstructionData->OpCodeSize = (*Byte == TWO_BYTE_OPCODE_ESCAPE) ? 2 : 1;
283
284 InstructionData->End = Byte + InstructionData->OpCodeSize;
285 InstructionData->Displacement = InstructionData->End;
286 InstructionData->Immediate = InstructionData->End;
287 return;
288 }
289 }
290 }
291
292 /**
293 Determine instruction length
294
295 Return the total length of the parsed instruction.
296
297 @param[in] InstructionData Instruction parsing context
298
299 @return Length of parsed instruction
300
301 **/
302 STATIC
303 UINT64
304 InstructionLength (
305 IN SEV_ES_INSTRUCTION_DATA *InstructionData
306 )
307 {
308 return (UINT64) (InstructionData->End - InstructionData->Begin);
309 }
310
311 /**
312 Initialize the instruction parsing context.
313
314 Initialize the instruction parsing context, which includes decoding the
315 instruction prefixes.
316
317 @param[in, out] InstructionData Instruction parsing context
318 @param[in] Ghcb Pointer to the Guest-Hypervisor Communication
319 Block
320 @param[in] Regs x64 processor context
321
322 **/
323 STATIC
324 VOID
325 InitInstructionData (
326 IN OUT SEV_ES_INSTRUCTION_DATA *InstructionData,
327 IN GHCB *Ghcb,
328 IN EFI_SYSTEM_CONTEXT_X64 *Regs
329 )
330 {
331 SetMem (InstructionData, sizeof (*InstructionData), 0);
332 InstructionData->Ghcb = Ghcb;
333 InstructionData->Begin = (UINT8 *) Regs->Rip;
334 InstructionData->End = (UINT8 *) Regs->Rip;
335
336 DecodePrefixes (Regs, InstructionData);
337 }
338
339 /**
340 Report an unsupported event to the hypervisor
341
342 Use the VMGEXIT support to report an unsupported event to the hypervisor.
343
344 @param[in] Ghcb Pointer to the Guest-Hypervisor Communication
345 Block
346 @param[in] Regs x64 processor context
347 @param[in] InstructionData Instruction parsing context
348
349 @return New exception value to propagate
350
351 **/
352 STATIC
353 UINT64
354 UnsupportedExit (
355 IN GHCB *Ghcb,
356 IN EFI_SYSTEM_CONTEXT_X64 *Regs,
357 IN SEV_ES_INSTRUCTION_DATA *InstructionData
358 )
359 {
360 UINT64 Status;
361
362 Status = VmgExit (Ghcb, SVM_EXIT_UNSUPPORTED, Regs->ExceptionData, 0);
363 if (Status == 0) {
364 GHCB_EVENT_INJECTION Event;
365
366 Event.Uint64 = 0;
367 Event.Elements.Vector = GP_EXCEPTION;
368 Event.Elements.Type = GHCB_EVENT_INJECTION_TYPE_EXCEPTION;
369 Event.Elements.Valid = 1;
370
371 Status = Event.Uint64;
372 }
373
374 return Status;
375 }
376
377 /**
378 Handle an MSR event.
379
380 Use the VMGEXIT instruction to handle either a RDMSR or WRMSR event.
381
382 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
383 Block
384 @param[in, out] Regs x64 processor context
385 @param[in] InstructionData Instruction parsing context
386
387 @retval 0 Event handled successfully
388 @return New exception value to propagate
389
390 **/
391 STATIC
392 UINT64
393 MsrExit (
394 IN OUT GHCB *Ghcb,
395 IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,
396 IN SEV_ES_INSTRUCTION_DATA *InstructionData
397 )
398 {
399 UINT64 ExitInfo1, Status;
400
401 ExitInfo1 = 0;
402
403 switch (*(InstructionData->OpCodes + 1)) {
404 case 0x30: // WRMSR
405 ExitInfo1 = 1;
406 Ghcb->SaveArea.Rax = Regs->Rax;
407 GhcbSetRegValid (Ghcb, GhcbRax);
408 Ghcb->SaveArea.Rdx = Regs->Rdx;
409 GhcbSetRegValid (Ghcb, GhcbRdx);
410 //
411 // fall through
412 //
413 case 0x32: // RDMSR
414 Ghcb->SaveArea.Rcx = Regs->Rcx;
415 GhcbSetRegValid (Ghcb, GhcbRcx);
416 break;
417 default:
418 return UnsupportedExit (Ghcb, Regs, InstructionData);
419 }
420
421 Status = VmgExit (Ghcb, SVM_EXIT_MSR, ExitInfo1, 0);
422 if (Status != 0) {
423 return Status;
424 }
425
426 if (ExitInfo1 == 0) {
427 if (!GhcbIsRegValid (Ghcb, GhcbRax) ||
428 !GhcbIsRegValid (Ghcb, GhcbRdx)) {
429 return UnsupportedExit (Ghcb, Regs, InstructionData);
430 }
431 Regs->Rax = Ghcb->SaveArea.Rax;
432 Regs->Rdx = Ghcb->SaveArea.Rdx;
433 }
434
435 return 0;
436 }
437
438 /**
439 Build the IOIO event information.
440
441 The IOIO event information identifies the type of IO operation to be performed
442 by the hypervisor. Build this information based on the instruction data.
443
444 @param[in] Regs x64 processor context
445 @param[in, out] InstructionData Instruction parsing context
446
447 @return IOIO event information value
448
449 **/
450 STATIC
451 UINT64
452 IoioExitInfo (
453 IN EFI_SYSTEM_CONTEXT_X64 *Regs,
454 IN OUT SEV_ES_INSTRUCTION_DATA *InstructionData
455 )
456 {
457 UINT64 ExitInfo;
458
459 ExitInfo = 0;
460
461 switch (*(InstructionData->OpCodes)) {
462 //
463 // INS opcodes
464 //
465 case 0x6C:
466 case 0x6D:
467 ExitInfo |= IOIO_TYPE_INS;
468 ExitInfo |= IOIO_SEG_ES;
469 ExitInfo |= ((Regs->Rdx & 0xffff) << 16);
470 break;
471
472 //
473 // OUTS opcodes
474 //
475 case 0x6E:
476 case 0x6F:
477 ExitInfo |= IOIO_TYPE_OUTS;
478 ExitInfo |= IOIO_SEG_DS;
479 ExitInfo |= ((Regs->Rdx & 0xffff) << 16);
480 break;
481
482 //
483 // IN immediate opcodes
484 //
485 case 0xE4:
486 case 0xE5:
487 InstructionData->ImmediateSize = 1;
488 InstructionData->End++;
489 ExitInfo |= IOIO_TYPE_IN;
490 ExitInfo |= ((*(InstructionData->OpCodes + 1)) << 16);
491 break;
492
493 //
494 // OUT immediate opcodes
495 //
496 case 0xE6:
497 case 0xE7:
498 InstructionData->ImmediateSize = 1;
499 InstructionData->End++;
500 ExitInfo |= IOIO_TYPE_OUT;
501 ExitInfo |= ((*(InstructionData->OpCodes + 1)) << 16) | IOIO_TYPE_OUT;
502 break;
503
504 //
505 // IN register opcodes
506 //
507 case 0xEC:
508 case 0xED:
509 ExitInfo |= IOIO_TYPE_IN;
510 ExitInfo |= ((Regs->Rdx & 0xffff) << 16);
511 break;
512
513 //
514 // OUT register opcodes
515 //
516 case 0xEE:
517 case 0xEF:
518 ExitInfo |= IOIO_TYPE_OUT;
519 ExitInfo |= ((Regs->Rdx & 0xffff) << 16);
520 break;
521
522 default:
523 return 0;
524 }
525
526 switch (*(InstructionData->OpCodes)) {
527 //
528 // Single-byte opcodes
529 //
530 case 0x6C:
531 case 0x6E:
532 case 0xE4:
533 case 0xE6:
534 case 0xEC:
535 case 0xEE:
536 ExitInfo |= IOIO_DATA_8;
537 break;
538
539 //
540 // Length determined by instruction parsing
541 //
542 default:
543 ExitInfo |= (InstructionData->DataSize == Size16Bits) ? IOIO_DATA_16
544 : IOIO_DATA_32;
545 }
546
547 switch (InstructionData->AddrSize) {
548 case Size16Bits:
549 ExitInfo |= IOIO_ADDR_16;
550 break;
551
552 case Size32Bits:
553 ExitInfo |= IOIO_ADDR_32;
554 break;
555
556 case Size64Bits:
557 ExitInfo |= IOIO_ADDR_64;
558 break;
559
560 default:
561 break;
562 }
563
564 if (InstructionData->RepMode != 0) {
565 ExitInfo |= IOIO_REP;
566 }
567
568 return ExitInfo;
569 }
570
571 /**
572 Handle an IOIO event.
573
574 Use the VMGEXIT instruction to handle an IOIO event.
575
576 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
577 Block
578 @param[in, out] Regs x64 processor context
579 @param[in] InstructionData Instruction parsing context
580
581 @retval 0 Event handled successfully
582 @return New exception value to propagate
583
584 **/
585 STATIC
586 UINT64
587 IoioExit (
588 IN OUT GHCB *Ghcb,
589 IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,
590 IN SEV_ES_INSTRUCTION_DATA *InstructionData
591 )
592 {
593 UINT64 ExitInfo1, ExitInfo2, Status;
594 BOOLEAN IsString;
595
596 ExitInfo1 = IoioExitInfo (Regs, InstructionData);
597 if (ExitInfo1 == 0) {
598 return UnsupportedExit (Ghcb, Regs, InstructionData);
599 }
600
601 IsString = ((ExitInfo1 & IOIO_TYPE_STR) != 0) ? TRUE : FALSE;
602 if (IsString) {
603 UINTN IoBytes, VmgExitBytes;
604 UINTN GhcbCount, OpCount;
605
606 Status = 0;
607
608 IoBytes = IOIO_DATA_BYTES (ExitInfo1);
609 GhcbCount = sizeof (Ghcb->SharedBuffer) / IoBytes;
610
611 OpCount = ((ExitInfo1 & IOIO_REP) != 0) ? Regs->Rcx : 1;
612 while (OpCount != 0) {
613 ExitInfo2 = MIN (OpCount, GhcbCount);
614 VmgExitBytes = ExitInfo2 * IoBytes;
615
616 if ((ExitInfo1 & IOIO_TYPE_IN) == 0) {
617 CopyMem (Ghcb->SharedBuffer, (VOID *) Regs->Rsi, VmgExitBytes);
618 Regs->Rsi += VmgExitBytes;
619 }
620
621 Ghcb->SaveArea.SwScratch = (UINT64) Ghcb->SharedBuffer;
622 Status = VmgExit (Ghcb, SVM_EXIT_IOIO_PROT, ExitInfo1, ExitInfo2);
623 if (Status != 0) {
624 return Status;
625 }
626
627 if ((ExitInfo1 & IOIO_TYPE_IN) != 0) {
628 CopyMem ((VOID *) Regs->Rdi, Ghcb->SharedBuffer, VmgExitBytes);
629 Regs->Rdi += VmgExitBytes;
630 }
631
632 if ((ExitInfo1 & IOIO_REP) != 0) {
633 Regs->Rcx -= ExitInfo2;
634 }
635
636 OpCount -= ExitInfo2;
637 }
638 } else {
639 if ((ExitInfo1 & IOIO_TYPE_IN) != 0) {
640 Ghcb->SaveArea.Rax = 0;
641 } else {
642 CopyMem (&Ghcb->SaveArea.Rax, &Regs->Rax, IOIO_DATA_BYTES (ExitInfo1));
643 }
644 GhcbSetRegValid (Ghcb, GhcbRax);
645
646 Status = VmgExit (Ghcb, SVM_EXIT_IOIO_PROT, ExitInfo1, 0);
647 if (Status != 0) {
648 return Status;
649 }
650
651 if ((ExitInfo1 & IOIO_TYPE_IN) != 0) {
652 if (!GhcbIsRegValid (Ghcb, GhcbRax)) {
653 return UnsupportedExit (Ghcb, Regs, InstructionData);
654 }
655 CopyMem (&Regs->Rax, &Ghcb->SaveArea.Rax, IOIO_DATA_BYTES (ExitInfo1));
656 }
657 }
658
659 return 0;
660 }
661
662 /**
663 Handle a CPUID event.
664
665 Use the VMGEXIT instruction to handle a CPUID event.
666
667 @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
668 Block
669 @param[in, out] Regs x64 processor context
670 @param[in] InstructionData Instruction parsing context
671
672 @retval 0 Event handled successfully
673 @return New exception value to propagate
674
675 **/
676 STATIC
677 UINT64
678 CpuidExit (
679 IN OUT GHCB *Ghcb,
680 IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,
681 IN SEV_ES_INSTRUCTION_DATA *InstructionData
682 )
683 {
684 UINT64 Status;
685
686 Ghcb->SaveArea.Rax = Regs->Rax;
687 GhcbSetRegValid (Ghcb, GhcbRax);
688 Ghcb->SaveArea.Rcx = Regs->Rcx;
689 GhcbSetRegValid (Ghcb, GhcbRcx);
690 if (Regs->Rax == CPUID_EXTENDED_STATE) {
691 IA32_CR4 Cr4;
692
693 Cr4.UintN = AsmReadCr4 ();
694 Ghcb->SaveArea.XCr0 = (Cr4.Bits.OSXSAVE == 1) ? AsmXGetBv (0) : 1;
695 GhcbSetRegValid (Ghcb, GhcbXCr0);
696 }
697
698 Status = VmgExit (Ghcb, SVM_EXIT_CPUID, 0, 0);
699 if (Status != 0) {
700 return Status;
701 }
702
703 if (!GhcbIsRegValid (Ghcb, GhcbRax) ||
704 !GhcbIsRegValid (Ghcb, GhcbRbx) ||
705 !GhcbIsRegValid (Ghcb, GhcbRcx) ||
706 !GhcbIsRegValid (Ghcb, GhcbRdx)) {
707 return UnsupportedExit (Ghcb, Regs, InstructionData);
708 }
709 Regs->Rax = Ghcb->SaveArea.Rax;
710 Regs->Rbx = Ghcb->SaveArea.Rbx;
711 Regs->Rcx = Ghcb->SaveArea.Rcx;
712 Regs->Rdx = Ghcb->SaveArea.Rdx;
713
714 return 0;
715 }
716
717 /**
718 Handle a #VC exception.
719
720 Performs the necessary processing to handle a #VC exception.
721
722 @param[in, out] ExceptionType Pointer to an EFI_EXCEPTION_TYPE to be set
723 as value to use on error.
724 @param[in, out] SystemContext Pointer to EFI_SYSTEM_CONTEXT
725
726 @retval EFI_SUCCESS Exception handled
727 @retval EFI_UNSUPPORTED #VC not supported, (new) exception value to
728 propagate provided
729 @retval EFI_PROTOCOL_ERROR #VC handling failed, (new) exception value to
730 propagate provided
731
732 **/
733 EFI_STATUS
734 EFIAPI
735 VmgExitHandleVc (
736 IN OUT EFI_EXCEPTION_TYPE *ExceptionType,
737 IN OUT EFI_SYSTEM_CONTEXT SystemContext
738 )
739 {
740 MSR_SEV_ES_GHCB_REGISTER Msr;
741 EFI_SYSTEM_CONTEXT_X64 *Regs;
742 GHCB *Ghcb;
743 NAE_EXIT NaeExit;
744 SEV_ES_INSTRUCTION_DATA InstructionData;
745 UINT64 ExitCode, Status;
746 EFI_STATUS VcRet;
747
748 VcRet = EFI_SUCCESS;
749
750 Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);
751 ASSERT (Msr.GhcbInfo.Function == 0);
752 ASSERT (Msr.Ghcb != 0);
753
754 Regs = SystemContext.SystemContextX64;
755 Ghcb = Msr.Ghcb;
756
757 VmgInit (Ghcb);
758
759 ExitCode = Regs->ExceptionData;
760 switch (ExitCode) {
761 case SVM_EXIT_CPUID:
762 NaeExit = CpuidExit;
763 break;
764
765 case SVM_EXIT_IOIO_PROT:
766 NaeExit = IoioExit;
767 break;
768
769 case SVM_EXIT_MSR:
770 NaeExit = MsrExit;
771 break;
772
773 default:
774 NaeExit = UnsupportedExit;
775 }
776
777 InitInstructionData (&InstructionData, Ghcb, Regs);
778
779 Status = NaeExit (Ghcb, Regs, &InstructionData);
780 if (Status == 0) {
781 Regs->Rip += InstructionLength (&InstructionData);
782 } else {
783 GHCB_EVENT_INJECTION Event;
784
785 Event.Uint64 = Status;
786 if (Event.Elements.ErrorCodeValid != 0) {
787 Regs->ExceptionData = Event.Elements.ErrorCode;
788 } else {
789 Regs->ExceptionData = 0;
790 }
791
792 *ExceptionType = Event.Elements.Vector;
793
794 VcRet = EFI_PROTOCOL_ERROR;
795 }
796
797 VmgDone (Ghcb);
798
799 return VcRet;
800 }