OvmfPkg/UefiCpuPkg/UefiPayloadPkg: Rename VmgExitLib to CcExitLib

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=4123

VmgExitLib once was designed to provide interfaces to support #VC handler
and issue VMGEXIT instruction. After TDVF (enable TDX feature in OVMF) is
introduced, this library is updated to support #VE as well. Now the name
of VmgExitLib cannot reflect what the lib does.

This patch renames VmgExitLib to CcExitLib (Cc means Confidential
Computing). This is a simple renaming and there is no logic changes.
After renaming all the VmgExitLib related codes are updated with
CcExitLib. These changes are in OvmfPkg/UefiCpuPkg/UefiPayloadPkg.

Cc: Guo Dong <guo.dong@intel.com>
Cc: Sean Rhodes <sean@starlabs.systems>
Cc: James Lu <james.lu@intel.com>
Cc: Gua Guo <gua.guo@intel.com>
Cc: Eric Dong <eric.dong@intel.com>
Cc: Ray Ni <ray.ni@intel.com>
Cc: Brijesh Singh <brijesh.singh@amd.com>
Cc: Erdem Aktas <erdemaktas@google.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Cc: James Bottomley <jejb@linux.ibm.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Reviewed-by: James Lu <james.lu@intel.com>
Reviewed-by: Gua Guo <gua.guo@intel.com>
Reviewed-by: Jiewen Yao <jiewen.yao@intel.com>
Reviewed-by: Ray Ni <ray.ni@intel.com>
Signed-off-by: Min Xu <min.m.xu@intel.com>
This commit is contained in:
Min M Xu
2022-11-07 14:30:26 +08:00
committed by mergify[bot]
parent b9e702c3c9
commit a89f558d3c
49 changed files with 135 additions and 141 deletions

View File

