diff mbox series

[kvm-unit-tests,7/9] svm: add tests for case when L1 intercepts various hardware interrupts

Message ID 20220322205613.250925-8-mlevitsk@redhat.com (mailing list archive)
State New, archived
Headers show
Series KVM unit tests for SVM options features | expand

Commit Message

Maxim Levitsky March 22, 2022, 8:56 p.m. UTC
(an interrupt, SMI, NMI), but lets L2 control either EFLAG.IF or GIF

Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
---
 x86/svm.h       |  11 +++
 x86/svm_tests.c | 194 ++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 205 insertions(+)
diff mbox series

Patch

diff --git a/x86/svm.h b/x86/svm.h
index 58b9410..df1b1ac 100644
--- a/x86/svm.h
+++ b/x86/svm.h
@@ -426,6 +426,17 @@  void test_set_guest(test_guest_func func);
 extern struct vmcb *vmcb;
 extern struct svm_test svm_tests[];
 
+static inline void stgi(void)
+{
+    asm volatile ("stgi");
+}
+
+static inline void clgi(void)
+{
+    asm volatile ("clgi");
+}
+
+
 
 #define SAVE_GPR_C                              \
         "xchg %%rbx, regs+0x8\n\t"              \
diff --git a/x86/svm_tests.c b/x86/svm_tests.c
index b2ba283..ef8b5ee 100644
--- a/x86/svm_tests.c
+++ b/x86/svm_tests.c
@@ -3312,6 +3312,195 @@  static void svm_lbrv_nested_test2(void)
 	check_lbr(&host_branch4_from, &host_branch4_to);
 }
 
