[kvm-unit-tests RFC v2 03/18] x86 TDX: Add #VE handler

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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





[Index of Archives]     [KVM ARM]     [KVM ia64]     [KVM ppc]     [Virtualization Tools]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite Questions]     [Linux Kernel]     [Linux SCSI]     [XFree86]

  Powered by Linux