@@ -0,0 +1,239 @@
/** @file
CcExitLib Support Library.
Copyright (C) 2020, Advanced Micro Devices, Inc. All rights reserved.<BR>
Copyright (C) 2020 - 2022, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include <Base.h>
#include <Uefi.h>
#include <Library/BaseMemoryLib.h>
#include <Library/CcExitLib.h>
#include <Register/Amd/Msr.h>
/**
Check for VMGEXIT error
Check if the hypervisor has returned an error after completion of the VMGEXIT
by examining the SwExitInfo1 field of the GHCB.
@param[in] Ghcb A pointer to the GHCB
@retval 0 VMGEXIT succeeded.
@return Exception number to be propagated, VMGEXIT processing
did not succeed.
**/
STATIC
UINT64
VmgExitErrorCheck (
IN GHCB *Ghcb
)
{
GHCB_EVENT_INJECTION Event;
GHCB_EXIT_INFO ExitInfo;
UINT64 Status;
ExitInfo.Uint64 = Ghcb->SaveArea.SwExitInfo1;
ASSERT (
(ExitInfo.Elements.Lower32Bits == 0) ||
(ExitInfo.Elements.Lower32Bits == 1)
);
Status = 0;
if (ExitInfo.Elements.Lower32Bits == 0) {
return Status;
}
if (ExitInfo.Elements.Lower32Bits == 1) {
ASSERT (Ghcb->SaveArea.SwExitInfo2 != 0);
//
// Check that the return event is valid
//
Event.Uint64 = Ghcb->SaveArea.SwExitInfo2;
if (Event.Elements.Valid &&
(Event.Elements.Type == GHCB_EVENT_INJECTION_TYPE_EXCEPTION))
{
switch (Event.Elements.Vector) {
case GP_EXCEPTION:
case UD_EXCEPTION:
//
// Use returned event as return code
//
Status = Event.Uint64;
}
}
}
if (Status == 0) {
GHCB_EVENT_INJECTION GpEvent;
GpEvent.Uint64 = 0;
GpEvent.Elements.Vector = GP_EXCEPTION;
GpEvent.Elements.Type = GHCB_EVENT_INJECTION_TYPE_EXCEPTION;
GpEvent.Elements.Valid = 1;
Status = GpEvent.Uint64;
}
return Status;
}
/**
Perform VMGEXIT.
Sets the necessary fields of the GHCB, invokes the VMGEXIT instruction and
then handles the return actions.
@param[in, out] Ghcb A pointer to the GHCB
@param[in] ExitCode VMGEXIT code to be assigned to the SwExitCode
field of the GHCB.
@param[in] ExitInfo1 VMGEXIT information to be assigned to the
SwExitInfo1 field of the GHCB.
@param[in] ExitInfo2 VMGEXIT information to be assigned to the
SwExitInfo2 field of the GHCB.
@retval 0 VMGEXIT succeeded.
@return Exception number to be propagated, VMGEXIT
processing did not succeed.
**/
UINT64
EFIAPI
VmgExit (
IN OUT GHCB *Ghcb,
IN UINT64 ExitCode,
IN UINT64 ExitInfo1,
IN UINT64 ExitInfo2
)
{
Ghcb->SaveArea.SwExitCode = ExitCode;
Ghcb->SaveArea.SwExitInfo1 = ExitInfo1;
Ghcb->SaveArea.SwExitInfo2 = ExitInfo2;
VmgSetOffsetValid (Ghcb, GhcbSwExitCode);
VmgSetOffsetValid (Ghcb, GhcbSwExitInfo1);
VmgSetOffsetValid (Ghcb, GhcbSwExitInfo2);
//
// Guest memory is used for the guest-hypervisor communication, so fence
// the invocation of the VMGEXIT instruction to ensure GHCB accesses are
// synchronized properly.
//
MemoryFence ();
AsmVmgExit ();
MemoryFence ();
return VmgExitErrorCheck (Ghcb);
}
/**
Perform pre-VMGEXIT initialization/preparation.
Performs the necessary steps in preparation for invoking VMGEXIT. Must be
called before setting any fields within the GHCB.
@param[in, out] Ghcb A pointer to the GHCB
@param[in, out] InterruptState A pointer to hold the current interrupt
state, used for restoring in VmgDone ()
**/
VOID
EFIAPI
VmgInit (
IN OUT GHCB *Ghcb,
IN OUT BOOLEAN *InterruptState
)
{
//
// Be sure that an interrupt can't cause a #VC while the GHCB is
// being used.
//
*InterruptState = GetInterruptState ();
if (*InterruptState) {
DisableInterrupts ();
}
SetMem (&Ghcb->SaveArea, sizeof (Ghcb->SaveArea), 0);
}
/**
Perform post-VMGEXIT cleanup.
Performs the necessary steps to cleanup after invoking VMGEXIT. Must be
called after obtaining needed fields within the GHCB.
@param[in, out] Ghcb A pointer to the GHCB
@param[in] InterruptState An indicator to conditionally (re)enable
interrupts
**/
VOID
EFIAPI
VmgDone (
IN OUT GHCB *Ghcb,
IN BOOLEAN InterruptState
)
{
if (InterruptState) {
EnableInterrupts ();
}
}
/**
Marks a field at the specified offset as valid in the GHCB.
The ValidBitmap area represents the areas of the GHCB that have been marked
valid. Set the bit in ValidBitmap for the input offset.
@param[in, out] Ghcb Pointer to the Guest-Hypervisor Communication Block
@param[in] Offset Qword offset in the GHCB to mark valid
**/
VOID
EFIAPI
VmgSetOffsetValid (
IN OUT GHCB *Ghcb,
IN GHCB_REGISTER Offset
)
{
UINT32 OffsetIndex;
UINT32 OffsetBit;
OffsetIndex = Offset / 8;
OffsetBit = Offset % 8;
Ghcb->SaveArea.ValidBitmap[OffsetIndex] |= (1 << OffsetBit);
}
/**
Checks if a specified offset is valid in the GHCB.
The ValidBitmap area represents the areas of the GHCB that have been marked
valid. Return whether the bit in the ValidBitmap is set for the input offset.
@param[in] Ghcb A pointer to the GHCB
@param[in] Offset Qword offset in the GHCB to mark valid
@retval TRUE Offset is marked valid in the GHCB
@retval FALSE Offset is not marked valid in the GHCB
**/
BOOLEAN
EFIAPI
VmgIsOffsetValid (
IN GHCB *Ghcb,
IN GHCB_REGISTER Offset
)
{
UINT32 OffsetIndex;
UINT32 OffsetBit;
OffsetIndex = Offset / 8;
OffsetBit = Offset % 8;
return ((Ghcb->SaveArea.ValidBitmap[OffsetIndex] & (1 << OffsetBit)) != 0);
}

View File

