mirror of
https://github.com/CloverHackyColor/CloverBootloader.git
synced 2024-12-02 13:03:28 +01:00
798 lines
21 KiB
C
798 lines
21 KiB
C
|
/** @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;
|
||
|
}
|