Skip to content

Commit 6c32117

Browse files
tlendackybp3tk0v
authored andcommitted
x86/sev: Add SNP-specific unaccepted memory support
Add SNP-specific hooks to the unaccepted memory support in the boot path (__accept_memory()) and the core kernel (accept_memory()) in order to support booting SNP guests when unaccepted memory is present. Without this support, SNP guests will fail to boot and/or panic() when unaccepted memory is present in the EFI memory map. The process of accepting memory under SNP involves invoking the hypervisor to perform a page state change for the page to private memory and then issuing a PVALIDATE instruction to accept the page. Since the boot path and the core kernel paths perform similar operations, move the pvalidate_pages() and vmgexit_psc() functions into sev-shared.c to avoid code duplication. Create the new header file arch/x86/boot/compressed/sev.h because adding the function declaration to any of the existing SEV related header files pulls in too many other header files, causing the build to fail. Signed-off-by: Tom Lendacky <[email protected]> Signed-off-by: Borislav Petkov (AMD) <[email protected]> Link: https://lore.kernel.org/r/a52fa69f460fd1876d70074b20ad68210dfc31dd.1686063086.git.thomas.lendacky@amd.com
1 parent 15d9088 commit 6c32117

File tree

8 files changed

+204
-99
lines changed

8 files changed

+204
-99
lines changed

arch/x86/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1543,11 +1543,13 @@ config X86_MEM_ENCRYPT
15431543
config AMD_MEM_ENCRYPT
15441544
bool "AMD Secure Memory Encryption (SME) support"
15451545
depends on X86_64 && CPU_SUP_AMD
1546+
depends on EFI_STUB
15461547
select DMA_COHERENT_POOL
15471548
select ARCH_USE_MEMREMAP_PROT
15481549
select INSTRUCTION_DECODER
15491550
select ARCH_HAS_CC_PLATFORM
15501551
select X86_MEM_ENCRYPT
1552+
select UNACCEPTED_MEMORY
15511553
help
15521554
Say yes to enable support for the encryption of system memory.
15531555
This requires an AMD processor that supports Secure Memory

arch/x86/boot/compressed/mem.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include "error.h"
44
#include "misc.h"
55
#include "tdx.h"
6+
#include "sev.h"
67
#include <asm/shared/tdx.h>
78

89
/*
@@ -37,6 +38,8 @@ void arch_accept_memory(phys_addr_t start, phys_addr_t end)
3738
if (early_is_tdx_guest()) {
3839
if (!tdx_accept_memory(start, end))
3940
panic("TDX: Failed to accept memory\n");
41+
} else if (sev_snp_enabled()) {
42+
snp_accept_memory(start, end);
4043
} else {
4144
error("Cannot accept memory: unknown platform\n");
4245
}

arch/x86/boot/compressed/sev.c

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ static enum es_result vc_read_mem(struct es_em_ctxt *ctxt,
115115
/* Include code for early handlers */
116116
#include "../../kernel/sev-shared.c"
117117

118-
static inline bool sev_snp_enabled(void)
118+
bool sev_snp_enabled(void)
119119
{
120120
return sev_status & MSR_AMD64_SEV_SNP_ENABLED;
121121
}
@@ -181,6 +181,58 @@ static bool early_setup_ghcb(void)
181181
return true;
182182
}
183183

