| /** @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; | |
| } |