Skip to content

Commit 216d106

Browse files
codomaniabp3tk0v
authored andcommitted
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
1 parent 04d65a9 commit 216d106

File tree

6 files changed

+253
-1
lines changed

6 files changed

+253
-1
lines changed

arch/x86/Kbuild

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,7 @@ obj-y += net/
2828

2929
obj-$(CONFIG_KEXEC_FILE) += purgatory/
3030

31+
obj-y += virt/svm/
32+
3133
# for cleaning
3234
subdir- += boot tools

arch/x86/include/asm/msr-index.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -599,6 +599,8 @@
599599
#define MSR_AMD64_SEV_ENABLED BIT_ULL(MSR_AMD64_SEV_ENABLED_BIT)
600600
#define MSR_AMD64_SEV_ES_ENABLED BIT_ULL(MSR_AMD64_SEV_ES_ENABLED_BIT)
601601
#define MSR_AMD64_SEV_SNP_ENABLED BIT_ULL(MSR_AMD64_SEV_SNP_ENABLED_BIT)
602+
#define MSR_AMD64_RMP_BASE 0xc0010132
603+
#define MSR_AMD64_RMP_END 0xc0010133
602604

603605
/* SNP feature bits enabled by the hypervisor */
604606
#define MSR_AMD64_SNP_VTOM BIT_ULL(3)
@@ -708,8 +710,15 @@
708710
#define MSR_K8_TOP_MEM1 0xc001001a
709711
#define MSR_K8_TOP_MEM2 0xc001001d
710712
#define MSR_AMD64_SYSCFG 0xc0010010
711-
#define MSR_AMD64_SYSCFG_MEM_ENCRYPT_BIT 23
713+
#define MSR_AMD64_SYSCFG_MEM_ENCRYPT_BIT 23
712714
#define MSR_AMD64_SYSCFG_MEM_ENCRYPT BIT_ULL(MSR_AMD64_SYSCFG_MEM_ENCRYPT_BIT)
715+
#define MSR_AMD64_SYSCFG_SNP_EN_BIT 24
716+
#define MSR_AMD64_SYSCFG_SNP_EN BIT_ULL(MSR_AMD64_SYSCFG_SNP_EN_BIT)
717+
#define MSR_AMD64_SYSCFG_SNP_VMPL_EN_BIT 25
718+
#define MSR_AMD64_SYSCFG_SNP_VMPL_EN BIT_ULL(MSR_AMD64_SYSCFG_SNP_VMPL_EN_BIT)
719+
#define MSR_AMD64_SYSCFG_MFDM_BIT 19
720+
#define MSR_AMD64_SYSCFG_MFDM BIT_ULL(MSR_AMD64_SYSCFG_MFDM_BIT)
721+
713722
#define MSR_K8_INT_PENDING_MSG 0xc0010055
714723
/* C1E active bits in int pending message */
715724
#define K8_INTP_C1E_ACTIVE_MASK 0x18000000

arch/x86/include/asm/sev.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,4 +243,10 @@ static inline u64 snp_get_unsupported_features(u64 status) { return 0; }
243243
static inline u64 sev_get_status(void) { return 0; }
244244
#endif
245245

246+
#ifdef CONFIG_KVM_AMD_SEV
247+
bool snp_probe_rmptable_info(void);
248+
#else
249+
static inline bool snp_probe_rmptable_info(void) { return false; }
250+
#endif
251+
246252
#endif

arch/x86/kernel/cpu/amd.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <asm/delay.h>
2121
#include <asm/debugreg.h>
2222
#include <asm/resctrl.h>
23+
#include <asm/sev.h>
2324

2425
#ifdef CONFIG_X86_64
2526
# include <asm/mmconfig.h>
@@ -587,6 +588,21 @@ static void bsp_init_amd(struct cpuinfo_x86 *c)
587588
break;
588589
}
589590

591+
if (cpu_has(c, X86_FEATURE_SEV_SNP)) {
592+
/*
593+
* RMP table entry format is not architectural and it can vary by processor
594+
* and is defined by the per-processor PPR. Restrict SNP support on the
595+
* known CPU model and family for which the RMP table entry format is
596+
* currently defined for.
597+
*/
598+
if (!boot_cpu_has(X86_FEATURE_ZEN3) &&
599+
!boot_cpu_has(X86_FEATURE_ZEN4) &&
600+
!boot_cpu_has(X86_FEATURE_ZEN5))
601+
setup_clear_cpu_cap(X86_FEATURE_SEV_SNP);
602+
else if (!snp_probe_rmptable_info())
603+
setup_clear_cpu_cap(X86_FEATURE_SEV_SNP);
604+
}
605+
590606
return;
591607

