xref: /kvm-unit-tests/x86/hyperv_connections.c (revision 5aca024ecf2c01430d0993df439374c46c6f2a29)
11472a384SRoman Kagan #include "libcflat.h"
21472a384SRoman Kagan #include "vm.h"
31472a384SRoman Kagan #include "smp.h"
41472a384SRoman Kagan #include "isr.h"
51472a384SRoman Kagan #include "atomic.h"
61472a384SRoman Kagan #include "hyperv.h"
71472a384SRoman Kagan #include "bitops.h"
8*5aca024eSPaolo Bonzini #include "alloc_page.h"
91472a384SRoman Kagan 
101472a384SRoman Kagan #define MAX_CPUS 64
111472a384SRoman Kagan 
121472a384SRoman Kagan #define MSG_VEC 0xb0
131472a384SRoman Kagan #define EVT_VEC 0xb1
141472a384SRoman Kagan #define MSG_SINT 0x8
151472a384SRoman Kagan #define EVT_SINT 0x9
161472a384SRoman Kagan #define MSG_CONN_BASE 0x10
171472a384SRoman Kagan #define EVT_CONN_BASE 0x20
181472a384SRoman Kagan #define MSG_TYPE 0x12345678
191472a384SRoman Kagan 
201472a384SRoman Kagan #define WAIT_CYCLES 10000000
211472a384SRoman Kagan 
221472a384SRoman Kagan static atomic_t ncpus_done;
231472a384SRoman Kagan 
241472a384SRoman Kagan struct hv_vcpu {
251472a384SRoman Kagan 	struct hv_message_page *msg_page;
261472a384SRoman Kagan 	struct hv_event_flags_page *evt_page;
271472a384SRoman Kagan 	struct hv_input_post_message *post_msg;
281472a384SRoman Kagan 	u8 msg_conn;
291472a384SRoman Kagan 	u8 evt_conn;
301472a384SRoman Kagan 	u64 hvcall_status;
311472a384SRoman Kagan 	atomic_t sint_received;
321472a384SRoman Kagan };
331472a384SRoman Kagan 
341472a384SRoman Kagan static struct hv_vcpu hv_vcpus[MAX_CPUS];
351472a384SRoman Kagan 
361472a384SRoman Kagan static void sint_isr(isr_regs_t *regs)
371472a384SRoman Kagan {
381472a384SRoman Kagan 	atomic_inc(&hv_vcpus[smp_id()].sint_received);
391472a384SRoman Kagan }
401472a384SRoman Kagan 
411472a384SRoman Kagan static void *hypercall_page;
421472a384SRoman Kagan 
431472a384SRoman Kagan static void setup_hypercall(void)
441472a384SRoman Kagan {
451472a384SRoman Kagan 	u64 guestid = (0x8f00ull << 48);
461472a384SRoman Kagan 
471472a384SRoman Kagan 	hypercall_page = alloc_page();
481472a384SRoman Kagan 	if (!hypercall_page)
491472a384SRoman Kagan 		report_abort("failed to allocate hypercall page");
501472a384SRoman Kagan 	memset(hypercall_page, 0, PAGE_SIZE);
511472a384SRoman Kagan 
521472a384SRoman Kagan 	wrmsr(HV_X64_MSR_GUEST_OS_ID, guestid);
531472a384SRoman Kagan 
541472a384SRoman Kagan 	wrmsr(HV_X64_MSR_HYPERCALL,
551472a384SRoman Kagan 	      (u64)virt_to_phys(hypercall_page) | HV_X64_MSR_HYPERCALL_ENABLE);
561472a384SRoman Kagan }
571472a384SRoman Kagan 
581472a384SRoman Kagan static void teardown_hypercall(void)
591472a384SRoman Kagan {
601472a384SRoman Kagan 	wrmsr(HV_X64_MSR_HYPERCALL, 0);
611472a384SRoman Kagan 	wrmsr(HV_X64_MSR_GUEST_OS_ID, 0);
621472a384SRoman Kagan 	free_page(hypercall_page);
631472a384SRoman Kagan }
641472a384SRoman Kagan 
651472a384SRoman Kagan static u64 do_hypercall(u16 code, u64 arg, bool fast)
661472a384SRoman Kagan {
671472a384SRoman Kagan 	u64 ret;
681472a384SRoman Kagan 	u64 ctl = code;
691472a384SRoman Kagan 	if (fast)
701472a384SRoman Kagan 		ctl |= HV_HYPERCALL_FAST;
711472a384SRoman Kagan 
721472a384SRoman Kagan 	asm volatile ("call *%[hcall_page]"
731472a384SRoman Kagan #ifdef __x86_64__
741472a384SRoman Kagan 		      "\n mov $0,%%r8"
751472a384SRoman Kagan 		      : "=a"(ret)
761472a384SRoman Kagan 		      : "c"(ctl), "d"(arg),
771472a384SRoman Kagan #else
781472a384SRoman Kagan 		      : "=A"(ret)
791472a384SRoman Kagan 		      : "A"(ctl),
801472a384SRoman Kagan 			"b" ((u32)(arg >> 32)), "c" ((u32)arg),
811472a384SRoman Kagan 			"D"(0), "S"(0),
821472a384SRoman Kagan #endif
831472a384SRoman Kagan 		      [hcall_page] "m" (hypercall_page)
841472a384SRoman Kagan #ifdef __x86_64__
851472a384SRoman Kagan 		      : "r8"
861472a384SRoman Kagan #endif
871472a384SRoman Kagan 		     );
881472a384SRoman Kagan 
891472a384SRoman Kagan 	return ret;
901472a384SRoman Kagan }
911472a384SRoman Kagan 
921472a384SRoman Kagan static void setup_cpu(void *ctx)
931472a384SRoman Kagan {
941472a384SRoman Kagan 	int vcpu;
951472a384SRoman Kagan 	struct hv_vcpu *hv;
961472a384SRoman Kagan 
971472a384SRoman Kagan 	write_cr3((ulong)ctx);
981472a384SRoman Kagan 	irq_enable();
991472a384SRoman Kagan 
1001472a384SRoman Kagan 	vcpu = smp_id();
1011472a384SRoman Kagan 	hv = &hv_vcpus[vcpu];
1021472a384SRoman Kagan 
1031472a384SRoman Kagan 	hv->msg_page = alloc_page();
1041472a384SRoman Kagan 	hv->evt_page = alloc_page();
1051472a384SRoman Kagan 	hv->post_msg = alloc_page();
1061472a384SRoman Kagan 	if (!hv->msg_page || !hv->evt_page || !hv->post_msg)
1071472a384SRoman Kagan 		report_abort("failed to allocate synic pages for vcpu");
1081472a384SRoman Kagan 	memset(hv->msg_page, 0, sizeof(*hv->msg_page));
1091472a384SRoman Kagan 	memset(hv->evt_page, 0, sizeof(*hv->evt_page));
1101472a384SRoman Kagan 	memset(hv->post_msg, 0, sizeof(*hv->post_msg));
1111472a384SRoman Kagan 	hv->msg_conn = MSG_CONN_BASE + vcpu;
1121472a384SRoman Kagan 	hv->evt_conn = EVT_CONN_BASE + vcpu;
1131472a384SRoman Kagan 
1141472a384SRoman Kagan 	wrmsr(HV_X64_MSR_SIMP,
1151472a384SRoman Kagan 	      (u64)virt_to_phys(hv->msg_page) | HV_SYNIC_SIMP_ENABLE);
1161472a384SRoman Kagan 	wrmsr(HV_X64_MSR_SIEFP,
1171472a384SRoman Kagan 	      (u64)virt_to_phys(hv->evt_page) | HV_SYNIC_SIEFP_ENABLE);
1181472a384SRoman Kagan 	wrmsr(HV_X64_MSR_SCONTROL, HV_SYNIC_CONTROL_ENABLE);
1191472a384SRoman Kagan 
1201472a384SRoman Kagan 	msg_conn_create(MSG_SINT, MSG_VEC, hv->msg_conn);
1211472a384SRoman Kagan 	evt_conn_create(EVT_SINT, EVT_VEC, hv->evt_conn);
1221472a384SRoman Kagan 
1231472a384SRoman Kagan 	hv->post_msg->connectionid = hv->msg_conn;
1241472a384SRoman Kagan 	hv->post_msg->message_type = MSG_TYPE;
1251472a384SRoman Kagan 	hv->post_msg->payload_size = 8;
1261472a384SRoman Kagan 	hv->post_msg->payload[0] = (u64)vcpu << 16;
1271472a384SRoman Kagan }
1281472a384SRoman Kagan 
1291472a384SRoman Kagan static void teardown_cpu(void *ctx)
1301472a384SRoman Kagan {
1311472a384SRoman Kagan 	int vcpu = smp_id();
1321472a384SRoman Kagan 	struct hv_vcpu *hv = &hv_vcpus[vcpu];
1331472a384SRoman Kagan 
1341472a384SRoman Kagan 	evt_conn_destroy(EVT_SINT, hv->evt_conn);
1351472a384SRoman Kagan 	msg_conn_destroy(MSG_SINT, hv->msg_conn);
1361472a384SRoman Kagan 
1371472a384SRoman Kagan 	wrmsr(HV_X64_MSR_SCONTROL, 0);
1381472a384SRoman Kagan 	wrmsr(HV_X64_MSR_SIEFP, 0);
1391472a384SRoman Kagan 	wrmsr(HV_X64_MSR_SIMP, 0);
1401472a384SRoman Kagan 
1411472a384SRoman Kagan 	free_page(hv->post_msg);
1421472a384SRoman Kagan 	free_page(hv->evt_page);
1431472a384SRoman Kagan 	free_page(hv->msg_page);
1441472a384SRoman Kagan }
1451472a384SRoman Kagan 
1461472a384SRoman Kagan static void do_msg(void *ctx)
1471472a384SRoman Kagan {
1481472a384SRoman Kagan 	int vcpu = (ulong)ctx;
1491472a384SRoman Kagan 	struct hv_vcpu *hv = &hv_vcpus[vcpu];
1501472a384SRoman Kagan 	struct hv_input_post_message *msg = hv->post_msg;
1511472a384SRoman Kagan 
1521472a384SRoman Kagan 	msg->payload[0]++;
1531472a384SRoman Kagan 	atomic_set(&hv->sint_received, 0);
1541472a384SRoman Kagan 	hv->hvcall_status = do_hypercall(HVCALL_POST_MESSAGE,
1551472a384SRoman Kagan 					 virt_to_phys(msg), 0);
1561472a384SRoman Kagan 	atomic_inc(&ncpus_done);
1571472a384SRoman Kagan }
1581472a384SRoman Kagan 
1591472a384SRoman Kagan static void clear_msg(void *ctx)
1601472a384SRoman Kagan {
1611472a384SRoman Kagan 	/* should only be done on the current vcpu */
1621472a384SRoman Kagan 	int vcpu = smp_id();
1631472a384SRoman Kagan 	struct hv_vcpu *hv = &hv_vcpus[vcpu];
1641472a384SRoman Kagan 	struct hv_message *msg = &hv->msg_page->sint_message[MSG_SINT];
1651472a384SRoman Kagan 
1661472a384SRoman Kagan 	atomic_set(&hv->sint_received, 0);
1671472a384SRoman Kagan 	msg->header.message_type = 0;
1681472a384SRoman Kagan 	barrier();
1691472a384SRoman Kagan 	wrmsr(HV_X64_MSR_EOM, 0);
1701472a384SRoman Kagan 	atomic_inc(&ncpus_done);
1711472a384SRoman Kagan }
1721472a384SRoman Kagan 
1731472a384SRoman Kagan static bool msg_ok(int vcpu)
1741472a384SRoman Kagan {
1751472a384SRoman Kagan 	struct hv_vcpu *hv = &hv_vcpus[vcpu];
1761472a384SRoman Kagan 	struct hv_input_post_message *post_msg = hv->post_msg;
1771472a384SRoman Kagan 	struct hv_message *msg = &hv->msg_page->sint_message[MSG_SINT];
1781472a384SRoman Kagan 
1791472a384SRoman Kagan 	return msg->header.message_type == post_msg->message_type &&
1801472a384SRoman Kagan 		msg->header.payload_size == post_msg->payload_size &&
1811472a384SRoman Kagan 		msg->header.message_flags.msg_pending == 0 &&
1821472a384SRoman Kagan 		msg->u.payload[0] == post_msg->payload[0] &&
1831472a384SRoman Kagan 		hv->hvcall_status == 0 &&
1841472a384SRoman Kagan 		atomic_read(&hv->sint_received) == 1;
1851472a384SRoman Kagan }
1861472a384SRoman Kagan 
1871472a384SRoman Kagan static bool msg_busy(int vcpu)
1881472a384SRoman Kagan {
1891472a384SRoman Kagan 	struct hv_vcpu *hv = &hv_vcpus[vcpu];
1901472a384SRoman Kagan 	struct hv_input_post_message *post_msg = hv->post_msg;
1911472a384SRoman Kagan 	struct hv_message *msg = &hv->msg_page->sint_message[MSG_SINT];
1921472a384SRoman Kagan 
1931472a384SRoman Kagan 	return msg->header.message_type == post_msg->message_type &&
1941472a384SRoman Kagan 		msg->header.payload_size == post_msg->payload_size &&
1951472a384SRoman Kagan 		msg->header.message_flags.msg_pending == 1 &&
1961472a384SRoman Kagan 		msg->u.payload[0] == post_msg->payload[0] - 1 &&
1971472a384SRoman Kagan 		hv->hvcall_status == 0 &&
1981472a384SRoman Kagan 		atomic_read(&hv->sint_received) == 0;
1991472a384SRoman Kagan }
2001472a384SRoman Kagan 
2011472a384SRoman Kagan static void do_evt(void *ctx)
2021472a384SRoman Kagan {
2031472a384SRoman Kagan 	int vcpu = (ulong)ctx;
2041472a384SRoman Kagan 	struct hv_vcpu *hv = &hv_vcpus[vcpu];
2051472a384SRoman Kagan 
2061472a384SRoman Kagan 	atomic_set(&hv->sint_received, 0);
2071472a384SRoman Kagan 	hv->hvcall_status = do_hypercall(HVCALL_SIGNAL_EVENT,
2081472a384SRoman Kagan 					 hv->evt_conn, 1);
2091472a384SRoman Kagan 	atomic_inc(&ncpus_done);
2101472a384SRoman Kagan }
2111472a384SRoman Kagan 
2121472a384SRoman Kagan static void clear_evt(void *ctx)
2131472a384SRoman Kagan {
2141472a384SRoman Kagan 	/* should only be done on the current vcpu */
2151472a384SRoman Kagan 	int vcpu = smp_id();
2161472a384SRoman Kagan 	struct hv_vcpu *hv = &hv_vcpus[vcpu];
2171472a384SRoman Kagan 	ulong *flags = hv->evt_page->slot[EVT_SINT].flags;
2181472a384SRoman Kagan 
2191472a384SRoman Kagan 	atomic_set(&hv->sint_received, 0);
2201472a384SRoman Kagan 	flags[BIT_WORD(hv->evt_conn)] &= ~BIT_MASK(hv->evt_conn);
2211472a384SRoman Kagan 	barrier();
2221472a384SRoman Kagan 	atomic_inc(&ncpus_done);
2231472a384SRoman Kagan }
2241472a384SRoman Kagan 
2251472a384SRoman Kagan static bool evt_ok(int vcpu)
2261472a384SRoman Kagan {
2271472a384SRoman Kagan 	struct hv_vcpu *hv = &hv_vcpus[vcpu];
2281472a384SRoman Kagan 	ulong *flags = hv->evt_page->slot[EVT_SINT].flags;
2291472a384SRoman Kagan 
2301472a384SRoman Kagan 	return flags[BIT_WORD(hv->evt_conn)] == BIT_MASK(hv->evt_conn) &&
2311472a384SRoman Kagan 		hv->hvcall_status == 0 &&
2321472a384SRoman Kagan 		atomic_read(&hv->sint_received) == 1;
2331472a384SRoman Kagan }
2341472a384SRoman Kagan 
2351472a384SRoman Kagan static bool evt_busy(int vcpu)
2361472a384SRoman Kagan {
2371472a384SRoman Kagan 	struct hv_vcpu *hv = &hv_vcpus[vcpu];
2381472a384SRoman Kagan 	ulong *flags = hv->evt_page->slot[EVT_SINT].flags;
2391472a384SRoman Kagan 
2401472a384SRoman Kagan 	return flags[BIT_WORD(hv->evt_conn)] == BIT_MASK(hv->evt_conn) &&
2411472a384SRoman Kagan 		hv->hvcall_status == 0 &&
2421472a384SRoman Kagan 		atomic_read(&hv->sint_received) == 0;
2431472a384SRoman Kagan }
2441472a384SRoman Kagan 
2451472a384SRoman Kagan static int run_test(int ncpus, int dst_add, ulong wait_cycles,
2461472a384SRoman Kagan 		    void (*func)(void *), bool (*is_ok)(int))
2471472a384SRoman Kagan {
2481472a384SRoman Kagan 	int i, ret = 0;
2491472a384SRoman Kagan 
2501472a384SRoman Kagan 	atomic_set(&ncpus_done, 0);
2511472a384SRoman Kagan 	for (i = 0; i < ncpus; i++) {
2521472a384SRoman Kagan 		ulong dst = (i + dst_add) % ncpus;
2531472a384SRoman Kagan 		on_cpu_async(i, func, (void *)dst);
2541472a384SRoman Kagan 	}
2551472a384SRoman Kagan 	while (atomic_read(&ncpus_done) != ncpus)
2561472a384SRoman Kagan 		pause();
2571472a384SRoman Kagan 
2581472a384SRoman Kagan 	while (wait_cycles--)
2591472a384SRoman Kagan 		pause();
2601472a384SRoman Kagan 
2611472a384SRoman Kagan 	if (is_ok)
2621472a384SRoman Kagan 		for (i = 0; i < ncpus; i++)
2631472a384SRoman Kagan 			ret += is_ok(i);
2641472a384SRoman Kagan 	return ret;
2651472a384SRoman Kagan }
2661472a384SRoman Kagan 
2671472a384SRoman Kagan #define HV_STATUS_INVALID_HYPERCALL_CODE        2
2681472a384SRoman Kagan 
2691472a384SRoman Kagan int main(int ac, char **av)
2701472a384SRoman Kagan {
2711472a384SRoman Kagan 	int ncpus, ncpus_ok, i;
2721472a384SRoman Kagan 
2731472a384SRoman Kagan 	if (!synic_supported()) {
2741472a384SRoman Kagan 		report_skip("Hyper-V SynIC is not supported");
2751472a384SRoman Kagan 		goto summary;
2761472a384SRoman Kagan 	}
2771472a384SRoman Kagan 
2781472a384SRoman Kagan 	setup_vm();
2791472a384SRoman Kagan 	smp_init();
2801472a384SRoman Kagan 	ncpus = cpu_count();
2811472a384SRoman Kagan 	if (ncpus > MAX_CPUS)
2821472a384SRoman Kagan 		report_abort("# cpus: %d > %d", ncpus, MAX_CPUS);
2831472a384SRoman Kagan 
2841472a384SRoman Kagan 	handle_irq(MSG_VEC, sint_isr);
2851472a384SRoman Kagan 	handle_irq(EVT_VEC, sint_isr);
2861472a384SRoman Kagan 
2871472a384SRoman Kagan 	setup_hypercall();
2881472a384SRoman Kagan 
2891472a384SRoman Kagan 	if (do_hypercall(HVCALL_SIGNAL_EVENT, 0x1234, 1) ==
2901472a384SRoman Kagan 	    HV_STATUS_INVALID_HYPERCALL_CODE) {
2911472a384SRoman Kagan 		report_skip("Hyper-V SynIC connections are not supported");
2921472a384SRoman Kagan 		goto summary;
2931472a384SRoman Kagan 	}
2941472a384SRoman Kagan 
2951472a384SRoman Kagan 	for (i = 0; i < ncpus; i++)
2961472a384SRoman Kagan 		on_cpu(i, setup_cpu, (void *)read_cr3());
2971472a384SRoman Kagan 
2981472a384SRoman Kagan 	ncpus_ok = run_test(ncpus, 0, WAIT_CYCLES, do_msg, msg_ok);
2991472a384SRoman Kagan 	report("send message to self: %d/%d",
3001472a384SRoman Kagan 	       ncpus_ok == ncpus, ncpus_ok, ncpus);
3011472a384SRoman Kagan 
3021472a384SRoman Kagan 	run_test(ncpus, 0, 0, clear_msg, NULL);
3031472a384SRoman Kagan 
3041472a384SRoman Kagan 	ncpus_ok = run_test(ncpus, 1, WAIT_CYCLES, do_msg, msg_ok);
3051472a384SRoman Kagan 	report("send message to another cpu: %d/%d",
3061472a384SRoman Kagan 	       ncpus_ok == ncpus, ncpus_ok, ncpus);
3071472a384SRoman Kagan 
3081472a384SRoman Kagan 	ncpus_ok = run_test(ncpus, 1, WAIT_CYCLES, do_msg, msg_busy);
3091472a384SRoman Kagan 	report("send message to busy slot: %d/%d",
3101472a384SRoman Kagan 	       ncpus_ok == ncpus, ncpus_ok, ncpus);
3111472a384SRoman Kagan 
3121472a384SRoman Kagan 	ncpus_ok = run_test(ncpus, 0, WAIT_CYCLES, clear_msg, msg_ok);
3131472a384SRoman Kagan 	report("receive pending message: %d/%d",
3141472a384SRoman Kagan 	       ncpus_ok == ncpus, ncpus_ok, ncpus);
3151472a384SRoman Kagan 
3161472a384SRoman Kagan 	ncpus_ok = run_test(ncpus, 0, WAIT_CYCLES, do_evt, evt_ok);
3171472a384SRoman Kagan 	report("signal event on self: %d/%d",
3181472a384SRoman Kagan 	       ncpus_ok == ncpus, ncpus_ok, ncpus);
3191472a384SRoman Kagan 
3201472a384SRoman Kagan 	run_test(ncpus, 0, 0, clear_evt, NULL);
3211472a384SRoman Kagan 
3221472a384SRoman Kagan 	ncpus_ok = run_test(ncpus, 1, WAIT_CYCLES, do_evt, evt_ok);
3231472a384SRoman Kagan 	report("signal event on another cpu: %d/%d",
3241472a384SRoman Kagan 	       ncpus_ok == ncpus, ncpus_ok, ncpus);
3251472a384SRoman Kagan 
3261472a384SRoman Kagan 	ncpus_ok = run_test(ncpus, 1, WAIT_CYCLES, do_evt, evt_busy);
3271472a384SRoman Kagan 	report("signal event already set: %d/%d",
3281472a384SRoman Kagan 	       ncpus_ok == ncpus, ncpus_ok, ncpus);
3291472a384SRoman Kagan 
3301472a384SRoman Kagan 	for (i = 0; i < ncpus; i++)
3311472a384SRoman Kagan 		on_cpu(i, teardown_cpu, NULL);
3321472a384SRoman Kagan 
3331472a384SRoman Kagan 	teardown_hypercall();
3341472a384SRoman Kagan 
3351472a384SRoman Kagan summary:
3361472a384SRoman Kagan 	return report_summary();
3371472a384SRoman Kagan }
338