diff mbox series

[kvm-unit-tests] x86/hyper-v: stimer_direct test

Message ID 20181113152218.22049-6-vkuznets@redhat.com (mailing list archive)
State New, archived
Headers show
Series [kvm-unit-tests] x86/hyper-v: stimer_direct test | expand

Commit Message

Vitaly Kuznetsov Nov. 13, 2018, 3:22 p.m. UTC
Signed-off-by: Vitaly Kuznetsov <vkuznets@redhat.com>
---
 x86/Makefile.common        |   3 +
 x86/hyperv.h               |   7 ++
 x86/hyperv_stimer_direct.c | 250 +++++++++++++++++++++++++++++++++++++
 x86/unittests.cfg          |   6 +
 4 files changed, 266 insertions(+)
 create mode 100644 x86/hyperv_stimer_direct.c
diff mbox series

Patch

diff --git a/x86/Makefile.common b/x86/Makefile.common
index e612dbe..115189a 100644
--- a/x86/Makefile.common
+++ b/x86/Makefile.common
@@ -57,6 +57,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_stimer_direct.flat \
                $(TEST_DIR)/hyperv_connections.flat \
                $(TEST_DIR)/umip.flat
 
@@ -81,6 +82,8 @@  $(TEST_DIR)/hyperv_synic.elf: $(TEST_DIR)/hyperv.o
 
 $(TEST_DIR)/hyperv_stimer.elf: $(TEST_DIR)/hyperv.o
 
+$(TEST_DIR)/hyperv_stimer_direct.elf: $(TEST_DIR)/hyperv.o
+
 $(TEST_DIR)/hyperv_connections.elf: $(TEST_DIR)/hyperv.o
 
 arch_clean:
diff --git a/x86/hyperv.h b/x86/hyperv.h
index 9a83da4..718744c 100644
--- a/x86/hyperv.h
+++ b/x86/hyperv.h
@@ -10,6 +10,8 @@ 
 #define HV_X64_MSR_SYNIC_AVAILABLE              (1 << 2)
 #define HV_X64_MSR_SYNTIMER_AVAILABLE           (1 << 3)
 
+#define HV_STIMER_DIRECT_MODE_AVAILABLE		(1 << 19)
+
 #define HV_X64_MSR_GUEST_OS_ID                  0x40000000
 #define HV_X64_MSR_HYPERCALL                    0x40000001
 
@@ -205,6 +207,11 @@  static inline bool stimer_supported(void)
     return cpuid(HYPERV_CPUID_FEATURES).a & HV_X64_MSR_SYNIC_AVAILABLE;
 }
 
