[kvm-unit-tests PATCH] x86: hyperv_synic: Hyper-V SynIC test

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

 



Hyper-V SynIC is a Hyper-V synthetic interrupt controller.

The test runs on every vCPU and performs the following steps:
* read from all Hyper-V SynIC MSR's
* setup Hyper-V SynIC evt/msg pages
* setup SINT's routing
* inject SINT's into destination vCPU by 'hyperv-synic-test-device'
* wait for SINT's isr's completion
* clear Hyper-V SynIC evt/msg pages and destroy SINT's routing

Signed-off-by: Andrey Smetanin <asmetanin@xxxxxxxxxxxxx>
Reviewed-by: Roman Kagan <rkagan@xxxxxxxxxxxxx>
Signed-off-by: Denis V. Lunev <den@xxxxxxxxxx>
CC: Vitaly Kuznetsov <vkuznets@xxxxxxxxxx>
CC: "K. Y. Srinivasan" <kys@xxxxxxxxxxxxx>
CC: Gleb Natapov <gleb@xxxxxxxxxx>
CC: Paolo Bonzini <pbonzini@xxxxxxxxxx>
CC: Roman Kagan <rkagan@xxxxxxxxxxxxx>
CC: Denis V. Lunev <den@xxxxxxxxxx>
CC: qemu-devel@xxxxxxxxxx
CC: virtualization@xxxxxxxxxxxxxxxxxxxxxxxxxx
---
 config/config-x86-common.mak |   5 +-
 lib/x86/msr.h                |  23 +++++
 x86/hyperv_synic.c           | 229 +++++++++++++++++++++++++++++++++++++++++++
 x86/run                      |  10 +-
 x86/unittests.cfg            |   5 +
 5 files changed, 270 insertions(+), 2 deletions(-)
 create mode 100644 x86/hyperv_synic.c

diff --git a/config/config-x86-common.mak b/config/config-x86-common.mak
index c2f9908..dc80eaa 100644
--- a/config/config-x86-common.mak
+++ b/config/config-x86-common.mak
@@ -36,7 +36,8 @@ tests-common = $(TEST_DIR)/vmexit.flat $(TEST_DIR)/tsc.flat \
                $(TEST_DIR)/kvmclock_test.flat  $(TEST_DIR)/eventinj.flat \
                $(TEST_DIR)/s3.flat $(TEST_DIR)/pmu.flat \
                $(TEST_DIR)/tsc_adjust.flat $(TEST_DIR)/asyncpf.flat \
-               $(TEST_DIR)/init.flat $(TEST_DIR)/smap.flat
+               $(TEST_DIR)/init.flat $(TEST_DIR)/smap.flat \
+               $(TEST_DIR)/hyperv_synic.flat
 
 ifdef API
 tests-common += api/api-sample
@@ -108,6 +109,8 @@ $(TEST_DIR)/vmx.elf: $(cstart.o) $(TEST_DIR)/vmx.o $(TEST_DIR)/vmx_tests.o
 
 $(TEST_DIR)/debug.elf: $(cstart.o) $(TEST_DIR)/debug.o
 
+$(TEST_DIR)/hyperv_synic.elf: $(cstart.o) $(TEST_DIR)/hyperv_synic.o
+
 arch_clean:
 	$(RM) $(TEST_DIR)/*.o $(TEST_DIR)/*.flat $(TEST_DIR)/*.elf \
 	$(TEST_DIR)/.*.d lib/x86/.*.d
diff --git a/lib/x86/msr.h b/lib/x86/msr.h
index 281255a..54da420 100644
--- a/lib/x86/msr.h
+++ b/lib/x86/msr.h
@@ -408,4 +408,27 @@
 #define MSR_VM_IGNNE                    0xc0010115
 #define MSR_VM_HSAVE_PA                 0xc0010117
 
+/* Define synthetic interrupt controller model specific registers. */
+#define HV_X64_MSR_SCONTROL                     0x40000080
+#define HV_X64_MSR_SVERSION                     0x40000081
+#define HV_X64_MSR_SIEFP                        0x40000082
+#define HV_X64_MSR_SIMP                         0x40000083
+#define HV_X64_MSR_EOM                          0x40000084
+#define HV_X64_MSR_SINT0                        0x40000090
+#define HV_X64_MSR_SINT1                        0x40000091
+#define HV_X64_MSR_SINT2                        0x40000092
+#define HV_X64_MSR_SINT3                        0x40000093
+#define HV_X64_MSR_SINT4                        0x40000094
+#define HV_X64_MSR_SINT5                        0x40000095
+#define HV_X64_MSR_SINT6                        0x40000096
+#define HV_X64_MSR_SINT7                        0x40000097
+#define HV_X64_MSR_SINT8                        0x40000098
+#define HV_X64_MSR_SINT9                        0x40000099
+#define HV_X64_MSR_SINT10                       0x4000009A
+#define HV_X64_MSR_SINT11                       0x4000009B
+#define HV_X64_MSR_SINT12                       0x4000009C
+#define HV_X64_MSR_SINT13                       0x4000009D
+#define HV_X64_MSR_SINT14                       0x4000009E
+#define HV_X64_MSR_SINT15                       0x4000009F
+
 #endif /* _ASM_X86_MSR_INDEX_H */
