BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=2198 Allocate memory for the GHCB pages and the per-CPU variable pages during SEV initialization for use during Pei and Dxe phases. The GHCB page(s) must be shared pages, so clear the encryption mask from the current page table entries. Upon successful allocation, set the GHCB PCDs (PcdGhcbBase and PcdGhcbSize). The per-CPU variable page needs to be unique per AP. Using the page after the GHCB ensures that it is unique per AP. Only the GHCB page is marked as shared, keeping the per-CPU variable page encyrpted. The same logic is used in DXE using CreateIdentityMappingPageTables() before switching to the DXE pagetables. The GHCB pages (one per vCPU) will be used by the PEI and DXE #VC exception handlers. The #VC exception handler will fill in the necessary fields of the GHCB and exit to the hypervisor using the VMGEXIT instruction. The hypervisor then accesses the GHCB associated with the vCPU in order to perform the requested function. Cc: Jordan Justen <jordan.l.justen@intel.com> Cc: Laszlo Ersek <lersek@redhat.com> Cc: Ard Biesheuvel <ard.biesheuvel@arm.com> Reviewed-by: Laszlo Ersek <lersek@redhat.com> Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com> Regression-tested-by: Laszlo Ersek <lersek@redhat.com>
176 lines
4.9 KiB
C
176 lines
4.9 KiB
C
/**@file
|
|
Initialize Secure Encrypted Virtualization (SEV) support
|
|
|
|
Copyright (c) 2017, Advanced Micro Devices. All rights reserved.<BR>
|
|
|
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
|
|
|
**/
|
|
//
|
|
// The package level header files this module uses
|
|
//
|
|
#include <IndustryStandard/Q35MchIch9.h>
|
|
#include <Library/BaseMemoryLib.h>
|
|
#include <Library/DebugLib.h>
|
|
#include <Library/HobLib.h>
|
|
#include <Library/MemEncryptSevLib.h>
|
|
#include <Library/MemoryAllocationLib.h>
|
|
#include <Library/PcdLib.h>
|
|
#include <PiPei.h>
|
|
#include <Register/Amd/Cpuid.h>
|
|
#include <Register/Amd/Msr.h>
|
|
#include <Register/Cpuid.h>
|
|
#include <Register/Intel/SmramSaveStateMap.h>
|
|
|
|
#include "Platform.h"
|
|
|
|
/**
|
|
|
|
Initialize SEV-ES support if running as an SEV-ES guest.
|
|
|
|
**/
|
|
STATIC
|
|
VOID
|
|
AmdSevEsInitialize (
|
|
VOID
|
|
)
|
|
{
|
|
VOID *GhcbBase;
|
|
PHYSICAL_ADDRESS GhcbBasePa;
|
|
UINTN GhcbPageCount, PageCount;
|
|
RETURN_STATUS PcdStatus, DecryptStatus;
|
|
|
|
if (!MemEncryptSevEsIsEnabled ()) {
|
|
return;
|
|
}
|
|
|
|
PcdStatus = PcdSetBoolS (PcdSevEsIsEnabled, TRUE);
|
|
ASSERT_RETURN_ERROR (PcdStatus);
|
|
|
|
//
|
|
// Allocate GHCB and per-CPU variable pages.
|
|
//
|
|
GhcbPageCount = mMaxCpuCount * 2;
|
|
GhcbBase = AllocatePages (GhcbPageCount);
|
|
ASSERT (GhcbBase != NULL);
|
|
|
|
GhcbBasePa = (PHYSICAL_ADDRESS)(UINTN) GhcbBase;
|
|
|
|
//
|
|
// Each vCPU gets two consecutive pages, the first is the GHCB and the
|
|
// second is the per-CPU variable page. Loop through the allocation and
|
|
// only clear the encryption mask for the GHCB pages.
|
|
//
|
|
for (PageCount = 0; PageCount < GhcbPageCount; PageCount += 2) {
|
|
DecryptStatus = MemEncryptSevClearPageEncMask (
|
|
0,
|
|
GhcbBasePa + EFI_PAGES_TO_SIZE (PageCount),
|
|
1,
|
|
TRUE
|
|
);
|
|
ASSERT_RETURN_ERROR (DecryptStatus);
|
|
}
|
|
|
|
ZeroMem (GhcbBase, EFI_PAGES_TO_SIZE (GhcbPageCount));
|
|
|
|
PcdStatus = PcdSet64S (PcdGhcbBase, GhcbBasePa);
|
|
ASSERT_RETURN_ERROR (PcdStatus);
|
|
PcdStatus = PcdSet64S (PcdGhcbSize, EFI_PAGES_TO_SIZE (GhcbPageCount));
|
|
ASSERT_RETURN_ERROR (PcdStatus);
|
|
|
|
DEBUG ((DEBUG_INFO,
|
|
"SEV-ES is enabled, %lu GHCB pages allocated starting at 0x%p\n",
|
|
(UINT64)GhcbPageCount, GhcbBase));
|
|
|
|
AsmWriteMsr64 (MSR_SEV_ES_GHCB, GhcbBasePa);
|
|
}
|
|
|
|
/**
|
|
|
|
Function checks if SEV support is available, if present then it sets
|
|
the dynamic PcdPteMemoryEncryptionAddressOrMask with memory encryption mask.
|
|
|
|
**/
|
|
VOID
|
|
AmdSevInitialize (
|
|
VOID
|
|
)
|
|
{
|
|
CPUID_MEMORY_ENCRYPTION_INFO_EBX Ebx;
|
|
UINT64 EncryptionMask;
|
|
RETURN_STATUS PcdStatus;
|
|
|
|
//
|
|
// Check if SEV is enabled
|
|
//
|
|
if (!MemEncryptSevIsEnabled ()) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// CPUID Fn8000_001F[EBX] Bit 0:5 (memory encryption bit position)
|
|
//
|
|
AsmCpuid (CPUID_MEMORY_ENCRYPTION_INFO, NULL, &Ebx.Uint32, NULL, NULL);
|
|
EncryptionMask = LShiftU64 (1, Ebx.Bits.PtePosBits);
|
|
|
|
//
|
|
// Set Memory Encryption Mask PCD
|
|
//
|
|
PcdStatus = PcdSet64S (PcdPteMemoryEncryptionAddressOrMask, EncryptionMask);
|
|
ASSERT_RETURN_ERROR (PcdStatus);
|
|
|
|
DEBUG ((DEBUG_INFO, "SEV is enabled (mask 0x%lx)\n", EncryptionMask));
|
|
|
|
//
|
|
// Set Pcd to Deny the execution of option ROM when security
|
|
// violation.
|
|
//
|
|
PcdStatus = PcdSet32S (PcdOptionRomImageVerificationPolicy, 0x4);
|
|
ASSERT_RETURN_ERROR (PcdStatus);
|
|
|
|
//
|
|
// When SMM is required, cover the pages containing the initial SMRAM Save
|
|
// State Map with a memory allocation HOB:
|
|
//
|
|
// There's going to be a time interval between our decrypting those pages for
|
|
// SMBASE relocation and re-encrypting the same pages after SMBASE
|
|
// relocation. We shall ensure that the DXE phase stay away from those pages
|
|
// until after re-encryption, in order to prevent an information leak to the
|
|
// hypervisor.
|
|
//
|
|
if (FeaturePcdGet (PcdSmmSmramRequire) && (mBootMode != BOOT_ON_S3_RESUME)) {
|
|
RETURN_STATUS LocateMapStatus;
|
|
UINTN MapPagesBase;
|
|
UINTN MapPagesCount;
|
|
|
|
LocateMapStatus = MemEncryptSevLocateInitialSmramSaveStateMapPages (
|
|
&MapPagesBase,
|
|
&MapPagesCount
|
|
);
|
|
ASSERT_RETURN_ERROR (LocateMapStatus);
|
|
|
|
if (mQ35SmramAtDefaultSmbase) {
|
|
//
|
|
// The initial SMRAM Save State Map has been covered as part of a larger
|
|
// reserved memory allocation in InitializeRamRegions().
|
|
//
|
|
ASSERT (SMM_DEFAULT_SMBASE <= MapPagesBase);
|
|
ASSERT (
|
|
(MapPagesBase + EFI_PAGES_TO_SIZE (MapPagesCount) <=
|
|
SMM_DEFAULT_SMBASE + MCH_DEFAULT_SMBASE_SIZE)
|
|
);
|
|
} else {
|
|
BuildMemoryAllocationHob (
|
|
MapPagesBase, // BaseAddress
|
|
EFI_PAGES_TO_SIZE (MapPagesCount), // Length
|
|
EfiBootServicesData // MemoryType
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check and perform SEV-ES initialization if required.
|
|
//
|
|
AmdSevEsInitialize ();
|
|
}
|