#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
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
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
}\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
} 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
}\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
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
\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