]> git.proxmox.com Git - mirror_edk2.git/blobdiff - OvmfPkg/Library/VmgExitLib/VmgExitVcHandler.c
OvmfPkg/VmgExitLib: Set the SwScratch valid bit for MMIO events
[mirror_edk2.git] / OvmfPkg / Library / VmgExitLib / VmgExitVcHandler.c
index bc2e270a7ce87da42297ab4c3f3bde10e18cc879..9bf9d160179c1f036a404a7e56e1c70f2024f3c3 100644 (file)
@@ -11,6 +11,7 @@
 #include <Library/BaseMemoryLib.h>\r
 #include <Library/VmgExitLib.h>\r
 #include <Register/Amd/Msr.h>\r
+#include <Register/Intel/Cpuid.h>\r
 #include <IndustryStandard/InstructionParsing.h>\r
 \r
 //\r
@@ -125,61 +126,288 @@ UINT64
   SEV_ES_INSTRUCTION_DATA  *InstructionData\r
   );\r
 \r
+//\r
+// Per-CPU data mapping structure\r
+//\r
+typedef struct {\r
+  BOOLEAN  Dr7Cached;\r
+  UINT64   Dr7;\r
+} SEV_ES_PER_CPU_DATA;\r
+\r
 \r
 /**\r
-  Checks the GHCB to determine if the specified register has been marked valid.\r
+  Return a pointer to the contents of the specified register.\r
 \r
-  The ValidBitmap area represents the areas of the GHCB that have been marked\r
-  valid. Return an indication of whether the area of the GHCB that holds the\r
-  specified register has been marked valid.\r
+  Based upon the input register, return a pointer to the registers contents\r
+  in the x86 processor context.\r
 \r
-  @param[in] Ghcb    Pointer to the Guest-Hypervisor Communication Block\r
-  @param[in] Reg     Offset in the GHCB of the register to check\r
+  @param[in] Regs      x64 processor context\r
+  @param[in] Register  Register to obtain pointer for\r
 \r
-  @retval TRUE       Register has been marked vald in the GHCB\r
-  @retval FALSE      Register has not been marked valid in the GHCB\r
+  @return              Pointer to the contents of the requested register\r
+\r
+**/\r
+STATIC\r
+UINT64 *\r
+GetRegisterPointer (\r
+  IN EFI_SYSTEM_CONTEXT_X64   *Regs,\r
+  IN UINT8                    Register\r
+  )\r
+{\r
+  UINT64 *Reg;\r
+\r
+  switch (Register) {\r
+  case 0:\r
+    Reg = &Regs->Rax;\r
+    break;\r
+  case 1:\r
+    Reg = &Regs->Rcx;\r
+    break;\r
+  case 2:\r
+    Reg = &Regs->Rdx;\r
+    break;\r
+  case 3:\r
+    Reg = &Regs->Rbx;\r
+    break;\r
+  case 4:\r
+    Reg = &Regs->Rsp;\r
+    break;\r
+  case 5:\r
+    Reg = &Regs->Rbp;\r
+    break;\r
+  case 6:\r
+    Reg = &Regs->Rsi;\r
+    break;\r
+  case 7:\r
+    Reg = &Regs->Rdi;\r
+    break;\r
+  case 8:\r
+    Reg = &Regs->R8;\r
+    break;\r
+  case 9:\r
+    Reg = &Regs->R9;\r
+    break;\r
+  case 10:\r
+    Reg = &Regs->R10;\r
+    break;\r
+  case 11:\r
+    Reg = &Regs->R11;\r
+    break;\r
+  case 12:\r
+    Reg = &Regs->R12;\r
+    break;\r
+  case 13:\r
+    Reg = &Regs->R13;\r
+    break;\r
+  case 14:\r
+    Reg = &Regs->R14;\r
+    break;\r
+  case 15:\r
+    Reg = &Regs->R15;\r
+    break;\r
+  default:\r
+    Reg = NULL;\r
+  }\r
+  ASSERT (Reg != NULL);\r
+\r
+  return Reg;\r
+}\r
+\r
+/**\r
+  Update the instruction parsing context for displacement bytes.\r
+\r
+  @param[in, out] InstructionData  Instruction parsing context\r
+  @param[in]      Size             The instruction displacement size\r
+\r
+**/\r
+STATIC\r
+VOID\r
+UpdateForDisplacement (\r
+  IN OUT SEV_ES_INSTRUCTION_DATA  *InstructionData,\r
+  IN     UINTN                    Size\r
+  )\r
+{\r
+  InstructionData->DisplacementSize = Size;\r
+  InstructionData->Immediate += Size;\r
+  InstructionData->End += Size;\r
+}\r
+\r
+/**\r
+  Determine if an instruction address if RIP relative.\r
+\r
+  Examine the instruction parsing context to determine if the address offset\r
+  is relative to the instruction pointer.\r
+\r
+  @param[in] InstructionData  Instruction parsing context\r
+\r
+  @retval TRUE                Instruction addressing is RIP relative\r
+  @retval FALSE               Instruction addressing is not RIP relative\r
 \r
 **/\r
 STATIC\r
 BOOLEAN\r