+
+// test that a nested guest which does enable INTR interception
+// but doesn't enable virtual interrupt masking works
+
+static volatile int dummy_isr_recevied;
+static void dummy_isr(isr_regs_t *regs)
+{
+	dummy_isr_recevied++;
+	eoi();
+}
+
+
+static volatile int nmi_recevied;
+static void dummy_nmi_handler(struct ex_regs *regs)
+{
+	nmi_recevied++;
+}
+
+
+static void svm_intr_intercept_mix_run_guest(volatile int *counter, int expected_vmexit)
+{
+	if (counter)
+		*counter = 0;
+
+	sti();  // host IF value should not matter
+	clgi(); // vmrun will set back GI to 1
+
+	svm_vmrun();
+
+	if (counter)
+		report(!*counter, "No interrupt expected");
+
+	stgi();
+
+	if (counter)
+		report(*counter == 1, "Interrupt is expected");
+
+	report (vmcb->control.exit_code == expected_vmexit, "Test expected VM exit");
+	report(vmcb->save.rflags & X86_EFLAGS_IF, "Guest should have EFLAGS.IF set now");
+	cli();
+}
+
+
+// subtest: test that enabling EFLAGS.IF is enought to trigger an interrupt
+static void svm_intr_intercept_mix_if_guest(struct svm_test *test)
+{
+	asm volatile("nop;nop;nop;nop");
+	report(!dummy_isr_recevied, "No interrupt expected");
+	sti();
+	asm volatile("nop");
+	report(0, "must not reach here");
+}
+
+static void svm_intr_intercept_mix_if(void)
+{
+	// make a physical interrupt to be pending
+	handle_irq(0x55, dummy_isr);
+
+	vmcb->control.intercept |= (1 << INTERCEPT_INTR);
+	vmcb->control.int_ctl &= ~V_INTR_MASKING_MASK;
+	vmcb->save.rflags &= ~X86_EFLAGS_IF;
+
+	test_set_guest(svm_intr_intercept_mix_if_guest);
+	apic_icr_write(APIC_DEST_SELF | APIC_DEST_PHYSICAL | APIC_DM_FIXED | 0x55, 0);
+	svm_intr_intercept_mix_run_guest(&dummy_isr_recevied, SVM_EXIT_INTR);
+}
+
+
+// subtest: test that a clever guest can trigger an interrupt by setting GIF
+// if GIF is not intercepted
+static void svm_intr_intercept_mix_gif_guest(struct svm_test *test)
+{
+
+	asm volatile("nop;nop;nop;nop");
+	report(!dummy_isr_recevied, "No interrupt expected");
+
+	// clear GIF and enable IF
+	// that should still not cause VM exit
+	clgi();
+	sti();
+	asm volatile("nop");
+	report(!dummy_isr_recevied, "No interrupt expected");
+
+	stgi();
+	asm volatile("nop");
+	report(0, "must not reach here");
+}
+
+static void svm_intr_intercept_mix_gif(void)
+{
+	handle_irq(0x55, dummy_isr);
+
+	vmcb->control.intercept |= (1 << INTERCEPT_INTR);
+	vmcb->control.int_ctl &= ~V_INTR_MASKING_MASK;
+	vmcb->save.rflags &= ~X86_EFLAGS_IF;
+
+	test_set_guest(svm_intr_intercept_mix_gif_guest);
+	apic_icr_write(APIC_DEST_SELF | APIC_DEST_PHYSICAL | APIC_DM_FIXED | 0x55, 0);
+	svm_intr_intercept_mix_run_guest(&dummy_isr_recevied, SVM_EXIT_INTR);
+}
+
+
+
+// subtest: test that a clever guest can trigger an interrupt by setting GIF
+// if GIF is not intercepted and interrupt comes after guest
+// started running
+static void svm_intr_intercept_mix_gif_guest2(struct svm_test *test)
+{
+	asm volatile("nop;nop;nop;nop");
+	report(!dummy_isr_recevied, "No interrupt expected");
+
+	clgi();
+	apic_icr_write(APIC_DEST_SELF | APIC_DEST_PHYSICAL | APIC_DM_FIXED | 0x55, 0);
+	report(!dummy_isr_recevied, "No interrupt expected");
+
+	stgi();
+	asm volatile("nop");
+	report(0, "must not reach here");
+}
+
+static void svm_intr_intercept_mix_gif2(void)
+{
+	handle_irq(0x55, dummy_isr);
+
+	vmcb->control.intercept |= (1 << INTERCEPT_INTR);
+	vmcb->control.int_ctl &= ~V_INTR_MASKING_MASK;
+	vmcb->save.rflags |= X86_EFLAGS_IF;
+
+	test_set_guest(svm_intr_intercept_mix_gif_guest2);
+	svm_intr_intercept_mix_run_guest(&dummy_isr_recevied, SVM_EXIT_INTR);
+}
+
+
+// subtest: test that pending NMI will be handled when guest enables GIF
+static void svm_intr_intercept_mix_nmi_guest(struct svm_test *test)
+{
+	asm volatile("nop;nop;nop;nop");
+	report(!nmi_recevied, "No NMI expected");
+	cli(); // should have no effect
+
+	clgi();
+	asm volatile("nop");
+	apic_icr_write(APIC_DEST_SELF | APIC_DEST_PHYSICAL | APIC_DM_NMI, 0);
+	sti(); // should have no effect
+	asm volatile("nop");
+	report(!nmi_recevied, "No NMI expected");
+
+	stgi();
+	asm volatile("nop");
+	report(0, "must not reach here");
+}
+
+static void svm_intr_intercept_mix_nmi(void)
+{
+	handle_exception(2, dummy_nmi_handler);
+
+	vmcb->control.intercept |= (1 << INTERCEPT_NMI);
+	vmcb->control.int_ctl &= ~V_INTR_MASKING_MASK;
+	vmcb->save.rflags |= X86_EFLAGS_IF;
+
+	test_set_guest(svm_intr_intercept_mix_nmi_guest);
+	svm_intr_intercept_mix_run_guest(&nmi_recevied, SVM_EXIT_NMI);
+}
+
+// test that pending SMI will be handled when guest enables GIF
+// TODO: can't really count #SMIs so just test that guest doesn't hang
+// and VMexits on SMI
+static void svm_intr_intercept_mix_smi_guest(struct svm_test *test)
+{
+	asm volatile("nop;nop;nop;nop");
+
+	clgi();
+	asm volatile("nop");
+	apic_icr_write(APIC_DEST_SELF | APIC_DEST_PHYSICAL | APIC_DM_SMI, 0);
+	sti(); // should have no effect
+	asm volatile("nop");
+	stgi();
+	asm volatile("nop");
+	report(0, "must not reach here");
+}
+
+static void svm_intr_intercept_mix_smi(void)
+{
+	vmcb->control.intercept |= (1 << INTERCEPT_SMI);
+	vmcb->control.int_ctl &= ~V_INTR_MASKING_MASK;
+	test_set_guest(svm_intr_intercept_mix_smi_guest);
+	svm_intr_intercept_mix_run_guest(NULL, SVM_EXIT_SMI);
+}
+
 struct svm_test svm_tests[] = {
     { "null", default_supported, default_prepare,
       default_prepare_gif_clear, null_test,
@@ -3439,5 +3628,10 @@  struct svm_test svm_tests[] = {
     TEST(svm_lbrv_test2),
     TEST(svm_lbrv_nested_test1),
     TEST(svm_lbrv_nested_test2),
+    TEST(svm_intr_intercept_mix_if),
+    TEST(svm_intr_intercept_mix_gif),
+    TEST(svm_intr_intercept_mix_gif2),
+    TEST(svm_intr_intercept_mix_nmi),
+    TEST(svm_intr_intercept_mix_smi),
     { NULL, NULL, NULL, NULL, NULL, NULL, NULL }
 };