Files
system76-edk2/OvmfPkg/Library/BaseMemEncryptSevLib/X64/PeiSnpSystemRamValidate.c
Tom Lendacky 2b330b57db OvmfPkg/BaseMemEncryptSevLib: Re-organize page state change support
BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=4654

In preparation for running under an SVSM at VMPL1 or higher (higher
numerically, lower privilege), re-organize the way a page state change
is performed in order to free up the GHCB for use by the SVSM support.

Currently, the page state change logic directly uses the GHCB shared
buffer to build the page state change structures. However, this will be
in conflict with the use of the GHCB should an SVSM call be required.

Instead, use a separate buffer (an area in the workarea during SEC and
an allocated page during PEI/DXE) to hold the page state change request
and only update the GHCB shared buffer as needed.

Since the information is copied to, and operated on, in the GHCB shared
buffer this has the added benefit of not requiring to save the start and
end entries for use when validating the memory during the page state
change sequence.

Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Erdem Aktas <erdemaktas@google.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Michael Roth <michael.roth@amd.com>
Cc: Min Xu <min.m.xu@intel.com>
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
Acked-by: Gerd Hoffmann <kraxel@redhat.com>
2024-04-17 18:30:03 +00:00

145 lines
3.8 KiB
C

/** @file
SEV-SNP Page Validation functions.
Copyright (c) 2021 - 2024, AMD Incorporated. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include <Uefi/UefiBaseType.h>
#include <Library/BaseLib.h>
#include <Library/PcdLib.h>
#include <Library/DebugLib.h>
#include <Library/MemEncryptSevLib.h>
#include "SnpPageStateChange.h"
#include "VirtualMemory.h"
STATIC UINT8 mPscBufferPage[EFI_PAGE_SIZE];
typedef struct {
UINT64 StartAddress;
UINT64 EndAddress;
} SNP_PRE_VALIDATED_RANGE;
STATIC SNP_PRE_VALIDATED_RANGE mPreValidatedRange[] = {
// The below address range was part of the SEV OVMF metadata, and range
// should be pre-validated by the Hypervisor.
{
FixedPcdGet32 (PcdOvmfSecPageTablesBase),
FixedPcdGet32 (PcdOvmfPeiMemFvBase),
},
// The below range is pre-validated by the Sec/SecMain.c
{
FixedPcdGet32 (PcdOvmfSecValidatedStart),
FixedPcdGet32 (PcdOvmfSecValidatedEnd)
},
};
STATIC
BOOLEAN
DetectPreValidatedOverLap (
IN PHYSICAL_ADDRESS StartAddress,
IN PHYSICAL_ADDRESS EndAddress,
OUT SNP_PRE_VALIDATED_RANGE *OverlapRange
)
{
UINTN i;
//
// Check if the specified address range exist in pre-validated array.
//
for (i = 0; i < ARRAY_SIZE (mPreValidatedRange); i++) {
if ((mPreValidatedRange[i].StartAddress < EndAddress) &&
(StartAddress < mPreValidatedRange[i].EndAddress))
{
OverlapRange->StartAddress = mPreValidatedRange[i].StartAddress;
OverlapRange->EndAddress = mPreValidatedRange[i].EndAddress;
return TRUE;
}
}
return FALSE;
}
/**
Pre-validate the system RAM when SEV-SNP is enabled in the guest VM.
@param[in] BaseAddress Base address
@param[in] NumPages Number of pages starting from the base address
**/
VOID
EFIAPI
MemEncryptSevSnpPreValidateSystemRam (
IN PHYSICAL_ADDRESS BaseAddress,
IN UINTN NumPages
)
{
PHYSICAL_ADDRESS EndAddress;
SNP_PRE_VALIDATED_RANGE OverlapRange;
EFI_STATUS Status;
if (!MemEncryptSevSnpIsEnabled ()) {
return;
}
EndAddress = BaseAddress + EFI_PAGES_TO_SIZE (NumPages);
//
// The page table used in PEI can address up to 4GB memory. If we are asked to
// validate a range above the 4GB, then create an identity mapping so that the
// PVALIDATE instruction can execute correctly. If the page table entry is not
// present then PVALIDATE will #GP.
//
if (BaseAddress >= SIZE_4GB) {
Status = InternalMemEncryptSevCreateIdentityMap1G (
0,
BaseAddress,
EFI_PAGES_TO_SIZE (NumPages)
);
if (EFI_ERROR (Status)) {
ASSERT (FALSE);
CpuDeadLoop ();
}
}
while (BaseAddress < EndAddress) {
//
// Check if the range overlaps with the pre-validated ranges.
//
if (DetectPreValidatedOverLap (BaseAddress, EndAddress, &OverlapRange)) {
// Validate the non-overlap regions.
if (BaseAddress < OverlapRange.StartAddress) {
NumPages = EFI_SIZE_TO_PAGES (OverlapRange.StartAddress - BaseAddress);
InternalSetPageState (
BaseAddress,
NumPages,
SevSnpPagePrivate,
TRUE,
mPscBufferPage,
sizeof (mPscBufferPage)
);
}
BaseAddress = OverlapRange.EndAddress;
continue;
}
// Validate the remaining pages.
NumPages = EFI_SIZE_TO_PAGES (EndAddress - BaseAddress);
InternalSetPageState (
BaseAddress,
NumPages,
SevSnpPagePrivate,
TRUE,
mPscBufferPage,
sizeof (mPscBufferPage)
);
BaseAddress = EndAddress;
}
}