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