1 #include "libcflat.h" 2 #include "processor.h" 3 #include "msr.h" 4 #include "isr.h" 5 #include "vm.h" 6 #include "apic.h" 7 #include "desc.h" 8 #include "smp.h" 9 #include "atomic.h" 10 #include "hyperv.h" 11 #include "asm/barrier.h" 12 #include "alloc_page.h" 13 14 #define MAX_CPUS 4 15 16 #define SINT1_VEC 0xF1 17 #define SINT2_VEC 0xF2 18 19 #define APIC_VEC1 0xF3 20 #define APIC_VEC2 0xF4 21 22 #define SINT1_NUM 2 23 #define SINT2_NUM 3 24 #define ONE_MS_IN_100NS 10000 25 26 static struct spinlock g_synic_alloc_lock; 27 28 struct stimer { 29 int sint; 30 int index; 31 bool direct; 32 int apic_vec; 33 atomic_t fire_count; 34 }; 35 36 struct svcpu { 37 int vcpu; 38 void *msg_page; 39 void *evt_page; 40 struct stimer timer[HV_SYNIC_STIMER_COUNT]; 41 }; 42 43 static struct svcpu g_synic_vcpu[MAX_CPUS]; 44 45 static void *synic_alloc_page(void) 46 { 47 void *page; 48 49 spin_lock(&g_synic_alloc_lock); 50 page = alloc_page(); 51 spin_unlock(&g_synic_alloc_lock); 52 return page; 53 } 54 55 static void synic_free_page(void *page) 56 { 57 spin_lock(&g_synic_alloc_lock); 58 free_page(page); 59 spin_unlock(&g_synic_alloc_lock); 60 } 61 62 static void stimer_init(struct stimer *timer, int index) 63 { 64 memset(timer, 0, sizeof(*timer)); 65 timer->index = index; 66 } 67 68 static void synic_enable(void) 69 { 70 int vcpu = smp_id(), i; 71 struct svcpu *svcpu = &g_synic_vcpu[vcpu]; 72 73 memset(svcpu, 0, sizeof(*svcpu)); 74 svcpu->vcpu = vcpu; 75 svcpu->msg_page = synic_alloc_page(); 76 for (i = 0; i < ARRAY_SIZE(svcpu->timer); i++) { 77 stimer_init(&svcpu->timer[i], i); 78 } 79 wrmsr(HV_X64_MSR_SIMP, (u64)virt_to_phys(svcpu->msg_page) | 80 HV_SYNIC_SIMP_ENABLE); 81 wrmsr(HV_X64_MSR_SCONTROL, HV_SYNIC_CONTROL_ENABLE); 82 } 83 84 static void stimer_shutdown(struct stimer *timer) 85 { 86 wrmsr(HV_X64_MSR_STIMER0_CONFIG + 2*timer->index, 0); 87 } 88 89 static void process_stimer_expired(struct stimer *timer) 90 { 91 atomic_inc(&timer->fire_count); 92 } 93 94 static void process_stimer_msg(struct svcpu *svcpu, 95 struct hv_message *msg, int sint) 96 { 97 struct hv_timer_message_payload *payload = 98 (struct hv_timer_message_payload *)msg->u.payload; 99 struct stimer *timer; 100 101 if (msg->header.message_type != HVMSG_TIMER_EXPIRED && 102 msg->header.message_type != HVMSG_NONE) { 103 report_fail("invalid Hyper-V SynIC msg type"); 104 report_summary(); 105 abort(); 106 } 107 108 if (msg->header.message_type == HVMSG_NONE) { 109 return; 110 } 111 112 if (msg->header.payload_size < sizeof(*payload)) { 113 report_fail("invalid Hyper-V SynIC msg payload size"); 114 report_summary(); 115 abort(); 116 } 117 118 /* Now process timer expiration message */ 119 120 if (payload->timer_index >= ARRAY_SIZE(svcpu->timer)) { 121 report_fail("invalid Hyper-V SynIC timer index"); 122 report_summary(); 123 abort(); 124 } 125 timer = &svcpu->timer[payload->timer_index]; 126 127 if (timer->direct) { 128 report(false, "VMBus message in direct mode received"); 129 report_summary(); 130 abort(); 131 } 132 133 process_stimer_expired(timer); 134 135 msg->header.message_type = HVMSG_NONE; 136 mb(); 137 if (msg->header.message_flags.msg_pending) { 138 wrmsr(HV_X64_MSR_EOM, 0); 139 } 140 } 141 142 static void __stimer_isr(int vcpu) 143 { 144 struct svcpu *svcpu = &g_synic_vcpu[vcpu]; 145 struct hv_message_page *msg_page; 146 struct hv_message *msg; 147 int i; 148 149 150 msg_page = (struct hv_message_page *)svcpu->msg_page; 151 for (i = 0; i < ARRAY_SIZE(msg_page->sint_message); i++) { 152 msg = &msg_page->sint_message[i]; 153 process_stimer_msg(svcpu, msg, i); 154 } 155 } 156 157 static void stimer_isr(isr_regs_t *regs) 158 { 159 int vcpu = smp_id(); 160 161 __stimer_isr(vcpu); 162 eoi(); 163 } 164 165 static void stimer_isr_auto_eoi(isr_regs_t *regs) 166 { 167 int vcpu = smp_id(); 168 169 __stimer_isr(vcpu); 170 } 171 172 static void __stimer_isr_direct(int vcpu, int timer_index) 173 { 174 struct svcpu *svcpu = &g_synic_vcpu[vcpu]; 175 struct stimer *timer = &svcpu->timer[timer_index]; 176 177 process_stimer_expired(timer); 178 } 179 180 static void stimer_isr_direct1(isr_regs_t *regs) 181 { 182 int vcpu = smp_id(); 183 184 __stimer_isr_direct(vcpu, 0); 185 186 eoi(); 187 } 188 189 static void stimer_isr_direct2(isr_regs_t *regs) 190 { 191 int vcpu = smp_id(); 192 193 __stimer_isr_direct(vcpu, 1); 194 195 eoi(); 196 } 197 198 static void stimer_start(struct stimer *timer, 199 bool auto_enable, bool periodic, 200 u64 tick_100ns) 201 { 202 u64 count; 203 union hv_stimer_config config = {.as_uint64 = 0}; 204 205 atomic_set(&timer->fire_count, 0); 206 207 config.periodic = periodic; 208 config.enable = 1; 209 config.auto_enable = auto_enable; 210 if (!timer->direct) { 211 config.sintx = timer->sint; 212 } else { 213 config.direct_mode = 1; 214 config.apic_vector = timer->apic_vec; 215 } 216 217 if (periodic) { 218 count = tick_100ns; 219 } else { 220 count = rdmsr(HV_X64_MSR_TIME_REF_COUNT) + tick_100ns; 221 } 222 223 if (!auto_enable) { 224 wrmsr(HV_X64_MSR_STIMER0_COUNT + timer->index*2, count); 225 wrmsr(HV_X64_MSR_STIMER0_CONFIG + timer->index*2, config.as_uint64); 226 } else { 227 wrmsr(HV_X64_MSR_STIMER0_CONFIG + timer->index*2, config.as_uint64); 228 wrmsr(HV_X64_MSR_STIMER0_COUNT + timer->index*2, count); 229 } 230 } 231 232 static void stimers_shutdown(void) 233 { 234 int vcpu = smp_id(), i; 235 struct svcpu *svcpu = &g_synic_vcpu[vcpu]; 236 237 for (i = 0; i < ARRAY_SIZE(svcpu->timer); i++) { 238 stimer_shutdown(&svcpu->timer[i]); 239 } 240 } 241 242 static void synic_disable(void) 243 { 244 int vcpu = smp_id(); 245 struct svcpu *svcpu = &g_synic_vcpu[vcpu]; 246 247 wrmsr(HV_X64_MSR_SCONTROL, 0); 248 wrmsr(HV_X64_MSR_SIMP, 0); 249 wrmsr(HV_X64_MSR_SIEFP, 0); 250 synic_free_page(svcpu->msg_page); 251 } 252 253 254 static void stimer_test_prepare(void *ctx) 255 { 256 int vcpu = smp_id(); 257 struct svcpu *svcpu = &g_synic_vcpu[vcpu]; 258 struct stimer *timer1, *timer2; 259 260 write_cr3((ulong)ctx); 261 synic_enable(); 262 263 synic_sint_create(SINT1_NUM, SINT1_VEC, false); 264 synic_sint_create(SINT2_NUM, SINT2_VEC, true); 265 266 timer1 = &svcpu->timer[0]; 267 timer2 = &svcpu->timer[1]; 268 269 timer1->sint = SINT1_NUM; 270 timer2->sint = SINT2_NUM; 271 } 272 273 static void stimer_test_prepare_direct(void *ctx) 274 { 275 int vcpu = smp_id(); 276 struct svcpu *svcpu = &g_synic_vcpu[vcpu]; 277 struct stimer *timer1, *timer2; 278 279 write_cr3((ulong)ctx); 280 281 timer1 = &svcpu->timer[0]; 282 timer2 = &svcpu->timer[1]; 283 284 stimer_init(timer1, 0); 285 stimer_init(timer2, 1); 286 287 timer1->apic_vec = APIC_VEC1; 288 timer2->apic_vec = APIC_VEC2; 289 290 timer1->direct = true; 291 timer2->direct = true; 292 } 293 294 295 static void stimer_test_periodic(int vcpu, struct stimer *timer1, 296 struct stimer *timer2) 297 { 298 /* Check periodic timers */ 299 stimer_start(timer1, false, true, ONE_MS_IN_100NS); 300 stimer_start(timer2, false, true, ONE_MS_IN_100NS); 301 while ((atomic_read(&timer1->fire_count) < 1000) || 302 (atomic_read(&timer2->fire_count) < 1000)) { 303 pause(); 304 } 305 report_pass("Hyper-V SynIC periodic timers test vcpu %d", vcpu); 306 stimer_shutdown(timer1); 307 stimer_shutdown(timer2); 308 } 309 310 static void stimer_test_one_shot(int vcpu, struct stimer *timer) 311 { 312 /* Check one-shot timer */ 313 stimer_start(timer, false, false, ONE_MS_IN_100NS); 314 while (atomic_read(&timer->fire_count) < 1) { 315 pause(); 316 } 317 report_pass("Hyper-V SynIC one-shot test vcpu %d", vcpu); 318 stimer_shutdown(timer); 319 } 320 321 static void stimer_test_auto_enable_one_shot(int vcpu, struct stimer *timer) 322 { 323 /* Check auto-enable one-shot timer */ 324 stimer_start(timer, true, false, ONE_MS_IN_100NS); 325 while (atomic_read(&timer->fire_count) < 1) { 326 pause(); 327 } 328 report_pass("Hyper-V SynIC auto-enable one-shot timer test vcpu %d", vcpu); 329 stimer_shutdown(timer); 330 } 331 332 static void stimer_test_auto_enable_periodic(int vcpu, struct stimer *timer) 333 { 334 /* Check auto-enable periodic timer */ 335 stimer_start(timer, true, true, ONE_MS_IN_100NS); 336 while (atomic_read(&timer->fire_count) < 1000) { 337 pause(); 338 } 339 report_pass("Hyper-V SynIC auto-enable periodic timer test vcpu %d", vcpu); 340 stimer_shutdown(timer); 341 } 342 343 static void stimer_test_one_shot_busy(int vcpu, struct stimer *timer) 344 { 345 struct hv_message_page *msg_page; 346 struct hv_message *msg; 347 348 /* Skipping msg slot busy test in direct mode */ 349 if (timer->direct) 350 return; 351 352 msg_page = g_synic_vcpu[vcpu].msg_page; 353 msg = &msg_page->sint_message[timer->sint]; 354 355 msg->header.message_type = HVMSG_TIMER_EXPIRED; 356 wmb(); 357 358 stimer_start(timer, false, false, ONE_MS_IN_100NS); 359 360 do 361 rmb(); 362 while (!msg->header.message_flags.msg_pending); 363 364 report(!atomic_read(&timer->fire_count), 365 "no timer fired while msg slot busy: vcpu %d", vcpu); 366 367 msg->header.message_type = HVMSG_NONE; 368 wmb(); 369 wrmsr(HV_X64_MSR_EOM, 0); 370 371 while (atomic_read(&timer->fire_count) < 1) { 372 pause(); 373 } 374 report_pass("timer resumed when msg slot released: vcpu %d", vcpu); 375 376 stimer_shutdown(timer); 377 } 378 379 static void stimer_test(void *ctx) 380 { 381 int vcpu = smp_id(); 382 struct svcpu *svcpu = &g_synic_vcpu[vcpu]; 383 struct stimer *timer1, *timer2; 384 385 sti(); 386 387 timer1 = &svcpu->timer[0]; 388 timer2 = &svcpu->timer[1]; 389 390 stimer_test_periodic(vcpu, timer1, timer2); 391 stimer_test_one_shot(vcpu, timer1); 392 stimer_test_auto_enable_one_shot(vcpu, timer2); 393 stimer_test_auto_enable_periodic(vcpu, timer1); 394 stimer_test_one_shot_busy(vcpu, timer1); 395 396 cli(); 397 } 398 399 static void stimer_test_cleanup(void *ctx) 400 { 401 stimers_shutdown(); 402 synic_sint_destroy(SINT1_NUM); 403 synic_sint_destroy(SINT2_NUM); 404 synic_disable(); 405 } 406 407 static void stimer_test_cleanup_direct(void *ctx) 408 { 409 stimers_shutdown(); 410 } 411 412 static void stimer_test_all(bool direct) 413 { 414 int ncpus; 415 416 setup_vm(); 417 enable_apic(); 418 419 ncpus = cpu_count(); 420 if (ncpus > MAX_CPUS) 421 report_abort("number cpus exceeds %d", MAX_CPUS); 422 printf("cpus = %d\n", ncpus); 423 424 if (!direct) { 425 printf("Starting Hyper-V SynIC timers tests: message mode\n"); 426 427 handle_irq(SINT1_VEC, stimer_isr); 428 handle_irq(SINT2_VEC, stimer_isr_auto_eoi); 429 430 on_cpus(stimer_test_prepare, (void *)read_cr3()); 431 on_cpus(stimer_test, NULL); 432 on_cpus(stimer_test_cleanup, NULL); 433 } else { 434 printf("Starting Hyper-V SynIC timers tests: direct mode\n"); 435 436 handle_irq(APIC_VEC1, stimer_isr_direct1); 437 handle_irq(APIC_VEC2, stimer_isr_direct2); 438 439 on_cpus(stimer_test_prepare_direct, (void *)read_cr3()); 440 on_cpus(stimer_test, NULL); 441 on_cpus(stimer_test_cleanup_direct, NULL); 442 } 443 } 444 445 int main(int argc, char **argv) 446 { 447 bool direct = argc >= 2 && !strcmp(argv[1], "direct"); 448 449 if (!hv_synic_supported()) { 450 report_skip("Hyper-V SynIC is not supported"); 451 goto done; 452 } 453 454 if (!hv_stimer_supported()) { 455 report_skip("Hyper-V SynIC timers are not supported"); 456 goto done; 457 } 458 459 if (!hv_time_ref_counter_supported()) { 460 report_skip("Hyper-V time reference counter is not supported"); 461 goto done; 462 } 463 464 if (direct && !stimer_direct_supported()) { 465 report_skip("Hyper-V SinIC timer direct mode is not supported"); 466 } 467 468 stimer_test_all(direct); 469 done: 470 return report_summary(); 471 } 472