hyperv_ipi.c 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Hyper-V HvCallSendSyntheticClusterIpi{,Ex} tests
  4. *
  5. * Copyright (C) 2022, Red Hat, Inc.
  6. *
  7. */
  8. #include <pthread.h>
  9. #include <inttypes.h>
  10. #include "kvm_util.h"
  11. #include "hyperv.h"
  12. #include "test_util.h"
  13. #include "vmx.h"
  14. #define RECEIVER_VCPU_ID_1 2
  15. #define RECEIVER_VCPU_ID_2 65
  16. #define IPI_VECTOR 0xfe
  17. static volatile uint64_t ipis_rcvd[RECEIVER_VCPU_ID_2 + 1];
  18. struct hv_vpset {
  19. u64 format;
  20. u64 valid_bank_mask;
  21. u64 bank_contents[2];
  22. };
  23. enum HV_GENERIC_SET_FORMAT {
  24. HV_GENERIC_SET_SPARSE_4K,
  25. HV_GENERIC_SET_ALL,
  26. };
  27. /* HvCallSendSyntheticClusterIpi hypercall */
  28. struct hv_send_ipi {
  29. u32 vector;
  30. u32 reserved;
  31. u64 cpu_mask;
  32. };
  33. /* HvCallSendSyntheticClusterIpiEx hypercall */
  34. struct hv_send_ipi_ex {
  35. u32 vector;
  36. u32 reserved;
  37. struct hv_vpset vp_set;
  38. };
  39. static inline void hv_init(vm_vaddr_t pgs_gpa)
  40. {
  41. wrmsr(HV_X64_MSR_GUEST_OS_ID, HYPERV_LINUX_OS_ID);
  42. wrmsr(HV_X64_MSR_HYPERCALL, pgs_gpa);
  43. }
  44. static void receiver_code(void *hcall_page, vm_vaddr_t pgs_gpa)
  45. {
  46. u32 vcpu_id;
  47. x2apic_enable();
  48. hv_init(pgs_gpa);
  49. vcpu_id = rdmsr(HV_X64_MSR_VP_INDEX);
  50. /* Signal sender vCPU we're ready */
  51. ipis_rcvd[vcpu_id] = (u64)-1;
  52. for (;;) {
  53. safe_halt();
  54. cli();
  55. }
  56. }
  57. static void guest_ipi_handler(struct ex_regs *regs)
  58. {
  59. u32 vcpu_id = rdmsr(HV_X64_MSR_VP_INDEX);
  60. ipis_rcvd[vcpu_id]++;
  61. wrmsr(HV_X64_MSR_EOI, 1);
  62. }
  63. static inline void nop_loop(void)
  64. {
  65. int i;
  66. for (i = 0; i < 100000000; i++)
  67. asm volatile("nop");
  68. }
  69. static void sender_guest_code(void *hcall_page, vm_vaddr_t pgs_gpa)
  70. {
  71. struct hv_send_ipi *ipi = (struct hv_send_ipi *)hcall_page;
  72. struct hv_send_ipi_ex *ipi_ex = (struct hv_send_ipi_ex *)hcall_page;
  73. int stage = 1, ipis_expected[2] = {0};
  74. hv_init(pgs_gpa);
  75. GUEST_SYNC(stage++);
  76. /* Wait for receiver vCPUs to come up */
  77. while (!ipis_rcvd[RECEIVER_VCPU_ID_1] || !ipis_rcvd[RECEIVER_VCPU_ID_2])
  78. nop_loop();
  79. ipis_rcvd[RECEIVER_VCPU_ID_1] = ipis_rcvd[RECEIVER_VCPU_ID_2] = 0;
  80. /* 'Slow' HvCallSendSyntheticClusterIpi to RECEIVER_VCPU_ID_1 */
  81. ipi->vector = IPI_VECTOR;
  82. ipi->cpu_mask = 1 << RECEIVER_VCPU_ID_1;
  83. hyperv_hypercall(HVCALL_SEND_IPI, pgs_gpa, pgs_gpa + PAGE_SIZE);
  84. nop_loop();
  85. GUEST_ASSERT(ipis_rcvd[RECEIVER_VCPU_ID_1] == ++ipis_expected[0]);
  86. GUEST_ASSERT(ipis_rcvd[RECEIVER_VCPU_ID_2] == ipis_expected[1]);
  87. GUEST_SYNC(stage++);
  88. /* 'Fast' HvCallSendSyntheticClusterIpi to RECEIVER_VCPU_ID_1 */
  89. hyperv_hypercall(HVCALL_SEND_IPI | HV_HYPERCALL_FAST_BIT,
  90. IPI_VECTOR, 1 << RECEIVER_VCPU_ID_1);
  91. nop_loop();
  92. GUEST_ASSERT(ipis_rcvd[RECEIVER_VCPU_ID_1] == ++ipis_expected[0]);
  93. GUEST_ASSERT(ipis_rcvd[RECEIVER_VCPU_ID_2] == ipis_expected[1]);
  94. GUEST_SYNC(stage++);
  95. /* 'Slow' HvCallSendSyntheticClusterIpiEx to RECEIVER_VCPU_ID_1 */
  96. memset(hcall_page, 0, PAGE_SIZE);
  97. ipi_ex->vector = IPI_VECTOR;
  98. ipi_ex->vp_set.format = HV_GENERIC_SET_SPARSE_4K;
  99. ipi_ex->vp_set.valid_bank_mask = 1 << 0;
  100. ipi_ex->vp_set.bank_contents[0] = BIT(RECEIVER_VCPU_ID_1);
  101. hyperv_hypercall(HVCALL_SEND_IPI_EX | (1 << HV_HYPERCALL_VARHEAD_OFFSET),
  102. pgs_gpa, pgs_gpa + PAGE_SIZE);
  103. nop_loop();
  104. GUEST_ASSERT(ipis_rcvd[RECEIVER_VCPU_ID_1] == ++ipis_expected[0]);
  105. GUEST_ASSERT(ipis_rcvd[RECEIVER_VCPU_ID_2] == ipis_expected[1]);
  106. GUEST_SYNC(stage++);
  107. /* 'XMM Fast' HvCallSendSyntheticClusterIpiEx to RECEIVER_VCPU_ID_1 */
  108. hyperv_write_xmm_input(&ipi_ex->vp_set.valid_bank_mask, 1);
  109. hyperv_hypercall(HVCALL_SEND_IPI_EX | HV_HYPERCALL_FAST_BIT |
  110. (1 << HV_HYPERCALL_VARHEAD_OFFSET),
  111. IPI_VECTOR, HV_GENERIC_SET_SPARSE_4K);
  112. nop_loop();
  113. GUEST_ASSERT(ipis_rcvd[RECEIVER_VCPU_ID_1] == ++ipis_expected[0]);
  114. GUEST_ASSERT(ipis_rcvd[RECEIVER_VCPU_ID_2] == ipis_expected[1]);
  115. GUEST_SYNC(stage++);
  116. /* 'Slow' HvCallSendSyntheticClusterIpiEx to RECEIVER_VCPU_ID_2 */
  117. memset(hcall_page, 0, PAGE_SIZE);
  118. ipi_ex->vector = IPI_VECTOR;
  119. ipi_ex->vp_set.format = HV_GENERIC_SET_SPARSE_4K;
  120. ipi_ex->vp_set.valid_bank_mask = 1 << 1;
  121. ipi_ex->vp_set.bank_contents[0] = BIT(RECEIVER_VCPU_ID_2 - 64);
  122. hyperv_hypercall(HVCALL_SEND_IPI_EX | (1 << HV_HYPERCALL_VARHEAD_OFFSET),
  123. pgs_gpa, pgs_gpa + PAGE_SIZE);
  124. nop_loop();
  125. GUEST_ASSERT(ipis_rcvd[RECEIVER_VCPU_ID_1] == ipis_expected[0]);
  126. GUEST_ASSERT(ipis_rcvd[RECEIVER_VCPU_ID_2] == ++ipis_expected[1]);
  127. GUEST_SYNC(stage++);
  128. /* 'XMM Fast' HvCallSendSyntheticClusterIpiEx to RECEIVER_VCPU_ID_2 */
  129. hyperv_write_xmm_input(&ipi_ex->vp_set.valid_bank_mask, 1);
  130. hyperv_hypercall(HVCALL_SEND_IPI_EX | HV_HYPERCALL_FAST_BIT |
  131. (1 << HV_HYPERCALL_VARHEAD_OFFSET),
  132. IPI_VECTOR, HV_GENERIC_SET_SPARSE_4K);
  133. nop_loop();
  134. GUEST_ASSERT(ipis_rcvd[RECEIVER_VCPU_ID_1] == ipis_expected[0]);
  135. GUEST_ASSERT(ipis_rcvd[RECEIVER_VCPU_ID_2] == ++ipis_expected[1]);
  136. GUEST_SYNC(stage++);
  137. /* 'Slow' HvCallSendSyntheticClusterIpiEx to both RECEIVER_VCPU_ID_{1,2} */
  138. memset(hcall_page, 0, PAGE_SIZE);
  139. ipi_ex->vector = IPI_VECTOR;
  140. ipi_ex->vp_set.format = HV_GENERIC_SET_SPARSE_4K;
  141. ipi_ex->vp_set.valid_bank_mask = 1 << 1 | 1;
  142. ipi_ex->vp_set.bank_contents[0] = BIT(RECEIVER_VCPU_ID_1);
  143. ipi_ex->vp_set.bank_contents[1] = BIT(RECEIVER_VCPU_ID_2 - 64);
  144. hyperv_hypercall(HVCALL_SEND_IPI_EX | (2 << HV_HYPERCALL_VARHEAD_OFFSET),
  145. pgs_gpa, pgs_gpa + PAGE_SIZE);
  146. nop_loop();
  147. GUEST_ASSERT(ipis_rcvd[RECEIVER_VCPU_ID_1] == ++ipis_expected[0]);
  148. GUEST_ASSERT(ipis_rcvd[RECEIVER_VCPU_ID_2] == ++ipis_expected[1]);
  149. GUEST_SYNC(stage++);
  150. /* 'XMM Fast' HvCallSendSyntheticClusterIpiEx to both RECEIVER_VCPU_ID_{1, 2} */
  151. hyperv_write_xmm_input(&ipi_ex->vp_set.valid_bank_mask, 2);
  152. hyperv_hypercall(HVCALL_SEND_IPI_EX | HV_HYPERCALL_FAST_BIT |
  153. (2 << HV_HYPERCALL_VARHEAD_OFFSET),
  154. IPI_VECTOR, HV_GENERIC_SET_SPARSE_4K);
  155. nop_loop();
  156. GUEST_ASSERT(ipis_rcvd[RECEIVER_VCPU_ID_1] == ++ipis_expected[0]);
  157. GUEST_ASSERT(ipis_rcvd[RECEIVER_VCPU_ID_2] == ++ipis_expected[1]);
  158. GUEST_SYNC(stage++);
  159. /* 'Slow' HvCallSendSyntheticClusterIpiEx to HV_GENERIC_SET_ALL */
  160. memset(hcall_page, 0, PAGE_SIZE);
  161. ipi_ex->vector = IPI_VECTOR;
  162. ipi_ex->vp_set.format = HV_GENERIC_SET_ALL;
  163. hyperv_hypercall(HVCALL_SEND_IPI_EX, pgs_gpa, pgs_gpa + PAGE_SIZE);
  164. nop_loop();
  165. GUEST_ASSERT(ipis_rcvd[RECEIVER_VCPU_ID_1] == ++ipis_expected[0]);
  166. GUEST_ASSERT(ipis_rcvd[RECEIVER_VCPU_ID_2] == ++ipis_expected[1]);
  167. GUEST_SYNC(stage++);
  168. /*
  169. * 'XMM Fast' HvCallSendSyntheticClusterIpiEx to HV_GENERIC_SET_ALL.
  170. */
  171. ipi_ex->vp_set.valid_bank_mask = 0;
  172. hyperv_write_xmm_input(&ipi_ex->vp_set.valid_bank_mask, 2);
  173. hyperv_hypercall(HVCALL_SEND_IPI_EX | HV_HYPERCALL_FAST_BIT,
  174. IPI_VECTOR, HV_GENERIC_SET_ALL);
  175. nop_loop();
  176. GUEST_ASSERT(ipis_rcvd[RECEIVER_VCPU_ID_1] == ++ipis_expected[0]);
  177. GUEST_ASSERT(ipis_rcvd[RECEIVER_VCPU_ID_2] == ++ipis_expected[1]);
  178. GUEST_SYNC(stage++);
  179. GUEST_DONE();
  180. }
  181. static void *vcpu_thread(void *arg)
  182. {
  183. struct kvm_vcpu *vcpu = (struct kvm_vcpu *)arg;
  184. int old, r;
  185. r = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &old);
  186. TEST_ASSERT(!r, "pthread_setcanceltype failed on vcpu_id=%u with errno=%d",
  187. vcpu->id, r);
  188. vcpu_run(vcpu);
  189. TEST_FAIL("vCPU %u exited unexpectedly", vcpu->id);
  190. return NULL;
  191. }
  192. static void cancel_join_vcpu_thread(pthread_t thread, struct kvm_vcpu *vcpu)
  193. {
  194. void *retval;
  195. int r;
  196. r = pthread_cancel(thread);
  197. TEST_ASSERT(!r, "pthread_cancel on vcpu_id=%d failed with errno=%d",
  198. vcpu->id, r);
  199. r = pthread_join(thread, &retval);
  200. TEST_ASSERT(!r, "pthread_join on vcpu_id=%d failed with errno=%d",
  201. vcpu->id, r);
  202. TEST_ASSERT(retval == PTHREAD_CANCELED,
  203. "expected retval=%p, got %p", PTHREAD_CANCELED,
  204. retval);
  205. }
  206. int main(int argc, char *argv[])
  207. {
  208. struct kvm_vm *vm;
  209. struct kvm_vcpu *vcpu[3];
  210. vm_vaddr_t hcall_page;
  211. pthread_t threads[2];
  212. int stage = 1, r;
  213. struct ucall uc;
  214. TEST_REQUIRE(kvm_has_cap(KVM_CAP_HYPERV_SEND_IPI));
  215. vm = vm_create_with_one_vcpu(&vcpu[0], sender_guest_code);
  216. /* Hypercall input/output */
  217. hcall_page = vm_vaddr_alloc_pages(vm, 2);
  218. memset(addr_gva2hva(vm, hcall_page), 0x0, 2 * getpagesize());
  219. vcpu[1] = vm_vcpu_add(vm, RECEIVER_VCPU_ID_1, receiver_code);
  220. vcpu_args_set(vcpu[1], 2, hcall_page, addr_gva2gpa(vm, hcall_page));
  221. vcpu_set_msr(vcpu[1], HV_X64_MSR_VP_INDEX, RECEIVER_VCPU_ID_1);
  222. vcpu_set_hv_cpuid(vcpu[1]);
  223. vcpu[2] = vm_vcpu_add(vm, RECEIVER_VCPU_ID_2, receiver_code);
  224. vcpu_args_set(vcpu[2], 2, hcall_page, addr_gva2gpa(vm, hcall_page));
  225. vcpu_set_msr(vcpu[2], HV_X64_MSR_VP_INDEX, RECEIVER_VCPU_ID_2);
  226. vcpu_set_hv_cpuid(vcpu[2]);
  227. vm_install_exception_handler(vm, IPI_VECTOR, guest_ipi_handler);
  228. vcpu_args_set(vcpu[0], 2, hcall_page, addr_gva2gpa(vm, hcall_page));
  229. vcpu_set_hv_cpuid(vcpu[0]);
  230. r = pthread_create(&threads[0], NULL, vcpu_thread, vcpu[1]);
  231. TEST_ASSERT(!r, "pthread_create failed errno=%d", r);
  232. r = pthread_create(&threads[1], NULL, vcpu_thread, vcpu[2]);
  233. TEST_ASSERT(!r, "pthread_create failed errno=%d", errno);
  234. while (true) {
  235. vcpu_run(vcpu[0]);
  236. TEST_ASSERT_KVM_EXIT_REASON(vcpu[0], KVM_EXIT_IO);
  237. switch (get_ucall(vcpu[0], &uc)) {
  238. case UCALL_SYNC:
  239. TEST_ASSERT(uc.args[1] == stage,
  240. "Unexpected stage: %ld (%d expected)",
  241. uc.args[1], stage);
  242. break;
  243. case UCALL_DONE:
  244. goto done;
  245. case UCALL_ABORT:
  246. REPORT_GUEST_ASSERT(uc);
  247. /* NOT REACHED */
  248. default:
  249. TEST_FAIL("Unknown ucall %lu", uc.cmd);
  250. }
  251. stage++;
  252. }
  253. done:
  254. cancel_join_vcpu_thread(threads[0], vcpu[1]);
  255. cancel_join_vcpu_thread(threads[1], vcpu[2]);
  256. kvm_vm_free(vm);
  257. return r;
  258. }