qemu-devel
[Top][All Lists]
Advanced

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

[Qemu-devel] [PATCH v1] KVM-UNIT-TESTS:x86/hyperv: Hyper-V hypercalls te


From: Andrey Smetanin
Subject: [Qemu-devel] [PATCH v1] KVM-UNIT-TESTS:x86/hyperv: Hyper-V hypercalls test
Date: Tue, 12 Jan 2016 14:03:43 +0300

This is a simple test which performs the
following:
* setup hypecall page
* do some hypercalls and output their results

Signed-off-by: Andrey Smetanin <address@hidden>
CC: Paolo Bonzini <address@hidden>
CC: Marcelo Tosatti <address@hidden>
CC: Roman Kagan <address@hidden>
CC: Denis V. Lunev <address@hidden>
CC: address@hidden
---
 config/config-x86-common.mak |  4 ++
 lib/x86/vm.h                 |  2 +
 x86/hyperv.c                 | 95 ++++++++++++++++++++++++++++++++++++++++++
 x86/hyperv.h                 | 28 +++++++++++++
 x86/hyperv_hypercall.c       | 99 ++++++++++++++++++++++++++++++++++++++++++++
 x86/unittests.cfg            |  5 +++
 6 files changed, 233 insertions(+)
 create mode 100644 x86/hyperv_hypercall.c

diff --git a/config/config-x86-common.mak b/config/config-x86-common.mak
index 72b95e3..11714d6 100644
--- a/config/config-x86-common.mak
+++ b/config/config-x86-common.mak
@@ -38,6 +38,7 @@ tests-common = $(TEST_DIR)/vmexit.flat $(TEST_DIR)/tsc.flat \
                $(TEST_DIR)/tsc_adjust.flat $(TEST_DIR)/asyncpf.flat \
                $(TEST_DIR)/init.flat $(TEST_DIR)/smap.flat \
                $(TEST_DIR)/hyperv_synic.flat $(TEST_DIR)/hyperv_stimer.flat \
+               $(TEST_DIR)/hyperv_hypercall.flat \
 
 ifdef API
 tests-common += api/api-sample
@@ -119,6 +120,9 @@ $(TEST_DIR)/hyperv_synic.elf: $(cstart.o) 
$(TEST_DIR)/hyperv.o \
 $(TEST_DIR)/hyperv_stimer.elf: $(cstart.o) $(TEST_DIR)/hyperv.o \
                                $(TEST_DIR)/hyperv_stimer.o
 
