1*1472a384SRoman Kagan #include "libcflat.h" 2*1472a384SRoman Kagan #include "vm.h" 3*1472a384SRoman Kagan #include "smp.h" 4*1472a384SRoman Kagan #include "isr.h" 5*1472a384SRoman Kagan #include "atomic.h" 6*1472a384SRoman Kagan #include "hyperv.h" 7*1472a384SRoman Kagan #include "bitops.h" 8*1472a384SRoman Kagan 9*1472a384SRoman Kagan #define MAX_CPUS 64 10*1472a384SRoman Kagan 11*1472a384SRoman Kagan #define MSG_VEC 0xb0 12*1472a384SRoman Kagan #define EVT_VEC 0xb1 13*1472a384SRoman Kagan #define MSG_SINT 0x8 14*1472a384SRoman Kagan #define EVT_SINT 0x9 15*1472a384SRoman Kagan #define MSG_CONN_BASE 0x10 16*1472a384SRoman Kagan #define EVT_CONN_BASE 0x20 17*1472a384SRoman Kagan #define MSG_TYPE 0x12345678 18*1472a384SRoman Kagan 19*1472a384SRoman Kagan #define WAIT_CYCLES 10000000 20*1472a384SRoman Kagan 21*1472a384SRoman Kagan static atomic_t ncpus_done; 22*1472a384SRoman Kagan 23*1472a384SRoman Kagan struct hv_vcpu { 24*1472a384SRoman Kagan struct hv_message_page *msg_page; 25*1472a384SRoman Kagan struct hv_event_flags_page *evt_page; 26*1472a384SRoman Kagan struct hv_input_post_message *post_msg; 27*1472a384SRoman Kagan u8 msg_conn; 28*1472a384SRoman Kagan u8 evt_conn; 29*1472a384SRoman Kagan u64 hvcall_status; 30*1472a384SRoman Kagan atomic_t sint_received; 31*1472a384SRoman Kagan }; 32*1472a384SRoman Kagan 33*1472a384SRoman Kagan static struct hv_vcpu hv_vcpus[MAX_CPUS]; 34*1472a384SRoman Kagan 35*1472a384SRoman Kagan static void sint_isr(isr_regs_t *regs) 36*1472a384SRoman Kagan { 37*1472a384SRoman Kagan atomic_inc(&hv_vcpus[smp_id()].sint_received); 38*1472a384SRoman Kagan } 39*1472a384SRoman Kagan 40*1472a384SRoman Kagan static void *hypercall_page; 41*1472a384SRoman Kagan 42*1472a384SRoman Kagan static void setup_hypercall(void) 43*1472a384SRoman Kagan { 44*1472a384SRoman Kagan u64 guestid = (0x8f00ull << 48); 45*1472a384SRoman Kagan 46*1472a384SRoman Kagan hypercall_page = alloc_page(); 47*1472a384SRoman Kagan if (!hypercall_page) 48*1472a384SRoman Kagan report_abort("failed to allocate hypercall page"); 49*1472a384SRoman Kagan memset(hypercall_page, 0, PAGE_SIZE); 50*1472a384SRoman Kagan 51*1472a384SRoman Kagan wrmsr(HV_X64_MSR_GUEST_OS_ID, guestid); 52*1472a384SRoman Kagan 53*1472a384SRoman Kagan wrmsr(HV_X64_MSR_HYPERCALL, 54*1472a384SRoman Kagan (u64)virt_to_phys(hypercall_page) | HV_X64_MSR_HYPERCALL_ENABLE); 55*1472a384SRoman Kagan } 56*1472a384SRoman Kagan 57*1472a384SRoman Kagan static void teardown_hypercall(void) 58*1472a384SRoman Kagan { 59*1472a384SRoman Kagan wrmsr(HV_X64_MSR_HYPERCALL, 0); 60*1472a384SRoman Kagan wrmsr(HV_X64_MSR_GUEST_OS_ID, 0); 61*1472a384SRoman Kagan free_page(hypercall_page); 62*1472a384SRoman Kagan } 63*1472a384SRoman Kagan 64*1472a384SRoman Kagan static u64 do_hypercall(u16 code, u64 arg, bool fast) 65*1472a384SRoman Kagan { 66*1472a384SRoman Kagan u64 ret; 67*1472a384SRoman Kagan u64 ctl = code; 68*1472a384SRoman Kagan if (fast) 69*1472a384SRoman Kagan ctl |= HV_HYPERCALL_FAST; 70*1472a384SRoman Kagan 71*1472a384SRoman Kagan asm volatile ("call *%[hcall_page]" 72*1472a384SRoman Kagan #ifdef __x86_64__ 73*1472a384SRoman Kagan "\n mov $0,%%r8" 74*1472a384SRoman Kagan : "=a"(ret) 75*1472a384SRoman Kagan : "c"(ctl), "d"(arg), 76*1472a384SRoman Kagan #else 77*1472a384SRoman Kagan : "=A"(ret) 78*1472a384SRoman Kagan : "A"(ctl), 79*1472a384SRoman Kagan "b" ((u32)(arg >> 32)), "c" ((u32)arg), 80*1472a384SRoman Kagan "D"(0), "S"(0), 81*1472a384SRoman Kagan #endif 82*1472a384SRoman Kagan [hcall_page] "m" (hypercall_page) 83*1472a384SRoman Kagan #ifdef __x86_64__ 84*1472a384SRoman Kagan : "r8" 85*1472a384SRoman Kagan #endif 86*1472a384SRoman Kagan ); 87*1472a384SRoman Kagan 88*1472a384SRoman Kagan return ret; 89*1472a384SRoman Kagan } 90*1472a384SRoman Kagan 91*1472a384SRoman Kagan static void setup_cpu(void *ctx) 92*1472a384SRoman Kagan { 93*1472a384SRoman Kagan int vcpu; 94*1472a384SRoman Kagan struct hv_vcpu *hv; 95*1472a384SRoman Kagan 96*1472a384SRoman Kagan write_cr3((ulong)ctx); 97*1472a384SRoman Kagan irq_enable(); 98*1472a384SRoman Kagan 99*1472a384SRoman Kagan vcpu = smp_id(); 100*1472a384SRoman Kagan hv = &hv_vcpus[vcpu]; 101*1472a384SRoman Kagan 102*1472a384SRoman Kagan hv->msg_page = alloc_page(); 103*1472a384SRoman Kagan hv->evt_page = alloc_page(); 104*1472a384SRoman Kagan hv->post_msg = alloc_page(); 105*1472a384SRoman Kagan if (!hv->msg_page || !hv->evt_page || !hv->post_msg) 106*1472a384SRoman Kagan report_abort("failed to allocate synic pages for vcpu"); 107*1472a384SRoman Kagan memset(hv->msg_page, 0, sizeof(*hv->msg_page)); 108*1472a384SRoman Kagan memset(hv->evt_page, 0, sizeof(*hv->evt_page)); 109*1472a384SRoman Kagan memset(hv->post_msg, 0, sizeof(*hv->post_msg)); 110*1472a384SRoman Kagan hv->msg_conn = MSG_CONN_BASE + vcpu; 111*1472a384SRoman Kagan hv->evt_conn = EVT_CONN_BASE + vcpu; 112*1472a384SRoman Kagan 113*1472a384SRoman Kagan wrmsr(HV_X64_MSR_SIMP, 114*1472a384SRoman Kagan (u64)virt_to_phys(hv->msg_page) | HV_SYNIC_SIMP_ENABLE); 115*1472a384SRoman Kagan wrmsr(HV_X64_MSR_SIEFP, 116*1472a384SRoman Kagan (u64)virt_to_phys(hv->evt_page) | HV_SYNIC_SIEFP_ENABLE); 117*1472a384SRoman Kagan wrmsr(HV_X64_MSR_SCONTROL, HV_SYNIC_CONTROL_ENABLE); 118*1472a384SRoman Kagan 119*1472a384SRoman Kagan msg_conn_create(MSG_SINT, MSG_VEC, hv->msg_conn); 120*1472a384SRoman Kagan evt_conn_create(EVT_SINT, EVT_VEC, hv->evt_conn); 121*1472a384SRoman Kagan 122*1472a384SRoman Kagan hv->post_msg->connectionid = hv->msg_conn; 123*1472a384SRoman Kagan hv->post_msg->message_type = MSG_TYPE; 124*1472a384SRoman Kagan hv->post_msg->payload_size = 8; 125*1472a384SRoman Kagan hv->post_msg->payload[0] = (u64)vcpu << 16; 126*1472a384SRoman Kagan } 127*1472a384SRoman Kagan 128*1472a384SRoman Kagan static void teardown_cpu(void *ctx) 129*1472a384SRoman Kagan { 130*1472a384SRoman Kagan int vcpu = smp_id(); 131*1472a384SRoman Kagan struct hv_vcpu *hv = &hv_vcpus[vcpu]; 132*1472a384SRoman Kagan 133*1472a384SRoman Kagan evt_conn_destroy(EVT_SINT, hv->evt_conn); 134*1472a384SRoman Kagan msg_conn_destroy(MSG_SINT, hv->msg_conn); 135*1472a384SRoman Kagan 136*1472a384SRoman Kagan wrmsr(HV_X64_MSR_SCONTROL, 0); 137*1472a384SRoman Kagan wrmsr(HV_X64_MSR_SIEFP, 0); 138*1472a384SRoman Kagan wrmsr(HV_X64_MSR_SIMP, 0); 139*1472a384SRoman Kagan 140*1472a384SRoman Kagan free_page(hv->post_msg); 141*1472a384SRoman Kagan free_page(hv->evt_page); 142*1472a384SRoman Kagan free_page(hv->msg_page); 143*1472a384SRoman Kagan } 144*1472a384SRoman Kagan 145*1472a384SRoman Kagan static void do_msg(void *ctx) 146*1472a384SRoman Kagan { 147*1472a384SRoman Kagan int vcpu = (ulong)ctx; 148*1472a384SRoman Kagan struct hv_vcpu *hv = &hv_vcpus[vcpu]; 149*1472a384SRoman Kagan struct hv_input_post_message *msg = hv->post_msg; 150*1472a384SRoman Kagan 151*1472a384SRoman Kagan msg->payload[0]++; 152*1472a384SRoman Kagan atomic_set(&hv->sint_received, 0); 153*1472a384SRoman Kagan hv->hvcall_status = do_hypercall(HVCALL_POST_MESSAGE, 154*1472a384SRoman Kagan virt_to_phys(msg), 0); 155*1472a384SRoman Kagan atomic_inc(&ncpus_done); 156*1472a384SRoman Kagan } 157*1472a384SRoman Kagan 158*1472a384SRoman Kagan static void clear_msg(void *ctx) 159*1472a384SRoman Kagan { 160*1472a384SRoman Kagan /* should only be done on the current vcpu */ 161*1472a384SRoman Kagan int vcpu = smp_id(); 162*1472a384SRoman Kagan struct hv_vcpu *hv = &hv_vcpus[vcpu]; 163*1472a384SRoman Kagan struct hv_message *msg = &hv->msg_page->sint_message[MSG_SINT]; 164*1472a384SRoman Kagan 165*1472a384SRoman Kagan atomic_set(&hv->sint_received, 0); 166*1472a384SRoman Kagan msg->header.message_type = 0; 167*1472a384SRoman Kagan barrier(); 168*1472a384SRoman Kagan wrmsr(HV_X64_MSR_EOM, 0); 169*1472a384SRoman Kagan atomic_inc(&ncpus_done); 170*1472a384SRoman Kagan } 171*1472a384SRoman Kagan 172*1472a384SRoman Kagan static bool msg_ok(int vcpu) 173*1472a384SRoman Kagan { 174*1472a384SRoman Kagan struct hv_vcpu *hv = &hv_vcpus[vcpu]; 175*1472a384SRoman Kagan struct hv_input_post_message *post_msg = hv->post_msg; 176*1472a384SRoman Kagan struct hv_message *msg = &hv->msg_page->sint_message[MSG_SINT]; 177*1472a384SRoman Kagan 178*1472a384SRoman Kagan return msg->header.message_type == post_msg->message_type && 179*1472a384SRoman Kagan msg->header.payload_size == post_msg->payload_size && 180*1472a384SRoman Kagan msg->header.message_flags.msg_pending == 0 && 181*1472a384SRoman Kagan msg->u.payload[0] == post_msg->payload[0] && 182*1472a384SRoman Kagan hv->hvcall_status == 0 && 183*1472a384SRoman Kagan atomic_read(&hv->sint_received) == 1; 184*1472a384SRoman Kagan } 185*1472a384SRoman Kagan 186*1472a384SRoman Kagan static bool msg_busy(int vcpu) 187*1472a384SRoman Kagan { 188*1472a384SRoman Kagan struct hv_vcpu *hv = &hv_vcpus[vcpu]; 189*1472a384SRoman Kagan struct hv_input_post_message *post_msg = hv->post_msg; 190*1472a384SRoman Kagan struct hv_message *msg = &hv->msg_page->sint_message[MSG_SINT]; 191*1472a384SRoman Kagan 192*1472a384SRoman Kagan return msg->header.message_type == post_msg->message_type && 193*1472a384SRoman Kagan msg->header.payload_size == post_msg->payload_size && 194*1472a384SRoman Kagan msg->header.message_flags.msg_pending == 1 && 195*1472a384SRoman Kagan msg->u.payload[0] == post_msg->payload[0] - 1 && 196*1472a384SRoman Kagan hv->hvcall_status == 0 && 197*1472a384SRoman Kagan atomic_read(&hv->sint_received) == 0; 198*1472a384SRoman Kagan } 199*1472a384SRoman Kagan 200*1472a384SRoman Kagan static void do_evt(void *ctx) 201*1472a384SRoman Kagan { 202*1472a384SRoman Kagan int vcpu = (ulong)ctx; 203*1472a384SRoman Kagan struct hv_vcpu *hv = &hv_vcpus[vcpu]; 204*1472a384SRoman Kagan 205*1472a384SRoman Kagan atomic_set(&hv->sint_received, 0); 206*1472a384SRoman Kagan hv->hvcall_status = do_hypercall(HVCALL_SIGNAL_EVENT, 207*1472a384SRoman Kagan hv->evt_conn, 1); 208*1472a384SRoman Kagan atomic_inc(&ncpus_done); 209*1472a384SRoman Kagan } 210*1472a384SRoman Kagan 211*1472a384SRoman Kagan static void clear_evt(void *ctx) 212*1472a384SRoman Kagan { 213*1472a384SRoman Kagan /* should only be done on the current vcpu */ 214*1472a384SRoman Kagan int vcpu = smp_id(); 215*1472a384SRoman Kagan struct hv_vcpu *hv = &hv_vcpus[vcpu]; 216*1472a384SRoman Kagan ulong *flags = hv->evt_page->slot[EVT_SINT].flags; 217*1472a384SRoman Kagan 218*1472a384SRoman Kagan atomic_set(&hv->sint_received, 0); 219*1472a384SRoman Kagan flags[BIT_WORD(hv->evt_conn)] &= ~BIT_MASK(hv->evt_conn); 220*1472a384SRoman Kagan barrier(); 221*1472a384SRoman Kagan atomic_inc(&ncpus_done); 222*1472a384SRoman Kagan } 223*1472a384SRoman Kagan 224*1472a384SRoman Kagan static bool evt_ok(int vcpu) 225*1472a384SRoman Kagan { 226*1472a384SRoman Kagan struct hv_vcpu *hv = &hv_vcpus[vcpu]; 227*1472a384SRoman Kagan ulong *flags = hv->evt_page->slot[EVT_SINT].flags; 228*1472a384SRoman Kagan 229*1472a384SRoman Kagan return flags[BIT_WORD(hv->evt_conn)] == BIT_MASK(hv->evt_conn) && 230*1472a384SRoman Kagan hv->hvcall_status == 0 && 231*1472a384SRoman Kagan atomic_read(&hv->sint_received) == 1; 232*1472a384SRoman Kagan } 233*1472a384SRoman Kagan 234*1472a384SRoman Kagan static bool evt_busy(int vcpu) 235*1472a384SRoman Kagan { 236*1472a384SRoman Kagan struct hv_vcpu *hv = &hv_vcpus[vcpu]; 237*1472a384SRoman Kagan ulong *flags = hv->evt_page->slot[EVT_SINT].flags; 238*1472a384SRoman Kagan 239*1472a384SRoman Kagan return flags[BIT_WORD(hv->evt_conn)] == BIT_MASK(hv->evt_conn) && 240*1472a384SRoman Kagan hv->hvcall_status == 0 && 241*1472a384SRoman Kagan atomic_read(&hv->sint_received) == 0; 242*1472a384SRoman Kagan } 243*1472a384SRoman Kagan 244*1472a384SRoman Kagan static int run_test(int ncpus, int dst_add, ulong wait_cycles, 245*1472a384SRoman Kagan void (*func)(void *), bool (*is_ok)(int)) 246*1472a384SRoman Kagan { 247*1472a384SRoman Kagan int i, ret = 0; 248*1472a384SRoman Kagan 249*1472a384SRoman Kagan atomic_set(&ncpus_done, 0); 250*1472a384SRoman Kagan for (i = 0; i < ncpus; i++) { 251*1472a384SRoman Kagan ulong dst = (i + dst_add) % ncpus; 252*1472a384SRoman Kagan on_cpu_async(i, func, (void *)dst); 253*1472a384SRoman Kagan } 254*1472a384SRoman Kagan while (atomic_read(&ncpus_done) != ncpus) 255*1472a384SRoman Kagan pause(); 256*1472a384SRoman Kagan 257*1472a384SRoman Kagan while (wait_cycles--) 258*1472a384SRoman Kagan pause(); 259*1472a384SRoman Kagan 260*1472a384SRoman Kagan if (is_ok) 261*1472a384SRoman Kagan for (i = 0; i < ncpus; i++) 262*1472a384SRoman Kagan ret += is_ok(i); 263*1472a384SRoman Kagan return ret; 264*1472a384SRoman Kagan } 265*1472a384SRoman Kagan 266*1472a384SRoman Kagan #define HV_STATUS_INVALID_HYPERCALL_CODE 2 267*1472a384SRoman Kagan 268*1472a384SRoman Kagan int main(int ac, char **av) 269*1472a384SRoman Kagan { 270*1472a384SRoman Kagan int ncpus, ncpus_ok, i; 271*1472a384SRoman Kagan 272*1472a384SRoman Kagan if (!synic_supported()) { 273*1472a384SRoman Kagan report_skip("Hyper-V SynIC is not supported"); 274*1472a384SRoman Kagan goto summary; 275*1472a384SRoman Kagan } 276*1472a384SRoman Kagan 277*1472a384SRoman Kagan setup_vm(); 278*1472a384SRoman Kagan smp_init(); 279*1472a384SRoman Kagan ncpus = cpu_count(); 280*1472a384SRoman Kagan if (ncpus > MAX_CPUS) 281*1472a384SRoman Kagan report_abort("# cpus: %d > %d", ncpus, MAX_CPUS); 282*1472a384SRoman Kagan 283*1472a384SRoman Kagan handle_irq(MSG_VEC, sint_isr); 284*1472a384SRoman Kagan handle_irq(EVT_VEC, sint_isr); 285*1472a384SRoman Kagan 286*1472a384SRoman Kagan setup_hypercall(); 287*1472a384SRoman Kagan 288*1472a384SRoman Kagan if (do_hypercall(HVCALL_SIGNAL_EVENT, 0x1234, 1) == 289*1472a384SRoman Kagan HV_STATUS_INVALID_HYPERCALL_CODE) { 290*1472a384SRoman Kagan report_skip("Hyper-V SynIC connections are not supported"); 291*1472a384SRoman Kagan goto summary; 292*1472a384SRoman Kagan } 293*1472a384SRoman Kagan 294*1472a384SRoman Kagan for (i = 0; i < ncpus; i++) 295*1472a384SRoman Kagan on_cpu(i, setup_cpu, (void *)read_cr3()); 296*1472a384SRoman Kagan 297*1472a384SRoman Kagan ncpus_ok = run_test(ncpus, 0, WAIT_CYCLES, do_msg, msg_ok); 298*1472a384SRoman Kagan report("send message to self: %d/%d", 299*1472a384SRoman Kagan ncpus_ok == ncpus, ncpus_ok, ncpus); 300*1472a384SRoman Kagan 301*1472a384SRoman Kagan run_test(ncpus, 0, 0, clear_msg, NULL); 302*1472a384SRoman Kagan 303*1472a384SRoman Kagan ncpus_ok = run_test(ncpus, 1, WAIT_CYCLES, do_msg, msg_ok); 304*1472a384SRoman Kagan report("send message to another cpu: %d/%d", 305*1472a384SRoman Kagan ncpus_ok == ncpus, ncpus_ok, ncpus); 306*1472a384SRoman Kagan 307*1472a384SRoman Kagan ncpus_ok = run_test(ncpus, 1, WAIT_CYCLES, do_msg, msg_busy); 308*1472a384SRoman Kagan report("send message to busy slot: %d/%d", 309*1472a384SRoman Kagan ncpus_ok == ncpus, ncpus_ok, ncpus); 310*1472a384SRoman Kagan 311*1472a384SRoman Kagan ncpus_ok = run_test(ncpus, 0, WAIT_CYCLES, clear_msg, msg_ok); 312*1472a384SRoman Kagan report("receive pending message: %d/%d", 313*1472a384SRoman Kagan ncpus_ok == ncpus, ncpus_ok, ncpus); 314*1472a384SRoman Kagan 315*1472a384SRoman Kagan ncpus_ok = run_test(ncpus, 0, WAIT_CYCLES, do_evt, evt_ok); 316*1472a384SRoman Kagan report("signal event on self: %d/%d", 317*1472a384SRoman Kagan ncpus_ok == ncpus, ncpus_ok, ncpus); 318*1472a384SRoman Kagan 319*1472a384SRoman Kagan run_test(ncpus, 0, 0, clear_evt, NULL); 320*1472a384SRoman Kagan 321*1472a384SRoman Kagan ncpus_ok = run_test(ncpus, 1, WAIT_CYCLES, do_evt, evt_ok); 322*1472a384SRoman Kagan report("signal event on another cpu: %d/%d", 323*1472a384SRoman Kagan ncpus_ok == ncpus, ncpus_ok, ncpus); 324*1472a384SRoman Kagan 325*1472a384SRoman Kagan ncpus_ok = run_test(ncpus, 1, WAIT_CYCLES, do_evt, evt_busy); 326*1472a384SRoman Kagan report("signal event already set: %d/%d", 327*1472a384SRoman Kagan ncpus_ok == ncpus, ncpus_ok, ncpus); 328*1472a384SRoman Kagan 329*1472a384SRoman Kagan for (i = 0; i < ncpus; i++) 330*1472a384SRoman Kagan on_cpu(i, teardown_cpu, NULL); 331*1472a384SRoman Kagan 332*1472a384SRoman Kagan teardown_hypercall(); 333*1472a384SRoman Kagan 334*1472a384SRoman Kagan summary: 335*1472a384SRoman Kagan return report_summary(); 336*1472a384SRoman Kagan } 337