-GhcbIsRegValid (\r
-  IN GHCB                *Ghcb,\r
-  IN GHCB_REGISTER       Reg\r
+IsRipRelative (\r
+  IN SEV_ES_INSTRUCTION_DATA  *InstructionData\r
   )\r
 {\r
-  UINT32  RegIndex;\r
-  UINT32  RegBit;\r
+  SEV_ES_INSTRUCTION_OPCODE_EXT  *Ext;\r
 \r
-  RegIndex = Reg / 8;\r
-  RegBit   = Reg & 0x07;\r
+  Ext = &InstructionData->Ext;\r
 \r
-  return ((Ghcb->SaveArea.ValidBitmap[RegIndex] & (1 << RegBit)) != 0);\r
+  return ((InstructionData->Mode == LongMode64Bit) &&\r
+          (Ext->ModRm.Mod == 0) &&\r
+          (Ext->ModRm.Rm == 5)  &&\r
+          (InstructionData->SibPresent == FALSE));\r
 }\r
 \r
 /**\r
-  Marks a register as valid in the GHCB.\r
+  Return the effective address of a memory operand.\r
 \r
-  The ValidBitmap area represents the areas of the GHCB that have been marked\r
-  valid. Set the area of the GHCB that holds the specified register as valid.\r
+  Examine the instruction parsing context to obtain the effective memory\r
+  address of a memory operand.\r
 \r
-  @param[in, out] Ghcb    Pointer to the Guest-Hypervisor Communication Block\r
-  @param[in] Reg          Offset in the GHCB of the register to mark valid\r
+  @param[in] Regs             x64 processor context\r
+  @param[in] InstructionData  Instruction parsing context\r
+\r
+  @return                     The memory operand effective address\r
+\r
+**/\r
+STATIC\r
+UINT64\r
+GetEffectiveMemoryAddress (\r
+  IN EFI_SYSTEM_CONTEXT_X64   *Regs,\r
+  IN SEV_ES_INSTRUCTION_DATA  *InstructionData\r
+  )\r
+{\r
+  SEV_ES_INSTRUCTION_OPCODE_EXT  *Ext;\r
+  UINT64                         EffectiveAddress;\r
+\r
+  Ext = &InstructionData->Ext;\r
+  EffectiveAddress = 0;\r
+\r
+  if (IsRipRelative (InstructionData)) {\r
+    //\r
+    // RIP-relative displacement is a 32-bit signed value\r
+    //\r
+    INT32 RipRelative;\r
+\r
+    RipRelative = *(INT32 *) InstructionData->Displacement;\r
+\r
+    UpdateForDisplacement (InstructionData, 4);\r
+\r
+    //\r
+    // Negative displacement is handled by standard UINT64 wrap-around.\r
+    //\r
+    return Regs->Rip + (UINT64) RipRelative;\r
+  }\r
+\r
+  switch (Ext->ModRm.Mod) {\r
+  case 1:\r
+    UpdateForDisplacement (InstructionData, 1);\r
+    EffectiveAddress += (UINT64) (*(INT8 *) (InstructionData->Displacement));\r
+    break;\r
+  case 2:\r
+    switch (InstructionData->AddrSize) {\r
+    case Size16Bits:\r
+      UpdateForDisplacement (InstructionData, 2);\r
+      EffectiveAddress += (UINT64) (*(INT16 *) (InstructionData->Displacement));\r
+      break;\r
+    default:\r
+      UpdateForDisplacement (InstructionData, 4);\r
+      EffectiveAddress += (UINT64) (*(INT32 *) (InstructionData->Displacement));\r
+      break;\r
+    }\r
+    break;\r
+  }\r
+\r
+  if (InstructionData->SibPresent) {\r
+    INT64  Displacement;\r
+\r
+    if (Ext->Sib.Index != 4) {\r
+      CopyMem (\r
+        &Displacement,\r
+        GetRegisterPointer (Regs, Ext->Sib.Index),\r
+        sizeof (Displacement)\r
+        );\r
+      Displacement *= (INT64)(1 << Ext->Sib.Scale);\r
+\r
+      //\r
+      // Negative displacement is handled by standard UINT64 wrap-around.\r
+      //\r
+      EffectiveAddress += (UINT64) Displacement;\r
+    }\r
+\r
+    if ((Ext->Sib.Base != 5) || Ext->ModRm.Mod) {\r
+      EffectiveAddress += *GetRegisterPointer (Regs, Ext->Sib.Base);\r
+    } else {\r
+      UpdateForDisplacement (InstructionData, 4);\r
+      EffectiveAddress += (UINT64) (*(INT32 *) (InstructionData->Displacement));\r
+    }\r
+  } else {\r
+    EffectiveAddress += *GetRegisterPointer (Regs, Ext->ModRm.Rm);\r
+  }\r
+\r
+  return EffectiveAddress;\r
+}\r
+\r
+/**\r
+  Decode a ModRM byte.\r
+\r
+  Examine the instruction parsing context to decode a ModRM byte and the SIB\r
+  byte, if present.\r
+\r
+  @param[in]      Regs             x64 processor context\r
+  @param[in, out] InstructionData  Instruction parsing context\r
 \r
 **/\r
 STATIC\r
 VOID\r
-GhcbSetRegValid (\r
-  IN OUT GHCB                *Ghcb,\r
-  IN     GHCB_REGISTER       Reg\r
+DecodeModRm (\r
+  IN     EFI_SYSTEM_CONTEXT_X64   *Regs,\r
+  IN OUT SEV_ES_INSTRUCTION_DATA  *InstructionData\r
   )\r
 {\r
-  UINT32  RegIndex;\r
-  UINT32  RegBit;\r
+  SEV_ES_INSTRUCTION_OPCODE_EXT  *Ext;\r
+  INSTRUCTION_REX_PREFIX         *RexPrefix;\r
+  INSTRUCTION_MODRM              *ModRm;\r
+  INSTRUCTION_SIB                *Sib;\r
+\r
+  RexPrefix = &InstructionData->RexPrefix;\r
+  Ext = &InstructionData->Ext;\r
+  ModRm = &InstructionData->ModRm;\r
+  Sib = &InstructionData->Sib;\r
+\r
+  InstructionData->ModRmPresent = TRUE;\r
+  ModRm->Uint8 = *(InstructionData->End);\r
+\r
+  InstructionData->Displacement++;\r
+  InstructionData->Immediate++;\r
+  InstructionData->End++;\r
+\r
+  Ext->ModRm.Mod = ModRm->Bits.Mod;\r
+  Ext->ModRm.Reg = (RexPrefix->Bits.BitR << 3) | ModRm->Bits.Reg;\r
+  Ext->ModRm.Rm  = (RexPrefix->Bits.BitB << 3) | ModRm->Bits.Rm;\r
 \r
-  RegIndex = Reg / 8;\r
-  RegBit   = Reg & 0x07;\r
+  Ext->RegData = *GetRegisterPointer (Regs, Ext->ModRm.Reg);\r
 \r
-  Ghcb->SaveArea.ValidBitmap[RegIndex] |= (1 << RegBit);\r
+  if (Ext->ModRm.Mod == 3) {\r
+    Ext->RmData = *GetRegisterPointer (Regs, Ext->ModRm.Rm);\r
+  } else {\r
+    if (ModRm->Bits.Rm == 4) {\r
+      InstructionData->SibPresent = TRUE;\r
+      Sib->Uint8 = *(InstructionData->End);\r
+\r
+      InstructionData->Displacement++;\r
+      InstructionData->Immediate++;\r
+      InstructionData->End++;\r
+\r
+      Ext->Sib.Scale = Sib->Bits.Scale;\r
+      Ext->Sib.Index = (RexPrefix->Bits.BitX << 3) | Sib->Bits.Index;\r
+      Ext->Sib.Base  = (RexPrefix->Bits.BitB << 3) | Sib->Bits.Base;\r
+    }\r
+\r
+    Ext->RmData = GetEffectiveMemoryAddress (Regs, InstructionData);\r
+  }\r
 }\r
 \r
 /**\r
@@ -332,45 +560,498 @@ InitInstructionData (
   InstructionData->Begin = (UINT8 *) Regs->Rip;\r
   InstructionData->End = (UINT8 *) Regs->Rip;\r
 \r
-  DecodePrefixes (Regs, InstructionData);\r
+  DecodePrefixes (Regs, InstructionData);\r
+}\r
+\r
+/**\r
+  Report an unsupported event to the hypervisor\r
+\r
+  Use the VMGEXIT support to report an unsupported event to the hypervisor.\r
+\r
+  @param[in] Ghcb             Pointer to the Guest-Hypervisor Communication\r
+                              Block\r
+  @param[in] Regs             x64 processor context\r
+  @param[in] InstructionData  Instruction parsing context\r
+\r
+  @return                     New exception value to propagate\r
+\r
+**/\r
+STATIC\r
+UINT64\r
+UnsupportedExit (\r
+  IN GHCB                     *Ghcb,\r
+  IN EFI_SYSTEM_CONTEXT_X64   *Regs,\r
+  IN SEV_ES_INSTRUCTION_DATA  *InstructionData\r
+  )\r
+{\r
+  UINT64  Status;\r
+\r
+  Status = VmgExit (Ghcb, SVM_EXIT_UNSUPPORTED, Regs->ExceptionData, 0);\r
+  if (Status == 0) {\r
+    GHCB_EVENT_INJECTION  Event;\r
+\r
+    Event.Uint64 = 0;\r
+    Event.Elements.Vector = GP_EXCEPTION;\r
+    Event.Elements.Type   = GHCB_EVENT_INJECTION_TYPE_EXCEPTION;\r
+    Event.Elements.Valid  = 1;\r
+\r
+    Status = Event.Uint64;\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Handle an MMIO event.\r
+\r
+  Use the VMGEXIT instruction to handle either an MMIO read or an MMIO write.\r
+\r
+  @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication\r
+                                   Block\r
+  @param[in, out] Regs             x64 processor context\r
+  @param[in, out] InstructionData  Instruction parsing context\r
+\r
+  @retval 0                        Event handled successfully\r
+  @return                          New exception value to propagate\r
+\r
+**/\r
+STATIC\r
+UINT64\r
+MmioExit (\r
+  IN OUT GHCB                     *Ghcb,\r
+  IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,\r
+  IN OUT SEV_ES_INSTRUCTION_DATA  *InstructionData\r
+  )\r
+{\r
+  UINT64  ExitInfo1, ExitInfo2, Status;\r
+  UINTN   Bytes;\r
+  UINT64  *Register;\r
+  UINT8   OpCode, SignByte;\r
+\r
+  Bytes = 0;\r
+\r
+  OpCode = *(InstructionData->OpCodes);\r
+  if (OpCode == TWO_BYTE_OPCODE_ESCAPE) {\r
+    OpCode = *(InstructionData->OpCodes + 1);\r
+  }\r
+\r
+  switch (OpCode) {\r
+  //\r
+  // MMIO write (MOV reg/memX, regX)\r
+  //\r
+  case 0x88:\r
+    Bytes = 1;\r
+    //\r
+    // fall through\r
+    //\r
+  case 0x89:\r
+    DecodeModRm (Regs, InstructionData);\r
+    Bytes = ((Bytes != 0) ? Bytes :\r
+             (InstructionData->DataSize == Size16Bits) ? 2 :\r
+             (InstructionData->DataSize == Size32Bits) ? 4 :\r
+             (InstructionData->DataSize == Size64Bits) ? 8 :\r
+             0);\r
+\r
+    if (InstructionData->Ext.ModRm.Mod == 3) {\r
+      //\r
+      // NPF on two register operands???\r
+      //\r
+      return UnsupportedExit (Ghcb, Regs, InstructionData);\r
+    }\r
+\r
+    ExitInfo1 = InstructionData->Ext.RmData;\r
+    ExitInfo2 = Bytes;\r
+    CopyMem (Ghcb->SharedBuffer, &InstructionData->Ext.RegData, Bytes);\r
+\r
+    Ghcb->SaveArea.SwScratch = (UINT64) Ghcb->SharedBuffer;\r
+    VmgSetOffsetValid (Ghcb, GhcbSwScratch);\r
+    Status = VmgExit (Ghcb, SVM_EXIT_MMIO_WRITE, ExitInfo1, ExitInfo2);\r
+    if (Status != 0) {\r
+      return Status;\r
+    }\r
+    break;\r
+\r
+  //\r
+  // MMIO write (MOV reg/memX, immX)\r
+  //\r
+  case 0xC6:\r
+    Bytes = 1;\r
+    //\r
+    // fall through\r
+    //\r
+  case 0xC7:\r
+    DecodeModRm (Regs, InstructionData);\r
+    Bytes = ((Bytes != 0) ? Bytes :\r
+             (InstructionData->DataSize == Size16Bits) ? 2 :\r
+             (InstructionData->DataSize == Size32Bits) ? 4 :\r
+             0);\r
+\r
+    InstructionData->ImmediateSize = Bytes;\r
+    InstructionData->End += Bytes;\r
+\r
+    ExitInfo1 = InstructionData->Ext.RmData;\r
+    ExitInfo2 = Bytes;\r
+    CopyMem (Ghcb->SharedBuffer, InstructionData->Immediate, Bytes);\r
+\r
+    Ghcb->SaveArea.SwScratch = (UINT64) Ghcb->SharedBuffer;\r
+    VmgSetOffsetValid (Ghcb, GhcbSwScratch);\r
+    Status = VmgExit (Ghcb, SVM_EXIT_MMIO_WRITE, ExitInfo1, ExitInfo2);\r
+    if (Status != 0) {\r
+      return Status;\r
+    }\r
+    break;\r
+\r
+  //\r
+  // MMIO read (MOV regX, reg/memX)\r
+  //\r
+  case 0x8A:\r
+    Bytes = 1;\r
+    //\r
+    // fall through\r
+    //\r
+  case 0x8B:\r
+    DecodeModRm (Regs, InstructionData);\r
+    Bytes = ((Bytes != 0) ? Bytes :\r
+             (InstructionData->DataSize == Size16Bits) ? 2 :\r
+             (InstructionData->DataSize == Size32Bits) ? 4 :\r
+             (InstructionData->DataSize == Size64Bits) ? 8 :\r
+             0);\r
+    if (InstructionData->Ext.ModRm.Mod == 3) {\r
+      //\r
+      // NPF on two register operands???\r
+      //\r
+      return UnsupportedExit (Ghcb, Regs, InstructionData);\r
+    }\r
+\r
+    ExitInfo1 = InstructionData->Ext.RmData;\r
+    ExitInfo2 = Bytes;\r
+\r
+    Ghcb->SaveArea.SwScratch = (UINT64) Ghcb->SharedBuffer;\r
+    VmgSetOffsetValid (Ghcb, GhcbSwScratch);\r
+    Status = VmgExit (Ghcb, SVM_EXIT_MMIO_READ, ExitInfo1, ExitInfo2);\r
+    if (Status != 0) {\r
+      return Status;\r
+    }\r
+\r
+    Register = GetRegisterPointer (Regs, InstructionData->Ext.ModRm.Reg);\r
+    if (Bytes == 4) {\r
+      //\r
+      // Zero-extend for 32-bit operation\r
+      //\r
+      *Register = 0;\r
+    }\r
+    CopyMem (Register, Ghcb->SharedBuffer, Bytes);\r
+    break;\r
+\r
+  //\r
+  // MMIO read w/ zero-extension ((MOVZX regX, reg/memX)\r
+  //\r
+  case 0xB6:\r
+    Bytes = 1;\r
+    //\r
+    // fall through\r
+    //\r
+  case 0xB7:\r
+    Bytes = (Bytes != 0) ? Bytes : 2;\r
+\r
+    ExitInfo1 = InstructionData->Ext.RmData;\r
+    ExitInfo2 = Bytes;\r
+\r
+    Ghcb->SaveArea.SwScratch = (UINT64) Ghcb->SharedBuffer;\r
+    VmgSetOffsetValid (Ghcb, GhcbSwScratch);\r
+    Status = VmgExit (Ghcb, SVM_EXIT_MMIO_READ, ExitInfo1, ExitInfo2);\r
+    if (Status != 0) {\r
+      return Status;\r
+    }\r
+\r
+    Register = GetRegisterPointer (Regs, InstructionData->Ext.ModRm.Reg);\r
+    SetMem (Register, InstructionData->DataSize, 0);\r
+    CopyMem (Register, Ghcb->SharedBuffer, Bytes);\r
+    break;\r
+\r
+  //\r
+  // MMIO read w/ sign-extension (MOVSX regX, reg/memX)\r
+  //\r
+  case 0xBE:\r
+    Bytes = 1;\r
+    //\r
+    // fall through\r
+    //\r
+  case 0xBF:\r
+    Bytes = (Bytes != 0) ? Bytes : 2;\r
+\r
+    ExitInfo1 = InstructionData->Ext.RmData;\r
+    ExitInfo2 = Bytes;\r
+\r
+    Ghcb->SaveArea.SwScratch = (UINT64) Ghcb->SharedBuffer;\r
+    VmgSetOffsetValid (Ghcb, GhcbSwScratch);\r
+    Status = VmgExit (Ghcb, SVM_EXIT_MMIO_READ, ExitInfo1, ExitInfo2);\r
+    if (Status != 0) {\r
+      return Status;\r
+    }\r
+\r
+    if (Bytes == 1) {\r
+      UINT8 *Data;\r
+\r
+      Data = (UINT8 *) Ghcb->SharedBuffer;\r
+      SignByte = ((*Data & BIT7) != 0) ? 0xFF : 0x00;\r
+    } else {\r
+      UINT16 *Data;\r
+\r
+      Data = (UINT16 *) Ghcb->SharedBuffer;\r
+      SignByte = ((*Data & BIT15) != 0) ? 0xFF : 0x00;\r
+    }\r
+\r
+    Register = GetRegisterPointer (Regs, InstructionData->Ext.ModRm.Reg);\r
+    SetMem (Register, InstructionData->DataSize, SignByte);\r
+    CopyMem (Register, Ghcb->SharedBuffer, Bytes);\r
+    break;\r
+\r
+  default:\r
+    Status = GP_EXCEPTION;\r
+    ASSERT (FALSE);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Handle a MWAIT event.\r
+\r
+  Use the VMGEXIT instruction to handle a MWAIT event.\r
+\r
+  @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication\r
+                                   Block\r
+  @param[in, out] Regs             x64 processor context\r
+  @param[in]      InstructionData  Instruction parsing context\r
+\r
+  @retval 0                        Event handled successfully\r
+  @return                          New exception value to propagate\r
+\r
+**/\r
+STATIC\r
+UINT64\r
+MwaitExit (\r
+  IN OUT GHCB                     *Ghcb,\r
+  IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,\r
+  IN     SEV_ES_INSTRUCTION_DATA  *InstructionData\r
+  )\r
+{\r
+  DecodeModRm (Regs, InstructionData);\r
+\r
+  Ghcb->SaveArea.Rax = Regs->Rax;\r
+  VmgSetOffsetValid (Ghcb, GhcbRax);\r
+  Ghcb->SaveArea.Rcx = Regs->Rcx;\r
+  VmgSetOffsetValid (Ghcb, GhcbRcx);\r
+\r
+  return VmgExit (Ghcb, SVM_EXIT_MWAIT, 0, 0);\r
+}\r
+\r
+/**\r
+  Handle a MONITOR event.\r
+\r
+  Use the VMGEXIT instruction to handle a MONITOR event.\r
+\r
+  @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication\r
+                                   Block\r
+  @param[in, out] Regs             x64 processor context\r
+  @param[in]      InstructionData  Instruction parsing context\r
+\r
+  @retval 0                        Event handled successfully\r
+  @return                          New exception value to propagate\r
+\r
+**/\r
+STATIC\r
+UINT64\r
+MonitorExit (\r
+  IN OUT GHCB                     *Ghcb,\r
+  IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,\r
+  IN     SEV_ES_INSTRUCTION_DATA  *InstructionData\r
+  )\r
+{\r
+  DecodeModRm (Regs, InstructionData);\r
+\r
+  Ghcb->SaveArea.Rax = Regs->Rax;  // Identity mapped, so VA = PA\r
+  VmgSetOffsetValid (Ghcb, GhcbRax);\r
+  Ghcb->SaveArea.Rcx = Regs->Rcx;\r
+  VmgSetOffsetValid (Ghcb, GhcbRcx);\r
+  Ghcb->SaveArea.Rdx = Regs->Rdx;\r
+  VmgSetOffsetValid (Ghcb, GhcbRdx);\r
+\r
+  return VmgExit (Ghcb, SVM_EXIT_MONITOR, 0, 0);\r
+}\r
+\r
+/**\r
+  Handle a WBINVD event.\r
+\r
+  Use the VMGEXIT instruction to handle a WBINVD event.\r
+\r
+  @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication\r
+                                   Block\r
+  @param[in, out] Regs             x64 processor context\r
+  @param[in]      InstructionData  Instruction parsing context\r
+\r
+  @retval 0                        Event handled successfully\r
+  @return                          New exception value to propagate\r
+\r
+**/\r
+STATIC\r
+UINT64\r
+WbinvdExit (\r
+  IN OUT GHCB                     *Ghcb,\r
+  IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,\r
+  IN     SEV_ES_INSTRUCTION_DATA  *InstructionData\r
+  )\r
+{\r
+  return VmgExit (Ghcb, SVM_EXIT_WBINVD, 0, 0);\r
+}\r
+\r
+/**\r
+  Handle a RDTSCP event.\r
+\r
+  Use the VMGEXIT instruction to handle a RDTSCP event.\r
+\r
+  @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication\r
+                                   Block\r
+  @param[in, out] Regs             x64 processor context\r
+  @param[in]      InstructionData  Instruction parsing context\r
+\r
+  @retval 0                        Event handled successfully\r
+  @return                          New exception value to propagate\r
+\r
+**/\r
+STATIC\r
+UINT64\r
+RdtscpExit (\r
+  IN OUT GHCB                     *Ghcb,\r
+  IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,\r
+  IN     SEV_ES_INSTRUCTION_DATA  *InstructionData\r
+  )\r
+{\r
+  UINT64  Status;\r
+\r
+  DecodeModRm (Regs, InstructionData);\r
+\r
+  Status = VmgExit (Ghcb, SVM_EXIT_RDTSCP, 0, 0);\r
+  if (Status != 0) {\r
+    return Status;\r
+  }\r
+\r
+  if (!VmgIsOffsetValid (Ghcb, GhcbRax) ||\r
+      !VmgIsOffsetValid (Ghcb, GhcbRcx) ||\r
+      !VmgIsOffsetValid (Ghcb, GhcbRdx)) {\r
+    return UnsupportedExit (Ghcb, Regs, InstructionData);\r
+  }\r
+  Regs->Rax = Ghcb->SaveArea.Rax;\r
+  Regs->Rcx = Ghcb->SaveArea.Rcx;\r
+  Regs->Rdx = Ghcb->SaveArea.Rdx;\r
+\r
+  return 0;\r
+}\r
+\r
+/**\r
+  Handle a VMMCALL event.\r
+\r
+  Use the VMGEXIT instruction to handle a VMMCALL event.\r
+\r
+  @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication\r
+                                   Block\r
+  @param[in, out] Regs             x64 processor context\r
+  @param[in]      InstructionData  Instruction parsing context\r
+\r
+  @retval 0                        Event handled successfully\r
+  @return                          New exception value to propagate\r
+\r
+**/\r
+STATIC\r
+UINT64\r
+VmmCallExit (\r
+  IN OUT GHCB                     *Ghcb,\r
+  IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,\r
+  IN     SEV_ES_INSTRUCTION_DATA  *InstructionData\r
+  )\r
+{\r
+  UINT64  Status;\r
+\r
+  DecodeModRm (Regs, InstructionData);\r
+\r
+  Ghcb->SaveArea.Rax = Regs->Rax;\r
+  VmgSetOffsetValid (Ghcb, GhcbRax);\r
+  Ghcb->SaveArea.Cpl = (UINT8) (Regs->Cs & 0x3);\r
+  VmgSetOffsetValid (Ghcb, GhcbCpl);\r
+\r
+  Status = VmgExit (Ghcb, SVM_EXIT_VMMCALL, 0, 0);\r
+  if (Status != 0) {\r
+    return Status;\r
+  }\r
+\r
+  if (!VmgIsOffsetValid (Ghcb, GhcbRax)) {\r
+    return UnsupportedExit (Ghcb, Regs, InstructionData);\r
+  }\r
+  Regs->Rax = Ghcb->SaveArea.Rax;\r
+\r
+  return 0;\r
 }\r
 \r
 /**\r
-  Report an unsupported event to the hypervisor\r
+  Handle an MSR event.\r
 \r
-  Use the VMGEXIT support to report an unsupported event to the hypervisor.\r
+  Use the VMGEXIT instruction to handle either a RDMSR or WRMSR event.\r
 \r
-  @param[in] Ghcb             Pointer to the Guest-Hypervisor Communication\r
-                              Block\r
-  @param[in] Regs             x64 processor context\r
-  @param[in] InstructionData  Instruction parsing context\r
+  @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication\r
+                                   Block\r
+  @param[in, out] Regs             x64 processor context\r
+  @param[in]      InstructionData  Instruction parsing context\r
 \r
-  @return                     New exception value to propagate\r
+  @retval 0                        Event handled successfully\r
+  @return                          New exception value to propagate\r
 \r
 **/\r
 STATIC\r
 UINT64\r
-UnsupportedExit (\r
-  IN GHCB                     *Ghcb,\r
-  IN EFI_SYSTEM_CONTEXT_X64   *Regs,\r
-  IN SEV_ES_INSTRUCTION_DATA  *InstructionData\r
+MsrExit (\r
+  IN OUT GHCB                     *Ghcb,\r
+  IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,\r
+  IN     SEV_ES_INSTRUCTION_DATA  *InstructionData\r
   )\r
 {\r
-  UINT64  Status;\r
+  UINT64  ExitInfo1, Status;\r
 \r
-  Status = VmgExit (Ghcb, SVM_EXIT_UNSUPPORTED, Regs->ExceptionData, 0);\r
-  if (Status == 0) {\r
-    GHCB_EVENT_INJECTION  Event;\r
+  ExitInfo1 = 0;\r
 \r
-    Event.Uint64 = 0;\r
-    Event.Elements.Vector = GP_EXCEPTION;\r
-    Event.Elements.Type   = GHCB_EVENT_INJECTION_TYPE_EXCEPTION;\r
-    Event.Elements.Valid  = 1;\r
+  switch (*(InstructionData->OpCodes + 1)) {\r
+  case 0x30: // WRMSR\r
+    ExitInfo1 = 1;\r
+    Ghcb->SaveArea.Rax = Regs->Rax;\r
+    VmgSetOffsetValid (Ghcb, GhcbRax);\r
+    Ghcb->SaveArea.Rdx = Regs->Rdx;\r
+    VmgSetOffsetValid (Ghcb, GhcbRdx);\r
+    //\r
+    // fall through\r
+    //\r
+  case 0x32: // RDMSR\r
+    Ghcb->SaveArea.Rcx = Regs->Rcx;\r
+    VmgSetOffsetValid (Ghcb, GhcbRcx);\r
+    break;\r
+  default:\r
+    return UnsupportedExit (Ghcb, Regs, InstructionData);\r
+  }\r
 \r
-    Status = Event.Uint64;\r
+  Status = VmgExit (Ghcb, SVM_EXIT_MSR, ExitInfo1, 0);\r
+  if (Status != 0) {\r
+    return Status;\r
   }\r
 \r
-  return Status;\r
+  if (ExitInfo1 == 0) {\r
+    if (!VmgIsOffsetValid (Ghcb, GhcbRax) ||\r
+        !VmgIsOffsetValid (Ghcb, GhcbRdx)) {\r
+      return UnsupportedExit (Ghcb, Regs, InstructionData);\r
+    }\r
+    Regs->Rax = Ghcb->SaveArea.Rax;\r
+    Regs->Rdx = Ghcb->SaveArea.Rdx;\r
+  }\r
+\r
+  return 0;\r
 }\r
 \r
 /**\r
@@ -557,6 +1238,7 @@ IoioExit (
       }\r
 \r
       Ghcb->SaveArea.SwScratch = (UINT64) Ghcb->SharedBuffer;\r
+      VmgSetOffsetValid (Ghcb, GhcbSwScratch);\r
       Status = VmgExit (Ghcb, SVM_EXIT_IOIO_PROT, ExitInfo1, ExitInfo2);\r
       if (Status != 0) {\r
         return Status;\r
@@ -579,7 +1261,7 @@ IoioExit (
     } else {\r
       CopyMem (&Ghcb->SaveArea.Rax, &Regs->Rax, IOIO_DATA_BYTES (ExitInfo1));\r
     }\r
-    GhcbSetRegValid (Ghcb, GhcbRax);\r
+    VmgSetOffsetValid (Ghcb, GhcbRax);\r
 \r
     Status = VmgExit (Ghcb, SVM_EXIT_IOIO_PROT, ExitInfo1, 0);\r
     if (Status != 0) {\r
@@ -587,7 +1269,7 @@ IoioExit (
     }\r
 \r
     if ((ExitInfo1 & IOIO_TYPE_IN) != 0) {\r
-      if (!GhcbIsRegValid (Ghcb, GhcbRax)) {\r
+      if (!VmgIsOffsetValid (Ghcb, GhcbRax)) {\r
         return UnsupportedExit (Ghcb, Regs, InstructionData);\r
       }\r
       CopyMem (&Regs->Rax, &Ghcb->SaveArea.Rax, IOIO_DATA_BYTES (ExitInfo1));\r
@@ -597,6 +1279,265 @@ IoioExit (
   return 0;\r
 }\r
 \r
+/**\r
+  Handle a INVD event.\r
+\r
+  Use the VMGEXIT instruction to handle a INVD event.\r
+\r
+  @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication\r
+                                   Block\r
+  @param[in, out] Regs             x64 processor context\r
+  @param[in]      InstructionData  Instruction parsing context\r
+\r
+  @retval 0                        Event handled successfully\r
+  @return                          New exception value to propagate\r
+\r
+**/\r
+STATIC\r
+UINT64\r
+InvdExit (\r
+  IN OUT GHCB                     *Ghcb,\r
+  IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,\r
+  IN     SEV_ES_INSTRUCTION_DATA  *InstructionData\r
+  )\r
+{\r
+  return VmgExit (Ghcb, SVM_EXIT_INVD, 0, 0);\r
+}\r
+\r
+/**\r
+  Handle a CPUID event.\r
+\r
+  Use the VMGEXIT instruction to handle a CPUID event.\r
+\r
+  @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication\r
+                                   Block\r
+  @param[in, out] Regs             x64 processor context\r
+  @param[in]      InstructionData  Instruction parsing context\r
+\r
+  @retval 0                        Event handled successfully\r
+  @return                          New exception value to propagate\r
+\r
+**/\r
+STATIC\r
+UINT64\r
+CpuidExit (\r
+  IN OUT GHCB                     *Ghcb,\r
+  IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,\r
+  IN     SEV_ES_INSTRUCTION_DATA  *InstructionData\r
+  )\r
+{\r
+  UINT64  Status;\r
+\r
+  Ghcb->SaveArea.Rax = Regs->Rax;\r
+  VmgSetOffsetValid (Ghcb, GhcbRax);\r
+  Ghcb->SaveArea.Rcx = Regs->Rcx;\r
+  VmgSetOffsetValid (Ghcb, GhcbRcx);\r
+  if (Regs->Rax == CPUID_EXTENDED_STATE) {\r
+    IA32_CR4  Cr4;\r
+\r
+    Cr4.UintN = AsmReadCr4 ();\r
+    Ghcb->SaveArea.XCr0 = (Cr4.Bits.OSXSAVE == 1) ? AsmXGetBv (0) : 1;\r
+    VmgSetOffsetValid (Ghcb, GhcbXCr0);\r
+  }\r
+\r
+  Status = VmgExit (Ghcb, SVM_EXIT_CPUID, 0, 0);\r
+  if (Status != 0) {\r
+    return Status;\r
+  }\r
+\r
+  if (!VmgIsOffsetValid (Ghcb, GhcbRax) ||\r
+      !VmgIsOffsetValid (Ghcb, GhcbRbx) ||\r
+      !VmgIsOffsetValid (Ghcb, GhcbRcx) ||\r
+      !VmgIsOffsetValid (Ghcb, GhcbRdx)) {\r
+    return UnsupportedExit (Ghcb, Regs, InstructionData);\r
+  }\r
+  Regs->Rax = Ghcb->SaveArea.Rax;\r
+  Regs->Rbx = Ghcb->SaveArea.Rbx;\r
+  Regs->Rcx = Ghcb->SaveArea.Rcx;\r
+  Regs->Rdx = Ghcb->SaveArea.Rdx;\r
+\r
+  return 0;\r
+}\r
+\r
+/**\r
+  Handle a RDPMC event.\r
+\r
+  Use the VMGEXIT instruction to handle a RDPMC event.\r
+\r
+  @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication\r
+                                   Block\r
+  @param[in, out] Regs             x64 processor context\r
+  @param[in]      InstructionData  Instruction parsing context\r
+\r
+  @retval 0                        Event handled successfully\r
+  @return                          New exception value to propagate\r
+\r
+**/\r
+STATIC\r
+UINT64\r
+RdpmcExit (\r
+  IN OUT GHCB                     *Ghcb,\r
+  IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,\r
+  IN     SEV_ES_INSTRUCTION_DATA  *InstructionData\r
+  )\r
+{\r
+  UINT64  Status;\r
+\r
+  Ghcb->SaveArea.Rcx = Regs->Rcx;\r
+  VmgSetOffsetValid (Ghcb, GhcbRcx);\r
+\r
+  Status = VmgExit (Ghcb, SVM_EXIT_RDPMC, 0, 0);\r
+  if (Status != 0) {\r
+    return Status;\r
+  }\r
+\r
+  if (!VmgIsOffsetValid (Ghcb, GhcbRax) ||\r
+      !VmgIsOffsetValid (Ghcb, GhcbRdx)) {\r
+    return UnsupportedExit (Ghcb, Regs, InstructionData);\r
+  }\r
+  Regs->Rax = Ghcb->SaveArea.Rax;\r
+  Regs->Rdx = Ghcb->SaveArea.Rdx;\r
+\r
+  return 0;\r
+}\r
+\r
+/**\r
+  Handle a RDTSC event.\r
+\r
+  Use the VMGEXIT instruction to handle a RDTSC event.\r
+\r
+  @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication\r
+                                   Block\r
+  @param[in, out] Regs             x64 processor context\r
+  @param[in]      InstructionData  Instruction parsing context\r
+\r
+  @retval 0                        Event handled successfully\r
+  @return                          New exception value to propagate\r
+\r
+**/\r
+STATIC\r
+UINT64\r
+RdtscExit (\r
+  IN OUT GHCB                     *Ghcb,\r
+  IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,\r
+  IN     SEV_ES_INSTRUCTION_DATA  *InstructionData\r
+  )\r
+{\r
+  UINT64  Status;\r
+\r
+  Status = VmgExit (Ghcb, SVM_EXIT_RDTSC, 0, 0);\r
+  if (Status != 0) {\r
+    return Status;\r
+  }\r
+\r
+  if (!VmgIsOffsetValid (Ghcb, GhcbRax) ||\r
+      !VmgIsOffsetValid (Ghcb, GhcbRdx)) {\r
+    return UnsupportedExit (Ghcb, Regs, InstructionData);\r
+  }\r
+  Regs->Rax = Ghcb->SaveArea.Rax;\r
+  Regs->Rdx = Ghcb->SaveArea.Rdx;\r
+\r
+  return 0;\r
+}\r
+\r
+/**\r
+  Handle a DR7 register write event.\r
+\r
+  Use the VMGEXIT instruction to handle a DR7 write event.\r
+\r
+  @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication\r
+                                   Block\r
+  @param[in, out] Regs             x64 processor context\r
+  @param[in]      InstructionData  Instruction parsing context\r
+\r
+  @retval 0                        Event handled successfully\r
+  @return                          New exception value to propagate\r
+\r
+**/\r
+STATIC\r
+UINT64\r
+Dr7WriteExit (\r
+  IN OUT GHCB                     *Ghcb,\r
+  IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,\r
+  IN     SEV_ES_INSTRUCTION_DATA  *InstructionData\r
+  )\r
+{\r
+  SEV_ES_INSTRUCTION_OPCODE_EXT  *Ext;\r
+  SEV_ES_PER_CPU_DATA            *SevEsData;\r
+  UINT64                         *Register;\r
+  UINT64                         Status;\r
+\r
+  Ext = &InstructionData->Ext;\r
+  SevEsData = (SEV_ES_PER_CPU_DATA *) (Ghcb + 1);\r
+\r
+  DecodeModRm (Regs, InstructionData);\r
+\r
+  //\r
+  // MOV DRn always treats MOD == 3 no matter how encoded\r
+  //\r
+  Register = GetRegisterPointer (Regs, Ext->ModRm.Rm);\r
+\r
+  //\r
+  // Using a value of 0 for ExitInfo1 means RAX holds the value\r
+  //\r
+  Ghcb->SaveArea.Rax = *Register;\r
+  VmgSetOffsetValid (Ghcb, GhcbRax);\r
+\r
+  Status = VmgExit (Ghcb, SVM_EXIT_DR7_WRITE, 0, 0);\r
+  if (Status != 0) {\r
+    return Status;\r
+  }\r
+\r
+  SevEsData->Dr7 = *Register;\r
+  SevEsData->Dr7Cached = TRUE;\r
+\r
+  return 0;\r
+}\r
+\r
+/**\r
+  Handle a DR7 register read event.\r
+\r
+  Use the VMGEXIT instruction to handle a DR7 read event.\r
+\r
+  @param[in, out] Ghcb             Pointer to the Guest-Hypervisor Communication\r
+                                   Block\r
+  @param[in, out] Regs             x64 processor context\r
+  @param[in]      InstructionData  Instruction parsing context\r
+\r
+  @retval 0                        Event handled successfully\r
+\r
+**/\r
+STATIC\r
+UINT64\r
+Dr7ReadExit (\r
+  IN OUT GHCB                     *Ghcb,\r
+  IN OUT EFI_SYSTEM_CONTEXT_X64   *Regs,\r
+  IN     SEV_ES_INSTRUCTION_DATA  *InstructionData\r
+  )\r
+{\r
+  SEV_ES_INSTRUCTION_OPCODE_EXT  *Ext;\r
+  SEV_ES_PER_CPU_DATA            *SevEsData;\r
+  UINT64                         *Register;\r
+\r
+  Ext = &InstructionData->Ext;\r
+  SevEsData = (SEV_ES_PER_CPU_DATA *) (Ghcb + 1);\r
+\r
+  DecodeModRm (Regs, InstructionData);\r
+\r
+  //\r
+  // MOV DRn always treats MOD == 3 no matter how encoded\r
+  //\r
+  Register = GetRegisterPointer (Regs, Ext->ModRm.Rm);\r
+\r
+  //\r
+  // If there is a cached valued for DR7, return that. Otherwise return the\r
+  // DR7 standard reset value of 0x400 (no debug breakpoints set).\r
+  //\r
+  *Register = (SevEsData->Dr7Cached) ? SevEsData->Dr7 : 0x400;\r
+\r
+  return 0;\r
+}\r
+\r
 /**\r
   Handle a #VC exception.\r
 \r
@@ -641,10 +1582,62 @@ VmgExitHandleVc (
 \r
   ExitCode = Regs->ExceptionData;\r
   switch (ExitCode) {\r
+  case SVM_EXIT_DR7_READ:\r
+    NaeExit = Dr7ReadExit;\r
+    break;\r
+\r
+  case SVM_EXIT_DR7_WRITE:\r
+    NaeExit = Dr7WriteExit;\r
+    break;\r
+\r
+  case SVM_EXIT_RDTSC:\r
+    NaeExit = RdtscExit;\r
+    break;\r
+\r
+  case SVM_EXIT_RDPMC:\r
+    NaeExit = RdpmcExit;\r
+    break;\r
+\r
+  case SVM_EXIT_CPUID:\r
+    NaeExit = CpuidExit;\r
+    break;\r
+\r
+  case SVM_EXIT_INVD:\r
+    NaeExit = InvdExit;\r
+    break;\r
+\r
   case SVM_EXIT_IOIO_PROT:\r
     NaeExit = IoioExit;\r
     break;\r
 \r
+  case SVM_EXIT_MSR:\r
+    NaeExit = MsrExit;\r
+    break;\r
+\r
+  case SVM_EXIT_VMMCALL:\r
+    NaeExit = VmmCallExit;\r
+    break;\r
+\r
+  case SVM_EXIT_RDTSCP:\r
+    NaeExit = RdtscpExit;\r
+    break;\r
+\r
+  case SVM_EXIT_WBINVD:\r
+    NaeExit = WbinvdExit;\r
+    break;\r
+\r
+  case SVM_EXIT_MONITOR:\r
+    NaeExit = MonitorExit;\r
+    break;\r
+\r
+  case SVM_EXIT_MWAIT:\r
+    NaeExit = MwaitExit;\r
+    break;\r
+\r
+  case SVM_EXIT_NPF:\r
+    NaeExit = MmioExit;\r
+    break;\r
+\r
   default:\r
     NaeExit = UnsupportedExit;\r
   }\r