xapic_tpr_test.c 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. #include <fcntl.h>
  3. #include <stdatomic.h>
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include <string.h>
  7. #include <sys/ioctl.h>
  8. #include <unistd.h>
  9. #include "apic.h"
  10. #include "kvm_util.h"
  11. #include "processor.h"
  12. #include "test_util.h"
  13. static bool is_x2apic;
  14. #define IRQ_VECTOR 0x20
  15. /* See also the comment at similar assertion in memslot_perf_test.c */
  16. static_assert(ATOMIC_INT_LOCK_FREE == 2, "atomic int is not lockless");
  17. static atomic_uint tpr_guest_irq_sync_val;
  18. static void tpr_guest_irq_sync_flag_reset(void)
  19. {
  20. atomic_store_explicit(&tpr_guest_irq_sync_val, 0,
  21. memory_order_release);
  22. }
  23. static unsigned int tpr_guest_irq_sync_val_get(void)
  24. {
  25. return atomic_load_explicit(&tpr_guest_irq_sync_val,
  26. memory_order_acquire);
  27. }
  28. static void tpr_guest_irq_sync_val_inc(void)
  29. {
  30. atomic_fetch_add_explicit(&tpr_guest_irq_sync_val, 1,
  31. memory_order_acq_rel);
  32. }
  33. static void tpr_guest_irq_handler_xapic(struct ex_regs *regs)
  34. {
  35. tpr_guest_irq_sync_val_inc();
  36. xapic_write_reg(APIC_EOI, 0);
  37. }
  38. static void tpr_guest_irq_handler_x2apic(struct ex_regs *regs)
  39. {
  40. tpr_guest_irq_sync_val_inc();
  41. x2apic_write_reg(APIC_EOI, 0);
  42. }
  43. static void tpr_guest_irq_queue(void)
  44. {
  45. if (is_x2apic) {
  46. x2apic_write_reg(APIC_SELF_IPI, IRQ_VECTOR);
  47. } else {
  48. uint32_t icr, icr2;
  49. icr = APIC_DEST_SELF | APIC_DEST_PHYSICAL | APIC_DM_FIXED |
  50. IRQ_VECTOR;
  51. icr2 = 0;
  52. xapic_write_reg(APIC_ICR2, icr2);
  53. xapic_write_reg(APIC_ICR, icr);
  54. }
  55. }
  56. static uint8_t tpr_guest_tpr_get(void)
  57. {
  58. uint32_t taskpri;
  59. if (is_x2apic)
  60. taskpri = x2apic_read_reg(APIC_TASKPRI);
  61. else
  62. taskpri = xapic_read_reg(APIC_TASKPRI);
  63. return GET_APIC_PRI(taskpri);
  64. }
  65. static uint8_t tpr_guest_ppr_get(void)
  66. {
  67. uint32_t procpri;
  68. if (is_x2apic)
  69. procpri = x2apic_read_reg(APIC_PROCPRI);
  70. else
  71. procpri = xapic_read_reg(APIC_PROCPRI);
  72. return GET_APIC_PRI(procpri);
  73. }
  74. static uint8_t tpr_guest_cr8_get(void)
  75. {
  76. uint64_t cr8;
  77. asm volatile ("mov %%cr8, %[cr8]\n\t" : [cr8] "=r"(cr8));
  78. return cr8 & GENMASK(3, 0);
  79. }
  80. static void tpr_guest_check_tpr_ppr_cr8_equal(void)
  81. {
  82. uint8_t tpr;
  83. tpr = tpr_guest_tpr_get();
  84. GUEST_ASSERT_EQ(tpr_guest_ppr_get(), tpr);
  85. GUEST_ASSERT_EQ(tpr_guest_cr8_get(), tpr);
  86. }
  87. static void tpr_guest_code(void)
  88. {
  89. cli();
  90. if (is_x2apic)
  91. x2apic_enable();
  92. else
  93. xapic_enable();
  94. GUEST_ASSERT_EQ(tpr_guest_tpr_get(), 0);
  95. tpr_guest_check_tpr_ppr_cr8_equal();
  96. tpr_guest_irq_queue();
  97. /* TPR = 0 but IRQ masked by IF=0, should not fire */
  98. udelay(1000);
  99. GUEST_ASSERT_EQ(tpr_guest_irq_sync_val_get(), 0);
  100. sti();
  101. /* IF=1 now, IRQ should fire */
  102. while (tpr_guest_irq_sync_val_get() == 0)
  103. cpu_relax();
  104. GUEST_ASSERT_EQ(tpr_guest_irq_sync_val_get(), 1);
  105. GUEST_SYNC(true);
  106. tpr_guest_check_tpr_ppr_cr8_equal();
  107. tpr_guest_irq_queue();
  108. /* IRQ masked by barely high enough TPR now, should not fire */
  109. udelay(1000);
  110. GUEST_ASSERT_EQ(tpr_guest_irq_sync_val_get(), 1);
  111. GUEST_SYNC(false);
  112. tpr_guest_check_tpr_ppr_cr8_equal();
  113. /* TPR barely low enough now to unmask IRQ, should fire */
  114. while (tpr_guest_irq_sync_val_get() == 1)
  115. cpu_relax();
  116. GUEST_ASSERT_EQ(tpr_guest_irq_sync_val_get(), 2);
  117. GUEST_DONE();
  118. }
  119. static uint8_t lapic_tpr_get(struct kvm_lapic_state *xapic)
  120. {
  121. return GET_APIC_PRI(*((u32 *)&xapic->regs[APIC_TASKPRI]));
  122. }
  123. static void lapic_tpr_set(struct kvm_lapic_state *xapic, uint8_t val)
  124. {
  125. u32 *taskpri = (u32 *)&xapic->regs[APIC_TASKPRI];
  126. *taskpri = SET_APIC_PRI(*taskpri, val);
  127. }
  128. static uint8_t sregs_tpr(struct kvm_sregs *sregs)
  129. {
  130. return sregs->cr8 & GENMASK(3, 0);
  131. }
  132. static void test_tpr_check_tpr_zero(struct kvm_vcpu *vcpu)
  133. {
  134. struct kvm_lapic_state xapic;
  135. vcpu_ioctl(vcpu, KVM_GET_LAPIC, &xapic);
  136. TEST_ASSERT_EQ(lapic_tpr_get(&xapic), 0);
  137. }
  138. static void test_tpr_check_tpr_cr8_equal(struct kvm_vcpu *vcpu)
  139. {
  140. struct kvm_sregs sregs;
  141. struct kvm_lapic_state xapic;
  142. vcpu_sregs_get(vcpu, &sregs);
  143. vcpu_ioctl(vcpu, KVM_GET_LAPIC, &xapic);
  144. TEST_ASSERT_EQ(sregs_tpr(&sregs), lapic_tpr_get(&xapic));
  145. }
  146. static void test_tpr_set_tpr_for_irq(struct kvm_vcpu *vcpu, bool mask)
  147. {
  148. struct kvm_lapic_state xapic;
  149. uint8_t tpr;
  150. static_assert(IRQ_VECTOR >= 16, "invalid IRQ vector number");
  151. tpr = IRQ_VECTOR / 16;
  152. if (!mask)
  153. tpr--;
  154. vcpu_ioctl(vcpu, KVM_GET_LAPIC, &xapic);
  155. lapic_tpr_set(&xapic, tpr);
  156. vcpu_ioctl(vcpu, KVM_SET_LAPIC, &xapic);
  157. }
  158. static void test_tpr(bool __is_x2apic)
  159. {
  160. struct kvm_vcpu *vcpu;
  161. struct kvm_vm *vm;
  162. bool done = false;
  163. is_x2apic = __is_x2apic;
  164. vm = vm_create_with_one_vcpu(&vcpu, tpr_guest_code);
  165. if (is_x2apic) {
  166. vm_install_exception_handler(vm, IRQ_VECTOR,
  167. tpr_guest_irq_handler_x2apic);
  168. } else {
  169. vm_install_exception_handler(vm, IRQ_VECTOR,
  170. tpr_guest_irq_handler_xapic);
  171. vcpu_clear_cpuid_feature(vcpu, X86_FEATURE_X2APIC);
  172. virt_pg_map(vm, APIC_DEFAULT_GPA, APIC_DEFAULT_GPA);
  173. }
  174. sync_global_to_guest(vcpu->vm, is_x2apic);
  175. /* According to the SDM/APM the TPR value at reset is 0 */
  176. test_tpr_check_tpr_zero(vcpu);
  177. test_tpr_check_tpr_cr8_equal(vcpu);
  178. tpr_guest_irq_sync_flag_reset();
  179. sync_global_to_guest(vcpu->vm, tpr_guest_irq_sync_val);
  180. while (!done) {
  181. struct ucall uc;
  182. alarm(2);
  183. vcpu_run(vcpu);
  184. alarm(0);
  185. switch (get_ucall(vcpu, &uc)) {
  186. case UCALL_ABORT:
  187. REPORT_GUEST_ASSERT(uc);
  188. break;
  189. case UCALL_DONE:
  190. test_tpr_check_tpr_cr8_equal(vcpu);
  191. done = true;
  192. break;
  193. case UCALL_SYNC:
  194. test_tpr_check_tpr_cr8_equal(vcpu);
  195. test_tpr_set_tpr_for_irq(vcpu, uc.args[1]);
  196. break;
  197. default:
  198. TEST_FAIL("Unknown ucall result 0x%lx", uc.cmd);
  199. break;
  200. }
  201. }
  202. kvm_vm_free(vm);
  203. }
  204. int main(int argc, char *argv[])
  205. {
  206. /*
  207. * Use separate VMs for the xAPIC and x2APIC tests so that x2APIC can
  208. * be fully hidden from the guest. KVM disallows changing CPUID after
  209. * KVM_RUN and AVIC is disabled if _any_ vCPU is allowed to use x2APIC.
  210. */
  211. test_tpr(false);
  212. test_tpr(true);
  213. }