Skip to content

Commit

Permalink
x86/sev: Add the host SEV-SNP initialization support
Browse files Browse the repository at this point in the history
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. APM2 section 15.36 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. 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.

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>
Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
[mdr: rework commit message to be clearer about what patch does, squash
      in early_rmptable_check() handling from Tom]
Signed-off-by: Michael Roth <michael.roth@amd.com>
  • Loading branch information
codomania authored and mdroth committed Jun 29, 2023
1 parent ed73aa5 commit a889a2d
Show file tree
Hide file tree
Showing 9 changed files with 282 additions and 4 deletions.
2 changes: 2 additions & 0 deletions arch/x86/Kbuild
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,7 @@ obj-y += net/

obj-$(CONFIG_KEXEC_FILE) += purgatory/

obj-y += virt/svm/

# for cleaning
subdir- += boot tools
8 changes: 7 additions & 1 deletion arch/x86/include/asm/disabled-features.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,12 @@
# define DISABLE_TDX_GUEST (1 << (X86_FEATURE_TDX_GUEST & 31))
#endif

#ifdef CONFIG_KVM_AMD_SEV
# define DISABLE_SEV_SNP 0
#else
# define DISABLE_SEV_SNP (1 << (X86_FEATURE_SEV_SNP & 31))
#endif

/*
* Make sure to add features to the correct mask
*/
Expand All @@ -123,7 +129,7 @@
DISABLE_ENQCMD)
#define DISABLED_MASK17 0
#define DISABLED_MASK18 0
#define DISABLED_MASK19 0
#define DISABLED_MASK19 (DISABLE_SEV_SNP)
#define DISABLED_MASK20 0
#define DISABLED_MASK_CHECK BUILD_BUG_ON_ZERO(NCAPINTS != 21)

Expand Down
11 changes: 10 additions & 1 deletion arch/x86/include/asm/msr-index.h
Original file line number Diff line number Diff line change
Expand Up @@ -574,6 +574,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)
Expand Down Expand Up @@ -675,7 +677,14 @@
#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_ULL(MSR_AMD64_SYSCFG_MEM_ENCRYPT_BIT)
#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
Expand Down
2 changes: 2 additions & 0 deletions arch/x86/include/asm/sev.h
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ void snp_set_wakeup_secondary_cpu(void);
bool snp_init(struct boot_params *bp);
void __init __noreturn snp_abort(void);
int snp_issue_guest_request(u64 exit_code, struct snp_req_data *input, unsigned long *fw_err);
bool snp_get_rmptable_info(u64 *start, u64 *len);
#else
static inline void sev_es_ist_enter(struct pt_regs *regs) { }
static inline void sev_es_ist_exit(void) { }
Expand All @@ -221,6 +222,7 @@ static inline int snp_issue_guest_request(u64 exit_code, struct snp_req_data *in
{
return -ENOTTY;
}
static inline bool snp_get_rmptable_info(u64 *start, u64 *len) { return false; }
#endif

#endif
19 changes: 19 additions & 0 deletions arch/x86/kernel/cpu/amd.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -546,6 +547,20 @@ static void bsp_init_amd(struct cpuinfo_x86 *c)
resctrl_cpu_detect(c);
}

static bool early_rmptable_check(void)
{
u64 rmp_base, rmp_size;

/*
* For early BSP initialization, max_pfn won't be set up yet, wait until
* it is set before performing the RMP table calculations.
*/
if (!max_pfn)
return true;

return snp_get_rmptable_info(&rmp_base, &rmp_size);
}

static void early_detect_mem_encrypt(struct cpuinfo_x86 *c)
{
u64 msr;
Expand Down Expand Up @@ -587,13 +602,17 @@ static void early_detect_mem_encrypt(struct cpuinfo_x86 *c)
if (!(msr & MSR_K7_HWCR_SMMLOCK))
goto clear_sev;

if (cpu_has(c, X86_FEATURE_SEV_SNP) && !early_rmptable_check())
goto clear_snp;

return;

clear_all:
setup_clear_cpu_cap(X86_FEATURE_SME);
clear_sev:
setup_clear_cpu_cap(X86_FEATURE_SEV);
setup_clear_cpu_cap(X86_FEATURE_SEV_ES);
clear_snp:
setup_clear_cpu_cap(X86_FEATURE_SEV_SNP);
}
}
Expand Down
3 changes: 3 additions & 0 deletions arch/x86/virt/svm/Makefile
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
237 changes: 237 additions & 0 deletions arch/x86/virt/svm/sev.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
// 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 {
union {
struct {
u64 assigned : 1,
pagesize : 1,
immutable : 1,
rsvd1 : 9,
gpa : 39,
asid : 10,
vmsa : 1,
validated : 1,
rsvd2 : 1;
} info;
u64 low;
};
u64 high;
} __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 struct rmpentry *rmptable_start __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());
}

