summaryrefslogtreecommitdiff
path: root/OvmfPkg/Library/CcExitLib/CcExitVcHandler.c
diff options
context:
space:
mode:
Diffstat (limited to 'OvmfPkg/Library/CcExitLib/CcExitVcHandler.c')
-rw-r--r--OvmfPkg/Library/CcExitLib/CcExitVcHandler.c2355
1 files changed, 2355 insertions, 0 deletions
diff --git a/OvmfPkg/Library/CcExitLib/CcExitVcHandler.c b/OvmfPkg/Library/CcExitLib/CcExitVcHandler.c
new file mode 100644
index 0000000..ad2a922
--- /dev/null
+++ b/OvmfPkg/Library/CcExitLib/CcExitVcHandler.c
@@ -0,0 +1,2355 @@
+/** @file
+ X64 #VC Exception Handler functon.
+
+ Copyright (C) 2020, Advanced Micro Devices, Inc. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Base.h>
+#include <Uefi.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/LocalApicLib.h>
+#include <Library/MemEncryptSevLib.h>
+#include <Library/CcExitLib.h>
+#include <Register/Amd/Msr.h>
+#include <Register/Intel/Cpuid.h>
+#include <IndustryStandard/InstructionParsing.h>
+
+#include "CcExitVcHandler.h"
+
+//
+// Instruction execution mode definition
+//
+typedef enum {
+ LongMode64Bit = 0,
+ LongModeCompat32Bit,
+ LongModeCompat16Bit,
+} SEV_ES_INSTRUCTION_MODE;
+
+//
+// Instruction size definition (for operand and address)
+//
+typedef enum {
+ Size8Bits = 0,
+ Size16Bits,
+ Size32Bits,
+ Size64Bits,
+} SEV_ES_INSTRUCTION_SIZE;
+
+//
+// Intruction segment definition
+//
+typedef enum {
+ SegmentEs = 0,
+ SegmentCs,
+ SegmentSs,
+ SegmentDs,
+ SegmentFs,
+ SegmentGs,
+} SEV_ES_INSTRUCTION_SEGMENT;
+
+//
+// Instruction rep function definition
+//
+typedef enum {
+ RepNone = 0,
+ RepZ,
+ RepNZ,
+} SEV_ES_INSTRUCTION_REP;
+
+typedef struct {
+ UINT8 Rm;
+ UINT8 Reg;
+ UINT8 Mod;
+} SEV_ES_INSTRUCTION_MODRM_EXT;
+
+typedef struct {
+ UINT8 Base;
+ UINT8 Index;
+ UINT8 Scale;
+} SEV_ES_INSTRUCTION_SIB_EXT;
+
+//
+// Instruction opcode definition
+//
+typedef struct {
+ SEV_ES_INSTRUCTION_MODRM_EXT ModRm;
+
+ SEV_ES_INSTRUCTION_SIB_EXT Sib;
+
+ UINTN RegData;
+ UINTN RmData;
+} SEV_ES_INSTRUCTION_OPCODE_EXT;
+
+//
+// Instruction parsing context definition
+//
+typedef struct {
+ GHCB *Ghcb;
+
+ SEV_ES_INSTRUCTION_MODE Mode;
+ SEV_ES_INSTRUCTION_SIZE DataSize;
+ SEV_ES_INSTRUCTION_SIZE AddrSize;
+ BOOLEAN SegmentSpecified;
+ SEV_ES_INSTRUCTION_SEGMENT Segment;
+ SEV_ES_INSTRUCTION_REP RepMode;
+
+ UINT8 *Begin;
+ UINT8 *End;
+
+ UINT8 *Prefixes;
+ UINT8 *OpCodes;
+ UINT8 *Displacement;
+ UINT8 *Immediate;
+
+ INSTRUCTION_REX_PREFIX RexPrefix;
+
+ BOOLEAN ModRmPresent;
+ INSTRUCTION_MODRM ModRm;
+
+ BOOLEAN SibPresent;
+ INSTRUCTION_SIB Sib;
+
+ UINTN PrefixSize;
+ UINTN OpCodeSize;
+ UINTN DisplacementSize;
+ UINTN ImmediateSize;
+
+ SEV_ES_INSTRUCTION_OPCODE_EXT Ext;
+} SEV_ES_INSTRUCTION_DATA;
+
+//
+// Non-automatic Exit function prototype
+//
+typedef
+UINT64
+(*NAE_EXIT) (
+ GHCB *Ghcb,
+ EFI_SYSTEM_CONTEXT_X64 *Regs,
+ SEV_ES_INSTRUCTION_DATA *InstructionData
+ );
+
+//
+// SEV-SNP Cpuid table entry/function
+//
+typedef PACKED struct {
+ UINT32 EaxIn;
+ UINT32 EcxIn;
+ UINT64 Unused;
+ UINT64 Unused2;
+ UINT32 Eax;
+ UINT32 Ebx;
+ UINT32 Ecx;
+ UINT32 Edx;
+ UINT64 Reserved;
+} SEV_SNP_CPUID_FUNCTION;
+
+//
+// SEV-SNP Cpuid page format
+//
+typedef PACKED struct {
+ UINT32 Count;
+ UINT32 Reserved1;
+ UINT64 Reserved2;
+ SEV_SNP_CPUID_FUNCTION function[0];
+} SEV_SNP_CPUID_INFO;
+
+/**
+ Return a pointer to the contents of the specified register.
+
+ Based upon the input register, return a pointer to the registers contents
+ in the x86 processor context.
+
+ @param[in] Regs x64 processor context
+ @param[in] Register Register to obtain pointer for
+
+ @return Pointer to the contents of the requested register
+
+**/
+STATIC
+UINT64 *
+GetRegisterPointer (
+ IN EFI_SYSTEM_CONTEXT_X64 *Regs,
+ IN UINT8 Register
+ )
+{
+ UINT64 *Reg;
+
+ switch (Register) {
+ case 0:
+ Reg = &Regs->Rax;
+ break;
+ case 1:
+ Reg = &Regs->Rcx;
+ break;
+ case 2:
+ Reg = &Regs->Rdx;
+ break;
+ case 3:
+ Reg = &Regs->Rbx;
+ break;
+ case 4:
+ Reg = &Regs->Rsp;
+ break;
+ case 5:
+ Reg = &Regs->Rbp;
+ break;
+ case 6:
+ Reg = &Regs->Rsi;
+ break;
+ case 7:
+ Reg = &Regs->Rdi;
+ break;
+ case 8:
+ Reg = &Regs->R8;
+ break;
+ case 9:
+ Reg = &Regs->R9;
+ break;
+ case 10:
+ Reg = &Regs->R10;
+ break;
+ case 11:
+ Reg = &Regs->R11;
+ break;
+ case 12:
+ Reg = &Regs->R12;
+ break;
+ case 13:
+ Reg = &Regs->R13;
+ break;
+ case 14:
+ Reg = &Regs->R14;
+ break;
+ case 15:
+ Reg = &Regs->R15;
+ break;
+ default:
+ Reg = NULL;
+ }
+
+ ASSERT (Reg != NULL);
+
+ return Reg;
+}
+
+/**
+ Update the instruction parsing context for displacement bytes.
+
+ @param[in, out] InstructionData Instruction parsing context
+ @param[in] Size The instruction displacement size
+
+**/
+STATIC
+VOID
+UpdateForDisplacement (
+ IN OUT SEV_ES_INSTRUCTION_DATA *InstructionData,
+ IN UINTN Size
+ )
+{
+ InstructionData->DisplacementSize = Size;
+ InstructionData->Immediate += Size;
+ InstructionData->End += Size;
+}
+
+/**
+ Determine if an instruction address if RIP relative.
+
+ Examine the instruction parsing context to determine if the address offset
+ is relative to the instruction pointer.
+
+ @param[in] InstructionData Instruction parsing context
+
+ @retval TRUE Instruction addressing is RIP relative
+ @retval FALSE Instruction addressing is not RIP relative
+
+**/
+STATIC
+BOOLEAN
+IsRipRelative (
+ IN SEV_ES_INSTRUCTION_DATA *InstructionData
+ )
+{
+ SEV_ES_INSTRUCTION_OPCODE_EXT *Ext;
+
+ Ext = &InstructionData->Ext;
+
+ return ((InstructionData->Mode == LongMode64Bit) &&
+ (Ext->ModRm.Mod == 0) &&
+ (Ext->ModRm.Rm == 5) &&
+ (InstructionData->SibPresent == FALSE));
+}
+
+/**
+ Return the effective address of a memory operand.
+
+ Examine the instruction parsing context to obtain the effective memory
+ address of a memory operand.
+
+ @param[in] Regs x64 processor context
+ @param[in] InstructionData Instruction parsing context
+
+ @return The memory operand effective address
+
+**/
+STATIC
+UINT64
+GetEffectiveMemoryAddress (
+ IN EFI_SYSTEM_CONTEXT_X64 *Regs,
+ IN SEV_ES_INSTRUCTION_DATA *InstructionData
+ )
+{
+ SEV_ES_INSTRUCTION_OPCODE_EXT *Ext;
+ UINT64 EffectiveAddress;
+
+ Ext = &InstructionData->Ext;
+ EffectiveAddress = 0;
+
+ if (IsRipRelative (InstructionData)) {
+ //
+ // RIP-relative displacement is a 32-bit signed value
+ //
+ INT32 RipRelative;
+
+ RipRelative = *(INT32 *)InstructionData->Displacement;
+
+ UpdateForDisplacement (InstructionData, 4);
+
+ //
+ // Negative displacement is handled by standard UINT64 wrap-around.
+ //
+ return Regs->Rip + (UINT64)RipRelative;
+ }
+
+ switch (Ext->ModRm.Mod) {
+ case 1:
+ UpdateForDisplacement (InstructionData, 1);
+ EffectiveAddress += (UINT64)(*(INT8 *)(InstructionData->Displacement));
+ break;
+ case 2:
+ switch (InstructionData->AddrSize) {
+ case Size16Bits:
+ UpdateForDisplacement (InstructionData, 2);
+ EffectiveAddress += (UINT64)(*(INT16 *)(InstructionData->Displacement));
+ break;
+ default:
+ UpdateForDisplacement (InstructionData, 4);
+ EffectiveAddress += (UINT64)(*(INT32 *)(InstructionData->Displacement));
+ break;
+ }
+
+ break;
+ }
+
+ if (InstructionData->SibPresent) {
+ INT64 Displacement;
+
+ if (Ext->Sib.Index != 4) {
+ CopyMem (
+ &Displacement,
+ GetRegisterPointer (Regs, Ext->Sib.Index),
+ sizeof (Displacement)
+ );
+ Displacement *= (INT64)(1 << Ext->Sib.Scale);
+
+ //
+ // Negative displacement is handled by standard UINT64 wrap-around.
+ //
+ EffectiveAddress += (UINT64)Displacement;
+ }
+
+ if ((Ext->Sib.Base != 5) || Ext->ModRm.Mod) {
+ EffectiveAddress += *GetRegisterPointer (Regs, Ext->Sib.Base);
+ } else {
+ UpdateForDisplacement (InstructionData, 4);
+ EffectiveAddress += (UINT64)(*(INT32 *)(InstructionData->Displacement));
+ }
+ } else {
+ EffectiveAddress += *GetRegisterPointer (Regs, Ext->ModRm.Rm);
+ }
+
+ return EffectiveAddress;
+}
+
+/**
+ Decode a ModRM byte.
+
+ Examine the instruction parsing context to decode a ModRM byte and the SIB
+ byte, if present.
+
+ @param[in] Regs x64 processor context
+ @param[in, out] InstructionData Instruction parsing context
+
+**/
+STATIC
+VOID
+DecodeModRm (
+ IN EFI_SYSTEM_CONTEXT_X64 *Regs,
+ IN OUT SEV_ES_INSTRUCTION_DATA *InstructionData
+ )
+{
+ SEV_ES_INSTRUCTION_OPCODE_EXT *Ext;
+ INSTRUCTION_REX_PREFIX *RexPrefix;
+ INSTRUCTION_MODRM *ModRm;
+ INSTRUCTION_SIB *Sib;
+
+ RexPrefix = &InstructionData->RexPrefix;
+ Ext = &InstructionData->Ext;
+ ModRm = &InstructionData->ModRm;
+ Sib = &InstructionData->Sib;
+
+ InstructionData->ModRmPresent = TRUE;
+ ModRm->Uint8 = *(InstructionData->End);
+
+ InstructionData->Displacement++;
+ InstructionData->Immediate++;
+ InstructionData->End++;
+
+ Ext->ModRm.Mod = ModRm->Bits.Mod;
+ Ext->ModRm.Reg = (RexPrefix->Bits.BitR << 3) | ModRm->Bits.Reg;
+ Ext->ModRm.Rm = (RexPrefix->Bits.BitB << 3) | ModRm->Bits.Rm;
+
+ Ext->RegData = *GetRegisterPointer (Regs, Ext->ModRm.Reg);
+
+ if (Ext->ModRm.Mod == 3) {
+ Ext->RmData = *GetRegisterPointer (Regs, Ext->ModRm.Rm);
+ } else {
+ if (ModRm->Bits.Rm == 4) {
+ InstructionData->SibPresent = TRUE;
+ Sib->Uint8 = *(InstructionData->End);
+
+ InstructionData->Displacement++;
+ InstructionData->Immediate++;
+ InstructionData->End++;
+
+ Ext->Sib.Scale = Sib->Bits.Scale;
+ Ext->Sib.Index = (RexPrefix->Bits.BitX << 3) | Sib->Bits.Index;
+ Ext->Sib.Base = (RexPrefix->Bits.BitB << 3) | Sib->Bits.Base;
+ }
+
+ Ext->RmData = GetEffectiveMemoryAddress (Regs, InstructionData);
+ }
+}
+
+/**
+ Decode instruction prefixes.
+
+ Parse the instruction data to track the instruction prefixes that have
+ been used.
+
+ @param[in] Regs x64 processor context
+ @param[in, out] InstructionData Instruction parsing context
+
+**/
+STATIC
+VOID
+DecodePrefixes (
+ IN EFI_SYSTEM_CONTEXT_X64 *Regs,
+ IN OUT SEV_ES_INSTRUCTION_DATA *InstructionData
+ )
+{
+ SEV_ES_INSTRUCTION_MODE Mode;
+ SEV_ES_INSTRUCTION_SIZE ModeDataSize;
+ SEV_ES_INSTRUCTION_SIZE ModeAddrSize;
+ UINT8 *Byte;
+
+ //
+ // Always in 64-bit mode
+ //
+ Mode = LongMode64Bit;
+ ModeDataSize = Size32Bits;
+ ModeAddrSize = Size64Bits;
+
+ InstructionData->Mode = Mode;
+ InstructionData->DataSize = ModeDataSize;
+ InstructionData->AddrSize = ModeAddrSize;
+
+ InstructionData->Prefixes = InstructionData->Begin;
+
+ Byte = InstructionData->Prefixes;
+ for ( ; ; Byte++, InstructionData->PrefixSize++) {
+ //
+ // Check the 0x40 to 0x4F range using an if statement here since some
+ // compilers don't like the "case 0x40 ... 0x4F:" syntax. This avoids
+ // 16 case statements below.
+ //
+ if ((*Byte >= REX_PREFIX_START) && (*Byte <= REX_PREFIX_STOP)) {
+ InstructionData->RexPrefix.Uint8 = *Byte;
+ if ((*Byte & REX_64BIT_OPERAND_SIZE_MASK) != 0) {
+ InstructionData->DataSize = Size64Bits;
+ }
+
+ continue;
+ }
+
+ switch (*Byte) {
+ case OVERRIDE_SEGMENT_CS:
+ case OVERRIDE_SEGMENT_DS:
+ case OVERRIDE_SEGMENT_ES:
+ case OVERRIDE_SEGMENT_SS:
+ if (Mode != LongMode64Bit) {
+ InstructionData->SegmentSpecified = TRUE;
+ InstructionData->Segment = (*Byte >> 3) & 3;
+ }
+
+ break;
+
+ case OVERRIDE_SEGMENT_FS:
+ case OVERRIDE_SEGMENT_GS:
+ InstructionData->SegmentSpecified = TRUE;
+ InstructionData->Segment = *Byte & 7;
+ break;
+
+ case OVERRIDE_OPERAND_SIZE:
+ if (InstructionData->RexPrefix.Uint8 == 0) {
+ InstructionData->DataSize =
+ (Mode == LongMode64Bit) ? Size16Bits :
+ (Mode == LongModeCompat32Bit) ? Size16Bits :
+ (Mode == LongModeCompat16Bit) ? Size32Bits : 0;
+ }
+
+ break;
+
+ case OVERRIDE_ADDRESS_SIZE:
+ InstructionData->AddrSize =
+ (Mode == LongMode64Bit) ? Size32Bits :
+ (Mode == LongModeCompat32Bit) ? Size16Bits :
+ (Mode == LongModeCompat16Bit) ? Size32Bits : 0;
+ break;
+
+ case LOCK_PREFIX:
+ break;
+
+ case REPZ_PREFIX:
+ InstructionData->RepMode = RepZ;
+ break;
+
+ case REPNZ_PREFIX:
+ InstructionData->RepMode = RepNZ;
+ break;
+
+ default:
+ InstructionData->OpCodes = Byte;
+ InstructionData->OpCodeSize = (*Byte == TWO_BYTE_OPCODE_ESCAPE) ? 2 : 1;
+
+ InstructionData->End = Byte + InstructionData->OpCodeSize;
+ InstructionData->Displacement = InstructionData->End;
+ InstructionData->Immediate = InstructionData->End;
+ return;
+ }
+ }
+}
+
+/**
+ Determine instruction length
+
+ Return the total length of the parsed instruction.
+
+ @param[in] InstructionData Instruction parsing context
+
+ @return Length of parsed instruction
+
+**/
+STATIC
+UINT64
+InstructionLength (
+ IN SEV_ES_INSTRUCTION_DATA *InstructionData
+ )
+{
+ return (UINT64)(InstructionData->End - InstructionData->Begin);
+}
+
+/**
+ Initialize the instruction parsing context.
+
+ Initialize the instruction parsing context, which includes decoding the
+ instruction prefixes.
+
+ @param[in, out] InstructionData Instruction parsing context
+ @param[in] Ghcb Pointer to the Guest-Hypervisor Communication
+ Block
+ @param[in] Regs x64 processor context
+
+**/
+STATIC
+VOID
+InitInstructionData (
+ IN OUT SEV_ES_INSTRUCTION_DATA *InstructionData,
+ IN GHCB *Ghcb,
+ IN EFI_SYSTEM_CONTEXT_X64 *Regs
+ )
+{
+ SetMem (InstructionData, sizeof (*InstructionData), 0);
+ InstructionData->Ghcb = Ghcb;
+ InstructionData->Begin = (UINT8 *)Regs->Rip;
+ InstructionData->End = (UINT8 *)Regs->Rip;
+
+ DecodePrefixes (Regs, InstructionData);
+}
+
+/**
+ Report an unsupported event to the hypervisor
+
+ Use the VMGEXIT support to report an unsupported event to the hypervisor.
+
+ @param[in] Ghcb Pointer to the Guest-Hypervisor Communication
+ Block
+ @param[in] Regs x64 processor context
+ @param[in] InstructionData Instruction parsing context
+
+ @return New exception value to propagate
+
+**/
+STATIC
+UINT64
+UnsupportedExit (
+ IN GHCB *Ghcb,
+ IN EFI_SYSTEM_CONTEXT_X64 *Regs,
+ IN SEV_ES_INSTRUCTION_DATA *InstructionData
+ )
+{
+ UINT64 Status;
+
+ Status = VmgExit (Ghcb, SVM_EXIT_UNSUPPORTED, Regs->ExceptionData, 0);
+ if (Status == 0) {
+ GHCB_EVENT_INJECTION Event;
+
+ Event.Uint64 = 0;
+ Event.Elements.Vector = GP_EXCEPTION;
+ Event.Elements.Type = GHCB_EVENT_INJECTION_TYPE_EXCEPTION;
+ Event.Elements.Valid = 1;
+
+ Status = Event.Uint64;
+ }
+
+ return Status;
+}
+
+/**
+ Validate that the MMIO memory access is not to encrypted memory.
+
+ Examine the pagetable entry for the memory specified. MMIO should not be
+ performed against encrypted memory. MMIO to the APIC page is always allowed.
+
+ @param[in] Ghcb Pointer to the Guest-Hypervisor Communication Block
+ @param[in] MemoryAddress Memory address to validate
+ @param[in] MemoryLength Memory length to validate
+
+ @retval 0 Memory is not encrypted
+ @return New exception value to propogate
+
+**/
+STATIC
+UINT64
+ValidateMmioMemory (
+ IN GHCB *Ghcb,
+ IN UINTN MemoryAddress,
+ IN UINTN MemoryLength
+ )
+{
+ MEM_ENCRYPT_SEV_ADDRESS_RANGE_STATE State;
+ GHCB_EVENT_INJECTION GpEvent;
+ UINTN Address;
+
+ //
+ // Allow APIC accesses (which will have the encryption bit set during
+ // SEC and PEI phases).
+ //
+ Address = MemoryAddress & ~(SIZE_4KB - 1);
+ if (Address == GetLocalApicBaseAddress ()) {
+ return 0;
+ }
+
+ State = MemEncryptSevGetAddressRangeState (
+ 0,
+ MemoryAddress,
+ MemoryLength
+ );
+ if (State == MemEncryptSevAddressRangeUnencrypted) {
+ return 0;
+ }
+
+ //
+ // Any state other than unencrypted is an error, issue a #GP.
+ //
+ DEBUG ((
+ DEBUG_ERROR,
+ "MMIO using encrypted memory: %lx\n",
+ (UINT64)MemoryAddress
+ ));
+ GpEvent.Uint64 = 0;
+ GpEvent.Elements.Vector = GP_EXCEPTION;
+ GpEvent.Elements.Type = GHCB_EVENT_INJECTION_TYPE_EXCEPTION;
+ GpEvent.Elements.Valid = 1;
+
+ return GpEvent.Uint64;
+}
+
+/**
+ Handle an MMIO event.
+
+ Use the VMGEXIT instruction to handle either an MMIO read or an MMIO write.
+
+ @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
+ Block
+ @param[in, out] Regs x64 processor context
+ @param[in, out] InstructionData Instruction parsing context
+
+ @retval 0 Event handled successfully
+ @return New exception value to propagate
+
+**/
+STATIC
+UINT64
+MmioExit (
+ IN OUT GHCB *Ghcb,
+ IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,
+ IN OUT SEV_ES_INSTRUCTION_DATA *InstructionData
+ )
+{
+ UINT64 ExitInfo1, ExitInfo2, Status;
+ UINTN Bytes;
+ UINT64 *Register;
+ UINT8 OpCode, SignByte;
+ UINTN Address;
+
+ Bytes = 0;
+
+ OpCode = *(InstructionData->OpCodes);
+ if (OpCode == TWO_BYTE_OPCODE_ESCAPE) {
+ OpCode = *(InstructionData->OpCodes + 1);
+ }
+
+ switch (OpCode) {
+ //
+ // MMIO write (MOV reg/memX, regX)
+ //
+ case 0x88:
+ Bytes = 1;
+ //
+ // fall through
+ //
+ case 0x89:
+ DecodeModRm (Regs, InstructionData);
+ Bytes = ((Bytes != 0) ? Bytes :
+ (InstructionData->DataSize == Size16Bits) ? 2 :
+ (InstructionData->DataSize == Size32Bits) ? 4 :
+ (InstructionData->DataSize == Size64Bits) ? 8 :
+ 0);
+
+ if (InstructionData->Ext.ModRm.Mod == 3) {
+ //
+ // NPF on two register operands???
+ //
+ return UnsupportedExit (Ghcb, Regs, InstructionData);
+ }
+
+ Status = ValidateMmioMemory (Ghcb, InstructionData->Ext.RmData, Bytes);
+ if (Status != 0) {
+ return Status;
+ }
+
+ ExitInfo1 = InstructionData->Ext.RmData;
+ ExitInfo2 = Bytes;
+ CopyMem (Ghcb->SharedBuffer, &InstructionData->Ext.RegData, Bytes);
+
+ Ghcb->SaveArea.SwScratch = (UINT64)Ghcb->SharedBuffer;
+ VmgSetOffsetValid (Ghcb, GhcbSwScratch);
+ Status = VmgExit (Ghcb, SVM_EXIT_MMIO_WRITE, ExitInfo1, ExitInfo2);
+ if (Status != 0) {
+ return Status;
+ }
+
+ break;
+
+ //
+ // MMIO write (MOV moffsetX, aX)
+ //
+ case 0xA2:
+ Bytes = 1;
+ //
+ // fall through
+ //
+ case 0xA3:
+ Bytes = ((Bytes != 0) ? Bytes :
+ (InstructionData->DataSize == Size16Bits) ? 2 :
+ (InstructionData->DataSize == Size32Bits) ? 4 :
+ (InstructionData->DataSize == Size64Bits) ? 8 :
+ 0);
+
+ InstructionData->ImmediateSize = (UINTN)(1 << InstructionData->AddrSize);
+ InstructionData->End += InstructionData->ImmediateSize;
+
+ //
+ // This code is X64 only, so a possible 8-byte copy to a UINTN is ok.
+ // Use a STATIC_ASSERT to be certain the code is being built as X64.
+ //
+ STATIC_ASSERT (
+ sizeof (UINTN) == sizeof (UINT64),
+ "sizeof (UINTN) != sizeof (UINT64), this file must be built as X64"
+ );
+
+ Address = 0;
+ CopyMem (
+ &Address,
+ InstructionData->Immediate,
+ InstructionData->ImmediateSize
+ );
+
+ Status = ValidateMmioMemory (Ghcb, Address, Bytes);
+ if (Status != 0) {
+ return Status;
+ }
+
+ ExitInfo1 = Address;
+ ExitInfo2 = Bytes;
+ CopyMem (Ghcb->SharedBuffer, &Regs->Rax, Bytes);
+
+ Ghcb->SaveArea.SwScratch = (UINT64)Ghcb->SharedBuffer;
+ VmgSetOffsetValid (Ghcb, GhcbSwScratch);
+ Status = VmgExit (Ghcb, SVM_EXIT_MMIO_WRITE, ExitInfo1, ExitInfo2);
+ if (Status != 0) {
+ return Status;
+ }
+
+ break;
+
+ //
+ // MMIO write (MOV reg/memX, immX)
+ //
+ case 0xC6:
+ Bytes = 1;
+ //
+ // fall through
+ //
+ case 0xC7:
+ DecodeModRm (Regs, InstructionData);
+ Bytes = ((Bytes != 0) ? Bytes :
+ (InstructionData->DataSize == Size16Bits) ? 2 :
+ (InstructionData->DataSize == Size32Bits) ? 4 :
+ 0);
+
+ InstructionData->ImmediateSize = Bytes;
+ InstructionData->End += Bytes;
+
+ Status = ValidateMmioMemory (Ghcb, InstructionData->Ext.RmData, Bytes);
+ if (Status != 0) {
+ return Status;
+ }
+
+ ExitInfo1 = InstructionData->Ext.RmData;
+ ExitInfo2 = Bytes;
+ CopyMem (Ghcb->SharedBuffer, InstructionData->Immediate, Bytes);
+
+ Ghcb->SaveArea.SwScratch = (UINT64)Ghcb->SharedBuffer;
+ VmgSetOffsetValid (Ghcb, GhcbSwScratch);
+ Status = VmgExit (Ghcb, SVM_EXIT_MMIO_WRITE, ExitInfo1, ExitInfo2);
+ if (Status != 0) {
+ return Status;
+ }
+
+ break;
+
+ //
+ // MMIO read (MOV regX, reg/memX)
+ //
+ case 0x8A:
+ Bytes = 1;
+ //
+ // fall through
+ //
+ case 0x8B:
+ DecodeModRm (Regs, InstructionData);
+ Bytes = ((Bytes != 0) ? Bytes :
+ (InstructionData->DataSize == Size16Bits) ? 2 :
+ (InstructionData->DataSize == Size32Bits) ? 4 :
+ (InstructionData->DataSize == Size64Bits) ? 8 :
+ 0);
+ if (InstructionData->Ext.ModRm.Mod == 3) {
+ //
+ // NPF on two register operands???
+ //
+ return UnsupportedExit (Ghcb, Regs, InstructionData);
+ }
+
+ Status = ValidateMmioMemory (Ghcb, InstructionData->Ext.RmData, Bytes);
+ if (Status != 0) {
+ return Status;
+ }
+
+ ExitInfo1 = InstructionData->Ext.RmData;
+ ExitInfo2 = Bytes;
+
+ Ghcb->SaveArea.SwScratch = (UINT64)Ghcb->SharedBuffer;
+ VmgSetOffsetValid (Ghcb, GhcbSwScratch);
+ Status = VmgExit (Ghcb, SVM_EXIT_MMIO_READ, ExitInfo1, ExitInfo2);
+ if (Status != 0) {
+ return Status;
+ }
+
+ Register = GetRegisterPointer (Regs, InstructionData->Ext.ModRm.Reg);
+ if (Bytes == 4) {
+ //
+ // Zero-extend for 32-bit operation
+ //
+ *Register = 0;
+ }
+
+ CopyMem (Register, Ghcb->SharedBuffer, Bytes);
+ break;
+
+ //
+ // MMIO read (MOV aX, moffsetX)
+ //
+ case 0xA0:
+ Bytes = 1;
+ //
+ // fall through
+ //
+ case 0xA1:
+ Bytes = ((Bytes != 0) ? Bytes :
+ (InstructionData->DataSize == Size16Bits) ? 2 :
+ (InstructionData->DataSize == Size32Bits) ? 4 :
+ (InstructionData->DataSize == Size64Bits) ? 8 :
+ 0);
+
+ InstructionData->ImmediateSize = (UINTN)(1 << InstructionData->AddrSize);
+ InstructionData->End += InstructionData->ImmediateSize;
+
+ //
+ // This code is X64 only, so a possible 8-byte copy to a UINTN is ok.
+ // Use a STATIC_ASSERT to be certain the code is being built as X64.
+ //
+ STATIC_ASSERT (
+ sizeof (UINTN) == sizeof (UINT64),
+ "sizeof (UINTN) != sizeof (UINT64), this file must be built as X64"
+ );
+
+ Address = 0;
+ CopyMem (
+ &Address,
+ InstructionData->Immediate,
+ InstructionData->ImmediateSize
+ );
+
+ Status = ValidateMmioMemory (Ghcb, Address, Bytes);
+ if (Status != 0) {
+ return Status;
+ }
+
+ ExitInfo1 = Address;
+ ExitInfo2 = Bytes;
+
+ Ghcb->SaveArea.SwScratch = (UINT64)Ghcb->SharedBuffer;
+ VmgSetOffsetValid (Ghcb, GhcbSwScratch);
+ Status = VmgExit (Ghcb, SVM_EXIT_MMIO_READ, ExitInfo1, ExitInfo2);
+ if (Status != 0) {
+ return Status;
+ }
+
+ if (Bytes == 4) {
+ //
+ // Zero-extend for 32-bit operation
+ //
+ Regs->Rax = 0;
+ }
+
+ CopyMem (&Regs->Rax, Ghcb->SharedBuffer, Bytes);
+ break;
+
+ //
+ // MMIO read w/ zero-extension ((MOVZX regX, reg/memX)
+ //
+ case 0xB6:
+ Bytes = 1;
+ //
+ // fall through
+ //
+ case 0xB7:
+ DecodeModRm (Regs, InstructionData);
+ Bytes = (Bytes != 0) ? Bytes : 2;
+
+ Status = ValidateMmioMemory (Ghcb, InstructionData->Ext.RmData, Bytes);
+ if (Status != 0) {
+ return Status;
+ }
+
+ ExitInfo1 = InstructionData->Ext.RmData;
+ ExitInfo2 = Bytes;
+
+ Ghcb->SaveArea.SwScratch = (UINT64)Ghcb->SharedBuffer;
+ VmgSetOffsetValid (Ghcb, GhcbSwScratch);
+ Status = VmgExit (Ghcb, SVM_EXIT_MMIO_READ, ExitInfo1, ExitInfo2);
+ if (Status != 0) {
+ return Status;
+ }
+
+ Register = GetRegisterPointer (Regs, InstructionData->Ext.ModRm.Reg);
+ SetMem (Register, (UINTN)(1 << InstructionData->DataSize), 0);
+ CopyMem (Register, Ghcb->SharedBuffer, Bytes);
+ break;
+
+ //
+ // MMIO read w/ sign-extension (MOVSX regX, reg/memX)
+ //
+ case 0xBE:
+ Bytes = 1;
+ //
+ // fall through
+ //
+ case 0xBF:
+ DecodeModRm (Regs, InstructionData);
+ Bytes = (Bytes != 0) ? Bytes : 2;
+
+ Status = ValidateMmioMemory (Ghcb, InstructionData->Ext.RmData, Bytes);
+ if (Status != 0) {
+ return Status;
+ }
+
+ ExitInfo1 = InstructionData->Ext.RmData;
+ ExitInfo2 = Bytes;
+
+ Ghcb->SaveArea.SwScratch = (UINT64)Ghcb->SharedBuffer;
+ VmgSetOffsetValid (Ghcb, GhcbSwScratch);
+ Status = VmgExit (Ghcb, SVM_EXIT_MMIO_READ, ExitInfo1, ExitInfo2);
+ if (Status != 0) {
+ return Status;
+ }
+
+ if (Bytes == 1) {
+ UINT8 *Data;
+
+ Data = (UINT8 *)Ghcb->SharedBuffer;
+ SignByte = ((*Data & BIT7) != 0) ? 0xFF : 0x00;
+ } else {
+ UINT16 *Data;
+
+ Data = (UINT16 *)Ghcb->SharedBuffer;
+ SignByte = ((*Data & BIT15) != 0) ? 0xFF : 0x00;
+ }
+
+ Register = GetRegisterPointer (Regs, InstructionData->Ext.ModRm.Reg);
+ SetMem (Register, (UINTN)(1 << InstructionData->DataSize), SignByte);
+ CopyMem (Register, Ghcb->SharedBuffer, Bytes);
+ break;
+
+ default:
+ DEBUG ((DEBUG_ERROR, "Invalid MMIO opcode (%x)\n", OpCode));
+ Status = GP_EXCEPTION;
+ ASSERT (FALSE);
+ }
+
+ return Status;
+}
+
+/**
+ Handle a MWAIT event.
+
+ Use the VMGEXIT instruction to handle a MWAIT event.
+
+ @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
+ Block
+ @param[in, out] Regs x64 processor context
+ @param[in] InstructionData Instruction parsing context
+
+ @retval 0 Event handled successfully
+ @return New exception value to propagate
+
+**/
+STATIC
+UINT64
+MwaitExit (
+ IN OUT GHCB *Ghcb,
+ IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,
+ IN SEV_ES_INSTRUCTION_DATA *InstructionData
+ )
+{
+ DecodeModRm (Regs, InstructionData);
+
+ Ghcb->SaveArea.Rax = Regs->Rax;
+ VmgSetOffsetValid (Ghcb, GhcbRax);
+ Ghcb->SaveArea.Rcx = Regs->Rcx;
+ VmgSetOffsetValid (Ghcb, GhcbRcx);
+
+ return VmgExit (Ghcb, SVM_EXIT_MWAIT, 0, 0);
+}
+
+/**
+ Handle a MONITOR event.
+
+ Use the VMGEXIT instruction to handle a MONITOR event.
+
+ @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
+ Block
+ @param[in, out] Regs x64 processor context
+ @param[in] InstructionData Instruction parsing context
+
+ @retval 0 Event handled successfully
+ @return New exception value to propagate
+
+**/
+STATIC
+UINT64
+MonitorExit (
+ IN OUT GHCB *Ghcb,
+ IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,
+ IN SEV_ES_INSTRUCTION_DATA *InstructionData
+ )
+{
+ DecodeModRm (Regs, InstructionData);
+
+ Ghcb->SaveArea.Rax = Regs->Rax; // Identity mapped, so VA = PA
+ VmgSetOffsetValid (Ghcb, GhcbRax);
+ Ghcb->SaveArea.Rcx = Regs->Rcx;
+ VmgSetOffsetValid (Ghcb, GhcbRcx);
+ Ghcb->SaveArea.Rdx = Regs->Rdx;
+ VmgSetOffsetValid (Ghcb, GhcbRdx);
+
+ return VmgExit (Ghcb, SVM_EXIT_MONITOR, 0, 0);
+}
+
+/**
+ Handle a WBINVD event.
+
+ Use the VMGEXIT instruction to handle a WBINVD event.
+
+ @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
+ Block
+ @param[in, out] Regs x64 processor context
+ @param[in] InstructionData Instruction parsing context
+
+ @retval 0 Event handled successfully
+ @return New exception value to propagate
+
+**/
+STATIC
+UINT64
+WbinvdExit (
+ IN OUT GHCB *Ghcb,
+ IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,
+ IN SEV_ES_INSTRUCTION_DATA *InstructionData
+ )
+{
+ return VmgExit (Ghcb, SVM_EXIT_WBINVD, 0, 0);
+}
+
+/**
+ Handle a RDTSCP event.
+
+ Use the VMGEXIT instruction to handle a RDTSCP event.
+
+ @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
+ Block
+ @param[in, out] Regs x64 processor context
+ @param[in] InstructionData Instruction parsing context
+
+ @retval 0 Event handled successfully
+ @return New exception value to propagate
+
+**/
+STATIC
+UINT64
+RdtscpExit (
+ IN OUT GHCB *Ghcb,
+ IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,
+ IN SEV_ES_INSTRUCTION_DATA *InstructionData
+ )
+{
+ UINT64 Status;
+
+ DecodeModRm (Regs, InstructionData);
+
+ Status = VmgExit (Ghcb, SVM_EXIT_RDTSCP, 0, 0);
+ if (Status != 0) {
+ return Status;
+ }
+
+ if (!VmgIsOffsetValid (Ghcb, GhcbRax) ||
+ !VmgIsOffsetValid (Ghcb, GhcbRcx) ||
+ !VmgIsOffsetValid (Ghcb, GhcbRdx))
+ {
+ return UnsupportedExit (Ghcb, Regs, InstructionData);
+ }
+
+ Regs->Rax = Ghcb->SaveArea.Rax;
+ Regs->Rcx = Ghcb->SaveArea.Rcx;
+ Regs->Rdx = Ghcb->SaveArea.Rdx;
+
+ return 0;
+}
+
+/**
+ Handle a VMMCALL event.
+
+ Use the VMGEXIT instruction to handle a VMMCALL event.
+
+ @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
+ Block
+ @param[in, out] Regs x64 processor context
+ @param[in] InstructionData Instruction parsing context
+
+ @retval 0 Event handled successfully
+ @return New exception value to propagate
+
+**/
+STATIC
+UINT64
+VmmCallExit (
+ IN OUT GHCB *Ghcb,
+ IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,
+ IN SEV_ES_INSTRUCTION_DATA *InstructionData
+ )
+{
+ UINT64 Status;
+
+ DecodeModRm (Regs, InstructionData);
+
+ Ghcb->SaveArea.Rax = Regs->Rax;
+ VmgSetOffsetValid (Ghcb, GhcbRax);
+ Ghcb->SaveArea.Cpl = (UINT8)(Regs->Cs & 0x3);
+ VmgSetOffsetValid (Ghcb, GhcbCpl);
+
+ Status = VmgExit (Ghcb, SVM_EXIT_VMMCALL, 0, 0);
+ if (Status != 0) {
+ return Status;
+ }
+
+ if (!VmgIsOffsetValid (Ghcb, GhcbRax)) {
+ return UnsupportedExit (Ghcb, Regs, InstructionData);
+ }
+
+ Regs->Rax = Ghcb->SaveArea.Rax;
+
+ return 0;
+}
+
+/**
+ Handle an MSR event.
+
+ Use the VMGEXIT instruction to handle either a RDMSR or WRMSR event.
+
+ @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
+ Block
+ @param[in, out] Regs x64 processor context
+ @param[in] InstructionData Instruction parsing context
+
+ @retval 0 Event handled successfully
+ @return New exception value to propagate
+
+**/
+STATIC
+UINT64
+MsrExit (
+ IN OUT GHCB *Ghcb,
+ IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,
+ IN SEV_ES_INSTRUCTION_DATA *InstructionData
+ )
+{
+ UINT64 ExitInfo1, Status;
+
+ ExitInfo1 = 0;
+
+ switch (*(InstructionData->OpCodes + 1)) {
+ case 0x30: // WRMSR
+ ExitInfo1 = 1;
+ Ghcb->SaveArea.Rax = Regs->Rax;
+ VmgSetOffsetValid (Ghcb, GhcbRax);
+ Ghcb->SaveArea.Rdx = Regs->Rdx;
+ VmgSetOffsetValid (Ghcb, GhcbRdx);
+ //
+ // fall through
+ //
+ case 0x32: // RDMSR
+ Ghcb->SaveArea.Rcx = Regs->Rcx;
+ VmgSetOffsetValid (Ghcb, GhcbRcx);
+ break;
+ default:
+ return UnsupportedExit (Ghcb, Regs, InstructionData);
+ }
+
+ Status = VmgExit (Ghcb, SVM_EXIT_MSR, ExitInfo1, 0);
+ if (Status != 0) {
+ return Status;
+ }
+
+ if (ExitInfo1 == 0) {
+ if (!VmgIsOffsetValid (Ghcb, GhcbRax) ||
+ !VmgIsOffsetValid (Ghcb, GhcbRdx))
+ {
+ return UnsupportedExit (Ghcb, Regs, InstructionData);
+ }
+
+ Regs->Rax = Ghcb->SaveArea.Rax;
+ Regs->Rdx = Ghcb->SaveArea.Rdx;
+ }
+
+ return 0;
+}
+
+/**
+ Build the IOIO event information.
+
+ The IOIO event information identifies the type of IO operation to be performed
+ by the hypervisor. Build this information based on the instruction data.
+
+ @param[in] Regs x64 processor context
+ @param[in, out] InstructionData Instruction parsing context
+
+ @return IOIO event information value
+
+**/
+STATIC
+UINT64
+IoioExitInfo (
+ IN EFI_SYSTEM_CONTEXT_X64 *Regs,
+ IN OUT SEV_ES_INSTRUCTION_DATA *InstructionData
+ )
+{
+ UINT64 ExitInfo;
+
+ ExitInfo = 0;
+
+ switch (*(InstructionData->OpCodes)) {
+ //
+ // INS opcodes
+ //
+ case 0x6C:
+ case 0x6D:
+ ExitInfo |= IOIO_TYPE_INS;
+ ExitInfo |= IOIO_SEG_ES;
+ ExitInfo |= ((Regs->Rdx & 0xffff) << 16);
+ break;
+
+ //
+ // OUTS opcodes
+ //
+ case 0x6E:
+ case 0x6F:
+ ExitInfo |= IOIO_TYPE_OUTS;
+ ExitInfo |= IOIO_SEG_DS;
+ ExitInfo |= ((Regs->Rdx & 0xffff) << 16);
+ break;
+
+ //
+ // IN immediate opcodes
+ //
+ case 0xE4:
+ case 0xE5:
+ InstructionData->ImmediateSize = 1;
+ InstructionData->End++;
+ ExitInfo |= IOIO_TYPE_IN;
+ ExitInfo |= ((*(InstructionData->OpCodes + 1)) << 16);
+ break;
+
+ //
+ // OUT immediate opcodes
+ //
+ case 0xE6:
+ case 0xE7:
+ InstructionData->ImmediateSize = 1;
+ InstructionData->End++;
+ ExitInfo |= IOIO_TYPE_OUT;
+ ExitInfo |= ((*(InstructionData->OpCodes + 1)) << 16) | IOIO_TYPE_OUT;
+ break;
+
+ //
+ // IN register opcodes
+ //
+ case 0xEC:
+ case 0xED:
+ ExitInfo |= IOIO_TYPE_IN;
+ ExitInfo |= ((Regs->Rdx & 0xffff) << 16);
+ break;
+
+ //
+ // OUT register opcodes
+ //
+ case 0xEE:
+ case 0xEF:
+ ExitInfo |= IOIO_TYPE_OUT;
+ ExitInfo |= ((Regs->Rdx & 0xffff) << 16);
+ break;
+
+ default:
+ return 0;
+ }
+
+ switch (*(InstructionData->OpCodes)) {
+ //
+ // Single-byte opcodes
+ //
+ case 0x6C:
+ case 0x6E:
+ case 0xE4:
+ case 0xE6:
+ case 0xEC:
+ case 0xEE:
+ ExitInfo |= IOIO_DATA_8;
+ break;
+
+ //
+ // Length determined by instruction parsing
+ //
+ default:
+ ExitInfo |= (InstructionData->DataSize == Size16Bits) ? IOIO_DATA_16
+ : IOIO_DATA_32;
+ }
+
+ switch (InstructionData->AddrSize) {
+ case Size16Bits:
+ ExitInfo |= IOIO_ADDR_16;
+ break;
+
+ case Size32Bits:
+ ExitInfo |= IOIO_ADDR_32;
+ break;
+
+ case Size64Bits:
+ ExitInfo |= IOIO_ADDR_64;
+ break;
+
+ default:
+ break;
+ }
+
+ if (InstructionData->RepMode != 0) {
+ ExitInfo |= IOIO_REP;
+ }
+
+ return ExitInfo;
+}
+
+/**
+ Handle an IOIO event.
+
+ Use the VMGEXIT instruction to handle an IOIO event.
+
+ @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
+ Block
+ @param[in, out] Regs x64 processor context
+ @param[in] InstructionData Instruction parsing context
+
+ @retval 0 Event handled successfully
+ @return New exception value to propagate
+
+**/
+STATIC
+UINT64
+IoioExit (
+ IN OUT GHCB *Ghcb,
+ IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,
+ IN SEV_ES_INSTRUCTION_DATA *InstructionData
+ )
+{
+ UINT64 ExitInfo1, ExitInfo2, Status;
+ BOOLEAN IsString;
+
+ ExitInfo1 = IoioExitInfo (Regs, InstructionData);
+ if (ExitInfo1 == 0) {
+ return UnsupportedExit (Ghcb, Regs, InstructionData);
+ }
+
+ IsString = ((ExitInfo1 & IOIO_TYPE_STR) != 0) ? TRUE : FALSE;
+ if (IsString) {
+ UINTN IoBytes, VmgExitBytes;
+ UINTN GhcbCount, OpCount;
+
+ Status = 0;
+
+ IoBytes = IOIO_DATA_BYTES (ExitInfo1);
+ GhcbCount = sizeof (Ghcb->SharedBuffer) / IoBytes;
+
+ OpCount = ((ExitInfo1 & IOIO_REP) != 0) ? Regs->Rcx : 1;
+ while (OpCount != 0) {
+ ExitInfo2 = MIN (OpCount, GhcbCount);
+ VmgExitBytes = ExitInfo2 * IoBytes;
+
+ if ((ExitInfo1 & IOIO_TYPE_IN) == 0) {
+ CopyMem (Ghcb->SharedBuffer, (VOID *)Regs->Rsi, VmgExitBytes);
+ Regs->Rsi += VmgExitBytes;
+ }
+
+ Ghcb->SaveArea.SwScratch = (UINT64)Ghcb->SharedBuffer;
+ VmgSetOffsetValid (Ghcb, GhcbSwScratch);
+ Status = VmgExit (Ghcb, SVM_EXIT_IOIO_PROT, ExitInfo1, ExitInfo2);
+ if (Status != 0) {
+ return Status;
+ }
+
+ if ((ExitInfo1 & IOIO_TYPE_IN) != 0) {
+ CopyMem ((VOID *)Regs->Rdi, Ghcb->SharedBuffer, VmgExitBytes);
+ Regs->Rdi += VmgExitBytes;
+ }
+
+ if ((ExitInfo1 & IOIO_REP) != 0) {
+ Regs->Rcx -= ExitInfo2;
+ }
+
+ OpCount -= ExitInfo2;
+ }
+ } else {
+ if ((ExitInfo1 & IOIO_TYPE_IN) != 0) {
+ Ghcb->SaveArea.Rax = 0;
+ } else {
+ CopyMem (&Ghcb->SaveArea.Rax, &Regs->Rax, IOIO_DATA_BYTES (ExitInfo1));
+ }
+
+ VmgSetOffsetValid (Ghcb, GhcbRax);
+
+ Status = VmgExit (Ghcb, SVM_EXIT_IOIO_PROT, ExitInfo1, 0);
+ if (Status != 0) {
+ return Status;
+ }
+
+ if ((ExitInfo1 & IOIO_TYPE_IN) != 0) {
+ if (!VmgIsOffsetValid (Ghcb, GhcbRax)) {
+ return UnsupportedExit (Ghcb, Regs, InstructionData);
+ }
+
+ CopyMem (&Regs->Rax, &Ghcb->SaveArea.Rax, IOIO_DATA_BYTES (ExitInfo1));
+ }
+ }
+
+ return 0;
+}
+
+/**
+ Handle a INVD event.
+
+ Use the VMGEXIT instruction to handle a INVD event.
+
+ @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
+ Block
+ @param[in, out] Regs x64 processor context
+ @param[in] InstructionData Instruction parsing context
+
+ @retval 0 Event handled successfully
+ @return New exception value to propagate
+
+**/
+STATIC
+UINT64
+InvdExit (
+ IN OUT GHCB *Ghcb,
+ IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,
+ IN SEV_ES_INSTRUCTION_DATA *InstructionData
+ )
+{
+ return VmgExit (Ghcb, SVM_EXIT_INVD, 0, 0);
+}
+
+/**
+ Fetch CPUID leaf/function via hypervisor/VMGEXIT.
+
+ @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
+ Block
+ @param[in] EaxIn EAX input for cpuid instruction
+ @param[in] EcxIn ECX input for cpuid instruction
+ @param[in] Xcr0In XCR0 at time of cpuid instruction
+ @param[in, out] Eax Pointer to store leaf's EAX value
+ @param[in, out] Ebx Pointer to store leaf's EBX value
+ @param[in, out] Ecx Pointer to store leaf's ECX value
+ @param[in, out] Edx Pointer to store leaf's EDX value
+ @param[in, out] Status Pointer to store status from VMGEXIT (always 0
+ unless return value indicates failure)
+ @param[in, out] Unsupported Pointer to store indication of unsupported
+ VMGEXIT (always false unless return value
+ indicates failure)
+
+ @retval TRUE CPUID leaf fetch successfully.
+ @retval FALSE Error occurred while fetching CPUID leaf. Callers
+ should Status and Unsupported and handle
+ accordingly if they indicate a more precise
+ error condition.
+
+**/
+STATIC
+BOOLEAN
+GetCpuidHyp (
+ IN OUT GHCB *Ghcb,
+ IN UINT32 EaxIn,
+ IN UINT32 EcxIn,
+ IN UINT64 XCr0,
+ IN OUT UINT32 *Eax,
+ IN OUT UINT32 *Ebx,
+ IN OUT UINT32 *Ecx,
+ IN OUT UINT32 *Edx,
+ IN OUT UINT64 *Status,
+ IN OUT BOOLEAN *UnsupportedExit
+ )
+{
+ *UnsupportedExit = FALSE;
+ Ghcb->SaveArea.Rax = EaxIn;
+ VmgSetOffsetValid (Ghcb, GhcbRax);
+ Ghcb->SaveArea.Rcx = EcxIn;
+ VmgSetOffsetValid (Ghcb, GhcbRcx);
+ if (EaxIn == CPUID_EXTENDED_STATE) {
+ Ghcb->SaveArea.XCr0 = XCr0;
+ VmgSetOffsetValid (Ghcb, GhcbXCr0);
+ }
+
+ *Status = VmgExit (Ghcb, SVM_EXIT_CPUID, 0, 0);
+ if (*Status != 0) {
+ return FALSE;
+ }
+
+ if (!VmgIsOffsetValid (Ghcb, GhcbRax) ||
+ !VmgIsOffsetValid (Ghcb, GhcbRbx) ||
+ !VmgIsOffsetValid (Ghcb, GhcbRcx) ||
+ !VmgIsOffsetValid (Ghcb, GhcbRdx))
+ {
+ *UnsupportedExit = TRUE;
+ return FALSE;
+ }
+
+ if (Eax) {
+ *Eax = (UINT32)(UINTN)Ghcb->SaveArea.Rax;
+ }
+
+ if (Ebx) {
+ *Ebx = (UINT32)(UINTN)Ghcb->SaveArea.Rbx;
+ }
+
+ if (Ecx) {
+ *Ecx = (UINT32)(UINTN)Ghcb->SaveArea.Rcx;
+ }
+
+ if (Edx) {
+ *Edx = (UINT32)(UINTN)Ghcb->SaveArea.Rdx;
+ }
+
+ return TRUE;
+}
+
+/**
+ Check if SEV-SNP enabled.
+
+ @retval TRUE SEV-SNP is enabled.
+ @retval FALSE SEV-SNP is disabled.
+
+**/
+STATIC
+BOOLEAN
+SnpEnabled (
+ VOID
+ )
+{
+ MSR_SEV_STATUS_REGISTER Msr;
+
+ Msr.Uint32 = AsmReadMsr32 (MSR_SEV_STATUS);
+
+ return !!Msr.Bits.SevSnpBit;
+}
+
+/**
+ Calculate the total XSAVE area size for enabled XSAVE areas
+
+ @param[in] XFeaturesEnabled Bit-mask of enabled XSAVE features/areas as
+ indicated by XCR0/MSR_IA32_XSS bits
+ @param[in] XSaveBaseSize Base/legacy XSAVE area size (e.g. when
+ XCR0 is 1)
+ @param[in, out] XSaveSize Pointer to storage for calculated XSAVE area
+ size
+ @param[in] Compacted Whether or not the calculation is for the
+ normal XSAVE area size (leaf 0xD,0x0,EBX) or
+ compacted XSAVE area size (leaf 0xD,0x1,EBX)
+
+
+ @retval TRUE XSAVE size calculation was successful.
+ @retval FALSE XSAVE size calculation was unsuccessful.
+**/
+STATIC
+BOOLEAN
+GetCpuidXSaveSize (
+ IN UINT64 XFeaturesEnabled,
+ IN UINT32 XSaveBaseSize,
+ IN OUT UINT32 *XSaveSize,
+ IN BOOLEAN Compacted
+ )
+{
+ SEV_SNP_CPUID_INFO *CpuidInfo;
+ UINT64 XFeaturesFound = 0;
+ UINT32 Idx;
+
+ *XSaveSize = XSaveBaseSize;
+ CpuidInfo = (SEV_SNP_CPUID_INFO *)(UINT64)PcdGet32 (PcdOvmfCpuidBase);
+
+ for (Idx = 0; Idx < CpuidInfo->Count; Idx++) {
+ SEV_SNP_CPUID_FUNCTION *CpuidFn = &CpuidInfo->function[Idx];
+
+ if (!((CpuidFn->EaxIn == 0xD) &&
+ ((CpuidFn->EcxIn == 0) || (CpuidFn->EcxIn == 1))))
+ {
+ continue;
+ }
+
+ if (XFeaturesFound & (1ULL << CpuidFn->EcxIn) ||
+ !(XFeaturesEnabled & (1ULL << CpuidFn->EcxIn)))
+ {
+ continue;
+ }
+
+ XFeaturesFound |= (1ULL << CpuidFn->EcxIn);
+ if (Compacted) {
+ *XSaveSize += CpuidFn->Eax;
+ } else {
+ *XSaveSize = MAX (*XSaveSize, CpuidFn->Eax + CpuidFn->Ebx);
+ }
+ }
+
+ /*
+ * Either the guest set unsupported XCR0/XSS bits, or the corresponding
+ * entries in the CPUID table were not present. This is an invalid state.
+ */
+ if (XFeaturesFound != (XFeaturesEnabled & ~3UL)) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ Check if a CPUID leaf/function is indexed via ECX sub-leaf/sub-function
+
+ @param[in] EaxIn EAX input for cpuid instruction
+
+ @retval FALSE cpuid leaf/function is not indexed by ECX input
+ @retval TRUE cpuid leaf/function is indexed by ECX input
+
+**/
+STATIC
+BOOLEAN
+IsFunctionIndexed (
+ IN UINT32 EaxIn
+ )
+{
+ switch (EaxIn) {
+ case CPUID_CACHE_PARAMS:
+ case CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS:
+ case CPUID_EXTENDED_TOPOLOGY:
+ case CPUID_EXTENDED_STATE:
+ case CPUID_INTEL_RDT_MONITORING:
+ case CPUID_INTEL_RDT_ALLOCATION:
+ case CPUID_INTEL_SGX:
+ case CPUID_INTEL_PROCESSOR_TRACE:
+ case CPUID_DETERMINISTIC_ADDRESS_TRANSLATION_PARAMETERS:
+ case CPUID_V2_EXTENDED_TOPOLOGY:
+ case 0x8000001D: /* Cache Topology Information */
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ Fetch CPUID leaf/function via SEV-SNP CPUID table.
+
+ @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
+ Block
+ @param[in] EaxIn EAX input for cpuid instruction
+ @param[in] EcxIn ECX input for cpuid instruction
+ @param[in] Xcr0In XCR0 at time of cpuid instruction
+ @param[in, out] Eax Pointer to store leaf's EAX value
+ @param[in, out] Ebx Pointer to store leaf's EBX value
+ @param[in, out] Ecx Pointer to store leaf's ECX value
+ @param[in, out] Edx Pointer to store leaf's EDX value
+ @param[in, out] Status Pointer to store status from VMGEXIT (always 0
+ unless return value indicates failure)
+ @param[in, out] Unsupported Pointer to store indication of unsupported
+ VMGEXIT (always false unless return value
+ indicates failure)
+
+ @retval TRUE CPUID leaf fetch successfully.
+ @retval FALSE Error occurred while fetching CPUID leaf. Callers
+ should Status and Unsupported and handle
+ accordingly if they indicate a more precise
+ error condition.
+
+**/
+STATIC
+BOOLEAN
+GetCpuidFw (
+ IN OUT GHCB *Ghcb,
+ IN UINT32 EaxIn,
+ IN UINT32 EcxIn,
+ IN UINT64 XCr0,
+ IN OUT UINT32 *Eax,
+ IN OUT UINT32 *Ebx,
+ IN OUT UINT32 *Ecx,
+ IN OUT UINT32 *Edx,
+ IN OUT UINT64 *Status,
+ IN OUT BOOLEAN *Unsupported
+ )
+{
+ SEV_SNP_CPUID_INFO *CpuidInfo;
+ BOOLEAN Found;
+ UINT32 Idx;
+
+ CpuidInfo = (SEV_SNP_CPUID_INFO *)(UINT64)PcdGet32 (PcdOvmfCpuidBase);
+ Found = FALSE;
+
+ for (Idx = 0; Idx < CpuidInfo->Count; Idx++) {
+ SEV_SNP_CPUID_FUNCTION *CpuidFn = &CpuidInfo->function[Idx];
+
+ if (CpuidFn->EaxIn != EaxIn) {
+ continue;
+ }
+
+ if (IsFunctionIndexed (CpuidFn->EaxIn) && (CpuidFn->EcxIn != EcxIn)) {
+ continue;
+ }
+
+ *Eax = CpuidFn->Eax;
+ *Ebx = CpuidFn->Ebx;
+ *Ecx = CpuidFn->Ecx;
+ *Edx = CpuidFn->Edx;
+
+ Found = TRUE;
+ break;
+ }
+
+ if (!Found) {
+ *Eax = *Ebx = *Ecx = *Edx = 0;
+ goto Out;
+ }
+
+ if (EaxIn == CPUID_VERSION_INFO) {
+ IA32_CR4 Cr4;
+ UINT32 Ebx2;
+ UINT32 Edx2;
+
+ if (!GetCpuidHyp (
+ Ghcb,
+ EaxIn,
+ EcxIn,
+ XCr0,
+ NULL,
+ &Ebx2,
+ NULL,
+ &Edx2,
+ Status,
+ Unsupported
+ ))
+ {
+ return FALSE;
+ }
+
+ /* initial APIC ID */
+ *Ebx = (*Ebx & 0x00FFFFFF) | (Ebx2 & 0xFF000000);
+ /* APIC enabled bit */
+ *Edx = (*Edx & ~BIT9) | (Edx2 & BIT9);
+ /* OSXSAVE enabled bit */
+ Cr4.UintN = AsmReadCr4 ();
+ *Ecx = (Cr4.Bits.OSXSAVE) ? (*Ecx & ~BIT27) | (*Ecx & BIT27)
+ : (*Ecx & ~BIT27);
+ } else if (EaxIn == CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS) {
+ IA32_CR4 Cr4;
+
+ Cr4.UintN = AsmReadCr4 ();
+ /* OSPKE enabled bit */
+ *Ecx = (Cr4.Bits.PKE) ? (*Ecx | BIT4) : (*Ecx & ~BIT4);
+ } else if (EaxIn == CPUID_EXTENDED_TOPOLOGY) {
+ if (!GetCpuidHyp (
+ Ghcb,
+ EaxIn,
+ EcxIn,
+ XCr0,
+ NULL,
+ NULL,
+ NULL,
+ Edx,
+ Status,
+ Unsupported
+ ))
+ {
+ return FALSE;
+ }
+ } else if ((EaxIn == CPUID_EXTENDED_STATE) && ((EcxIn == 0) || (EcxIn == 1))) {
+ MSR_IA32_XSS_REGISTER XssMsr;
+ BOOLEAN Compacted;
+ UINT32 XSaveSize;
+
+ XssMsr.Uint64 = 0;
+ Compacted = FALSE;
+ if (EcxIn == 1) {
+ /*
+ * The PPR and APM aren't clear on what size should be encoded in
+ * 0xD:0x1:EBX when compaction is not enabled by either XSAVEC or
+ * XSAVES, as these are generally fixed to 1 on real CPUs. Report
+ * this undefined case as an error.
+ */
+ if (!(*Eax & (BIT3 | BIT1))) {
+ /* (XSAVES | XSAVEC) */
+ return FALSE;
+ }
+
+ Compacted = TRUE;
+ XssMsr.Uint64 = AsmReadMsr64 (MSR_IA32_XSS);
+ }
+
+ if (!GetCpuidXSaveSize (
+ XCr0 | XssMsr.Uint64,
+ *Ebx,
+ &XSaveSize,
+ Compacted
+ ))
+ {
+ return FALSE;
+ }
+
+ *Ebx = XSaveSize;
+ } else if (EaxIn == 0x8000001E) {
+ UINT32 Ebx2;
+ UINT32 Ecx2;
+
+ /* extended APIC ID */
+ if (!GetCpuidHyp (
+ Ghcb,
+ EaxIn,
+ EcxIn,
+ XCr0,
+ Eax,
+ &Ebx2,
+ &Ecx2,
+ NULL,
+ Status,
+ Unsupported
+ ))
+ {
+ return FALSE;
+ }
+
+ /* compute ID */
+ *Ebx = (*Ebx & 0xFFFFFF00) | (Ebx2 & 0x000000FF);
+ /* node ID */
+ *Ecx = (*Ecx & 0xFFFFFF00) | (Ecx2 & 0x000000FF);
+ }
+
+Out:
+ *Status = 0;
+ *Unsupported = FALSE;
+ return TRUE;
+}
+
+/**
+ Handle a CPUID event.
+
+ Use VMGEXIT instruction or CPUID table to handle a CPUID event.
+
+ @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
+ Block
+ @param[in, out] Regs x64 processor context
+ @param[in] InstructionData Instruction parsing context
+
+ @retval 0 Event handled successfully
+ @return New exception value to propagate
+
+**/
+STATIC
+UINT64
+CpuidExit (
+ IN OUT GHCB *Ghcb,
+ IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,
+ IN SEV_ES_INSTRUCTION_DATA *InstructionData
+ )
+{
+ BOOLEAN Unsupported;
+ UINT64 Status;
+ UINT32 EaxIn;
+ UINT32 EcxIn;
+ UINT64 XCr0;
+ UINT32 Eax;
+ UINT32 Ebx;
+ UINT32 Ecx;
+ UINT32 Edx;
+
+ EaxIn = (UINT32)(UINTN)Regs->Rax;
+ EcxIn = (UINT32)(UINTN)Regs->Rcx;
+
+ if (EaxIn == CPUID_EXTENDED_STATE) {
+ IA32_CR4 Cr4;
+
+ Cr4.UintN = AsmReadCr4 ();
+ Ghcb->SaveArea.XCr0 = (Cr4.Bits.OSXSAVE == 1) ? AsmXGetBv (0) : 1;
+ XCr0 = (Cr4.Bits.OSXSAVE == 1) ? AsmXGetBv (0) : 1;
+ }
+
+ if (SnpEnabled ()) {
+ if (!GetCpuidFw (
+ Ghcb,
+ EaxIn,
+ EcxIn,
+ XCr0,
+ &Eax,
+ &Ebx,
+ &Ecx,
+ &Edx,
+ &Status,
+ &Unsupported
+ ))
+ {
+ goto CpuidFail;
+ }
+ } else {
+ if (!GetCpuidHyp (
+ Ghcb,
+ EaxIn,
+ EcxIn,
+ XCr0,
+ &Eax,
+ &Ebx,
+ &Ecx,
+ &Edx,
+ &Status,
+ &Unsupported
+ ))
+ {
+ goto CpuidFail;
+ }
+ }
+
+ Regs->Rax = Eax;
+ Regs->Rbx = Ebx;
+ Regs->Rcx = Ecx;
+ Regs->Rdx = Edx;
+
+ return 0;
+
+CpuidFail:
+ if (Unsupported) {
+ return UnsupportedExit (Ghcb, Regs, InstructionData);
+ }
+
+ return Status;
+}
+
+/**
+ Handle a RDPMC event.
+
+ Use the VMGEXIT instruction to handle a RDPMC event.
+
+ @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
+ Block
+ @param[in, out] Regs x64 processor context
+ @param[in] InstructionData Instruction parsing context
+
+ @retval 0 Event handled successfully
+ @return New exception value to propagate
+
+**/
+STATIC
+UINT64
+RdpmcExit (
+ IN OUT GHCB *Ghcb,
+ IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,
+ IN SEV_ES_INSTRUCTION_DATA *InstructionData
+ )
+{
+ UINT64 Status;
+
+ Ghcb->SaveArea.Rcx = Regs->Rcx;
+ VmgSetOffsetValid (Ghcb, GhcbRcx);
+
+ Status = VmgExit (Ghcb, SVM_EXIT_RDPMC, 0, 0);
+ if (Status != 0) {
+ return Status;
+ }
+
+ if (!VmgIsOffsetValid (Ghcb, GhcbRax) ||
+ !VmgIsOffsetValid (Ghcb, GhcbRdx))
+ {
+ return UnsupportedExit (Ghcb, Regs, InstructionData);
+ }
+
+ Regs->Rax = Ghcb->SaveArea.Rax;
+ Regs->Rdx = Ghcb->SaveArea.Rdx;
+
+ return 0;
+}
+
+/**
+ Handle a RDTSC event.
+
+ Use the VMGEXIT instruction to handle a RDTSC event.
+
+ @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
+ Block
+ @param[in, out] Regs x64 processor context
+ @param[in] InstructionData Instruction parsing context
+
+ @retval 0 Event handled successfully
+ @return New exception value to propagate
+
+**/
+STATIC
+UINT64
+RdtscExit (
+ IN OUT GHCB *Ghcb,
+ IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,
+ IN SEV_ES_INSTRUCTION_DATA *InstructionData
+ )
+{
+ UINT64 Status;
+
+ Status = VmgExit (Ghcb, SVM_EXIT_RDTSC, 0, 0);
+ if (Status != 0) {
+ return Status;
+ }
+
+ if (!VmgIsOffsetValid (Ghcb, GhcbRax) ||
+ !VmgIsOffsetValid (Ghcb, GhcbRdx))
+ {
+ return UnsupportedExit (Ghcb, Regs, InstructionData);
+ }
+
+ Regs->Rax = Ghcb->SaveArea.Rax;
+ Regs->Rdx = Ghcb->SaveArea.Rdx;
+
+ return 0;
+}
+
+/**
+ Handle a DR7 register write event.
+
+ Use the VMGEXIT instruction to handle a DR7 write event.
+
+ @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
+ Block
+ @param[in, out] Regs x64 processor context
+ @param[in] InstructionData Instruction parsing context
+
+ @retval 0 Event handled successfully
+ @return New exception value to propagate
+
+**/
+STATIC
+UINT64
+Dr7WriteExit (
+ IN OUT GHCB *Ghcb,
+ IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,
+ IN SEV_ES_INSTRUCTION_DATA *InstructionData
+ )
+{
+ SEV_ES_INSTRUCTION_OPCODE_EXT *Ext;
+ SEV_ES_PER_CPU_DATA *SevEsData;
+ UINT64 *Register;
+ UINT64 Status;
+
+ Ext = &InstructionData->Ext;
+ SevEsData = (SEV_ES_PER_CPU_DATA *)(Ghcb + 1);
+
+ DecodeModRm (Regs, InstructionData);
+
+ //
+ // MOV DRn always treats MOD == 3 no matter how encoded
+ //
+ Register = GetRegisterPointer (Regs, Ext->ModRm.Rm);
+
+ //
+ // Using a value of 0 for ExitInfo1 means RAX holds the value
+ //
+ Ghcb->SaveArea.Rax = *Register;
+ VmgSetOffsetValid (Ghcb, GhcbRax);
+
+ Status = VmgExit (Ghcb, SVM_EXIT_DR7_WRITE, 0, 0);
+ if (Status != 0) {
+ return Status;
+ }
+
+ SevEsData->Dr7 = *Register;
+ SevEsData->Dr7Cached = 1;
+
+ return 0;
+}
+
+/**
+ Handle a DR7 register read event.
+
+ Use the VMGEXIT instruction to handle a DR7 read event.
+
+ @param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication
+ Block
+ @param[in, out] Regs x64 processor context
+ @param[in] InstructionData Instruction parsing context
+
+ @retval 0 Event handled successfully
+
+**/
+STATIC
+UINT64
+Dr7ReadExit (
+ IN OUT GHCB *Ghcb,
+ IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,
+ IN SEV_ES_INSTRUCTION_DATA *InstructionData
+ )
+{
+ SEV_ES_INSTRUCTION_OPCODE_EXT *Ext;
+ SEV_ES_PER_CPU_DATA *SevEsData;
+ UINT64 *Register;
+
+ Ext = &InstructionData->Ext;
+ SevEsData = (SEV_ES_PER_CPU_DATA *)(Ghcb + 1);
+
+ DecodeModRm (Regs, InstructionData);
+
+ //
+ // MOV DRn always treats MOD == 3 no matter how encoded
+ //
+ Register = GetRegisterPointer (Regs, Ext->ModRm.Rm);
+
+ //
+ // If there is a cached valued for DR7, return that. Otherwise return the
+ // DR7 standard reset value of 0x400 (no debug breakpoints set).
+ //
+ *Register = (SevEsData->Dr7Cached == 1) ? SevEsData->Dr7 : 0x400;
+
+ return 0;
+}
+
+/**
+ Handle a #VC exception.
+
+ Performs the necessary processing to handle a #VC exception.
+
+ @param[in, out] Ghcb Pointer to the GHCB
+ @param[in, out] ExceptionType Pointer to an EFI_EXCEPTION_TYPE to be set
+ as value to use on error.
+ @param[in, out] SystemContext Pointer to EFI_SYSTEM_CONTEXT
+
+ @retval EFI_SUCCESS Exception handled
+ @retval EFI_UNSUPPORTED #VC not supported, (new) exception value to
+ propagate provided
+ @retval EFI_PROTOCOL_ERROR #VC handling failed, (new) exception value to
+ propagate provided
+
+**/
+EFI_STATUS
+EFIAPI
+InternalVmgExitHandleVc (
+ IN OUT GHCB *Ghcb,
+ IN OUT EFI_EXCEPTION_TYPE *ExceptionType,
+ IN OUT EFI_SYSTEM_CONTEXT SystemContext
+ )
+{
+ EFI_SYSTEM_CONTEXT_X64 *Regs;
+ NAE_EXIT NaeExit;
+ SEV_ES_INSTRUCTION_DATA InstructionData;
+ UINT64 ExitCode, Status;
+ EFI_STATUS VcRet;
+ BOOLEAN InterruptState;
+
+ VcRet = EFI_SUCCESS;
+
+ Regs = SystemContext.SystemContextX64;
+
+ VmgInit (Ghcb, &InterruptState);
+
+ ExitCode = Regs->ExceptionData;
+ switch (ExitCode) {
+ case SVM_EXIT_DR7_READ:
+ NaeExit = Dr7ReadExit;
+ break;
+
+ case SVM_EXIT_DR7_WRITE:
+ NaeExit = Dr7WriteExit;
+ break;
+
+ case SVM_EXIT_RDTSC:
+ NaeExit = RdtscExit;
+ break;
+
+ case SVM_EXIT_RDPMC:
+ NaeExit = RdpmcExit;
+ break;
+
+ case SVM_EXIT_CPUID:
+ NaeExit = CpuidExit;
+ break;
+
+ case SVM_EXIT_INVD:
+ NaeExit = InvdExit;
+ break;
+
+ case SVM_EXIT_IOIO_PROT:
+ NaeExit = IoioExit;
+ break;
+
+ case SVM_EXIT_MSR:
+ NaeExit = MsrExit;
+ break;
+
+ case SVM_EXIT_VMMCALL:
+ NaeExit = VmmCallExit;
+ break;
+
+ case SVM_EXIT_RDTSCP:
+ NaeExit = RdtscpExit;
+ break;
+
+ case SVM_EXIT_WBINVD:
+ NaeExit = WbinvdExit;
+ break;
+
+ case SVM_EXIT_MONITOR:
+ NaeExit = MonitorExit;
+ break;
+
+ case SVM_EXIT_MWAIT:
+ NaeExit = MwaitExit;
+ break;
+
+ case SVM_EXIT_NPF:
+ NaeExit = MmioExit;
+ break;
+
+ default:
+ NaeExit = UnsupportedExit;
+ }
+
+ InitInstructionData (&InstructionData, Ghcb, Regs);
+
+ Status = NaeExit (Ghcb, Regs, &InstructionData);
+ if (Status == 0) {
+ Regs->Rip += InstructionLength (&InstructionData);
+ } else {
+ GHCB_EVENT_INJECTION Event;
+
+ Event.Uint64 = Status;
+ if (Event.Elements.ErrorCodeValid != 0) {
+ Regs->ExceptionData = Event.Elements.ErrorCode;
+ } else {
+ Regs->ExceptionData = 0;
+ }
+
+ *ExceptionType = Event.Elements.Vector;
+
+ VcRet = EFI_PROTOCOL_ERROR;
+ }
+
+ VmgDone (Ghcb, InterruptState);
+
+ return VcRet;
+}
+
+/**
+ Routine to allow ASSERT from within #VC.
+
+ @param[in, out] SevEsData Pointer to the per-CPU data
+
+**/
+VOID
+EFIAPI
+VmgExitIssueAssert (
+ IN OUT SEV_ES_PER_CPU_DATA *SevEsData
+ )
+{
+ //
+ // Progress will be halted, so set VcCount to allow for ASSERT output
+ // to be seen.
+ //
+ SevEsData->VcCount = 0;
+
+ ASSERT (FALSE);
+ CpuDeadLoop ();
+}