OvmfPkg/VmgExitLib: Support nested #VCs

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

In order to be able to issue messages or make interface calls that cause
another #VC (e.g. GetLocalApicBaseAddress () issues RDMSR), add support
for nested #VCs.

In order to support nested #VCs, GHCB backup pages are required. If a #VC
is received while currently processing a #VC, a backup of the current GHCB
content is made. This allows the #VC handler to continue processing the
new #VC. Upon completion of the new #VC, the GHCB is restored from the
backup page. The #VC recursion level is tracked in the per-vCPU variable
area.

Support is added to handle up to one nested #VC (or two #VCs total). If
a second nested #VC is encountered, an ASSERT will be issued and the vCPU
will enter CpuDeadLoop ().

For SEC, the GHCB backup pages are reserved in the OvmfPkgX64.fdf memory
layout, with two new fixed PCDs to provide the address and size of the
backup area.

For PEI/DXE, the GHCB backup pages are allocated as boot services pages
using the memory allocation library.

Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
Cc: Brijesh Singh <brijesh.singh@amd.com>
Acked-by: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
Message-Id: <ac2e8203fc41a351b43f60d68bdad6b57c4fb106.1610045305.git.thomas.lendacky@amd.com>
This commit is contained in:
Tom Lendacky 2021-01-07 12:48:23 -06:00 committed by mergify[bot]
parent c330af0246
commit 5667dc43d8
13 changed files with 404 additions and 27 deletions

View File

@ -236,6 +236,7 @@
!else
CpuExceptionHandlerLib|UefiCpuPkg/Library/CpuExceptionHandlerLib/SecPeiCpuExceptionHandlerLib.inf
!endif
VmgExitLib|OvmfPkg/Library/VmgExitLib/SecVmgExitLib.inf
[LibraryClasses.common.PEI_CORE]
HobLib|MdePkg/Library/PeiHobLib/PeiHobLib.inf

View File

@ -62,6 +62,9 @@ gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase|gUefiCpuPkgTokenSpaceGuid.PcdSevE
0x00C000|0x001000
gUefiOvmfPkgTokenSpaceGuid.PcdSevLaunchSecretBase|gUefiOvmfPkgTokenSpaceGuid.PcdSevLaunchSecretSize
0x00D000|0x001000
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBackupBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBackupSize
0x010000|0x010000
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamSize

View File

@ -13,6 +13,29 @@
#include <Base.h>
//
// Define the maximum number of #VCs allowed (e.g. the level of nesting
// that is allowed => 2 allows for 1 nested #VCs). I this value is changed,
// be sure to increase the size of
// gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBackupSize
// in any FDF file using this PCD.
//
#define VMGEXIT_MAXIMUM_VC_COUNT 2
//
// Per-CPU data mapping structure
// Use UINT32 for cached indicators and compare to a specific value
// so that the hypervisor can't indicate a value is cached by just
// writing random data to that area.
//
typedef struct {
UINT32 Dr7Cached;
UINT64 Dr7;
UINTN VcCount;
VOID *GhcbBackupPages;
} SEV_ES_PER_CPU_DATA;
//
// Internal structure for holding SEV-ES information needed during SEC phase
// and valid only during SEC phase and early PEI during platform

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/VmgExitLib.h>
#include <Register/Amd/Msr.h>
#include "VmgExitVcHandler.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,43 @@
## @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 = SecVmgExitLib
FILE_GUID = dafff819-f86c-4cff-a70e-83161e5bcf9a
MODULE_TYPE = BASE
VERSION_STRING = 1.0
LIBRARY_CLASS = VmgExitLib|SEC
#
# The following information is for reference only and not required by the build tools.
#
# VALID_ARCHITECTURES = X64
#
[Sources.common]
VmgExitLib.c
VmgExitVcHandler.c
VmgExitVcHandler.h
SecVmgExitVcHandler.c
[Packages]
MdePkg/MdePkg.dec
OvmfPkg/OvmfPkg.dec
UefiCpuPkg/UefiCpuPkg.dec
[LibraryClasses]
BaseLib
BaseMemoryLib
DebugLib
PcdLib
[FixedPcd]
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBackupBase
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBackupSize

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/VmgExitLib.h>
#include <Register/Amd/Msr.h>
#include "VmgExitVcHandler.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

@ -12,7 +12,7 @@
FILE_GUID = 0e923c25-13cd-430b-8714-ffe85652a97b
MODULE_TYPE = BASE
VERSION_STRING = 1.0
LIBRARY_CLASS = VmgExitLib
LIBRARY_CLASS = VmgExitLib|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.
@ -23,6 +23,8 @@
[Sources.common]
VmgExitLib.c
VmgExitVcHandler.c
VmgExitVcHandler.h
PeiDxeVmgExitVcHandler.c
[Packages]
MdePkg/MdePkg.dec

View File

