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 // Per-CPU data mapping structure
131 //
132 typedef struct {
133   BOOLEAN  Dr7Cached;
134   UINT64   Dr7;
135 } SEV_ES_PER_CPU_DATA;
136 
137 
138 /**
139   Checks the GHCB to determine if the specified register has been marked valid.
140 
141   The ValidBitmap area represents the areas of the GHCB that have been marked
142   valid. Return an indication of whether the area of the GHCB that holds the
143   specified register has been marked valid.
144 
145   @param[in] Ghcb    Pointer to the Guest-Hypervisor Communication Block
146   @param[in] Reg     Offset in the GHCB of the register to check
147 
148   @retval TRUE       Register has been marked vald in the GHCB
149   @retval FALSE      Register has not been marked valid in the GHCB
150 
151 **/
152 STATIC
153 BOOLEAN
GhcbIsRegValid(IN GHCB * Ghcb,IN GHCB_REGISTER Reg)154 GhcbIsRegValid (
155   IN GHCB                *Ghcb,
156   IN GHCB_REGISTER       Reg
157   )
158 {
159   UINT32  RegIndex;
160   UINT32  RegBit;
161 
162   RegIndex = Reg / 8;
163   RegBit   = Reg & 0x07;
164 
165   return ((Ghcb->SaveArea.ValidBitmap[RegIndex] & (1 << RegBit)) != 0);
166 }
167 
168 /**
169   Marks a register as valid in the GHCB.
170 
171   The ValidBitmap area represents the areas of the GHCB that have been marked
172   valid. Set the area of the GHCB that holds the specified register as valid.
173 
174   @param[in, out] Ghcb    Pointer to the Guest-Hypervisor Communication Block
175   @param[in] Reg          Offset in the GHCB of the register to mark valid
176 
177 **/
178 STATIC
179 VOID
GhcbSetRegValid(IN OUT GHCB * Ghcb,IN GHCB_REGISTER Reg)180 GhcbSetRegValid (
181   IN OUT GHCB                *Ghcb,
182   IN     GHCB_REGISTER       Reg
183   )
184 {
185   UINT32  RegIndex;
186   UINT32  RegBit;
187 
188   RegIndex = Reg / 8;
189   RegBit   = Reg & 0x07;
190 
191   Ghcb->SaveArea.ValidBitmap[RegIndex] |= (1 << RegBit);
192 }
193 
194 /**
195   Return a pointer to the contents of the specified register.
196 
197   Based upon the input register, return a pointer to the registers contents
198   in the x86 processor context.
199 
200   @param[in] Regs      x64 processor context
201   @param[in] Register  Register to obtain pointer for
202 
203   @return              Pointer to the contents of the requested register
204 
205 **/
206 STATIC
207 UINT64 *
GetRegisterPointer(IN EFI_SYSTEM_CONTEXT_X64 * Regs,IN UINT8 Register)208 GetRegisterPointer (
209   IN EFI_SYSTEM_CONTEXT_X64   *Regs,
210   IN UINT8                    Register
211   )
212 {
213   UINT64 *Reg;
214 
215   switch (Register) {
216   case 0:
217     Reg = &Regs->Rax;
218     break;
219   case 1:
220     Reg = &Regs->Rcx;
221     break;
222   case 2:
223     Reg = &Regs->Rdx;
224     break;
225   case 3:
226     Reg = &Regs->Rbx;
227     break;
228   case 4:
229     Reg = &Regs->Rsp;
230     break;
231   case 5:
232     Reg = &Regs->Rbp;
233     break;
234   case 6:
235     Reg = &Regs->Rsi;
236     break;
237   case 7:
238     Reg = &Regs->Rdi;
239     break;
240   case 8:
241     Reg = &Regs->R8;
242     break;
243   case 9:
244     Reg = &Regs->R9;
245     break;
246   case 10:
247     Reg = &Regs->R10;
248     break;
249   case 11:
250     Reg = &Regs->R11;
251     break;
252   case 12:
253     Reg = &Regs->R12;
254     break;
255   case 13:
256     Reg = &Regs->R13;
257     break;
258   case 14:
259     Reg = &Regs->R14;
260     break;
261   case 15:
262     Reg = &Regs->R15;
263     break;
264   default:
265     Reg = NULL;
266   }
267   ASSERT (Reg != NULL);
268 
269   return Reg;
270 }
271 
272 /**
273   Update the instruction parsing context for displacement bytes.
274 
275   @param[in, out] InstructionData  Instruction parsing context
276   @param[in]      Size             The instruction displacement size
277 
278 **/
279 STATIC
280 VOID
UpdateForDisplacement(IN OUT SEV_ES_INSTRUCTION_DATA * InstructionData,IN UINTN Size)281 UpdateForDisplacement (
282   IN OUT SEV_ES_INSTRUCTION_DATA  *InstructionData,
283   IN     UINTN                    Size
284   )
285 {
286   InstructionData->DisplacementSize = Size;
287   InstructionData->Immediate += Size;
288   InstructionData->End += Size;
289 }
290 
291 /**
292   Determine if an instruction address if RIP relative.
293 
294   Examine the instruction parsing context to determine if the address offset
295   is relative to the instruction pointer.
296 
297   @param[in] InstructionData  Instruction parsing context
298 
299   @retval TRUE                Instruction addressing is RIP relative
300   @retval FALSE               Instruction addressing is not RIP relative
301 
302 **/
303 STATIC
304 BOOLEAN
IsRipRelative(IN SEV_ES_INSTRUCTION_DATA * InstructionData)305 IsRipRelative (
306   IN SEV_ES_INSTRUCTION_DATA  *InstructionData
307   )
308 {
309   SEV_ES_INSTRUCTION_OPCODE_EXT  *Ext;
310 
311   Ext = &InstructionData->Ext;
312 
313   return ((InstructionData->Mode == LongMode64Bit) &&
314           (Ext->ModRm.Mod == 0) &&
315           (Ext->ModRm.Rm == 5)  &&
316           (InstructionData->SibPresent == FALSE));
317 }
318 
319 /**
320   Return the effective address of a memory operand.
321 
322   Examine the instruction parsing context to obtain the effective memory
323   address of a memory operand.
324 
325   @param[in] Regs             x64 processor context
326   @param[in] InstructionData  Instruction parsing context
327 
328   @return                     The memory operand effective address
329 
330 **/
331 STATIC
332 UINT64
GetEffectiveMemoryAddress(IN EFI_SYSTEM_CONTEXT_X64 * Regs,IN SEV_ES_INSTRUCTION_DATA * InstructionData)333 GetEffectiveMemoryAddress (
334   IN EFI_SYSTEM_CONTEXT_X64   *Regs,
335   IN SEV_ES_INSTRUCTION_DATA  *InstructionData
336   )
337 {
338   SEV_ES_INSTRUCTION_OPCODE_EXT  *Ext;
339   UINT64                         EffectiveAddress;
340 
341   Ext = &InstructionData->Ext;
342   EffectiveAddress = 0;
343 
344   if (IsRipRelative (InstructionData)) {
345     //
346     // RIP-relative displacement is a 32-bit signed value
347     //
348     INT32 RipRelative;
349 
350     RipRelative = *(INT32 *) InstructionData->Displacement;
351 
352     UpdateForDisplacement (InstructionData, 4);
353 
354     //
355     // Negative displacement is handled by standard UINT64 wrap-around.
356     //
357     return Regs->Rip + (UINT64) RipRelative;
358   }
359 
360   switch (Ext->ModRm.Mod) {
361   case 1:
362     UpdateForDisplacement (InstructionData, 1);
363     EffectiveAddress += (UINT64) (*(INT8 *) (InstructionData->Displacement));
364     break;
365   case 2:
366     switch (InstructionData->AddrSize) {
367     case Size16Bits:
368       UpdateForDisplacement (InstructionData, 2);
369       EffectiveAddress += (UINT64) (*(INT16 *) (InstructionData->Displacement));
370       break;
371     default:
372       UpdateForDisplacement (InstructionData, 4);
373       EffectiveAddress += (UINT64) (*(INT32 *) (InstructionData->Displacement));
374       break;
375     }
376     break;
377   }
378 
379   if (InstructionData->SibPresent) {
380     INT64  Displacement;
381 
382     if (Ext->Sib.Index != 4) {
383       CopyMem (
384         &Displacement,
385         GetRegisterPointer (Regs, Ext->Sib.Index),
386         sizeof (Displacement)
387         );
388       Displacement *= (INT64)(1 << Ext->Sib.Scale);
389 
390       //
391       // Negative displacement is handled by standard UINT64 wrap-around.
392       //
393       EffectiveAddress += (UINT64) Displacement;
394     }
395 
396     if ((Ext->Sib.Base != 5) || Ext->ModRm.Mod) {
397       EffectiveAddress += *GetRegisterPointer (Regs, Ext->Sib.Base);
398     } else {
399       UpdateForDisplacement (InstructionData, 4);
400       EffectiveAddress += (UINT64) (*(INT32 *) (InstructionData->Displacement));
401     }
402   } else {
403     EffectiveAddress += *GetRegisterPointer (Regs, Ext->ModRm.Rm);
404   }
405 
406   return EffectiveAddress;
407 }
408 
409 /**
410   Decode a ModRM byte.
411 
412   Examine the instruction parsing context to decode a ModRM byte and the SIB
413   byte, if present.
414 
415   @param[in]      Regs             x64 processor context
416   @param[in, out] InstructionData  Instruction parsing context
417 
418 **/
419 STATIC
420 VOID
DecodeModRm(IN EFI_SYSTEM_CONTEXT_X64 * Regs,IN OUT SEV_ES_INSTRUCTION_DATA * InstructionData)421 DecodeModRm (
422   IN     EFI_SYSTEM_CONTEXT_X64   *Regs,
423   IN OUT SEV_ES_INSTRUCTION_DATA  *InstructionData
424   )
425 {
426   SEV_ES_INSTRUCTION_OPCODE_EXT  *Ext;
427   INSTRUCTION_REX_PREFIX         *RexPrefix;
428   INSTRUCTION_MODRM              *ModRm;
429   INSTRUCTION_SIB                *Sib;
430 
431   RexPrefix = &InstructionData->RexPrefix;
432   Ext = &InstructionData->Ext;
433   ModRm = &InstructionData->ModRm;
434   Sib = &InstructionData->Sib;
435 
436   InstructionData->ModRmPresent = TRUE;
437   ModRm->Uint8 = *(InstructionData->End);
438 
439   InstructionData->Displacement++;
440   InstructionData->Immediate++;
441   InstructionData->End++;
442 
443   Ext->ModRm.Mod = ModRm->Bits.Mod;
444   Ext->ModRm.Reg = (RexPrefix->Bits.BitR << 3) | ModRm->Bits.Reg;
445   Ext->ModRm.Rm  = (RexPrefix->Bits.BitB << 3) | ModRm->Bits.Rm;
446 
447   Ext->RegData = *GetRegisterPointer (Regs, Ext->ModRm.Reg);
448 
449   if (Ext->ModRm.Mod == 3) {
450     Ext->RmData = *GetRegisterPointer (Regs, Ext->ModRm.Rm);
451   } else {
452     if (ModRm->Bits.Rm == 4) {
453       InstructionData->SibPresent = TRUE;
454       Sib->Uint8 = *(InstructionData->End);
455 
456       InstructionData->Displacement++;
457       InstructionData->Immediate++;
458       InstructionData->End++;
459 
460       Ext->Sib.Scale = Sib->Bits.Scale;
461       Ext->Sib.Index = (RexPrefix->Bits.BitX << 3) | Sib->Bits.Index;
462       Ext->Sib.Base  = (RexPrefix->Bits.BitB << 3) | Sib->Bits.Base;
463     }
464 
465     Ext->RmData = GetEffectiveMemoryAddress (Regs, InstructionData);
466   }
467 }
468 
469 /**
470   Decode instruction prefixes.
471 
472   Parse the instruction data to track the instruction prefixes that have
473   been used.
474 
475   @param[in]      Regs             x64 processor context
476   @param[in, out] InstructionData  Instruction parsing context
477 
478 **/
479 STATIC
480 VOID
DecodePrefixes(IN EFI_SYSTEM_CONTEXT_X64 * Regs,IN OUT SEV_ES_INSTRUCTION_DATA * InstructionData)481 DecodePrefixes (
482   IN     EFI_SYSTEM_CONTEXT_X64   *Regs,
483   IN OUT SEV_ES_INSTRUCTION_DATA  *InstructionData
484   )
485 {
486   SEV_ES_INSTRUCTION_MODE  Mode;
487   SEV_ES_INSTRUCTION_SIZE  ModeDataSize;
488   SEV_ES_INSTRUCTION_SIZE  ModeAddrSize;
489   UINT8                    *Byte;
490 
491   //
492   // Always in 64-bit mode
493   //
494   Mode = LongMode64Bit;
495   ModeDataSize = Size32Bits;
496   ModeAddrSize = Size64Bits;
497 
498   InstructionData->Mode = Mode;
499   InstructionData->DataSize = ModeDataSize;
500   InstructionData->AddrSize = ModeAddrSize;
501 
502   InstructionData->Prefixes = InstructionData->Begin;
503 
504   Byte = InstructionData->Prefixes;
505   for ( ; ; Byte++, InstructionData->PrefixSize++) {
506     //
507     // Check the 0x40 to 0x4F range using an if statement here since some
508     // compilers don't like the "case 0x40 ... 0x4F:" syntax. This avoids
509     // 16 case statements below.
510     //
511     if ((*Byte >= REX_PREFIX_START) && (*Byte <= REX_PREFIX_STOP)) {
512       InstructionData->RexPrefix.Uint8 = *Byte;
513       if ((*Byte & REX_64BIT_OPERAND_SIZE_MASK) != 0) {
514         InstructionData->DataSize = Size64Bits;
515       }
516       continue;
517     }
518 
519     switch (*Byte) {
520     case OVERRIDE_SEGMENT_CS:
521     case OVERRIDE_SEGMENT_DS:
522     case OVERRIDE_SEGMENT_ES:
523     case OVERRIDE_SEGMENT_SS:
524       if (Mode != LongMode64Bit) {
525         InstructionData->SegmentSpecified = TRUE;
526         InstructionData->Segment = (*Byte >> 3) & 3;
527       }
528       break;
529 
530     case OVERRIDE_SEGMENT_FS:
531     case OVERRIDE_SEGMENT_GS:
532       InstructionData->SegmentSpecified = TRUE;
533       InstructionData->Segment = *Byte & 7;
534       break;
535 
536     case OVERRIDE_OPERAND_SIZE:
537       if (InstructionData->RexPrefix.Uint8 == 0) {
538         InstructionData->DataSize =
539           (Mode == LongMode64Bit)       ? Size16Bits :
540           (Mode == LongModeCompat32Bit) ? Size16Bits :
541           (Mode == LongModeCompat16Bit) ? Size32Bits : 0;
542       }
543       break;
544 
545     case OVERRIDE_ADDRESS_SIZE:
546       InstructionData->AddrSize =
547         (Mode == LongMode64Bit)       ? Size32Bits :
548         (Mode == LongModeCompat32Bit) ? Size16Bits :
549         (Mode == LongModeCompat16Bit) ? Size32Bits : 0;
550       break;
551 
552     case LOCK_PREFIX:
553       break;
554 
555     case REPZ_PREFIX:
556       InstructionData->RepMode = RepZ;
557       break;
558 
559     case REPNZ_PREFIX:
560       InstructionData->RepMode = RepNZ;
561       break;
562 
563     default:
564       InstructionData->OpCodes = Byte;
565       InstructionData->OpCodeSize = (*Byte == TWO_BYTE_OPCODE_ESCAPE) ? 2 : 1;
566 
567       InstructionData->End = Byte + InstructionData->OpCodeSize;
568       InstructionData->Displacement = InstructionData->End;
569       InstructionData->Immediate = InstructionData->End;
570       return;
571     }
572   }
573 }
574 
575 /**
576   Determine instruction length
577 
578   Return the total length of the parsed instruction.
579 
580   @param[in] InstructionData  Instruction parsing context
581 
582   @return                     Length of parsed instruction
583 
584 **/
585 STATIC
586 UINT64
InstructionLength(IN SEV_ES_INSTRUCTION_DATA * InstructionData)587 InstructionLength (
588   IN SEV_ES_INSTRUCTION_DATA  *InstructionData
589   )
590 {
591   return (UINT64) (InstructionData->End - InstructionData->Begin);
592 }
593 
594 /**
595   Initialize the instruction parsing context.
596 
597   Initialize the instruction parsing context, which includes decoding the
598   instruction prefixes.
599 
600   @param[in, out] InstructionData  Instruction parsing context
601   @param[in]      Ghcb             Pointer to the Guest-Hypervisor Communication
602                                    Block
603   @param[in]      Regs             x64 processor context
604 
605 **/
606 STATIC
607 VOID
InitInstructionData(IN OUT SEV_ES_INSTRUCTION_DATA * InstructionData,IN GHCB * Ghcb,IN EFI_SYSTEM_CONTEXT_X64 * Regs)608 InitInstructionData (
609   IN OUT SEV_ES_INSTRUCTION_DATA  *InstructionData,
610   IN     GHCB                     *Ghcb,
611   IN     EFI_SYSTEM_CONTEXT_X64   *Regs
612   )
613 {
614   SetMem (InstructionData, sizeof (*InstructionData), 0);
615   InstructionData->Ghcb = Ghcb;
616   InstructionData->Begin = (UINT8 *) Regs->Rip;
617   InstructionData->End = (UINT8 *) Regs->Rip;
618 
619   DecodePrefixes (Regs, InstructionData);
620 }
621 
622 /**
623   Report an unsupported event to the hypervisor
624 
625   Use the VMGEXIT support to report an unsupported event to the hypervisor.
626 
627   @param[in] Ghcb             Pointer to the Guest-Hypervisor Communication
628                               Block
629   @param[in] Regs             x64 processor context
630   @param[in] InstructionData  Instruction parsing context
631 
632   @return                     New exception value to propagate
633 
634 **/
635 STATIC
636 UINT64
UnsupportedExit(IN GHCB * Ghcb,IN EFI_SYSTEM_CONTEXT_X64 * Regs,IN SEV_ES_INSTRUCTION_DATA * InstructionData)637 UnsupportedExit (
638   IN GHCB                     *Ghcb,
639   IN EFI_SYSTEM_CONTEXT_X64   *Regs,
640   IN SEV_ES_INSTRUCTION_DATA  *InstructionData
641   )
642 {
643   UINT64  Status;
644 
645   Status = VmgExit (Ghcb, SVM_EXIT_UNSUPPORTED, Regs->ExceptionData, 0);
646   if (Status == 0) {
647     GHCB_EVENT_INJECTION  Event;
648 
649     Event.Uint64 = 0;
650     Event.Elements.Vector = GP_EXCEPTION;
651     Event.Elements.Type   = GHCB_EVENT_INJECTION_TYPE_EXCEPTION;
652     Event.Elements.Valid  = 1;
653 
654     Status = Event.Uint64;
655   }
656 
657   return Status;
658 }
659 
660 /**
661   Handle an MMIO event.
662 
663   Use the VMGEXIT instruction to handle either an MMIO read or an MMIO write.
664 
665   @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication
666                                    Block
667   @param[in, out] Regs             x64 processor context
668   @param[in, out] InstructionData  Instruction parsing context
669 
670   @retval 0                        Event handled successfully
671   @return                          New exception value to propagate
672 
673 **/
674 STATIC
675 UINT64
MmioExit(IN OUT GHCB * Ghcb,IN OUT EFI_SYSTEM_CONTEXT_X64 * Regs,IN OUT SEV_ES_INSTRUCTION_DATA * InstructionData)676 MmioExit (
677   IN OUT GHCB                     *Ghcb,
678   IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,
679   IN OUT SEV_ES_INSTRUCTION_DATA  *InstructionData
680   )
681 {
682   UINT64  ExitInfo1, ExitInfo2, Status;
683   UINTN   Bytes;
684   UINT64  *Register;
685   UINT8   OpCode, SignByte;
686 
687   Bytes = 0;
688 
689   OpCode = *(InstructionData->OpCodes);
690   if (OpCode == TWO_BYTE_OPCODE_ESCAPE) {
691     OpCode = *(InstructionData->OpCodes + 1);
692   }
693 
694   switch (OpCode) {
695   //
696   // MMIO write (MOV reg/memX, regX)
697   //
698   case 0x88:
699     Bytes = 1;
700     //
701     // fall through
702     //
703   case 0x89:
704     DecodeModRm (Regs, InstructionData);
705     Bytes = ((Bytes != 0) ? Bytes :
706              (InstructionData->DataSize == Size16Bits) ? 2 :
707              (InstructionData->DataSize == Size32Bits) ? 4 :
708              (InstructionData->DataSize == Size64Bits) ? 8 :
709              0);
710 
711     if (InstructionData->Ext.ModRm.Mod == 3) {
712       //
713       // NPF on two register operands???
714       //
715       return UnsupportedExit (Ghcb, Regs, InstructionData);
716     }
717 
718     ExitInfo1 = InstructionData->Ext.RmData;
719     ExitInfo2 = Bytes;
720     CopyMem (Ghcb->SharedBuffer, &InstructionData->Ext.RegData, Bytes);
721 
722     Ghcb->SaveArea.SwScratch = (UINT64) Ghcb->SharedBuffer;
723     Status = VmgExit (Ghcb, SVM_EXIT_MMIO_WRITE, ExitInfo1, ExitInfo2);
724     if (Status != 0) {
725       return Status;
726     }
727     break;
728 
729   //
730   // MMIO write (MOV reg/memX, immX)
731   //
732   case 0xC6:
733     Bytes = 1;
734     //
735     // fall through
736     //
737   case 0xC7:
738     DecodeModRm (Regs, InstructionData);
739     Bytes = ((Bytes != 0) ? Bytes :
740              (InstructionData->DataSize == Size16Bits) ? 2 :
741              (InstructionData->DataSize == Size32Bits) ? 4 :
742              0);
743 
744     InstructionData->ImmediateSize = Bytes;
745     InstructionData->End += Bytes;
746 
747     ExitInfo1 = InstructionData->Ext.RmData;
748     ExitInfo2 = Bytes;
749     CopyMem (Ghcb->SharedBuffer, InstructionData->Immediate, Bytes);
750 
751     Ghcb->SaveArea.SwScratch = (UINT64) Ghcb->SharedBuffer;
752     Status = VmgExit (Ghcb, SVM_EXIT_MMIO_WRITE, ExitInfo1, ExitInfo2);
753     if (Status != 0) {
754       return Status;
755     }
756     break;
757 
758   //
759   // MMIO read (MOV regX, reg/memX)
760   //
761   case 0x8A:
762     Bytes = 1;
763     //
764     // fall through
765     //
766   case 0x8B:
767     DecodeModRm (Regs, InstructionData);
768     Bytes = ((Bytes != 0) ? Bytes :
769              (InstructionData->DataSize == Size16Bits) ? 2 :
770              (InstructionData->DataSize == Size32Bits) ? 4 :
771              (InstructionData->DataSize == Size64Bits) ? 8 :
772              0);
773     if (InstructionData->Ext.ModRm.Mod == 3) {
774       //
775       // NPF on two register operands???
776       //
777       return UnsupportedExit (Ghcb, Regs, InstructionData);
778     }
779 
780     ExitInfo1 = InstructionData->Ext.RmData;
781     ExitInfo2 = Bytes;
782 
783     Ghcb->SaveArea.SwScratch = (UINT64) Ghcb->SharedBuffer;
784     Status = VmgExit (Ghcb, SVM_EXIT_MMIO_READ, ExitInfo1, ExitInfo2);
785     if (Status != 0) {
786       return Status;
787     }
788 
789     Register = GetRegisterPointer (Regs, InstructionData->Ext.ModRm.Reg);
790     if (Bytes == 4) {
791       //
792       // Zero-extend for 32-bit operation
793       //
794       *Register = 0;
795     }
796     CopyMem (Register, Ghcb->SharedBuffer, Bytes);
797     break;
798 
799   //
800   // MMIO read w/ zero-extension ((MOVZX regX, reg/memX)
801   //
802   case 0xB6:
803     Bytes = 1;
804     //
805     // fall through
806     //
807   case 0xB7:
808     Bytes = (Bytes != 0) ? Bytes : 2;
809 
810     ExitInfo1 = InstructionData->Ext.RmData;
811     ExitInfo2 = Bytes;
812 
813     Ghcb->SaveArea.SwScratch = (UINT64) Ghcb->SharedBuffer;
814     Status = VmgExit (Ghcb, SVM_EXIT_MMIO_READ, ExitInfo1, ExitInfo2);
815     if (Status != 0) {
816       return Status;
817     }
818 
819     Register = GetRegisterPointer (Regs, InstructionData->Ext.ModRm.Reg);
820     SetMem (Register, InstructionData->DataSize, 0);
821     CopyMem (Register, Ghcb->SharedBuffer, Bytes);
822     break;
823 
824   //
825   // MMIO read w/ sign-extension (MOVSX regX, reg/memX)
826   //
827   case 0xBE:
828     Bytes = 1;
829     //
830     // fall through
831     //
832   case 0xBF:
833     Bytes = (Bytes != 0) ? Bytes : 2;
834 
835     ExitInfo1 = InstructionData->Ext.RmData;
836     ExitInfo2 = Bytes;
837 
838     Ghcb->SaveArea.SwScratch = (UINT64) Ghcb->SharedBuffer;
839     Status = VmgExit (Ghcb, SVM_EXIT_MMIO_READ, ExitInfo1, ExitInfo2);
840     if (Status != 0) {
841       return Status;
842     }
843 
844     if (Bytes == 1) {
845       UINT8 *Data;
846 
847       Data = (UINT8 *) Ghcb->SharedBuffer;
848       SignByte = ((*Data & BIT7) != 0) ? 0xFF : 0x00;
849     } else {
850       UINT16 *Data;
851 
852       Data = (UINT16 *) Ghcb->SharedBuffer;
853       SignByte = ((*Data & BIT15) != 0) ? 0xFF : 0x00;
854     }
855 
856     Register = GetRegisterPointer (Regs, InstructionData->Ext.ModRm.Reg);
857     SetMem (Register, InstructionData->DataSize, SignByte);
858     CopyMem (Register, Ghcb->SharedBuffer, Bytes);
859     break;
860 
861   default:
862     Status = GP_EXCEPTION;
863     ASSERT (FALSE);
864   }
865 
866   return Status;
867 }
868 
869 /**
870   Handle a MWAIT event.
871 
872   Use the VMGEXIT instruction to handle a MWAIT event.
873 
874   @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication
875                                    Block
876   @param[in, out] Regs             x64 processor context
877   @param[in]      InstructionData  Instruction parsing context
878 
879   @retval 0                        Event handled successfully
880   @return                          New exception value to propagate
881 
882 **/
883 STATIC
884 UINT64
MwaitExit(IN OUT GHCB * Ghcb,IN OUT EFI_SYSTEM_CONTEXT_X64 * Regs,IN SEV_ES_INSTRUCTION_DATA * InstructionData)885 MwaitExit (
886   IN OUT GHCB                     *Ghcb,
887   IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,
888   IN     SEV_ES_INSTRUCTION_DATA  *InstructionData
889   )
890 {
891   DecodeModRm (Regs, InstructionData);
892 
893   Ghcb->SaveArea.Rax = Regs->Rax;
894   GhcbSetRegValid (Ghcb, GhcbRax);
895   Ghcb->SaveArea.Rcx = Regs->Rcx;
896   GhcbSetRegValid (Ghcb, GhcbRcx);
897 
898   return VmgExit (Ghcb, SVM_EXIT_MWAIT, 0, 0);
899 }
900 
901 /**
902   Handle a MONITOR event.
903 
904   Use the VMGEXIT instruction to handle a MONITOR event.
905 
906   @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication
907                                    Block
908   @param[in, out] Regs             x64 processor context
909   @param[in]      InstructionData  Instruction parsing context
910 
911   @retval 0                        Event handled successfully
912   @return                          New exception value to propagate
913 
914 **/
915 STATIC
916 UINT64
MonitorExit(IN OUT GHCB * Ghcb,IN OUT EFI_SYSTEM_CONTEXT_X64 * Regs,IN SEV_ES_INSTRUCTION_DATA * InstructionData)917 MonitorExit (
918   IN OUT GHCB                     *Ghcb,
919   IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,
920   IN     SEV_ES_INSTRUCTION_DATA  *InstructionData
921   )
922 {
923   DecodeModRm (Regs, InstructionData);
924 
925   Ghcb->SaveArea.Rax = Regs->Rax;  // Identity mapped, so VA = PA
926   GhcbSetRegValid (Ghcb, GhcbRax);
927   Ghcb->SaveArea.Rcx = Regs->Rcx;
928   GhcbSetRegValid (Ghcb, GhcbRcx);
929   Ghcb->SaveArea.Rdx = Regs->Rdx;
930   GhcbSetRegValid (Ghcb, GhcbRdx);
931 
932   return VmgExit (Ghcb, SVM_EXIT_MONITOR, 0, 0);
933 }
934 
935 /**
936   Handle a WBINVD event.
937 
938   Use the VMGEXIT instruction to handle a WBINVD event.
939 
940   @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication
941                                    Block
942   @param[in, out] Regs             x64 processor context
943   @param[in]      InstructionData  Instruction parsing context
944 
945   @retval 0                        Event handled successfully
946   @return                          New exception value to propagate
947 
948 **/
949 STATIC
950 UINT64
WbinvdExit(IN OUT GHCB * Ghcb,IN OUT EFI_SYSTEM_CONTEXT_X64 * Regs,IN SEV_ES_INSTRUCTION_DATA * InstructionData)951 WbinvdExit (
952   IN OUT GHCB                     *Ghcb,
953   IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,
954   IN     SEV_ES_INSTRUCTION_DATA  *InstructionData
955   )
956 {
957   return VmgExit (Ghcb, SVM_EXIT_WBINVD, 0, 0);
958 }
959 
960 /**
961   Handle a RDTSCP event.
962 
963   Use the VMGEXIT instruction to handle a RDTSCP event.
964 
965   @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication
966                                    Block
967   @param[in, out] Regs             x64 processor context
968   @param[in]      InstructionData  Instruction parsing context
969 
970   @retval 0                        Event handled successfully
971   @return                          New exception value to propagate
972 
973 **/
974 STATIC
975 UINT64
RdtscpExit(IN OUT GHCB * Ghcb,IN OUT EFI_SYSTEM_CONTEXT_X64 * Regs,IN SEV_ES_INSTRUCTION_DATA * InstructionData)976 RdtscpExit (
977   IN OUT GHCB                     *Ghcb,
978   IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,
979   IN     SEV_ES_INSTRUCTION_DATA  *InstructionData
980   )
981 {
982   UINT64  Status;
983 
984   DecodeModRm (Regs, InstructionData);
985 
986   Status = VmgExit (Ghcb, SVM_EXIT_RDTSCP, 0, 0);
987   if (Status != 0) {
988     return Status;
989   }
990 
991   if (!GhcbIsRegValid (Ghcb, GhcbRax) ||
992       !GhcbIsRegValid (Ghcb, GhcbRcx) ||
993       !GhcbIsRegValid (Ghcb, GhcbRdx)) {
994     return UnsupportedExit (Ghcb, Regs, InstructionData);
995   }
996   Regs->Rax = Ghcb->SaveArea.Rax;
997   Regs->Rcx = Ghcb->SaveArea.Rcx;
998   Regs->Rdx = Ghcb->SaveArea.Rdx;
999 
1000   return 0;
1001 }
1002 
1003 /**
1004   Handle a VMMCALL event.
1005 
1006   Use the VMGEXIT instruction to handle a VMMCALL event.
1007 
1008   @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication
1009                                    Block
1010   @param[in, out] Regs             x64 processor context
1011   @param[in]      InstructionData  Instruction parsing context
1012 
1013   @retval 0                        Event handled successfully
1014   @return                          New exception value to propagate
1015 
1016 **/
1017 STATIC
1018 UINT64
VmmCallExit(IN OUT GHCB * Ghcb,IN OUT EFI_SYSTEM_CONTEXT_X64 * Regs,IN SEV_ES_INSTRUCTION_DATA * InstructionData)1019 VmmCallExit (
1020   IN OUT GHCB                     *Ghcb,
1021   IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,
1022   IN     SEV_ES_INSTRUCTION_DATA  *InstructionData
1023   )
1024 {
1025   UINT64  Status;
1026 
1027   DecodeModRm (Regs, InstructionData);
1028 
1029   Ghcb->SaveArea.Rax = Regs->Rax;
1030   GhcbSetRegValid (Ghcb, GhcbRax);
1031   Ghcb->SaveArea.Cpl = (UINT8) (Regs->Cs & 0x3);
1032   GhcbSetRegValid (Ghcb, GhcbCpl);
1033 
1034   Status = VmgExit (Ghcb, SVM_EXIT_VMMCALL, 0, 0);
1035   if (Status != 0) {
1036     return Status;
1037   }
1038 
1039   if (!GhcbIsRegValid (Ghcb, GhcbRax)) {
1040     return UnsupportedExit (Ghcb, Regs, InstructionData);
1041   }
1042   Regs->Rax = Ghcb->SaveArea.Rax;
1043 
1044   return 0;
1045 }
1046 
1047 /**
1048   Handle an MSR event.
1049 
1050   Use the VMGEXIT instruction to handle either a RDMSR or WRMSR event.
1051 
1052   @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication
1053                                    Block
1054   @param[in, out] Regs             x64 processor context
1055   @param[in]      InstructionData  Instruction parsing context
1056 
1057   @retval 0                        Event handled successfully
1058   @return                          New exception value to propagate
1059 
1060 **/
1061 STATIC
1062 UINT64
MsrExit(IN OUT GHCB * Ghcb,IN OUT EFI_SYSTEM_CONTEXT_X64 * Regs,IN SEV_ES_INSTRUCTION_DATA * InstructionData)1063 MsrExit (
1064   IN OUT GHCB                     *Ghcb,
1065   IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,
1066   IN     SEV_ES_INSTRUCTION_DATA  *InstructionData
1067   )
1068 {
1069   UINT64  ExitInfo1, Status;
1070 
1071   ExitInfo1 = 0;
1072 
1073   switch (*(InstructionData->OpCodes + 1)) {
1074   case 0x30: // WRMSR
1075     ExitInfo1 = 1;
1076     Ghcb->SaveArea.Rax = Regs->Rax;
1077     GhcbSetRegValid (Ghcb, GhcbRax);
1078     Ghcb->SaveArea.Rdx = Regs->Rdx;
1079     GhcbSetRegValid (Ghcb, GhcbRdx);
1080     //
1081     // fall through
1082     //
1083   case 0x32: // RDMSR
1084     Ghcb->SaveArea.Rcx = Regs->Rcx;
1085     GhcbSetRegValid (Ghcb, GhcbRcx);
1086     break;
1087   default:
1088     return UnsupportedExit (Ghcb, Regs, InstructionData);
1089   }
1090 
1091   Status = VmgExit (Ghcb, SVM_EXIT_MSR, ExitInfo1, 0);
1092   if (Status != 0) {
1093     return Status;
1094   }
1095 
1096   if (ExitInfo1 == 0) {
1097     if (!GhcbIsRegValid (Ghcb, GhcbRax) ||
1098         !GhcbIsRegValid (Ghcb, GhcbRdx)) {
1099       return UnsupportedExit (Ghcb, Regs, InstructionData);
1100     }
1101     Regs->Rax = Ghcb->SaveArea.Rax;
1102     Regs->Rdx = Ghcb->SaveArea.Rdx;
1103   }
1104 
1105   return 0;
1106 }
1107 
1108 /**
1109   Build the IOIO event information.
1110 
1111   The IOIO event information identifies the type of IO operation to be performed
1112   by the hypervisor. Build this information based on the instruction data.
1113 
1114   @param[in]       Regs             x64 processor context
1115   @param[in, out]  InstructionData  Instruction parsing context
1116 
1117   @return                           IOIO event information value
1118 
1119 **/
1120 STATIC
1121 UINT64
IoioExitInfo(IN EFI_SYSTEM_CONTEXT_X64 * Regs,IN OUT SEV_ES_INSTRUCTION_DATA * InstructionData)1122 IoioExitInfo (
1123   IN     EFI_SYSTEM_CONTEXT_X64   *Regs,
1124   IN OUT SEV_ES_INSTRUCTION_DATA  *InstructionData
1125   )
1126 {
1127   UINT64  ExitInfo;
1128 
1129   ExitInfo = 0;
1130 
1131   switch (*(InstructionData->OpCodes)) {
1132   //
1133   // INS opcodes
1134   //
1135   case 0x6C:
1136   case 0x6D:
1137     ExitInfo |= IOIO_TYPE_INS;
1138     ExitInfo |= IOIO_SEG_ES;
1139     ExitInfo |= ((Regs->Rdx & 0xffff) << 16);
1140     break;
1141 
1142   //
1143   // OUTS opcodes
1144   //
1145   case 0x6E:
1146   case 0x6F:
1147     ExitInfo |= IOIO_TYPE_OUTS;
1148     ExitInfo |= IOIO_SEG_DS;
1149     ExitInfo |= ((Regs->Rdx & 0xffff) << 16);
1150     break;
1151 
1152   //
1153   // IN immediate opcodes
1154   //
1155   case 0xE4:
1156   case 0xE5:
1157     InstructionData->ImmediateSize = 1;
1158     InstructionData->End++;
1159     ExitInfo |= IOIO_TYPE_IN;
1160     ExitInfo |= ((*(InstructionData->OpCodes + 1)) << 16);
1161     break;
1162 
1163   //
1164   // OUT immediate opcodes
1165   //
1166   case 0xE6:
1167   case 0xE7:
1168     InstructionData->ImmediateSize = 1;
1169     InstructionData->End++;
1170     ExitInfo |= IOIO_TYPE_OUT;
1171     ExitInfo |= ((*(InstructionData->OpCodes + 1)) << 16) | IOIO_TYPE_OUT;
1172     break;
1173 
1174   //
1175   // IN register opcodes
1176   //
1177   case 0xEC:
1178   case 0xED:
1179     ExitInfo |= IOIO_TYPE_IN;
1180     ExitInfo |= ((Regs->Rdx & 0xffff) << 16);
1181     break;
1182 
1183   //
1184   // OUT register opcodes
1185   //
1186   case 0xEE:
1187   case 0xEF:
1188     ExitInfo |= IOIO_TYPE_OUT;
1189     ExitInfo |= ((Regs->Rdx & 0xffff) << 16);
1190     break;
1191 
1192   default:
1193     return 0;
1194   }
1195 
1196   switch (*(InstructionData->OpCodes)) {
1197   //
1198   // Single-byte opcodes
1199   //
1200   case 0x6C:
1201   case 0x6E:
1202   case 0xE4:
1203   case 0xE6:
1204   case 0xEC:
1205   case 0xEE:
1206     ExitInfo |= IOIO_DATA_8;
1207     break;
1208 
1209   //
1210   // Length determined by instruction parsing
1211   //
1212   default:
1213     ExitInfo |= (InstructionData->DataSize == Size16Bits) ? IOIO_DATA_16
1214                                                           : IOIO_DATA_32;
1215   }
1216 
1217   switch (InstructionData->AddrSize) {
1218   case Size16Bits:
1219     ExitInfo |= IOIO_ADDR_16;
1220     break;
1221 
1222   case Size32Bits:
1223     ExitInfo |= IOIO_ADDR_32;
1224     break;
1225 
1226   case Size64Bits:
1227     ExitInfo |= IOIO_ADDR_64;
1228     break;
1229 
1230   default:
1231     break;
1232   }
1233 
1234   if (InstructionData->RepMode != 0) {
1235     ExitInfo |= IOIO_REP;
1236   }
1237 
1238   return ExitInfo;
1239 }
1240 
1241 /**
1242   Handle an IOIO event.
1243 
1244   Use the VMGEXIT instruction to handle an IOIO event.
1245 
1246   @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication
1247                                    Block
1248   @param[in, out] Regs             x64 processor context
1249   @param[in]      InstructionData  Instruction parsing context
1250 
1251   @retval 0                        Event handled successfully
1252   @return                          New exception value to propagate
1253 
1254 **/
1255 STATIC
1256 UINT64
IoioExit(IN OUT GHCB * Ghcb,IN OUT EFI_SYSTEM_CONTEXT_X64 * Regs,IN SEV_ES_INSTRUCTION_DATA * InstructionData)1257 IoioExit (
1258   IN OUT GHCB                     *Ghcb,
1259   IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,
1260   IN     SEV_ES_INSTRUCTION_DATA  *InstructionData
1261   )
1262 {
1263   UINT64   ExitInfo1, ExitInfo2, Status;
1264   BOOLEAN  IsString;
1265 
1266   ExitInfo1 = IoioExitInfo (Regs, InstructionData);
1267   if (ExitInfo1 == 0) {
1268     return UnsupportedExit (Ghcb, Regs, InstructionData);
1269   }
1270 
1271   IsString = ((ExitInfo1 & IOIO_TYPE_STR) != 0) ? TRUE : FALSE;
1272   if (IsString) {
1273     UINTN  IoBytes, VmgExitBytes;
1274     UINTN  GhcbCount, OpCount;
1275 
1276     Status = 0;
1277 
1278     IoBytes = IOIO_DATA_BYTES (ExitInfo1);
1279     GhcbCount = sizeof (Ghcb->SharedBuffer) / IoBytes;
1280 
1281     OpCount = ((ExitInfo1 & IOIO_REP) != 0) ? Regs->Rcx : 1;
1282     while (OpCount != 0) {
1283       ExitInfo2 = MIN (OpCount, GhcbCount);
1284       VmgExitBytes = ExitInfo2 * IoBytes;
1285 
1286       if ((ExitInfo1 & IOIO_TYPE_IN) == 0) {
1287         CopyMem (Ghcb->SharedBuffer, (VOID *) Regs->Rsi, VmgExitBytes);
1288         Regs->Rsi += VmgExitBytes;
1289       }
1290 
1291       Ghcb->SaveArea.SwScratch = (UINT64) Ghcb->SharedBuffer;
1292       Status = VmgExit (Ghcb, SVM_EXIT_IOIO_PROT, ExitInfo1, ExitInfo2);
1293       if (Status != 0) {
1294         return Status;
1295       }
1296 
1297       if ((ExitInfo1 & IOIO_TYPE_IN) != 0) {
1298         CopyMem ((VOID *) Regs->Rdi, Ghcb->SharedBuffer, VmgExitBytes);
1299         Regs->Rdi += VmgExitBytes;
1300       }
1301 
1302       if ((ExitInfo1 & IOIO_REP) != 0) {
1303         Regs->Rcx -= ExitInfo2;
1304       }
1305 
1306       OpCount -= ExitInfo2;
1307     }
1308   } else {
1309     if ((ExitInfo1 & IOIO_TYPE_IN) != 0) {
1310       Ghcb->SaveArea.Rax = 0;
1311     } else {
1312       CopyMem (&Ghcb->SaveArea.Rax, &Regs->Rax, IOIO_DATA_BYTES (ExitInfo1));
1313     }
1314     GhcbSetRegValid (Ghcb, GhcbRax);
1315 
1316     Status = VmgExit (Ghcb, SVM_EXIT_IOIO_PROT, ExitInfo1, 0);
1317     if (Status != 0) {
1318       return Status;
1319     }
1320 
1321     if ((ExitInfo1 & IOIO_TYPE_IN) != 0) {
1322       if (!GhcbIsRegValid (Ghcb, GhcbRax)) {
1323         return UnsupportedExit (Ghcb, Regs, InstructionData);
1324       }
1325       CopyMem (&Regs->Rax, &Ghcb->SaveArea.Rax, IOIO_DATA_BYTES (ExitInfo1));
1326     }
1327   }
1328 
1329   return 0;
1330 }
1331 
1332 /**
1333   Handle a INVD event.
1334 
1335   Use the VMGEXIT instruction to handle a INVD event.
1336 
1337   @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication
1338                                    Block
1339   @param[in, out] Regs             x64 processor context
1340   @param[in]      InstructionData  Instruction parsing context
1341 
1342   @retval 0                        Event handled successfully
1343   @return                          New exception value to propagate
1344 
1345 **/
1346 STATIC
1347 UINT64
InvdExit(IN OUT GHCB * Ghcb,IN OUT EFI_SYSTEM_CONTEXT_X64 * Regs,IN SEV_ES_INSTRUCTION_DATA * InstructionData)1348 InvdExit (
1349   IN OUT GHCB                     *Ghcb,
1350   IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,
1351   IN     SEV_ES_INSTRUCTION_DATA  *InstructionData
1352   )
1353 {
1354   return VmgExit (Ghcb, SVM_EXIT_INVD, 0, 0);
1355 }
1356 
1357 /**
1358   Handle a CPUID event.
1359 
1360   Use the VMGEXIT instruction to handle a CPUID event.
1361 
1362   @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication
1363                                    Block
1364   @param[in, out] Regs             x64 processor context
1365   @param[in]      InstructionData  Instruction parsing context
1366 
1367   @retval 0                        Event handled successfully
1368   @return                          New exception value to propagate
1369 
1370 **/
1371 STATIC
1372 UINT64
CpuidExit(IN OUT GHCB * Ghcb,IN OUT EFI_SYSTEM_CONTEXT_X64 * Regs,IN SEV_ES_INSTRUCTION_DATA * InstructionData)1373 CpuidExit (
1374   IN OUT GHCB                     *Ghcb,
1375   IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,
1376   IN     SEV_ES_INSTRUCTION_DATA  *InstructionData
1377   )
1378 {
1379   UINT64  Status;
1380 
1381   Ghcb->SaveArea.Rax = Regs->Rax;
1382   GhcbSetRegValid (Ghcb, GhcbRax);
1383   Ghcb->SaveArea.Rcx = Regs->Rcx;
1384   GhcbSetRegValid (Ghcb, GhcbRcx);
1385   if (Regs->Rax == CPUID_EXTENDED_STATE) {
1386     IA32_CR4  Cr4;
1387 
1388     Cr4.UintN = AsmReadCr4 ();
1389     Ghcb->SaveArea.XCr0 = (Cr4.Bits.OSXSAVE == 1) ? AsmXGetBv (0) : 1;
1390     GhcbSetRegValid (Ghcb, GhcbXCr0);
1391   }
1392 
1393   Status = VmgExit (Ghcb, SVM_EXIT_CPUID, 0, 0);
1394   if (Status != 0) {
1395     return Status;
1396   }
1397 
1398   if (!GhcbIsRegValid (Ghcb, GhcbRax) ||
1399       !GhcbIsRegValid (Ghcb, GhcbRbx) ||
1400       !GhcbIsRegValid (Ghcb, GhcbRcx) ||
1401       !GhcbIsRegValid (Ghcb, GhcbRdx)) {
1402     return UnsupportedExit (Ghcb, Regs, InstructionData);
1403   }
1404   Regs->Rax = Ghcb->SaveArea.Rax;
1405   Regs->Rbx = Ghcb->SaveArea.Rbx;
1406   Regs->Rcx = Ghcb->SaveArea.Rcx;
1407   Regs->Rdx = Ghcb->SaveArea.Rdx;
1408 
1409   return 0;
1410 }
1411 
1412 /**
1413   Handle a RDPMC event.
1414 
1415   Use the VMGEXIT instruction to handle a RDPMC event.
1416 
1417   @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication
1418                                    Block
1419   @param[in, out] Regs             x64 processor context
1420   @param[in]      InstructionData  Instruction parsing context
1421 
1422   @retval 0                        Event handled successfully
1423   @return                          New exception value to propagate
1424 
1425 **/
1426 STATIC
1427 UINT64
RdpmcExit(IN OUT GHCB * Ghcb,IN OUT EFI_SYSTEM_CONTEXT_X64 * Regs,IN SEV_ES_INSTRUCTION_DATA * InstructionData)1428 RdpmcExit (
1429   IN OUT GHCB                     *Ghcb,
1430   IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,
1431   IN     SEV_ES_INSTRUCTION_DATA  *InstructionData
1432   )
1433 {
1434   UINT64  Status;
1435 
1436   Ghcb->SaveArea.Rcx = Regs->Rcx;
1437   GhcbSetRegValid (Ghcb, GhcbRcx);
1438 
1439   Status = VmgExit (Ghcb, SVM_EXIT_RDPMC, 0, 0);
1440   if (Status != 0) {
1441     return Status;
1442   }
1443 
1444   if (!GhcbIsRegValid (Ghcb, GhcbRax) ||
1445       !GhcbIsRegValid (Ghcb, GhcbRdx)) {
1446     return UnsupportedExit (Ghcb, Regs, InstructionData);
1447   }
1448   Regs->Rax = Ghcb->SaveArea.Rax;
1449   Regs->Rdx = Ghcb->SaveArea.Rdx;
1450 
1451   return 0;
1452 }
1453 
1454 /**
1455   Handle a RDTSC event.
1456 
1457   Use the VMGEXIT instruction to handle a RDTSC event.
1458 
1459   @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication
1460                                    Block
1461   @param[in, out] Regs             x64 processor context
1462   @param[in]      InstructionData  Instruction parsing context
1463 
1464   @retval 0                        Event handled successfully
1465   @return                          New exception value to propagate
1466 
1467 **/
1468 STATIC
1469 UINT64
RdtscExit(IN OUT GHCB * Ghcb,IN OUT EFI_SYSTEM_CONTEXT_X64 * Regs,IN SEV_ES_INSTRUCTION_DATA * InstructionData)1470 RdtscExit (
1471   IN OUT GHCB                     *Ghcb,
1472   IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,
1473   IN     SEV_ES_INSTRUCTION_DATA  *InstructionData
1474   )
1475 {
1476   UINT64  Status;
1477 
1478   Status = VmgExit (Ghcb, SVM_EXIT_RDTSC, 0, 0);
1479   if (Status != 0) {
1480     return Status;
1481   }
1482 
1483   if (!GhcbIsRegValid (Ghcb, GhcbRax) ||
1484       !GhcbIsRegValid (Ghcb, GhcbRdx)) {
1485     return UnsupportedExit (Ghcb, Regs, InstructionData);
1486   }
1487   Regs->Rax = Ghcb->SaveArea.Rax;
1488   Regs->Rdx = Ghcb->SaveArea.Rdx;
1489 
1490   return 0;
1491 }
1492 
1493 /**
1494   Handle a DR7 register write event.
1495 
1496   Use the VMGEXIT instruction to handle a DR7 write event.
1497 
1498   @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication
1499                                    Block
1500   @param[in, out] Regs             x64 processor context
1501   @param[in]      InstructionData  Instruction parsing context
1502 
1503   @retval 0                        Event handled successfully
1504   @return                          New exception value to propagate
1505 
1506 **/
1507 STATIC
1508 UINT64
Dr7WriteExit(IN OUT GHCB * Ghcb,IN OUT EFI_SYSTEM_CONTEXT_X64 * Regs,IN SEV_ES_INSTRUCTION_DATA * InstructionData)1509 Dr7WriteExit (
1510   IN OUT GHCB                     *Ghcb,
1511   IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,
1512   IN     SEV_ES_INSTRUCTION_DATA  *InstructionData
1513   )
1514 {
1515   SEV_ES_INSTRUCTION_OPCODE_EXT  *Ext;
1516   SEV_ES_PER_CPU_DATA            *SevEsData;
1517   UINT64                         *Register;
1518   UINT64                         Status;
1519 
1520   Ext = &InstructionData->Ext;
1521   SevEsData = (SEV_ES_PER_CPU_DATA *) (Ghcb + 1);
1522 
1523   DecodeModRm (Regs, InstructionData);
1524 
1525   //
1526   // MOV DRn always treats MOD == 3 no matter how encoded
1527   //
1528   Register = GetRegisterPointer (Regs, Ext->ModRm.Rm);
1529 
1530   //
1531   // Using a value of 0 for ExitInfo1 means RAX holds the value
1532   //
1533   Ghcb->SaveArea.Rax = *Register;
1534   GhcbSetRegValid (Ghcb, GhcbRax);
1535 
1536   Status = VmgExit (Ghcb, SVM_EXIT_DR7_WRITE, 0, 0);
1537   if (Status != 0) {
1538     return Status;
1539   }
1540 
1541   SevEsData->Dr7 = *Register;
1542   SevEsData->Dr7Cached = TRUE;
1543 
1544   return 0;
1545 }
1546 
1547 /**
1548   Handle a DR7 register read event.
1549 
1550   Use the VMGEXIT instruction to handle a DR7 read event.
1551 
1552   @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication
1553                                    Block
1554   @param[in, out] Regs             x64 processor context
1555   @param[in]      InstructionData  Instruction parsing context
1556 
1557   @retval 0                        Event handled successfully
1558 
1559 **/
1560 STATIC
1561 UINT64
Dr7ReadExit(IN OUT GHCB * Ghcb,IN OUT EFI_SYSTEM_CONTEXT_X64 * Regs,IN SEV_ES_INSTRUCTION_DATA * InstructionData)1562 Dr7ReadExit (
1563   IN OUT GHCB                     *Ghcb,
1564   IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,
1565   IN     SEV_ES_INSTRUCTION_DATA  *InstructionData
1566   )
1567 {
1568   SEV_ES_INSTRUCTION_OPCODE_EXT  *Ext;
1569   SEV_ES_PER_CPU_DATA            *SevEsData;
1570   UINT64                         *Register;
1571 
1572   Ext = &InstructionData->Ext;
1573   SevEsData = (SEV_ES_PER_CPU_DATA *) (Ghcb + 1);
1574 
1575   DecodeModRm (Regs, InstructionData);
1576 
1577   //
1578   // MOV DRn always treats MOD == 3 no matter how encoded
1579   //
1580   Register = GetRegisterPointer (Regs, Ext->ModRm.Rm);
1581 
1582   //
1583   // If there is a cached valued for DR7, return that. Otherwise return the
1584   // DR7 standard reset value of 0x400 (no debug breakpoints set).
1585   //
1586   *Register = (SevEsData->Dr7Cached) ? SevEsData->Dr7 : 0x400;
1587 
1588   return 0;
1589 }
1590 
1591 /**
1592   Handle a #VC exception.
1593 
1594   Performs the necessary processing to handle a #VC exception.
1595 
1596   @param[in, out]  ExceptionType  Pointer to an EFI_EXCEPTION_TYPE to be set
1597                                   as value to use on error.
1598   @param[in, out]  SystemContext  Pointer to EFI_SYSTEM_CONTEXT
1599 
1600   @retval  EFI_SUCCESS            Exception handled
1601   @retval  EFI_UNSUPPORTED        #VC not supported, (new) exception value to
1602                                   propagate provided
1603   @retval  EFI_PROTOCOL_ERROR     #VC handling failed, (new) exception value to
1604                                   propagate provided
1605 
1606 **/
1607 EFI_STATUS
1608 EFIAPI
VmgExitHandleVc(IN OUT EFI_EXCEPTION_TYPE * ExceptionType,IN OUT EFI_SYSTEM_CONTEXT SystemContext)1609 VmgExitHandleVc (
1610   IN OUT EFI_EXCEPTION_TYPE  *ExceptionType,
1611   IN OUT EFI_SYSTEM_CONTEXT  SystemContext
1612   )
1613 {
1614   MSR_SEV_ES_GHCB_REGISTER  Msr;
1615   EFI_SYSTEM_CONTEXT_X64    *Regs;
1616   GHCB                      *Ghcb;
1617   NAE_EXIT                  NaeExit;
1618   SEV_ES_INSTRUCTION_DATA   InstructionData;
1619   UINT64                    ExitCode, Status;
1620   EFI_STATUS                VcRet;
1621 
1622   VcRet = EFI_SUCCESS;
1623 
1624   Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);
1625   ASSERT (Msr.GhcbInfo.Function == 0);
1626   ASSERT (Msr.Ghcb != 0);
1627 
1628   Regs = SystemContext.SystemContextX64;
1629   Ghcb = Msr.Ghcb;
1630 
1631   VmgInit (Ghcb);
1632 
1633   ExitCode = Regs->ExceptionData;
1634   switch (ExitCode) {
1635   case SVM_EXIT_DR7_READ:
1636     NaeExit = Dr7ReadExit;
1637     break;
1638 
1639   case SVM_EXIT_DR7_WRITE:
1640     NaeExit = Dr7WriteExit;
1641     break;
1642 
1643   case SVM_EXIT_RDTSC:
1644     NaeExit = RdtscExit;
1645     break;
1646 
1647   case SVM_EXIT_RDPMC:
1648     NaeExit = RdpmcExit;
1649     break;
1650 
1651   case SVM_EXIT_CPUID:
1652     NaeExit = CpuidExit;
1653     break;
1654 
1655   case SVM_EXIT_INVD:
1656     NaeExit = InvdExit;
1657     break;
1658 
1659   case SVM_EXIT_IOIO_PROT:
1660     NaeExit = IoioExit;
1661     break;
1662 
1663   case SVM_EXIT_MSR:
1664     NaeExit = MsrExit;
1665     break;
1666 
1667   case SVM_EXIT_VMMCALL:
1668     NaeExit = VmmCallExit;
1669     break;
1670 
1671   case SVM_EXIT_RDTSCP:
1672     NaeExit = RdtscpExit;
1673     break;
1674 
1675   case SVM_EXIT_WBINVD:
1676     NaeExit = WbinvdExit;
1677     break;
1678 
1679   case SVM_EXIT_MONITOR:
1680     NaeExit = MonitorExit;
1681     break;
1682 
1683   case SVM_EXIT_MWAIT:
1684     NaeExit = MwaitExit;
1685     break;
1686 
1687   case SVM_EXIT_NPF:
1688     NaeExit = MmioExit;
1689     break;
1690 
1691   default:
1692     NaeExit = UnsupportedExit;
1693   }
1694 
1695   InitInstructionData (&InstructionData, Ghcb, Regs);
1696 
1697   Status = NaeExit (Ghcb, Regs, &InstructionData);
1698   if (Status == 0) {
1699     Regs->Rip += InstructionLength (&InstructionData);
1700   } else {
1701     GHCB_EVENT_INJECTION  Event;
1702 
1703     Event.Uint64 = Status;
1704     if (Event.Elements.ErrorCodeValid != 0) {
1705       Regs->ExceptionData = Event.Elements.ErrorCode;
1706     } else {
1707       Regs->ExceptionData = 0;
1708     }
1709 
1710     *ExceptionType = Event.Elements.Vector;
1711 
1712     VcRet = EFI_PROTOCOL_ERROR;
1713   }
1714 
1715   VmgDone (Ghcb);
1716 
1717   return VcRet;
1718 }
1719