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

x86/tdx: Handle CPUID via #VE

In TDX guests, most CPUID leaf/sub-leaf combinations are virtualized
by the TDX module while some trigger #VE.

Implement the #VE handling for EXIT_REASON_CPUID by handing it through
the hypercall, which in turn lets the TDX module handle it by invoking
the host VMM.

More details on CPUID Virtualization can be found in the TDX module
specification, the section titled "CPUID Virtualization".

Note that VMM that handles the hypercall is not trusted. It can return
data that may steer the guest kernel in wrong direct. Only allow VMM
to control range reserved for hypervisor communication.

Return all-zeros for any CPUID outside the hypervisor range. It matches
CPU behaviour for non-supported leaf.

Co-developed-by: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>
Signed-off-by: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>
Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Signed-off-by: Dave Hansen <dave.hansen@linux.intel.com>
Reviewed-by: Andi Kleen <ak@linux.intel.com>
Reviewed-by: Tony Luck <tony.luck@intel.com>
Reviewed-by: Dave Hansen <dave.hansen@linux.intel.com>
Reviewed-by: Thomas Gleixner <tglx@linutronix.de>
Link: https://lkml.kernel.org/r/20220405232939.73860-11-kirill.shutemov@linux.intel.com

authored by

Kirill A. Shutemov and committed by
Dave Hansen
c141fa2c ae87f609

+57 -1
+57 -1
arch/x86/coco/tdx/tdx.c
··· 180 180 return !__tdx_hypercall(&args, 0); 181 181 } 182 182 183 + static bool handle_cpuid(struct pt_regs *regs) 184 + { 185 + struct tdx_hypercall_args args = { 186 + .r10 = TDX_HYPERCALL_STANDARD, 187 + .r11 = hcall_func(EXIT_REASON_CPUID), 188 + .r12 = regs->ax, 189 + .r13 = regs->cx, 190 + }; 191 + 192 + /* 193 + * Only allow VMM to control range reserved for hypervisor 194 + * communication. 195 + * 196 + * Return all-zeros for any CPUID outside the range. It matches CPU 197 + * behaviour for non-supported leaf. 198 + */ 199 + if (regs->ax < 0x40000000 || regs->ax > 0x4FFFFFFF) { 200 + regs->ax = regs->bx = regs->cx = regs->dx = 0; 201 + return true; 202 + } 203 + 204 + /* 205 + * Emulate the CPUID instruction via a hypercall. More info about 206 + * ABI can be found in TDX Guest-Host-Communication Interface 207 + * (GHCI), section titled "VP.VMCALL<Instruction.CPUID>". 208 + */ 209 + if (__tdx_hypercall(&args, TDX_HCALL_HAS_OUTPUT)) 210 + return false; 211 + 212 + /* 213 + * As per TDX GHCI CPUID ABI, r12-r15 registers contain contents of 214 + * EAX, EBX, ECX, EDX registers after the CPUID instruction execution. 215 + * So copy the register contents back to pt_regs. 216 + */ 217 + regs->ax = args.r12; 218 + regs->bx = args.r13; 219 + regs->cx = args.r14; 220 + regs->dx = args.r15; 221 + 222 + return true; 223 + } 224 + 183 225 void tdx_get_ve_info(struct ve_info *ve) 184 226 { 185 227 struct tdx_module_output out; ··· 252 210 ve->instr_info = upper_32_bits(out.r10); 253 211 } 254 212 213 + /* Handle the user initiated #VE */ 214 + static bool virt_exception_user(struct pt_regs *regs, struct ve_info *ve) 215 + { 216 + switch (ve->exit_reason) { 217 + case EXIT_REASON_CPUID: 218 + return handle_cpuid(regs); 219 + default: 220 + pr_warn("Unexpected #VE: %lld\n", ve->exit_reason); 221 + return false; 222 + } 223 + } 224 + 255 225 /* Handle the kernel #VE */ 256 226 static bool virt_exception_kernel(struct pt_regs *regs, struct ve_info *ve) 257 227 { ··· 274 220 return read_msr(regs); 275 221 case EXIT_REASON_MSR_WRITE: 276 222 return write_msr(regs); 223 + case EXIT_REASON_CPUID: 224 + return handle_cpuid(regs); 277 225 default: 278 226 pr_warn("Unexpected #VE: %lld\n", ve->exit_reason); 279 227 return false; ··· 287 231 bool ret; 288 232 289 233 if (user_mode(regs)) 290 - ret = false; 234 + ret = virt_exception_user(regs, ve); 291 235 else 292 236 ret = virt_exception_kernel(regs, ve); 293 237