Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

x86/sev: Use kernel provided SVSM Calling Areas

The SVSM Calling Area (CA) is used to communicate between Linux and the
SVSM. Since the firmware supplied CA for the BSP is likely to be in
reserved memory, switch off that CA to a kernel provided CA so that access
and use of the CA is available during boot. The CA switch is done using
the SVSM core protocol SVSM_CORE_REMAP_CA call.

An SVSM call is executed by filling out the SVSM CA and setting the proper
register state as documented by the SVSM protocol. The SVSM is invoked by
by requesting the hypervisor to run VMPL0.

Once it is safe to allocate/reserve memory, allocate a CA for each CPU.
After allocating the new CAs, the BSP will switch from the boot CA to the
per-CPU CA. The CA for an AP is identified to the SVSM when creating the
VMSA in preparation for booting the AP.

[ bp: Heavily simplify svsm_issue_call() asm, other touchups. ]

Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de>
Link: https://lore.kernel.org/r/fa8021130bcc3bcf14d722a25548cb0cdf325456.1717600736.git.thomas.lendacky@amd.com

authored by

Tom Lendacky and committed by
Borislav Petkov (AMD)
34ff6590 878e70db

+362 -39
+13
arch/x86/include/asm/sev-common.h
··· 98 98 /* GHCBData[63:32] */ \ 99 99 (((u64)(val) & GENMASK_ULL(63, 32)) >> 32) 100 100 101 + /* GHCB Run at VMPL Request/Response */ 102 + #define GHCB_MSR_VMPL_REQ 0x016 103 + #define GHCB_MSR_VMPL_REQ_LEVEL(v) \ 104 + /* GHCBData[39:32] */ \ 105 + (((u64)(v) & GENMASK_ULL(7, 0) << 32) | \ 106 + /* GHCBDdata[11:0] */ \ 107 + GHCB_MSR_VMPL_REQ) 108 + 109 + #define GHCB_MSR_VMPL_RESP 0x017 110 + #define GHCB_MSR_VMPL_RESP_VAL(v) \ 111 + /* GHCBData[63:32] */ \ 112 + (((u64)(v) & GENMASK_ULL(63, 32)) >> 32) 113 + 101 114 /* GHCB Hypervisor Feature Request/Response */ 102 115 #define GHCB_MSR_HV_FT_REQ 0x080 103 116 #define GHCB_MSR_HV_FT_RESP 0x081
+32
arch/x86/include/asm/sev.h
··· 178 178 u8 svsm_buffer[PAGE_SIZE - 8]; 179 179 }; 180 180 181 + #define SVSM_SUCCESS 0 182 + #define SVSM_ERR_INCOMPLETE 0x80000000 183 + #define SVSM_ERR_UNSUPPORTED_PROTOCOL 0x80000001 184 + #define SVSM_ERR_UNSUPPORTED_CALL 0x80000002 185 + #define SVSM_ERR_INVALID_ADDRESS 0x80000003 186 + #define SVSM_ERR_INVALID_FORMAT 0x80000004 187 + #define SVSM_ERR_INVALID_PARAMETER 0x80000005 188 + #define SVSM_ERR_INVALID_REQUEST 0x80000006 189 + #define SVSM_ERR_BUSY 0x80000007 190 + 191 + /* 192 + * SVSM protocol structure 193 + */ 194 + struct svsm_call { 195 + struct svsm_ca *caa; 196 + u64 rax; 197 + u64 rcx; 198 + u64 rdx; 199 + u64 r8; 200 + u64 r9; 201 + u64 rax_out; 202 + u64 rcx_out; 203 + u64 rdx_out; 204 + u64 r8_out; 205 + u64 r9_out; 206 + }; 207 + 208 + #define SVSM_CORE_CALL(x) ((0ULL << 32) | (x)) 209 + #define SVSM_CORE_REMAP_CA 0 210 + 181 211 #ifdef CONFIG_AMD_MEM_ENCRYPT 182 212 extern void __sev_es_ist_enter(struct pt_regs *regs); 183 213 extern void __sev_es_ist_exit(void); ··· 290 260 u64 snp_get_unsupported_features(u64 status); 291 261 u64 sev_get_status(void); 292 262 void sev_show_status(void); 263 + void snp_update_svsm_ca(void); 293 264 #else 294 265 static inline void sev_es_ist_enter(struct pt_regs *regs) { } 295 266 static inline void sev_es_ist_exit(void) { } ··· 320 289 static inline u64 snp_get_unsupported_features(u64 status) { return 0; } 321 290 static inline u64 sev_get_status(void) { return 0; } 322 291 static inline void sev_show_status(void) { } 292 + static inline void snp_update_svsm_ca(void) { } 323 293 #endif 324 294 325 295 #ifdef CONFIG_KVM_AMD_SEV
+1
arch/x86/include/uapi/asm/svm.h
··· 115 115 #define SVM_VMGEXIT_AP_CREATE_ON_INIT 0 116 116 #define SVM_VMGEXIT_AP_CREATE 1 117 117 #define SVM_VMGEXIT_AP_DESTROY 2 118 + #define SVM_VMGEXIT_SNP_RUN_VMPL 0x80000018 118 119 #define SVM_VMGEXIT_HV_FEATURES 0x8000fffd 119 120 #define SVM_VMGEXIT_TERM_REQUEST 0x8000fffe 120 121 #define SVM_VMGEXIT_TERM_REASON(reason_set, reason_code) \
+126 -2
arch/x86/kernel/sev-shared.c
··· 21 21 #define WARN(condition, format...) (!!(condition)) 22 22 #define sev_printk(fmt, ...) 23 23 #define sev_printk_rtl(fmt, ...) 24 + #undef vc_forward_exception 25 + #define vc_forward_exception(c) panic("SNP: Hypervisor requested exception\n") 24 26 #endif 25 27 26 28 /* ··· 244 242 } 245 243 246 244 return ES_VMM_ERROR; 245 + } 246 + 247 + static inline int svsm_process_result_codes(struct svsm_call *call) 248 + { 249 + switch (call->rax_out) { 250 + case SVSM_SUCCESS: 251 + return 0; 252 + case SVSM_ERR_INCOMPLETE: 253 + case SVSM_ERR_BUSY: 254 + return -EAGAIN; 255 + default: 256 + return -EINVAL; 257 + } 258 + } 259 + 260 + /* 261 + * Issue a VMGEXIT to call the SVSM: 262 + * - Load the SVSM register state (RAX, RCX, RDX, R8 and R9) 263 + * - Set the CA call pending field to 1 264 + * - Issue VMGEXIT 265 + * - Save the SVSM return register state (RAX, RCX, RDX, R8 and R9) 266 + * - Perform atomic exchange of the CA call pending field 267 + * 268 + * - See the "Secure VM Service Module for SEV-SNP Guests" specification for 269 + * details on the calling convention. 270 + * - The calling convention loosely follows the Microsoft X64 calling 271 + * convention by putting arguments in RCX, RDX, R8 and R9. 272 + * - RAX specifies the SVSM protocol/callid as input and the return code 273 + * as output. 274 + */ 275 + static __always_inline void svsm_issue_call(struct svsm_call *call, u8 *pending) 276 + { 277 + register unsigned long rax asm("rax") = call->rax; 278 + register unsigned long rcx asm("rcx") = call->rcx; 279 + register unsigned long rdx asm("rdx") = call->rdx; 280 + register unsigned long r8 asm("r8") = call->r8; 281 + register unsigned long r9 asm("r9") = call->r9; 282 + 283 + call->caa->call_pending = 1; 284 + 285 + asm volatile("rep; vmmcall\n\t" 286 + : "+r" (rax), "+r" (rcx), "+r" (rdx), "+r" (r8), "+r" (r9) 287 + : : "memory"); 288 + 289 + *pending = xchg(&call->caa->call_pending, *pending); 290 + 291 + call->rax_out = rax; 292 + call->rcx_out = rcx; 293 + call->rdx_out = rdx; 294 + call->r8_out = r8; 295 + call->r9_out = r9; 296 + } 297 + 298 + static int svsm_perform_msr_protocol(struct svsm_call *call) 299 + { 300 + u8 pending = 0; 301 + u64 val, resp; 302 + 303 + /* 304 + * When using the MSR protocol, be sure to save and restore 305 + * the current MSR value. 306 + */ 307 + val = sev_es_rd_ghcb_msr(); 308 + 309 + sev_es_wr_ghcb_msr(GHCB_MSR_VMPL_REQ_LEVEL(0)); 310 + 311 + svsm_issue_call(call, &pending); 312 + 313 + resp = sev_es_rd_ghcb_msr(); 314 + 315 + sev_es_wr_ghcb_msr(val); 316 + 317 + if (pending) 318 + return -EINVAL; 319 + 320 + if (GHCB_RESP_CODE(resp) != GHCB_MSR_VMPL_RESP) 321 + return -EINVAL; 322 + 323 + if (GHCB_MSR_VMPL_RESP_VAL(resp)) 324 + return -EINVAL; 325 + 326 + return svsm_process_result_codes(call); 327 + } 328 + 329 + static int svsm_perform_ghcb_protocol(struct ghcb *ghcb, struct svsm_call *call) 330 + { 331 + struct es_em_ctxt ctxt; 332 + u8 pending = 0; 333 + 334 + vc_ghcb_invalidate(ghcb); 335 + 336 + /* 337 + * Fill in protocol and format specifiers. This can be called very early 338 + * in the boot, so use rip-relative references as needed. 339 + */ 340 + ghcb->protocol_version = RIP_REL_REF(ghcb_version); 341 + ghcb->ghcb_usage = GHCB_DEFAULT_USAGE; 342 + 343 + ghcb_set_sw_exit_code(ghcb, SVM_VMGEXIT_SNP_RUN_VMPL); 344 + ghcb_set_sw_exit_info_1(ghcb, 0); 345 + ghcb_set_sw_exit_info_2(ghcb, 0); 346 + 347 + sev_es_wr_ghcb_msr(__pa(ghcb)); 348 + 349 + svsm_issue_call(call, &pending); 350 + 351 + if (pending) 352 + return -EINVAL; 353 + 354 + switch (verify_exception_info(ghcb, &ctxt)) { 355 + case ES_OK: 356 + break; 357 + case ES_EXCEPTION: 358 + vc_forward_exception(&ctxt); 359 + fallthrough; 360 + default: 361 + return -EINVAL; 362 + } 363 + 364 + return svsm_process_result_codes(call); 247 365 } 248 366 249 367 static enum es_result sev_es_ghcb_hv_call(struct ghcb *ghcb, ··· 1411 1289 * Maintain the GPA of the SVSM Calling Area (CA) in order to utilize the SVSM 1412 1290 * services needed when not running in VMPL0. 1413 1291 */ 1414 - static void __head svsm_setup_ca(const struct cc_blob_sev_info *cc_info) 1292 + static bool __head svsm_setup_ca(const struct cc_blob_sev_info *cc_info) 1415 1293 { 1416 1294 struct snp_secrets_page *secrets_page; 1417 1295 u64 caa; ··· 1433 1311 * code and the early kernel code. 1434 1312 */ 1435 1313 if (!rmpadjust((unsigned long)&RIP_REL_REF(boot_ghcb_page), RMP_PG_SIZE_4K, 1)) 1436 - return; 1314 + return false; 1437 1315 1438 1316 /* 1439 1317 * Not running at VMPL0, ensure everything has been properly supplied ··· 1466 1344 */ 1467 1345 RIP_REL_REF(boot_svsm_caa) = (struct svsm_ca *)caa; 1468 1346 RIP_REL_REF(boot_svsm_caa_pa) = caa; 1347 + 1348 + return true; 1469 1349 }
+183 -36
arch/x86/kernel/sev.c
··· 133 133 struct ghcb *ghcb; 134 134 }; 135 135 136 + /* For early boot SVSM communication */ 137 + static struct svsm_ca boot_svsm_ca_page __aligned(PAGE_SIZE); 138 + 136 139 static DEFINE_PER_CPU(struct sev_es_runtime_data*, runtime_data); 137 140 static DEFINE_PER_CPU(struct sev_es_save_area *, sev_vmsa); 141 + static DEFINE_PER_CPU(struct svsm_ca *, svsm_caa); 142 + static DEFINE_PER_CPU(u64, svsm_caa_pa); 138 143 139 144 struct sev_config { 140 145 __u64 debug : 1, 141 146 142 147 /* 143 - * A flag used by __set_pages_state() that indicates when the 144 - * per-CPU GHCB has been created and registered and thus can be 145 - * used by the BSP instead of the early boot GHCB. 148 + * Indicates when the per-CPU GHCB has been created and registered 149 + * and thus can be used by the BSP instead of the early boot GHCB. 146 150 * 147 151 * For APs, the per-CPU GHCB is created before they are started 148 152 * and registered upon startup, so this flag can be used globally 149 153 * for the BSP and APs. 150 154 */ 151 155 ghcbs_initialized : 1, 156 + 157 + /* 158 + * Indicates when the per-CPU SVSM CA is to be used instead of the 159 + * boot SVSM CA. 160 + * 161 + * For APs, the per-CPU SVSM CA is created as part of the AP 162 + * bringup, so this flag can be used globally for the BSP and APs. 163 + */ 164 + use_cas : 1, 152 165 153 166 __reserved : 62; 154 167 }; ··· 585 572 return ES_EXCEPTION; 586 573 } 587 574 575 + static __always_inline void vc_forward_exception(struct es_em_ctxt *ctxt) 576 + { 577 + long error_code = ctxt->fi.error_code; 578 + int trapnr = ctxt->fi.vector; 579 + 580 + ctxt->regs->orig_ax = ctxt->fi.error_code; 581 + 582 + switch (trapnr) { 583 + case X86_TRAP_GP: 584 + exc_general_protection(ctxt->regs, error_code); 585 + break; 586 + case X86_TRAP_UD: 587 + exc_invalid_op(ctxt->regs); 588 + break; 589 + case X86_TRAP_PF: 590 + write_cr2(ctxt->fi.cr2); 591 + exc_page_fault(ctxt->regs, error_code); 592 + break; 593 + case X86_TRAP_AC: 594 + exc_alignment_check(ctxt->regs, error_code); 595 + break; 596 + default: 597 + pr_emerg("Unsupported exception in #VC instruction emulation - can't continue\n"); 598 + BUG(); 599 + } 600 + } 601 + 588 602 /* Include code shared with pre-decompression boot stage */ 589 603 #include "sev-shared.c" 604 + 605 + static inline struct svsm_ca *svsm_get_caa(void) 606 + { 607 + /* 608 + * Use rIP-relative references when called early in the boot. If 609 + * ->use_cas is set, then it is late in the boot and no need 610 + * to worry about rIP-relative references. 611 + */ 612 + if (RIP_REL_REF(sev_cfg).use_cas) 613 + return this_cpu_read(svsm_caa); 614 + else 615 + return RIP_REL_REF(boot_svsm_caa); 616 + } 590 617 591 618 static noinstr void __sev_put_ghcb(struct ghcb_state *state) 592 619 { ··· 651 598 vc_ghcb_invalidate(ghcb); 652 599 data->ghcb_active = false; 653 600 } 601 + } 602 + 603 + static int svsm_perform_call_protocol(struct svsm_call *call) 604 + { 605 + struct ghcb_state state; 606 + unsigned long flags; 607 + struct ghcb *ghcb; 608 + int ret; 609 + 610 + /* 611 + * This can be called very early in the boot, use native functions in 612 + * order to avoid paravirt issues. 613 + */ 614 + flags = native_local_irq_save(); 615 + 616 + /* 617 + * Use rip-relative references when called early in the boot. If 618 + * ghcbs_initialized is set, then it is late in the boot and no need 619 + * to worry about rip-relative references in called functions. 620 + */ 621 + if (RIP_REL_REF(sev_cfg).ghcbs_initialized) 622 + ghcb = __sev_get_ghcb(&state); 623 + else if (RIP_REL_REF(boot_ghcb)) 624 + ghcb = RIP_REL_REF(boot_ghcb); 625 + else 626 + ghcb = NULL; 627 + 628 + do { 629 + ret = ghcb ? svsm_perform_ghcb_protocol(ghcb, call) 630 + : svsm_perform_msr_protocol(call); 631 + } while (ret == -EAGAIN); 632 + 633 + if (RIP_REL_REF(sev_cfg).ghcbs_initialized) 634 + __sev_put_ghcb(&state); 635 + 636 + native_local_irq_restore(flags); 637 + 638 + return ret; 654 639 } 655 640 656 641 void noinstr __sev_es_nmi_complete(void) ··· 1437 1346 panic("Can't allocate SEV-ES runtime data"); 1438 1347 1439 1348 per_cpu(runtime_data, cpu) = data; 1349 + 1350 + if (snp_vmpl) { 1351 + struct svsm_ca *caa; 1352 + 1353 + /* Allocate the SVSM CA page if an SVSM is present */ 1354 + caa = memblock_alloc(sizeof(*caa), PAGE_SIZE); 1355 + if (!caa) 1356 + panic("Can't allocate SVSM CA page\n"); 1357 + 1358 + per_cpu(svsm_caa, cpu) = caa; 1359 + per_cpu(svsm_caa_pa, cpu) = __pa(caa); 1360 + } 1440 1361 } 1441 1362 1442 1363 static void __init init_ghcb(int cpu) ··· 1496 1393 for_each_possible_cpu(cpu) { 1497 1394 alloc_runtime_data(cpu); 1498 1395 init_ghcb(cpu); 1396 + } 1397 + 1398 + /* If running under an SVSM, switch to the per-cpu CA */ 1399 + if (snp_vmpl) { 1400 + struct svsm_call call = {}; 1401 + unsigned long flags; 1402 + int ret; 1403 + 1404 + local_irq_save(flags); 1405 + 1406 + /* 1407 + * SVSM_CORE_REMAP_CA call: 1408 + * RAX = 0 (Protocol=0, CallID=0) 1409 + * RCX = New CA GPA 1410 + */ 1411 + call.caa = svsm_get_caa(); 1412 + call.rax = SVSM_CORE_CALL(SVSM_CORE_REMAP_CA); 1413 + call.rcx = this_cpu_read(svsm_caa_pa); 1414 + ret = svsm_perform_call_protocol(&call); 1415 + if (ret) 1416 + panic("Can't remap the SVSM CA, ret=%d, rax_out=0x%llx\n", 1417 + ret, call.rax_out); 1418 + 1419 + sev_cfg.use_cas = true; 1420 + 1421 + local_irq_restore(flags); 1499 1422 } 1500 1423 1501 1424 sev_es_setup_play_dead(); ··· 1948 1819 return result; 1949 1820 } 1950 1821 1951 - static __always_inline void vc_forward_exception(struct es_em_ctxt *ctxt) 1952 - { 1953 - long error_code = ctxt->fi.error_code; 1954 - int trapnr = ctxt->fi.vector; 1955 - 1956 - ctxt->regs->orig_ax = ctxt->fi.error_code; 1957 - 1958 - switch (trapnr) { 1959 - case X86_TRAP_GP: 1960 - exc_general_protection(ctxt->regs, error_code); 1961 - break; 1962 - case X86_TRAP_UD: 1963 - exc_invalid_op(ctxt->regs); 1964 - break; 1965 - case X86_TRAP_PF: 1966 - write_cr2(ctxt->fi.cr2); 1967 - exc_page_fault(ctxt->regs, error_code); 1968 - break; 1969 - case X86_TRAP_AC: 1970 - exc_alignment_check(ctxt->regs, error_code); 1971 - break; 1972 - default: 1973 - pr_emerg("Unsupported exception in #VC instruction emulation - can't continue\n"); 1974 - BUG(); 1975 - } 1976 - } 1977 - 1978 1822 static __always_inline bool is_vc2_stack(unsigned long sp) 1979 1823 { 1980 1824 return (sp >= __this_cpu_ist_bottom_va(VC2) && sp < __this_cpu_ist_top_va(VC2)); ··· 2197 2095 return cc_info; 2198 2096 } 2199 2097 2098 + static __head void svsm_setup(struct cc_blob_sev_info *cc_info) 2099 + { 2100 + struct svsm_call call = {}; 2101 + int ret; 2102 + u64 pa; 2103 + 2104 + /* 2105 + * Record the SVSM Calling Area address (CAA) if the guest is not 2106 + * running at VMPL0. The CA will be used to communicate with the 2107 + * SVSM to perform the SVSM services. 2108 + */ 2109 + if (!svsm_setup_ca(cc_info)) 2110 + return; 2111 + 2112 + /* 2113 + * It is very early in the boot and the kernel is running identity 2114 + * mapped but without having adjusted the pagetables to where the 2115 + * kernel was loaded (physbase), so the get the CA address using 2116 + * RIP-relative addressing. 2117 + */ 2118 + pa = (u64)&RIP_REL_REF(boot_svsm_ca_page); 2119 + 2120 + /* 2121 + * Switch over to the boot SVSM CA while the current CA is still 2122 + * addressable. There is no GHCB at this point so use the MSR protocol. 2123 + * 2124 + * SVSM_CORE_REMAP_CA call: 2125 + * RAX = 0 (Protocol=0, CallID=0) 2126 + * RCX = New CA GPA 2127 + */ 2128 + call.caa = svsm_get_caa(); 2129 + call.rax = SVSM_CORE_CALL(SVSM_CORE_REMAP_CA); 2130 + call.rcx = pa; 2131 + ret = svsm_perform_call_protocol(&call); 2132 + if (ret) 2133 + panic("Can't remap the SVSM CA, ret=%d, rax_out=0x%llx\n", ret, call.rax_out); 2134 + 2135 + RIP_REL_REF(boot_svsm_caa) = (struct svsm_ca *)pa; 2136 + RIP_REL_REF(boot_svsm_caa_pa) = pa; 2137 + } 2138 + 2200 2139 bool __head snp_init(struct boot_params *bp) 2201 2140 { 2202 2141 struct cc_blob_sev_info *cc_info; ··· 2251 2108 2252 2109 setup_cpuid_table(cc_info); 2253 2110 2254 - /* 2255 - * Record the SVSM Calling Area address (CAA) if the guest is not 2256 - * running at VMPL0. The CA will be used to communicate with the 2257 - * SVSM to perform the SVSM services. 2258 - */ 2259 - svsm_setup_ca(cc_info); 2111 + svsm_setup(cc_info); 2260 2112 2261 2113 /* 2262 2114 * The CC blob will be used later to access the secrets page. Cache ··· 2443 2305 } 2444 2306 } 2445 2307 pr_cont("\n"); 2308 + } 2309 + 2310 + void __init snp_update_svsm_ca(void) 2311 + { 2312 + if (!snp_vmpl) 2313 + return; 2314 + 2315 + /* Update the CAA to a proper kernel address */ 2316 + boot_svsm_caa = &boot_svsm_ca_page; 2446 2317 }
+7 -1
arch/x86/mm/mem_encrypt_amd.c
··· 2 2 /* 3 3 * AMD Memory Encryption Support 4 4 * 5 - * Copyright (C) 2016 Advanced Micro Devices, Inc. 5 + * Copyright (C) 2016-2024 Advanced Micro Devices, Inc. 6 6 * 7 7 * Author: Tom Lendacky <thomas.lendacky@amd.com> 8 8 */ ··· 510 510 */ 511 511 x86_init.resources.dmi_setup = snp_dmi_setup; 512 512 } 513 + 514 + /* 515 + * Switch the SVSM CA mapping (if active) from identity mapped to 516 + * kernel mapped. 517 + */ 518 + snp_update_svsm_ca(); 513 519 } 514 520 515 521 void __init mem_encrypt_free_decrypted_mem(void)