[PATCH 01/13] VMX: Add test cases around interrupt injection and halting

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

 



From: Jan Kiszka <jan.kiszka@xxxxxxxxxxx>

This checks for interrupt delivery to L2, unintercepted hlt in L2 and
explicit L2 suspension via the activity state HLT. All tests are
performed both with direct interrupt injection and external interrupt
interception.

Signed-off-by: Jan Kiszka <jan.kiszka@xxxxxxxxxxx>
---
 x86/vmx.c       |   3 +-
 x86/vmx.h       |   3 ++
 x86/vmx_tests.c | 144 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 148 insertions(+), 2 deletions(-)

diff --git a/x86/vmx.c b/x86/vmx.c
index fe950e6..a475aec 100644
--- a/x86/vmx.c
+++ b/x86/vmx.c
@@ -457,7 +457,7 @@ static void init_vmcs_guest(void)
 	vmcs_write(GUEST_RFLAGS, 0x2);
 
 	/* 26.3.1.5 */
-	vmcs_write(GUEST_ACTV_STATE, 0);
+	vmcs_write(GUEST_ACTV_STATE, ACTV_ACTIVE);
 	vmcs_write(GUEST_INTR_STATE, 0);
 }
 
@@ -482,7 +482,6 @@ static int init_vmcs(struct vmcs **vmcs)
 	ctrl_pin |= PIN_EXTINT | PIN_NMI | PIN_VIRT_NMI;
 	ctrl_exit = EXI_LOAD_EFER | EXI_HOST_64;
 	ctrl_enter = (ENT_LOAD_EFER | ENT_GUEST_64);
-	ctrl_cpu[0] |= CPU_HLT;
 	/* DIsable IO instruction VMEXIT now */
 	ctrl_cpu[0] &= (~(CPU_IO | CPU_IO_BITMAP));
 	ctrl_cpu[1] = 0;
diff --git a/x86/vmx.h b/x86/vmx.h
index bc8c86f..3867793 100644
--- a/x86/vmx.h
+++ b/x86/vmx.h
@@ -500,6 +500,9 @@ enum Ctrl1 {
 #define INVEPT_SINGLE		1
 #define INVEPT_GLOBAL		2
 
+#define ACTV_ACTIVE		0
+#define ACTV_HLT		1
+
 extern struct regs regs;
 
 extern union vmx_basic basic;
diff --git a/x86/vmx_tests.c b/x86/vmx_tests.c
index bec34c4..70efb50 100644
--- a/x86/vmx_tests.c
+++ b/x86/vmx_tests.c
@@ -9,6 +9,8 @@
 #include "vm.h"
 #include "io.h"
 #include "fwcfg.h"
+#include "isr.h"
+#include "apic.h"
 
 u64 ia32_pat;
 u64 ia32_efer;
@@ -1117,6 +1119,146 @@ static int ept_exit_handler()
 	return VMX_TEST_VMEXIT;
 }
 
