Merge branch 'nohv_v2_wstimer_direct' into 'master'

x86: hyperv-v: Various unmerged patches

See merge request kvm-unit-tests/kvm-unit-tests!52
diff --git a/x86/hyperv.h b/x86/hyperv.h
index e3803e0..a144384 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
 
@@ -60,11 +62,23 @@
 #define HV_SYNIC_SINT_VECTOR_MASK               (0xFF)
 #define HV_SYNIC_SINT_COUNT                     16
 
-#define HV_STIMER_ENABLE                (1ULL << 0)
-#define HV_STIMER_PERIODIC              (1ULL << 1)
-#define HV_STIMER_LAZY                  (1ULL << 2)
-#define HV_STIMER_AUTOENABLE            (1ULL << 3)
-#define HV_STIMER_SINT(config)          (__u8)(((config) >> 16) & 0x0F)
+/*
+ * Synthetic timer configuration.
+ */
+union hv_stimer_config {
+	u64 as_uint64;
+	struct {
+		u64 enable:1;
+		u64 periodic:1;
+		u64 lazy:1;
+		u64 auto_enable:1;
+		u64 apic_vector:8;
+		u64 direct_mode:1;
+		u64 reserved_z0:3;
+		u64 sintx:4;
+		u64 reserved_z1:44;
+	};
+};
 
 #define HV_SYNIC_STIMER_COUNT           (4)
 
@@ -183,14 +197,19 @@
 	u64 payload[HV_MESSAGE_PAYLOAD_QWORD_COUNT];
 };
 
-static inline bool synic_supported(void)
+static inline bool hv_synic_supported(void)
 {
    return cpuid(HYPERV_CPUID_FEATURES).a & HV_X64_MSR_SYNIC_AVAILABLE;
 }
 
-static inline bool stimer_supported(void)
+static inline bool hv_stimer_supported(void)
 {
-    return cpuid(HYPERV_CPUID_FEATURES).a & HV_X64_MSR_SYNIC_AVAILABLE;
+    return cpuid(HYPERV_CPUID_FEATURES).a & HV_X64_MSR_SYNTIMER_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)
diff --git a/x86/hyperv_clock.c b/x86/hyperv_clock.c
index f1e7204..9061da8 100644
--- a/x86/hyperv_clock.c
+++ b/x86/hyperv_clock.c
@@ -63,41 +63,52 @@
 
 static void hv_clock_test(void *data)
 {
-	int i = smp_id();
-	uint64_t t = rdmsr(HV_X64_MSR_TIME_REF_COUNT);
-	uint64_t end = t + 3 * TICKS_PER_SEC;
-	uint64_t msr_sample = t + TICKS_PER_SEC;
-	int min_delta = 123456, max_delta = -123456;
+	int i = (long)data;
+	uint64_t t_msr_prev = rdmsr(HV_X64_MSR_TIME_REF_COUNT);
+	uint64_t t_page_prev = hv_clock_read();
+	uint64_t end = t_page_prev + TICKS_PER_SEC;
 	bool got_drift = false;
-	bool got_warp = false;
+	bool got_warp_msr = false;
+	bool got_warp_page = false;
 
 	ok[i] = true;
 	do {
-		uint64_t now = hv_clock_read();
-		int delta = rdmsr(HV_X64_MSR_TIME_REF_COUNT) - now;
+		uint64_t t_page_1, t_page_2, t_msr;
 
-		min_delta = delta < min_delta ? delta : min_delta;
-		if (t < msr_sample) {
-			max_delta = delta > max_delta ? delta: max_delta;
-		} else if (delta < 0 || delta > max_delta * 3 / 2) {
-			printf("suspecting drift on CPU %d? delta = %d, acceptable [0, %d)\n", smp_id(),
-			       delta, max_delta);
+		t_page_1 = hv_clock_read();
+		barrier();
+		t_msr = rdmsr(HV_X64_MSR_TIME_REF_COUNT);
+		barrier();
+		t_page_2 = hv_clock_read();
+
+		if (!got_drift && (t_msr < t_page_1 || t_msr > t_page_2)) {
+			printf("drift on CPU %d, MSR value = %ld, acceptable [%ld, %ld]\n", i,
+			       t_msr, t_page_1, t_page_2);
 			ok[i] = false;
 			got_drift = true;
-			max_delta *= 2;
 		}
 
-		if (now < t && !got_warp) {
-			printf("warp on CPU %d!\n", smp_id());
+		if (!got_warp_msr && t_msr < t_msr_prev) {
+			printf("warp on CPU %d, MSR value = %ld prev MSR value = %ld!\n", i,
+			       t_msr, t_msr_prev);
 			ok[i] = false;
-			got_warp = true;
+			got_warp_msr = true;
 			break;
 		}
-		t = now;
-	} while(t < end);
 
-	if (!got_drift)
-		printf("delta on CPU %d was %d...%d\n", smp_id(), min_delta, max_delta);
+		if (!got_warp_page && t_page_1 < t_page_prev) {
+			printf("warp on CPU %d, TSC page value = %ld prev TSC page value = %ld!\n", i,
+			       t_page_1, t_page_prev);
+			ok[i] = false;
+			got_warp_page = true;
+			break;
+		}
+
+		t_page_prev = t_page_1;
+		t_msr_prev = t_msr;
+
+	} while(t_page_prev < end);
+
 	barrier();
 }
 
@@ -106,7 +117,11 @@
 	int i;
 	bool pass;
 
-	on_cpus(hv_clock_test, NULL);
+	for (i = ncpus - 1; i >= 0; i--)
+		on_cpu_async(i, hv_clock_test, (void *)(long)i);
+
+	while (cpus_active() > 1)
+		pause();
 
 	pass = true;
 	for (i = ncpus - 1; i >= 0; i--)
@@ -117,6 +132,7 @@
 
 static void hv_perf_test(void *data)
 {
+	int i = (long)data;
 	uint64_t t = hv_clock_read();
 	uint64_t end = t + 1000000000 / 100;
 	uint64_t local_loops = 0;
@@ -126,7 +142,7 @@
 		local_loops++;
 	} while(t < end);
 
-	loops[smp_id()] = local_loops;
+	loops[i] = local_loops;
 }
 
 static void perf_test(int ncpus)