184+
static phys_addr_t __snp_accept_memory(struct snp_psc_desc *desc,
185+
phys_addr_t pa, phys_addr_t pa_end)
186+
{
187+
struct psc_hdr *hdr;
188+
struct psc_entry *e;
189+
unsigned int i;
190+
191+
hdr = &desc->hdr;
192+
memset(hdr, 0, sizeof(*hdr));
193+
194+
e = desc->entries;
195+
196+
i = 0;
197+
while (pa < pa_end && i < VMGEXIT_PSC_MAX_ENTRY) {
198+
hdr->end_entry = i;
199+
200+
e->gfn = pa >> PAGE_SHIFT;
201+
e->operation = SNP_PAGE_STATE_PRIVATE;
202+
if (IS_ALIGNED(pa, PMD_SIZE) && (pa_end - pa) >= PMD_SIZE) {
203+
e->pagesize = RMP_PG_SIZE_2M;
204+
pa += PMD_SIZE;
205+
} else {
206+
e->pagesize = RMP_PG_SIZE_4K;
207+
pa += PAGE_SIZE;
208+
}
209+
210+
e++;
211+
i++;
212+
}
213+
214+
if (vmgexit_psc(boot_ghcb, desc))
215+
sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_PSC);
216+
217+
pvalidate_pages(desc);
218+
219+
return pa;
220+
}
221+
222+
void snp_accept_memory(phys_addr_t start, phys_addr_t end)
223+
{
224+
struct snp_psc_desc desc = {};
225+
unsigned int i;
226+
phys_addr_t pa;
227+
228+
if (!boot_ghcb && !early_setup_ghcb())
229+
sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_PSC);
230+
231+
pa = start;
232+
while (pa < end)
233+
pa = __snp_accept_memory(&desc, pa, end);
234+
}
235+
184236
void sev_es_shutdown_ghcb(void)
185237
{
186238
if (!boot_ghcb)

arch/x86/boot/compressed/sev.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
/*
3+
* AMD SEV header for early boot related functions.
4+
*
5+
* Author: Tom Lendacky <[email protected]>
6+
*/
7+
8+
#ifndef BOOT_COMPRESSED_SEV_H
9+
#define BOOT_COMPRESSED_SEV_H
10+
11+
#ifdef CONFIG_AMD_MEM_ENCRYPT
12+
13+
bool sev_snp_enabled(void);
14+
void snp_accept_memory(phys_addr_t start, phys_addr_t end);
15+
16+
#else
17+
18+
static inline bool sev_snp_enabled(void) { return false; }
19+
static inline void snp_accept_memory(phys_addr_t start, phys_addr_t end) { }
20+
21+
#endif
22+
23+
#endif

arch/x86/include/asm/sev.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ void snp_set_wakeup_secondary_cpu(void);
206206
bool snp_init(struct boot_params *bp);
207207
void __init __noreturn snp_abort(void);
208208
int snp_issue_guest_request(u64 exit_code, struct snp_req_data *input, struct snp_guest_request_ioctl *rio);
209+
void snp_accept_memory(phys_addr_t start, phys_addr_t end);
209210
#else
210211
static inline void sev_es_ist_enter(struct pt_regs *regs) { }
211212
static inline void sev_es_ist_exit(void) { }
@@ -229,6 +230,8 @@ static inline int snp_issue_guest_request(u64 exit_code, struct snp_req_data *in
229230
{
230231
return -ENOTTY;
231232
}
233+
234+
static inline void snp_accept_memory(phys_addr_t start, phys_addr_t end) { }
232235
#endif
233236

234237
#endif

arch/x86/include/asm/unaccepted_memory.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,16 @@
33

44
#include <linux/efi.h>
55
#include <asm/tdx.h>
6+
#include <asm/sev.h>
67

78
static inline void arch_accept_memory(phys_addr_t start, phys_addr_t end)
89
{
910
/* Platform-specific memory-acceptance call goes here */
1011
if (cpu_feature_enabled(X86_FEATURE_TDX_GUEST)) {
1112
if (!tdx_accept_memory(start, end))
1213
panic("TDX: Failed to accept memory\n");
14+
} else if (cc_platform_has(CC_ATTR_GUEST_SEV_SNP)) {
15+
snp_accept_memory(start, end);
1316
} else {
1417
panic("Cannot accept memory: unknown platform\n");
1518
}

arch/x86/kernel/sev-shared.c

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212
#ifndef __BOOT_COMPRESSED
1313
#define error(v) pr_err(v)
1414
#define has_cpuflag(f) boot_cpu_has(f)
15+
#else
16+
#undef WARN
17+
#define WARN(condition, format...) (!!(condition))
1518
#endif
1619

1720
/* I/O parameters for CPUID-related helpers */
@@ -991,3 +994,103 @@ static void __init setup_cpuid_table(const struct cc_blob_sev_info *cc_info)
991994
cpuid_ext_range_max = fn->eax;
992995
}
993996
}
997+
998+
static void pvalidate_pages(struct snp_psc_desc *desc)
999+
{
1000+
struct psc_entry *e;
1001+
unsigned long vaddr;
1002+
unsigned int size;
1003+
unsigned int i;
1004+
bool validate;
1005+
int rc;
1006+
1007+
for (i = 0; i <= desc->hdr.end_entry; i++) {
1008+
e = &desc->entries[i];
1009+
1010+
vaddr = (unsigned long)pfn_to_kaddr(e->gfn);
1011+
size = e->pagesize ? RMP_PG_SIZE_2M : RMP_PG_SIZE_4K;
1012+
validate = e->operation == SNP_PAGE_STATE_PRIVATE;
1013+
1014+
rc = pvalidate(vaddr, size, validate);
1015+
if (rc == PVALIDATE_FAIL_SIZEMISMATCH && size == RMP_PG_SIZE_2M) {
1016+
unsigned long vaddr_end = vaddr + PMD_SIZE;
1017+
1018+
for (; vaddr < vaddr_end; vaddr += PAGE_SIZE) {
1019+
rc = pvalidate(vaddr, RMP_PG_SIZE_4K, validate);
1020+
if (rc)
1021+
break;
1022+
}
1023+
}
1024+
1025+
if (rc) {
1026+
WARN(1, "Failed to validate address 0x%lx ret %d", vaddr, rc);
1027+
sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_PVALIDATE);
1028+
}
1029+
}
1030+
}
1031+
1032+
static int vmgexit_psc(struct ghcb *ghcb, struct snp_psc_desc *desc)
1033+
{
1034+
int cur_entry, end_entry, ret = 0;
1035+
struct snp_psc_desc *data;
1036+
struct es_em_ctxt ctxt;
1037+
1038+
vc_ghcb_invalidate(ghcb);
1039+
1040+
/* Copy the input desc into GHCB shared buffer */
1041+
data = (struct snp_psc_desc *)ghcb->shared_buffer;
1042+
memcpy(ghcb->shared_buffer, desc, min_t(int, GHCB_SHARED_BUF_SIZE, sizeof(*desc)));
1043+
1044+
/*
1045+
* As per the GHCB specification, the hypervisor can resume the guest
1046+
* before processing all the entries. Check whether all the entries
1047+
* are processed. If not, then keep retrying. Note, the hypervisor
1048+
* will update the data memory directly to indicate the status, so
1049+
* reference the data->hdr everywhere.
1050+
*
1051+
* The strategy here is to wait for the hypervisor to change the page
1052+
* state in the RMP table before guest accesses the memory pages. If the
1053+
* page state change was not successful, then later memory access will
1054+
* result in a crash.
1055+
*/
1056+
cur_entry = data->hdr.cur_entry;
1057+
end_entry = data->hdr.end_entry;
1058+
1059+
while (data->hdr.cur_entry <= data->hdr.end_entry) {
1060+
ghcb_set_sw_scratch(ghcb, (u64)__pa(data));
1061+
1062+
/* This will advance the shared buffer data points to. */
1063+
ret = sev_es_ghcb_hv_call(ghcb, &ctxt, SVM_VMGEXIT_PSC, 0, 0);
1064+
1065+
/*
1066+
* Page State Change VMGEXIT can pass error code through
1067+
* exit_info_2.
1068+
*/
1069+
if (WARN(ret || ghcb->save.sw_exit_info_2,
1070+
"SNP: PSC failed ret=%d exit_info_2=%llx\n",
1071+
ret, ghcb->save.sw_exit_info_2)) {
1072+
ret = 1;
1073+
goto out;
1074+
}
1075+
1076+
/* Verify that reserved bit is not set */
1077+
if (WARN(data->hdr.reserved, "Reserved bit is set in the PSC header\n")) {
1078+
ret = 1;
1079+
goto out;
1080+
}
1081+
1082+
/*
1083+
* Sanity check that entry processing is not going backwards.
1084+
* This will happen only if hypervisor is tricking us.
1085+
*/
1086+
if (WARN(data->hdr.end_entry > end_entry || cur_entry > data->hdr.cur_entry,
1087+
"SNP: PSC processing going backward, end_entry %d (got %d) cur_entry %d (got %d)\n",
1088+
end_entry, data->hdr.end_entry, cur_entry, data->hdr.cur_entry)) {
1089+
ret = 1;
1090+
goto out;
1091+
}
1092+
}
1093+
1094+
out:
1095+
return ret;
1096+
}

0 commit comments

Comments
 (0)