/** @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 | |
CcExitVmgExit ( | |
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; | |
CcExitVmgSetOffsetValid (Ghcb, GhcbSwExitCode); | |
CcExitVmgSetOffsetValid (Ghcb, GhcbSwExitInfo1); | |
CcExitVmgSetOffsetValid (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 CcExitVmgDone () | |
**/ | |
VOID | |
EFIAPI | |
CcExitVmgInit ( | |
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 | |
CcExitVmgDone ( | |
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 | |
CcExitVmgSetOffsetValid ( | |
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 | |
CcExitVmgIsOffsetValid ( | |
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); | |
} |