diff mbox

[kvm-unit-tests] x86/nVMX: Test L1 interception of #BP and #OF in the L2 guest

Message ID 1481744321-31324-1-git-send-email-jmattson@google.com (mailing list archive)
State New, archived
Headers show

Commit Message

Jim Mattson Dec. 14, 2016, 7:38 p.m. UTC
Int3 (#BP) and INTO (#OF) are unusual, in that they are reported as
"software exception" rather than "hardware exception" in the VM-exit
interruption information field of the VMCS.

Signed-off-by: Jim Mattson <jmattson@google.com>
---
 lib/x86/processor.h |  5 ++++
 x86/vmx.h           | 19 ++++++++++++++
 x86/vmx_tests.c     | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 100 insertions(+)
diff mbox

Patch

diff --git a/lib/x86/processor.h b/lib/x86/processor.h
index ee7f180..895d992 100644
--- a/lib/x86/processor.h
+++ b/lib/x86/processor.h
@@ -41,6 +41,11 @@ 
 #define X86_IA32_EFER          0xc0000080
 #define X86_EFER_LMA           (1UL << 8)
 
+struct far_pointer32 {
+	u32 offset;
+	u16 selector;
+} __attribute__((packed));
+
 struct descriptor_table_ptr {
     u16 limit;
     ulong base;
diff --git a/x86/vmx.h b/x86/vmx.h
index 432ffa6..a2bacd3 100644
--- a/x86/vmx.h
+++ b/x86/vmx.h
@@ -377,6 +377,25 @@  enum Ctrl1 {
 	CPU_RDRAND		= 1ul << 11,
 };
 
+enum Intr_type {
+	VMX_INTR_TYPE_EXT_INTR = 0,
+	VMX_INTR_TYPE_NMI_INTR = 2,
+	VMX_INTR_TYPE_HARD_EXCEPTION = 3,
+	VMX_INTR_TYPE_SOFT_INTR = 4,
+	VMX_INTR_TYPE_SOFT_EXCEPTION = 6,
+};
+
+/*
+ * Interruption-information format
+ */
+#define INTR_INFO_VECTOR_MASK           0xff            /* 7:0 */
+#define INTR_INFO_INTR_TYPE_MASK        0x700           /* 10:8 */
+#define INTR_INFO_DELIVER_CODE_MASK     0x800           /* 11 */
+#define INTR_INFO_UNBLOCK_NMI_MASK      0x1000          /* 12 */
+#define INTR_INFO_VALID_MASK            0x80000000      /* 31 */
+
+#define INTR_INFO_INTR_TYPE_SHIFT       8
+
 #define SAVE_GPR				\
 	"xchg %rax, regs\n\t"			\
 	"xchg %rbx, regs+0x8\n\t"		\
diff --git a/x86/vmx_tests.c b/x86/vmx_tests.c
index 5aba999..5fd9570 100644
--- a/x86/vmx_tests.c
+++ b/x86/vmx_tests.c
@@ -1742,6 +1742,80 @@  static int disable_rdtscp_exit_handler(void)
 	return VMX_TEST_VMEXIT;
 }
 
+int int3_init()
+{
+	vmcs_write(EXC_BITMAP, ~0u);
+	return VMX_TEST_START;
+}
+
+void int3_guest_main()
+{
+	asm volatile ("int3");
+}
+
+int int3_exit_handler()
+{
+	u32 reason = vmcs_read(EXI_REASON);
+	u32 intr_info = vmcs_read(EXI_INTR_INFO);
+
+	report("L1 intercepts #BP", reason == VMX_EXC_NMI &&
+	       (intr_info & INTR_INFO_VALID_MASK) &&
+	       (intr_info & INTR_INFO_VECTOR_MASK) == BP_VECTOR &&
+	       ((intr_info & INTR_INFO_INTR_TYPE_MASK) >>
+		INTR_INFO_INTR_TYPE_SHIFT) == VMX_INTR_TYPE_SOFT_EXCEPTION);
+
+	return VMX_TEST_VMEXIT;
+}
+
+int into_init()
+{
+	vmcs_write(EXC_BITMAP, ~0u);
+	return VMX_TEST_START;
+}
+
+void into_guest_main()
+{
+	struct far_pointer32 fp = {
+		.offset = (uintptr_t)&&into,
+		.selector = KERNEL_CS32,
+	};
+	register uintptr_t rsp asm("rsp");
+
+	if (fp.offset != (uintptr_t)&&into) {
+		printf("Code address too high.\n");
+		return;
+	}
+	if ((u32)rsp != rsp) {
+		printf("Stack address too high.\n");
+		return;
+	}
+
+	asm goto ("lcall *%0" : : "m" (fp) : "rax" : into);
+	return;
+into:
+	asm volatile (".code32;"
+		      "movl $0x7fffffff, %eax;"
+		      "addl %eax, %eax;"
+		      "into;"
+		      "lret;"
+		      ".code64");
+	__builtin_unreachable();
+}
+
+int into_exit_handler()
+{
+	u32 reason = vmcs_read(EXI_REASON);
+	u32 intr_info = vmcs_read(EXI_INTR_INFO);
+
+	report("L1 intercepts #OF", reason == VMX_EXC_NMI &&
+	       (intr_info & INTR_INFO_VALID_MASK) &&
+	       (intr_info & INTR_INFO_VECTOR_MASK) == OF_VECTOR &&
+	       ((intr_info & INTR_INFO_INTR_TYPE_MASK) >>
+		INTR_INFO_INTR_TYPE_SHIFT) == VMX_INTR_TYPE_SOFT_EXCEPTION);
+
+	return VMX_TEST_VMEXIT;
+}
+
 /* name/init/guest_main/exit_handler/syscall_handler/guest_regs */
 struct vmx_test vmx_tests[] = {
 	{ "null", NULL, basic_guest_main, basic_exit_handler, NULL, {0} },
@@ -1769,5 +1843,7 @@  struct vmx_test vmx_tests[] = {
 	{ "vmmcall", vmmcall_init, vmmcall_main, vmmcall_exit_handler, NULL, {0} },
 	{ "disable RDTSCP", disable_rdtscp_init, disable_rdtscp_main,
 		disable_rdtscp_exit_handler, NULL, {0} },
+	{ "int3", int3_init, int3_guest_main, int3_exit_handler, NULL, {0} },
+	{ "into", into_init, into_guest_main, into_exit_handler, NULL, {0} },
 	{ NULL, NULL, NULL, NULL, NULL, {0} },
 };