xref: /kvm-unit-tests/x86/hyperv_synic.c (revision 44e9e31959a70314839a425dae42b7970d188a16)
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 
12 #define MAX_CPUS 4
13 
14 static atomic_t isr_enter_count[MAX_CPUS];
15 
16 static void synic_sint_auto_eoi_isr(isr_regs_t *regs)
17 {
18     atomic_inc(&isr_enter_count[smp_id()]);
19 }
20 
21 static void synic_sint_isr(isr_regs_t *regs)
22 {
23     atomic_inc(&isr_enter_count[smp_id()]);
24     eoi();
25 }
26 
27 struct sint_vec_entry {
28     int vec;
29     bool auto_eoi;
30 };
31 
32 struct sint_vec_entry sint_vecs[HV_SYNIC_SINT_COUNT] = {
33     {0xB0, false},
34     {0xB1, false},
35     {0xB2, false},
36     {0xB3, true},
37     {0xB4, false},
38     {0xB5, false},
39     {0xB6, false},
40     {0xB7, false},
41     {0xB8, true},
42     {0xB9, false},
43     {0xBA, true},
44     {0xBB, false},
45     {0xBC, false},
46     {0xBD, false},
47     {0xBE, true},
48     {0xBF, false},
49 };
50 
51 static void synic_prepare_sint_vecs(void)
52 {
53     bool auto_eoi;
54     int i, vec;
55 
56     for (i = 0; i < HV_SYNIC_SINT_COUNT; i++) {
57         vec = sint_vecs[i].vec;
58         auto_eoi = sint_vecs[i].auto_eoi;
59         handle_irq(vec, (auto_eoi) ? synic_sint_auto_eoi_isr : synic_sint_isr);
60     }
61 }
62 
63 static void synic_sints_prepare(int vcpu)
64 {
65     bool auto_eoi;
66     int i, vec;
67 
68     for (i = 0; i < HV_SYNIC_SINT_COUNT; i++) {
69         vec = sint_vecs[i].vec;
70         auto_eoi = sint_vecs[i].auto_eoi;
71         synic_sint_create(i, vec, auto_eoi);
72     }
73 }
74 
75 static void synic_test_prepare(void *ctx)
76 {
77     u64 r;
78     int i = 0;
79 
80     write_cr3((ulong)ctx);
81     irq_enable();
82 
83     rdmsr(HV_X64_MSR_SVERSION);
84     rdmsr(HV_X64_MSR_SIMP);
85     rdmsr(HV_X64_MSR_SIEFP);
86     rdmsr(HV_X64_MSR_SCONTROL);
87     for (i = 0; i < HV_SYNIC_SINT_COUNT; i++) {
88         rdmsr(HV_X64_MSR_SINT0 + i);
89     }
90     r = rdmsr(HV_X64_MSR_EOM);
91     if (r != 0) {
92         report("Hyper-V SynIC test, EOM read %#" PRIx64, false, r);
93         return;
94     }
95 
96     wrmsr(HV_X64_MSR_SIMP, (u64)virt_to_phys(alloc_page()) |
97             HV_SYNIC_SIMP_ENABLE);
98     wrmsr(HV_X64_MSR_SIEFP, (u64)virt_to_phys(alloc_page())|
99             HV_SYNIC_SIEFP_ENABLE);
100     wrmsr(HV_X64_MSR_SCONTROL, HV_SYNIC_CONTROL_ENABLE);
101 
102     synic_sints_prepare(smp_id());
103 }
104 
105 static void synic_sints_test(int dst_vcpu)
106 {
107     int i;
108 
109     atomic_set(&isr_enter_count[dst_vcpu], 0);
110     for (i = 0; i < HV_SYNIC_SINT_COUNT; i++) {
111         synic_sint_set(dst_vcpu, i);
112     }
113 
114     while (atomic_read(&isr_enter_count[dst_vcpu]) != HV_SYNIC_SINT_COUNT) {
115         pause();
116     }
117 }
118 
119 static void synic_test(void *ctx)
120 {
121     int dst_vcpu = (ulong)ctx;
122 
123     irq_enable();
124     synic_sints_test(dst_vcpu);
125 }
126 
127 static void synic_test_cleanup(void *ctx)
128 {
129     int i;
130 
131     irq_enable();
132     for (i = 0; i < HV_SYNIC_SINT_COUNT; i++) {
133         synic_sint_destroy(i);
134     }
135 
136     wrmsr(HV_X64_MSR_SCONTROL, 0);
137     wrmsr(HV_X64_MSR_SIMP, 0);
138     wrmsr(HV_X64_MSR_SIEFP, 0);
139 }
140 
141 int main(int ac, char **av)
142 {
143 
144     if (synic_supported()) {
145         int ncpus, i;
146         bool ok;
147 
148         setup_vm();
149         smp_init();
150         enable_apic();
151 
152         ncpus = cpu_count();
153         if (ncpus > MAX_CPUS)
154             report_abort("number cpus exceeds %d", MAX_CPUS);
155         printf("ncpus = %d\n", ncpus);
156 
157         synic_prepare_sint_vecs();
158 
159         printf("prepare\n");
160         on_cpus(synic_test_prepare, (void *)read_cr3());
161 
162         for (i = 0; i < ncpus; i++) {
163             printf("test %d -> %d\n", i, ncpus - 1 - i);
164             on_cpu_async(i, synic_test, (void *)(ulong)(ncpus - 1 - i));
165         }
166         while (cpus_active() > 1)
167             pause();
168 
169         printf("cleanup\n");
170         on_cpus(synic_test_cleanup, NULL);
171 
172         ok = true;
173         for (i = 0; i < ncpus; ++i) {
174             printf("isr_enter_count[%d] = %d\n",
175                    i, atomic_read(&isr_enter_count[i]));
176             ok &= atomic_read(&isr_enter_count[i]) == 16;
177         }
178 
179         report("Hyper-V SynIC test", ok);
180     } else {
181         printf("Hyper-V SynIC is not supported");
182     }
183 
184     return report_summary();
185 }
186