1a3cbbb47SArd Biesheuvel // SPDX-License-Identifier: GPL-2.0 2a3cbbb47SArd Biesheuvel /* 3a3cbbb47SArd Biesheuvel * AMD Encrypted Register State Support 4a3cbbb47SArd Biesheuvel * 5a3cbbb47SArd Biesheuvel * Author: Joerg Roedel <jroedel@suse.de> 6a3cbbb47SArd Biesheuvel * 7a3cbbb47SArd Biesheuvel * This file is not compiled stand-alone. It contains code shared 8a3cbbb47SArd Biesheuvel * between the pre-decompression boot code and the running Linux kernel 9a3cbbb47SArd Biesheuvel * and is included directly into both code-bases. 10a3cbbb47SArd Biesheuvel */ 11a3cbbb47SArd Biesheuvel 12a3cbbb47SArd Biesheuvel #include <asm/setup_data.h> 13a3cbbb47SArd Biesheuvel 14a3cbbb47SArd Biesheuvel #ifndef __BOOT_COMPRESSED 15a3cbbb47SArd Biesheuvel #define error(v) pr_err(v) 16a3cbbb47SArd Biesheuvel #define has_cpuflag(f) boot_cpu_has(f) 17a3cbbb47SArd Biesheuvel #else 18a3cbbb47SArd Biesheuvel #undef WARN 19a3cbbb47SArd Biesheuvel #define WARN(condition, format...) (!!(condition)) 20a3cbbb47SArd Biesheuvel #undef vc_forward_exception 21a3cbbb47SArd Biesheuvel #define vc_forward_exception(c) panic("SNP: Hypervisor requested exception\n") 22a3cbbb47SArd Biesheuvel #endif 23a3cbbb47SArd Biesheuvel 24a3cbbb47SArd Biesheuvel /* 25a3cbbb47SArd Biesheuvel * SVSM related information: 26a3cbbb47SArd Biesheuvel * During boot, the page tables are set up as identity mapped and later 27a3cbbb47SArd Biesheuvel * changed to use kernel virtual addresses. Maintain separate virtual and 28a3cbbb47SArd Biesheuvel * physical addresses for the CAA to allow SVSM functions to be used during 29a3cbbb47SArd Biesheuvel * early boot, both with identity mapped virtual addresses and proper kernel 30a3cbbb47SArd Biesheuvel * virtual addresses. 31a3cbbb47SArd Biesheuvel */ 32a3cbbb47SArd Biesheuvel struct svsm_ca *boot_svsm_caa __ro_after_init; 33a3cbbb47SArd Biesheuvel u64 boot_svsm_caa_pa __ro_after_init; 34a3cbbb47SArd Biesheuvel 35a3cbbb47SArd Biesheuvel /* 36a3cbbb47SArd Biesheuvel * Since feature negotiation related variables are set early in the boot 37a3cbbb47SArd Biesheuvel * process they must reside in the .data section so as not to be zeroed 38a3cbbb47SArd Biesheuvel * out when the .bss section is later cleared. 39a3cbbb47SArd Biesheuvel * 40a3cbbb47SArd Biesheuvel * GHCB protocol version negotiated with the hypervisor. 41a3cbbb47SArd Biesheuvel */ 42a3cbbb47SArd Biesheuvel static u16 ghcb_version __ro_after_init; 43a3cbbb47SArd Biesheuvel 44a3cbbb47SArd Biesheuvel /* Copy of the SNP firmware's CPUID page. */ 45a3cbbb47SArd Biesheuvel static struct snp_cpuid_table cpuid_table_copy __ro_after_init; 46a3cbbb47SArd Biesheuvel 47a3cbbb47SArd Biesheuvel /* 48a3cbbb47SArd Biesheuvel * These will be initialized based on CPUID table so that non-present 49a3cbbb47SArd Biesheuvel * all-zero leaves (for sparse tables) can be differentiated from 50a3cbbb47SArd Biesheuvel * invalid/out-of-range leaves. This is needed since all-zero leaves 51a3cbbb47SArd Biesheuvel * still need to be post-processed. 52a3cbbb47SArd Biesheuvel */ 53a3cbbb47SArd Biesheuvel static u32 cpuid_std_range_max __ro_after_init; 54a3cbbb47SArd Biesheuvel static u32 cpuid_hyp_range_max __ro_after_init; 55a3cbbb47SArd Biesheuvel static u32 cpuid_ext_range_max __ro_after_init; 56a3cbbb47SArd Biesheuvel 57a3cbbb47SArd Biesheuvel bool __init sev_es_check_cpu_features(void) 58a3cbbb47SArd Biesheuvel { 59a3cbbb47SArd Biesheuvel if (!has_cpuflag(X86_FEATURE_RDRAND)) { 60a3cbbb47SArd Biesheuvel error("RDRAND instruction not supported - no trusted source of randomness available\n"); 61a3cbbb47SArd Biesheuvel return false; 62a3cbbb47SArd Biesheuvel } 63a3cbbb47SArd Biesheuvel 64a3cbbb47SArd Biesheuvel return true; 65a3cbbb47SArd Biesheuvel } 66a3cbbb47SArd Biesheuvel 67a3cbbb47SArd Biesheuvel void __head __noreturn 68a3cbbb47SArd Biesheuvel sev_es_terminate(unsigned int set, unsigned int reason) 69a3cbbb47SArd Biesheuvel { 70a3cbbb47SArd Biesheuvel u64 val = GHCB_MSR_TERM_REQ; 71a3cbbb47SArd Biesheuvel 72a3cbbb47SArd Biesheuvel /* Tell the hypervisor what went wrong. */ 73a3cbbb47SArd Biesheuvel val |= GHCB_SEV_TERM_REASON(set, reason); 74a3cbbb47SArd Biesheuvel 75a3cbbb47SArd Biesheuvel /* Request Guest Termination from Hypervisor */ 76a3cbbb47SArd Biesheuvel sev_es_wr_ghcb_msr(val); 77a3cbbb47SArd Biesheuvel VMGEXIT(); 78a3cbbb47SArd Biesheuvel 79a3cbbb47SArd Biesheuvel while (true) 80a3cbbb47SArd Biesheuvel asm volatile("hlt\n" : : : "memory"); 81a3cbbb47SArd Biesheuvel } 82a3cbbb47SArd Biesheuvel 83a3cbbb47SArd Biesheuvel /* 84a3cbbb47SArd Biesheuvel * The hypervisor features are available from GHCB version 2 onward. 85a3cbbb47SArd Biesheuvel */ 86a3cbbb47SArd Biesheuvel u64 get_hv_features(void) 87a3cbbb47SArd Biesheuvel { 88a3cbbb47SArd Biesheuvel u64 val; 89a3cbbb47SArd Biesheuvel 90a3cbbb47SArd Biesheuvel if (ghcb_version < 2) 91a3cbbb47SArd Biesheuvel return 0; 92a3cbbb47SArd Biesheuvel 93a3cbbb47SArd Biesheuvel sev_es_wr_ghcb_msr(GHCB_MSR_HV_FT_REQ); 94a3cbbb47SArd Biesheuvel VMGEXIT(); 95a3cbbb47SArd Biesheuvel 96a3cbbb47SArd Biesheuvel val = sev_es_rd_ghcb_msr(); 97a3cbbb47SArd Biesheuvel if (GHCB_RESP_CODE(val) != GHCB_MSR_HV_FT_RESP) 98a3cbbb47SArd Biesheuvel return 0; 99a3cbbb47SArd Biesheuvel 100a3cbbb47SArd Biesheuvel return GHCB_MSR_HV_FT_RESP_VAL(val); 101a3cbbb47SArd Biesheuvel } 102a3cbbb47SArd Biesheuvel 103a3cbbb47SArd Biesheuvel void snp_register_ghcb_early(unsigned long paddr) 104a3cbbb47SArd Biesheuvel { 105a3cbbb47SArd Biesheuvel unsigned long pfn = paddr >> PAGE_SHIFT; 106a3cbbb47SArd Biesheuvel u64 val; 107a3cbbb47SArd Biesheuvel 108a3cbbb47SArd Biesheuvel sev_es_wr_ghcb_msr(GHCB_MSR_REG_GPA_REQ_VAL(pfn)); 109a3cbbb47SArd Biesheuvel VMGEXIT(); 110a3cbbb47SArd Biesheuvel 111a3cbbb47SArd Biesheuvel val = sev_es_rd_ghcb_msr(); 112a3cbbb47SArd Biesheuvel 113a3cbbb47SArd Biesheuvel /* If the response GPA is not ours then abort the guest */ 114a3cbbb47SArd Biesheuvel if ((GHCB_RESP_CODE(val) != GHCB_MSR_REG_GPA_RESP) || 115a3cbbb47SArd Biesheuvel (GHCB_MSR_REG_GPA_RESP_VAL(val) != pfn)) 116a3cbbb47SArd Biesheuvel sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_REGISTER); 117a3cbbb47SArd Biesheuvel } 118a3cbbb47SArd Biesheuvel 119a3cbbb47SArd Biesheuvel bool sev_es_negotiate_protocol(void) 120a3cbbb47SArd Biesheuvel { 121a3cbbb47SArd Biesheuvel u64 val; 122a3cbbb47SArd Biesheuvel 123a3cbbb47SArd Biesheuvel /* Do the GHCB protocol version negotiation */ 124a3cbbb47SArd Biesheuvel sev_es_wr_ghcb_msr(GHCB_MSR_SEV_INFO_REQ); 125a3cbbb47SArd Biesheuvel VMGEXIT(); 126a3cbbb47SArd Biesheuvel val = sev_es_rd_ghcb_msr(); 127a3cbbb47SArd Biesheuvel 128a3cbbb47SArd Biesheuvel if (GHCB_MSR_INFO(val) != GHCB_MSR_SEV_INFO_RESP) 129a3cbbb47SArd Biesheuvel return false; 130a3cbbb47SArd Biesheuvel 131a3cbbb47SArd Biesheuvel if (GHCB_MSR_PROTO_MAX(val) < GHCB_PROTOCOL_MIN || 132a3cbbb47SArd Biesheuvel GHCB_MSR_PROTO_MIN(val) > GHCB_PROTOCOL_MAX) 133a3cbbb47SArd Biesheuvel return false; 134a3cbbb47SArd Biesheuvel 135a3cbbb47SArd Biesheuvel ghcb_version = min_t(size_t, GHCB_MSR_PROTO_MAX(val), GHCB_PROTOCOL_MAX); 136a3cbbb47SArd Biesheuvel 137a3cbbb47SArd Biesheuvel return true; 138a3cbbb47SArd Biesheuvel } 139a3cbbb47SArd Biesheuvel 140a3cbbb47SArd Biesheuvel static enum es_result verify_exception_info(struct ghcb *ghcb, struct es_em_ctxt *ctxt) 141a3cbbb47SArd Biesheuvel { 142a3cbbb47SArd Biesheuvel u32 ret; 143a3cbbb47SArd Biesheuvel 144a3cbbb47SArd Biesheuvel ret = ghcb->save.sw_exit_info_1 & GENMASK_ULL(31, 0); 145a3cbbb47SArd Biesheuvel if (!ret) 146a3cbbb47SArd Biesheuvel return ES_OK; 147a3cbbb47SArd Biesheuvel 148a3cbbb47SArd Biesheuvel if (ret == 1) { 149a3cbbb47SArd Biesheuvel u64 info = ghcb->save.sw_exit_info_2; 150a3cbbb47SArd Biesheuvel unsigned long v = info & SVM_EVTINJ_VEC_MASK; 151a3cbbb47SArd Biesheuvel 152a3cbbb47SArd Biesheuvel /* Check if exception information from hypervisor is sane. */ 153a3cbbb47SArd Biesheuvel if ((info & SVM_EVTINJ_VALID) && 154a3cbbb47SArd Biesheuvel ((v == X86_TRAP_GP) || (v == X86_TRAP_UD)) && 155a3cbbb47SArd Biesheuvel ((info & SVM_EVTINJ_TYPE_MASK) == SVM_EVTINJ_TYPE_EXEPT)) { 156a3cbbb47SArd Biesheuvel ctxt->fi.vector = v; 157a3cbbb47SArd Biesheuvel 158a3cbbb47SArd Biesheuvel if (info & SVM_EVTINJ_VALID_ERR) 159a3cbbb47SArd Biesheuvel ctxt->fi.error_code = info >> 32; 160a3cbbb47SArd Biesheuvel 161a3cbbb47SArd Biesheuvel return ES_EXCEPTION; 162a3cbbb47SArd Biesheuvel } 163a3cbbb47SArd Biesheuvel } 164a3cbbb47SArd Biesheuvel 165a3cbbb47SArd Biesheuvel return ES_VMM_ERROR; 166a3cbbb47SArd Biesheuvel } 167a3cbbb47SArd Biesheuvel 168a3cbbb47SArd Biesheuvel static inline int svsm_process_result_codes(struct svsm_call *call) 169a3cbbb47SArd Biesheuvel { 170a3cbbb47SArd Biesheuvel switch (call->rax_out) { 171a3cbbb47SArd Biesheuvel case SVSM_SUCCESS: 172a3cbbb47SArd Biesheuvel return 0; 173a3cbbb47SArd Biesheuvel case SVSM_ERR_INCOMPLETE: 174a3cbbb47SArd Biesheuvel case SVSM_ERR_BUSY: 175a3cbbb47SArd Biesheuvel return -EAGAIN; 176a3cbbb47SArd Biesheuvel default: 177a3cbbb47SArd Biesheuvel return -EINVAL; 178a3cbbb47SArd Biesheuvel } 179a3cbbb47SArd Biesheuvel } 180a3cbbb47SArd Biesheuvel 181a3cbbb47SArd Biesheuvel /* 182a3cbbb47SArd Biesheuvel * Issue a VMGEXIT to call the SVSM: 183a3cbbb47SArd Biesheuvel * - Load the SVSM register state (RAX, RCX, RDX, R8 and R9) 184a3cbbb47SArd Biesheuvel * - Set the CA call pending field to 1 185a3cbbb47SArd Biesheuvel * - Issue VMGEXIT 186a3cbbb47SArd Biesheuvel * - Save the SVSM return register state (RAX, RCX, RDX, R8 and R9) 187a3cbbb47SArd Biesheuvel * - Perform atomic exchange of the CA call pending field 188a3cbbb47SArd Biesheuvel * 189a3cbbb47SArd Biesheuvel * - See the "Secure VM Service Module for SEV-SNP Guests" specification for 190a3cbbb47SArd Biesheuvel * details on the calling convention. 191a3cbbb47SArd Biesheuvel * - The calling convention loosely follows the Microsoft X64 calling 192a3cbbb47SArd Biesheuvel * convention by putting arguments in RCX, RDX, R8 and R9. 193a3cbbb47SArd Biesheuvel * - RAX specifies the SVSM protocol/callid as input and the return code 194a3cbbb47SArd Biesheuvel * as output. 195a3cbbb47SArd Biesheuvel */ 196a3cbbb47SArd Biesheuvel static __always_inline void svsm_issue_call(struct svsm_call *call, u8 *pending) 197a3cbbb47SArd Biesheuvel { 198a3cbbb47SArd Biesheuvel register unsigned long rax asm("rax") = call->rax; 199a3cbbb47SArd Biesheuvel register unsigned long rcx asm("rcx") = call->rcx; 200a3cbbb47SArd Biesheuvel register unsigned long rdx asm("rdx") = call->rdx; 201a3cbbb47SArd Biesheuvel register unsigned long r8 asm("r8") = call->r8; 202a3cbbb47SArd Biesheuvel register unsigned long r9 asm("r9") = call->r9; 203a3cbbb47SArd Biesheuvel 204a3cbbb47SArd Biesheuvel call->caa->call_pending = 1; 205a3cbbb47SArd Biesheuvel 206a3cbbb47SArd Biesheuvel asm volatile("rep; vmmcall\n\t" 207a3cbbb47SArd Biesheuvel : "+r" (rax), "+r" (rcx), "+r" (rdx), "+r" (r8), "+r" (r9) 208a3cbbb47SArd Biesheuvel : : "memory"); 209a3cbbb47SArd Biesheuvel 210a3cbbb47SArd Biesheuvel *pending = xchg(&call->caa->call_pending, *pending); 211a3cbbb47SArd Biesheuvel 212a3cbbb47SArd Biesheuvel call->rax_out = rax; 213a3cbbb47SArd Biesheuvel call->rcx_out = rcx; 214a3cbbb47SArd Biesheuvel call->rdx_out = rdx; 215a3cbbb47SArd Biesheuvel call->r8_out = r8; 216a3cbbb47SArd Biesheuvel call->r9_out = r9; 217a3cbbb47SArd Biesheuvel } 218a3cbbb47SArd Biesheuvel 219a3cbbb47SArd Biesheuvel static int svsm_perform_msr_protocol(struct svsm_call *call) 220a3cbbb47SArd Biesheuvel { 221a3cbbb47SArd Biesheuvel u8 pending = 0; 222a3cbbb47SArd Biesheuvel u64 val, resp; 223a3cbbb47SArd Biesheuvel 224a3cbbb47SArd Biesheuvel /* 225a3cbbb47SArd Biesheuvel * When using the MSR protocol, be sure to save and restore 226a3cbbb47SArd Biesheuvel * the current MSR value. 227a3cbbb47SArd Biesheuvel */ 228a3cbbb47SArd Biesheuvel val = sev_es_rd_ghcb_msr(); 229a3cbbb47SArd Biesheuvel 230a3cbbb47SArd Biesheuvel sev_es_wr_ghcb_msr(GHCB_MSR_VMPL_REQ_LEVEL(0)); 231a3cbbb47SArd Biesheuvel 232a3cbbb47SArd Biesheuvel svsm_issue_call(call, &pending); 233a3cbbb47SArd Biesheuvel 234a3cbbb47SArd Biesheuvel resp = sev_es_rd_ghcb_msr(); 235a3cbbb47SArd Biesheuvel 236a3cbbb47SArd Biesheuvel sev_es_wr_ghcb_msr(val); 237a3cbbb47SArd Biesheuvel 238a3cbbb47SArd Biesheuvel if (pending) 239a3cbbb47SArd Biesheuvel return -EINVAL; 240a3cbbb47SArd Biesheuvel 241a3cbbb47SArd Biesheuvel if (GHCB_RESP_CODE(resp) != GHCB_MSR_VMPL_RESP) 242a3cbbb47SArd Biesheuvel return -EINVAL; 243a3cbbb47SArd Biesheuvel 244a3cbbb47SArd Biesheuvel if (GHCB_MSR_VMPL_RESP_VAL(resp)) 245a3cbbb47SArd Biesheuvel return -EINVAL; 246a3cbbb47SArd Biesheuvel 247a3cbbb47SArd Biesheuvel return svsm_process_result_codes(call); 248a3cbbb47SArd Biesheuvel } 249a3cbbb47SArd Biesheuvel 250a3cbbb47SArd Biesheuvel static int svsm_perform_ghcb_protocol(struct ghcb *ghcb, struct svsm_call *call) 251a3cbbb47SArd Biesheuvel { 252a3cbbb47SArd Biesheuvel struct es_em_ctxt ctxt; 253a3cbbb47SArd Biesheuvel u8 pending = 0; 254a3cbbb47SArd Biesheuvel 255a3cbbb47SArd Biesheuvel vc_ghcb_invalidate(ghcb); 256a3cbbb47SArd Biesheuvel 257a3cbbb47SArd Biesheuvel /* 258a3cbbb47SArd Biesheuvel * Fill in protocol and format specifiers. This can be called very early 259a3cbbb47SArd Biesheuvel * in the boot, so use rip-relative references as needed. 260a3cbbb47SArd Biesheuvel */ 261681e2901SArd Biesheuvel ghcb->protocol_version = ghcb_version; 262a3cbbb47SArd Biesheuvel ghcb->ghcb_usage = GHCB_DEFAULT_USAGE; 263a3cbbb47SArd Biesheuvel 264a3cbbb47SArd Biesheuvel ghcb_set_sw_exit_code(ghcb, SVM_VMGEXIT_SNP_RUN_VMPL); 265a3cbbb47SArd Biesheuvel ghcb_set_sw_exit_info_1(ghcb, 0); 266a3cbbb47SArd Biesheuvel ghcb_set_sw_exit_info_2(ghcb, 0); 267a3cbbb47SArd Biesheuvel 268a3cbbb47SArd Biesheuvel sev_es_wr_ghcb_msr(__pa(ghcb)); 269a3cbbb47SArd Biesheuvel 270a3cbbb47SArd Biesheuvel svsm_issue_call(call, &pending); 271a3cbbb47SArd Biesheuvel 272a3cbbb47SArd Biesheuvel if (pending) 273a3cbbb47SArd Biesheuvel return -EINVAL; 274a3cbbb47SArd Biesheuvel 275a3cbbb47SArd Biesheuvel switch (verify_exception_info(ghcb, &ctxt)) { 276a3cbbb47SArd Biesheuvel case ES_OK: 277a3cbbb47SArd Biesheuvel break; 278a3cbbb47SArd Biesheuvel case ES_EXCEPTION: 279a3cbbb47SArd Biesheuvel vc_forward_exception(&ctxt); 280a3cbbb47SArd Biesheuvel fallthrough; 281a3cbbb47SArd Biesheuvel default: 282a3cbbb47SArd Biesheuvel return -EINVAL; 283a3cbbb47SArd Biesheuvel } 284a3cbbb47SArd Biesheuvel 285a3cbbb47SArd Biesheuvel return svsm_process_result_codes(call); 286a3cbbb47SArd Biesheuvel } 287a3cbbb47SArd Biesheuvel 288a3cbbb47SArd Biesheuvel enum es_result sev_es_ghcb_hv_call(struct ghcb *ghcb, 289a3cbbb47SArd Biesheuvel struct es_em_ctxt *ctxt, 290a3cbbb47SArd Biesheuvel u64 exit_code, u64 exit_info_1, 291a3cbbb47SArd Biesheuvel u64 exit_info_2) 292a3cbbb47SArd Biesheuvel { 293a3cbbb47SArd Biesheuvel /* Fill in protocol and format specifiers */ 294a3cbbb47SArd Biesheuvel ghcb->protocol_version = ghcb_version; 295a3cbbb47SArd Biesheuvel ghcb->ghcb_usage = GHCB_DEFAULT_USAGE; 296a3cbbb47SArd Biesheuvel 297a3cbbb47SArd Biesheuvel ghcb_set_sw_exit_code(ghcb, exit_code); 298a3cbbb47SArd Biesheuvel ghcb_set_sw_exit_info_1(ghcb, exit_info_1); 299a3cbbb47SArd Biesheuvel ghcb_set_sw_exit_info_2(ghcb, exit_info_2); 300a3cbbb47SArd Biesheuvel 301a3cbbb47SArd Biesheuvel sev_es_wr_ghcb_msr(__pa(ghcb)); 302a3cbbb47SArd Biesheuvel VMGEXIT(); 303a3cbbb47SArd Biesheuvel 304a3cbbb47SArd Biesheuvel return verify_exception_info(ghcb, ctxt); 305a3cbbb47SArd Biesheuvel } 306a3cbbb47SArd Biesheuvel 307a3cbbb47SArd Biesheuvel static int __sev_cpuid_hv(u32 fn, int reg_idx, u32 *reg) 308a3cbbb47SArd Biesheuvel { 309a3cbbb47SArd Biesheuvel u64 val; 310a3cbbb47SArd Biesheuvel 311a3cbbb47SArd Biesheuvel sev_es_wr_ghcb_msr(GHCB_CPUID_REQ(fn, reg_idx)); 312a3cbbb47SArd Biesheuvel VMGEXIT(); 313a3cbbb47SArd Biesheuvel val = sev_es_rd_ghcb_msr(); 314a3cbbb47SArd Biesheuvel if (GHCB_RESP_CODE(val) != GHCB_MSR_CPUID_RESP) 315a3cbbb47SArd Biesheuvel return -EIO; 316a3cbbb47SArd Biesheuvel 317a3cbbb47SArd Biesheuvel *reg = (val >> 32); 318a3cbbb47SArd Biesheuvel 319a3cbbb47SArd Biesheuvel return 0; 320a3cbbb47SArd Biesheuvel } 321a3cbbb47SArd Biesheuvel 322a3cbbb47SArd Biesheuvel static int __sev_cpuid_hv_msr(struct cpuid_leaf *leaf) 323a3cbbb47SArd Biesheuvel { 324a3cbbb47SArd Biesheuvel int ret; 325a3cbbb47SArd Biesheuvel 326a3cbbb47SArd Biesheuvel /* 327a3cbbb47SArd Biesheuvel * MSR protocol does not support fetching non-zero subfunctions, but is 328a3cbbb47SArd Biesheuvel * sufficient to handle current early-boot cases. Should that change, 329a3cbbb47SArd Biesheuvel * make sure to report an error rather than ignoring the index and 330a3cbbb47SArd Biesheuvel * grabbing random values. If this issue arises in the future, handling 331a3cbbb47SArd Biesheuvel * can be added here to use GHCB-page protocol for cases that occur late 332a3cbbb47SArd Biesheuvel * enough in boot that GHCB page is available. 333a3cbbb47SArd Biesheuvel */ 334a3cbbb47SArd Biesheuvel if (cpuid_function_is_indexed(leaf->fn) && leaf->subfn) 335a3cbbb47SArd Biesheuvel return -EINVAL; 336a3cbbb47SArd Biesheuvel 337a3cbbb47SArd Biesheuvel ret = __sev_cpuid_hv(leaf->fn, GHCB_CPUID_REQ_EAX, &leaf->eax); 338a3cbbb47SArd Biesheuvel ret = ret ? : __sev_cpuid_hv(leaf->fn, GHCB_CPUID_REQ_EBX, &leaf->ebx); 339a3cbbb47SArd Biesheuvel ret = ret ? : __sev_cpuid_hv(leaf->fn, GHCB_CPUID_REQ_ECX, &leaf->ecx); 340a3cbbb47SArd Biesheuvel ret = ret ? : __sev_cpuid_hv(leaf->fn, GHCB_CPUID_REQ_EDX, &leaf->edx); 341a3cbbb47SArd Biesheuvel 342a3cbbb47SArd Biesheuvel return ret; 343a3cbbb47SArd Biesheuvel } 344a3cbbb47SArd Biesheuvel 345a3cbbb47SArd Biesheuvel static int __sev_cpuid_hv_ghcb(struct ghcb *ghcb, struct es_em_ctxt *ctxt, struct cpuid_leaf *leaf) 346a3cbbb47SArd Biesheuvel { 347a3cbbb47SArd Biesheuvel u32 cr4 = native_read_cr4(); 348a3cbbb47SArd Biesheuvel int ret; 349a3cbbb47SArd Biesheuvel 350a3cbbb47SArd Biesheuvel ghcb_set_rax(ghcb, leaf->fn); 351a3cbbb47SArd Biesheuvel ghcb_set_rcx(ghcb, leaf->subfn); 352a3cbbb47SArd Biesheuvel 353a3cbbb47SArd Biesheuvel if (cr4 & X86_CR4_OSXSAVE) 354a3cbbb47SArd Biesheuvel /* Safe to read xcr0 */ 355a3cbbb47SArd Biesheuvel ghcb_set_xcr0(ghcb, xgetbv(XCR_XFEATURE_ENABLED_MASK)); 356a3cbbb47SArd Biesheuvel else 357a3cbbb47SArd Biesheuvel /* xgetbv will cause #UD - use reset value for xcr0 */ 358a3cbbb47SArd Biesheuvel ghcb_set_xcr0(ghcb, 1); 359a3cbbb47SArd Biesheuvel 360a3cbbb47SArd Biesheuvel ret = sev_es_ghcb_hv_call(ghcb, ctxt, SVM_EXIT_CPUID, 0, 0); 361a3cbbb47SArd Biesheuvel if (ret != ES_OK) 362a3cbbb47SArd Biesheuvel return ret; 363a3cbbb47SArd Biesheuvel 364a3cbbb47SArd Biesheuvel if (!(ghcb_rax_is_valid(ghcb) && 365a3cbbb47SArd Biesheuvel ghcb_rbx_is_valid(ghcb) && 366a3cbbb47SArd Biesheuvel ghcb_rcx_is_valid(ghcb) && 367a3cbbb47SArd Biesheuvel ghcb_rdx_is_valid(ghcb))) 368a3cbbb47SArd Biesheuvel return ES_VMM_ERROR; 369a3cbbb47SArd Biesheuvel 370a3cbbb47SArd Biesheuvel leaf->eax = ghcb->save.rax; 371a3cbbb47SArd Biesheuvel leaf->ebx = ghcb->save.rbx; 372a3cbbb47SArd Biesheuvel leaf->ecx = ghcb->save.rcx; 373a3cbbb47SArd Biesheuvel leaf->edx = ghcb->save.rdx; 374a3cbbb47SArd Biesheuvel 375a3cbbb47SArd Biesheuvel return ES_OK; 376a3cbbb47SArd Biesheuvel } 377a3cbbb47SArd Biesheuvel 378a3cbbb47SArd Biesheuvel static int sev_cpuid_hv(struct ghcb *ghcb, struct es_em_ctxt *ctxt, struct cpuid_leaf *leaf) 379a3cbbb47SArd Biesheuvel { 380a3cbbb47SArd Biesheuvel return ghcb ? __sev_cpuid_hv_ghcb(ghcb, ctxt, leaf) 381a3cbbb47SArd Biesheuvel : __sev_cpuid_hv_msr(leaf); 382a3cbbb47SArd Biesheuvel } 383a3cbbb47SArd Biesheuvel 384a3cbbb47SArd Biesheuvel /* 385a3cbbb47SArd Biesheuvel * This may be called early while still running on the initial identity 386a3cbbb47SArd Biesheuvel * mapping. Use RIP-relative addressing to obtain the correct address 387a3cbbb47SArd Biesheuvel * while running with the initial identity mapping as well as the 388a3cbbb47SArd Biesheuvel * switch-over to kernel virtual addresses later. 389a3cbbb47SArd Biesheuvel */ 390a3cbbb47SArd Biesheuvel const struct snp_cpuid_table *snp_cpuid_get_table(void) 391a3cbbb47SArd Biesheuvel { 392a3cbbb47SArd Biesheuvel return rip_rel_ptr(&cpuid_table_copy); 393a3cbbb47SArd Biesheuvel } 394a3cbbb47SArd Biesheuvel 395a3cbbb47SArd Biesheuvel /* 396a3cbbb47SArd Biesheuvel * The SNP Firmware ABI, Revision 0.9, Section 7.1, details the use of 397a3cbbb47SArd Biesheuvel * XCR0_IN and XSS_IN to encode multiple versions of 0xD subfunctions 0 398a3cbbb47SArd Biesheuvel * and 1 based on the corresponding features enabled by a particular 399a3cbbb47SArd Biesheuvel * combination of XCR0 and XSS registers so that a guest can look up the 400a3cbbb47SArd Biesheuvel * version corresponding to the features currently enabled in its XCR0/XSS 401a3cbbb47SArd Biesheuvel * registers. The only values that differ between these versions/table 402a3cbbb47SArd Biesheuvel * entries is the enabled XSAVE area size advertised via EBX. 403a3cbbb47SArd Biesheuvel * 404a3cbbb47SArd Biesheuvel * While hypervisors may choose to make use of this support, it is more 405a3cbbb47SArd Biesheuvel * robust/secure for a guest to simply find the entry corresponding to the 406a3cbbb47SArd Biesheuvel * base/legacy XSAVE area size (XCR0=1 or XCR0=3), and then calculate the 407a3cbbb47SArd Biesheuvel * XSAVE area size using subfunctions 2 through 64, as documented in APM 408a3cbbb47SArd Biesheuvel * Volume 3, Rev 3.31, Appendix E.3.8, which is what is done here. 409a3cbbb47SArd Biesheuvel * 410a3cbbb47SArd Biesheuvel * Since base/legacy XSAVE area size is documented as 0x240, use that value 411a3cbbb47SArd Biesheuvel * directly rather than relying on the base size in the CPUID table. 412a3cbbb47SArd Biesheuvel * 413a3cbbb47SArd Biesheuvel * Return: XSAVE area size on success, 0 otherwise. 414a3cbbb47SArd Biesheuvel */ 415a3cbbb47SArd Biesheuvel static u32 __head snp_cpuid_calc_xsave_size(u64 xfeatures_en, bool compacted) 416a3cbbb47SArd Biesheuvel { 417a3cbbb47SArd Biesheuvel const struct snp_cpuid_table *cpuid_table = snp_cpuid_get_table(); 418a3cbbb47SArd Biesheuvel u64 xfeatures_found = 0; 419a3cbbb47SArd Biesheuvel u32 xsave_size = 0x240; 420a3cbbb47SArd Biesheuvel int i; 421a3cbbb47SArd Biesheuvel 422a3cbbb47SArd Biesheuvel for (i = 0; i < cpuid_table->count; i++) { 423a3cbbb47SArd Biesheuvel const struct snp_cpuid_fn *e = &cpuid_table->fn[i]; 424a3cbbb47SArd Biesheuvel 425a3cbbb47SArd Biesheuvel if (!(e->eax_in == 0xD && e->ecx_in > 1 && e->ecx_in < 64)) 426a3cbbb47SArd Biesheuvel continue; 427a3cbbb47SArd Biesheuvel if (!(xfeatures_en & (BIT_ULL(e->ecx_in)))) 428a3cbbb47SArd Biesheuvel continue; 429a3cbbb47SArd Biesheuvel if (xfeatures_found & (BIT_ULL(e->ecx_in))) 430a3cbbb47SArd Biesheuvel continue; 431a3cbbb47SArd Biesheuvel 432a3cbbb47SArd Biesheuvel xfeatures_found |= (BIT_ULL(e->ecx_in)); 433a3cbbb47SArd Biesheuvel 434a3cbbb47SArd Biesheuvel if (compacted) 435a3cbbb47SArd Biesheuvel xsave_size += e->eax; 436a3cbbb47SArd Biesheuvel else 437a3cbbb47SArd Biesheuvel xsave_size = max(xsave_size, e->eax + e->ebx); 438a3cbbb47SArd Biesheuvel } 439a3cbbb47SArd Biesheuvel 440a3cbbb47SArd Biesheuvel /* 441a3cbbb47SArd Biesheuvel * Either the guest set unsupported XCR0/XSS bits, or the corresponding 442a3cbbb47SArd Biesheuvel * entries in the CPUID table were not present. This is not a valid 443a3cbbb47SArd Biesheuvel * state to be in. 444a3cbbb47SArd Biesheuvel */ 445a3cbbb47SArd Biesheuvel if (xfeatures_found != (xfeatures_en & GENMASK_ULL(63, 2))) 446a3cbbb47SArd Biesheuvel return 0; 447a3cbbb47SArd Biesheuvel 448a3cbbb47SArd Biesheuvel return xsave_size; 449a3cbbb47SArd Biesheuvel } 450a3cbbb47SArd Biesheuvel 451a3cbbb47SArd Biesheuvel static bool __head 452a3cbbb47SArd Biesheuvel snp_cpuid_get_validated_func(struct cpuid_leaf *leaf) 453a3cbbb47SArd Biesheuvel { 454a3cbbb47SArd Biesheuvel const struct snp_cpuid_table *cpuid_table = snp_cpuid_get_table(); 455a3cbbb47SArd Biesheuvel int i; 456a3cbbb47SArd Biesheuvel 457a3cbbb47SArd Biesheuvel for (i = 0; i < cpuid_table->count; i++) { 458a3cbbb47SArd Biesheuvel const struct snp_cpuid_fn *e = &cpuid_table->fn[i]; 459a3cbbb47SArd Biesheuvel 460a3cbbb47SArd Biesheuvel if (e->eax_in != leaf->fn) 461a3cbbb47SArd Biesheuvel continue; 462a3cbbb47SArd Biesheuvel 463a3cbbb47SArd Biesheuvel if (cpuid_function_is_indexed(leaf->fn) && e->ecx_in != leaf->subfn) 464a3cbbb47SArd Biesheuvel continue; 465a3cbbb47SArd Biesheuvel 466a3cbbb47SArd Biesheuvel /* 467a3cbbb47SArd Biesheuvel * For 0xD subfunctions 0 and 1, only use the entry corresponding 468a3cbbb47SArd Biesheuvel * to the base/legacy XSAVE area size (XCR0=1 or XCR0=3, XSS=0). 469a3cbbb47SArd Biesheuvel * See the comments above snp_cpuid_calc_xsave_size() for more 470a3cbbb47SArd Biesheuvel * details. 471a3cbbb47SArd Biesheuvel */ 472a3cbbb47SArd Biesheuvel if (e->eax_in == 0xD && (e->ecx_in == 0 || e->ecx_in == 1)) 473a3cbbb47SArd Biesheuvel if (!(e->xcr0_in == 1 || e->xcr0_in == 3) || e->xss_in) 474a3cbbb47SArd Biesheuvel continue; 475a3cbbb47SArd Biesheuvel 476a3cbbb47SArd Biesheuvel leaf->eax = e->eax; 477a3cbbb47SArd Biesheuvel leaf->ebx = e->ebx; 478a3cbbb47SArd Biesheuvel leaf->ecx = e->ecx; 479a3cbbb47SArd Biesheuvel leaf->edx = e->edx; 480a3cbbb47SArd Biesheuvel 481a3cbbb47SArd Biesheuvel return true; 482a3cbbb47SArd Biesheuvel } 483a3cbbb47SArd Biesheuvel 484a3cbbb47SArd Biesheuvel return false; 485a3cbbb47SArd Biesheuvel } 486a3cbbb47SArd Biesheuvel 487a3cbbb47SArd Biesheuvel static void snp_cpuid_hv(struct ghcb *ghcb, struct es_em_ctxt *ctxt, struct cpuid_leaf *leaf) 488a3cbbb47SArd Biesheuvel { 489a3cbbb47SArd Biesheuvel if (sev_cpuid_hv(ghcb, ctxt, leaf)) 490a3cbbb47SArd Biesheuvel sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_CPUID_HV); 491a3cbbb47SArd Biesheuvel } 492a3cbbb47SArd Biesheuvel 493a3cbbb47SArd Biesheuvel static int __head 494a3cbbb47SArd Biesheuvel snp_cpuid_postprocess(struct ghcb *ghcb, struct es_em_ctxt *ctxt, 495a3cbbb47SArd Biesheuvel struct cpuid_leaf *leaf) 496a3cbbb47SArd Biesheuvel { 497a3cbbb47SArd Biesheuvel struct cpuid_leaf leaf_hv = *leaf; 498a3cbbb47SArd Biesheuvel 499a3cbbb47SArd Biesheuvel switch (leaf->fn) { 500a3cbbb47SArd Biesheuvel case 0x1: 501a3cbbb47SArd Biesheuvel snp_cpuid_hv(ghcb, ctxt, &leaf_hv); 502a3cbbb47SArd Biesheuvel 503a3cbbb47SArd Biesheuvel /* initial APIC ID */ 504a3cbbb47SArd Biesheuvel leaf->ebx = (leaf_hv.ebx & GENMASK(31, 24)) | (leaf->ebx & GENMASK(23, 0)); 505a3cbbb47SArd Biesheuvel /* APIC enabled bit */ 506a3cbbb47SArd Biesheuvel leaf->edx = (leaf_hv.edx & BIT(9)) | (leaf->edx & ~BIT(9)); 507a3cbbb47SArd Biesheuvel 508a3cbbb47SArd Biesheuvel /* OSXSAVE enabled bit */ 509a3cbbb47SArd Biesheuvel if (native_read_cr4() & X86_CR4_OSXSAVE) 510a3cbbb47SArd Biesheuvel leaf->ecx |= BIT(27); 511a3cbbb47SArd Biesheuvel break; 512a3cbbb47SArd Biesheuvel case 0x7: 513a3cbbb47SArd Biesheuvel /* OSPKE enabled bit */ 514a3cbbb47SArd Biesheuvel leaf->ecx &= ~BIT(4); 515a3cbbb47SArd Biesheuvel if (native_read_cr4() & X86_CR4_PKE) 516a3cbbb47SArd Biesheuvel leaf->ecx |= BIT(4); 517a3cbbb47SArd Biesheuvel break; 518a3cbbb47SArd Biesheuvel case 0xB: 519a3cbbb47SArd Biesheuvel leaf_hv.subfn = 0; 520a3cbbb47SArd Biesheuvel snp_cpuid_hv(ghcb, ctxt, &leaf_hv); 521a3cbbb47SArd Biesheuvel 522a3cbbb47SArd Biesheuvel /* extended APIC ID */ 523a3cbbb47SArd Biesheuvel leaf->edx = leaf_hv.edx; 524a3cbbb47SArd Biesheuvel break; 525a3cbbb47SArd Biesheuvel case 0xD: { 526a3cbbb47SArd Biesheuvel bool compacted = false; 527a3cbbb47SArd Biesheuvel u64 xcr0 = 1, xss = 0; 528a3cbbb47SArd Biesheuvel u32 xsave_size; 529a3cbbb47SArd Biesheuvel 530a3cbbb47SArd Biesheuvel if (leaf->subfn != 0 && leaf->subfn != 1) 531a3cbbb47SArd Biesheuvel return 0; 532a3cbbb47SArd Biesheuvel 533a3cbbb47SArd Biesheuvel if (native_read_cr4() & X86_CR4_OSXSAVE) 534a3cbbb47SArd Biesheuvel xcr0 = xgetbv(XCR_XFEATURE_ENABLED_MASK); 535a3cbbb47SArd Biesheuvel if (leaf->subfn == 1) { 536a3cbbb47SArd Biesheuvel /* Get XSS value if XSAVES is enabled. */ 537a3cbbb47SArd Biesheuvel if (leaf->eax & BIT(3)) { 538a3cbbb47SArd Biesheuvel unsigned long lo, hi; 539a3cbbb47SArd Biesheuvel 540a3cbbb47SArd Biesheuvel asm volatile("rdmsr" : "=a" (lo), "=d" (hi) 541a3cbbb47SArd Biesheuvel : "c" (MSR_IA32_XSS)); 542a3cbbb47SArd Biesheuvel xss = (hi << 32) | lo; 543a3cbbb47SArd Biesheuvel } 544a3cbbb47SArd Biesheuvel 545a3cbbb47SArd Biesheuvel /* 546a3cbbb47SArd Biesheuvel * The PPR and APM aren't clear on what size should be 547a3cbbb47SArd Biesheuvel * encoded in 0xD:0x1:EBX when compaction is not enabled 548a3cbbb47SArd Biesheuvel * by either XSAVEC (feature bit 1) or XSAVES (feature 549a3cbbb47SArd Biesheuvel * bit 3) since SNP-capable hardware has these feature 550a3cbbb47SArd Biesheuvel * bits fixed as 1. KVM sets it to 0 in this case, but 551a3cbbb47SArd Biesheuvel * to avoid this becoming an issue it's safer to simply 552a3cbbb47SArd Biesheuvel * treat this as unsupported for SNP guests. 553a3cbbb47SArd Biesheuvel */ 554a3cbbb47SArd Biesheuvel if (!(leaf->eax & (BIT(1) | BIT(3)))) 555a3cbbb47SArd Biesheuvel return -EINVAL; 556a3cbbb47SArd Biesheuvel 557a3cbbb47SArd Biesheuvel compacted = true; 558a3cbbb47SArd Biesheuvel } 559a3cbbb47SArd Biesheuvel 560a3cbbb47SArd Biesheuvel xsave_size = snp_cpuid_calc_xsave_size(xcr0 | xss, compacted); 561a3cbbb47SArd Biesheuvel if (!xsave_size) 562a3cbbb47SArd Biesheuvel return -EINVAL; 563a3cbbb47SArd Biesheuvel 564a3cbbb47SArd Biesheuvel leaf->ebx = xsave_size; 565a3cbbb47SArd Biesheuvel } 566a3cbbb47SArd Biesheuvel break; 567a3cbbb47SArd Biesheuvel case 0x8000001E: 568a3cbbb47SArd Biesheuvel snp_cpuid_hv(ghcb, ctxt, &leaf_hv); 569a3cbbb47SArd Biesheuvel 570a3cbbb47SArd Biesheuvel /* extended APIC ID */ 571a3cbbb47SArd Biesheuvel leaf->eax = leaf_hv.eax; 572a3cbbb47SArd Biesheuvel /* compute ID */ 573a3cbbb47SArd Biesheuvel leaf->ebx = (leaf->ebx & GENMASK(31, 8)) | (leaf_hv.ebx & GENMASK(7, 0)); 574a3cbbb47SArd Biesheuvel /* node ID */ 575a3cbbb47SArd Biesheuvel leaf->ecx = (leaf->ecx & GENMASK(31, 8)) | (leaf_hv.ecx & GENMASK(7, 0)); 576a3cbbb47SArd Biesheuvel break; 577a3cbbb47SArd Biesheuvel default: 578a3cbbb47SArd Biesheuvel /* No fix-ups needed, use values as-is. */ 579a3cbbb47SArd Biesheuvel break; 580a3cbbb47SArd Biesheuvel } 581a3cbbb47SArd Biesheuvel 582a3cbbb47SArd Biesheuvel return 0; 583a3cbbb47SArd Biesheuvel } 584a3cbbb47SArd Biesheuvel 585a3cbbb47SArd Biesheuvel /* 586a3cbbb47SArd Biesheuvel * Returns -EOPNOTSUPP if feature not enabled. Any other non-zero return value 587a3cbbb47SArd Biesheuvel * should be treated as fatal by caller. 588a3cbbb47SArd Biesheuvel */ 589ed4d95d0SArd Biesheuvel int __head 590a3cbbb47SArd Biesheuvel snp_cpuid(struct ghcb *ghcb, struct es_em_ctxt *ctxt, struct cpuid_leaf *leaf) 591a3cbbb47SArd Biesheuvel { 592a3cbbb47SArd Biesheuvel const struct snp_cpuid_table *cpuid_table = snp_cpuid_get_table(); 593a3cbbb47SArd Biesheuvel 594a3cbbb47SArd Biesheuvel if (!cpuid_table->count) 595a3cbbb47SArd Biesheuvel return -EOPNOTSUPP; 596a3cbbb47SArd Biesheuvel 597a3cbbb47SArd Biesheuvel if (!snp_cpuid_get_validated_func(leaf)) { 598a3cbbb47SArd Biesheuvel /* 599a3cbbb47SArd Biesheuvel * Some hypervisors will avoid keeping track of CPUID entries 600a3cbbb47SArd Biesheuvel * where all values are zero, since they can be handled the 601a3cbbb47SArd Biesheuvel * same as out-of-range values (all-zero). This is useful here 602a3cbbb47SArd Biesheuvel * as well as it allows virtually all guest configurations to 603a3cbbb47SArd Biesheuvel * work using a single SNP CPUID table. 604a3cbbb47SArd Biesheuvel * 605a3cbbb47SArd Biesheuvel * To allow for this, there is a need to distinguish between 606a3cbbb47SArd Biesheuvel * out-of-range entries and in-range zero entries, since the 607a3cbbb47SArd Biesheuvel * CPUID table entries are only a template that may need to be 608a3cbbb47SArd Biesheuvel * augmented with additional values for things like 609a3cbbb47SArd Biesheuvel * CPU-specific information during post-processing. So if it's 610a3cbbb47SArd Biesheuvel * not in the table, set the values to zero. Then, if they are 611a3cbbb47SArd Biesheuvel * within a valid CPUID range, proceed with post-processing 612a3cbbb47SArd Biesheuvel * using zeros as the initial values. Otherwise, skip 613a3cbbb47SArd Biesheuvel * post-processing and just return zeros immediately. 614a3cbbb47SArd Biesheuvel */ 615a3cbbb47SArd Biesheuvel leaf->eax = leaf->ebx = leaf->ecx = leaf->edx = 0; 616a3cbbb47SArd Biesheuvel 617a3cbbb47SArd Biesheuvel /* Skip post-processing for out-of-range zero leafs. */ 618681e2901SArd Biesheuvel if (!(leaf->fn <= cpuid_std_range_max || 619681e2901SArd Biesheuvel (leaf->fn >= 0x40000000 && leaf->fn <= cpuid_hyp_range_max) || 620681e2901SArd Biesheuvel (leaf->fn >= 0x80000000 && leaf->fn <= cpuid_ext_range_max))) 621a3cbbb47SArd Biesheuvel return 0; 622a3cbbb47SArd Biesheuvel } 623a3cbbb47SArd Biesheuvel 624a3cbbb47SArd Biesheuvel return snp_cpuid_postprocess(ghcb, ctxt, leaf); 625a3cbbb47SArd Biesheuvel } 626a3cbbb47SArd Biesheuvel 627a3cbbb47SArd Biesheuvel /* 628a3cbbb47SArd Biesheuvel * Boot VC Handler - This is the first VC handler during boot, there is no GHCB 629a3cbbb47SArd Biesheuvel * page yet, so it only supports the MSR based communication with the 630a3cbbb47SArd Biesheuvel * hypervisor and only the CPUID exit-code. 631a3cbbb47SArd Biesheuvel */ 632a3cbbb47SArd Biesheuvel void __head do_vc_no_ghcb(struct pt_regs *regs, unsigned long exit_code) 633a3cbbb47SArd Biesheuvel { 634a3cbbb47SArd Biesheuvel unsigned int subfn = lower_bits(regs->cx, 32); 635a3cbbb47SArd Biesheuvel unsigned int fn = lower_bits(regs->ax, 32); 636a3cbbb47SArd Biesheuvel u16 opcode = *(unsigned short *)regs->ip; 637a3cbbb47SArd Biesheuvel struct cpuid_leaf leaf; 638a3cbbb47SArd Biesheuvel int ret; 639a3cbbb47SArd Biesheuvel 640a3cbbb47SArd Biesheuvel /* Only CPUID is supported via MSR protocol */ 641a3cbbb47SArd Biesheuvel if (exit_code != SVM_EXIT_CPUID) 642a3cbbb47SArd Biesheuvel goto fail; 643a3cbbb47SArd Biesheuvel 644a3cbbb47SArd Biesheuvel /* Is it really a CPUID insn? */ 645a3cbbb47SArd Biesheuvel if (opcode != 0xa20f) 646a3cbbb47SArd Biesheuvel goto fail; 647a3cbbb47SArd Biesheuvel 648a3cbbb47SArd Biesheuvel leaf.fn = fn; 649a3cbbb47SArd Biesheuvel leaf.subfn = subfn; 650a3cbbb47SArd Biesheuvel 651a3cbbb47SArd Biesheuvel ret = snp_cpuid(NULL, NULL, &leaf); 652a3cbbb47SArd Biesheuvel if (!ret) 653a3cbbb47SArd Biesheuvel goto cpuid_done; 654a3cbbb47SArd Biesheuvel 655a3cbbb47SArd Biesheuvel if (ret != -EOPNOTSUPP) 656a3cbbb47SArd Biesheuvel goto fail; 657a3cbbb47SArd Biesheuvel 658a3cbbb47SArd Biesheuvel if (__sev_cpuid_hv_msr(&leaf)) 659a3cbbb47SArd Biesheuvel goto fail; 660a3cbbb47SArd Biesheuvel 661a3cbbb47SArd Biesheuvel cpuid_done: 662a3cbbb47SArd Biesheuvel regs->ax = leaf.eax; 663a3cbbb47SArd Biesheuvel regs->bx = leaf.ebx; 664a3cbbb47SArd Biesheuvel regs->cx = leaf.ecx; 665a3cbbb47SArd Biesheuvel regs->dx = leaf.edx; 666a3cbbb47SArd Biesheuvel 667a3cbbb47SArd Biesheuvel /* 668a3cbbb47SArd Biesheuvel * This is a VC handler and the #VC is only raised when SEV-ES is 669a3cbbb47SArd Biesheuvel * active, which means SEV must be active too. Do sanity checks on the 670a3cbbb47SArd Biesheuvel * CPUID results to make sure the hypervisor does not trick the kernel 671a3cbbb47SArd Biesheuvel * into the no-sev path. This could map sensitive data unencrypted and 672a3cbbb47SArd Biesheuvel * make it accessible to the hypervisor. 673a3cbbb47SArd Biesheuvel * 674a3cbbb47SArd Biesheuvel * In particular, check for: 675a3cbbb47SArd Biesheuvel * - Availability of CPUID leaf 0x8000001f 676a3cbbb47SArd Biesheuvel * - SEV CPUID bit. 677a3cbbb47SArd Biesheuvel * 678a3cbbb47SArd Biesheuvel * The hypervisor might still report the wrong C-bit position, but this 679a3cbbb47SArd Biesheuvel * can't be checked here. 680a3cbbb47SArd Biesheuvel */ 681a3cbbb47SArd Biesheuvel 682a3cbbb47SArd Biesheuvel if (fn == 0x80000000 && (regs->ax < 0x8000001f)) 683a3cbbb47SArd Biesheuvel /* SEV leaf check */ 684a3cbbb47SArd Biesheuvel goto fail; 685a3cbbb47SArd Biesheuvel else if ((fn == 0x8000001f && !(regs->ax & BIT(1)))) 686a3cbbb47SArd Biesheuvel /* SEV bit */ 687a3cbbb47SArd Biesheuvel goto fail; 688a3cbbb47SArd Biesheuvel 689a3cbbb47SArd Biesheuvel /* Skip over the CPUID two-byte opcode */ 690a3cbbb47SArd Biesheuvel regs->ip += 2; 691a3cbbb47SArd Biesheuvel 692a3cbbb47SArd Biesheuvel return; 693a3cbbb47SArd Biesheuvel 694a3cbbb47SArd Biesheuvel fail: 695a3cbbb47SArd Biesheuvel /* Terminate the guest */ 696a3cbbb47SArd Biesheuvel sev_es_terminate(SEV_TERM_SET_GEN, GHCB_SEV_ES_GEN_REQ); 697a3cbbb47SArd Biesheuvel } 698a3cbbb47SArd Biesheuvel 699a3cbbb47SArd Biesheuvel struct cc_setup_data { 700a3cbbb47SArd Biesheuvel struct setup_data header; 701a3cbbb47SArd Biesheuvel u32 cc_blob_address; 702a3cbbb47SArd Biesheuvel }; 703a3cbbb47SArd Biesheuvel 704a3cbbb47SArd Biesheuvel /* 705a3cbbb47SArd Biesheuvel * Search for a Confidential Computing blob passed in as a setup_data entry 706a3cbbb47SArd Biesheuvel * via the Linux Boot Protocol. 707a3cbbb47SArd Biesheuvel */ 708a3cbbb47SArd Biesheuvel static __head 709a3cbbb47SArd Biesheuvel struct cc_blob_sev_info *find_cc_blob_setup_data(struct boot_params *bp) 710a3cbbb47SArd Biesheuvel { 711a3cbbb47SArd Biesheuvel struct cc_setup_data *sd = NULL; 712a3cbbb47SArd Biesheuvel struct setup_data *hdr; 713a3cbbb47SArd Biesheuvel 714a3cbbb47SArd Biesheuvel hdr = (struct setup_data *)bp->hdr.setup_data; 715a3cbbb47SArd Biesheuvel 716a3cbbb47SArd Biesheuvel while (hdr) { 717a3cbbb47SArd Biesheuvel if (hdr->type == SETUP_CC_BLOB) { 718a3cbbb47SArd Biesheuvel sd = (struct cc_setup_data *)hdr; 719a3cbbb47SArd Biesheuvel return (struct cc_blob_sev_info *)(unsigned long)sd->cc_blob_address; 720a3cbbb47SArd Biesheuvel } 721a3cbbb47SArd Biesheuvel hdr = (struct setup_data *)hdr->next; 722a3cbbb47SArd Biesheuvel } 723a3cbbb47SArd Biesheuvel 724a3cbbb47SArd Biesheuvel return NULL; 725a3cbbb47SArd Biesheuvel } 726a3cbbb47SArd Biesheuvel 727a3cbbb47SArd Biesheuvel /* 728a3cbbb47SArd Biesheuvel * Initialize the kernel's copy of the SNP CPUID table, and set up the 729a3cbbb47SArd Biesheuvel * pointer that will be used to access it. 730a3cbbb47SArd Biesheuvel * 731a3cbbb47SArd Biesheuvel * Maintaining a direct mapping of the SNP CPUID table used by firmware would 732a3cbbb47SArd Biesheuvel * be possible as an alternative, but the approach is brittle since the 733a3cbbb47SArd Biesheuvel * mapping needs to be updated in sync with all the changes to virtual memory 734a3cbbb47SArd Biesheuvel * layout and related mapping facilities throughout the boot process. 735a3cbbb47SArd Biesheuvel */ 736a3cbbb47SArd Biesheuvel static void __head setup_cpuid_table(const struct cc_blob_sev_info *cc_info) 737a3cbbb47SArd Biesheuvel { 738a3cbbb47SArd Biesheuvel const struct snp_cpuid_table *cpuid_table_fw, *cpuid_table; 739a3cbbb47SArd Biesheuvel int i; 740a3cbbb47SArd Biesheuvel 741a3cbbb47SArd Biesheuvel if (!cc_info || !cc_info->cpuid_phys || cc_info->cpuid_len < PAGE_SIZE) 742a3cbbb47SArd Biesheuvel sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_CPUID); 743a3cbbb47SArd Biesheuvel 744a3cbbb47SArd Biesheuvel cpuid_table_fw = (const struct snp_cpuid_table *)cc_info->cpuid_phys; 745a3cbbb47SArd Biesheuvel if (!cpuid_table_fw->count || cpuid_table_fw->count > SNP_CPUID_COUNT_MAX) 746a3cbbb47SArd Biesheuvel sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_CPUID); 747a3cbbb47SArd Biesheuvel 748a3cbbb47SArd Biesheuvel cpuid_table = snp_cpuid_get_table(); 749a3cbbb47SArd Biesheuvel memcpy((void *)cpuid_table, cpuid_table_fw, sizeof(*cpuid_table)); 750a3cbbb47SArd Biesheuvel 751a3cbbb47SArd Biesheuvel /* Initialize CPUID ranges for range-checking. */ 752a3cbbb47SArd Biesheuvel for (i = 0; i < cpuid_table->count; i++) { 753a3cbbb47SArd Biesheuvel const struct snp_cpuid_fn *fn = &cpuid_table->fn[i]; 754a3cbbb47SArd Biesheuvel 755a3cbbb47SArd Biesheuvel if (fn->eax_in == 0x0) 756681e2901SArd Biesheuvel cpuid_std_range_max = fn->eax; 757a3cbbb47SArd Biesheuvel else if (fn->eax_in == 0x40000000) 758681e2901SArd Biesheuvel cpuid_hyp_range_max = fn->eax; 759a3cbbb47SArd Biesheuvel else if (fn->eax_in == 0x80000000) 760681e2901SArd Biesheuvel cpuid_ext_range_max = fn->eax; 761a3cbbb47SArd Biesheuvel } 762a3cbbb47SArd Biesheuvel } 763a3cbbb47SArd Biesheuvel 764a3cbbb47SArd Biesheuvel static void __head svsm_pval_4k_page(unsigned long paddr, bool validate) 765a3cbbb47SArd Biesheuvel { 766a3cbbb47SArd Biesheuvel struct svsm_pvalidate_call *pc; 767a3cbbb47SArd Biesheuvel struct svsm_call call = {}; 768a3cbbb47SArd Biesheuvel unsigned long flags; 769a3cbbb47SArd Biesheuvel u64 pc_pa; 770a3cbbb47SArd Biesheuvel int ret; 771a3cbbb47SArd Biesheuvel 772a3cbbb47SArd Biesheuvel /* 773a3cbbb47SArd Biesheuvel * This can be called very early in the boot, use native functions in 774a3cbbb47SArd Biesheuvel * order to avoid paravirt issues. 775a3cbbb47SArd Biesheuvel */ 776a3cbbb47SArd Biesheuvel flags = native_local_irq_save(); 777a3cbbb47SArd Biesheuvel 778a3cbbb47SArd Biesheuvel call.caa = svsm_get_caa(); 779a3cbbb47SArd Biesheuvel 780a3cbbb47SArd Biesheuvel pc = (struct svsm_pvalidate_call *)call.caa->svsm_buffer; 781a3cbbb47SArd Biesheuvel pc_pa = svsm_get_caa_pa() + offsetof(struct svsm_ca, svsm_buffer); 782a3cbbb47SArd Biesheuvel 783a3cbbb47SArd Biesheuvel pc->num_entries = 1; 784a3cbbb47SArd Biesheuvel pc->cur_index = 0; 785a3cbbb47SArd Biesheuvel pc->entry[0].page_size = RMP_PG_SIZE_4K; 786a3cbbb47SArd Biesheuvel pc->entry[0].action = validate; 787a3cbbb47SArd Biesheuvel pc->entry[0].ignore_cf = 0; 788*3ee9cebdSTom Lendacky pc->entry[0].rsvd = 0; 789a3cbbb47SArd Biesheuvel pc->entry[0].pfn = paddr >> PAGE_SHIFT; 790a3cbbb47SArd Biesheuvel 791a3cbbb47SArd Biesheuvel /* Protocol 0, Call ID 1 */ 792a3cbbb47SArd Biesheuvel call.rax = SVSM_CORE_CALL(SVSM_CORE_PVALIDATE); 793a3cbbb47SArd Biesheuvel call.rcx = pc_pa; 794a3cbbb47SArd Biesheuvel 795a3cbbb47SArd Biesheuvel ret = svsm_perform_call_protocol(&call); 796a3cbbb47SArd Biesheuvel if (ret) 797a3cbbb47SArd Biesheuvel sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_PVALIDATE); 798a3cbbb47SArd Biesheuvel 799a3cbbb47SArd Biesheuvel native_local_irq_restore(flags); 800a3cbbb47SArd Biesheuvel } 801a3cbbb47SArd Biesheuvel 802a3cbbb47SArd Biesheuvel static void __head pvalidate_4k_page(unsigned long vaddr, unsigned long paddr, 803a3cbbb47SArd Biesheuvel bool validate) 804a3cbbb47SArd Biesheuvel { 805a3cbbb47SArd Biesheuvel int ret; 806a3cbbb47SArd Biesheuvel 807681e2901SArd Biesheuvel if (snp_vmpl) { 808a3cbbb47SArd Biesheuvel svsm_pval_4k_page(paddr, validate); 809a3cbbb47SArd Biesheuvel } else { 810a3cbbb47SArd Biesheuvel ret = pvalidate(vaddr, RMP_PG_SIZE_4K, validate); 811a3cbbb47SArd Biesheuvel if (ret) 812a3cbbb47SArd Biesheuvel sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_PVALIDATE); 813a3cbbb47SArd Biesheuvel } 814a3cbbb47SArd Biesheuvel } 815a3cbbb47SArd Biesheuvel 816a3cbbb47SArd Biesheuvel /* 817a3cbbb47SArd Biesheuvel * Maintain the GPA of the SVSM Calling Area (CA) in order to utilize the SVSM 818a3cbbb47SArd Biesheuvel * services needed when not running in VMPL0. 819a3cbbb47SArd Biesheuvel */ 820a3cbbb47SArd Biesheuvel static bool __head svsm_setup_ca(const struct cc_blob_sev_info *cc_info) 821a3cbbb47SArd Biesheuvel { 822a3cbbb47SArd Biesheuvel struct snp_secrets_page *secrets_page; 823a3cbbb47SArd Biesheuvel struct snp_cpuid_table *cpuid_table; 824a3cbbb47SArd Biesheuvel unsigned int i; 825a3cbbb47SArd Biesheuvel u64 caa; 826a3cbbb47SArd Biesheuvel 827a3cbbb47SArd Biesheuvel BUILD_BUG_ON(sizeof(*secrets_page) != PAGE_SIZE); 828a3cbbb47SArd Biesheuvel 829a3cbbb47SArd Biesheuvel /* 830a3cbbb47SArd Biesheuvel * Check if running at VMPL0. 831a3cbbb47SArd Biesheuvel * 832a3cbbb47SArd Biesheuvel * Use RMPADJUST (see the rmpadjust() function for a description of what 833a3cbbb47SArd Biesheuvel * the instruction does) to update the VMPL1 permissions of a page. If 834a3cbbb47SArd Biesheuvel * the guest is running at VMPL0, this will succeed and implies there is 835a3cbbb47SArd Biesheuvel * no SVSM. If the guest is running at any other VMPL, this will fail. 836a3cbbb47SArd Biesheuvel * Linux SNP guests only ever run at a single VMPL level so permission mask 837a3cbbb47SArd Biesheuvel * changes of a lesser-privileged VMPL are a don't-care. 838a3cbbb47SArd Biesheuvel * 839a3cbbb47SArd Biesheuvel * Use a rip-relative reference to obtain the proper address, since this 840a3cbbb47SArd Biesheuvel * routine is running identity mapped when called, both by the decompressor 841a3cbbb47SArd Biesheuvel * code and the early kernel code. 842a3cbbb47SArd Biesheuvel */ 843a3cbbb47SArd Biesheuvel if (!rmpadjust((unsigned long)rip_rel_ptr(&boot_ghcb_page), RMP_PG_SIZE_4K, 1)) 844a3cbbb47SArd Biesheuvel return false; 845a3cbbb47SArd Biesheuvel 846a3cbbb47SArd Biesheuvel /* 847a3cbbb47SArd Biesheuvel * Not running at VMPL0, ensure everything has been properly supplied 848a3cbbb47SArd Biesheuvel * for running under an SVSM. 849a3cbbb47SArd Biesheuvel */ 850a3cbbb47SArd Biesheuvel if (!cc_info || !cc_info->secrets_phys || cc_info->secrets_len != PAGE_SIZE) 851a3cbbb47SArd Biesheuvel sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_SECRETS_PAGE); 852a3cbbb47SArd Biesheuvel 853a3cbbb47SArd Biesheuvel secrets_page = (struct snp_secrets_page *)cc_info->secrets_phys; 854a3cbbb47SArd Biesheuvel if (!secrets_page->svsm_size) 855a3cbbb47SArd Biesheuvel sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_NO_SVSM); 856a3cbbb47SArd Biesheuvel 857a3cbbb47SArd Biesheuvel if (!secrets_page->svsm_guest_vmpl) 858a3cbbb47SArd Biesheuvel sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_SVSM_VMPL0); 859a3cbbb47SArd Biesheuvel 860681e2901SArd Biesheuvel snp_vmpl = secrets_page->svsm_guest_vmpl; 861a3cbbb47SArd Biesheuvel 862a3cbbb47SArd Biesheuvel caa = secrets_page->svsm_caa; 863a3cbbb47SArd Biesheuvel 864a3cbbb47SArd Biesheuvel /* 865a3cbbb47SArd Biesheuvel * An open-coded PAGE_ALIGNED() in order to avoid including 866a3cbbb47SArd Biesheuvel * kernel-proper headers into the decompressor. 867a3cbbb47SArd Biesheuvel */ 868a3cbbb47SArd Biesheuvel if (caa & (PAGE_SIZE - 1)) 869a3cbbb47SArd Biesheuvel sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_SVSM_CAA); 870a3cbbb47SArd Biesheuvel 871a3cbbb47SArd Biesheuvel /* 872a3cbbb47SArd Biesheuvel * The CA is identity mapped when this routine is called, both by the 873a3cbbb47SArd Biesheuvel * decompressor code and the early kernel code. 874a3cbbb47SArd Biesheuvel */ 875681e2901SArd Biesheuvel boot_svsm_caa = (struct svsm_ca *)caa; 876681e2901SArd Biesheuvel boot_svsm_caa_pa = caa; 877a3cbbb47SArd Biesheuvel 878a3cbbb47SArd Biesheuvel /* Advertise the SVSM presence via CPUID. */ 879a3cbbb47SArd Biesheuvel cpuid_table = (struct snp_cpuid_table *)snp_cpuid_get_table(); 880a3cbbb47SArd Biesheuvel for (i = 0; i < cpuid_table->count; i++) { 881a3cbbb47SArd Biesheuvel struct snp_cpuid_fn *fn = &cpuid_table->fn[i]; 882a3cbbb47SArd Biesheuvel 883a3cbbb47SArd Biesheuvel if (fn->eax_in == 0x8000001f) 884a3cbbb47SArd Biesheuvel fn->eax |= BIT(28); 885a3cbbb47SArd Biesheuvel } 886a3cbbb47SArd Biesheuvel 887a3cbbb47SArd Biesheuvel return true; 888a3cbbb47SArd Biesheuvel } 889