diff mbox

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

Message ID cc3807ef752bf9932458f9fc9d4c821cdd9c8ad5.1388858359.git.jan.kiszka@web.de (mailing list archive)
State New, archived
Headers show

Commit Message

Jan Kiszka Jan. 4, 2014, 5:59 p.m. UTC
From: Jan Kiszka <jan.kiszka@siemens.com>

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@siemens.com>
---
 x86/vmx.c       |   3 +-
 x86/vmx.h       |   3 ++
 x86/vmx_tests.c | 144 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 148 insertions(+), 2 deletions(-)
diff mbox

Patch

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} },
 };