/** @file | |
Copyright (c) 2021, Intel Corporation. All rights reserved.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include <Library/BaseLib.h> | |
#include <Library/DebugLib.h> | |
#include "CcExitTd.h" | |
#include <Library/CcExitLib.h> | |
#include <Library/BaseMemoryLib.h> | |
#include <IndustryStandard/Tdx.h> | |
#include <IndustryStandard/InstructionParsing.h> | |
#include "CcInstruction.h" | |
#define TDX_MMIO_READ 0 | |
#define TDX_MMIO_WRITE 1 | |
typedef union { | |
struct { | |
UINT32 Eax; | |
UINT32 Edx; | |
} Regs; | |
UINT64 Val; | |
} MSR_DATA; | |
typedef union { | |
UINT8 Val; | |
struct { | |
UINT8 B : 1; | |
UINT8 X : 1; | |
UINT8 R : 1; | |
UINT8 W : 1; | |
} Bits; | |
} REX; | |
typedef union { | |
UINT8 Val; | |
struct { | |
UINT8 Rm : 3; | |
UINT8 Reg : 3; | |
UINT8 Mod : 2; | |
} Bits; | |
} MODRM; | |
typedef struct { | |
UINT64 Regs[4]; | |
} CPUID_DATA; | |
/** | |
Handle an CPUID event. | |
Use the TDVMCALL instruction to handle cpuid #ve | |
@param[in, out] Regs x64 processor context | |
@param[in] Veinfo VE Info | |
@retval 0 Event handled successfully | |
@return New exception value to propagate | |
**/ | |
STATIC | |
UINT64 | |
EFIAPI | |
CpuIdExit ( | |
IN EFI_SYSTEM_CONTEXT_X64 *Regs, | |
IN TDCALL_VEINFO_RETURN_DATA *Veinfo | |
) | |
{ | |
CPUID_DATA CpuIdData; | |
UINT64 Status; | |
Status = TdVmCallCpuid (Regs->Rax, Regs->Rcx, &CpuIdData); | |
if (Status == 0) { | |
Regs->Rax = CpuIdData.Regs[0]; | |
Regs->Rbx = CpuIdData.Regs[1]; | |
Regs->Rcx = CpuIdData.Regs[2]; | |
Regs->Rdx = CpuIdData.Regs[3]; | |
} | |
return Status; | |
} | |
/** | |
Handle an IO event. | |
Use the TDVMCALL instruction to handle either an IO read or an IO write. | |
@param[in, out] Regs x64 processor context | |
@param[in] Veinfo VE Info | |
@retval 0 Event handled successfully | |
@return New exception value to propagate | |
**/ | |
STATIC | |
UINT64 | |
EFIAPI | |
IoExit ( | |
IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs, | |
IN TDCALL_VEINFO_RETURN_DATA *Veinfo | |
) | |
{ | |
BOOLEAN Write; | |
UINTN Size; | |
UINTN Port; | |
UINT64 Val; | |
UINT64 RepCnt; | |
UINT64 Status; | |
Val = 0; | |
Status = 0; | |
Write = Veinfo->ExitQualification.Io.Direction ? FALSE : TRUE; | |
Size = Veinfo->ExitQualification.Io.Size + 1; | |
Port = Veinfo->ExitQualification.Io.Port; | |
if (Veinfo->ExitQualification.Io.String) { | |
// | |
// If REP is set, get rep-cnt from Rcx | |
// | |
RepCnt = Veinfo->ExitQualification.Io.Rep ? Regs->Rcx : 1; | |
while (RepCnt) { | |
Val = 0; | |
if (Write == TRUE) { | |
CopyMem (&Val, (VOID *)Regs->Rsi, Size); | |
Regs->Rsi += Size; | |
} | |
Status = TdVmCall (EXIT_REASON_IO_INSTRUCTION, Size, Write, Port, Val, (Write ? NULL : &Val)); | |
if (Status != 0) { | |
break; | |
} | |
if (Write == FALSE) { | |
CopyMem ((VOID *)Regs->Rdi, &Val, Size); | |
Regs->Rdi += Size; | |
} | |
if (Veinfo->ExitQualification.Io.Rep) { | |
Regs->Rcx -= 1; | |
} | |
RepCnt -= 1; | |
} | |
} else { | |
if (Write == TRUE) { | |
CopyMem (&Val, (VOID *)&Regs->Rax, Size); | |
} | |
Status = TdVmCall (EXIT_REASON_IO_INSTRUCTION, Size, Write, Port, Val, (Write ? NULL : &Val)); | |
if ((Status == 0) && (Write == FALSE)) { | |
CopyMem ((VOID *)&Regs->Rax, &Val, Size); | |
} | |
} | |
return Status; | |
} | |
/** | |
Handle an READ MSR event. | |
Use the TDVMCALL instruction to handle msr read | |
@param[in, out] Regs x64 processor context | |
@param[in] Veinfo VE Info | |
@retval 0 Event handled successfully | |
@return New exception value to propagate | |
**/ | |
STATIC | |
UINT64 | |
ReadMsrExit ( | |
IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs, | |
IN TDCALL_VEINFO_RETURN_DATA *Veinfo | |
) | |
{ | |
MSR_DATA Data; | |
UINT64 Status; | |
Status = TdVmCall (EXIT_REASON_MSR_READ, Regs->Rcx, 0, 0, 0, &Data); | |
if (Status == 0) { | |
Regs->Rax = Data.Regs.Eax; | |
Regs->Rdx = Data.Regs.Edx; | |
} | |
return Status; | |
} | |
/** | |
Handle an WRITE MSR event. | |
Use the TDVMCALL instruction to handle msr write | |
@param[in, out] Regs x64 processor context | |
@param[in] Veinfo VE Info | |
@retval 0 Event handled successfully | |
@return New exception value to propagate | |
**/ | |
STATIC | |
UINT64 | |
WriteMsrExit ( | |
IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs, | |
IN TDCALL_VEINFO_RETURN_DATA *Veinfo | |
) | |
{ | |
UINT64 Status; | |
MSR_DATA Data; | |
Data.Regs.Eax = (UINT32)Regs->Rax; | |
Data.Regs.Edx = (UINT32)Regs->Rdx; | |
Status = TdVmCall (EXIT_REASON_MSR_WRITE, Regs->Rcx, Data.Val, 0, 0, NULL); | |
return Status; | |
} | |
STATIC | |
VOID | |
EFIAPI | |
TdxDecodeInstruction ( | |
IN UINT8 *Rip, | |
IN UINT32 Length | |
) | |
{ | |
UINTN i; | |
DEBUG ((DEBUG_INFO, "TDX: #TD[EPT] instruction (%p):", Rip)); | |
for (i = 0; i < MIN (15, Length); i++) { | |
DEBUG ((DEBUG_INFO, "%02x ", Rip[i])); | |
} | |
DEBUG ((DEBUG_INFO, "\n")); | |
} | |
#define TDX_DECODER_BUG_ON(x) \ | |
if ((x)) { \ | |
TdxDecodeInstruction(Rip); \ | |
TdVmCall(TDVMCALL_HALT, 0, 0, 0, 0, 0); \ | |
CpuDeadLoop (); \ | |
} | |
/** | |
* Tdx MMIO access via TdVmcall. | |
* | |
* @param MmioSize Size of the MMIO access | |
* @param ReadOrWrite Read or write operation | |
* @param GuestPA Guest physical address | |
* @param Val Pointer to the value which is read or written | |
* @retval EFI_SUCCESS Successfully access the mmio | |
* @retval Others Other errors as indicated | |
*/ | |
STATIC | |
EFI_STATUS | |
TdxMmioReadWrite ( | |
IN UINT32 MmioSize, | |
IN UINT32 ReadOrWrite, | |
IN UINT64 GuestPA, | |
IN UINT64 *Val | |
) | |
{ | |
UINT64 TdStatus; | |
if ((MmioSize != 1) && (MmioSize != 2) && (MmioSize != 4) && (MmioSize != 8)) { | |
DEBUG ((DEBUG_ERROR, "%a: Invalid MmioSize - %d\n", __func__, MmioSize)); | |
return EFI_INVALID_PARAMETER; | |
} | |
if (Val == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
TdStatus = 0; | |
if (ReadOrWrite == TDX_MMIO_READ) { | |
TdStatus = TdVmCall (TDVMCALL_MMIO, MmioSize, TDX_MMIO_READ, GuestPA, 0, Val); | |
} else if (ReadOrWrite == TDX_MMIO_WRITE) { | |
TdStatus = TdVmCall (TDVMCALL_MMIO, MmioSize, TDX_MMIO_WRITE, GuestPA, *Val, 0); | |
} else { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (TdStatus != 0) { | |
DEBUG ((DEBUG_ERROR, "%a: TdVmcall failed with %llx\n", __func__, TdStatus)); | |
return EFI_ABORTED; | |
} | |
return EFI_SUCCESS; | |
} | |
typedef struct { | |
UINT8 OpCode; | |
UINT32 Bytes; | |
EFI_PHYSICAL_ADDRESS Address; | |
UINT64 Val; | |
UINT64 *Register; | |
UINT32 ReadOrWrite; | |
} MMIO_EXIT_PARSED_INSTRUCTION; | |
/** | |
* Parse the MMIO instructions. | |
* | |
* @param Regs Pointer to the EFI_SYSTEM_CONTEXT_X64 which includes the instructions | |
* @param InstructionData Pointer to the CC_INSTRUCTION_DATA | |
* @param ParsedInstruction Pointer to the parsed instruction data | |
* | |
* @retval EFI_SUCCESS Successfully parsed the instructions | |
* @retval Others Other error as indicated | |
*/ | |
STATIC | |
EFI_STATUS | |
ParseMmioExitInstructions ( | |
IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs, | |
IN OUT CC_INSTRUCTION_DATA *InstructionData, | |
OUT MMIO_EXIT_PARSED_INSTRUCTION *ParsedInstruction | |
) | |
{ | |
EFI_STATUS Status; | |
UINT8 OpCode; | |
UINT8 SignByte; | |
UINT32 Bytes; | |
EFI_PHYSICAL_ADDRESS Address; | |
UINT64 Val; | |
UINT64 *Register; | |
UINT32 ReadOrWrite; | |
Address = 0; | |
Bytes = 0; | |
Register = NULL; | |
Status = EFI_SUCCESS; | |
Val = 0; | |
Status = CcInitInstructionData (InstructionData, NULL, Regs); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_ERROR, "%a: Initialize InstructionData failed! (%r)\n", __func__, Status)); | |
return Status; | |
} | |
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: | |
CcDecodeModRm (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) { | |
DEBUG ((DEBUG_ERROR, "%a: Parse Ext.ModRm.Mod error! (OpCode: 0x%x)\n", __func__, OpCode)); | |
return EFI_UNSUPPORTED; | |
} | |
Address = InstructionData->Ext.RmData; | |
Val = InstructionData->Ext.RegData; | |
ReadOrWrite = TDX_MMIO_WRITE; | |
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; | |
CopyMem (&Address, InstructionData->Immediate, InstructionData->ImmediateSize); | |
Val = Regs->Rax; | |
ReadOrWrite = TDX_MMIO_WRITE; | |
break; | |
// | |
// MMIO write (MOV reg/memX, immX) | |
// | |
case 0xC6: | |
Bytes = 1; | |
// | |
// fall through | |
// | |
case 0xC7: | |
CcDecodeModRm (Regs, InstructionData); | |
Bytes = ((Bytes != 0) ? Bytes : | |
(InstructionData->DataSize == Size16Bits) ? 2 : | |
(InstructionData->DataSize == Size32Bits) ? 4 : | |
(InstructionData->DataSize == Size64Bits) ? 8 : | |
0); | |
InstructionData->ImmediateSize = Bytes; | |
InstructionData->End += Bytes; | |
Val = 0; | |
CopyMem (&Val, InstructionData->Immediate, InstructionData->ImmediateSize); | |
Address = InstructionData->Ext.RmData; | |
ReadOrWrite = TDX_MMIO_WRITE; | |
break; | |
// | |
// MMIO read (MOV regX, reg/memX) | |
// | |
case 0x8A: | |
Bytes = 1; | |
// | |
// fall through | |
// | |
case 0x8B: | |
CcDecodeModRm (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??? | |
// | |
DEBUG ((DEBUG_ERROR, "%a: Parse Ext.ModRm.Mod error! (OpCode: 0x%x)\n", __func__, OpCode)); | |
return EFI_UNSUPPORTED; | |
} | |
Address = InstructionData->Ext.RmData; | |
ReadOrWrite = TDX_MMIO_READ; | |
Register = CcGetRegisterPointer (Regs, InstructionData->Ext.ModRm.Reg); | |
if (Register == NULL) { | |
return EFI_ABORTED; | |
} | |
if (Bytes == 4) { | |
// | |
// Zero-extend for 32-bit operation | |
// | |
*Register = 0; | |
} | |
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; | |
Address = 0; | |
CopyMem ( | |
&Address, | |
InstructionData->Immediate, | |
InstructionData->ImmediateSize | |
); | |
if (Bytes == 4) { | |
// | |
// Zero-extend for 32-bit operation | |
// | |
Regs->Rax = 0; | |
} | |
Register = &Regs->Rax; | |
ReadOrWrite = TDX_MMIO_READ; | |
break; | |
// | |
// MMIO read w/ zero-extension ((MOVZX regX, reg/memX) | |
// | |
case 0xB6: | |
Bytes = 1; | |
// | |
// fall through | |
// | |
case 0xB7: | |
CcDecodeModRm (Regs, InstructionData); | |
Bytes = (Bytes != 0) ? Bytes : 2; | |
Address = InstructionData->Ext.RmData; | |
Register = CcGetRegisterPointer (Regs, InstructionData->Ext.ModRm.Reg); | |
if (Register == NULL) { | |
return EFI_ABORTED; | |
} | |
SetMem (Register, (UINTN)(1 << InstructionData->DataSize), 0); | |
ReadOrWrite = TDX_MMIO_READ; | |
break; | |
// | |
// MMIO read w/ sign-extension (MOVSX regX, reg/memX) | |
// | |
case 0xBE: | |
Bytes = 1; | |
// | |
// fall through | |
// | |
case 0xBF: | |
CcDecodeModRm (Regs, InstructionData); | |
Bytes = (Bytes != 0) ? Bytes : 2; | |
Address = InstructionData->Ext.RmData; | |
if (Bytes == 1) { | |
UINT8 *Data; | |
Data = (UINT8 *)&Val; | |
SignByte = ((*Data & BIT7) != 0) ? 0xFF : 0x00; | |
} else { | |
UINT16 *Data; | |
Data = (UINT16 *)&Val; | |
SignByte = ((*Data & BIT15) != 0) ? 0xFF : 0x00; | |
} | |
Register = CcGetRegisterPointer (Regs, InstructionData->Ext.ModRm.Reg); | |
if (Register == NULL) { | |
return EFI_ABORTED; | |
} | |
SetMem (Register, (UINTN)(1 << InstructionData->DataSize), SignByte); | |
ReadOrWrite = TDX_MMIO_READ; | |
break; | |
default: | |
DEBUG ((DEBUG_ERROR, "%a: Invalid MMIO opcode (%x)\n", __func__, OpCode)); | |
Status = EFI_UNSUPPORTED; | |
} | |
if (!EFI_ERROR (Status)) { | |
ParsedInstruction->OpCode = OpCode; | |
ParsedInstruction->Address = Address; | |
ParsedInstruction->Bytes = Bytes; | |
ParsedInstruction->Register = Register; | |
ParsedInstruction->Val = Val; | |
ParsedInstruction->ReadOrWrite = ReadOrWrite; | |
} | |
return Status; | |
} | |
/** | |
Handle an MMIO event. | |
Use the TDVMCALL instruction to handle either an mmio read or an mmio write. | |
@param[in, out] Regs x64 processor context | |
@param[in] Veinfo VE Info | |
@retval 0 Event handled successfully | |
**/ | |
STATIC | |
UINT64 | |
EFIAPI | |
MmioExit ( | |
IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs, | |
IN TDCALL_VEINFO_RETURN_DATA *Veinfo | |
) | |
{ | |
UINT64 TdStatus; | |
EFI_STATUS Status; | |
TD_RETURN_DATA TdReturnData; | |
UINT8 Gpaw; | |
UINT64 Val; | |
UINT64 TdSharedPageMask; | |
CC_INSTRUCTION_DATA InstructionData; | |
MMIO_EXIT_PARSED_INSTRUCTION ParsedInstruction; | |
TdStatus = TdCall (TDCALL_TDINFO, 0, 0, 0, &TdReturnData); | |
if (TdStatus == TDX_EXIT_REASON_SUCCESS) { | |
Gpaw = (UINT8)(TdReturnData.TdInfo.Gpaw & 0x3f); | |
TdSharedPageMask = 1ULL << (Gpaw - 1); | |
} else { | |
DEBUG ((DEBUG_ERROR, "%a: TDCALL failed with status=%llx\n", __func__, TdStatus)); | |
goto FatalError; | |
} | |
if ((Veinfo->GuestPA & TdSharedPageMask) == 0) { | |
DEBUG ((DEBUG_ERROR, "%a: EPT-violation #VE on private memory is not allowed!", __func__)); | |
goto FatalError; | |
} | |
Status = ParseMmioExitInstructions (Regs, &InstructionData, &ParsedInstruction); | |
if (EFI_ERROR (Status)) { | |
goto FatalError; | |
} | |
if (Veinfo->GuestPA != (ParsedInstruction.Address | TdSharedPageMask)) { | |
DEBUG (( | |
DEBUG_ERROR, | |
"%a: Address is not correct! (%d: 0x%llx != 0x%llx)\n", | |
__func__, | |
ParsedInstruction.OpCode, | |
Veinfo->GuestPA, | |
ParsedInstruction.Address | |
)); | |
goto FatalError; | |
} | |
if (ParsedInstruction.ReadOrWrite == TDX_MMIO_WRITE ) { | |
Status = TdxMmioReadWrite (ParsedInstruction.Bytes, TDX_MMIO_WRITE, Veinfo->GuestPA, &ParsedInstruction.Val); | |
} else if (ParsedInstruction.ReadOrWrite == TDX_MMIO_READ) { | |
Val = 0; | |
Status = TdxMmioReadWrite (ParsedInstruction.Bytes, TDX_MMIO_READ, Veinfo->GuestPA, &Val); | |
if (!EFI_ERROR (Status)) { | |
CopyMem (ParsedInstruction.Register, &Val, ParsedInstruction.Bytes); | |
} | |
} else { | |
goto FatalError; | |
} | |
if (EFI_ERROR (Status)) { | |
goto FatalError; | |
} | |
// | |
// We change instruction length to reflect true size so handler can | |
// bump rip | |
// | |
Veinfo->ExitInstructionLength = (UINT32)(CcInstructionLength (&InstructionData)); | |
TdxDecodeInstruction ((UINT8 *)Regs->Rip, Veinfo->ExitInstructionLength); | |
return 0; | |
FatalError: | |
TdVmCall (TDVMCALL_HALT, 0, 0, 0, 0, 0); | |
CpuDeadLoop (); | |
return 0; | |
} | |
/** | |
Handle a #VE exception. | |
Performs the necessary processing to handle a #VE exception. | |
@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 #VE not supported, (new) exception value to | |
propagate provided | |
@retval EFI_PROTOCOL_ERROR #VE handling failed, (new) exception value to | |
propagate provided | |
**/ | |
EFI_STATUS | |
EFIAPI | |
CcExitHandleVe ( | |
IN OUT EFI_EXCEPTION_TYPE *ExceptionType, | |
IN OUT EFI_SYSTEM_CONTEXT SystemContext | |
) | |
{ | |
UINT64 Status; | |
TD_RETURN_DATA ReturnData; | |
EFI_SYSTEM_CONTEXT_X64 *Regs; | |
Regs = SystemContext.SystemContextX64; | |
Status = TdCall (TDCALL_TDGETVEINFO, 0, 0, 0, &ReturnData); | |
ASSERT (Status == 0); | |
if (Status != 0) { | |
DEBUG ((DEBUG_ERROR, "#VE happened. TDGETVEINFO failed with Status = 0x%llx\n", Status)); | |
TdVmCall (TDVMCALL_HALT, 0, 0, 0, 0, 0); | |
CpuDeadLoop (); | |
} | |
switch (ReturnData.VeInfo.ExitReason) { | |
case EXIT_REASON_CPUID: | |
Status = CpuIdExit (Regs, &ReturnData.VeInfo); | |
DEBUG (( | |
DEBUG_VERBOSE, | |
"CPUID #VE happened, ExitReasion is %d, ExitQualification = 0x%x.\n", | |
ReturnData.VeInfo.ExitReason, | |
ReturnData.VeInfo.ExitQualification.Val | |
)); | |
break; | |
case EXIT_REASON_HLT: | |
Status = TdVmCall (EXIT_REASON_HLT, 0, 0, 0, 0, 0); | |
break; | |
case EXIT_REASON_IO_INSTRUCTION: | |
Status = IoExit (Regs, &ReturnData.VeInfo); | |
DEBUG (( | |
DEBUG_VERBOSE, | |
"IO_Instruction #VE happened, ExitReasion is %d, ExitQualification = 0x%x.\n", | |
ReturnData.VeInfo.ExitReason, | |
ReturnData.VeInfo.ExitQualification.Val | |
)); | |
break; | |
case EXIT_REASON_MSR_READ: | |
Status = ReadMsrExit (Regs, &ReturnData.VeInfo); | |
DEBUG (( | |
DEBUG_VERBOSE, | |
"RDMSR #VE happened, ExitReasion is %d, ExitQualification = 0x%x. Regs->Rcx=0x%llx, Status = 0x%llx\n", | |
ReturnData.VeInfo.ExitReason, | |
ReturnData.VeInfo.ExitQualification.Val, | |
Regs->Rcx, | |
Status | |
)); | |
break; | |
case EXIT_REASON_MSR_WRITE: | |
Status = WriteMsrExit (Regs, &ReturnData.VeInfo); | |
DEBUG (( | |
DEBUG_VERBOSE, | |
"WRMSR #VE happened, ExitReasion is %d, ExitQualification = 0x%x. Regs->Rcx=0x%llx, Status = 0x%llx\n", | |
ReturnData.VeInfo.ExitReason, | |
ReturnData.VeInfo.ExitQualification.Val, | |
Regs->Rcx, | |
Status | |
)); | |
break; | |
case EXIT_REASON_EPT_VIOLATION: | |
Status = MmioExit (Regs, &ReturnData.VeInfo); | |
DEBUG (( | |
DEBUG_VERBOSE, | |
"MMIO #VE happened, ExitReasion is %d, ExitQualification = 0x%x.\n", | |
ReturnData.VeInfo.ExitReason, | |
ReturnData.VeInfo.ExitQualification.Val | |
)); | |
break; | |
case EXIT_REASON_VMCALL: | |
case EXIT_REASON_MWAIT_INSTRUCTION: | |
case EXIT_REASON_MONITOR_INSTRUCTION: | |
case EXIT_REASON_WBINVD: | |
case EXIT_REASON_RDPMC: | |
case EXIT_REASON_INVD: | |
/* Handle as nops. */ | |
break; | |
default: | |
DEBUG (( | |
DEBUG_ERROR, | |
"Unsupported #VE happened, ExitReason is %d, ExitQualification = 0x%x.\n", | |
ReturnData.VeInfo.ExitReason, | |
ReturnData.VeInfo.ExitQualification.Val | |
)); | |
ASSERT (FALSE); | |
CpuDeadLoop (); | |
} | |
if (Status) { | |
DEBUG (( | |
DEBUG_ERROR, | |
"#VE Error (0x%llx) returned from host, ExitReason is %d, ExitQualification = 0x%x.\n", | |
Status, | |
ReturnData.VeInfo.ExitReason, | |
ReturnData.VeInfo.ExitQualification.Val | |
)); | |
TdVmCall (TDVMCALL_HALT, 0, 0, 0, 0, 0); | |
CpuDeadLoop (); | |
} | |
SystemContext.SystemContextX64->Rip += ReturnData.VeInfo.ExitInstructionLength; | |
return EFI_SUCCESS; | |
} |