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:
239
OvmfPkg/Library/CcExitLib/CcExitLib.c
Normal file
239
OvmfPkg/Library/CcExitLib/CcExitLib.c
Normal 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);
|
||||
}
|
46
OvmfPkg/Library/CcExitLib/CcExitLib.inf
Normal file
46
OvmfPkg/Library/CcExitLib/CcExitLib.inf
Normal 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
|
32
OvmfPkg/Library/CcExitLib/CcExitTd.h
Normal file
32
OvmfPkg/Library/CcExitLib/CcExitTd.h
Normal 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
|
2355
OvmfPkg/Library/CcExitLib/CcExitVcHandler.c
Normal file
2355
OvmfPkg/Library/CcExitLib/CcExitVcHandler.c
Normal file
File diff suppressed because it is too large
Load Diff
53
OvmfPkg/Library/CcExitLib/CcExitVcHandler.h
Normal file
53
OvmfPkg/Library/CcExitLib/CcExitVcHandler.h
Normal 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
|
577
OvmfPkg/Library/CcExitLib/CcExitVeHandler.c
Normal file
577
OvmfPkg/Library/CcExitLib/CcExitVeHandler.c
Normal 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;
|
||||
}
|
103
OvmfPkg/Library/CcExitLib/PeiDxeCcExitVcHandler.c
Normal file
103
OvmfPkg/Library/CcExitLib/PeiDxeCcExitVcHandler.c
Normal 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;
|
||||
}
|
48
OvmfPkg/Library/CcExitLib/SecCcExitLib.inf
Normal file
48
OvmfPkg/Library/CcExitLib/SecCcExitLib.inf
Normal 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
|
109
OvmfPkg/Library/CcExitLib/SecCcExitVcHandler.c
Normal file
109
OvmfPkg/Library/CcExitLib/SecCcExitVcHandler.c
Normal 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;
|
||||
}
|
146
OvmfPkg/Library/CcExitLib/X64/TdVmcallCpuid.nasm
Normal file
146
OvmfPkg/Library/CcExitLib/X64/TdVmcallCpuid.nasm
Normal 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
|
Reference in New Issue
Block a user