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
synic_alloc_page(void)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
synic_free_page(void * page)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
stimer_init(struct stimer * timer,int index)62 static void stimer_init(struct stimer *timer, int index)
63 {
64 memset(timer, 0, sizeof(*timer));
65 timer->index = index;
66 }
67
synic_enable(void)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
stimer_shutdown(struct stimer * timer)84 static void stimer_shutdown(struct stimer *timer)
85 {
86 wrmsr(HV_X64_MSR_STIMER0_CONFIG + 2*timer->index, 0);
87 }
88
process_stimer_expired(struct stimer * timer)89 static void process_stimer_expired(struct stimer *timer)
90 {
91 atomic_inc(&timer->fire_count);
92 }
93
process_stimer_msg(struct svcpu * svcpu,struct hv_message * msg,int sint)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
__stimer_isr(int vcpu)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
stimer_isr(isr_regs_t * regs)157 static void stimer_isr(isr_regs_t *regs)
158 {
159 int vcpu = smp_id();
160
161 __stimer_isr(vcpu);
162 eoi();
163 }
164
stimer_isr_auto_eoi(isr_regs_t * regs)165 static void stimer_isr_auto_eoi(isr_regs_t *regs)
166 {
167 int vcpu = smp_id();
168
169 __stimer_isr(vcpu);
170 }
171
__stimer_isr_direct(int vcpu,int timer_index)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
stimer_isr_direct1(isr_regs_t * regs)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
stimer_isr_direct2(isr_regs_t * regs)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
stimer_start(struct stimer * timer,bool auto_enable,bool periodic,u64 tick_100ns)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
stimers_shutdown(void)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
synic_disable(void)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
stimer_test_prepare(void * ctx)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
stimer_test_prepare_direct(void * ctx)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
stimer_test_periodic(int vcpu,struct stimer * timer1,struct stimer * timer2)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
stimer_test_one_shot(int vcpu,struct stimer * timer)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
stimer_test_auto_enable_one_shot(int vcpu,struct stimer * timer)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
stimer_test_auto_enable_periodic(int vcpu,struct stimer * timer)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
stimer_test_one_shot_busy(int vcpu,struct stimer * timer)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
stimer_test(void * ctx)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
stimer_test_cleanup(void * ctx)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
stimer_test_cleanup_direct(void * ctx)407 static void stimer_test_cleanup_direct(void *ctx)
408 {
409 stimers_shutdown();
410 }
411
stimer_test_all(bool direct)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
main(int argc,char ** argv)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