592608
warn:

arch/x86/virt/svm/Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# SPDX-License-Identifier: GPL-2.0
2+
3+
obj-$(CONFIG_KVM_AMD_SEV) += sev.o

arch/x86/virt/svm/sev.c

Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
/*
3+
* AMD SVM-SEV Host Support.
4+
*
5+
* Copyright (C) 2023 Advanced Micro Devices, Inc.
6+
*
7+
* Author: Ashish Kalra <ashish.kalra@amd.com>
8+
*
9+
*/
10+
11+
#include <linux/cc_platform.h>
12+
#include <linux/printk.h>
13+
#include <linux/mm_types.h>
14+
#include <linux/set_memory.h>
15+
#include <linux/memblock.h>
16+
#include <linux/kernel.h>
17+
#include <linux/mm.h>
18+
#include <linux/cpumask.h>
19+
#include <linux/iommu.h>
20+
#include <linux/amd-iommu.h>
21+
22+
#include <asm/sev.h>
23+
#include <asm/processor.h>
24+
#include <asm/setup.h>
25+
#include <asm/svm.h>
26+
#include <asm/smp.h>
27+
#include <asm/cpu.h>
28+
#include <asm/apic.h>
29+
#include <asm/cpuid.h>
30+
#include <asm/cmdline.h>
31+
#include <asm/iommu.h>
32+
33+
/*
34+
* The RMP entry format is not architectural. The format is defined in PPR
35+
* Family 19h Model 01h, Rev B1 processor.
36+
*/
37+
struct rmpentry {
38+
u64 assigned : 1,
39+
pagesize : 1,
40+
immutable : 1,
41+
rsvd1 : 9,
42+
gpa : 39,
43+
asid : 10,
44+
vmsa : 1,
45+
validated : 1,
46+
rsvd2 : 1;
47+
u64 rsvd3;
48+
} __packed;
49+
50+
/*
51+
* The first 16KB from the RMP_BASE is used by the processor for the
52+
* bookkeeping, the range needs to be added during the RMP entry lookup.
53+
*/
54+
#define RMPTABLE_CPU_BOOKKEEPING_SZ 0x4000
55+
56+
static u64 probed_rmp_base, probed_rmp_size;
57+
static struct rmpentry *rmptable __ro_after_init;
58+
static u64 rmptable_max_pfn __ro_after_init;
59+
60+
#undef pr_fmt
61+
#define pr_fmt(fmt) "SEV-SNP: " fmt
62+
63+
static int __mfd_enable(unsigned int cpu)
64+
{
65+
u64 val;
66+
67+
if (!cpu_feature_enabled(X86_FEATURE_SEV_SNP))
68+
return 0;
69+
70+
rdmsrl(MSR_AMD64_SYSCFG, val);
71+
72+
val |= MSR_AMD64_SYSCFG_MFDM;
73+
74+
wrmsrl(MSR_AMD64_SYSCFG, val);
75+
76+
return 0;
77+
}
78+
79+
static __init void mfd_enable(void *arg)
80+
{
81+
__mfd_enable(smp_processor_id());
82+
}
83+
84+
static int __snp_enable(unsigned int cpu)
85+
{
86+
u64 val;
87+
88+
if (!cpu_feature_enabled(X86_FEATURE_SEV_SNP))
89+
return 0;
90+
91+
rdmsrl(MSR_AMD64_SYSCFG, val);
92+
93+
val |= MSR_AMD64_SYSCFG_SNP_EN;
94+
val |= MSR_AMD64_SYSCFG_SNP_VMPL_EN;
95+
96+
wrmsrl(MSR_AMD64_SYSCFG, val);
97+
98+
return 0;
99+
}
100+
101+
static __init void snp_enable(void *arg)
102+
{
103+
__snp_enable(smp_processor_id());
104+
}
105+
106+
#define RMP_ADDR_MASK GENMASK_ULL(51, 13)
107+
108+
bool snp_probe_rmptable_info(void)
109+
{
110+
u64 max_rmp_pfn, calc_rmp_sz, rmp_sz, rmp_base, rmp_end;
111+
112+
rdmsrl(MSR_AMD64_RMP_BASE, rmp_base);
113+
rdmsrl(MSR_AMD64_RMP_END, rmp_end);
114+
115+
if (!(rmp_base & RMP_ADDR_MASK) || !(rmp_end & RMP_ADDR_MASK)) {
116+
pr_err("Memory for the RMP table has not been reserved by BIOS\n");
117+
return false;
118+
}
119+
120+
if (rmp_base > rmp_end) {
121+
pr_err("RMP configuration not valid: base=%#llx, end=%#llx\n", rmp_base, rmp_end);
122+
return false;
123+
}
124+
125+
rmp_sz = rmp_end - rmp_base + 1;
126+
127+
/*
128+
* Calculate the amount the memory that must be reserved by the BIOS to
129+
* address the whole RAM, including the bookkeeping area. The RMP itself
130+
* must also be covered.
131+
*/
132+
max_rmp_pfn = max_pfn;
133+
if (PHYS_PFN(rmp_end) > max_pfn)
134+
max_rmp_pfn = PHYS_PFN(rmp_end);
135+
136+
calc_rmp_sz = (max_rmp_pfn << 4) + RMPTABLE_CPU_BOOKKEEPING_SZ;
137+
138+
if (calc_rmp_sz > rmp_sz) {
139+
pr_err("Memory reserved for the RMP table does not cover full system RAM (expected 0x%llx got 0x%llx)\n",
140+
calc_rmp_sz, rmp_sz);
141+
return false;
142+
}
143+
144+
probed_rmp_base = rmp_base;
145+
probed_rmp_size = rmp_sz;
146+
147+
pr_info("RMP table physical range [0x%016llx - 0x%016llx]\n",
148+
probed_rmp_base, probed_rmp_base + probed_rmp_size - 1);
149+
150+
return true;
151+
}
152+
153+
/*
154+
* Do the necessary preparations which are verified by the firmware as
155+
* described in the SNP_INIT_EX firmware command description in the SNP
156+
* firmware ABI spec.
157+
*/
158+
static int __init snp_rmptable_init(void)
159+
{
160+
void *rmptable_start;
161+
u64 rmptable_size;
162+
u64 val;
163+
164+
if (!cpu_feature_enabled(X86_FEATURE_SEV_SNP))
165+
return 0;
166+
167+
if (!amd_iommu_snp_en)
168+
return 0;
169+
170+
if (!probed_rmp_size)
171+
goto nosnp;
172+
173+
rmptable_start = memremap(probed_rmp_base, probed_rmp_size, MEMREMAP_WB);
174+
if (!rmptable_start) {
175+
pr_err("Failed to map RMP table\n");
176+
return 1;
177+
}
178+
179+
/*
180+
* Check if SEV-SNP is already enabled, this can happen in case of
181+
* kexec boot.
182+
*/
183+
rdmsrl(MSR_AMD64_SYSCFG, val);
184+
if (val & MSR_AMD64_SYSCFG_SNP_EN)
185+
goto skip_enable;
186+
187+
memset(rmptable_start, 0, probed_rmp_size);
188+
189+
/* Flush the caches to ensure that data is written before SNP is enabled. */
190+
wbinvd_on_all_cpus();
191+
192+
/* MtrrFixDramModEn must be enabled on all the CPUs prior to enabling SNP. */
193+
on_each_cpu(mfd_enable, NULL, 1);
194+
195+
on_each_cpu(snp_enable, NULL, 1);
196+
197+
skip_enable:
198+
rmptable_start += RMPTABLE_CPU_BOOKKEEPING_SZ;
199+
rmptable_size = probed_rmp_size - RMPTABLE_CPU_BOOKKEEPING_SZ;
200+
201+
rmptable = (struct rmpentry *)rmptable_start;
202+
rmptable_max_pfn = rmptable_size / sizeof(struct rmpentry) - 1;
203+
204+
cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "x86/rmptable_init:online", __snp_enable, NULL);
205+
206+
return 0;
207+
208+
nosnp:
209+
setup_clear_cpu_cap(X86_FEATURE_SEV_SNP);
210+
return -ENOSYS;
211+
}
212+
213+
/*
214+
* This must be called after the IOMMU has been initialized.
215+
*/
216+
device_initcall(snp_rmptable_init);

0 commit comments

Comments
 (0)