@@ -134,7 +150,11 @@
 	int i;
 	uint64_t total_loops;
 
-	on_cpus(hv_perf_test, NULL);
+	for (i = ncpus - 1; i >= 0; i--)
+		on_cpu_async(i, hv_perf_test, (void *)(long)i);
+
+	while (cpus_active() > 1)
+		pause();
 
 	total_loops = 0;
 	for (i = ncpus - 1; i >= 0; i--)
@@ -144,7 +164,6 @@
 
 int main(int ac, char **av)
 {
-	int nerr = 0;
 	int ncpus;
 	struct hv_reference_tsc_page shadow;
 	uint64_t tsc1, t1, tsc2, t2;
@@ -152,7 +171,7 @@
 
 	if (!hv_time_ref_counter_supported()) {
 		report_skip("time reference counter is unsupported");
-		return report_summary();
+		goto done;
 	}
 
 	setup_vm();
@@ -167,12 +186,11 @@
 	       "MSR value after enabling");
 
 	hvclock_get_time_values(&shadow, hv_clock);
-	if (shadow.tsc_sequence == 0 || shadow.tsc_sequence == 0xFFFFFFFF) {
-		printf("Reference TSC page not available\n");
-		exit(1);
-	}
+	if (shadow.tsc_sequence == 0 || shadow.tsc_sequence == 0xFFFFFFFF)
+		report_abort("Reference TSC page not available\n");
 
-	printf("scale: %" PRIx64" offset: %" PRId64"\n", shadow.tsc_scale, shadow.tsc_offset);
+	printf("sequence: %u. scale: %" PRIx64" offset: %" PRId64"\n",
+	       shadow.tsc_sequence, shadow.tsc_scale, shadow.tsc_offset);
 	ref1 = rdmsr(HV_X64_MSR_TIME_REF_COUNT);
 	tsc1 = rdtsc();
 	t1 = hvclock_tsc_to_ticks(&shadow, tsc1);
@@ -196,5 +214,6 @@
 	report(rdmsr(HV_X64_MSR_REFERENCE_TSC) == 0,
 	       "MSR value after disabling");
 
-	return nerr > 0 ? 1 : 0;
+done:
+	return report_summary();
 }