+$(TEST_DIR)/hyperv_hypercall.elf: $(cstart.o) $(TEST_DIR)/hyperv.o \
+                                  $(TEST_DIR)/hyperv_hypercall.o
+
 arch_clean:
        $(RM) $(TEST_DIR)/*.o $(TEST_DIR)/*.flat $(TEST_DIR)/*.elf \
        $(TEST_DIR)/.*.d lib/x86/.*.d
diff --git a/lib/x86/vm.h b/lib/x86/vm.h
index 28794d7..085e29f 100644
--- a/lib/x86/vm.h
+++ b/lib/x86/vm.h
@@ -4,6 +4,8 @@
 #include "processor.h"
 
 #define PAGE_SIZE 4096ul
+#define PAGE_SHIFT 12
+
 #ifdef __x86_64__
 #define LARGE_PAGE_SIZE (512 * PAGE_SIZE)
 #else
diff --git a/x86/hyperv.c b/x86/hyperv.c
index 824773d..1d3ab23 100644
--- a/x86/hyperv.c
+++ b/x86/hyperv.c
@@ -1,4 +1,5 @@
 #include "hyperv.h"
+#include "vm.h"
 
 static void synic_ctl(u8 ctl, u8 vcpu_id, u8 sint)
 {
@@ -22,3 +23,97 @@ void synic_sint_destroy(int vcpu, int sint)
     wrmsr(HV_X64_MSR_SINT0 + sint, 0xFF|HV_SYNIC_SINT_MASKED);
     synic_ctl(HV_TEST_DEV_SINT_ROUTE_DESTROY, vcpu, sint);
 }
+
+static void *hv_hypercall_page;
+
+static inline u64 generate_guest_id(u8 d_info1, u32 kernel_version,
+                                    u16 d_info2)
+{
+    u64 guest_id = 0;
+
+    guest_id = (((u64)HV_LINUX_VENDOR_ID) << 48);
+    guest_id |= (((u64)(d_info1)) << 48);
+    guest_id |= (((u64)(kernel_version)) << 16);
+    guest_id |= ((u64)(d_info2));
+
+    return guest_id;
+}
+
+int hv_hypercall_init(void)
+{
+    void *page;
+    u64 val;
+
+    wrmsr(HV_X64_MSR_GUEST_OS_ID, generate_guest_id(0, 263168, 0));
+    page = alloc_page();
+    if (!page) {
+        return -1;
+    }
+
+    wrmsr(HV_X64_MSR_HYPERCALL,
+          ((virt_to_phys(page) >> PAGE_SHIFT) <<
+          HV_X64_MSR_HYPERCALL_PAGE_ADDRESS_SHIFT) |
+          HV_X64_MSR_HYPERCALL_ENABLE);
+
+    val = rdmsr(HV_X64_MSR_HYPERCALL);
+    if (!(val & HV_X64_MSR_HYPERCALL_ENABLE)) {
+        goto err;
+    }
+
+    hv_hypercall_page = page;
+    return 0;
+err:
+    free_page(page);
+    return -1;
+}
+
+void hv_hypercall_deinit(void)
+{
+    wrmsr(HV_X64_MSR_HYPERCALL, 0);
+    free_page(hv_hypercall_page);
+    wrmsr(HV_X64_MSR_GUEST_OS_ID, 0);
+    hv_hypercall_page = NULL;
+}
+
+u64 hv_hypercall(u64 control, void *input, void *output)
+{
+        u64 input_address = (input) ? virt_to_phys(input) : 0;
+        u64 output_address = (output) ? virt_to_phys(output) : 0;
+#ifdef __x86_64__
+        u64 hv_status = 0;
+
+        if (!hv_hypercall_page) {
+                return (u64)-1;
+        }
+
+        __asm__ __volatile__("mov %0, %%r8" : : "r" (output_address) : "r8");
+        __asm__ __volatile__("call *%3" : "=a" (hv_status) :
+                             "c" (control), "d" (input_address),
+                             "m" (hv_hypercall_page));
+
+        return hv_status;
+
+#else
+
+        u32 control_hi = control >> 32;
+        u32 control_lo = control & 0xFFFFFFFF;
+        u32 hv_status_hi = 1;
+        u32 hv_status_lo = 1;
+        u32 input_address_hi = input_address >> 32;
+        u32 input_address_lo = input_address & 0xFFFFFFFF;
+        u32 output_address_hi = output_address >> 32;
+        u32 output_address_lo = output_address & 0xFFFFFFFF;
+
+        if (!hv_hypercall_page) {
+                return (u64)-1;
+        }
+
+        __asm__ __volatile__ ("call *%8" : "=d"(hv_status_hi),
+                              "=a"(hv_status_lo) : "d" (control_hi),
+                              "a" (control_lo), "b" (input_address_hi),
+                              "c" (input_address_lo), "D"(output_address_hi),
+                              "S"(output_address_lo), "m" (hv_hypercall_page));
+
+        return hv_status_lo | ((u64)hv_status_hi << 32);
+#endif /* !x86_64 */
+}
diff --git a/x86/hyperv.h b/x86/hyperv.h
index faf931b..a5888bf 100644
--- a/x86/hyperv.h
+++ b/x86/hyperv.h
@@ -5,11 +5,30 @@
 #include "processor.h"
 #include "io.h"
 
+#define HV_LINUX_VENDOR_ID                      0x8100
+
 #define HYPERV_CPUID_FEATURES                   0x40000003
 
 #define HV_X64_MSR_TIME_REF_COUNT_AVAILABLE     (1 << 1)
 #define HV_X64_MSR_SYNIC_AVAILABLE              (1 << 2)
 #define HV_X64_MSR_SYNTIMER_AVAILABLE           (1 << 3)
+/* Hypercall MSRs (HV_X64_MSR_GUEST_OS_ID and HV_X64_MSR_HYPERCALL) available*/
+#define HV_X64_MSR_HYPERCALL_AVAILABLE          (1 << 5)
+
+/* MSR used to identify the guest OS. */
+#define HV_X64_MSR_GUEST_OS_ID                  0x40000000
+
+/* MSR used to setup pages used to communicate with the hypervisor. */
+#define HV_X64_MSR_HYPERCALL                    0x40000001
+#define HV_X64_MSR_HYPERCALL_ENABLE             0x00000001
+#define HV_X64_MSR_HYPERCALL_PAGE_ADDRESS_SHIFT 12
+#define HV_X64_MSR_HYPERCALL_PAGE_ADDRESS_MASK  \
+                (~((1ull << HV_X64_MSR_HYPERCALL_PAGE_ADDRESS_SHIFT) - 1))
+
+/* Declare the various hypercall operations. */
+#define HV_X64_HCALL_NOTIFY_LONG_SPIN_WAIT              0x0008
+#define HV_X64_HCALL_POST_MESSAGE                       0x005c
+#define HV_X64_HCALL_SIGNAL_EVENT                       0x005d
 
 #define HV_X64_MSR_TIME_REF_COUNT               0x40000020
 
