hyperv_stimer.c 9.1 KB

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