[edk2-devel] [PATCH 11/23] OvmfPkg: Implement library support for VmTdExitLib in Ovmf

Min Xu min.m.xu at intel.com
Thu Aug 12 11:56:50 UTC 2021


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

The base VmTdExitLib library provides a default limited interface. As
it does not provide full support, create an OVMF version of this library
to begin the process of providing full support of TDX within OVMF.

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

Cc: Ard Biesheuvel <ardb+tianocore at kernel.org>
Cc: Jiewen Yao <jiewen.yao at intel.com>
Cc: Jordan Justen <jordan.l.justen at intel.com>
Cc: Brijesh Singh <brijesh.singh at amd.com>
Cc: Erdem Aktas <erdemaktas at google.com>
Cc: James Bottomley <jejb at linux.ibm.com>
Cc: Jiewen Yao <jiewen.yao at intel.com>
Cc: Tom Lendacky <thomas.lendacky at amd.com>
Signed-off-by: Min Xu <min.m.xu at intel.com>
---
 OvmfPkg/Library/VmTdExitLib/VmTdExitLib.inf   |  41 ++
 .../Library/VmTdExitLib/VmTdExitVeHandler.c   | 515 ++++++++++++++++++
 OvmfPkg/OvmfPkg.dec                           |   3 +
 3 files changed, 559 insertions(+)
 create mode 100644 OvmfPkg/Library/VmTdExitLib/VmTdExitLib.inf
 create mode 100644 OvmfPkg/Library/VmTdExitLib/VmTdExitVeHandler.c

diff --git a/OvmfPkg/Library/VmTdExitLib/VmTdExitLib.inf b/OvmfPkg/Library/VmTdExitLib/VmTdExitLib.inf
new file mode 100644
index 000000000000..0d9430034330
--- /dev/null
+++ b/OvmfPkg/Library/VmTdExitLib/VmTdExitLib.inf
@@ -0,0 +1,41 @@
+## @file
+#  VMTDEXIT Support Library.
+#
+#  Copyright (c) 2020, Intel Inc. All rights reserved.<BR>
+#  Copyright (C) 2020, Advanced Micro Devices, Inc. All rights reserved.<BR>
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = VmTdExitLib
+  FILE_GUID                      = b29eabb0-f9a3-11ea-8b6e-0800200c9a66
+  MODULE_TYPE                    = BASE
+  VERSION_STRING                 = 1.0
+  LIBRARY_CLASS                  = VmTdExitLib
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+#  VALID_ARCHITECTURES           = X64
+#
+
+[Sources.common]
+  VmTdExitVeHandler.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+  OvmfPkg/OvmfPkg.dec
+  UefiCpuPkg/UefiCpuPkg.dec
+
+[LibraryClasses]
+  BaseLib
+  BaseMemoryLib
+  DebugLib
+  TdxLib
+
+[Pcd]
+  gUefiOvmfPkgTokenSpaceGuid.PcdIgnoreVeHalt
+
+
diff --git a/OvmfPkg/Library/VmTdExitLib/VmTdExitVeHandler.c b/OvmfPkg/Library/VmTdExitLib/VmTdExitVeHandler.c
new file mode 100644
index 000000000000..6e41261cd108
--- /dev/null
+++ b/OvmfPkg/Library/VmTdExitLib/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/VmTdExitLib.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/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec
index ccda130f0ef6..8d1f3b03488c 100644
--- a/OvmfPkg/OvmfPkg.dec
+++ b/OvmfPkg/OvmfPkg.dec
@@ -342,6 +342,9 @@
   ## Size of the Ovmf image in KB
   gUefiOvmfPkgTokenSpaceGuid.PcdOvmfImageSizeInKb|0|UINT32|0x4f
 
+  ## Ignore the VE halt in Tdx
+  gUefiOvmfPkgTokenSpaceGuid.PcdIgnoreVeHalt|FALSE|BOOLEAN|0x50
+
 [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 (#79167): https://edk2.groups.io/g/devel/message/79167
Mute This Topic: https://groups.io/mt/84837902/1813853
Group Owner: devel+owner at edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [edk2-devel-archive at redhat.com]
-=-=-=-=-=-=-=-=-=-=-=-





More information about the edk2-devel-archive mailing list