@@ -0,0 +1,46 @@
## @file
# CcExitLib Library.
#
# Copyright (C) 2020, Advanced Micro Devices, Inc. All rights reserved.<BR>
# Copyright (C) 2020 - 2022, Intel Corporation. All rights reserved.<BR>
# SPDX-License-Identifier: BSD-2-Clause-Patent
#
##
[Defines]
INF_VERSION = 0x00010005
BASE_NAME = CcExitLib
FILE_GUID = 0e923c25-13cd-430b-8714-ffe85652a97b
MODULE_TYPE = BASE
VERSION_STRING = 1.0
LIBRARY_CLASS = CcExitLib|PEIM DXE_CORE DXE_DRIVER DXE_RUNTIME_DRIVER DXE_SMM_DRIVER UEFI_DRIVER
#
# The following information is for reference only and not required by the build tools.
#
# VALID_ARCHITECTURES = X64
#
[Sources.common]
CcExitLib.c
CcExitVcHandler.c
CcExitVcHandler.h
PeiDxeCcExitVcHandler.c
CcExitVeHandler.c
X64/TdVmcallCpuid.nasm
[Packages]
MdePkg/MdePkg.dec
OvmfPkg/OvmfPkg.dec
UefiCpuPkg/UefiCpuPkg.dec
[LibraryClasses]
BaseLib
BaseMemoryLib
DebugLib
LocalApicLib
MemEncryptSevLib
[Pcd]
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfCpuidBase
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfCpuidSize

View File

@@ -0,0 +1,32 @@
/** @file
Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#ifndef CC_EXIT_TD_H_
#define CC_EXIT_TD_H_
#include <Base.h>
#include <Uefi.h>
/**
This function enable the TD guest to request the VMM to emulate CPUID
operation, especially for non-architectural, CPUID leaves.
@param[in] Eax Main leaf of the CPUID
@param[in] Ecx Sub-leaf of the CPUID
@param[out] Results Returned result of CPUID operation
@return EFI_SUCCESS
**/
EFI_STATUS
EFIAPI
TdVmCallCpuid (
IN UINT64 Eax,
IN UINT64 Ecx,
OUT VOID *Results
);
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,53 @@
/** @file
X64 #VC Exception Handler functon header file.
Copyright (C) 2020, Advanced Micro Devices, Inc. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#ifndef CC_EXIT_VC_HANDLER_H_
#define CC_EXIT_VC_HANDLER_H_
#include <Base.h>
#include <Uefi.h>
#include <Library/CcExitLib.h>
/**
Handle a #VC exception.
Performs the necessary processing to handle a #VC exception.
@param[in, out] Ghcb Pointer to the GHCB
@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 #VC not supported, (new) exception value to
propagate provided
@retval EFI_PROTOCOL_ERROR #VC handling failed, (new) exception value to
propagate provided
**/
EFI_STATUS
EFIAPI
InternalVmgExitHandleVc (
IN OUT GHCB *Ghcb,
IN OUT EFI_EXCEPTION_TYPE *ExceptionType,
IN OUT EFI_SYSTEM_CONTEXT SystemContext
);
/**
Routine to allow ASSERT from within #VC.
@param[in, out] SevEsData Pointer to the per-CPU data
**/
VOID
EFIAPI
VmgExitIssueAssert (
IN OUT SEV_ES_PER_CPU_DATA *SevEsData
);
#endif

View File

@@ -0,0 +1,577 @@
/** @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>
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;
}

View File

@@ -0,0 +1,103 @@
/** @file
X64 #VC Exception Handler functon.
Copyright (C) 2020, Advanced Micro Devices, Inc. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include <Base.h>
#include <Uefi.h>
#include <Library/BaseMemoryLib.h>
#include <Library/MemEncryptSevLib.h>
#include <Library/CcExitLib.h>
#include <Register/Amd/Msr.h>
#include "CcExitVcHandler.h"
/**
Handle a #VC exception.
Performs the necessary processing to handle a #VC 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 #VC not supported, (new) exception value to
propagate provided
@retval EFI_PROTOCOL_ERROR #VC handling failed, (new) exception value to
propagate provided
**/
EFI_STATUS
EFIAPI
VmgExitHandleVc (
IN OUT EFI_EXCEPTION_TYPE *ExceptionType,
IN OUT EFI_SYSTEM_CONTEXT SystemContext
)
{
MSR_SEV_ES_GHCB_REGISTER Msr;
GHCB *Ghcb;
GHCB *GhcbBackup;
EFI_STATUS VcRet;
BOOLEAN InterruptState;
SEV_ES_PER_CPU_DATA *SevEsData;
InterruptState = GetInterruptState ();
if (InterruptState) {
DisableInterrupts ();
}
Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);
ASSERT (Msr.GhcbInfo.Function == 0);
ASSERT (Msr.Ghcb != 0);
Ghcb = Msr.Ghcb;
GhcbBackup = NULL;
SevEsData = (SEV_ES_PER_CPU_DATA *)(Ghcb + 1);
SevEsData->VcCount++;
//
// Check for maximum PEI/DXE #VC nesting.
//
if (SevEsData->VcCount > VMGEXIT_MAXIMUM_VC_COUNT) {
VmgExitIssueAssert (SevEsData);
} else if (SevEsData->VcCount > 1) {
//
// Nested #VC
//
if (SevEsData->GhcbBackupPages == NULL) {
VmgExitIssueAssert (SevEsData);
}
//
// Save the active GHCB to a backup page.
// To access the correct backup page, increment the backup page pointer
// based on the current VcCount.
//
GhcbBackup = (GHCB *)SevEsData->GhcbBackupPages;
GhcbBackup += (SevEsData->VcCount - 2);
CopyMem (GhcbBackup, Ghcb, sizeof (*Ghcb));
}
VcRet = InternalVmgExitHandleVc (Ghcb, ExceptionType, SystemContext);
if (GhcbBackup != NULL) {
//
// Restore the active GHCB from the backup page.
//
CopyMem (Ghcb, GhcbBackup, sizeof (*Ghcb));
}
SevEsData->VcCount--;
if (InterruptState) {
EnableInterrupts ();
}
return VcRet;
}

