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

x86/sev: Move GHCB page based HV communication out of startup code

Both the decompressor and the core kernel implement an early #VC handler,
which only deals with CPUID instructions, and full featured one, which can
handle any #VC exception.

The former communicates with the hypervisor using the MSR based protocol,
whereas the latter uses a shared GHCB page, which is configured a bit later
during the boot, when the kernel runs from its ordinary virtual mapping,
rather than the 1:1 mapping that the startup code uses.

Accessing this shared GHCB page from the core kernel's startup code is
problematic, because it involves converting the GHCB address provided by the
caller to a physical address. In the startup code, virtual to physical address
translations are problematic, given that the virtual address might be a 1:1
mapped address, and such translations should therefore be avoided.

This means that exposing startup code dealing with the GHCB to callers that
execute from the ordinary kernel virtual mapping should be avoided too. So
move all GHCB page based communication out of the startup code, now that all
communication occurring before the kernel virtual mapping is up relies on the
MSR protocol only.

As an exception, add a flag representing the need to apply the coherency
fix in order to avoid exporting CPUID* helpers because of the code
running too early for the *cpu_has* infrastructure.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de>
Link: https://lore.kernel.org/20250828102202.1849035-29-ardb+git@google.com

authored by

Ard Biesheuvel and committed by
Borislav Petkov (AMD)
37dbd78f e349241b

