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