xref: /kvm-unit-tests/x86/hyperv_synic.c (revision 17fdf23ec6d9e5c213da847d59f98e8c90d3ff34)
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 "io.h"
9 #include "smp.h"
10 #include "atomic.h"
11 
12 #define MAX_CPUS 4
13 #define HYPERV_CPUID_FEATURES                   0x40000003
14 #define HV_X64_MSR_SYNIC_AVAILABLE              (1 << 2)
15 #define HV_SYNIC_CONTROL_ENABLE                 (1ULL << 0)
16 #define HV_SYNIC_SIMP_ENABLE                    (1ULL << 0)
17 #define HV_SYNIC_SIEFP_ENABLE                   (1ULL << 0)
18 #define HV_SYNIC_SINT_MASKED                    (1ULL << 16)
19 #define HV_SYNIC_SINT_AUTO_EOI                  (1ULL << 17)
20 #define HV_SYNIC_SINT_VECTOR_MASK               (0xFF)
21 #define HV_SYNIC_SINT_COUNT                     16
22 
23 enum {
24     HV_TEST_DEV_SINT_ROUTE_CREATE = 1,
25     HV_TEST_DEV_SINT_ROUTE_DESTROY,
26     HV_TEST_DEV_SINT_ROUTE_SET_SINT
27 };
28 
29 static atomic_t isr_enter_count[MAX_CPUS];
30 static atomic_t cpus_comp_count;
31 
32 static bool synic_supported(void)
33 {
34    return cpuid(HYPERV_CPUID_FEATURES).a & HV_X64_MSR_SYNIC_AVAILABLE;
35 }
36 
37 static void synic_sint_auto_eoi_isr(isr_regs_t *regs)
38 {
39     atomic_inc(&isr_enter_count[smp_id()]);
40 }
41 
42 static void synic_sint_isr(isr_regs_t *regs)
43 {
44     atomic_inc(&isr_enter_count[smp_id()]);
45     eoi();
46 }
47 
48 static void synic_ctl(u8 ctl, u8 vcpu_id, u8 sint)
49 {
50     outl((ctl << 16)|((vcpu_id) << 8)|sint, 0x3000);
51 }
52 
53 struct sint_vec_entry {
54     int vec;
55     bool auto_eoi;
56 };
57 
58 struct sint_vec_entry sint_vecs[HV_SYNIC_SINT_COUNT] = {
59     {0xB0, false},
60     {0xB1, false},
61     {0xB2, false},
62     {0xB3, true},
63     {0xB4, false},
64     {0xB5, false},
65     {0xB6, false},
66     {0xB7, false},
67     {0xB8, true},
68     {0xB9, false},
69     {0xBA, true},
70     {0xBB, false},
71     {0xBC, false},
72     {0xBD, false},
73     {0xBE, true},
74     {0xBF, false},
75 };
76 
77 static void synic_prepare_sint_vecs(void)
78 {
79     bool auto_eoi;
80     int i, vec;
81 
82     for (i = 0; i < HV_SYNIC_SINT_COUNT; i++) {
83         vec = sint_vecs[i].vec;
84         auto_eoi = sint_vecs[i].auto_eoi;
85         handle_irq(vec, (auto_eoi) ? synic_sint_auto_eoi_isr : synic_sint_isr);
86     }
87 }
88 
89 static void synic_sints_prepare(u8 vcpu)
90 {
91     bool auto_eoi;
92     int i, vec;
93 
94     for (i = 0; i < HV_SYNIC_SINT_COUNT; i++) {
95         vec = sint_vecs[i].vec;
96         auto_eoi = sint_vecs[i].auto_eoi;
97         wrmsr(HV_X64_MSR_SINT0 + i,
98                 (u64)vec | ((auto_eoi) ? HV_SYNIC_SINT_AUTO_EOI : 0));
99         synic_ctl(HV_TEST_DEV_SINT_ROUTE_CREATE, vcpu, i);
100     }
101 }
102 
103 static void synic_test_prepare(void *ctx)
104 {
105     u64 r;
106     int i = 0;
107 
108     write_cr3((ulong)ctx);
109     irq_enable();
110 
111     rdmsr(HV_X64_MSR_SVERSION);
112     rdmsr(HV_X64_MSR_SIMP);
113     rdmsr(HV_X64_MSR_SIEFP);
114     rdmsr(HV_X64_MSR_SCONTROL);
115     for (i = 0; i < HV_SYNIC_SINT_COUNT; i++) {
116         rdmsr(HV_X64_MSR_SINT0 + i);
117     }
118     r = rdmsr(HV_X64_MSR_EOM);
119     if (r != 0) {
120         report("Hyper-V SynIC test, EOM read 0x%llx", false, r);
121         goto ret;
122     }
123 
124     wrmsr(HV_X64_MSR_SIMP, (u64)virt_to_phys(alloc_page()) |
125             HV_SYNIC_SIMP_ENABLE);
126     wrmsr(HV_X64_MSR_SIEFP, (u64)virt_to_phys(alloc_page())|
127             HV_SYNIC_SIEFP_ENABLE);
128     wrmsr(HV_X64_MSR_SCONTROL, HV_SYNIC_CONTROL_ENABLE);
129 
130     synic_sints_prepare(smp_id());
131 ret:
132     atomic_inc(&cpus_comp_count);
133 }
134 
135 static void synic_sints_test(u8 dst_vcpu)
136 {
137     int i;
138 
139     atomic_set(&isr_enter_count[dst_vcpu], 0);
140     for (i = 0; i < HV_SYNIC_SINT_COUNT; i++) {
141         synic_ctl(HV_TEST_DEV_SINT_ROUTE_SET_SINT, dst_vcpu, i);
142     }
143 
144     while (atomic_read(&isr_enter_count[dst_vcpu]) != HV_SYNIC_SINT_COUNT) {
145         pause();
146     }
147 }
148 
149 static void synic_test(void *ctx)
150 {
151     u8 dst_vcpu = (ulong)ctx;
152 
153     irq_enable();
154     synic_sints_test(dst_vcpu);
155     atomic_inc(&cpus_comp_count);
156 }
157 
158 static void synic_test_cleanup(void *ctx)
159 {
160     u8 vcpu = smp_id();
161     int i;
162 
163     irq_enable();
164     for (i = 0; i < HV_SYNIC_SINT_COUNT; i++) {
165         synic_ctl(HV_TEST_DEV_SINT_ROUTE_DESTROY, vcpu, i);
166         wrmsr(HV_X64_MSR_SINT0 + i, 0xFF|HV_SYNIC_SINT_MASKED);
167     }
168 
169     wrmsr(HV_X64_MSR_SCONTROL, 0);
170     wrmsr(HV_X64_MSR_SIMP, 0);
171     wrmsr(HV_X64_MSR_SIEFP, 0);
172     atomic_inc(&cpus_comp_count);
173 }
174 
175 int main(int ac, char **av)
176 {
177 
178     if (synic_supported()) {
179         int ncpus, i;
180         bool ok;
181 
182         setup_vm();
183         smp_init();
184         setup_idt();
185         enable_apic();
186 
187         synic_prepare_sint_vecs();
188 
189         ncpus = cpu_count();
190         if (ncpus > MAX_CPUS) {
191             ncpus = MAX_CPUS;
192         }
193         printf("ncpus = %d\n", ncpus);
194 
195         atomic_set(&cpus_comp_count, 0);
196         for (i = 0; i < ncpus; i++) {
197             on_cpu_async(i, synic_test_prepare, (void *)read_cr3());
198         }
199         printf("prepare\n");
200         while (atomic_read(&cpus_comp_count) != ncpus) {
201             pause();
202         }
203 
204         atomic_set(&cpus_comp_count, 0);
205         for (i = 0; i < ncpus; i++) {
206             printf("test %d -> %d\n", i, ncpus - 1 - i);
207             on_cpu_async(i, synic_test, (void *)(ulong)(ncpus - 1 - i));
208         }
209         while (atomic_read(&cpus_comp_count) != ncpus) {
210             pause();
211         }
212 
213         atomic_set(&cpus_comp_count, 0);
214         for (i = 0; i < ncpus; i++) {
215             on_cpu_async(i, synic_test_cleanup, NULL);
216         }
217         printf("cleanup\n");
218         while (atomic_read(&cpus_comp_count) != ncpus) {
219             pause();
220         }
221 
222         ok = true;
223         for (i = 0; i < ncpus; ++i) {
224             printf("isr_enter_count[%d] = %d\n",
225                    i, atomic_read(&isr_enter_count[i]));
226             ok &= atomic_read(&isr_enter_count[i]) == 16;
227         }
228 
229         report("Hyper-V SynIC test", ok);
230     } else {
231         report("Hyper-V SynIC is not supported", true);
232     }
233 
234     return report_summary();
235 }
236