[PATCH 8/8] test: add svm tests

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

 



Signed-off-by: Avi Kivity <avi@xxxxxxxxxx>
---
 kvm/test/config-x86-common.mak |    2 +
 kvm/test/config-x86_64.mak     |    1 +
 kvm/test/x86/svm.c             |  180 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 183 insertions(+), 0 deletions(-)
 create mode 100644 kvm/test/x86/svm.c

diff --git a/kvm/test/config-x86-common.mak b/kvm/test/config-x86-common.mak
index 00817dc..19bffd4 100644
--- a/kvm/test/config-x86-common.mak
+++ b/kvm/test/config-x86-common.mak
@@ -68,6 +68,8 @@ $(TEST_DIR)/xsave.flat: $(cstart.o) $(TEST_DIR)/idt.o $(TEST_DIR)/xsave.o
 $(TEST_DIR)/rmap_chain.flat: $(cstart.o) $(TEST_DIR)/rmap_chain.o \
 			     $(TEST_DIR)/print.o $(TEST_DIR)/vm.o
 
+$(TEST_DIR)/svm.flat: $(cstart.o) $(TEST_DIR)/vm.o
+
 arch_clean:
 	$(RM) $(TEST_DIR)/*.o $(TEST_DIR)/*.flat \
 	$(TEST_DIR)/.*.d $(TEST_DIR)/lib/.*.d $(TEST_DIR)/lib/*.o
diff --git a/kvm/test/config-x86_64.mak b/kvm/test/config-x86_64.mak
index 3ffbcc1..b99cf85 100644
--- a/kvm/test/config-x86_64.mak
+++ b/kvm/test/config-x86_64.mak
@@ -7,5 +7,6 @@ CFLAGS += -D__x86_64__
 tests = $(TEST_DIR)/access.flat $(TEST_DIR)/apic.flat \
 	  $(TEST_DIR)/emulator.flat $(TEST_DIR)/idt_test.flat \
 	  $(TEST_DIR)/xsave.flat $(TEST_DIR)/rmap_chain.flat
+tests += $(TEST_DIR)/svm.flat
 
 include config-x86-common.mak
diff --git a/kvm/test/x86/svm.c b/kvm/test/x86/svm.c
new file mode 100644
index 0000000..af0e60c
--- /dev/null
+++ b/kvm/test/x86/svm.c
@@ -0,0 +1,180 @@
+#include "svm.h"
+#include "libcflat.h"
+#include "processor.h"
+#include "msr.h"
+#include "vm.h"
+
+static void setup_svm(void)
+{
+    void *hsave = alloc_page();
+
+    wrmsr(MSR_VM_HSAVE_PA, virt_to_phys(hsave));
+    wrmsr(MSR_EFER, rdmsr(MSR_EFER) | EFER_SVME);
+}
+
+static void vmcb_set_seg(struct vmcb_seg *seg, u16 selector,
+                         u64 base, u32 limit, u32 attr)
+{
+    seg->selector = selector;
+    seg->attrib = attr;
+    seg->limit = limit;
+    seg->base = base;
+}
+
+static void vmcb_ident(struct vmcb *vmcb)
+{
+    u64 vmcb_phys = virt_to_phys(vmcb);
+    struct vmcb_save_area *save = &vmcb->save;
+    struct vmcb_control_area *ctrl = &vmcb->control;
+    u32 data_seg_attr = 3 | SVM_SELECTOR_S_MASK | SVM_SELECTOR_P_MASK
+        | SVM_SELECTOR_DB_MASK | SVM_SELECTOR_G_MASK;
+    u32 code_seg_attr = 9 | SVM_SELECTOR_S_MASK | SVM_SELECTOR_P_MASK
+        | SVM_SELECTOR_L_MASK | SVM_SELECTOR_G_MASK;
+    struct descriptor_table_ptr desc_table_ptr;
+
+    memset(vmcb, 0, sizeof(*vmcb));
+    asm volatile ("vmsave" : : "a"(vmcb_phys) : "memory");
+    vmcb_set_seg(&save->es, read_es(), 0, -1U, data_seg_attr);
+    vmcb_set_seg(&save->cs, read_cs(), 0, -1U, code_seg_attr);
+    vmcb_set_seg(&save->ss, read_ss(), 0, -1U, data_seg_attr);
+    vmcb_set_seg(&save->ds, read_ds(), 0, -1U, data_seg_attr);
+    sgdt(&desc_table_ptr);
+    vmcb_set_seg(&save->gdtr, 0, desc_table_ptr.base, desc_table_ptr.limit, 0);
+    sidt(&desc_table_ptr);
+    vmcb_set_seg(&save->idtr, 0, desc_table_ptr.base, desc_table_ptr.limit, 0);
+    save->cpl = 0;
+    save->efer = rdmsr(MSR_EFER);
+    save->cr4 = read_cr4();
+    save->cr3 = read_cr3();
+    save->cr0 = read_cr0();
+    save->dr7 = read_dr7();
+    save->dr6 = read_dr6();
+    save->cr2 = read_cr2();
+    save->g_pat = rdmsr(MSR_IA32_CR_PAT);
+    save->dbgctl = rdmsr(MSR_IA32_DEBUGCTLMSR);
+    ctrl->intercept = (1ULL << INTERCEPT_VMRUN) | (1ULL << INTERCEPT_VMMCALL);
+}
+
+struct test {
+    const char *name;
+    void (*prepare)(struct test *test);
+    void (*guest_func)(struct test *test);
+    bool (*finished)(struct test *test);
+    bool (*succeeded)(struct test *test);
+    struct vmcb *vmcb;
+    int exits;
+};
+
+static void test_thunk(struct test *test)
+{
+    test->guest_func(test);
+    asm volatile ("vmmcall" : : : "memory");
+}
+
+static bool test_run(struct test *test, struct vmcb *vmcb)
+{
+    u64 vmcb_phys = virt_to_phys(vmcb);
+    u64 guest_stack[10000];
+    bool success;
+
+    test->vmcb = vmcb;
+    test->prepare(test);
+    vmcb->save.rip = (ulong)test_thunk;
+    vmcb->save.rsp = (ulong)(guest_stack + ARRAY_SIZE(guest_stack));
+    do {
+        asm volatile (
+            "clgi \n\t"
+            "vmload \n\t"
+            "push %%rbp \n\t"
+            "push %1 \n\t"
+            "vmrun \n\t"
+            "pop %1 \n\t"
+            "pop %%rbp \n\t"
+            "vmsave \n\t"
+            "stgi"
+            : : "a"(vmcb_phys), "D"(test)
+            : "rbx", "rcx", "rdx", "rsi",
+              "r8", "r9", "r10", "r11" , "r12", "r13", "r14", "r15",
+              "memory");
+        ++test->exits;
+    } while (!test->finished(test));
+
+    success = test->succeeded(test);
+
+    printf("%s: %s\n", test->name, success ? "PASS" : "FAIL");
+
+    return success;
+}
+
+static void default_prepare(struct test *test)
+{
+    vmcb_ident(test->vmcb);
+}
+
+static bool default_finished(struct test *test)
+{
+    return true; /* one vmexit */
+}
+
+static void null_test(struct test *test)
+{
+}
+
+static bool null_check(struct test *test)
+{
+    return test->vmcb->control.exit_code == SVM_EXIT_VMMCALL;
+}
+
+static void prepare_no_vmrun_int(struct test *test)
+{
+    test->vmcb->control.intercept &= ~(1ULL << INTERCEPT_VMRUN);
+}
+
+static bool check_no_vmrun_int(struct test *test)
+{
+    return test->vmcb->control.exit_code == SVM_EXIT_ERR;
+}
+
+static void test_vmrun(struct test *test)
+{
+    asm volatile ("vmrun" : : "a"(virt_to_phys(test->vmcb)));
+}
+
+static bool check_vmrun(struct test *test)
+{
+    return test->vmcb->control.exit_code == SVM_EXIT_VMRUN;
+}
+
+static struct test tests[] = {
+    { "null", default_prepare, null_test, default_finished, null_check },
+    { "vmrun", default_prepare, test_vmrun, default_finished, check_vmrun },
+    { "vmrun intercept check", prepare_no_vmrun_int, null_test,
+      default_finished, check_no_vmrun_int },
+
+};
+
+int main(int ac, char **av)
+{
+    int i, nr, passed;
+    struct vmcb *vmcb;
+
+    setup_vm();
+
+    if (!(cpuid(0x80000001).c & 4)) {
+        printf("SVM not availble\n");
+        return 0;
+    }
+
+    setup_svm();
+
+    vmcb = alloc_page();
+
+    nr = ARRAY_SIZE(tests);
+    passed = 0;
+    for (i = 0; i < nr; ++i) {
+        passed += test_run(&tests[i], vmcb);
+    }
+
+    printf("\nSUMMARY: %d TESTS, %d FAILURES\n", nr, (nr - passed));
+    return passed == nr ? 0 : 1;
+}
-- 
1.7.1

--
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