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" 85aca024eSPaolo 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 511472a384SRoman Kagan wrmsr(HV_X64_MSR_GUEST_OS_ID, guestid); 521472a384SRoman Kagan 531472a384SRoman Kagan wrmsr(HV_X64_MSR_HYPERCALL, 541472a384SRoman Kagan (u64)virt_to_phys(hypercall_page) | HV_X64_MSR_HYPERCALL_ENABLE); 551472a384SRoman Kagan } 561472a384SRoman Kagan 571472a384SRoman Kagan static void teardown_hypercall(void) 581472a384SRoman Kagan { 591472a384SRoman Kagan wrmsr(HV_X64_MSR_HYPERCALL, 0); 601472a384SRoman Kagan wrmsr(HV_X64_MSR_GUEST_OS_ID, 0); 611472a384SRoman Kagan free_page(hypercall_page); 621472a384SRoman Kagan } 631472a384SRoman Kagan 641472a384SRoman Kagan static u64 do_hypercall(u16 code, u64 arg, bool fast) 651472a384SRoman Kagan { 661472a384SRoman Kagan u64 ret; 671472a384SRoman Kagan u64 ctl = code; 681472a384SRoman Kagan if (fast) 691472a384SRoman Kagan ctl |= HV_HYPERCALL_FAST; 701472a384SRoman Kagan 711472a384SRoman Kagan asm volatile ("call *%[hcall_page]" 721472a384SRoman Kagan #ifdef __x86_64__ 731472a384SRoman Kagan "\n mov $0,%%r8" 741472a384SRoman Kagan : "=a"(ret) 751472a384SRoman Kagan : "c"(ctl), "d"(arg), 761472a384SRoman Kagan #else 771472a384SRoman Kagan : "=A"(ret) 781472a384SRoman Kagan : "A"(ctl), 791472a384SRoman Kagan "b" ((u32)(arg >> 32)), "c" ((u32)arg), 801472a384SRoman Kagan "D"(0), "S"(0), 811472a384SRoman Kagan #endif 821472a384SRoman Kagan [hcall_page] "m" (hypercall_page) 831472a384SRoman Kagan #ifdef __x86_64__ 841472a384SRoman Kagan : "r8" 851472a384SRoman Kagan #endif 861472a384SRoman Kagan ); 871472a384SRoman Kagan 881472a384SRoman Kagan return ret; 891472a384SRoman Kagan } 901472a384SRoman Kagan 911472a384SRoman Kagan static void setup_cpu(void *ctx) 921472a384SRoman Kagan { 931472a384SRoman Kagan int vcpu; 941472a384SRoman Kagan struct hv_vcpu *hv; 951472a384SRoman Kagan 961472a384SRoman Kagan write_cr3((ulong)ctx); 971472a384SRoman Kagan irq_enable(); 981472a384SRoman Kagan 991472a384SRoman Kagan vcpu = smp_id(); 1001472a384SRoman Kagan hv = &hv_vcpus[vcpu]; 1011472a384SRoman Kagan 1021472a384SRoman Kagan hv->msg_page = alloc_page(); 1031472a384SRoman Kagan hv->evt_page = alloc_page(); 1041472a384SRoman Kagan hv->post_msg = alloc_page(); 1051472a384SRoman Kagan if (!hv->msg_page || !hv->evt_page || !hv->post_msg) 1061472a384SRoman Kagan report_abort("failed to allocate synic pages for vcpu"); 1071472a384SRoman Kagan hv->msg_conn = MSG_CONN_BASE + vcpu; 1081472a384SRoman Kagan hv->evt_conn = EVT_CONN_BASE + vcpu; 1091472a384SRoman Kagan 1101472a384SRoman Kagan wrmsr(HV_X64_MSR_SIMP, 1111472a384SRoman Kagan (u64)virt_to_phys(hv->msg_page) | HV_SYNIC_SIMP_ENABLE); 1121472a384SRoman Kagan wrmsr(HV_X64_MSR_SIEFP, 1131472a384SRoman Kagan (u64)virt_to_phys(hv->evt_page) | HV_SYNIC_SIEFP_ENABLE); 1141472a384SRoman Kagan wrmsr(HV_X64_MSR_SCONTROL, HV_SYNIC_CONTROL_ENABLE); 1151472a384SRoman Kagan 1161472a384SRoman Kagan msg_conn_create(MSG_SINT, MSG_VEC, hv->msg_conn); 1171472a384SRoman Kagan evt_conn_create(EVT_SINT, EVT_VEC, hv->evt_conn); 1181472a384SRoman Kagan 1191472a384SRoman Kagan hv->post_msg->connectionid = hv->msg_conn; 1201472a384SRoman Kagan hv->post_msg->message_type = MSG_TYPE; 1211472a384SRoman Kagan hv->post_msg->payload_size = 8; 1221472a384SRoman Kagan hv->post_msg->payload[0] = (u64)vcpu << 16; 1231472a384SRoman Kagan } 1241472a384SRoman Kagan 1251472a384SRoman Kagan static void teardown_cpu(void *ctx) 1261472a384SRoman Kagan { 1271472a384SRoman Kagan int vcpu = smp_id(); 1281472a384SRoman Kagan struct hv_vcpu *hv = &hv_vcpus[vcpu]; 1291472a384SRoman Kagan 1301472a384SRoman Kagan evt_conn_destroy(EVT_SINT, hv->evt_conn); 1311472a384SRoman Kagan msg_conn_destroy(MSG_SINT, hv->msg_conn); 1321472a384SRoman Kagan 1331472a384SRoman Kagan wrmsr(HV_X64_MSR_SCONTROL, 0); 1341472a384SRoman Kagan wrmsr(HV_X64_MSR_SIEFP, 0); 1351472a384SRoman Kagan wrmsr(HV_X64_MSR_SIMP, 0); 1361472a384SRoman Kagan 1371472a384SRoman Kagan free_page(hv->post_msg); 1381472a384SRoman Kagan free_page(hv->evt_page); 1391472a384SRoman Kagan free_page(hv->msg_page); 1401472a384SRoman Kagan } 1411472a384SRoman Kagan 1421472a384SRoman Kagan static void do_msg(void *ctx) 1431472a384SRoman Kagan { 1441472a384SRoman Kagan int vcpu = (ulong)ctx; 1451472a384SRoman Kagan struct hv_vcpu *hv = &hv_vcpus[vcpu]; 1461472a384SRoman Kagan struct hv_input_post_message *msg = hv->post_msg; 1471472a384SRoman Kagan 1481472a384SRoman Kagan msg->payload[0]++; 1491472a384SRoman Kagan atomic_set(&hv->sint_received, 0); 1501472a384SRoman Kagan hv->hvcall_status = do_hypercall(HVCALL_POST_MESSAGE, 1511472a384SRoman Kagan virt_to_phys(msg), 0); 1521472a384SRoman Kagan atomic_inc(&ncpus_done); 1531472a384SRoman Kagan } 1541472a384SRoman Kagan 1551472a384SRoman Kagan static void clear_msg(void *ctx) 1561472a384SRoman Kagan { 1571472a384SRoman Kagan /* should only be done on the current vcpu */ 1581472a384SRoman Kagan int vcpu = smp_id(); 1591472a384SRoman Kagan struct hv_vcpu *hv = &hv_vcpus[vcpu]; 1601472a384SRoman Kagan struct hv_message *msg = &hv->msg_page->sint_message[MSG_SINT]; 1611472a384SRoman Kagan 1621472a384SRoman Kagan atomic_set(&hv->sint_received, 0); 1631472a384SRoman Kagan msg->header.message_type = 0; 1641472a384SRoman Kagan barrier(); 1651472a384SRoman Kagan wrmsr(HV_X64_MSR_EOM, 0); 1661472a384SRoman Kagan atomic_inc(&ncpus_done); 1671472a384SRoman Kagan } 1681472a384SRoman Kagan 1691472a384SRoman Kagan static bool msg_ok(int vcpu) 1701472a384SRoman Kagan { 1711472a384SRoman Kagan struct hv_vcpu *hv = &hv_vcpus[vcpu]; 1721472a384SRoman Kagan struct hv_input_post_message *post_msg = hv->post_msg; 1731472a384SRoman Kagan struct hv_message *msg = &hv->msg_page->sint_message[MSG_SINT]; 1741472a384SRoman Kagan 1751472a384SRoman Kagan return msg->header.message_type == post_msg->message_type && 1761472a384SRoman Kagan msg->header.payload_size == post_msg->payload_size && 1771472a384SRoman Kagan msg->header.message_flags.msg_pending == 0 && 1781472a384SRoman Kagan msg->u.payload[0] == post_msg->payload[0] && 1791472a384SRoman Kagan hv->hvcall_status == 0 && 1801472a384SRoman Kagan atomic_read(&hv->sint_received) == 1; 1811472a384SRoman Kagan } 1821472a384SRoman Kagan 1831472a384SRoman Kagan static bool msg_busy(int vcpu) 1841472a384SRoman Kagan { 1851472a384SRoman Kagan struct hv_vcpu *hv = &hv_vcpus[vcpu]; 1861472a384SRoman Kagan struct hv_input_post_message *post_msg = hv->post_msg; 1871472a384SRoman Kagan struct hv_message *msg = &hv->msg_page->sint_message[MSG_SINT]; 1881472a384SRoman Kagan 1891472a384SRoman Kagan return msg->header.message_type == post_msg->message_type && 1901472a384SRoman Kagan msg->header.payload_size == post_msg->payload_size && 1911472a384SRoman Kagan msg->header.message_flags.msg_pending == 1 && 1921472a384SRoman Kagan msg->u.payload[0] == post_msg->payload[0] - 1 && 1931472a384SRoman Kagan hv->hvcall_status == 0 && 1941472a384SRoman Kagan atomic_read(&hv->sint_received) == 0; 1951472a384SRoman Kagan } 1961472a384SRoman Kagan 1971472a384SRoman Kagan static void do_evt(void *ctx) 1981472a384SRoman Kagan { 1991472a384SRoman Kagan int vcpu = (ulong)ctx; 2001472a384SRoman Kagan struct hv_vcpu *hv = &hv_vcpus[vcpu]; 2011472a384SRoman Kagan 2021472a384SRoman Kagan atomic_set(&hv->sint_received, 0); 2031472a384SRoman Kagan hv->hvcall_status = do_hypercall(HVCALL_SIGNAL_EVENT, 2041472a384SRoman Kagan hv->evt_conn, 1); 2051472a384SRoman Kagan atomic_inc(&ncpus_done); 2061472a384SRoman Kagan } 2071472a384SRoman Kagan 2081472a384SRoman Kagan static void clear_evt(void *ctx) 2091472a384SRoman Kagan { 2101472a384SRoman Kagan /* should only be done on the current vcpu */ 2111472a384SRoman Kagan int vcpu = smp_id(); 2121472a384SRoman Kagan struct hv_vcpu *hv = &hv_vcpus[vcpu]; 2131472a384SRoman Kagan ulong *flags = hv->evt_page->slot[EVT_SINT].flags; 2141472a384SRoman Kagan 2151472a384SRoman Kagan atomic_set(&hv->sint_received, 0); 2161472a384SRoman Kagan flags[BIT_WORD(hv->evt_conn)] &= ~BIT_MASK(hv->evt_conn); 2171472a384SRoman Kagan barrier(); 2181472a384SRoman Kagan atomic_inc(&ncpus_done); 2191472a384SRoman Kagan } 2201472a384SRoman Kagan 2211472a384SRoman Kagan static bool evt_ok(int vcpu) 2221472a384SRoman Kagan { 2231472a384SRoman Kagan struct hv_vcpu *hv = &hv_vcpus[vcpu]; 2241472a384SRoman Kagan ulong *flags = hv->evt_page->slot[EVT_SINT].flags; 2251472a384SRoman Kagan 2261472a384SRoman Kagan return flags[BIT_WORD(hv->evt_conn)] == BIT_MASK(hv->evt_conn) && 2271472a384SRoman Kagan hv->hvcall_status == 0 && 2281472a384SRoman Kagan atomic_read(&hv->sint_received) == 1; 2291472a384SRoman Kagan } 2301472a384SRoman Kagan 2311472a384SRoman Kagan static bool evt_busy(int vcpu) 2321472a384SRoman Kagan { 2331472a384SRoman Kagan struct hv_vcpu *hv = &hv_vcpus[vcpu]; 2341472a384SRoman Kagan ulong *flags = hv->evt_page->slot[EVT_SINT].flags; 2351472a384SRoman Kagan 2361472a384SRoman Kagan return flags[BIT_WORD(hv->evt_conn)] == BIT_MASK(hv->evt_conn) && 2371472a384SRoman Kagan hv->hvcall_status == 0 && 2381472a384SRoman Kagan atomic_read(&hv->sint_received) == 0; 2391472a384SRoman Kagan } 2401472a384SRoman Kagan 2411472a384SRoman Kagan static int run_test(int ncpus, int dst_add, ulong wait_cycles, 2421472a384SRoman Kagan void (*func)(void *), bool (*is_ok)(int)) 2431472a384SRoman Kagan { 2441472a384SRoman Kagan int i, ret = 0; 2451472a384SRoman Kagan 2461472a384SRoman Kagan atomic_set(&ncpus_done, 0); 2471472a384SRoman Kagan for (i = 0; i < ncpus; i++) { 2481472a384SRoman Kagan ulong dst = (i + dst_add) % ncpus; 2491472a384SRoman Kagan on_cpu_async(i, func, (void *)dst); 2501472a384SRoman Kagan } 2511472a384SRoman Kagan while (atomic_read(&ncpus_done) != ncpus) 2521472a384SRoman Kagan pause(); 2531472a384SRoman Kagan 2541472a384SRoman Kagan while (wait_cycles--) 2551472a384SRoman Kagan pause(); 2561472a384SRoman Kagan 2571472a384SRoman Kagan if (is_ok) 2581472a384SRoman Kagan for (i = 0; i < ncpus; i++) 2591472a384SRoman Kagan ret += is_ok(i); 2601472a384SRoman Kagan return ret; 2611472a384SRoman Kagan } 2621472a384SRoman Kagan 2631472a384SRoman Kagan #define HV_STATUS_INVALID_HYPERCALL_CODE 2 2641472a384SRoman Kagan 2651472a384SRoman Kagan int main(int ac, char **av) 2661472a384SRoman Kagan { 2671472a384SRoman Kagan int ncpus, ncpus_ok, i; 2681472a384SRoman Kagan 2691472a384SRoman Kagan if (!synic_supported()) { 2701472a384SRoman Kagan report_skip("Hyper-V SynIC is not supported"); 2711472a384SRoman Kagan goto summary; 2721472a384SRoman Kagan } 2731472a384SRoman Kagan 2741472a384SRoman Kagan setup_vm(); 2751472a384SRoman Kagan smp_init(); 2761472a384SRoman Kagan ncpus = cpu_count(); 2771472a384SRoman Kagan if (ncpus > MAX_CPUS) 2781472a384SRoman Kagan report_abort("# cpus: %d > %d", ncpus, MAX_CPUS); 2791472a384SRoman Kagan 2801472a384SRoman Kagan handle_irq(MSG_VEC, sint_isr); 2811472a384SRoman Kagan handle_irq(EVT_VEC, sint_isr); 2821472a384SRoman Kagan 2831472a384SRoman Kagan setup_hypercall(); 2841472a384SRoman Kagan 2851472a384SRoman Kagan if (do_hypercall(HVCALL_SIGNAL_EVENT, 0x1234, 1) == 2861472a384SRoman Kagan HV_STATUS_INVALID_HYPERCALL_CODE) { 2871472a384SRoman Kagan report_skip("Hyper-V SynIC connections are not supported"); 2881472a384SRoman Kagan goto summary; 2891472a384SRoman Kagan } 2901472a384SRoman Kagan 2911472a384SRoman Kagan for (i = 0; i < ncpus; i++) 2921472a384SRoman Kagan on_cpu(i, setup_cpu, (void *)read_cr3()); 2931472a384SRoman Kagan 2941472a384SRoman Kagan ncpus_ok = run_test(ncpus, 0, WAIT_CYCLES, do_msg, msg_ok); 295*a299895bSThomas Huth report(ncpus_ok == ncpus, "send message to self: %d/%d", ncpus_ok, 296*a299895bSThomas Huth ncpus); 2971472a384SRoman Kagan 2981472a384SRoman Kagan run_test(ncpus, 0, 0, clear_msg, NULL); 2991472a384SRoman Kagan 3001472a384SRoman Kagan ncpus_ok = run_test(ncpus, 1, WAIT_CYCLES, do_msg, msg_ok); 301*a299895bSThomas Huth report(ncpus_ok == ncpus, "send message to another cpu: %d/%d", 302*a299895bSThomas Huth ncpus_ok, ncpus); 3031472a384SRoman Kagan 3041472a384SRoman Kagan ncpus_ok = run_test(ncpus, 1, WAIT_CYCLES, do_msg, msg_busy); 305*a299895bSThomas Huth report(ncpus_ok == ncpus, "send message to busy slot: %d/%d", 306*a299895bSThomas Huth ncpus_ok, ncpus); 3071472a384SRoman Kagan 3081472a384SRoman Kagan ncpus_ok = run_test(ncpus, 0, WAIT_CYCLES, clear_msg, msg_ok); 309*a299895bSThomas Huth report(ncpus_ok == ncpus, "receive pending message: %d/%d", ncpus_ok, 310*a299895bSThomas Huth ncpus); 3111472a384SRoman Kagan 3121472a384SRoman Kagan ncpus_ok = run_test(ncpus, 0, WAIT_CYCLES, do_evt, evt_ok); 313*a299895bSThomas Huth report(ncpus_ok == ncpus, "signal event on self: %d/%d", ncpus_ok, 314*a299895bSThomas Huth ncpus); 3151472a384SRoman Kagan 3161472a384SRoman Kagan run_test(ncpus, 0, 0, clear_evt, NULL); 3171472a384SRoman Kagan 3181472a384SRoman Kagan ncpus_ok = run_test(ncpus, 1, WAIT_CYCLES, do_evt, evt_ok); 319*a299895bSThomas Huth report(ncpus_ok == ncpus, "signal event on another cpu: %d/%d", 320*a299895bSThomas Huth ncpus_ok, ncpus); 3211472a384SRoman Kagan 3221472a384SRoman Kagan ncpus_ok = run_test(ncpus, 1, WAIT_CYCLES, do_evt, evt_busy); 323*a299895bSThomas Huth report(ncpus_ok == ncpus, "signal event already set: %d/%d", ncpus_ok, 324*a299895bSThomas Huth ncpus); 3251472a384SRoman Kagan 3261472a384SRoman Kagan for (i = 0; i < ncpus; i++) 3271472a384SRoman Kagan on_cpu(i, teardown_cpu, NULL); 3281472a384SRoman Kagan 3291472a384SRoman Kagan teardown_hypercall(); 3301472a384SRoman Kagan 3311472a384SRoman Kagan summary: 3321472a384SRoman Kagan return report_summary(); 3331472a384SRoman Kagan } 334