View File

@@ -0,0 +1,48 @@
## @file
# VMGEXIT Support Library.
#
# Copyright (C) 2020, Advanced Micro Devices, Inc. All rights reserved.<BR>
# SPDX-License-Identifier: BSD-2-Clause-Patent
#
##
[Defines]
INF_VERSION = 0x00010005
BASE_NAME = SecCcExitLib
FILE_GUID = dafff819-f86c-4cff-a70e-83161e5bcf9a
MODULE_TYPE = BASE
VERSION_STRING = 1.0
LIBRARY_CLASS = CcExitLib|SEC
#
# The following information is for reference only and not required by the build tools.
#
# VALID_ARCHITECTURES = X64
#
[Sources.common]
CcExitLib.c
CcExitVcHandler.c
CcExitVcHandler.h
SecCcExitVcHandler.c
CcExitVeHandler.c
X64/TdVmcallCpuid.nasm
[Packages]
MdePkg/MdePkg.dec
OvmfPkg/OvmfPkg.dec
UefiCpuPkg/UefiCpuPkg.dec
[LibraryClasses]
BaseLib
BaseMemoryLib
DebugLib
LocalApicLib
MemEncryptSevLib
PcdLib
[FixedPcd]
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBackupBase
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBackupSize
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfCpuidBase
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfCpuidSize

View File

@@ -0,0 +1,109 @@
/** @file
X64 #VC Exception Handler functon.
Copyright (C) 2020, Advanced Micro Devices, Inc. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include <Base.h>
#include <Uefi.h>
#include <Library/BaseMemoryLib.h>
#include <Library/MemEncryptSevLib.h>
#include <Library/CcExitLib.h>
#include <Register/Amd/Msr.h>
#include "CcExitVcHandler.h"
/**
Handle a #VC exception.
Performs the necessary processing to handle a #VC 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 #VC not supported, (new) exception value to
propagate provided
@retval EFI_PROTOCOL_ERROR #VC handling failed, (new) exception value to
propagate provided
**/
EFI_STATUS
EFIAPI
VmgExitHandleVc (
IN OUT EFI_EXCEPTION_TYPE *ExceptionType,
IN OUT EFI_SYSTEM_CONTEXT SystemContext
)
{
MSR_SEV_ES_GHCB_REGISTER Msr;
GHCB *Ghcb;
GHCB *GhcbBackup;
EFI_STATUS VcRet;
BOOLEAN InterruptState;
SEV_ES_PER_CPU_DATA *SevEsData;
InterruptState = GetInterruptState ();
if (InterruptState) {
DisableInterrupts ();
}
Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);
ASSERT (Msr.GhcbInfo.Function == 0);
ASSERT (Msr.Ghcb != 0);
Ghcb = Msr.Ghcb;
GhcbBackup = NULL;
SevEsData = (SEV_ES_PER_CPU_DATA *)(Ghcb + 1);
SevEsData->VcCount++;
//
// Check for maximum SEC #VC nesting.
//
if (SevEsData->VcCount > VMGEXIT_MAXIMUM_VC_COUNT) {
VmgExitIssueAssert (SevEsData);
} else if (SevEsData->VcCount > 1) {
UINTN GhcbBackupSize;
//
// Be sure that the proper amount of pages are allocated
//
GhcbBackupSize = (VMGEXIT_MAXIMUM_VC_COUNT - 1) * sizeof (*Ghcb);
if (GhcbBackupSize > FixedPcdGet32 (PcdOvmfSecGhcbBackupSize)) {
//
// Not enough SEC backup pages allocated.
//
VmgExitIssueAssert (SevEsData);
}
//
// Save the active GHCB to a backup page.
// To access the correct backup page, increment the backup page pointer
// based on the current VcCount.
//
GhcbBackup = (GHCB *)FixedPcdGet32 (PcdOvmfSecGhcbBackupBase);
GhcbBackup += (SevEsData->VcCount - 2);
CopyMem (GhcbBackup, Ghcb, sizeof (*Ghcb));
}
VcRet = InternalVmgExitHandleVc (Ghcb, ExceptionType, SystemContext);
if (GhcbBackup != NULL) {
//
// Restore the active GHCB from the backup page.
//
CopyMem (Ghcb, GhcbBackup, sizeof (*Ghcb));
}
SevEsData->VcCount--;
if (InterruptState) {
EnableInterrupts ();
}
return VcRet;
}

