audk/OvmfPkg/PlatformPei/AmdSev.c

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

470 lines
13 KiB
C
Raw Normal View History

OvmfPkg/PlatformPei: Set memory encryption PCD when SEV is enabled Secure Encrypted Virtualization (SEV) guest VMs have the concept of private and shared memory. Private memory is encrypted with the guest-specific key, while shared memory may be encrypted with hypervisor key. Certain types of memory (namely instruction pages and guest page tables) are always treated as private memory by the hardware. For data memory, SEV guest VMs can choose which pages they would like to be private. The choice is done using the standard CPU page tables using the C-bit. When building the initial page table we mark all the memory as private. The patch sets the memory encryption PCD. The PCD is consumed by the following edk2 modules, which manipulate page tables: - PEI phase modules: CapsulePei, DxeIplPeim, S3Resume2Pei. CapsulePei is not used by OVMF. DxeIplPeim consumes the PCD at the end of the PEI phase, when it builds the initial page tables for the DXE core / DXE phase. S3Resume2Pei does not consume the PCD in its entry point function, only when DxeIplPeim branches to the S3 resume path at the end of the PEI phase, and calls S3Resume2Pei's EFI_PEI_S3_RESUME2_PPI.S3RestoreConfig2() member function. Therefore it is safe to set the PCD for these modules in PlatformPei. - DXE phase modules: BootScriptExecutorDxe, CpuDxe, PiSmmCpuDxeSmm. They are all dispatched after the PEI phase, so setting the PCD for them in PlatformPei is safe. (BootScriptExecutorDxe is launched "for real" in the PEI phase during S3 resume, but it caches the PCD into a static variable when its entry point is originally invoked in DXE.) Cc: Jordan Justen <jordan.l.justen@intel.com> Cc: Laszlo Ersek <lersek@redhat.com> Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Brijesh Singh <brijesh.singh@amd.com> Reviewed-by: Laszlo Ersek <lersek@redhat.com> Reviewed-by: Jordan Justen <jordan.l.justen@intel.com>
2017-07-06 15:25:48 +02:00
/**@file
Initialize Secure Encrypted Virtualization (SEV) support
Copyright (c) 2017 - 2020, Advanced Micro Devices. All rights reserved.<BR>
OvmfPkg/PlatformPei: Set memory encryption PCD when SEV is enabled Secure Encrypted Virtualization (SEV) guest VMs have the concept of private and shared memory. Private memory is encrypted with the guest-specific key, while shared memory may be encrypted with hypervisor key. Certain types of memory (namely instruction pages and guest page tables) are always treated as private memory by the hardware. For data memory, SEV guest VMs can choose which pages they would like to be private. The choice is done using the standard CPU page tables using the C-bit. When building the initial page table we mark all the memory as private. The patch sets the memory encryption PCD. The PCD is consumed by the following edk2 modules, which manipulate page tables: - PEI phase modules: CapsulePei, DxeIplPeim, S3Resume2Pei. CapsulePei is not used by OVMF. DxeIplPeim consumes the PCD at the end of the PEI phase, when it builds the initial page tables for the DXE core / DXE phase. S3Resume2Pei does not consume the PCD in its entry point function, only when DxeIplPeim branches to the S3 resume path at the end of the PEI phase, and calls S3Resume2Pei's EFI_PEI_S3_RESUME2_PPI.S3RestoreConfig2() member function. Therefore it is safe to set the PCD for these modules in PlatformPei. - DXE phase modules: BootScriptExecutorDxe, CpuDxe, PiSmmCpuDxeSmm. They are all dispatched after the PEI phase, so setting the PCD for them in PlatformPei is safe. (BootScriptExecutorDxe is launched "for real" in the PEI phase during S3 resume, but it caches the PCD into a static variable when its entry point is originally invoked in DXE.) Cc: Jordan Justen <jordan.l.justen@intel.com> Cc: Laszlo Ersek <lersek@redhat.com> Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Brijesh Singh <brijesh.singh@amd.com> Reviewed-by: Laszlo Ersek <lersek@redhat.com> Reviewed-by: Jordan Justen <jordan.l.justen@intel.com>
2017-07-06 15:25:48 +02:00
SPDX-License-Identifier: BSD-2-Clause-Patent
OvmfPkg/PlatformPei: Set memory encryption PCD when SEV is enabled Secure Encrypted Virtualization (SEV) guest VMs have the concept of private and shared memory. Private memory is encrypted with the guest-specific key, while shared memory may be encrypted with hypervisor key. Certain types of memory (namely instruction pages and guest page tables) are always treated as private memory by the hardware. For data memory, SEV guest VMs can choose which pages they would like to be private. The choice is done using the standard CPU page tables using the C-bit. When building the initial page table we mark all the memory as private. The patch sets the memory encryption PCD. The PCD is consumed by the following edk2 modules, which manipulate page tables: - PEI phase modules: CapsulePei, DxeIplPeim, S3Resume2Pei. CapsulePei is not used by OVMF. DxeIplPeim consumes the PCD at the end of the PEI phase, when it builds the initial page tables for the DXE core / DXE phase. S3Resume2Pei does not consume the PCD in its entry point function, only when DxeIplPeim branches to the S3 resume path at the end of the PEI phase, and calls S3Resume2Pei's EFI_PEI_S3_RESUME2_PPI.S3RestoreConfig2() member function. Therefore it is safe to set the PCD for these modules in PlatformPei. - DXE phase modules: BootScriptExecutorDxe, CpuDxe, PiSmmCpuDxeSmm. They are all dispatched after the PEI phase, so setting the PCD for them in PlatformPei is safe. (BootScriptExecutorDxe is launched "for real" in the PEI phase during S3 resume, but it caches the PCD into a static variable when its entry point is originally invoked in DXE.) Cc: Jordan Justen <jordan.l.justen@intel.com> Cc: Laszlo Ersek <lersek@redhat.com> Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Brijesh Singh <brijesh.singh@amd.com> Reviewed-by: Laszlo Ersek <lersek@redhat.com> Reviewed-by: Jordan Justen <jordan.l.justen@intel.com>
2017-07-06 15:25:48 +02:00
**/
//
// The package level header files this module uses
//
OvmfPkg/SEV: don't manage the lifecycle of the SMRAM at the default SMBASE When OVMF runs in a SEV guest, the initial SMM Save State Map is (1) allocated as EfiBootServicesData type memory in OvmfPkg/PlatformPei, function AmdSevInitialize(), for preventing unintended information sharing with the hypervisor; (2) decrypted in AmdSevDxe; (3) re-encrypted in OvmfPkg/Library/SmmCpuFeaturesLib, function SmmCpuFeaturesSmmRelocationComplete(), which is called by PiSmmCpuDxeSmm right after initial SMBASE relocation; (4) released to DXE at the same location. The SMRAM at the default SMBASE is a superset of the initial Save State Map. The reserved memory allocation in InitializeRamRegions(), from the previous patch, must override the allocating and freeing in (1) and (4), respectively. (Note: the decrypting and re-encrypting in (2) and (3) are unaffected.) In AmdSevInitialize(), only assert the containment of the initial Save State Map, in the larger area already allocated by InitializeRamRegions(). In SmmCpuFeaturesSmmRelocationComplete(), preserve the allocation of the initial Save State Map into OS runtime, as part of the allocation done by InitializeRamRegions(). Only assert containment. These changes only affect the normal boot path (the UEFI memory map is untouched during S3 resume). Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org> Cc: Jordan Justen <jordan.l.justen@intel.com> Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=1512 Signed-off-by: Laszlo Ersek <lersek@redhat.com> Reviewed-by: Jiewen Yao <jiewen.yao@intel.com> Message-Id: <20200129214412.2361-9-lersek@redhat.com> Reviewed-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
2019-09-20 17:07:43 +02:00
#include <IndustryStandard/Q35MchIch9.h>
#include <Library/BaseMemoryLib.h>
OvmfPkg/PlatformPei: Set memory encryption PCD when SEV is enabled Secure Encrypted Virtualization (SEV) guest VMs have the concept of private and shared memory. Private memory is encrypted with the guest-specific key, while shared memory may be encrypted with hypervisor key. Certain types of memory (namely instruction pages and guest page tables) are always treated as private memory by the hardware. For data memory, SEV guest VMs can choose which pages they would like to be private. The choice is done using the standard CPU page tables using the C-bit. When building the initial page table we mark all the memory as private. The patch sets the memory encryption PCD. The PCD is consumed by the following edk2 modules, which manipulate page tables: - PEI phase modules: CapsulePei, DxeIplPeim, S3Resume2Pei. CapsulePei is not used by OVMF. DxeIplPeim consumes the PCD at the end of the PEI phase, when it builds the initial page tables for the DXE core / DXE phase. S3Resume2Pei does not consume the PCD in its entry point function, only when DxeIplPeim branches to the S3 resume path at the end of the PEI phase, and calls S3Resume2Pei's EFI_PEI_S3_RESUME2_PPI.S3RestoreConfig2() member function. Therefore it is safe to set the PCD for these modules in PlatformPei. - DXE phase modules: BootScriptExecutorDxe, CpuDxe, PiSmmCpuDxeSmm. They are all dispatched after the PEI phase, so setting the PCD for them in PlatformPei is safe. (BootScriptExecutorDxe is launched "for real" in the PEI phase during S3 resume, but it caches the PCD into a static variable when its entry point is originally invoked in DXE.) Cc: Jordan Justen <jordan.l.justen@intel.com> Cc: Laszlo Ersek <lersek@redhat.com> Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Brijesh Singh <brijesh.singh@amd.com> Reviewed-by: Laszlo Ersek <lersek@redhat.com> Reviewed-by: Jordan Justen <jordan.l.justen@intel.com>
2017-07-06 15:25:48 +02:00
#include <Library/DebugLib.h>
OvmfPkg/PlatformPei: SEV: allocate pages of initial SMRAM save state map In the next two patches, we'll temporarily decrypt the pages containing the initial SMRAM save state map, for SMBASE relocation. (Unlike the separate, relocated SMRAM save state map of each VCPU, the original, shared map behaves similarly to a "common buffer" between guest and host.) The decryption will occur near the beginning of the DXE phase, in AmdSevDxe, and the re-encryption will occur in PiSmmCpuDxeSmm, via OVMF's SmmCpuFeaturesLib instance. There is a non-trivial time gap between these two points, and the DXE phase might use the pages overlapping the initial SMRAM save state map for arbitrary purposes meanwhile. In order to prevent any information leak towards the hypervisor, make sure the DXE phase puts nothing in those pages until re-encryption is done. Creating a memalloc HOB for the area in question is safe: - the temporary SEC/PEI RAM (stack and heap) is based at PcdOvmfSecPeiTempRamBase, which is above 8MB, - the permanent PEI RAM (installed in PlatformPei's PublishPeiMemory() function) never starts below PcdOvmfDxeMemFvBase, which is also above 8MB. The allocated pages can be released to the DXE phase after SMBASE relocation and re-encryption are complete. Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org> Cc: Brijesh Singh <brijesh.singh@amd.com> Cc: Jordan Justen <jordan.l.justen@intel.com> Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: Laszlo Ersek <lersek@redhat.com> Tested-by: Brijesh Singh <brijesh.singh@amd.com> Reviewed-by: Brijesh Singh <brijesh.singh@amd.com>
2018-03-01 17:59:19 +01:00
#include <Library/HobLib.h>
#include <Library/MemEncryptSevLib.h>
#include <Library/MemoryAllocationLib.h>
OvmfPkg/PlatformPei: Set memory encryption PCD when SEV is enabled Secure Encrypted Virtualization (SEV) guest VMs have the concept of private and shared memory. Private memory is encrypted with the guest-specific key, while shared memory may be encrypted with hypervisor key. Certain types of memory (namely instruction pages and guest page tables) are always treated as private memory by the hardware. For data memory, SEV guest VMs can choose which pages they would like to be private. The choice is done using the standard CPU page tables using the C-bit. When building the initial page table we mark all the memory as private. The patch sets the memory encryption PCD. The PCD is consumed by the following edk2 modules, which manipulate page tables: - PEI phase modules: CapsulePei, DxeIplPeim, S3Resume2Pei. CapsulePei is not used by OVMF. DxeIplPeim consumes the PCD at the end of the PEI phase, when it builds the initial page tables for the DXE core / DXE phase. S3Resume2Pei does not consume the PCD in its entry point function, only when DxeIplPeim branches to the S3 resume path at the end of the PEI phase, and calls S3Resume2Pei's EFI_PEI_S3_RESUME2_PPI.S3RestoreConfig2() member function. Therefore it is safe to set the PCD for these modules in PlatformPei. - DXE phase modules: BootScriptExecutorDxe, CpuDxe, PiSmmCpuDxeSmm. They are all dispatched after the PEI phase, so setting the PCD for them in PlatformPei is safe. (BootScriptExecutorDxe is launched "for real" in the PEI phase during S3 resume, but it caches the PCD into a static variable when its entry point is originally invoked in DXE.) Cc: Jordan Justen <jordan.l.justen@intel.com> Cc: Laszlo Ersek <lersek@redhat.com> Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Brijesh Singh <brijesh.singh@amd.com> Reviewed-by: Laszlo Ersek <lersek@redhat.com> Reviewed-by: Jordan Justen <jordan.l.justen@intel.com>
2017-07-06 15:25:48 +02:00
#include <Library/PcdLib.h>
#include <Pi/PrePiHob.h>
#include <PiPei.h>
#include <Register/Amd/Msr.h>
OvmfPkg/SEV: don't manage the lifecycle of the SMRAM at the default SMBASE When OVMF runs in a SEV guest, the initial SMM Save State Map is (1) allocated as EfiBootServicesData type memory in OvmfPkg/PlatformPei, function AmdSevInitialize(), for preventing unintended information sharing with the hypervisor; (2) decrypted in AmdSevDxe; (3) re-encrypted in OvmfPkg/Library/SmmCpuFeaturesLib, function SmmCpuFeaturesSmmRelocationComplete(), which is called by PiSmmCpuDxeSmm right after initial SMBASE relocation; (4) released to DXE at the same location. The SMRAM at the default SMBASE is a superset of the initial Save State Map. The reserved memory allocation in InitializeRamRegions(), from the previous patch, must override the allocating and freeing in (1) and (4), respectively. (Note: the decrypting and re-encrypting in (2) and (3) are unaffected.) In AmdSevInitialize(), only assert the containment of the initial Save State Map, in the larger area already allocated by InitializeRamRegions(). In SmmCpuFeaturesSmmRelocationComplete(), preserve the allocation of the initial Save State Map into OS runtime, as part of the allocation done by InitializeRamRegions(). Only assert containment. These changes only affect the normal boot path (the UEFI memory map is untouched during S3 resume). Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org> Cc: Jordan Justen <jordan.l.justen@intel.com> Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=1512 Signed-off-by: Laszlo Ersek <lersek@redhat.com> Reviewed-by: Jiewen Yao <jiewen.yao@intel.com> Message-Id: <20200129214412.2361-9-lersek@redhat.com> Reviewed-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
2019-09-20 17:07:43 +02:00
#include <Register/Intel/SmramSaveStateMap.h>
#include <Library/CcExitLib.h>
#include <ConfidentialComputingGuestAttr.h>
OvmfPkg/PlatformPei: Set memory encryption PCD when SEV is enabled Secure Encrypted Virtualization (SEV) guest VMs have the concept of private and shared memory. Private memory is encrypted with the guest-specific key, while shared memory may be encrypted with hypervisor key. Certain types of memory (namely instruction pages and guest page tables) are always treated as private memory by the hardware. For data memory, SEV guest VMs can choose which pages they would like to be private. The choice is done using the standard CPU page tables using the C-bit. When building the initial page table we mark all the memory as private. The patch sets the memory encryption PCD. The PCD is consumed by the following edk2 modules, which manipulate page tables: - PEI phase modules: CapsulePei, DxeIplPeim, S3Resume2Pei. CapsulePei is not used by OVMF. DxeIplPeim consumes the PCD at the end of the PEI phase, when it builds the initial page tables for the DXE core / DXE phase. S3Resume2Pei does not consume the PCD in its entry point function, only when DxeIplPeim branches to the S3 resume path at the end of the PEI phase, and calls S3Resume2Pei's EFI_PEI_S3_RESUME2_PPI.S3RestoreConfig2() member function. Therefore it is safe to set the PCD for these modules in PlatformPei. - DXE phase modules: BootScriptExecutorDxe, CpuDxe, PiSmmCpuDxeSmm. They are all dispatched after the PEI phase, so setting the PCD for them in PlatformPei is safe. (BootScriptExecutorDxe is launched "for real" in the PEI phase during S3 resume, but it caches the PCD into a static variable when its entry point is originally invoked in DXE.) Cc: Jordan Justen <jordan.l.justen@intel.com> Cc: Laszlo Ersek <lersek@redhat.com> Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Brijesh Singh <brijesh.singh@amd.com> Reviewed-by: Laszlo Ersek <lersek@redhat.com> Reviewed-by: Jordan Justen <jordan.l.justen@intel.com>
2017-07-06 15:25:48 +02:00
#include "Platform.h"
STATIC
UINT64
GetHypervisorFeature (
VOID
);
OvmfPkg/PlatformPei: validate the system RAM when SNP is active BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=3275 When SEV-SNP is active, a memory region mapped encrypted in the page table must be validated before access. There are two approaches that can be taken to validate the system RAM detected during the PEI phase: 1) Validate on-demand OR 2) Validate before access On-demand ========= If memory is not validated before access, it will cause a #VC exception with the page-not-validated error code. The VC exception handler can perform the validation steps. The pages that have been validated will need to be tracked to avoid the double validation scenarios. The range of memory that has not been validated will need to be communicated to the OS through the recently introduced unaccepted memory type https://github.com/microsoft/mu_basecore/pull/66, so that OS can validate those ranges before using them. Validate before access ====================== Since the PEI phase detects all the available system RAM, use the MemEncryptSevSnpValidateSystemRam() function to pre-validate the system RAM in the PEI phase. For now, choose option 2 due to the dependency and the complexity of the on-demand validation. Cc: Michael Roth <michael.roth@amd.com> Cc: James Bottomley <jejb@linux.ibm.com> Cc: Min Xu <min.m.xu@intel.com> Cc: Jiewen Yao <jiewen.yao@intel.com> Cc: Tom Lendacky <thomas.lendacky@amd.com> Cc: Jordan Justen <jordan.l.justen@intel.com> Cc: Ard Biesheuvel <ardb+tianocore@kernel.org> Cc: Erdem Aktas <erdemaktas@google.com> Cc: Gerd Hoffmann <kraxel@redhat.com> Acked-by: Jiewen Yao <Jiewen.yao@intel.com> Acked-by: Gerd Hoffmann <kraxel@redhat.com> Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
2021-12-09 04:27:47 +01:00
/**
Initialize SEV-SNP support if running as an SEV-SNP guest.
**/
STATIC
VOID
AmdSevSnpInitialize (
VOID
)
{
EFI_PEI_HOB_POINTERS Hob;
EFI_HOB_RESOURCE_DESCRIPTOR *ResourceHob;
UINT64 HvFeatures;
EFI_STATUS PcdStatus;
OvmfPkg/PlatformPei: validate the system RAM when SNP is active BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=3275 When SEV-SNP is active, a memory region mapped encrypted in the page table must be validated before access. There are two approaches that can be taken to validate the system RAM detected during the PEI phase: 1) Validate on-demand OR 2) Validate before access On-demand ========= If memory is not validated before access, it will cause a #VC exception with the page-not-validated error code. The VC exception handler can perform the validation steps. The pages that have been validated will need to be tracked to avoid the double validation scenarios. The range of memory that has not been validated will need to be communicated to the OS through the recently introduced unaccepted memory type https://github.com/microsoft/mu_basecore/pull/66, so that OS can validate those ranges before using them. Validate before access ====================== Since the PEI phase detects all the available system RAM, use the MemEncryptSevSnpValidateSystemRam() function to pre-validate the system RAM in the PEI phase. For now, choose option 2 due to the dependency and the complexity of the on-demand validation. Cc: Michael Roth <michael.roth@amd.com> Cc: James Bottomley <jejb@linux.ibm.com> Cc: Min Xu <min.m.xu@intel.com> Cc: Jiewen Yao <jiewen.yao@intel.com> Cc: Tom Lendacky <thomas.lendacky@amd.com> Cc: Jordan Justen <jordan.l.justen@intel.com> Cc: Ard Biesheuvel <ardb+tianocore@kernel.org> Cc: Erdem Aktas <erdemaktas@google.com> Cc: Gerd Hoffmann <kraxel@redhat.com> Acked-by: Jiewen Yao <Jiewen.yao@intel.com> Acked-by: Gerd Hoffmann <kraxel@redhat.com> Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
2021-12-09 04:27:47 +01:00
if (!MemEncryptSevSnpIsEnabled ()) {
return;
}
//
// Query the hypervisor feature using the CcExitVmgExit and set the value in the
// hypervisor features PCD.
//
HvFeatures = GetHypervisorFeature ();
PcdStatus = PcdSet64S (PcdGhcbHypervisorFeatures, HvFeatures);
ASSERT_RETURN_ERROR (PcdStatus);
OvmfPkg/PlatformPei: validate the system RAM when SNP is active BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=3275 When SEV-SNP is active, a memory region mapped encrypted in the page table must be validated before access. There are two approaches that can be taken to validate the system RAM detected during the PEI phase: 1) Validate on-demand OR 2) Validate before access On-demand ========= If memory is not validated before access, it will cause a #VC exception with the page-not-validated error code. The VC exception handler can perform the validation steps. The pages that have been validated will need to be tracked to avoid the double validation scenarios. The range of memory that has not been validated will need to be communicated to the OS through the recently introduced unaccepted memory type https://github.com/microsoft/mu_basecore/pull/66, so that OS can validate those ranges before using them. Validate before access ====================== Since the PEI phase detects all the available system RAM, use the MemEncryptSevSnpValidateSystemRam() function to pre-validate the system RAM in the PEI phase. For now, choose option 2 due to the dependency and the complexity of the on-demand validation. Cc: Michael Roth <michael.roth@amd.com> Cc: James Bottomley <jejb@linux.ibm.com> Cc: Min Xu <min.m.xu@intel.com> Cc: Jiewen Yao <jiewen.yao@intel.com> Cc: Tom Lendacky <thomas.lendacky@amd.com> Cc: Jordan Justen <jordan.l.justen@intel.com> Cc: Ard Biesheuvel <ardb+tianocore@kernel.org> Cc: Erdem Aktas <erdemaktas@google.com> Cc: Gerd Hoffmann <kraxel@redhat.com> Acked-by: Jiewen Yao <Jiewen.yao@intel.com> Acked-by: Gerd Hoffmann <kraxel@redhat.com> Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
2021-12-09 04:27:47 +01:00
//
// Iterate through the system RAM and validate it.
//
for (Hob.Raw = GetHobList (); !END_OF_HOB_LIST (Hob); Hob.Raw = GET_NEXT_HOB (Hob)) {
if ((Hob.Raw != NULL) && (GET_HOB_TYPE (Hob) == EFI_HOB_TYPE_RESOURCE_DESCRIPTOR)) {
ResourceHob = Hob.ResourceDescriptor;
if (ResourceHob->ResourceType == EFI_RESOURCE_SYSTEM_MEMORY) {
if (ResourceHob->PhysicalStart >= SIZE_4GB) {
ResourceHob->ResourceType = BZ3937_EFI_RESOURCE_MEMORY_UNACCEPTED;
continue;
}
OvmfPkg/PlatformPei: validate the system RAM when SNP is active BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=3275 When SEV-SNP is active, a memory region mapped encrypted in the page table must be validated before access. There are two approaches that can be taken to validate the system RAM detected during the PEI phase: 1) Validate on-demand OR 2) Validate before access On-demand ========= If memory is not validated before access, it will cause a #VC exception with the page-not-validated error code. The VC exception handler can perform the validation steps. The pages that have been validated will need to be tracked to avoid the double validation scenarios. The range of memory that has not been validated will need to be communicated to the OS through the recently introduced unaccepted memory type https://github.com/microsoft/mu_basecore/pull/66, so that OS can validate those ranges before using them. Validate before access ====================== Since the PEI phase detects all the available system RAM, use the MemEncryptSevSnpValidateSystemRam() function to pre-validate the system RAM in the PEI phase. For now, choose option 2 due to the dependency and the complexity of the on-demand validation. Cc: Michael Roth <michael.roth@amd.com> Cc: James Bottomley <jejb@linux.ibm.com> Cc: Min Xu <min.m.xu@intel.com> Cc: Jiewen Yao <jiewen.yao@intel.com> Cc: Tom Lendacky <thomas.lendacky@amd.com> Cc: Jordan Justen <jordan.l.justen@intel.com> Cc: Ard Biesheuvel <ardb+tianocore@kernel.org> Cc: Erdem Aktas <erdemaktas@google.com> Cc: Gerd Hoffmann <kraxel@redhat.com> Acked-by: Jiewen Yao <Jiewen.yao@intel.com> Acked-by: Gerd Hoffmann <kraxel@redhat.com> Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
2021-12-09 04:27:47 +01:00
MemEncryptSevSnpPreValidateSystemRam (
ResourceHob->PhysicalStart,
EFI_SIZE_TO_PAGES ((UINTN)ResourceHob->ResourceLength)
);
}
}
}
}
/**
Handle an SEV-SNP/GHCB protocol check failure.
Notify the hypervisor using the VMGEXIT instruction that the SEV-SNP guest
wishes to be terminated.
@param[in] ReasonCode Reason code to provide to the hypervisor for the
termination request.
**/
STATIC
VOID
SevEsProtocolFailure (
IN UINT8 ReasonCode
)
{
MSR_SEV_ES_GHCB_REGISTER Msr;
//
// Use the GHCB MSR Protocol to request termination by the hypervisor
//
Msr.GhcbPhysicalAddress = 0;
Msr.GhcbTerminate.Function = GHCB_INFO_TERMINATE_REQUEST;
Msr.GhcbTerminate.ReasonCodeSet = GHCB_TERMINATE_GHCB;
Msr.GhcbTerminate.ReasonCode = ReasonCode;
AsmWriteMsr64 (MSR_SEV_ES_GHCB, Msr.GhcbPhysicalAddress);
AsmVmgExit ();
ASSERT (FALSE);
CpuDeadLoop ();
}
/**
Get the hypervisor features bitmap
**/
STATIC
UINT64
GetHypervisorFeature (
VOID
)
{
UINT64 Status;
GHCB *Ghcb;
MSR_SEV_ES_GHCB_REGISTER Msr;
BOOLEAN InterruptState;
UINT64 Features;
Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);
Ghcb = Msr.Ghcb;
//
// Initialize the GHCB
//
CcExitVmgInit (Ghcb, &InterruptState);
//
// Query the Hypervisor Features.
//
Status = CcExitVmgExit (Ghcb, SVM_EXIT_HYPERVISOR_FEATURES, 0, 0);
if ((Status != 0)) {
SevEsProtocolFailure (GHCB_TERMINATE_GHCB_GENERAL);
}
Features = Ghcb->SaveArea.SwExitInfo2;
CcExitVmgDone (Ghcb, InterruptState);
return Features;
}
/**
This function can be used to register the GHCB GPA.
@param[in] Address The physical address to be registered.
**/
STATIC
VOID
GhcbRegister (
IN EFI_PHYSICAL_ADDRESS Address
)
{
MSR_SEV_ES_GHCB_REGISTER Msr;
MSR_SEV_ES_GHCB_REGISTER CurrentMsr;
//
// Save the current MSR Value
//
CurrentMsr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);
//
// Use the GHCB MSR Protocol to request to register the GPA.
//
Msr.GhcbPhysicalAddress = Address & ~EFI_PAGE_MASK;
Msr.GhcbGpaRegister.Function = GHCB_INFO_GHCB_GPA_REGISTER_REQUEST;
AsmWriteMsr64 (MSR_SEV_ES_GHCB, Msr.GhcbPhysicalAddress);
AsmVmgExit ();
Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);
//
// If hypervisor responded with a different GPA than requested then fail.
//
if ((Msr.GhcbGpaRegister.Function != GHCB_INFO_GHCB_GPA_REGISTER_RESPONSE) ||
((Msr.GhcbPhysicalAddress & ~EFI_PAGE_MASK) != Address))
{
SevEsProtocolFailure (GHCB_TERMINATE_GHCB_GENERAL);
}
//
// Restore the MSR
//
AsmWriteMsr64 (MSR_SEV_ES_GHCB, CurrentMsr.GhcbPhysicalAddress);
}
/**
Initialize SEV-ES support if running as an SEV-ES guest.
**/
STATIC
VOID
AmdSevEsInitialize (
IN EFI_HOB_PLATFORM_INFO *PlatformInfoHob
)
{
UINT8 *GhcbBase;
PHYSICAL_ADDRESS GhcbBasePa;
UINTN GhcbPageCount;
UINT8 *GhcbBackupBase;
UINT8 *GhcbBackupPages;
UINTN GhcbBackupPageCount;
SEV_ES_PER_CPU_DATA *SevEsData;
UINTN PageCount;
RETURN_STATUS Status;
IA32_DESCRIPTOR Gdtr;
VOID *Gdt;
if (!MemEncryptSevEsIsEnabled ()) {
return;
}
Status = PcdSetBoolS (PcdSevEsIsEnabled, TRUE);
ASSERT_RETURN_ERROR (Status);
//
// Allocate GHCB and per-CPU variable pages.
// Since the pages must survive across the UEFI to OS transition
// make them reserved.
//
GhcbPageCount = PlatformInfoHob->PcdCpuMaxLogicalProcessorNumber * 2;
GhcbBase = AllocateReservedPages (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) {
Status = MemEncryptSevClearPageEncMask (
0,
GhcbBasePa + EFI_PAGES_TO_SIZE (PageCount),
1
);
ASSERT_RETURN_ERROR (Status);
}
ZeroMem (GhcbBase, EFI_PAGES_TO_SIZE (GhcbPageCount));
Status = PcdSet64S (PcdGhcbBase, GhcbBasePa);
ASSERT_RETURN_ERROR (Status);
Status = PcdSet64S (PcdGhcbSize, EFI_PAGES_TO_SIZE (GhcbPageCount));
ASSERT_RETURN_ERROR (Status);
DEBUG ((
DEBUG_INFO,
"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 = PlatformInfoHob->PcdCpuMaxLogicalProcessorNumber * (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
));
//
// SEV-SNP guest requires that GHCB GPA must be registered before using it.
//
if (MemEncryptSevSnpIsEnabled ()) {
GhcbRegister (GhcbBasePa);
}
AsmWriteMsr64 (MSR_SEV_ES_GHCB, GhcbBasePa);
//
// Now that the PEI GHCB is set up, the SEC GHCB page is no longer necessary
// to keep shared. Later, it is exposed to the OS as EfiConventionalMemory, so
// it needs to be marked private. The size of the region is hardcoded in
// OvmfPkg/ResetVector/ResetVector.nasmb in the definition of
// SNP_SEC_MEM_BASE_DESC_2.
//
Status = MemEncryptSevSetPageEncMask (
0, // Cr3 -- use system Cr3
FixedPcdGet32 (PcdOvmfSecGhcbBase), // BaseAddress
1 // NumPages
);
ASSERT_RETURN_ERROR (Status);
//
// The SEV support will clear the C-bit from non-RAM areas. The early GDT
// lives in a non-RAM area, so when an exception occurs (like a #VC) the GDT
// will be read as un-encrypted even though it was created before the C-bit
// was cleared (encrypted). This will result in a failure to be able to
// handle the exception.
//
AsmReadGdtr (&Gdtr);
Gdt = AllocatePages (EFI_SIZE_TO_PAGES ((UINTN)Gdtr.Limit + 1));
ASSERT (Gdt != NULL);
CopyMem (Gdt, (VOID *)Gdtr.Base, Gdtr.Limit + 1);
Gdtr.Base = (UINTN)Gdt;
AsmWriteGdtr (&Gdtr);
}
OvmfPkg/PlatformPei: Set memory encryption PCD when SEV is enabled Secure Encrypted Virtualization (SEV) guest VMs have the concept of private and shared memory. Private memory is encrypted with the guest-specific key, while shared memory may be encrypted with hypervisor key. Certain types of memory (namely instruction pages and guest page tables) are always treated as private memory by the hardware. For data memory, SEV guest VMs can choose which pages they would like to be private. The choice is done using the standard CPU page tables using the C-bit. When building the initial page table we mark all the memory as private. The patch sets the memory encryption PCD. The PCD is consumed by the following edk2 modules, which manipulate page tables: - PEI phase modules: CapsulePei, DxeIplPeim, S3Resume2Pei. CapsulePei is not used by OVMF. DxeIplPeim consumes the PCD at the end of the PEI phase, when it builds the initial page tables for the DXE core / DXE phase. S3Resume2Pei does not consume the PCD in its entry point function, only when DxeIplPeim branches to the S3 resume path at the end of the PEI phase, and calls S3Resume2Pei's EFI_PEI_S3_RESUME2_PPI.S3RestoreConfig2() member function. Therefore it is safe to set the PCD for these modules in PlatformPei. - DXE phase modules: BootScriptExecutorDxe, CpuDxe, PiSmmCpuDxeSmm. They are all dispatched after the PEI phase, so setting the PCD for them in PlatformPei is safe. (BootScriptExecutorDxe is launched "for real" in the PEI phase during S3 resume, but it caches the PCD into a static variable when its entry point is originally invoked in DXE.) Cc: Jordan Justen <jordan.l.justen@intel.com> Cc: Laszlo Ersek <lersek@redhat.com> Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Brijesh Singh <brijesh.singh@amd.com> Reviewed-by: Laszlo Ersek <lersek@redhat.com> Reviewed-by: Jordan Justen <jordan.l.justen@intel.com>
2017-07-06 15:25:48 +02:00
/**
Function checks if SEV support is available, if present then it sets
the dynamic PcdPteMemoryEncryptionAddressOrMask with memory encryption mask.
**/
VOID
AmdSevInitialize (
IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob
OvmfPkg/PlatformPei: Set memory encryption PCD when SEV is enabled Secure Encrypted Virtualization (SEV) guest VMs have the concept of private and shared memory. Private memory is encrypted with the guest-specific key, while shared memory may be encrypted with hypervisor key. Certain types of memory (namely instruction pages and guest page tables) are always treated as private memory by the hardware. For data memory, SEV guest VMs can choose which pages they would like to be private. The choice is done using the standard CPU page tables using the C-bit. When building the initial page table we mark all the memory as private. The patch sets the memory encryption PCD. The PCD is consumed by the following edk2 modules, which manipulate page tables: - PEI phase modules: CapsulePei, DxeIplPeim, S3Resume2Pei. CapsulePei is not used by OVMF. DxeIplPeim consumes the PCD at the end of the PEI phase, when it builds the initial page tables for the DXE core / DXE phase. S3Resume2Pei does not consume the PCD in its entry point function, only when DxeIplPeim branches to the S3 resume path at the end of the PEI phase, and calls S3Resume2Pei's EFI_PEI_S3_RESUME2_PPI.S3RestoreConfig2() member function. Therefore it is safe to set the PCD for these modules in PlatformPei. - DXE phase modules: BootScriptExecutorDxe, CpuDxe, PiSmmCpuDxeSmm. They are all dispatched after the PEI phase, so setting the PCD for them in PlatformPei is safe. (BootScriptExecutorDxe is launched "for real" in the PEI phase during S3 resume, but it caches the PCD into a static variable when its entry point is originally invoked in DXE.) Cc: Jordan Justen <jordan.l.justen@intel.com> Cc: Laszlo Ersek <lersek@redhat.com> Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Brijesh Singh <brijesh.singh@amd.com> Reviewed-by: Laszlo Ersek <lersek@redhat.com> Reviewed-by: Jordan Justen <jordan.l.justen@intel.com>
2017-07-06 15:25:48 +02:00
)
{
UINT64 EncryptionMask;
RETURN_STATUS PcdStatus;
//
// Check if SEV is enabled
//
if (!MemEncryptSevIsEnabled ()) {
return;
}
OvmfPkg/PlatformPei: validate the system RAM when SNP is active BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=3275 When SEV-SNP is active, a memory region mapped encrypted in the page table must be validated before access. There are two approaches that can be taken to validate the system RAM detected during the PEI phase: 1) Validate on-demand OR 2) Validate before access On-demand ========= If memory is not validated before access, it will cause a #VC exception with the page-not-validated error code. The VC exception handler can perform the validation steps. The pages that have been validated will need to be tracked to avoid the double validation scenarios. The range of memory that has not been validated will need to be communicated to the OS through the recently introduced unaccepted memory type https://github.com/microsoft/mu_basecore/pull/66, so that OS can validate those ranges before using them. Validate before access ====================== Since the PEI phase detects all the available system RAM, use the MemEncryptSevSnpValidateSystemRam() function to pre-validate the system RAM in the PEI phase. For now, choose option 2 due to the dependency and the complexity of the on-demand validation. Cc: Michael Roth <michael.roth@amd.com> Cc: James Bottomley <jejb@linux.ibm.com> Cc: Min Xu <min.m.xu@intel.com> Cc: Jiewen Yao <jiewen.yao@intel.com> Cc: Tom Lendacky <thomas.lendacky@amd.com> Cc: Jordan Justen <jordan.l.justen@intel.com> Cc: Ard Biesheuvel <ardb+tianocore@kernel.org> Cc: Erdem Aktas <erdemaktas@google.com> Cc: Gerd Hoffmann <kraxel@redhat.com> Acked-by: Jiewen Yao <Jiewen.yao@intel.com> Acked-by: Gerd Hoffmann <kraxel@redhat.com> Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
2021-12-09 04:27:47 +01:00
//
// Check and perform SEV-SNP initialization if required. This need to be
// done before the GHCB page is made shared in the AmdSevEsInitialize(). This
// is because the system RAM must be validated before it is made shared.
// The AmdSevSnpInitialize() validates the system RAM.
//
AmdSevSnpInitialize ();
OvmfPkg/PlatformPei: Set memory encryption PCD when SEV is enabled Secure Encrypted Virtualization (SEV) guest VMs have the concept of private and shared memory. Private memory is encrypted with the guest-specific key, while shared memory may be encrypted with hypervisor key. Certain types of memory (namely instruction pages and guest page tables) are always treated as private memory by the hardware. For data memory, SEV guest VMs can choose which pages they would like to be private. The choice is done using the standard CPU page tables using the C-bit. When building the initial page table we mark all the memory as private. The patch sets the memory encryption PCD. The PCD is consumed by the following edk2 modules, which manipulate page tables: - PEI phase modules: CapsulePei, DxeIplPeim, S3Resume2Pei. CapsulePei is not used by OVMF. DxeIplPeim consumes the PCD at the end of the PEI phase, when it builds the initial page tables for the DXE core / DXE phase. S3Resume2Pei does not consume the PCD in its entry point function, only when DxeIplPeim branches to the S3 resume path at the end of the PEI phase, and calls S3Resume2Pei's EFI_PEI_S3_RESUME2_PPI.S3RestoreConfig2() member function. Therefore it is safe to set the PCD for these modules in PlatformPei. - DXE phase modules: BootScriptExecutorDxe, CpuDxe, PiSmmCpuDxeSmm. They are all dispatched after the PEI phase, so setting the PCD for them in PlatformPei is safe. (BootScriptExecutorDxe is launched "for real" in the PEI phase during S3 resume, but it caches the PCD into a static variable when its entry point is originally invoked in DXE.) Cc: Jordan Justen <jordan.l.justen@intel.com> Cc: Laszlo Ersek <lersek@redhat.com> Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Brijesh Singh <brijesh.singh@amd.com> Reviewed-by: Laszlo Ersek <lersek@redhat.com> Reviewed-by: Jordan Justen <jordan.l.justen@intel.com>
2017-07-06 15:25:48 +02:00
//
// Set Memory Encryption Mask PCD
//
EncryptionMask = MemEncryptSevGetEncryptionMask ();
OvmfPkg/PlatformPei: Set memory encryption PCD when SEV is enabled Secure Encrypted Virtualization (SEV) guest VMs have the concept of private and shared memory. Private memory is encrypted with the guest-specific key, while shared memory may be encrypted with hypervisor key. Certain types of memory (namely instruction pages and guest page tables) are always treated as private memory by the hardware. For data memory, SEV guest VMs can choose which pages they would like to be private. The choice is done using the standard CPU page tables using the C-bit. When building the initial page table we mark all the memory as private. The patch sets the memory encryption PCD. The PCD is consumed by the following edk2 modules, which manipulate page tables: - PEI phase modules: CapsulePei, DxeIplPeim, S3Resume2Pei. CapsulePei is not used by OVMF. DxeIplPeim consumes the PCD at the end of the PEI phase, when it builds the initial page tables for the DXE core / DXE phase. S3Resume2Pei does not consume the PCD in its entry point function, only when DxeIplPeim branches to the S3 resume path at the end of the PEI phase, and calls S3Resume2Pei's EFI_PEI_S3_RESUME2_PPI.S3RestoreConfig2() member function. Therefore it is safe to set the PCD for these modules in PlatformPei. - DXE phase modules: BootScriptExecutorDxe, CpuDxe, PiSmmCpuDxeSmm. They are all dispatched after the PEI phase, so setting the PCD for them in PlatformPei is safe. (BootScriptExecutorDxe is launched "for real" in the PEI phase during S3 resume, but it caches the PCD into a static variable when its entry point is originally invoked in DXE.) Cc: Jordan Justen <jordan.l.justen@intel.com> Cc: Laszlo Ersek <lersek@redhat.com> Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Brijesh Singh <brijesh.singh@amd.com> Reviewed-by: Laszlo Ersek <lersek@redhat.com> Reviewed-by: Jordan Justen <jordan.l.justen@intel.com>
2017-07-06 15:25:48 +02:00
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);
OvmfPkg/PlatformPei: SEV: allocate pages of initial SMRAM save state map In the next two patches, we'll temporarily decrypt the pages containing the initial SMRAM save state map, for SMBASE relocation. (Unlike the separate, relocated SMRAM save state map of each VCPU, the original, shared map behaves similarly to a "common buffer" between guest and host.) The decryption will occur near the beginning of the DXE phase, in AmdSevDxe, and the re-encryption will occur in PiSmmCpuDxeSmm, via OVMF's SmmCpuFeaturesLib instance. There is a non-trivial time gap between these two points, and the DXE phase might use the pages overlapping the initial SMRAM save state map for arbitrary purposes meanwhile. In order to prevent any information leak towards the hypervisor, make sure the DXE phase puts nothing in those pages until re-encryption is done. Creating a memalloc HOB for the area in question is safe: - the temporary SEC/PEI RAM (stack and heap) is based at PcdOvmfSecPeiTempRamBase, which is above 8MB, - the permanent PEI RAM (installed in PlatformPei's PublishPeiMemory() function) never starts below PcdOvmfDxeMemFvBase, which is also above 8MB. The allocated pages can be released to the DXE phase after SMBASE relocation and re-encryption are complete. Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org> Cc: Brijesh Singh <brijesh.singh@amd.com> Cc: Jordan Justen <jordan.l.justen@intel.com> Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: Laszlo Ersek <lersek@redhat.com> Tested-by: Brijesh Singh <brijesh.singh@amd.com> Reviewed-by: Brijesh Singh <brijesh.singh@amd.com>
2018-03-01 17:59:19 +01:00
//
// 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 (PlatformInfoHob->SmmSmramRequire && (PlatformInfoHob->BootMode != BOOT_ON_S3_RESUME)) {
OvmfPkg/PlatformPei: SEV: allocate pages of initial SMRAM save state map In the next two patches, we'll temporarily decrypt the pages containing the initial SMRAM save state map, for SMBASE relocation. (Unlike the separate, relocated SMRAM save state map of each VCPU, the original, shared map behaves similarly to a "common buffer" between guest and host.) The decryption will occur near the beginning of the DXE phase, in AmdSevDxe, and the re-encryption will occur in PiSmmCpuDxeSmm, via OVMF's SmmCpuFeaturesLib instance. There is a non-trivial time gap between these two points, and the DXE phase might use the pages overlapping the initial SMRAM save state map for arbitrary purposes meanwhile. In order to prevent any information leak towards the hypervisor, make sure the DXE phase puts nothing in those pages until re-encryption is done. Creating a memalloc HOB for the area in question is safe: - the temporary SEC/PEI RAM (stack and heap) is based at PcdOvmfSecPeiTempRamBase, which is above 8MB, - the permanent PEI RAM (installed in PlatformPei's PublishPeiMemory() function) never starts below PcdOvmfDxeMemFvBase, which is also above 8MB. The allocated pages can be released to the DXE phase after SMBASE relocation and re-encryption are complete. Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org> Cc: Brijesh Singh <brijesh.singh@amd.com> Cc: Jordan Justen <jordan.l.justen@intel.com> Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: Laszlo Ersek <lersek@redhat.com> Tested-by: Brijesh Singh <brijesh.singh@amd.com> Reviewed-by: Brijesh Singh <brijesh.singh@amd.com>
2018-03-01 17:59:19 +01:00
RETURN_STATUS LocateMapStatus;
UINTN MapPagesBase;
UINTN MapPagesCount;
LocateMapStatus = MemEncryptSevLocateInitialSmramSaveStateMapPages (
&MapPagesBase,
&MapPagesCount
);
ASSERT_RETURN_ERROR (LocateMapStatus);
if (PlatformInfoHob->Q35SmramAtDefaultSmbase) {
OvmfPkg/SEV: don't manage the lifecycle of the SMRAM at the default SMBASE When OVMF runs in a SEV guest, the initial SMM Save State Map is (1) allocated as EfiBootServicesData type memory in OvmfPkg/PlatformPei, function AmdSevInitialize(), for preventing unintended information sharing with the hypervisor; (2) decrypted in AmdSevDxe; (3) re-encrypted in OvmfPkg/Library/SmmCpuFeaturesLib, function SmmCpuFeaturesSmmRelocationComplete(), which is called by PiSmmCpuDxeSmm right after initial SMBASE relocation; (4) released to DXE at the same location. The SMRAM at the default SMBASE is a superset of the initial Save State Map. The reserved memory allocation in InitializeRamRegions(), from the previous patch, must override the allocating and freeing in (1) and (4), respectively. (Note: the decrypting and re-encrypting in (2) and (3) are unaffected.) In AmdSevInitialize(), only assert the containment of the initial Save State Map, in the larger area already allocated by InitializeRamRegions(). In SmmCpuFeaturesSmmRelocationComplete(), preserve the allocation of the initial Save State Map into OS runtime, as part of the allocation done by InitializeRamRegions(). Only assert containment. These changes only affect the normal boot path (the UEFI memory map is untouched during S3 resume). Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org> Cc: Jordan Justen <jordan.l.justen@intel.com> Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=1512 Signed-off-by: Laszlo Ersek <lersek@redhat.com> Reviewed-by: Jiewen Yao <jiewen.yao@intel.com> Message-Id: <20200129214412.2361-9-lersek@redhat.com> Reviewed-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
2019-09-20 17:07:43 +02:00
//
// 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
);
}
OvmfPkg/PlatformPei: SEV: allocate pages of initial SMRAM save state map In the next two patches, we'll temporarily decrypt the pages containing the initial SMRAM save state map, for SMBASE relocation. (Unlike the separate, relocated SMRAM save state map of each VCPU, the original, shared map behaves similarly to a "common buffer" between guest and host.) The decryption will occur near the beginning of the DXE phase, in AmdSevDxe, and the re-encryption will occur in PiSmmCpuDxeSmm, via OVMF's SmmCpuFeaturesLib instance. There is a non-trivial time gap between these two points, and the DXE phase might use the pages overlapping the initial SMRAM save state map for arbitrary purposes meanwhile. In order to prevent any information leak towards the hypervisor, make sure the DXE phase puts nothing in those pages until re-encryption is done. Creating a memalloc HOB for the area in question is safe: - the temporary SEC/PEI RAM (stack and heap) is based at PcdOvmfSecPeiTempRamBase, which is above 8MB, - the permanent PEI RAM (installed in PlatformPei's PublishPeiMemory() function) never starts below PcdOvmfDxeMemFvBase, which is also above 8MB. The allocated pages can be released to the DXE phase after SMBASE relocation and re-encryption are complete. Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org> Cc: Brijesh Singh <brijesh.singh@amd.com> Cc: Jordan Justen <jordan.l.justen@intel.com> Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: Laszlo Ersek <lersek@redhat.com> Tested-by: Brijesh Singh <brijesh.singh@amd.com> Reviewed-by: Brijesh Singh <brijesh.singh@amd.com>
2018-03-01 17:59:19 +01:00
}
//
// Check and perform SEV-ES initialization if required.
//
AmdSevEsInitialize (PlatformInfoHob);
//
// Set the Confidential computing attr PCD to communicate which SEV
// technology is active.
//
if (MemEncryptSevSnpIsEnabled ()) {
PcdStatus = PcdSet64S (PcdConfidentialComputingGuestAttr, CCAttrAmdSevSnp);
} else if (MemEncryptSevEsIsEnabled ()) {
PcdStatus = PcdSet64S (PcdConfidentialComputingGuestAttr, CCAttrAmdSevEs);
} else {
PcdStatus = PcdSet64S (PcdConfidentialComputingGuestAttr, CCAttrAmdSev);
}
ASSERT_RETURN_ERROR (PcdStatus);
OvmfPkg/PlatformPei: Set memory encryption PCD when SEV is enabled Secure Encrypted Virtualization (SEV) guest VMs have the concept of private and shared memory. Private memory is encrypted with the guest-specific key, while shared memory may be encrypted with hypervisor key. Certain types of memory (namely instruction pages and guest page tables) are always treated as private memory by the hardware. For data memory, SEV guest VMs can choose which pages they would like to be private. The choice is done using the standard CPU page tables using the C-bit. When building the initial page table we mark all the memory as private. The patch sets the memory encryption PCD. The PCD is consumed by the following edk2 modules, which manipulate page tables: - PEI phase modules: CapsulePei, DxeIplPeim, S3Resume2Pei. CapsulePei is not used by OVMF. DxeIplPeim consumes the PCD at the end of the PEI phase, when it builds the initial page tables for the DXE core / DXE phase. S3Resume2Pei does not consume the PCD in its entry point function, only when DxeIplPeim branches to the S3 resume path at the end of the PEI phase, and calls S3Resume2Pei's EFI_PEI_S3_RESUME2_PPI.S3RestoreConfig2() member function. Therefore it is safe to set the PCD for these modules in PlatformPei. - DXE phase modules: BootScriptExecutorDxe, CpuDxe, PiSmmCpuDxeSmm. They are all dispatched after the PEI phase, so setting the PCD for them in PlatformPei is safe. (BootScriptExecutorDxe is launched "for real" in the PEI phase during S3 resume, but it caches the PCD into a static variable when its entry point is originally invoked in DXE.) Cc: Jordan Justen <jordan.l.justen@intel.com> Cc: Laszlo Ersek <lersek@redhat.com> Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Brijesh Singh <brijesh.singh@amd.com> Reviewed-by: Laszlo Ersek <lersek@redhat.com> Reviewed-by: Jordan Justen <jordan.l.justen@intel.com>
2017-07-06 15:25:48 +02:00
}
/**
The function performs SEV specific region initialization.
**/
VOID
SevInitializeRam (
VOID
)
{
if (MemEncryptSevSnpIsEnabled ()) {
//
// If SEV-SNP is enabled, reserve the Secrets and CPUID memory area.
//
// This memory range is given to the PSP by the hypervisor to populate
// the information used during the SNP VM boots, and it need to persist
// across the kexec boots. Mark it as EfiReservedMemoryType so that
// the guest firmware and OS does not use it as a system memory.
//
BuildMemoryAllocationHob (
(EFI_PHYSICAL_ADDRESS)(UINTN)PcdGet32 (PcdOvmfSnpSecretsBase),
(UINT64)(UINTN)PcdGet32 (PcdOvmfSnpSecretsSize),
EfiReservedMemoryType
);
BuildMemoryAllocationHob (
(EFI_PHYSICAL_ADDRESS)(UINTN)PcdGet32 (PcdOvmfCpuidBase),
(UINT64)(UINTN)PcdGet32 (PcdOvmfCpuidSize),
EfiReservedMemoryType
);
}
}