forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
x86/sev: Add SEV-SNP host initialization support
The memory integrity guarantees of SEV-SNP are enforced through a new structure called the Reverse Map Table (RMP). The RMP is a single data structure shared across the system that contains one entry for every 4K page of DRAM that may be used by SEV-SNP VMs. The APM Volume 2 section on Secure Nested Paging (SEV-SNP) details a number of steps needed to detect/enable SEV-SNP and RMP table support on the host: - Detect SEV-SNP support based on CPUID bit - Initialize the RMP table memory reported by the RMP base/end MSR registers and configure IOMMU to be compatible with RMP access restrictions - Set the MtrrFixDramModEn bit in SYSCFG MSR - Set the SecureNestedPagingEn and VMPLEn bits in the SYSCFG MSR - Configure IOMMU RMP table entry format is non-architectural and it can vary by processor. It is defined by the PPR document for each respective CPU family. Restrict SNP support to CPU models/families which are compatible with the current RMP table entry format to guard against any undefined behavior when running on other system types. Future models/support will handle this through an architectural mechanism to allow for broader compatibility. SNP host code depends on CONFIG_KVM_AMD_SEV config flag which may be enabled even when CONFIG_AMD_MEM_ENCRYPT isn't set, so update the SNP-specific IOMMU helpers used here to rely on CONFIG_KVM_AMD_SEV instead of CONFIG_AMD_MEM_ENCRYPT. Signed-off-by: Brijesh Singh <brijesh.singh@amd.com> Co-developed-by: Ashish Kalra <ashish.kalra@amd.com> Signed-off-by: Ashish Kalra <ashish.kalra@amd.com> Co-developed-by: Tom Lendacky <thomas.lendacky@amd.com> Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com> Co-developed-by: Borislav Petkov (AMD) <bp@alien8.de> Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de> Co-developed-by: Michael Roth <michael.roth@amd.com> Signed-off-by: Michael Roth <michael.roth@amd.com> Link: https://lore.kernel.org/r/20240126041126.1927228-5-michael.roth@amd.com
- Loading branch information
Showing
6 changed files
with
253 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -28,5 +28,7 @@ obj-y += net/ | |
|
||
obj-$(CONFIG_KEXEC_FILE) += purgatory/ | ||
|
||
obj-y += virt/svm/ | ||
|
||
# for cleaning | ||
subdir- += boot tools |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# SPDX-License-Identifier: GPL-2.0 | ||
|
||
obj-$(CONFIG_KVM_AMD_SEV) += sev.o |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,216 @@ | ||
// SPDX-License-Identifier: GPL-2.0-only | ||
/* | ||
* AMD SVM-SEV Host Support. | ||
* | ||
* Copyright (C) 2023 Advanced Micro Devices, Inc. | ||
* | ||
* Author: Ashish Kalra <ashish.kalra@amd.com> | ||
* | ||
*/ | ||
|
||
#include <linux/cc_platform.h> | ||
#include <linux/printk.h> | ||
#include <linux/mm_types.h> | ||
#include <linux/set_memory.h> | ||
#include <linux/memblock.h> | ||
#include <linux/kernel.h> | ||
#include <linux/mm.h> | ||
#include <linux/cpumask.h> | ||
#include <linux/iommu.h> | ||
#include <linux/amd-iommu.h> | ||
|
||
#include <asm/sev.h> | ||
#include <asm/processor.h> | ||
#include <asm/setup.h> | ||
#include <asm/svm.h> | ||
#include <asm/smp.h> | ||
#include <asm/cpu.h> | ||
#include <asm/apic.h> | ||
#include <asm/cpuid.h> | ||
#include <asm/cmdline.h> | ||
#include <asm/iommu.h> | ||
|
||
/* | ||
* The RMP entry format is not architectural. The format is defined in PPR | ||
* Family 19h Model 01h, Rev B1 processor. | ||
*/ | ||
struct rmpentry { | ||
u64 assigned : 1, | ||
pagesize : 1, | ||
immutable : 1, | ||
rsvd1 : 9, | ||
gpa : 39, | ||
asid : 10, | ||
vmsa : 1, | ||
validated : 1, | ||
rsvd2 : 1; | ||
u64 rsvd3; | ||
} __packed; | ||
|
||
/* | ||
* The first 16KB from the RMP_BASE is used by the processor for the | ||
* bookkeeping, the range needs to be added during the RMP entry lookup. | ||
*/ | ||
#define RMPTABLE_CPU_BOOKKEEPING_SZ 0x4000 | ||
|
||
static u64 probed_rmp_base, probed_rmp_size; | ||
static struct rmpentry *rmptable __ro_after_init; | ||
static u64 rmptable_max_pfn __ro_after_init; | ||
|
||
#undef pr_fmt | ||
#define pr_fmt(fmt) "SEV-SNP: " fmt | ||
|
||
static int __mfd_enable(unsigned int cpu) | ||
{ | ||
u64 val; | ||
|
||
if (!cpu_feature_enabled(X86_FEATURE_SEV_SNP)) | ||
return 0; | ||
|
||
rdmsrl(MSR_AMD64_SYSCFG, val); | ||
|
||
val |= MSR_AMD64_SYSCFG_MFDM; | ||
|
||
wrmsrl(MSR_AMD64_SYSCFG, val); | ||
|
||
return 0; | ||
} | ||
|
||
static __init void mfd_enable(void *arg) | ||
{ | ||
__mfd_enable(smp_processor_id()); | ||
} | ||
|
||
static int __snp_enable(unsigned int cpu) | ||
{ | ||
u64 val; | ||
|
||
if (!cpu_feature_enabled(X86_FEATURE_SEV_SNP)) | ||
return 0; | ||
|
||
rdmsrl(MSR_AMD64_SYSCFG, val); | ||
|
||
val |= MSR_AMD64_SYSCFG_SNP_EN; | ||
val |= MSR_AMD64_SYSCFG_SNP_VMPL_EN; | ||
|
||
wrmsrl(MSR_AMD64_SYSCFG, val); | ||
|
||
return 0; | ||
} | ||
|
||
static __init void snp_enable(void *arg) | ||
{ | ||
__snp_enable(smp_processor_id()); | ||
} | ||
|
||
#define RMP_ADDR_MASK GENMASK_ULL(51, 13) | ||
|
||
bool snp_probe_rmptable_info(void) | ||
{ | ||
u64 max_rmp_pfn, calc_rmp_sz, rmp_sz, rmp_base, rmp_end; | ||
|
||
rdmsrl(MSR_AMD64_RMP_BASE, rmp_base); | ||
rdmsrl(MSR_AMD64_RMP_END, rmp_end); | ||
|
||
if (!(rmp_base & RMP_ADDR_MASK) || !(rmp_end & RMP_ADDR_MASK)) { | ||
pr_err("Memory for the RMP table has not been reserved by BIOS\n"); | ||
return false; | ||
} | ||
|
||
if (rmp_base > rmp_end) { | ||
pr_err("RMP configuration not valid: base=%#llx, end=%#llx\n", rmp_base, rmp_end); | ||
return false; | ||
} | ||
|
||
rmp_sz = rmp_end - rmp_base + 1; | ||
|
||
/* | ||
* Calculate the amount the memory that must be reserved by the BIOS to | ||
* address the whole RAM, including the bookkeeping area. The RMP itself | ||
* must also be covered. | ||
*/ | ||
max_rmp_pfn = max_pfn; | ||
if (PHYS_PFN(rmp_end) > max_pfn) | ||
max_rmp_pfn = PHYS_PFN(rmp_end); | ||
|
||
calc_rmp_sz = (max_rmp_pfn << 4) + RMPTABLE_CPU_BOOKKEEPING_SZ; | ||
|
||
if (calc_rmp_sz > rmp_sz) { | ||
pr_err("Memory reserved for the RMP table does not cover full system RAM (expected 0x%llx got 0x%llx)\n", | ||
calc_rmp_sz, rmp_sz); | ||
return false; | ||
} | ||
|
||
probed_rmp_base = rmp_base; | ||
probed_rmp_size = rmp_sz; | ||
|
||
pr_info("RMP table physical range [0x%016llx - 0x%016llx]\n", | ||
probed_rmp_base, probed_rmp_base + probed_rmp_size - 1); | ||
|
||
return true; | ||
} | ||
|
||
/* | ||
* Do the necessary preparations which are verified by the firmware as | ||
* described in the SNP_INIT_EX firmware command description in the SNP | ||
* firmware ABI spec. | ||
*/ | ||
static int __init snp_rmptable_init(void) | ||
{ | ||
void *rmptable_start; | ||
u64 rmptable_size; | ||
u64 val; | ||
|
||
if (!cpu_feature_enabled(X86_FEATURE_SEV_SNP)) | ||
return 0; | ||
|
||
if (!amd_iommu_snp_en) | ||
return 0; | ||
|
||
if (!probed_rmp_size) | ||
goto nosnp; | ||
|
||
rmptable_start = memremap(probed_rmp_base, probed_rmp_size, MEMREMAP_WB); | ||
if (!rmptable_start) { | ||
pr_err("Failed to map RMP table\n"); | ||
return 1; | ||
} | ||
|
||
/* | ||
* Check if SEV-SNP is already enabled, this can happen in case of | ||
* kexec boot. | ||
*/ | ||
rdmsrl(MSR_AMD64_SYSCFG, val); | ||
if (val & MSR_AMD64_SYSCFG_SNP_EN) | ||
goto skip_enable; | ||
|
||
memset(rmptable_start, 0, probed_rmp_size); | ||
|
||
/* Flush the caches to ensure that data is written before SNP is enabled. */ | ||
wbinvd_on_all_cpus(); | ||
|
||
/* MtrrFixDramModEn must be enabled on all the CPUs prior to enabling SNP. */ | ||
on_each_cpu(mfd_enable, NULL, 1); | ||
|
||
on_each_cpu(snp_enable, NULL, 1); | ||
|
||
skip_enable: | ||
rmptable_start += RMPTABLE_CPU_BOOKKEEPING_SZ; | ||
rmptable_size = probed_rmp_size - RMPTABLE_CPU_BOOKKEEPING_SZ; | ||
|
||
rmptable = (struct rmpentry *)rmptable_start; | ||
rmptable_max_pfn = rmptable_size / sizeof(struct rmpentry) - 1; | ||
|
||
cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "x86/rmptable_init:online", __snp_enable, NULL); | ||
|
||
return 0; | ||
|
||
nosnp: | ||
setup_clear_cpu_cap(X86_FEATURE_SEV_SNP); | ||
return -ENOSYS; | ||
} | ||
|
||
/* | ||
* This must be called after the IOMMU has been initialized. | ||
*/ | ||
device_initcall(snp_rmptable_init); |