[PATCH 8/8] test: add svm tests

Avi Kivity
2010-07-28T10:18:31Z

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