// SPDX-License-Identifier: GPL-2.0 /* Copyright (C) 2021-2022 Intel Corporation */ #undef pr_fmt #define pr_fmt(fmt) "tdx: " fmt #include #include #include /* TDX module Call Leaf IDs */ #define TDX_GET_INFO 1 #define TDX_GET_VEINFO 3 /* * Wrapper for standard use of __tdx_hypercall with no output aside from * return code. */ static inline u64 _tdx_hypercall(u64 fn, u64 r12, u64 r13, u64 r14, u64 r15) { struct tdx_hypercall_args args = { .r10 = TDX_HYPERCALL_STANDARD, .r11 = fn, .r12 = r12, .r13 = r13, .r14 = r14, .r15 = r15, }; return __tdx_hypercall(&args, 0); } /* Called from __tdx_hypercall() for unrecoverable failure */ void __tdx_hypercall_failed(void) { panic("TDVMCALL failed. TDX module bug?"); } /* * Used for TDX guests to make calls directly to the TD module. This * should only be used for calls that have no legitimate reason to fail * or where the kernel can not survive the call failing. */ static inline void tdx_module_call(u64 fn, u64 rcx, u64 rdx, u64 r8, u64 r9, struct tdx_module_output *out) { if (__tdx_module_call(fn, rcx, rdx, r8, r9, out)) panic("TDCALL %lld failed (Buggy TDX module!)\n", fn); } static u64 get_cc_mask(void) { struct tdx_module_output out; unsigned int gpa_width; /* * TDINFO TDX module call is used to get the TD execution environment * information like GPA width, number of available vcpus, debug mode * information, etc. More details about the ABI can be found in TDX * Guest-Host-Communication Interface (GHCI), section 2.4.2 TDCALL * [TDG.VP.INFO]. * * The GPA width that comes out of this call is critical. TDX guests * can not meaningfully run without it. */ tdx_module_call(TDX_GET_INFO, 0, 0, 0, 0, &out); gpa_width = out.rcx & GENMASK(5, 0); /* * The highest bit of a guest physical address is the "sharing" bit. * Set it for shared pages and clear it for private pages. */ return BIT_ULL(gpa_width - 1); } void tdx_get_ve_info(struct ve_info *ve) { struct tdx_module_output out; /* * Called during #VE handling to retrieve the #VE info from the * TDX module. * * This has to be called early in #VE handling. A "nested" #VE which * occurs before this will raise a #DF and is not recoverable. * * The call retrieves the #VE info from the TDX module, which also * clears the "#VE valid" flag. This must be done before anything else * because any #VE that occurs while the valid flag is set will lead to * #DF. * * Note, the TDX module treats virtual NMIs as inhibited if the #VE * valid flag is set. It means that NMI=>#VE will not result in a #DF. */ tdx_module_call(TDX_GET_VEINFO, 0, 0, 0, 0, &out); /* Transfer the output parameters */ ve->exit_reason = out.rcx; ve->exit_qual = out.rdx; ve->gla = out.r8; ve->gpa = out.r9; ve->instr_len = lower_32_bits(out.r10); ve->instr_info = upper_32_bits(out.r10); } bool tdx_handle_virt_exception(struct pt_regs *regs, struct ve_info *ve) { pr_warn("Unexpected #VE: %lld\n", ve->exit_reason); return false; } void __init tdx_early_init(void) { u64 cc_mask; u32 eax, sig[3]; cpuid_count(TDX_CPUID_LEAF_ID, 0, &eax, &sig[0], &sig[2], &sig[1]); if (memcmp(TDX_IDENT, sig, sizeof(sig))) return; setup_force_cpu_cap(X86_FEATURE_TDX_GUEST); cc_set_vendor(CC_VENDOR_INTEL); cc_mask = get_cc_mask(); cc_set_mask(cc_mask); /* * All bits above GPA width are reserved and kernel treats shared bit * as flag, not as part of physical address. * * Adjust physical mask to only cover valid GPA bits. */ physical_mask &= cc_mask - 1; pr_info("Guest detected\n"); }