View File

@@ -0,0 +1,146 @@
;------------------------------------------------------------------------------
;*
;* Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.<BR>
;* SPDX-License-Identifier: BSD-2-Clause-Patent
;*
;*
;------------------------------------------------------------------------------
DEFAULT REL
SECTION .text
%define TDVMCALL_EXPOSE_REGS_MASK 0xffec
%define TDVMCALL 0x0
%define EXIT_REASON_CPUID 0xa
%macro tdcall 0
db 0x66,0x0f,0x01,0xcc
%endmacro
%macro tdcall_push_regs 0
push rbp
mov rbp, rsp
push r15
push r14
push r13
push r12
push rbx
push rsi
push rdi
%endmacro
%macro tdcall_pop_regs 0
pop rdi
pop rsi
pop rbx
pop r12
pop r13
pop r14
pop r15
pop rbp
%endmacro
%define number_of_regs_pushed 8
%define number_of_parameters 4
;
; Keep these in sync for push_regs/pop_regs, code below
; uses them to find 5th or greater parameters
;
%define first_variable_on_stack_offset \
((number_of_regs_pushed * 8) + (number_of_parameters * 8) + 8)
%define second_variable_on_stack_offset \
((first_variable_on_stack_offset) + 8)
%macro tdcall_regs_preamble 2
mov rax, %1
xor rcx, rcx
mov ecx, %2
; R10 = 0 (standard TDVMCALL)
xor r10d, r10d
; Zero out unused (for standard TDVMCALL) registers to avoid leaking
; secrets to the VMM.
xor ebx, ebx
xor esi, esi
xor edi, edi
xor edx, edx
xor ebp, ebp
xor r8d, r8d
xor r9d, r9d
xor r14, r14
xor r15, r15
%endmacro
%macro tdcall_regs_postamble 0
xor ebx, ebx
xor esi, esi
xor edi, edi
xor ecx, ecx
xor edx, edx
xor r8d, r8d
xor r9d, r9d
xor r10d, r10d
xor r11d, r11d
%endmacro
;------------------------------------------------------------------------------
; 0 => RAX = TDCALL leaf / TDVMCALL
; M => RCX = TDVMCALL register behavior
; 0xa => R11 = TDVMCALL function / CPUID
; RCX => R12 = p1
; RDX => R13 = p2
;
; UINT64
; EFIAPI
; TdVmCallCpuid (
; UINT64 EaxIn, // Rcx
; UINT64 EcxIn, // Rdx
; UINT64 *Results // R8
; )
global ASM_PFX(TdVmCallCpuid)
ASM_PFX(TdVmCallCpuid):
tdcall_push_regs
mov r11, EXIT_REASON_CPUID
mov r12, rcx
mov r13, rdx
; Save *results pointers
push r8
tdcall_regs_preamble TDVMCALL, TDVMCALL_EXPOSE_REGS_MASK
tdcall
; ignore return data if TDCALL reports failure.
test rax, rax
jnz .no_return_data
; Propagate TDVMCALL success/failure to return value.
mov rax, r10
test rax, rax
jnz .no_return_data
; Retrieve *Results
pop r8
test r8, r8
jz .no_return_data
; Caller pass in buffer so store results r12-r15 contains eax-edx
mov [r8 + 0], r12
mov [r8 + 8], r13
mov [r8 + 16], r14
mov [r8 + 24], r15
.no_return_data:
tdcall_regs_postamble
tdcall_pop_regs
ret