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