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 SINT1_NUM 2 20 #define SINT2_NUM 3 21 #define ONE_MS_IN_100NS 10000 22 23 static struct spinlock g_synic_alloc_lock; 24 25 struct stimer { 26 int sint; 27 int index; 28 atomic_t fire_count; 29 }; 30 31 struct svcpu { 32 int vcpu; 33 void *msg_page; 34 void *evt_page; 35 struct stimer timer[HV_SYNIC_STIMER_COUNT]; 36 }; 37 38 static struct svcpu g_synic_vcpu[MAX_CPUS]; 39 40 static void *synic_alloc_page(void) 41 { 42 void *page; 43 44 spin_lock(&g_synic_alloc_lock); 45 page = alloc_page(); 46 spin_unlock(&g_synic_alloc_lock); 47 return page; 48 } 49 50 static void synic_free_page(void *page) 51 { 52 spin_lock(&g_synic_alloc_lock); 53 free_page(page); 54 spin_unlock(&g_synic_alloc_lock); 55 } 56 57 static void stimer_init(struct stimer *timer, int index) 58 { 59 memset(timer, 0, sizeof(*timer)); 60 timer->index = index; 61 } 62 63 static void synic_enable(void) 64 { 65 int vcpu = smp_id(), i; 66 struct svcpu *svcpu = &g_synic_vcpu[vcpu]; 67 68 memset(svcpu, 0, sizeof(*svcpu)); 69 svcpu->vcpu = vcpu; 70 svcpu->msg_page = synic_alloc_page(); 71 for (i = 0; i < ARRAY_SIZE(svcpu->timer); i++) { 72 stimer_init(&svcpu->timer[i], i); 73 } 74 wrmsr(HV_X64_MSR_SIMP, (u64)virt_to_phys(svcpu->msg_page) | 75 HV_SYNIC_SIMP_ENABLE); 76 wrmsr(HV_X64_MSR_SCONTROL, HV_SYNIC_CONTROL_ENABLE); 77 } 78 79 static void stimer_shutdown(struct stimer *timer) 80 { 81 wrmsr(HV_X64_MSR_STIMER0_CONFIG + 2*timer->index, 0); 82 } 83 84 static void process_stimer_expired(struct svcpu *svcpu, struct stimer *timer, 85 u64 expiration_time, u64 delivery_time) 86 { 87 atomic_inc(&timer->fire_count); 88 } 89 90 static void process_stimer_msg(struct svcpu *svcpu, 91 struct hv_message *msg, int sint) 92 { 93 struct hv_timer_message_payload *payload = 94 (struct hv_timer_message_payload *)msg->u.payload; 95 struct stimer *timer; 96 97 if (msg->header.message_type != HVMSG_TIMER_EXPIRED && 98 msg->header.message_type != HVMSG_NONE) { 99 report_fail("invalid Hyper-V SynIC msg type"); 100 report_summary(); 101 abort(); 102 } 103 104 if (msg->header.message_type == HVMSG_NONE) { 105 return; 106 } 107 108 if (msg->header.payload_size < sizeof(*payload)) { 109 report_fail("invalid Hyper-V SynIC msg payload size"); 110 report_summary(); 111 abort(); 112 } 113 114 /* Now process timer expiration message */ 115 116 if (payload->timer_index >= ARRAY_SIZE(svcpu->timer)) { 117 report_fail("invalid Hyper-V SynIC timer index"); 118 report_summary(); 119 abort(); 120 } 121 timer = &svcpu->timer[payload->timer_index]; 122 process_stimer_expired(svcpu, timer, payload->expiration_time, 123 payload->delivery_time); 124 125 msg->header.message_type = HVMSG_NONE; 126 mb(); 127 if (msg->header.message_flags.msg_pending) { 128 wrmsr(HV_X64_MSR_EOM, 0); 129 } 130 } 131 132 static void __stimer_isr(int vcpu) 133 { 134 struct svcpu *svcpu = &g_synic_vcpu[vcpu]; 135 struct hv_message_page *msg_page; 136 struct hv_message *msg; 137 int i; 138 139 140 msg_page = (struct hv_message_page *)svcpu->msg_page; 141 for (i = 0; i < ARRAY_SIZE(msg_page->sint_message); i++) { 142 msg = &msg_page->sint_message[i]; 143 process_stimer_msg(svcpu, msg, i); 144 } 145 } 146 147 static void stimer_isr(isr_regs_t *regs) 148 { 149 int vcpu = smp_id(); 150 151 __stimer_isr(vcpu); 152 eoi(); 153 } 154 155 static void stimer_isr_auto_eoi(isr_regs_t *regs) 156 { 157 int vcpu = smp_id(); 158 159 __stimer_isr(vcpu); 160 } 161 162 static void stimer_start(struct stimer *timer, 163 bool auto_enable, bool periodic, 164 u64 tick_100ns, int sint) 165 { 166 u64 config, count; 167 168 timer->sint = sint; 169 atomic_set(&timer->fire_count, 0); 170 171 config = 0; 172 if (periodic) { 173 config |= HV_STIMER_PERIODIC; 174 } 175 176 config |= ((u8)(sint & 0xFF)) << 16; 177 config |= HV_STIMER_ENABLE; 178 if (auto_enable) { 179 config |= HV_STIMER_AUTOENABLE; 180 } 181 182 if (periodic) { 183 count = tick_100ns; 184 } else { 185 count = rdmsr(HV_X64_MSR_TIME_REF_COUNT) + tick_100ns; 186 } 187 188 if (!auto_enable) { 189 wrmsr(HV_X64_MSR_STIMER0_COUNT + timer->index*2, count); 190 wrmsr(HV_X64_MSR_STIMER0_CONFIG + timer->index*2, config); 191 } else { 192 wrmsr(HV_X64_MSR_STIMER0_CONFIG + timer->index*2, config); 193 wrmsr(HV_X64_MSR_STIMER0_COUNT + timer->index*2, count); 194 } 195 } 196 197 static void stimers_shutdown(void) 198 { 199 int vcpu = smp_id(), i; 200 struct svcpu *svcpu = &g_synic_vcpu[vcpu]; 201 202 for (i = 0; i < ARRAY_SIZE(svcpu->timer); i++) { 203 stimer_shutdown(&svcpu->timer[i]); 204 } 205 } 206 207 static void synic_disable(void) 208 { 209 int vcpu = smp_id(); 210 struct svcpu *svcpu = &g_synic_vcpu[vcpu]; 211 212 wrmsr(HV_X64_MSR_SCONTROL, 0); 213 wrmsr(HV_X64_MSR_SIMP, 0); 214 wrmsr(HV_X64_MSR_SIEFP, 0); 215 synic_free_page(svcpu->msg_page); 216 } 217 218 219 static void stimer_test_prepare(void *ctx) 220 { 221 write_cr3((ulong)ctx); 222 synic_enable(); 223 synic_sint_create(SINT1_NUM, SINT1_VEC, false); 224 synic_sint_create(SINT2_NUM, SINT2_VEC, true); 225 } 226 227 static void stimer_test_periodic(int vcpu, struct stimer *timer1, 228 struct stimer *timer2) 229 { 230 /* Check periodic timers */ 231 stimer_start(timer1, false, true, ONE_MS_IN_100NS, SINT1_NUM); 232 stimer_start(timer2, false, true, ONE_MS_IN_100NS, SINT2_NUM); 233 while ((atomic_read(&timer1->fire_count) < 1000) || 234 (atomic_read(&timer2->fire_count) < 1000)) { 235 pause(); 236 } 237 report_pass("Hyper-V SynIC periodic timers test vcpu %d", vcpu); 238 stimer_shutdown(timer1); 239 stimer_shutdown(timer2); 240 } 241 242 static void stimer_test_one_shot(int vcpu, struct stimer *timer) 243 { 244 /* Check one-shot timer */ 245 stimer_start(timer, false, false, ONE_MS_IN_100NS, SINT1_NUM); 246 while (atomic_read(&timer->fire_count) < 1) { 247 pause(); 248 } 249 report_pass("Hyper-V SynIC one-shot test vcpu %d", vcpu); 250 stimer_shutdown(timer); 251 } 252 253 static void stimer_test_auto_enable_one_shot(int vcpu, struct stimer *timer) 254 { 255 /* Check auto-enable one-shot timer */ 256 stimer_start(timer, true, false, ONE_MS_IN_100NS, SINT1_NUM); 257 while (atomic_read(&timer->fire_count) < 1) { 258 pause(); 259 } 260 report_pass("Hyper-V SynIC auto-enable one-shot timer test vcpu %d", vcpu); 261 stimer_shutdown(timer); 262 } 263 264 static void stimer_test_auto_enable_periodic(int vcpu, struct stimer *timer) 265 { 266 /* Check auto-enable periodic timer */ 267 stimer_start(timer, true, true, ONE_MS_IN_100NS, SINT1_NUM); 268 while (atomic_read(&timer->fire_count) < 1000) { 269 pause(); 270 } 271 report_pass("Hyper-V SynIC auto-enable periodic timer test vcpu %d", vcpu); 272 stimer_shutdown(timer); 273 } 274 275 static void stimer_test_one_shot_busy(int vcpu, struct stimer *timer) 276 { 277 struct hv_message_page *msg_page = g_synic_vcpu[vcpu].msg_page; 278 struct hv_message *msg = &msg_page->sint_message[timer->sint]; 279 280 msg->header.message_type = HVMSG_TIMER_EXPIRED; 281 wmb(); 282 283 stimer_start(timer, false, false, ONE_MS_IN_100NS, SINT1_NUM); 284 285 do 286 rmb(); 287 while (!msg->header.message_flags.msg_pending); 288 289 report(!atomic_read(&timer->fire_count), 290 "no timer fired while msg slot busy: vcpu %d", vcpu); 291 292 msg->header.message_type = HVMSG_NONE; 293 wmb(); 294 wrmsr(HV_X64_MSR_EOM, 0); 295 296 while (atomic_read(&timer->fire_count) < 1) { 297 pause(); 298 } 299 report_pass("timer resumed when msg slot released: vcpu %d", vcpu); 300 301 stimer_shutdown(timer); 302 } 303 304 static void stimer_test(void *ctx) 305 { 306 int vcpu = smp_id(); 307 struct svcpu *svcpu = &g_synic_vcpu[vcpu]; 308 struct stimer *timer1, *timer2; 309 310 irq_enable(); 311 312 timer1 = &svcpu->timer[0]; 313 timer2 = &svcpu->timer[1]; 314 315 stimer_test_periodic(vcpu, timer1, timer2); 316 stimer_test_one_shot(vcpu, timer1); 317 stimer_test_auto_enable_one_shot(vcpu, timer2); 318 stimer_test_auto_enable_periodic(vcpu, timer1); 319 stimer_test_one_shot_busy(vcpu, timer1); 320 321 irq_disable(); 322 } 323 324 static void stimer_test_cleanup(void *ctx) 325 { 326 stimers_shutdown(); 327 synic_sint_destroy(SINT1_NUM); 328 synic_sint_destroy(SINT2_NUM); 329 synic_disable(); 330 } 331 332 static void stimer_test_all(void) 333 { 334 int ncpus; 335 336 setup_vm(); 337 enable_apic(); 338 339 ncpus = cpu_count(); 340 if (ncpus > MAX_CPUS) 341 report_abort("number cpus exceeds %d", MAX_CPUS); 342 printf("cpus = %d\n", ncpus); 343 344 handle_irq(SINT1_VEC, stimer_isr); 345 handle_irq(SINT2_VEC, stimer_isr_auto_eoi); 346 347 on_cpus(stimer_test_prepare, (void *)read_cr3()); 348 on_cpus(stimer_test, NULL); 349 on_cpus(stimer_test_cleanup, NULL); 350 } 351 352 int main(int ac, char **av) 353 { 354 355 if (!synic_supported()) { 356 report_pass("Hyper-V SynIC is not supported"); 357 goto done; 358 } 359 360 if (!stimer_supported()) { 361 report_pass("Hyper-V SynIC timers are not supported"); 362 goto done; 363 } 364 365 if (!hv_time_ref_counter_supported()) { 366 report_pass("Hyper-V time reference counter is not supported"); 367 goto done; 368 } 369 370 stimer_test_all(); 371 done: 372 return report_summary(); 373 } 374