@ -9,11 +9,14 @@
#include <Base.h>
#include <Uefi.h>
#include <Library/BaseMemoryLib.h>
#include <Library/MemEncryptSevLib.h>
#include <Library/VmgExitLib.h>
#include <Register/Amd/Msr.h>
#include <Register/Intel/Cpuid.h>
#include <IndustryStandard/InstructionParsing.h>
#include "VmgExitVcHandler.h"
//
// Instruction execution mode definition
//
@ -126,18 +129,6 @@ UINT64
SEV_ES_INSTRUCTION_DATA *InstructionData
);
//
// Per-CPU data mapping structure
// Use UINT32 for cached indicators and compare to a specific value
// so that the hypervisor can't indicate a value is cached by just
// writing random data to that area.
//
typedef struct {
UINT32 Dr7Cached;
UINT64 Dr7;
} SEV_ES_PER_CPU_DATA;
/**
Return a pointer to the contents of the specified register.
@ -1546,6 +1537,7 @@ Dr7ReadExit (
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
@ -1559,14 +1551,13 @@ Dr7ReadExit (
**/
EFI_STATUS
EFIAPI
VmgExitHandleVc (
InternalVmgExitHandleVc (
IN OUT GHCB *Ghcb,
IN OUT EFI_EXCEPTION_TYPE *ExceptionType,
IN OUT EFI_SYSTEM_CONTEXT SystemContext
)
{
MSR_SEV_ES_GHCB_REGISTER Msr;
EFI_SYSTEM_CONTEXT_X64 *Regs;
GHCB *Ghcb;
NAE_EXIT NaeExit;
SEV_ES_INSTRUCTION_DATA InstructionData;
UINT64 ExitCode, Status;
@ -1575,12 +1566,7 @@ VmgExitHandleVc (
VcRet = EFI_SUCCESS;
Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);
ASSERT (Msr.GhcbInfo.Function == 0);
ASSERT (Msr.Ghcb != 0);
Regs = SystemContext.SystemContextX64;
Ghcb = Msr.Ghcb;
VmgInit (Ghcb, &InterruptState);
@ -1670,3 +1656,25 @@ VmgExitHandleVc (
return VcRet;
}
/**
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
)
{
//
// Progress will be halted, so set VcCount to allow for ASSERT output
// to be seen.
//
SevEsData->VcCount = 0;
ASSERT (FALSE);
CpuDeadLoop ();
}

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 __VMG_EXIT_VC_HANDLER_H__
#define __VMG_EXIT_VC_HANDLER_H__
#include <Base.h>
#include <Uefi.h>
#include <Library/VmgExitLib.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

@ -304,6 +304,8 @@
## The base address of the SEC GHCB page used by SEV-ES.
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBase|0|UINT32|0x40
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbSize|0|UINT32|0x41
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBackupBase|0|UINT32|0x44
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBackupSize|0|UINT32|0x45
## The base address and size of the SEV Launch Secret Area provisioned
# after remote attestation. If this is set in the .fdf, the platform

View File

@ -265,6 +265,7 @@
!else
CpuExceptionHandlerLib|UefiCpuPkg/Library/CpuExceptionHandlerLib/SecPeiCpuExceptionHandlerLib.inf
!endif
VmgExitLib|OvmfPkg/Library/VmgExitLib/SecVmgExitLib.inf
[LibraryClasses.common.PEI_CORE]
HobLib|MdePkg/Library/PeiHobLib/PeiHobLib.inf

View File

@ -85,6 +85,9 @@ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmf
0x00B000|0x001000
gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase|gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaSize
0x00C000|0x001000
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBackupBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBackupSize
0x010000|0x010000
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamSize

View File

@ -33,12 +33,17 @@ AmdSevEsInitialize (
VOID
)
{
VOID *GhcbBase;
PHYSICAL_ADDRESS GhcbBasePa;
UINTN GhcbPageCount, PageCount;
RETURN_STATUS PcdStatus, DecryptStatus;
IA32_DESCRIPTOR Gdtr;
VOID *Gdt;
UINT8 *GhcbBase;
PHYSICAL_ADDRESS GhcbBasePa;
UINTN GhcbPageCount;
UINT8 *GhcbBackupBase;
UINT8 *GhcbBackupPages;
UINTN GhcbBackupPageCount;
SEV_ES_PER_CPU_DATA *SevEsData;
UINTN PageCount;
RETURN_STATUS PcdStatus, DecryptStatus;
IA32_DESCRIPTOR Gdtr;
VOID *Gdt;
if (!MemEncryptSevEsIsEnabled ()) {
return;
@ -84,6 +89,27 @@ AmdSevEsInitialize (
"SEV-ES is enabled, %lu GHCB pages allocated starting at 0x%p\n",
(UINT64)GhcbPageCount, GhcbBase));
//
// Allocate #VC recursion backup pages. The number of backup pages needed is
// one less than the maximum VC count.
//
GhcbBackupPageCount = mMaxCpuCount * (VMGEXIT_MAXIMUM_VC_COUNT - 1);
GhcbBackupBase = AllocatePages (GhcbBackupPageCount);
ASSERT (GhcbBackupBase != NULL);
GhcbBackupPages = GhcbBackupBase;
for (PageCount = 1; PageCount < GhcbPageCount; PageCount += 2) {
SevEsData =
(SEV_ES_PER_CPU_DATA *)(GhcbBase + EFI_PAGES_TO_SIZE (PageCount));
SevEsData->GhcbBackupPages = GhcbBackupPages;
GhcbBackupPages += EFI_PAGE_SIZE * (VMGEXIT_MAXIMUM_VC_COUNT - 1);
}
DEBUG ((DEBUG_INFO,
"SEV-ES is enabled, %lu GHCB backup pages allocated starting at 0x%p\n",
(UINT64)GhcbBackupPageCount, GhcbBackupBase));
AsmWriteMsr64 (MSR_SEV_ES_GHCB, GhcbBasePa);
//