debug_regs.c 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * KVM guest debug register tests
  4. *
  5. * Copyright (C) 2020, Red Hat, Inc.
  6. */
  7. #include <stdio.h>
  8. #include <string.h>
  9. #include "kvm_util.h"
  10. #include "processor.h"
  11. #include "apic.h"
  12. #define DR6_BD (1 << 13)
  13. #define DR7_GD (1 << 13)
  14. #define IRQ_VECTOR 0xAA
  15. /* For testing data access debug BP */
  16. uint32_t guest_value;
  17. extern unsigned char sw_bp, hw_bp, write_data, ss_start, bd_start;
  18. static void guest_code(void)
  19. {
  20. /* Create a pending interrupt on current vCPU */
  21. x2apic_enable();
  22. x2apic_write_reg(APIC_ICR, APIC_DEST_SELF | APIC_INT_ASSERT |
  23. APIC_DM_FIXED | IRQ_VECTOR);
  24. /*
  25. * Software BP tests.
  26. *
  27. * NOTE: sw_bp need to be before the cmd here, because int3 is an
  28. * exception rather than a normal trap for KVM_SET_GUEST_DEBUG (we
  29. * capture it using the vcpu exception bitmap).
  30. */
  31. asm volatile("sw_bp: int3");
  32. /* Hardware instruction BP test */
  33. asm volatile("hw_bp: nop");
  34. /* Hardware data BP test */
  35. asm volatile("mov $1234,%%rax;\n\t"
  36. "mov %%rax,%0;\n\t write_data:"
  37. : "=m" (guest_value) : : "rax");
  38. /*
  39. * Single step test, covers 2 basic instructions and 2 emulated
  40. *
  41. * Enable interrupts during the single stepping to see that pending
  42. * interrupt we raised is not handled due to KVM_GUESTDBG_BLOCKIRQ.
  43. *
  44. * Write MSR_IA32_TSC_DEADLINE to verify that KVM's fastpath handler
  45. * exits to userspace due to single-step being enabled.
  46. */
  47. asm volatile("ss_start: "
  48. "sti\n\t"
  49. "xor %%eax,%%eax\n\t"
  50. "cpuid\n\t"
  51. "movl $" __stringify(MSR_IA32_TSC_DEADLINE) ", %%ecx\n\t"
  52. "wrmsr\n\t"
  53. "cli\n\t"
  54. : : : "eax", "ebx", "ecx", "edx");
  55. /* DR6.BD test */
  56. asm volatile("bd_start: mov %%dr0, %%rax" : : : "rax");
  57. GUEST_DONE();
  58. }
  59. #define CAST_TO_RIP(v) ((unsigned long long)&(v))
  60. static void vcpu_skip_insn(struct kvm_vcpu *vcpu, int insn_len)
  61. {
  62. struct kvm_regs regs;
  63. vcpu_regs_get(vcpu, &regs);
  64. regs.rip += insn_len;
  65. vcpu_regs_set(vcpu, &regs);
  66. }
  67. int main(void)
  68. {
  69. struct kvm_guest_debug debug;
  70. unsigned long long target_dr6, target_rip;
  71. struct kvm_vcpu *vcpu;
  72. struct kvm_run *run;
  73. struct kvm_vm *vm;
  74. struct ucall uc;
  75. uint64_t cmd;
  76. int i;
  77. /* Instruction lengths starting at ss_start */
  78. int ss_size[6] = {
  79. 1, /* sti*/
  80. 2, /* xor */
  81. 2, /* cpuid */
  82. 5, /* mov */
  83. 2, /* rdmsr */
  84. 1, /* cli */
  85. };
  86. TEST_REQUIRE(kvm_has_cap(KVM_CAP_SET_GUEST_DEBUG));
  87. vm = vm_create_with_one_vcpu(&vcpu, guest_code);
  88. run = vcpu->run;
  89. /* Test software BPs - int3 */
  90. memset(&debug, 0, sizeof(debug));
  91. debug.control = KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_SW_BP;
  92. vcpu_guest_debug_set(vcpu, &debug);
  93. vcpu_run(vcpu);
  94. TEST_ASSERT(run->exit_reason == KVM_EXIT_DEBUG &&
  95. run->debug.arch.exception == BP_VECTOR &&
  96. run->debug.arch.pc == CAST_TO_RIP(sw_bp),
  97. "INT3: exit %d exception %d rip 0x%llx (should be 0x%llx)",
  98. run->exit_reason, run->debug.arch.exception,
  99. run->debug.arch.pc, CAST_TO_RIP(sw_bp));
  100. vcpu_skip_insn(vcpu, 1);
  101. /* Test instruction HW BP over DR[0-3] */
  102. for (i = 0; i < 4; i++) {
  103. memset(&debug, 0, sizeof(debug));
  104. debug.control = KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_HW_BP;
  105. debug.arch.debugreg[i] = CAST_TO_RIP(hw_bp);
  106. debug.arch.debugreg[7] = 0x400 | (1UL << (2*i+1));
  107. vcpu_guest_debug_set(vcpu, &debug);
  108. vcpu_run(vcpu);
  109. target_dr6 = 0xffff0ff0 | (1UL << i);
  110. TEST_ASSERT(run->exit_reason == KVM_EXIT_DEBUG &&
  111. run->debug.arch.exception == DB_VECTOR &&
  112. run->debug.arch.pc == CAST_TO_RIP(hw_bp) &&
  113. run->debug.arch.dr6 == target_dr6,
  114. "INS_HW_BP (DR%d): exit %d exception %d rip 0x%llx "
  115. "(should be 0x%llx) dr6 0x%llx (should be 0x%llx)",
  116. i, run->exit_reason, run->debug.arch.exception,
  117. run->debug.arch.pc, CAST_TO_RIP(hw_bp),
  118. run->debug.arch.dr6, target_dr6);
  119. }
  120. /* Skip "nop" */
  121. vcpu_skip_insn(vcpu, 1);
  122. /* Test data access HW BP over DR[0-3] */
  123. for (i = 0; i < 4; i++) {
  124. memset(&debug, 0, sizeof(debug));
  125. debug.control = KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_HW_BP;
  126. debug.arch.debugreg[i] = CAST_TO_RIP(guest_value);
  127. debug.arch.debugreg[7] = 0x00000400 | (1UL << (2*i+1)) |
  128. (0x000d0000UL << (4*i));
  129. vcpu_guest_debug_set(vcpu, &debug);
  130. vcpu_run(vcpu);
  131. target_dr6 = 0xffff0ff0 | (1UL << i);
  132. TEST_ASSERT(run->exit_reason == KVM_EXIT_DEBUG &&
  133. run->debug.arch.exception == DB_VECTOR &&
  134. run->debug.arch.pc == CAST_TO_RIP(write_data) &&
  135. run->debug.arch.dr6 == target_dr6,
  136. "DATA_HW_BP (DR%d): exit %d exception %d rip 0x%llx "
  137. "(should be 0x%llx) dr6 0x%llx (should be 0x%llx)",
  138. i, run->exit_reason, run->debug.arch.exception,
  139. run->debug.arch.pc, CAST_TO_RIP(write_data),
  140. run->debug.arch.dr6, target_dr6);
  141. /* Rollback the 4-bytes "mov" */
  142. vcpu_skip_insn(vcpu, -7);
  143. }
  144. /* Skip the 4-bytes "mov" */
  145. vcpu_skip_insn(vcpu, 7);
  146. /* Test single step */
  147. target_rip = CAST_TO_RIP(ss_start);
  148. target_dr6 = 0xffff4ff0ULL;
  149. for (i = 0; i < ARRAY_SIZE(ss_size); i++) {
  150. target_rip += ss_size[i];
  151. memset(&debug, 0, sizeof(debug));
  152. debug.control = KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_SINGLESTEP |
  153. KVM_GUESTDBG_BLOCKIRQ;
  154. debug.arch.debugreg[7] = 0x00000400;
  155. vcpu_guest_debug_set(vcpu, &debug);
  156. vcpu_run(vcpu);
  157. TEST_ASSERT(run->exit_reason == KVM_EXIT_DEBUG &&
  158. run->debug.arch.exception == DB_VECTOR &&
  159. run->debug.arch.pc == target_rip &&
  160. run->debug.arch.dr6 == target_dr6,
  161. "SINGLE_STEP[%d]: exit %d exception %d rip 0x%llx "
  162. "(should be 0x%llx) dr6 0x%llx (should be 0x%llx)",
  163. i, run->exit_reason, run->debug.arch.exception,
  164. run->debug.arch.pc, target_rip, run->debug.arch.dr6,
  165. target_dr6);
  166. }
  167. /* Finally test global disable */
  168. memset(&debug, 0, sizeof(debug));
  169. debug.control = KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_HW_BP;
  170. debug.arch.debugreg[7] = 0x400 | DR7_GD;
  171. vcpu_guest_debug_set(vcpu, &debug);
  172. vcpu_run(vcpu);
  173. target_dr6 = 0xffff0ff0 | DR6_BD;
  174. TEST_ASSERT(run->exit_reason == KVM_EXIT_DEBUG &&
  175. run->debug.arch.exception == DB_VECTOR &&
  176. run->debug.arch.pc == CAST_TO_RIP(bd_start) &&
  177. run->debug.arch.dr6 == target_dr6,
  178. "DR7.GD: exit %d exception %d rip 0x%llx "
  179. "(should be 0x%llx) dr6 0x%llx (should be 0x%llx)",
  180. run->exit_reason, run->debug.arch.exception,
  181. run->debug.arch.pc, target_rip, run->debug.arch.dr6,
  182. target_dr6);
  183. /* Disable all debug controls, run to the end */
  184. memset(&debug, 0, sizeof(debug));
  185. vcpu_guest_debug_set(vcpu, &debug);
  186. vcpu_run(vcpu);
  187. TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
  188. cmd = get_ucall(vcpu, &uc);
  189. TEST_ASSERT(cmd == UCALL_DONE, "UCALL_DONE");
  190. kvm_vm_free(vm);
  191. return 0;
  192. }