diff --git a/x86/hyperv_connections.c b/x86/hyperv_connections.c
index cf04366..2c68415 100644
--- a/x86/hyperv_connections.c
+++ b/x86/hyperv_connections.c
@@ -266,7 +266,7 @@
 {
 	int ncpus, ncpus_ok, i;
 
-	if (!synic_supported()) {
+	if (!hv_synic_supported()) {
 		report_skip("Hyper-V SynIC is not supported");
 		goto summary;
 	}
diff --git a/x86/hyperv_stimer.c b/x86/hyperv_stimer.c
index f7c6791..e50903b 100644
--- a/x86/hyperv_stimer.c
+++ b/x86/hyperv_stimer.c
@@ -16,6 +16,9 @@
 #define SINT1_VEC 0xF1
 #define SINT2_VEC 0xF2
 
+#define APIC_VEC1 0xF3
+#define APIC_VEC2 0xF4
+
 #define SINT1_NUM 2
 #define SINT2_NUM 3
 #define ONE_MS_IN_100NS 10000
@@ -25,6 +28,8 @@
 struct stimer {
     int sint;
     int index;
+    bool direct;
+    int apic_vec;
     atomic_t fire_count;
 };
 
@@ -81,8 +86,7 @@
     wrmsr(HV_X64_MSR_STIMER0_CONFIG + 2*timer->index, 0);
 }
 
-static void process_stimer_expired(struct svcpu *svcpu, struct stimer *timer,
-                                   u64 expiration_time, u64 delivery_time)
+static void process_stimer_expired(struct stimer *timer)
 {
     atomic_inc(&timer->fire_count);
 }
@@ -119,8 +123,14 @@
         abort();
     }
     timer = &svcpu->timer[payload->timer_index];
-    process_stimer_expired(svcpu, timer, payload->expiration_time,
-                          payload->delivery_time);
+
+    if (timer->direct) {
+        report(false, "VMBus message in direct mode received");
+        report_summary();
+        abort();
+    }
+
+    process_stimer_expired(timer);
 
     msg->header.message_type = HVMSG_NONE;
     mb();
@@ -159,24 +169,49 @@
     __stimer_isr(vcpu);
 }
 
