xref: /kvm-unit-tests/x86/hyperv_stimer.c (revision ddbcb8f4ca8aedc9835851bd9acf2cd5221d7476)
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