diff --git a/x86/hyperv_synic.c b/x86/hyperv_synic.c
new file mode 100644
index 0000000..5c5a43a
--- /dev/null
+++ b/x86/hyperv_synic.c
@@ -0,0 +1,229 @@
+#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"
+
+#define MAX_CPUS 4
+#define HYPERV_CPUID_FEATURES                   0x40000003
+#define HV_X64_MSR_SYNIC_AVAILABLE              (1 << 2)
+#define HV_SYNIC_CONTROL_ENABLE                 (1ULL << 0)
+#define HV_SYNIC_SIMP_ENABLE                    (1ULL << 0)
+#define HV_SYNIC_SIEFP_ENABLE                   (1ULL << 0)
+#define HV_SYNIC_SINT_MASKED                    (1ULL << 16)
+#define HV_SYNIC_SINT_AUTO_EOI                  (1ULL << 17)
+#define HV_SYNIC_SINT_VECTOR_MASK               (0xFF)
+#define HV_SYNIC_SINT_COUNT                     16
+
+enum {
+    HV_TEST_DEV_SINT_ROUTE_CREATE = 1,
+    HV_TEST_DEV_SINT_ROUTE_DESTROY,
+    HV_TEST_DEV_SINT_ROUTE_SET_SINT
+};
+
+static atomic_t isr_enter_count[MAX_CPUS];
+static atomic_t cpus_comp_count;
+
+static bool synic_supported(void)
+{
+   return cpuid(HYPERV_CPUID_FEATURES).a & HV_X64_MSR_SYNIC_AVAILABLE;
+}
+
+static void synic_sint_auto_eoi_isr(isr_regs_t *regs)
+{
+    atomic_inc(&isr_enter_count[smp_id()]);
+}
+
+static void synic_sint_isr(isr_regs_t *regs)
+{
+    atomic_inc(&isr_enter_count[smp_id()]);
+    eoi();
+}
+
+static void synic_ctl(u8 ctl, u8 vcpu_id, u8 sint)
+{
+    outl((ctl << 16)|((vcpu_id) << 8)|sint, 0x3000);
+}
+
+struct sint_vec_entry {
+    int vec;
+    bool auto_eoi;
+};
+
+struct sint_vec_entry sint_vecs[HV_SYNIC_SINT_COUNT] = {
+    {0xB0, false},
+    {0xB1, false},
+    {0xB2, false},
+    {0xB3, true},
+    {0xB4, false},
+    {0xB5, false},
+    {0xB6, false},
+    {0xB7, false},
+    {0xB8, true},
+    {0xB9, false},
+    {0xBA, true},
+    {0xBB, false},
+    {0xBC, false},
+    {0xBD, false},
+    {0xBE, true},
+    {0xBF, false},
+};
+
+static void synic_prepare_sint_vecs(void)
+{
+    bool auto_eoi;
+    int i, vec;
+
+    for (i = 0; i < HV_SYNIC_SINT_COUNT; i++) {
+        vec = sint_vecs[i].vec;
+        auto_eoi = sint_vecs[i].auto_eoi;
+        handle_irq(vec, (auto_eoi) ? synic_sint_auto_eoi_isr : synic_sint_isr);
+    }
+}
+
+static void synic_sints_prepare(u8 vcpu)
+{
+    bool auto_eoi;
+    int i, vec;
+
+    for (i = 0; i < HV_SYNIC_SINT_COUNT; i++) {
+        vec = sint_vecs[i].vec;
+        auto_eoi = sint_vecs[i].auto_eoi;
+        wrmsr(HV_X64_MSR_SINT0 + i,
+                (u64)vec | ((auto_eoi) ? HV_SYNIC_SINT_AUTO_EOI : 0));
+        synic_ctl(HV_TEST_DEV_SINT_ROUTE_CREATE, vcpu, i);
+    }
+}
+
+static void synic_test_prepare(void *ctx)
+{
+    u64 r;
+    int i = 0;
+
+    write_cr3((ulong)ctx);
+    irq_enable();
+
+    rdmsr(HV_X64_MSR_SVERSION);
+    rdmsr(HV_X64_MSR_SIMP);
+    rdmsr(HV_X64_MSR_SIEFP);
+    rdmsr(HV_X64_MSR_SCONTROL);
+    for (i = 0; i < HV_SYNIC_SINT_COUNT; i++) {
+        rdmsr(HV_X64_MSR_SINT0 + i);
+    }
+    r = rdmsr(HV_X64_MSR_EOM);
+    if (r != 0) {
+        report("Hyper-V SynIC test, EOM read 0x%llx", false, r);
+        goto ret;
+    }
+
+    wrmsr(HV_X64_MSR_SIMP, (u64)virt_to_phys(alloc_page()) |
+            HV_SYNIC_SIMP_ENABLE);
+    wrmsr(HV_X64_MSR_SIEFP, (u64)virt_to_phys(alloc_page())|
+            HV_SYNIC_SIEFP_ENABLE);
+    wrmsr(HV_X64_MSR_SCONTROL, HV_SYNIC_CONTROL_ENABLE);
+
+    synic_sints_prepare(smp_id());
+ret:
+    atomic_inc(&cpus_comp_count);
+}
+
+static void synic_sints_test(u8 dst_vcpu)
+{
+    int i;
+
+    atomic_set(&isr_enter_count[dst_vcpu], 0);
+    for (i = 0; i < HV_SYNIC_SINT_COUNT; i++) {
+        synic_ctl(HV_TEST_DEV_SINT_ROUTE_SET_SINT, dst_vcpu, i);
+    }
+
+    while (atomic_read(&isr_enter_count[dst_vcpu]) != HV_SYNIC_SINT_COUNT) {
+        pause();
+    }
+}
+
+static void synic_test(void *ctx)
+{
+    u8 dst_vcpu = (ulong)ctx;
+
+    irq_enable();
+    synic_sints_test(dst_vcpu);
+    atomic_inc(&cpus_comp_count);
+}
+
+static void synic_test_cleanup(void *ctx)
+{
+    u8 vcpu = smp_id();
+    int i;
+
+    irq_enable();
+    for (i = 0; i < HV_SYNIC_SINT_COUNT; i++) {
+        synic_ctl(HV_TEST_DEV_SINT_ROUTE_DESTROY, vcpu, i);
+        wrmsr(HV_X64_MSR_SINT0 + i, 0xFF|HV_SYNIC_SINT_MASKED);
+    }
+
+    wrmsr(HV_X64_MSR_SCONTROL, 0);
+    wrmsr(HV_X64_MSR_SIMP, 0);
+    wrmsr(HV_X64_MSR_SIEFP, 0);
+    atomic_inc(&cpus_comp_count);
+}
+
+int main(int ac, char **av)
+{
+
+    if (synic_supported()) {
+        int ncpus, i;
+
+        setup_vm();
+        smp_init();
+        setup_idt();
+        enable_apic();
+
+        synic_prepare_sint_vecs();
+
+        ncpus = cpu_count();
+        if (ncpus > MAX_CPUS) {
+            ncpus = MAX_CPUS;
+        }
+        printf("ncpus = %d\n", ncpus);
+
+        atomic_set(&cpus_comp_count, 0);
+        for (i = 0; i < ncpus; i++) {
+                on_cpu_async(i, synic_test_prepare, (void *)read_cr3());
+        }
+        while (atomic_read(&cpus_comp_count) != ncpus) {
+            pause();
+        }
+
+        atomic_set(&cpus_comp_count, 0);
+        for (i = 0; i < ncpus; i++) {
+                on_cpu_async(i, synic_test, (void *)(ulong)(ncpus - 1 - i));
+        }
+        while (atomic_read(&cpus_comp_count) != ncpus) {
+            pause();
+        }
+
+        atomic_set(&cpus_comp_count, 0);
+        for (i = 0; i < ncpus; i++) {
+                on_cpu_async(i, synic_test_cleanup, NULL);
+        }
+        while (atomic_read(&cpus_comp_count) != ncpus) {
+            pause();
+        }
+
+        for (i = 0; i < ncpus; ++i) {
+            printf("isr_enter_count[%d] = %d\n",
+                   i, atomic_read(&isr_enter_count[i]));
+        }
+
+        report("Hyper-V SynIC test", true);
+    } else {
+        report("Hyper-V SynIC is not supported", true);
+    }
+
+    return report_summary();
+}
diff --git a/x86/run b/x86/run
index b4a35b2..e50ce5f 100755
--- a/x86/run
+++ b/x86/run
@@ -42,7 +42,15 @@ else
 	pc_testdev="-device testdev,chardev=testlog -chardev file,id=testlog,path=msr.out"
 fi
 
-command="${qemu} -enable-kvm $pc_testdev -vnc none -serial stdio $pci_testdev -kernel"
+if
+	${qemu} -device '?' 2>&1 | grep -F "hyperv-testdev" > /dev/null;
+then
+	hyperv_testdev="-device hyperv-testdev"
+else
+	hyperv_testdev=""
+fi
+
+command="${qemu} -enable-kvm $pc_testdev -vnc none -serial stdio $pci_testdev $hyperv_testdev -kernel"
 echo ${command} "$@"
 
 if [ "$DRYRUN" != "yes" ]; then
diff --git a/x86/unittests.cfg b/x86/unittests.cfg
index a38544f..6b19f42 100644
--- a/x86/unittests.cfg
+++ b/x86/unittests.cfg
@@ -166,3 +166,8 @@ arch = x86_64
 [debug]
 file = debug.flat
 arch = x86_64
+
+[hyperv_synic]
+file = hyperv_synic.flat
+smp = 2
+extra_params = -cpu host,hv_synic
-- 
2.4.3

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