RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429

The base VmgExitLib library provides a default limited interface to
handle #VE exception. To provide full support, the OVMF version of
VmgExitLib is extended to provide full support of #VE handler.

PcdIgnoreVeHalt is created in OvmfPkg.dec to ignore the VE halt.

Cc: Ard Biesheuvel <ardb+tianoc...@kernel.org>
Cc: Jiewen Yao <jiewen....@intel.com>
Cc: Jordan Justen <jordan.l.jus...@intel.com>
Cc: Brijesh Singh <brijesh.si...@amd.com>
Cc: Erdem Aktas <erdemak...@google.com>
Cc: James Bottomley <j...@linux.ibm.com>
Cc: Jiewen Yao <jiewen....@intel.com>
Cc: Tom Lendacky <thomas.lenda...@amd.com>
Cc: Gerd Hoffmann <kra...@redhat.com>
Signed-off-by: Min Xu <min.m...@intel.com>
---
 OvmfPkg/Library/VmgExitLib/SecVmgExitLib.inf  |   3 +-
 .../Library/VmgExitLib/VmTdExitVeHandler.c    | 515 ++++++++++++++++++
 OvmfPkg/Library/VmgExitLib/VmgExitLib.inf     |   4 +
 OvmfPkg/OvmfPkg.dec                           |   3 +
 4 files changed, 524 insertions(+), 1 deletion(-)
 create mode 100644 OvmfPkg/Library/VmgExitLib/VmTdExitVeHandler.c

diff --git a/OvmfPkg/Library/VmgExitLib/SecVmgExitLib.inf 
b/OvmfPkg/Library/VmgExitLib/SecVmgExitLib.inf
index e6f6ea7972fd..20d381387bf9 100644
--- a/OvmfPkg/Library/VmgExitLib/SecVmgExitLib.inf
+++ b/OvmfPkg/Library/VmgExitLib/SecVmgExitLib.inf
@@ -25,6 +25,7 @@
   VmgExitVcHandler.c
   VmgExitVcHandler.h
   SecVmgExitVcHandler.c
+  VmTdExitVeHandler.c
 
 [Packages]
   MdePkg/MdePkg.dec
@@ -42,4 +43,4 @@
 [FixedPcd]
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBackupBase
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBackupSize
-
+  gUefiOvmfPkgTokenSpaceGuid.PcdIgnoreVeHalt
diff --git a/OvmfPkg/Library/VmgExitLib/VmTdExitVeHandler.c 
b/OvmfPkg/Library/VmgExitLib/VmTdExitVeHandler.c
new file mode 100644
index 000000000000..fe157208154a
--- /dev/null
+++ b/OvmfPkg/Library/VmgExitLib/VmTdExitVeHandler.c
@@ -0,0 +1,515 @@
+/** @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 <Library/TdxLib.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;
+
+  Rip = (UINT8 *) Regs->Rip;
+  Val = 0;
+  Rex.Val = 0;
+  SeenRex = FALSE;
+
+  //
+  // 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:
+    if (FixedPcdGetBool (PcdIgnoreVeHalt) == FALSE) {
+      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;
+}
diff --git a/OvmfPkg/Library/VmgExitLib/VmgExitLib.inf 
b/OvmfPkg/Library/VmgExitLib/VmgExitLib.inf
index c66c68726cdb..e3da12af2f7a 100644
--- a/OvmfPkg/Library/VmgExitLib/VmgExitLib.inf
+++ b/OvmfPkg/Library/VmgExitLib/VmgExitLib.inf
@@ -25,6 +25,7 @@
   VmgExitVcHandler.c
   VmgExitVcHandler.h
   PeiDxeVmgExitVcHandler.c
+  VmTdExitVeHandler.c
 
 [Packages]
   MdePkg/MdePkg.dec
@@ -37,4 +38,7 @@
   DebugLib
   LocalApicLib
   MemEncryptSevLib
+  TdxLib
 
+[Pcd]
+  gUefiOvmfPkgTokenSpaceGuid.PcdIgnoreVeHalt
diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec
index 340d83f794d0..2124bd639399 100644
--- a/OvmfPkg/OvmfPkg.dec
+++ b/OvmfPkg/OvmfPkg.dec
@@ -350,6 +350,9 @@
   gUefiOvmfPkgTokenSpaceGuid.PcdBfvRawDataOffset|0|UINT32|0x56
   gUefiOvmfPkgTokenSpaceGuid.PcdBfvRawDataSize|0|UINT32|0x57
 
+  ## Ignore the VE halt in Tdx
+  gUefiOvmfPkgTokenSpaceGuid.PcdIgnoreVeHalt|FALSE|BOOLEAN|0x58
+
 [PcdsDynamic, PcdsDynamicEx]
   gUefiOvmfPkgTokenSpaceGuid.PcdEmuVariableEvent|0|UINT64|2
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashVariablesEnable|FALSE|BOOLEAN|0x10
-- 
2.29.2.windows.2



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#82985): https://edk2.groups.io/g/devel/message/82985
Mute This Topic: https://groups.io/mt/86739958/21656
Mute #ve:https://edk2.groups.io/g/devel/mutehashtag/ve
Group Owner: devel+ow...@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [arch...@mail-archive.com]
-=-=-=-=-=-=-=-=-=-=-=-


Reply via email to