+static inline bool stimer_direct_supported(void)
+{
+    return cpuid(HYPERV_CPUID_FEATURES).d & HV_STIMER_DIRECT_MODE_AVAILABLE;
+}
+
 static inline bool hv_time_ref_counter_supported(void)
 {
     return cpuid(HYPERV_CPUID_FEATURES).a & HV_X64_MSR_TIME_REF_COUNT_AVAILABLE;
diff --git a/x86/hyperv_stimer_direct.c b/x86/hyperv_stimer_direct.c
new file mode 100644
index 0000000..e93f633
--- /dev/null
+++ b/x86/hyperv_stimer_direct.c
@@ -0,0 +1,250 @@ 
+#include "libcflat.h"
+#include "processor.h"
+#include "msr.h"
+#include "isr.h"
+#include "vm.h"
+#include "apic.h"
+#include "desc.h"
+#include "smp.h"
+#include "atomic.h"
+#include "hyperv.h"
+#include "asm/barrier.h"
+#include "alloc_page.h"
+
+#define MAX_CPUS 4
+
+#define INT1_VEC 0xF1
+#define INT2_VEC 0xF2
+
+#define ONE_MS_IN_100NS 10000
+
+struct stimer {
+    int sint;
+    int index;
+    atomic_t fire_count;
+};
+
+struct svcpu {
+    int vcpu;
+    struct stimer timer[HV_SYNIC_STIMER_COUNT];
+};
+
+static struct svcpu g_synic_vcpu[MAX_CPUS];
+
+static void stimer_init(struct stimer *timer, int index)
+{
+    memset(timer, 0, sizeof(*timer));
+    timer->index = index;
+}
+
+static void synic_enable(void)
+{
+    int vcpu = smp_id(), i;
+    struct svcpu *svcpu = &g_synic_vcpu[vcpu];
+
+    memset(svcpu, 0, sizeof(*svcpu));
+    svcpu->vcpu = vcpu;
+    for (i = 0; i < ARRAY_SIZE(svcpu->timer); i++) {
+        stimer_init(&svcpu->timer[i], i);
+    }
+}
+
+static void stimer_shutdown(struct stimer *timer)
+{
+    wrmsr(HV_X64_MSR_STIMER0_CONFIG + 2*timer->index, 0);
+}
+
+static void process_stimer_expired(struct stimer *timer)
+{
+    atomic_inc(&timer->fire_count);
+}
+
+static void __stimer_isr_direct(int vcpu, int timer_index)
+{
+    struct svcpu *svcpu = &g_synic_vcpu[vcpu];
+    struct stimer *timer = &svcpu->timer[timer_index];
+
+    process_stimer_expired(timer);
+}
+
+static void stimer_isr_direct1(isr_regs_t *regs)
+{
+    int vcpu = smp_id();
+
+    __stimer_isr_direct(vcpu, 0);
+
+    eoi();
+}
+
+static void stimer_isr_direct2(isr_regs_t *regs)
+{
+    int vcpu = smp_id();
+
+    __stimer_isr_direct(vcpu, 1);
+
+    eoi();
+}
+
+static void stimer_start(struct stimer *timer,
+                         bool auto_enable, bool periodic,
+                         u64 tick_100ns, int apic_vector)
+{
+    u64 count;
+    union hv_stimer_config config = {.as_uint64 = 0};
+
+    timer->sint = 0;
+    atomic_set(&timer->fire_count, 0);
+
+    config.periodic = periodic;
+    config.enable = 1;
+    config.auto_enable = auto_enable;
+    config.direct_mode = 1;
+    config.sintx = 0;
+    config.apic_vector = apic_vector;
+
+    if (periodic) {
+        count = tick_100ns;
+    } else {
+        count = rdmsr(HV_X64_MSR_TIME_REF_COUNT) + tick_100ns;
+    }
+
+    if (!auto_enable) {
+        wrmsr(HV_X64_MSR_STIMER0_COUNT + timer->index*2, count);
+        wrmsr(HV_X64_MSR_STIMER0_CONFIG + timer->index*2, config.as_uint64);
+    } else {
+        wrmsr(HV_X64_MSR_STIMER0_CONFIG + timer->index*2, config.as_uint64);
+        wrmsr(HV_X64_MSR_STIMER0_COUNT + timer->index*2, count);
+    }
+}
+
+static void stimers_shutdown(void)
+{
+    int vcpu = smp_id(), i;
+    struct svcpu *svcpu = &g_synic_vcpu[vcpu];
+
+    for (i = 0; i < ARRAY_SIZE(svcpu->timer); i++) {
+        stimer_shutdown(&svcpu->timer[i]);
+    }
+}
+
+static void stimer_test_prepare(void *ctx)
+{
+    write_cr3((ulong)ctx);
+    synic_enable();
+}
+
+static void stimer_test_periodic(int vcpu, struct stimer *timer1,
+                                 struct stimer *timer2)
+{
+    /* Check periodic timers */
+    stimer_start(timer1, false, true, ONE_MS_IN_100NS, INT1_VEC);
+    stimer_start(timer2, false, true, ONE_MS_IN_100NS, INT2_VEC);
+    while ((atomic_read(&timer1->fire_count) < 1000) ||
+           (atomic_read(&timer2->fire_count) < 1000)) {
+        pause();
+    }
+    report("Hyper-V SynIC periodic timers test vcpu %d", true, vcpu);
+    stimer_shutdown(timer1);
+    stimer_shutdown(timer2);
+}
+
+static void stimer_test_one_shot(int vcpu, struct stimer *timer)
+{
+    /* Check one-shot timer */
+    stimer_start(timer, false, false, ONE_MS_IN_100NS, INT1_VEC);
+    while (atomic_read(&timer->fire_count) < 1) {
+        pause();
+    }
+    report("Hyper-V SynIC one-shot test vcpu %d", true, vcpu);
+    stimer_shutdown(timer);
+}
+
+static void stimer_test_auto_enable_one_shot(int vcpu, struct stimer *timer)
+{
+    /* Check auto-enable one-shot timer */
+    stimer_start(timer, true, false, ONE_MS_IN_100NS, INT2_VEC);
+    while (atomic_read(&timer->fire_count) < 1) {
+        pause();
+    }
+    report("Hyper-V SynIC auto-enable one-shot timer test vcpu %d", true, vcpu);
+    stimer_shutdown(timer);
+}
+
+static void stimer_test_auto_enable_periodic(int vcpu, struct stimer *timer)
+{
+    /* Check auto-enable periodic timer */
+    stimer_start(timer, true, true, ONE_MS_IN_100NS, INT1_VEC);
+    while (atomic_read(&timer->fire_count) < 1000) {
+        pause();
+    }
+    report("Hyper-V SynIC auto-enable periodic timer test vcpu %d", true, vcpu);
+    stimer_shutdown(timer);
+}
+
+static void stimer_test(void *ctx)
+{
+    int vcpu = smp_id();
+    struct svcpu *svcpu = &g_synic_vcpu[vcpu];
+    struct stimer *timer1, *timer2;
+
+    irq_enable();
+
+    timer1 = &svcpu->timer[0];
+    timer2 = &svcpu->timer[1];
+
+    stimer_test_periodic(vcpu, timer1, timer2);
+    stimer_test_one_shot(vcpu, timer1);
+    stimer_test_auto_enable_one_shot(vcpu, timer2);
+    stimer_test_auto_enable_periodic(vcpu, timer1);
+
+    irq_disable();
+}
+
+static void stimer_test_cleanup(void *ctx)
+{
+    stimers_shutdown();
+}
+
+static void stimer_test_all(void)
+{
+    int ncpus;
+
+    setup_vm();
+    smp_init();
+    enable_apic();
+
+    ncpus = cpu_count();
+    if (ncpus > MAX_CPUS)
+        report_abort("number cpus exceeds %d", MAX_CPUS);
+    printf("cpus = %d\n", ncpus);
+
+    handle_irq(INT1_VEC, stimer_isr_direct1);
+    handle_irq(INT2_VEC, stimer_isr_direct2);
+
+    on_cpus(stimer_test_prepare, (void *)read_cr3());
+    on_cpus(stimer_test, NULL);
+    on_cpus(stimer_test_cleanup, NULL);
+}
+
+int main(int ac, char **av)
+{
+
+    if (!stimer_supported()) {
+        report("Hyper-V SynIC timers are not supported", true);
+        goto done;
+    }
+
+    if (!hv_time_ref_counter_supported()) {
+        report("Hyper-V time reference counter is not supported", true);
+        goto done;
+    }
+
+    if (!stimer_direct_supported()) {
+        report("Hyper-V direct mode for synthetic timers is not supported", true);
+        goto done;
+    }
+
+    stimer_test_all();
+done:
+    return report_summary();
+}
diff --git a/x86/unittests.cfg b/x86/unittests.cfg
index c744a6d..36c0657 100644
--- a/x86/unittests.cfg
+++ b/x86/unittests.cfg
@@ -635,6 +635,12 @@  smp = 2
 extra_params = -cpu kvm64,hv_vpindex,hv_time,hv_synic,hv_stimer -device hyperv-testdev
 groups = hyperv
 
+[hyperv_stimer_direct]
+file = hyperv_stimer_direct.flat
+smp = 2
+extra_params = -cpu kvm64,hv_vpindex,hv_time,hv_synic,hv_stimer,hv_stimer_direct -device hyperv-testdev
+groups = hyperv
+
 [hyperv_clock]
 file = hyperv_clock.flat
 smp = 2