+#define TIMER_VECTOR	222
+
+static volatile bool timer_fired;
+
+static void timer_isr(isr_regs_t *regs)
+{
+	timer_fired = true;
+	apic_write(APIC_EOI, 0);
+}
+
+static int interrupt_init(struct vmcs *vmcs)
+{
+	msr_bmp_init();
+	vmcs_write(PIN_CONTROLS, vmcs_read(PIN_CONTROLS) & ~PIN_EXTINT);
+	handle_irq(TIMER_VECTOR, timer_isr);
+	return VMX_TEST_START;
+}
+
+static void interrupt_main(void)
+{
+	long long start, loops;
+
+	set_stage(0);
+
+	apic_write(APIC_LVTT, TIMER_VECTOR);
+	irq_enable();
+
+	apic_write(APIC_TMICT, 1);
+	for (loops = 0; loops < 10000000 && !timer_fired; loops++)
+		asm volatile ("nop");
+	report("direct interrupt while running guest", timer_fired);
+
+	apic_write(APIC_TMICT, 0);
+	irq_disable();
+	vmcall();
+	timer_fired = false;
+	apic_write(APIC_TMICT, 1);
+	for (loops = 0; loops < 10000000 && !timer_fired; loops++)
+		asm volatile ("nop");
+	report("intercepted interrupt while running guest", timer_fired);
+
+	irq_enable();
+	apic_write(APIC_TMICT, 0);
+	irq_disable();
+	vmcall();
+	timer_fired = false;
+	start = rdtsc();
+	apic_write(APIC_TMICT, 1000000);
+
+	asm volatile ("sti; hlt");
+
+	report("direct interrupt + hlt",
+	       rdtsc() - start > 1000000 && timer_fired);
+
+	apic_write(APIC_TMICT, 0);
+	irq_disable();
+	vmcall();
+	timer_fired = false;
+	start = rdtsc();
+	apic_write(APIC_TMICT, 1000000);
+
+	asm volatile ("sti; hlt");
+
+	report("intercepted interrupt + hlt",
+	       rdtsc() - start > 10000 && timer_fired);
+
+	apic_write(APIC_TMICT, 0);
+	irq_disable();
+	vmcall();
+	timer_fired = false;
+	start = rdtsc();
+	apic_write(APIC_TMICT, 1000000);
+
+	irq_enable();
+	asm volatile ("nop");
+	vmcall();
+
+	report("direct interrupt + activity state hlt",
+	       rdtsc() - start > 10000 && timer_fired);
+
+	apic_write(APIC_TMICT, 0);
+	irq_disable();
+	vmcall();
+	timer_fired = false;
+	start = rdtsc();
+	apic_write(APIC_TMICT, 1000000);
+
+	irq_enable();
+	asm volatile ("nop");
+	vmcall();
+
+	report("intercepted interrupt + activity state hlt",
+	       rdtsc() - start > 10000 && timer_fired);
+}
+
+static int interrupt_exit_handler(void)
+{
+	u64 guest_rip = vmcs_read(GUEST_RIP);
+	ulong reason = vmcs_read(EXI_REASON) & 0xff;
+	u32 insn_len = vmcs_read(EXI_INST_LEN);
+
+	switch (reason) {
+	case VMX_VMCALL:
+		switch (get_stage()) {
+		case 0:
+		case 2:
+		case 5:
+			vmcs_write(PIN_CONTROLS,
+				   vmcs_read(PIN_CONTROLS) | PIN_EXTINT);
+			break;
+		case 1:
+		case 3:
+			vmcs_write(PIN_CONTROLS,
+				   vmcs_read(PIN_CONTROLS) & ~PIN_EXTINT);
+			break;
+		case 4:
+		case 6:
+			vmcs_write(GUEST_ACTV_STATE, ACTV_HLT);
+			break;
+		}
+		set_stage(get_stage() + 1);
+		vmcs_write(GUEST_RIP, guest_rip + insn_len);
+		return VMX_TEST_RESUME;
+	case VMX_EXTINT:
+		irq_enable();
+		asm volatile ("nop");
+		irq_disable();
+		if (get_stage() >= 2) {
+			vmcs_write(GUEST_ACTV_STATE, ACTV_ACTIVE);
+			vmcs_write(GUEST_RIP, guest_rip + insn_len);
+		}
+		return VMX_TEST_RESUME;
+	default:
+		printf("Unknown exit reason, %d\n", reason);
+		print_vmexit_info();
+	}
+
+	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} },
@@ -1134,5 +1276,7 @@ struct vmx_test vmx_tests[] = {
 	{ "instruction intercept", insn_intercept_init, insn_intercept_main,
 		insn_intercept_exit_handler, NULL, {0} },
 	{ "EPT framework", ept_init, ept_main, ept_exit_handler, NULL, {0} },
+	{ "interrupt", interrupt_init, interrupt_main,
+		interrupt_exit_handler, NULL, {0} },
 	{ NULL, NULL, NULL, NULL, NULL, {0} },
 };
-- 
1.8.1.1.298.ge7eed54

--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[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