From: Zhenzhong Duan <zhenzhong.duan@xxxxxxxxx> Some instructions execution trigger #VE and are simulated in #VE handler. Add such a handler, currently support simulation of IO and MSR read/write, cpuid and hlt instructions. Signed-off-by: Zhenzhong Duan <zhenzhong.duan@xxxxxxxxx> Reviewed-by: Yu Zhang <yu.c.zhang@xxxxxxxxx> Link: https://lore.kernel.org/r/20220303071907.650203-3-zhenzhong.duan@xxxxxxxxx Co-developed-by: Qian Wen <qian.wen@xxxxxxxxx> Signed-off-by: Qian Wen <qian.wen@xxxxxxxxx> --- lib/x86/desc.c | 6 +++- lib/x86/tdx.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++++- lib/x86/tdx.h | 1 + 3 files changed, 85 insertions(+), 2 deletions(-) diff --git a/lib/x86/desc.c b/lib/x86/desc.c index d054899c..5b41549e 100644 --- a/lib/x86/desc.c +++ b/lib/x86/desc.c @@ -249,6 +249,7 @@ EX(mf, 16); EX_E(ac, 17); EX(mc, 18); EX(xm, 19); +EX(ve, 20); EX_E(cp, 21); asm (".pushsection .text \n\t" @@ -295,6 +296,7 @@ static void *idt_handlers[32] = { [17] = &ac_fault, [18] = &mc_fault, [19] = &xm_fault, + [20] = &ve_fault, [21] = &cp_fault, }; @@ -312,7 +314,9 @@ void setup_idt(void) continue; set_idt_entry(i, idt_handlers[i], 0); - handle_exception(i, check_exception_table); + + if (!exception_handlers[i]) + handle_exception(i, check_exception_table); } } diff --git a/lib/x86/tdx.c b/lib/x86/tdx.c index a01bfcbb..d10e02b9 100644 --- a/lib/x86/tdx.c +++ b/lib/x86/tdx.c @@ -117,7 +117,8 @@ static int handle_halt(struct ex_regs *regs, struct ve_info *ve) * pending, without hanging/breaking the guest. */ if (__tdx_hypercall(&args)) - return -EIO; + /* Bypass failed hlt is better than hang */ + printf("WARNING: HLT instruction emulation failed\n"); return ve_instr_len(ve); } @@ -302,11 +303,88 @@ done: return !!tdx_guest; } +static bool tdx_get_ve_info(struct ve_info *ve) +{ + struct tdx_module_args args = {}; + u64 ret; + + if (!ve) + return false; + + /* + * NMIs and machine checks are suppressed. Before this point any + * #VE is fatal. After this point (TDGETVEINFO call), NMIs and + * additional #VEs are permitted (but it is expected not to + * happen unless kernel panics). + */ + ret = __tdcall_ret(TDG_VP_VEINFO_GET, &args); + if (ret) + return false; + + ve->exit_reason = args.rcx; + ve->exit_qual = args.rdx; + ve->gla = args.r8; + ve->gpa = args.r9; + ve->instr_len = args.r10 & UINT_MAX; + ve->instr_info = args.r10 >> 32; + + return true; +} + +static bool tdx_handle_virt_exception(struct ex_regs *regs, + struct ve_info *ve) +{ + int insn_len = -EIO; + + switch (ve->exit_reason) { + case EXIT_REASON_HLT: + insn_len = handle_halt(regs, ve); + break; + case EXIT_REASON_MSR_READ: + insn_len = read_msr(regs, ve); + break; + case EXIT_REASON_MSR_WRITE: + insn_len = write_msr(regs, ve); + break; + case EXIT_REASON_CPUID: + insn_len = handle_cpuid(regs, ve); + break; + case EXIT_REASON_IO_INSTRUCTION: + insn_len = handle_io(regs, ve); + break; + default: + printf("WARNING: Unexpected #VE: %ld\n", ve->exit_reason); + return false; + } + if (insn_len < 0) + return false; + + /* After successful #VE handling, move the IP */ + regs->rip += insn_len; + + return true; +} + +/* #VE exception handler. */ +static void tdx_handle_ve(struct ex_regs *regs) +{ + struct ve_info ve; + + if (!tdx_get_ve_info(&ve)) { + printf("tdx_get_ve_info failed\n"); + return; + } + + tdx_handle_virt_exception(regs, &ve); +} + efi_status_t setup_tdx(void) { if (!is_tdx_guest()) return EFI_UNSUPPORTED; + handle_exception(20, tdx_handle_ve); + /* The printf can work here. Since TDVF default exception handler * can handle the #VE caused by IO read/write during printf() before * finalizing configuration of the unit test's #VE handler. diff --git a/lib/x86/tdx.h b/lib/x86/tdx.h index 45350b70..fe0a4d81 100644 --- a/lib/x86/tdx.h +++ b/lib/x86/tdx.h @@ -26,6 +26,7 @@ /* TDX module Call Leaf IDs */ #define TDG_VP_VMCALL 0 +#define TDG_VP_VEINFO_GET 3 /* * Bitmasks of exposed registers (with VMM). -- 2.25.1