bool snp_get_rmptable_info(u64 *start, u64 *len)
{
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_end) {
pr_err("Memory for the RMP table has not been reserved by BIOS\n");
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;
}

*start = rmp_base;
*len = rmp_sz;

return true;
}

static __init int __snp_rmptable_init(void)
{
u64 rmp_base, rmp_size;
void *rmp_start;
u64 val;

if (!snp_get_rmptable_info(&rmp_base, &rmp_size))
return 1;

pr_info("RMP table physical address [0x%016llx - 0x%016llx]\n",
rmp_base, rmp_base + rmp_size - 1);

rmp_start = memremap(rmp_base, rmp_size, MEMREMAP_WB);
if (!rmp_start) {
pr_err("Failed to map RMP table addr 0x%llx size 0x%llx\n", rmp_base, rmp_size);
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;

/* Initialize the RMP table to zero */
memset(rmp_start, 0, rmp_size);

/* Flush the caches to ensure that data is written before SNP is enabled. */
wbinvd_on_all_cpus();

/* MFDM must be enabled on all the CPUs prior to enabling SNP. */
on_each_cpu(mfd_enable, NULL, 1);

/* Enable SNP on all CPUs. */
on_each_cpu(snp_enable, NULL, 1);

skip_enable:
rmp_start += RMPTABLE_CPU_BOOKKEEPING_SZ;
rmp_size -= RMPTABLE_CPU_BOOKKEEPING_SZ;

rmptable_start = (struct rmpentry *)rmp_start;
rmptable_max_pfn = rmp_size / sizeof(struct rmpentry) - 1;

return 0;
}

static int __init snp_rmptable_init(void)
{
int family, model;

if (!cpu_feature_enabled(X86_FEATURE_SEV_SNP))
return 0;

family = boot_cpu_data.x86;
model = boot_cpu_data.x86_model;

/*
* 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 (family != 0x19 || model > 0xaf)
goto nosnp;

if (amd_iommu_snp_enable())
goto nosnp;

if (__snp_rmptable_init())
goto nosnp;

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 PCI subsystem. This is because amd_iommu_snp_enable()
* is called to ensure the IOMMU supports the SEV-SNP feature, which can only be
* called after subsys_initcall().
*
* NOTE: IOMMU is enforced by SNP to ensure that hypervisor cannot program DMA
* directly into guest private memory. In case of SNP, the IOMMU ensures that
* the page(s) used for DMA are hypervisor owned.
*/
fs_initcall(snp_rmptable_init);
2 changes: 1 addition & 1 deletion drivers/iommu/amd/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -3665,7 +3665,7 @@ int amd_iommu_pc_set_reg(struct amd_iommu *iommu, u8 bank, u8 cntr, u8 fxn, u64
return iommu_pc_get_set_reg(iommu, bank, cntr, fxn, value, true);
}

#ifdef CONFIG_AMD_MEM_ENCRYPT
#ifdef CONFIG_KVM_AMD_SEV
int amd_iommu_snp_enable(void)
{
/*
Expand Down
2 changes: 1 addition & 1 deletion include/linux/amd-iommu.h
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ int amd_iommu_pc_get_reg(struct amd_iommu *iommu, u8 bank, u8 cntr, u8 fxn,
u64 *value);
struct amd_iommu *get_amd_iommu(unsigned int idx);

#ifdef CONFIG_AMD_MEM_ENCRYPT
#ifdef CONFIG_KVM_AMD_SEV
int amd_iommu_snp_enable(void);
#endif

Expand Down

0 comments on commit a889a2d

Please sign in to comment.