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