mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2024-12-28 16:52:18 +00:00
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
This commit is contained in:
parent
04d65a9dbb
commit
216d106c7f
@ -28,5 +28,7 @@ obj-y += net/
|
||||
|
||||
obj-$(CONFIG_KEXEC_FILE) += purgatory/
|
||||
|
||||
obj-y += virt/svm/
|
||||
|
||||
# for cleaning
|
||||
subdir- += boot tools
|
||||
|
@ -599,6 +599,8 @@
|
||||
#define MSR_AMD64_SEV_ENABLED BIT_ULL(MSR_AMD64_SEV_ENABLED_BIT)
|
||||
#define MSR_AMD64_SEV_ES_ENABLED BIT_ULL(MSR_AMD64_SEV_ES_ENABLED_BIT)
|
||||
#define MSR_AMD64_SEV_SNP_ENABLED BIT_ULL(MSR_AMD64_SEV_SNP_ENABLED_BIT)
|
||||
#define MSR_AMD64_RMP_BASE 0xc0010132
|
||||
#define MSR_AMD64_RMP_END 0xc0010133
|
||||
|
||||
/* SNP feature bits enabled by the hypervisor */
|
||||
#define MSR_AMD64_SNP_VTOM BIT_ULL(3)
|
||||
@ -708,8 +710,15 @@
|
||||
#define MSR_K8_TOP_MEM1 0xc001001a
|
||||
#define MSR_K8_TOP_MEM2 0xc001001d
|
||||
#define MSR_AMD64_SYSCFG 0xc0010010
|
||||
#define MSR_AMD64_SYSCFG_MEM_ENCRYPT_BIT 23
|
||||
#define MSR_AMD64_SYSCFG_MEM_ENCRYPT_BIT 23
|
||||
#define MSR_AMD64_SYSCFG_MEM_ENCRYPT BIT_ULL(MSR_AMD64_SYSCFG_MEM_ENCRYPT_BIT)
|
||||
#define MSR_AMD64_SYSCFG_SNP_EN_BIT 24
|
||||
#define MSR_AMD64_SYSCFG_SNP_EN BIT_ULL(MSR_AMD64_SYSCFG_SNP_EN_BIT)
|
||||
#define MSR_AMD64_SYSCFG_SNP_VMPL_EN_BIT 25
|
||||
#define MSR_AMD64_SYSCFG_SNP_VMPL_EN BIT_ULL(MSR_AMD64_SYSCFG_SNP_VMPL_EN_BIT)
|
||||
#define MSR_AMD64_SYSCFG_MFDM_BIT 19
|
||||
#define MSR_AMD64_SYSCFG_MFDM BIT_ULL(MSR_AMD64_SYSCFG_MFDM_BIT)
|
||||
|
||||
#define MSR_K8_INT_PENDING_MSG 0xc0010055
|
||||
/* C1E active bits in int pending message */
|
||||
#define K8_INTP_C1E_ACTIVE_MASK 0x18000000
|
||||
|
@ -243,4 +243,10 @@ static inline u64 snp_get_unsupported_features(u64 status) { return 0; }
|
||||
static inline u64 sev_get_status(void) { return 0; }
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_KVM_AMD_SEV
|
||||
bool snp_probe_rmptable_info(void);
|
||||
#else
|
||||
static inline bool snp_probe_rmptable_info(void) { return false; }
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include <asm/delay.h>
|
||||
#include <asm/debugreg.h>
|
||||
#include <asm/resctrl.h>
|
||||
#include <asm/sev.h>
|
||||
|
||||
#ifdef CONFIG_X86_64
|
||||
# include <asm/mmconfig.h>
|
||||
@ -587,6 +588,21 @@ static void bsp_init_amd(struct cpuinfo_x86 *c)
|
||||
break;
|
||||
}
|
||||
|
||||
if (cpu_has(c, X86_FEATURE_SEV_SNP)) {
|
||||
/*
|
||||
* RMP table entry format is not architectural and it can vary by processor
|
||||
* and is defined by the per-processor PPR. Restrict SNP support on the
|
||||
* known CPU model and family for which the RMP table entry format is
|
||||
* currently defined for.
|
||||
*/
|
||||
if (!boot_cpu_has(X86_FEATURE_ZEN3) &&
|
||||
!boot_cpu_has(X86_FEATURE_ZEN4) &&
|
||||
!boot_cpu_has(X86_FEATURE_ZEN5))
|
||||
setup_clear_cpu_cap(X86_FEATURE_SEV_SNP);
|
||||
else if (!snp_probe_rmptable_info())
|
||||
setup_clear_cpu_cap(X86_FEATURE_SEV_SNP);
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
warn:
|
||||
|
3
arch/x86/virt/svm/Makefile
Normal file
3
arch/x86/virt/svm/Makefile
Normal file
@ -0,0 +1,3 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
obj-$(CONFIG_KVM_AMD_SEV) += sev.o
|
216
arch/x86/virt/svm/sev.c
Normal file
216
arch/x86/virt/svm/sev.c
Normal file
@ -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);
|
Loading…
Reference in New Issue
Block a user