@@ -180,4 +199,13 @@ void synic_sint_create(int vcpu, int sint, int vec, bool 
auto_eoi);
 void synic_sint_set(int vcpu, int sint);
 void synic_sint_destroy(int vcpu, int sint);
 
+static inline bool hv_hypercall_supported(void)
+{
+   return cpuid(HYPERV_CPUID_FEATURES).a & HV_X64_MSR_HYPERCALL_AVAILABLE;
+}
+
+int hv_hypercall_init(void);
+void hv_hypercall_deinit(void);
+u64 hv_hypercall(u64 control, void *input, void *output);
+
 #endif
diff --git a/x86/hyperv_hypercall.c b/x86/hyperv_hypercall.c
new file mode 100644
index 0000000..8964bd7
--- /dev/null
+++ b/x86/hyperv_hypercall.c
@@ -0,0 +1,99 @@
+#include "libcflat.h"
+#include "processor.h"
+#include "msr.h"
+#include "isr.h"
+#include "vm.h"
+#include "apic.h"
+#include "desc.h"
+#include "io.h"
+#include "smp.h"
+#include "atomic.h"
+#include "hyperv.h"
+
+#define MAX_CPUS 4
+
+static atomic_t g_cpus_comp_count;
+static int g_cpus_count;
+
+static void cpu_comp(void)
+{
+    atomic_inc(&g_cpus_comp_count);
+}
+
+static void hv_hypercall_test_prepare(void *ctx)
+{
+    write_cr3((ulong)ctx);
+
+    cpu_comp();
+}
+
+static void hv_hypercall_test(void *ctx)
+{
+    int vcpu = smp_id();
+    u64 result;
+    u32 codes[] = {HV_X64_HCALL_NOTIFY_LONG_SPIN_WAIT,
+                   HV_X64_HCALL_POST_MESSAGE, HV_X64_HCALL_SIGNAL_EVENT};
+
+    int i;
+
+    for (i = 0; i < ARRAY_SIZE(codes); i++) {
+        result = hv_hypercall(codes[i], 0, 0);
+        report("Hyper-V hypercall 0x%x result 0x%llx vcpu %d", true,
+               codes[i], result, vcpu);
+    }
+    cpu_comp();
+}
+
+static void on_each_cpu_async_wait(void (*func)(void *ctx), void *ctx)
+{
+    int i;
+
+    atomic_set(&g_cpus_comp_count, 0);
+    for (i = 0; i < g_cpus_count; i++) {
+        on_cpu_async(i, func, ctx);
+    }
+    while (atomic_read(&g_cpus_comp_count) != g_cpus_count) {
+        pause();
+    }
+}
+
+static void hv_hypercall_test_all(void)
+{
+    int ncpus;
+
+    setup_vm();
+    smp_init();
+    setup_idt();
+    enable_apic();
+
+    if (hv_hypercall_init()) {
+        report("Hyper-V hypercall setup", false);
+        return;
+    }
+    report("Hyper-V hypercall setup", true);
+
+    ncpus = cpu_count();
+    if (ncpus > MAX_CPUS) {
+        ncpus = MAX_CPUS;
+    }
+
+    printf("cpus = %d\n", ncpus);
+    g_cpus_count = ncpus;
+    on_each_cpu_async_wait(hv_hypercall_test_prepare, (void *)read_cr3());
+    on_each_cpu_async_wait(hv_hypercall_test, NULL);
+
+    hv_hypercall_deinit();
+}
+
+int main(int ac, char **av)
+{
+
+    if (!hv_hypercall_supported()) {
+        report("Hyper-V hypercall is not supported", true);
+        goto done;
+    }
+
+    hv_hypercall_test_all();
+done:
+    return report_summary();
+}
diff --git a/x86/unittests.cfg b/x86/unittests.cfg
index 99eff26..8805091 100644
--- a/x86/unittests.cfg
+++ b/x86/unittests.cfg
@@ -188,3 +188,8 @@ extra_params = -cpu kvm64,hv_synic -device hyperv-testdev
 file = hyperv_stimer.flat
 smp = 2
 extra_params = -cpu kvm64,hv_time,hv_synic,hv_stimer -device hyperv-testdev
+
+[hyperv_hypercall]
+file = hyperv_hypercall.flat
+smp = 2
+extra_params = -cpu kvm64,hv_time
-- 
2.4.3




reply via email to

[Prev in Thread] Current Thread [Next in Thread]