Skip to content

Commit c141fa2

Browse files
kirylhansendc
authored andcommitted
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
1 parent ae87f60 commit c141fa2

File tree

1 file changed

+57
-1
lines changed

1 file changed

+57
-1
lines changed

arch/x86/coco/tdx/tdx.c

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,48 @@ static bool write_msr(struct pt_regs *regs)
180180
return !__tdx_hypercall(&args, 0);
181181
}
182182

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+
183225
void tdx_get_ve_info(struct ve_info *ve)
184226
{
185227
struct tdx_module_output out;
@@ -210,6 +252,18 @@ void tdx_get_ve_info(struct ve_info *ve)
210252
ve->instr_info = upper_32_bits(out.r10);
211253
}
212254

255+
/* Handle the user initiated #VE */
256+
static bool virt_exception_user(struct pt_regs *regs, struct ve_info *ve)
257+
{
258+
switch (ve->exit_reason) {
259+
case EXIT_REASON_CPUID:
260+
return handle_cpuid(regs);
261+
default:
262+
pr_warn("Unexpected #VE: %lld\n", ve->exit_reason);
263+
return false;
264+
}
265+
}
266+
213267
/* Handle the kernel #VE */
214268
static bool virt_exception_kernel(struct pt_regs *regs, struct ve_info *ve)
215269
{
@@ -220,6 +274,8 @@ static bool virt_exception_kernel(struct pt_regs *regs, struct ve_info *ve)
220274
return read_msr(regs);
221275
case EXIT_REASON_MSR_WRITE:
222276
return write_msr(regs);
277+
case EXIT_REASON_CPUID:
278+
return handle_cpuid(regs);
223279
default:
224280
pr_warn("Unexpected #VE: %lld\n", ve->exit_reason);
225281
return false;
@@ -231,7 +287,7 @@ bool tdx_handle_virt_exception(struct pt_regs *regs, struct ve_info *ve)
231287
bool ret;
232288

233289
if (user_mode(regs))
234-
ret = false;
290+
ret = virt_exception_user(regs, ve);
235291
else
236292
ret = virt_exception_kernel(regs, ve);
237293

0 commit comments

Comments
 (0)