+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 sint)
+                         u64 tick_100ns)
 {
-    u64 config, count;
+    u64 count;
+    union hv_stimer_config config = {.as_uint64 = 0};
 
-    timer->sint = sint;
     atomic_set(&timer->fire_count, 0);
 
-    config = 0;
-    if (periodic) {
-        config |= HV_STIMER_PERIODIC;
-    }
-
-    config |= ((u8)(sint & 0xFF)) << 16;
-    config |= HV_STIMER_ENABLE;
-    if (auto_enable) {
-        config |= HV_STIMER_AUTOENABLE;
+    config.periodic = periodic;
+    config.enable = 1;
+    config.auto_enable = auto_enable;
+    if (!timer->direct) {
+        config.sintx = timer->sint;
+    } else {
+        config.direct_mode = 1;
+        config.apic_vector = timer->apic_vec;
     }
 
     if (periodic) {
@@ -187,9 +222,9 @@
 
     if (!auto_enable) {
         wrmsr(HV_X64_MSR_STIMER0_COUNT + timer->index*2, count);
-        wrmsr(HV_X64_MSR_STIMER0_CONFIG + timer->index*2, config);
+        wrmsr(HV_X64_MSR_STIMER0_CONFIG + timer->index*2, config.as_uint64);
     } else {
-        wrmsr(HV_X64_MSR_STIMER0_CONFIG + timer->index*2, config);
+        wrmsr(HV_X64_MSR_STIMER0_CONFIG + timer->index*2, config.as_uint64);
         wrmsr(HV_X64_MSR_STIMER0_COUNT + timer->index*2, count);
     }
 }
@@ -218,18 +253,51 @@
 
 static void stimer_test_prepare(void *ctx)
 {
+    int vcpu = smp_id();
+    struct svcpu *svcpu = &g_synic_vcpu[vcpu];
+    struct stimer *timer1, *timer2;
+
     write_cr3((ulong)ctx);
     synic_enable();
+
     synic_sint_create(SINT1_NUM, SINT1_VEC, false);
     synic_sint_create(SINT2_NUM, SINT2_VEC, true);
+
+    timer1 = &svcpu->timer[0];
+    timer2 = &svcpu->timer[1];
+
+    timer1->sint = SINT1_NUM;
+    timer2->sint = SINT2_NUM;
 }
 
+static void stimer_test_prepare_direct(void *ctx)
+{
+    int vcpu = smp_id();
+    struct svcpu *svcpu = &g_synic_vcpu[vcpu];
+    struct stimer *timer1, *timer2;
+
+    write_cr3((ulong)ctx);
+
+    timer1 = &svcpu->timer[0];
+    timer2 = &svcpu->timer[1];
+
+    stimer_init(timer1, 0);
+    stimer_init(timer2, 1);
+
+    timer1->apic_vec = APIC_VEC1;
+    timer2->apic_vec = APIC_VEC2;
+
+    timer1->direct = true;
+    timer2->direct = true;
+}
+
+
 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, SINT1_NUM);
-    stimer_start(timer2, false, true, ONE_MS_IN_100NS, SINT2_NUM);
+    stimer_start(timer1, false, true, ONE_MS_IN_100NS);
+    stimer_start(timer2, false, true, ONE_MS_IN_100NS);
     while ((atomic_read(&timer1->fire_count) < 1000) ||
            (atomic_read(&timer2->fire_count) < 1000)) {
         pause();
@@ -242,7 +310,7 @@
 static void stimer_test_one_shot(int vcpu, struct stimer *timer)
 {
     /* Check one-shot timer */
-    stimer_start(timer, false, false, ONE_MS_IN_100NS, SINT1_NUM);
+    stimer_start(timer, false, false, ONE_MS_IN_100NS);
     while (atomic_read(&timer->fire_count) < 1) {
         pause();
     }
@@ -253,7 +321,7 @@
 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, SINT1_NUM);
+    stimer_start(timer, true, false, ONE_MS_IN_100NS);
     while (atomic_read(&timer->fire_count) < 1) {
         pause();
     }
@@ -264,7 +332,7 @@
 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, SINT1_NUM);
+    stimer_start(timer, true, true, ONE_MS_IN_100NS);
     while (atomic_read(&timer->fire_count) < 1000) {
         pause();
     }
@@ -274,13 +342,20 @@
 
 static void stimer_test_one_shot_busy(int vcpu, struct stimer *timer)
 {
-    struct hv_message_page *msg_page = g_synic_vcpu[vcpu].msg_page;
-    struct hv_message *msg = &msg_page->sint_message[timer->sint];
+    struct hv_message_page *msg_page;
+    struct hv_message *msg;
+
+    /* Skipping msg slot busy test in direct mode */
+    if (timer->direct)
+        return;
+
+    msg_page = g_synic_vcpu[vcpu].msg_page;
+    msg = &msg_page->sint_message[timer->sint];
 
     msg->header.message_type = HVMSG_TIMER_EXPIRED;
     wmb();
 
-    stimer_start(timer, false, false, ONE_MS_IN_100NS, SINT1_NUM);
+    stimer_start(timer, false, false, ONE_MS_IN_100NS);
 
     do
         rmb();
@@ -329,7 +404,12 @@
     synic_disable();
 }
 
-static void stimer_test_all(void)
+static void stimer_test_cleanup_direct(void *ctx)
+{
+    stimers_shutdown();
+}
+
+static void stimer_test_all(bool direct)
 {
     int ncpus;
 
@@ -341,33 +421,51 @@
         report_abort("number cpus exceeds %d", MAX_CPUS);
     printf("cpus = %d\n", ncpus);
 
-    handle_irq(SINT1_VEC, stimer_isr);
-    handle_irq(SINT2_VEC, stimer_isr_auto_eoi);
+    if (!direct) {
+        printf("Starting Hyper-V SynIC timers tests: message mode\n");
 
-    on_cpus(stimer_test_prepare, (void *)read_cr3());
-    on_cpus(stimer_test, NULL);
-    on_cpus(stimer_test_cleanup, NULL);
+        handle_irq(SINT1_VEC, stimer_isr);
+        handle_irq(SINT2_VEC, stimer_isr_auto_eoi);
+
+	on_cpus(stimer_test_prepare, (void *)read_cr3());
+	on_cpus(stimer_test, NULL);
+	on_cpus(stimer_test_cleanup, NULL);
+    } else {
+        printf("Starting Hyper-V SynIC timers tests: direct mode\n");
+
+        handle_irq(APIC_VEC1, stimer_isr_direct1);
+        handle_irq(APIC_VEC2, stimer_isr_direct2);
+
+        on_cpus(stimer_test_prepare_direct, (void *)read_cr3());
+        on_cpus(stimer_test, NULL);
+        on_cpus(stimer_test_cleanup_direct, NULL);
+    }
 }
 
-int main(int ac, char **av)
+int main(int argc, char **argv)
 {
+    bool direct = argc >= 2 && !strcmp(argv[1], "direct");
 
-    if (!synic_supported()) {
-        report_pass("Hyper-V SynIC is not supported");
+    if (!hv_synic_supported()) {
+        report_skip("Hyper-V SynIC is not supported");
         goto done;
     }
 
-    if (!stimer_supported()) {
-        report_pass("Hyper-V SynIC timers are not supported");
+    if (!hv_stimer_supported()) {
+        report_skip("Hyper-V SynIC timers are not supported");
         goto done;
     }
 
     if (!hv_time_ref_counter_supported()) {
-        report_pass("Hyper-V time reference counter is not supported");
+        report_skip("Hyper-V time reference counter is not supported");
         goto done;
     }
 
-    stimer_test_all();
+    if (direct && !stimer_direct_supported()) {
+	report_skip("Hyper-V SinIC timer direct mode is not supported");
+    }
+
+    stimer_test_all(direct);
 done:
     return report_summary();
 }
diff --git a/x86/hyperv_synic.c b/x86/hyperv_synic.c
index 9d61d83..761df8a 100644
--- a/x86/hyperv_synic.c
+++ b/x86/hyperv_synic.c
@@ -141,45 +141,46 @@
 
 int main(int ac, char **av)
 {
+    int ncpus, i;
+    bool ok;
 
-    if (synic_supported()) {
-        int ncpus, i;
-        bool ok;
-
-        setup_vm();
-        enable_apic();
-
-        ncpus = cpu_count();
-        if (ncpus > MAX_CPUS)
-            report_abort("number cpus exceeds %d", MAX_CPUS);
-        printf("ncpus = %d\n", ncpus);
-
-        synic_prepare_sint_vecs();
-
-        printf("prepare\n");
-        on_cpus(synic_test_prepare, (void *)read_cr3());
-
-        for (i = 0; i < ncpus; i++) {
-            printf("test %d -> %d\n", i, ncpus - 1 - i);
-            on_cpu_async(i, synic_test, (void *)(ulong)(ncpus - 1 - i));
-        }
-        while (cpus_active() > 1)
-            pause();
-
-        printf("cleanup\n");
-        on_cpus(synic_test_cleanup, NULL);
-
-        ok = true;
-        for (i = 0; i < ncpus; ++i) {
-            printf("isr_enter_count[%d] = %d\n",
-                   i, atomic_read(&isr_enter_count[i]));
-            ok &= atomic_read(&isr_enter_count[i]) == 16;
-        }
-
-        report(ok, "Hyper-V SynIC test");
-    } else {
-        printf("Hyper-V SynIC is not supported");
+    if (!hv_synic_supported()) {
+	report_skip("Hyper-V SynIC is not supported");
+	goto done;
     }
 
+    setup_vm();
+    enable_apic();
+
+    ncpus = cpu_count();
+    if (ncpus > MAX_CPUS)
+	report_abort("number cpus exceeds %d", MAX_CPUS);
+    printf("ncpus = %d\n", ncpus);
+
+    synic_prepare_sint_vecs();
+
+    printf("prepare\n");
+    on_cpus(synic_test_prepare, (void *)read_cr3());
+
+    for (i = 0; i < ncpus; i++) {
+	printf("test %d -> %d\n", i, ncpus - 1 - i);
+	on_cpu_async(i, synic_test, (void *)(ulong)(ncpus - 1 - i));
+    }
+    while (cpus_active() > 1)
+	pause();
+
+    printf("cleanup\n");
+    on_cpus(synic_test_cleanup, NULL);
+
+    ok = true;
+    for (i = 0; i < ncpus; ++i) {
+	printf("isr_enter_count[%d] = %d\n",
+	       i, atomic_read(&isr_enter_count[i]));
+	ok &= atomic_read(&isr_enter_count[i]) == 16;
+    }
+
+    report(ok, "Hyper-V SynIC test");
+
+done:
     return report_summary();
 }
diff --git a/x86/unittests.cfg b/x86/unittests.cfg
index 3fe5944..124be7a 100644
--- a/x86/unittests.cfg
+++ b/x86/unittests.cfg
@@ -451,25 +451,31 @@
 [hyperv_synic]
 file = hyperv_synic.flat
 smp = 2
-extra_params = -cpu kvm64,hv_vpindex,hv_synic -device hyperv-testdev
+extra_params = -cpu host,hv_passthrough -device hyperv-testdev
 groups = hyperv
 
 [hyperv_connections]
 file = hyperv_connections.flat
 smp = 2
-extra_params = -cpu kvm64,hv_vpindex,hv_synic -device hyperv-testdev
+extra_params = -cpu host,hv_passthrough -device hyperv-testdev
 groups = hyperv
 
 [hyperv_stimer]
 file = hyperv_stimer.flat
 smp = 2
-extra_params = -cpu kvm64,hv_vpindex,hv_time,hv_synic,hv_stimer -device hyperv-testdev
+extra_params = -cpu host,hv_passthrough
+groups = hyperv
+
+[hyperv_stimer_direct]
+file = hyperv_stimer.flat
+smp = 2
+extra_params = -cpu host,hv_passthrough -append direct
 groups = hyperv
 
 [hyperv_clock]
 file = hyperv_clock.flat
 smp = 2
-extra_params = -cpu kvm64,hv_time
+extra_params = -cpu host,hv_passthrough
 arch = x86_64
 groups = hyperv
 check = /sys/devices/system/clocksource/clocksource0/current_clocksource=tsc