123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577 |
- /** @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 "VmTdExitHandler.h"
- #include <Library/VmgExitLib.h>
- #include <Library/BaseMemoryLib.h>
- #include <IndustryStandard/Tdx.h>
- #include <IndustryStandard/InstructionParsing.h>
- 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;
- 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
- )
- {
- UINTN i;
- DEBUG ((DEBUG_INFO, "TDX: #TD[EPT] instruction (%p):", Rip));
- for (i = 0; i < 15; 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); \
- }
- STATIC
- UINT64 *
- EFIAPI
- GetRegFromContext (
- IN EFI_SYSTEM_CONTEXT_X64 *Regs,
- IN UINTN RegIndex
- )
- {
- switch (RegIndex) {
- case 0: return &Regs->Rax;
- break;
- case 1: return &Regs->Rcx;
- break;
- case 2: return &Regs->Rdx;
- break;
- case 3: return &Regs->Rbx;
- break;
- case 4: return &Regs->Rsp;
- break;
- case 5: return &Regs->Rbp;
- break;
- case 6: return &Regs->Rsi;
- break;
- case 7: return &Regs->Rdi;
- break;
- case 8: return &Regs->R8;
- break;
- case 9: return &Regs->R9;
- break;
- case 10: return &Regs->R10;
- break;
- case 11: return &Regs->R11;
- break;
- case 12: return &Regs->R12;
- break;
- case 13: return &Regs->R13;
- break;
- case 14: return &Regs->R14;
- break;
- case 15: return &Regs->R15;
- break;
- }
- return NULL;
- }
- /**
- 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
- @return New exception value to propagate
- **/
- STATIC
- INTN
- EFIAPI
- MmioExit (
- IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs,
- IN TDCALL_VEINFO_RETURN_DATA *Veinfo
- )
- {
- UINT64 Status;
- UINT32 MmioSize;
- UINT32 RegSize;
- UINT8 OpCode;
- BOOLEAN SeenRex;
- UINT64 *Reg;
- UINT8 *Rip;
- UINT64 Val;
- UINT32 OpSize;
- MODRM ModRm;
- REX Rex;
- TD_RETURN_DATA TdReturnData;
- UINT8 Gpaw;
- UINT64 TdSharedPageMask;
- Rip = (UINT8 *)Regs->Rip;
- Val = 0;
- Rex.Val = 0;
- SeenRex = FALSE;
- Status = TdCall (TDCALL_TDINFO, 0, 0, 0, &TdReturnData);
- if (Status == TDX_EXIT_REASON_SUCCESS) {
- Gpaw = (UINT8)(TdReturnData.TdInfo.Gpaw & 0x3f);
- TdSharedPageMask = 1ULL << (Gpaw - 1);
- } else {
- DEBUG ((DEBUG_ERROR, "TDCALL failed with status=%llx\n", Status));
- return Status;
- }
- if ((Veinfo->GuestPA & TdSharedPageMask) == 0) {
- DEBUG ((DEBUG_ERROR, "EPT-violation #VE on private memory is not allowed!"));
- TdVmCall (TDVMCALL_HALT, 0, 0, 0, 0, 0);
- CpuDeadLoop ();
- }
- //
- // Default to 32bit transfer
- //
- OpSize = 4;
- do {
- OpCode = *Rip++;
- if (OpCode == 0x66) {
- OpSize = 2;
- } else if ((OpCode == 0x64) || (OpCode == 0x65) || (OpCode == 0x67)) {
- continue;
- } else if ((OpCode >= 0x40) && (OpCode <= 0x4f)) {
- SeenRex = TRUE;
- Rex.Val = OpCode;
- } else {
- break;
- }
- } while (TRUE);
- //
- // We need to have at least 2 more bytes for this instruction
- //
- TDX_DECODER_BUG_ON (((UINT64)Rip - Regs->Rip) > 13);
- OpCode = *Rip++;
- //
- // Two-byte opecode, get next byte
- //
- if (OpCode == 0x0F) {
- OpCode = *Rip++;
- }
- switch (OpCode) {
- case 0x88:
- case 0x8A:
- case 0xB6:
- MmioSize = 1;
- break;
- case 0xB7:
- MmioSize = 2;
- break;
- default:
- MmioSize = Rex.Bits.W ? 8 : OpSize;
- break;
- }
- /* Punt on AH/BH/CH/DH unless it shows up. */
- ModRm.Val = *Rip++;
- TDX_DECODER_BUG_ON (MmioSize == 1 && ModRm.Bits.Reg > 4 && !SeenRex && OpCode != 0xB6);
- Reg = GetRegFromContext (Regs, ModRm.Bits.Reg | ((int)Rex.Bits.R << 3));
- TDX_DECODER_BUG_ON (!Reg);
- if (ModRm.Bits.Rm == 4) {
- ++Rip; /* SIB byte */
- }
- if ((ModRm.Bits.Mod == 2) || ((ModRm.Bits.Mod == 0) && (ModRm.Bits.Rm == 5))) {
- Rip += 4; /* DISP32 */
- } else if (ModRm.Bits.Mod == 1) {
- ++Rip; /* DISP8 */
- }
- switch (OpCode) {
- case 0x88:
- case 0x89:
- CopyMem ((void *)&Val, Reg, MmioSize);
- Status = TdVmCall (TDVMCALL_MMIO, MmioSize, 1, Veinfo->GuestPA, Val, 0);
- break;
- case 0xC7:
- CopyMem ((void *)&Val, Rip, OpSize);
- Status = TdVmCall (TDVMCALL_MMIO, MmioSize, 1, Veinfo->GuestPA, Val, 0);
- Rip += OpSize;
- default:
- //
- // 32-bit write registers are zero extended to the full register
- // Hence 'MOVZX r[32/64], r/m16' is
- // hardcoded to reg size 8, and the straight MOV case has a reg
- // size of 8 in the 32-bit read case.
- //
- switch (OpCode) {
- case 0xB6:
- RegSize = Rex.Bits.W ? 8 : OpSize;
- break;
- case 0xB7:
- RegSize = 8;
- break;
- default:
- RegSize = MmioSize == 4 ? 8 : MmioSize;
- break;
- }
- Status = TdVmCall (TDVMCALL_MMIO, MmioSize, 0, Veinfo->GuestPA, 0, &Val);
- if (Status == 0) {
- ZeroMem (Reg, RegSize);
- CopyMem (Reg, (void *)&Val, MmioSize);
- }
- }
- if (Status == 0) {
- TDX_DECODER_BUG_ON (((UINT64)Rip - Regs->Rip) > 15);
- //
- // We change instruction length to reflect true size so handler can
- // bump rip
- //
- Veinfo->ExitInstructionLength = (UINT32)((UINT64)Rip - Regs->Rip);
- }
- return Status;
- }
- /**
- 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
- VmTdExitHandleVe (
- 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);
- }
- 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:
- /* 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);
- }
- SystemContext.SystemContextX64->Rip += ReturnData.VeInfo.ExitInstructionLength;
- return EFI_SUCCESS;
- }
|