[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
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [Qemu-devel] [PATCH v1] KVM-UNIT-TESTS:x86/hyperv: Hyper-V hypercalls test,
Andrey Smetanin <=