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