+196 -201
+3
arch/x86/boot/compressed/sev-handle-vc.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0 2 2 3 3 #include "misc.h" 4 + #include "error.h" 4 5 #include "sev.h" 5 6 6 7 #include <linux/kernel.h> ··· 15 14 #include <asm/fpu/xcr.h> 16 15 17 16 #define __BOOT_COMPRESSED 17 + #undef __init 18 + #define __init 18 19 19 20 /* Basic instruction decoding support needed */ 20 21 #include "../../lib/inat.c"
+2
arch/x86/boot/compressed/sev.c
··· 371 371 if (!(eax & BIT(1))) 372 372 return -ENODEV; 373 373 374 + sev_snp_needs_sfw = !(ebx & BIT(31)); 375 + 374 376 return ebx & 0x3f; 375 377 } 376 378
-13
arch/x86/boot/cpuflags.c
··· 106 106 cpuid(0x80000001, &ignored, &ignored, &cpu.flags[6], 107 107 &cpu.flags[1]); 108 108 } 109 - 110 - if (max_amd_level >= 0x8000001f) { 111 - u32 ebx; 112 - 113 - /* 114 - * The X86_FEATURE_COHERENCY_SFW_NO feature bit is in 115 - * the virtualization flags entry (word 8) and set by 116 - * scattered.c, so the bit needs to be explicitly set. 117 - */ 118 - cpuid(0x8000001f, &ignored, &ebx, &ignored, &ignored); 119 - if (ebx & BIT(31)) 120 - set_bit(X86_FEATURE_COHERENCY_SFW_NO, cpu.flags); 121 - } 122 109 } 123 110 }
+6 -139
arch/x86/boot/startup/sev-shared.c
··· 13 13 14 14 #ifndef __BOOT_COMPRESSED 15 15 #define error(v) pr_err(v) 16 - #define has_cpuflag(f) boot_cpu_has(f) 17 16 #else 18 17 #undef WARN 19 18 #define WARN(condition, format...) (!!(condition)) 20 - #undef vc_forward_exception 21 - #define vc_forward_exception(c) panic("SNP: Hypervisor requested exception\n") 22 19 #endif 23 20 24 21 /* ··· 36 39 * 37 40 * GHCB protocol version negotiated with the hypervisor. 38 41 */ 39 - static u16 ghcb_version __ro_after_init; 42 + u16 ghcb_version __ro_after_init; 40 43 41 44 /* Copy of the SNP firmware's CPUID page. */ 42 45 static struct snp_cpuid_table cpuid_table_copy __ro_after_init; ··· 51 54 static u32 cpuid_hyp_range_max __ro_after_init; 52 55 static u32 cpuid_ext_range_max __ro_after_init; 53 56 54 - bool __init sev_es_check_cpu_features(void) 55 - { 56 - if (!has_cpuflag(X86_FEATURE_RDRAND)) { 57 - error("RDRAND instruction not supported - no trusted source of randomness available\n"); 58 - return false; 59 - } 60 - 61 - return true; 62 - } 57 + bool sev_snp_needs_sfw; 63 58 64 59 void __head __noreturn 65 60 sev_es_terminate(unsigned int set, unsigned int reason) ··· 89 100 return GHCB_MSR_HV_FT_RESP_VAL(val); 90 101 } 91 102 92 - void snp_register_ghcb_early(unsigned long paddr) 93 - { 94 - unsigned long pfn = paddr >> PAGE_SHIFT; 95 - u64 val; 96 - 97 - sev_es_wr_ghcb_msr(GHCB_MSR_REG_GPA_REQ_VAL(pfn)); 98 - VMGEXIT(); 99 - 100 - val = sev_es_rd_ghcb_msr(); 101 - 102 - /* If the response GPA is not ours then abort the guest */ 103 - if ((GHCB_RESP_CODE(val) != GHCB_MSR_REG_GPA_RESP) || 104 - (GHCB_MSR_REG_GPA_RESP_VAL(val) != pfn)) 105 - sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_REGISTER); 106 - } 107 - 108 - bool sev_es_negotiate_protocol(void) 109 - { 110 - u64 val; 111 - 112 - /* Do the GHCB protocol version negotiation */ 113 - sev_es_wr_ghcb_msr(GHCB_MSR_SEV_INFO_REQ); 114 - VMGEXIT(); 115 - val = sev_es_rd_ghcb_msr(); 116 - 117 - if (GHCB_MSR_INFO(val) != GHCB_MSR_SEV_INFO_RESP) 118 - return false; 119 - 120 - if (GHCB_MSR_PROTO_MAX(val) < GHCB_PROTOCOL_MIN || 121 - GHCB_MSR_PROTO_MIN(val) > GHCB_PROTOCOL_MAX) 122 - return false; 123 - 124 - ghcb_version = min_t(size_t, GHCB_MSR_PROTO_MAX(val), GHCB_PROTOCOL_MAX); 125 - 126 - return true; 127 - } 128 - 129 - static enum es_result verify_exception_info(struct ghcb *ghcb, struct es_em_ctxt *ctxt) 130 - { 131 - u32 ret; 132 - 133 - ret = ghcb->save.sw_exit_info_1 & GENMASK_ULL(31, 0); 134 - if (!ret) 135 - return ES_OK; 136 - 137 - if (ret == 1) { 138 - u64 info = ghcb->save.sw_exit_info_2; 139 - unsigned long v = info & SVM_EVTINJ_VEC_MASK; 140 - 141 - /* Check if exception information from hypervisor is sane. */ 142 - if ((info & SVM_EVTINJ_VALID) && 143 - ((v == X86_TRAP_GP) || (v == X86_TRAP_UD)) && 144 - ((info & SVM_EVTINJ_TYPE_MASK) == SVM_EVTINJ_TYPE_EXEPT)) { 145 - ctxt->fi.vector = v; 146 - 147 - if (info & SVM_EVTINJ_VALID_ERR) 148 - ctxt->fi.error_code = info >> 32; 149 - 150 - return ES_EXCEPTION; 151 - } 152 - } 153 - 154 - return ES_VMM_ERROR; 155 - } 156 - 157 - static inline int svsm_process_result_codes(struct svsm_call *call) 103 + int svsm_process_result_codes(struct svsm_call *call) 158 104 { 159 105 switch (call->rax_out) { 160 106 case SVSM_SUCCESS: ··· 117 193 * - RAX specifies the SVSM protocol/callid as input and the return code 118 194 * as output. 119 195 */ 120 - static __always_inline void svsm_issue_call(struct svsm_call *call, u8 *pending) 196 + void svsm_issue_call(struct svsm_call *call, u8 *pending) 121 197 { 122 198 register unsigned long rax asm("rax") = call->rax; 123 199 register unsigned long rcx asm("rcx") = call->rcx; ··· 140 216 call->r9_out = r9; 141 217 } 142 218 143 - static int svsm_perform_msr_protocol(struct svsm_call *call) 219 + int svsm_perform_msr_protocol(struct svsm_call *call) 144 220 { 145 221 u8 pending = 0; 146 222 u64 val, resp; ··· 169 245 return -EINVAL; 170 246 171 247 return svsm_process_result_codes(call); 172 - } 173 - 174 - static int svsm_perform_ghcb_protocol(struct ghcb *ghcb, struct svsm_call *call) 175 - { 176 - struct es_em_ctxt ctxt; 177 - u8 pending = 0; 178 - 179 - vc_ghcb_invalidate(ghcb); 180 - 181 - /* 182 - * Fill in protocol and format specifiers. This can be called very early 183 - * in the boot, so use rip-relative references as needed. 184 - */ 185 - ghcb->protocol_version = ghcb_version; 186 - ghcb->ghcb_usage = GHCB_DEFAULT_USAGE; 187 - 188 - ghcb_set_sw_exit_code(ghcb, SVM_VMGEXIT_SNP_RUN_VMPL); 189 - ghcb_set_sw_exit_info_1(ghcb, 0); 190 - ghcb_set_sw_exit_info_2(ghcb, 0); 191 - 192 - sev_es_wr_ghcb_msr(__pa(ghcb)); 193 - 194 - svsm_issue_call(call, &pending); 195 - 196 - if (pending) 197 - return -EINVAL; 198 - 199 - switch (verify_exception_info(ghcb, &ctxt)) { 200 - case ES_OK: 201 - break; 202 - case ES_EXCEPTION: 203 - vc_forward_exception(&ctxt); 204 - fallthrough; 205 - default: 206 - return -EINVAL; 207 - } 208 - 209 - return svsm_process_result_codes(call); 210 - } 211 - 212 - enum es_result sev_es_ghcb_hv_call(struct ghcb *ghcb, 213 - struct es_em_ctxt *ctxt, 214 - u64 exit_code, u64 exit_info_1, 215 - u64 exit_info_2) 216 - { 217 - /* Fill in protocol and format specifiers */ 218 - ghcb->protocol_version = ghcb_version; 219 - ghcb->ghcb_usage = GHCB_DEFAULT_USAGE; 220 - 221 - ghcb_set_sw_exit_code(ghcb, exit_code); 222 - ghcb_set_sw_exit_info_1(ghcb, exit_info_1); 223 - ghcb_set_sw_exit_info_2(ghcb, exit_info_2); 224 - 225 - sev_es_wr_ghcb_msr(__pa(ghcb)); 226 - VMGEXIT(); 227 - 228 - return verify_exception_info(ghcb, ctxt); 229 248 } 230 249 231 250 static int __sev_cpuid_hv(u32 fn, int reg_idx, u32 *reg) ··· 660 793 * If validating memory (making it private) and affected by the 661 794 * cache-coherency vulnerability, perform the cache eviction mitigation. 662 795 */ 663 - if (validate && !has_cpuflag(X86_FEATURE_COHERENCY_SFW_NO)) 796 + if (validate && sev_snp_needs_sfw) 664 797 sev_evict_cache((void *)vaddr, 1); 665 798 } 666 799
-42
arch/x86/boot/startup/sev-startup.c
··· 41 41 #include <asm/cpuid/api.h> 42 42 #include <asm/cmdline.h> 43 43 44 - /* For early boot hypervisor communication in SEV-ES enabled guests */ 45 - struct ghcb boot_ghcb_page __bss_decrypted __aligned(PAGE_SIZE); 46 - 47 - /* 48 - * Needs to be in the .data section because we need it NULL before bss is 49 - * cleared 50 - */ 51 - struct ghcb *boot_ghcb __section(".data"); 52 - 53 44 /* Bitmap of SEV features supported by the hypervisor */ 54 45 u64 sev_hv_features __ro_after_init; 55 46 ··· 128 137 vc_ghcb_invalidate(ghcb); 129 138 data->ghcb_active = false; 130 139 } 131 - } 132 - 133 - int svsm_perform_call_protocol(struct svsm_call *call) 134 - { 135 - struct ghcb_state state; 136 - unsigned long flags; 137 - struct ghcb *ghcb; 138 - int ret; 139 - 140 - /* 141 - * This can be called very early in the boot, use native functions in 142 - * order to avoid paravirt issues. 143 - */ 144 - flags = native_local_irq_save(); 145 - 146 - if (sev_cfg.ghcbs_initialized) 147 - ghcb = __sev_get_ghcb(&state); 148 - else if (boot_ghcb) 149 - ghcb = boot_ghcb; 150 - else 151 - ghcb = NULL; 152 - 153 - do { 154 - ret = ghcb ? svsm_perform_ghcb_protocol(ghcb, call) 155 - : svsm_perform_msr_protocol(call); 156 - } while (ret == -EAGAIN); 157 - 158 - if (sev_cfg.ghcbs_initialized) 159 - __sev_put_ghcb(&state); 160 - 161 - native_local_irq_restore(flags); 162 - 163 - return ret; 164 140 } 165 141 166 142 void __head
+1
arch/x86/boot/startup/sme.c
··· 521 521 return; 522 522 523 523 me_mask = 1UL << (ebx & 0x3f); 524 + sev_snp_needs_sfw = !(ebx & BIT(31)); 524 525 525 526 /* Check the SEV MSR whether SEV or SME is enabled */ 526 527 sev_status = msr = native_rdmsrq(MSR_AMD64_SEV);
+76
arch/x86/coco/sev/core.c
··· 101 101 u8 snp_vmpl __ro_after_init; 102 102 EXPORT_SYMBOL_GPL(snp_vmpl); 103 103 104 + /* For early boot hypervisor communication in SEV-ES enabled guests */ 105 + static struct ghcb boot_ghcb_page __bss_decrypted __aligned(PAGE_SIZE); 106 + 107 + /* 108 + * Needs to be in the .data section because we need it NULL before bss is 109 + * cleared 110 + */ 111 + struct ghcb *boot_ghcb __section(".data"); 112 + 104 113 static u64 __init get_snp_jump_table_addr(void) 105 114 { 106 115 struct snp_secrets_page *secrets; ··· 159 150 __sev_put_ghcb(&state); 160 151 161 152 local_irq_restore(flags); 153 + 154 + return ret; 155 + } 156 + 157 + static int svsm_perform_ghcb_protocol(struct ghcb *ghcb, struct svsm_call *call) 158 + { 159 + struct es_em_ctxt ctxt; 160 + u8 pending = 0; 161 + 162 + vc_ghcb_invalidate(ghcb); 163 + 164 + /* 165 + * Fill in protocol and format specifiers. This can be called very early 166 + * in the boot, so use rip-relative references as needed. 167 + */ 168 + ghcb->protocol_version = ghcb_version; 169 + ghcb->ghcb_usage = GHCB_DEFAULT_USAGE; 170 + 171 + ghcb_set_sw_exit_code(ghcb, SVM_VMGEXIT_SNP_RUN_VMPL); 172 + ghcb_set_sw_exit_info_1(ghcb, 0); 173 + ghcb_set_sw_exit_info_2(ghcb, 0); 174 + 175 + sev_es_wr_ghcb_msr(__pa(ghcb)); 176 + 177 + svsm_issue_call(call, &pending); 178 + 179 + if (pending) 180 + return -EINVAL; 181 + 182 + switch (verify_exception_info(ghcb, &ctxt)) { 183 + case ES_OK: 184 + break; 185 + case ES_EXCEPTION: 186 + vc_forward_exception(&ctxt); 187 + fallthrough; 188 + default: 189 + return -EINVAL; 190 + } 191 + 192 + return svsm_process_result_codes(call); 193 + } 194 + 195 + static int svsm_perform_call_protocol(struct svsm_call *call) 196 + { 197 + struct ghcb_state state; 198 + unsigned long flags; 199 + struct ghcb *ghcb; 200 + int ret; 201 + 202 + flags = native_local_irq_save(); 203 + 204 + if (sev_cfg.ghcbs_initialized) 205 + ghcb = __sev_get_ghcb(&state); 206 + else if (boot_ghcb) 207 + ghcb = boot_ghcb; 208 + else 209 + ghcb = NULL; 210 + 211 + do { 212 + ret = ghcb ? svsm_perform_ghcb_protocol(ghcb, call) 213 + : svsm_perform_msr_protocol(call); 214 + } while (ret == -EAGAIN); 215 + 216 + if (sev_cfg.ghcbs_initialized) 217 + __sev_put_ghcb(&state); 218 + 219 + native_local_irq_restore(flags); 162 220 163 221 return ret; 164 222 }
+2
arch/x86/coco/sev/vc-handle.c
··· 351 351 } 352 352 353 353 #define sev_printk(fmt, ...) printk(fmt, ##__VA_ARGS__) 354 + #define error(v) 355 + #define has_cpuflag(f) boot_cpu_has(f) 354 356 355 357 #include "vc-shared.c" 356 358
+94
arch/x86/coco/sev/vc-shared.c
··· 409 409 return ret; 410 410 } 411 411 412 + enum es_result verify_exception_info(struct ghcb *ghcb, struct es_em_ctxt *ctxt) 413 + { 414 + u32 ret; 415 + 416 + ret = ghcb->save.sw_exit_info_1 & GENMASK_ULL(31, 0); 417 + if (!ret) 418 + return ES_OK; 419 + 420 + if (ret == 1) { 421 + u64 info = ghcb->save.sw_exit_info_2; 422 + unsigned long v = info & SVM_EVTINJ_VEC_MASK; 423 + 424 + /* Check if exception information from hypervisor is sane. */ 425 + if ((info & SVM_EVTINJ_VALID) && 426 + ((v == X86_TRAP_GP) || (v == X86_TRAP_UD)) && 427 + ((info & SVM_EVTINJ_TYPE_MASK) == SVM_EVTINJ_TYPE_EXEPT)) { 428 + ctxt->fi.vector = v; 429 + 430 + if (info & SVM_EVTINJ_VALID_ERR) 431 + ctxt->fi.error_code = info >> 32; 432 + 433 + return ES_EXCEPTION; 434 + } 435 + } 436 + 437 + return ES_VMM_ERROR; 438 + } 439 + 440 + enum es_result sev_es_ghcb_hv_call(struct ghcb *ghcb, 441 + struct es_em_ctxt *ctxt, 442 + u64 exit_code, u64 exit_info_1, 443 + u64 exit_info_2) 444 + { 445 + /* Fill in protocol and format specifiers */ 446 + ghcb->protocol_version = ghcb_version; 447 + ghcb->ghcb_usage = GHCB_DEFAULT_USAGE; 448 + 449 + ghcb_set_sw_exit_code(ghcb, exit_code); 450 + ghcb_set_sw_exit_info_1(ghcb, exit_info_1); 451 + ghcb_set_sw_exit_info_2(ghcb, exit_info_2); 452 + 453 + sev_es_wr_ghcb_msr(__pa(ghcb)); 454 + VMGEXIT(); 455 + 456 + return verify_exception_info(ghcb, ctxt); 457 + } 458 + 412 459 static int __sev_cpuid_hv_ghcb(struct ghcb *ghcb, struct es_em_ctxt *ctxt, struct cpuid_leaf *leaf) 413 460 { 414 461 u32 cr4 = native_read_cr4(); ··· 595 548 ctxt->regs->cx = ghcb->save.rcx; 596 549 597 550 return ES_OK; 551 + } 552 + 553 + void snp_register_ghcb_early(unsigned long paddr) 554 + { 555 + unsigned long pfn = paddr >> PAGE_SHIFT; 556 + u64 val; 557 + 558 + sev_es_wr_ghcb_msr(GHCB_MSR_REG_GPA_REQ_VAL(pfn)); 559 + VMGEXIT(); 560 + 561 + val = sev_es_rd_ghcb_msr(); 562 + 563 + /* If the response GPA is not ours then abort the guest */ 564 + if ((GHCB_RESP_CODE(val) != GHCB_MSR_REG_GPA_RESP) || 565 + (GHCB_MSR_REG_GPA_RESP_VAL(val) != pfn)) 566 + sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_REGISTER); 567 + } 568 + 569 + bool __init sev_es_check_cpu_features(void) 570 + { 571 + if (!has_cpuflag(X86_FEATURE_RDRAND)) { 572 + error("RDRAND instruction not supported - no trusted source of randomness available\n"); 573 + return false; 574 + } 575 + 576 + return true; 577 + } 578 + 579 + bool sev_es_negotiate_protocol(void) 580 + { 581 + u64 val; 582 + 583 + /* Do the GHCB protocol version negotiation */ 584 + sev_es_wr_ghcb_msr(GHCB_MSR_SEV_INFO_REQ); 585 + VMGEXIT(); 586 + val = sev_es_rd_ghcb_msr(); 587 + 588 + if (GHCB_MSR_INFO(val) != GHCB_MSR_SEV_INFO_RESP) 589 + return false; 590 + 591 + if (GHCB_MSR_PROTO_MAX(val) < GHCB_PROTOCOL_MIN || 592 + GHCB_MSR_PROTO_MIN(val) > GHCB_PROTOCOL_MAX) 593 + return false; 594 + 595 + ghcb_version = min_t(size_t, GHCB_MSR_PROTO_MAX(val), GHCB_PROTOCOL_MAX); 596 + 597 + return true; 598 598 }
+2 -5
arch/x86/include/asm/sev-internal.h
··· 2 2 3 3 #define DR7_RESET_VALUE 0x400 4 4 5 - extern struct ghcb boot_ghcb_page; 6 5 extern u64 sev_hv_features; 7 6 extern u64 sev_secrets_pa; 8 7 ··· 79 80 return boot_svsm_caa_pa; 80 81 } 81 82 82 - int svsm_perform_call_protocol(struct svsm_call *call); 83 + enum es_result verify_exception_info(struct ghcb *ghcb, struct es_em_ctxt *ctxt); 84 + void vc_forward_exception(struct es_em_ctxt *ctxt); 83 85 84 86 static inline u64 sev_es_rd_ghcb_msr(void) 85 87 { ··· 97 97 native_wrmsr(MSR_AMD64_SEV_ES_GHCB, low, high); 98 98 } 99 99 100 - void snp_register_ghcb_early(unsigned long paddr); 101 - bool sev_es_negotiate_protocol(void); 102 - bool sev_es_check_cpu_features(void); 103 100 u64 get_hv_features(void); 104 101 105 102 const struct snp_cpuid_table *snp_cpuid_get_table(void);
+10 -2
arch/x86/include/asm/sev.h
··· 503 503 } 504 504 505 505 void setup_ghcb(void); 506 + void snp_register_ghcb_early(unsigned long paddr); 506 507 void early_snp_set_memory_private(unsigned long vaddr, unsigned long paddr, 507 508 unsigned long npages); 508 509 void early_snp_set_memory_shared(unsigned long vaddr, unsigned long paddr, ··· 541 540 __builtin_memset(ghcb->save.valid_bitmap, 0, sizeof(ghcb->save.valid_bitmap)); 542 541 } 543 542 544 - void vc_forward_exception(struct es_em_ctxt *ctxt); 545 - 546 543 /* I/O parameters for CPUID-related helpers */ 547 544 struct cpuid_leaf { 548 545 u32 fn; ··· 551 552 u32 edx; 552 553 }; 553 554 555 + int svsm_perform_msr_protocol(struct svsm_call *call); 554 556 int snp_cpuid(void (*cpuid_fn)(void *ctx, struct cpuid_leaf *leaf), 555 557 void *ctx, struct cpuid_leaf *leaf); 558 + 559 + void svsm_issue_call(struct svsm_call *call, u8 *pending); 560 + int svsm_process_result_codes(struct svsm_call *call); 556 561 557 562 void __noreturn sev_es_terminate(unsigned int set, unsigned int reason); 558 563 enum es_result sev_es_ghcb_hv_call(struct ghcb *ghcb, ··· 564 561 u64 exit_code, u64 exit_info_1, 565 562 u64 exit_info_2); 566 563 564 + bool sev_es_negotiate_protocol(void); 565 + bool sev_es_check_cpu_features(void); 566 + 567 + extern u16 ghcb_version; 567 568 extern struct ghcb *boot_ghcb; 569 + extern bool sev_snp_needs_sfw; 568 570 569 571 #else /* !CONFIG_AMD_MEM_ENCRYPT */ 570 572