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 13 #define DR6_BD (1 << 13) 14 #define DR7_GD (1 << 13) 15 16 #define IRQ_VECTOR 0xAA 17 18 #define CAST_TO_RIP(v) ((unsigned long long)&(v)) 19 20 /* For testing data access debug BP */ 21 u32 guest_value; 22 23 extern unsigned char sw_bp, hw_bp, write_data, ss_start, bd_start; 24 extern unsigned char fep_bd_start, fep_sti_start, fep_sti_end; 25 26 static int irqs_received; 27 28 static void guest_db_handler(struct ex_regs *regs) 29 { 30 static int count; 31 unsigned long target_rips[2] = { 32 CAST_TO_RIP(fep_sti_start), 33 CAST_TO_RIP(fep_sti_end), 34 }; 35 36 __GUEST_ASSERT(regs->rip == target_rips[count], 37 "STI[%u]: unexpected rip 0x%lx (should be 0x%lx)", 38 count, regs->rip, target_rips[count]); 39 regs->rflags &= ~X86_EFLAGS_TF; 40 count++; 41 } 42 43 static void guest_irq_handler(struct ex_regs *regs) 44 { 45 /* 46 * The pending IRQ should finally be take when KVM_GUESTDBG_BLOCKIRQ is 47 * cleared and IRQs are enabled. Note, the IRQ is expected to arrive 48 * on the instruction immediately after STI, even though its in an STI 49 * shadow. Because the next instruction has a coincident #DB, and #DBs 50 * are not subject to STI-blocking, the #DB will push RFLAGS.IF=1 on 51 * the stack, and the eventual IRET will unmask IRQs and obliterate the 52 * STI shadow in the process. 53 */ 54 unsigned long target_rip = CAST_TO_RIP(fep_sti_start); 55 56 __GUEST_ASSERT(regs->rip == target_rip, 57 "IRQ: unexpected rip 0x%lx (should be 0x%lx)", 58 regs->rip, target_rip); 59 60 irqs_received++; 61 x2apic_write_reg(APIC_EOI, 0); 62 } 63 64 static void guest_code(void) 65 { 66 /* Create a pending interrupt on current vCPU */ 67 x2apic_enable(); 68 x2apic_write_reg(APIC_ICR, APIC_DEST_SELF | APIC_INT_ASSERT | 69 APIC_DM_FIXED | IRQ_VECTOR); 70 71 /* 72 * Software BP tests. 73 * 74 * NOTE: sw_bp need to be before the cmd here, because int3 is an 75 * exception rather than a normal trap for KVM_SET_GUEST_DEBUG (we 76 * capture it using the vcpu exception bitmap). 77 */ 78 asm volatile("sw_bp: int3"); 79 80 /* Hardware instruction BP test */ 81 asm volatile("hw_bp: nop"); 82 83 /* Hardware data BP test */ 84 asm volatile("mov $1234,%%rax;\n\t" 85 "mov %%rax,%0;\n\t write_data:" 86 : "=m" (guest_value) : : "rax"); 87 88 /* 89 * Single step test, covers 2 basic instructions and 2 emulated 90 * 91 * Enable interrupts during the single stepping to see that pending 92 * interrupt we raised is not handled due to KVM_GUESTDBG_BLOCKIRQ. 93 * 94 * Write MSR_IA32_TSC_DEADLINE to verify that KVM's fastpath handler 95 * exits to userspace due to single-step being enabled. 96 */ 97 asm volatile("ss_start: " 98 "sti\n\t" 99 "xor %%eax,%%eax\n\t" 100 "cpuid\n\t" 101 "movl $" __stringify(MSR_IA32_TSC_DEADLINE) ", %%ecx\n\t" 102 "wrmsr\n\t" 103 "cli\n\t" 104 : : : "eax", "ebx", "ecx", "edx"); 105 106 /* DR6.BD test */ 107 asm volatile("bd_start: mov %%dr0, %%rax" : : : "rax"); 108 109 /* 110 * Note, the IRET from the #DB that occurs in the below STI-shadow will 111 * unmask IRQs, i.e. the pending interrupt will be delivered after #DB 112 * handling, on the CLI! 113 */ 114 if (is_forced_emulation_enabled) { 115 asm volatile(KVM_FEP "fep_bd_start: mov %%dr0, %%rax" : : : "rax"); 116 117 /* pending debug exceptions for emulation */ 118 asm volatile("pushf\n\t" 119 "orq $" __stringify(X86_EFLAGS_TF) ", (%rsp)\n\t" 120 "popf\n\t" 121 "sti\n\t" 122 "fep_sti_start:" 123 "cli\n\t" 124 "pushf\n\t" 125 "orq $" __stringify(X86_EFLAGS_TF) ", (%rsp)\n\t" 126 "popf\n\t" 127 KVM_FEP "sti\n\t" 128 "fep_sti_end:" 129 "cli\n\t"); 130 GUEST_ASSERT(irqs_received == 1); 131 } 132 GUEST_DONE(); 133 } 134 135 static void vcpu_skip_insn(struct kvm_vcpu *vcpu, int insn_len) 136 { 137 struct kvm_regs regs; 138 139 vcpu_regs_get(vcpu, ®s); 140 regs.rip += insn_len; 141 vcpu_regs_set(vcpu, ®s); 142 } 143 144 int main(void) 145 { 146 struct kvm_guest_debug debug; 147 unsigned long long target_dr6, target_rip; 148 struct kvm_vcpu *vcpu; 149 struct kvm_run *run; 150 struct kvm_vm *vm; 151 struct ucall uc; 152 u64 cmd; 153 int i; 154 /* Instruction lengths starting at ss_start */ 155 int ss_size[6] = { 156 1, /* sti*/ 157 2, /* xor */ 158 2, /* cpuid */ 159 5, /* mov */ 160 2, /* rdmsr */ 161 1, /* cli */ 162 }; 163 164 TEST_REQUIRE(kvm_has_cap(KVM_CAP_SET_GUEST_DEBUG)); 165 166 vm = vm_create_with_one_vcpu(&vcpu, guest_code); 167 run = vcpu->run; 168 169 /* Test software BPs - int3 */ 170 memset(&debug, 0, sizeof(debug)); 171 debug.control = KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_SW_BP; 172 vcpu_guest_debug_set(vcpu, &debug); 173 vcpu_run(vcpu); 174 TEST_ASSERT(run->exit_reason == KVM_EXIT_DEBUG && 175 run->debug.arch.exception == BP_VECTOR && 176 run->debug.arch.pc == CAST_TO_RIP(sw_bp), 177 "INT3: exit %d exception %d rip 0x%llx (should be 0x%llx)", 178 run->exit_reason, run->debug.arch.exception, 179 run->debug.arch.pc, CAST_TO_RIP(sw_bp)); 180 vcpu_skip_insn(vcpu, 1); 181 182 /* Test instruction HW BP over DR[0-3] */ 183 for (i = 0; i < 4; i++) { 184 memset(&debug, 0, sizeof(debug)); 185 debug.control = KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_HW_BP; 186 debug.arch.debugreg[i] = CAST_TO_RIP(hw_bp); 187 debug.arch.debugreg[7] = 0x400 | (1UL << (2*i+1)); 188 vcpu_guest_debug_set(vcpu, &debug); 189 vcpu_run(vcpu); 190 target_dr6 = 0xffff0ff0 | (1UL << i); 191 TEST_ASSERT(run->exit_reason == KVM_EXIT_DEBUG && 192 run->debug.arch.exception == DB_VECTOR && 193 run->debug.arch.pc == CAST_TO_RIP(hw_bp) && 194 run->debug.arch.dr6 == target_dr6, 195 "INS_HW_BP (DR%d): exit %d exception %d rip 0x%llx " 196 "(should be 0x%llx) dr6 0x%llx (should be 0x%llx)", 197 i, run->exit_reason, run->debug.arch.exception, 198 run->debug.arch.pc, CAST_TO_RIP(hw_bp), 199 run->debug.arch.dr6, target_dr6); 200 } 201 /* Skip "nop" */ 202 vcpu_skip_insn(vcpu, 1); 203 204 /* Test data access HW BP over DR[0-3] */ 205 for (i = 0; i < 4; i++) { 206 memset(&debug, 0, sizeof(debug)); 207 debug.control = KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_HW_BP; 208 debug.arch.debugreg[i] = CAST_TO_RIP(guest_value); 209 debug.arch.debugreg[7] = 0x00000400 | (1UL << (2*i+1)) | 210 (0x000d0000UL << (4*i)); 211 vcpu_guest_debug_set(vcpu, &debug); 212 vcpu_run(vcpu); 213 target_dr6 = 0xffff0ff0 | (1UL << i); 214 TEST_ASSERT(run->exit_reason == KVM_EXIT_DEBUG && 215 run->debug.arch.exception == DB_VECTOR && 216 run->debug.arch.pc == CAST_TO_RIP(write_data) && 217 run->debug.arch.dr6 == target_dr6, 218 "DATA_HW_BP (DR%d): exit %d exception %d rip 0x%llx " 219 "(should be 0x%llx) dr6 0x%llx (should be 0x%llx)", 220 i, run->exit_reason, run->debug.arch.exception, 221 run->debug.arch.pc, CAST_TO_RIP(write_data), 222 run->debug.arch.dr6, target_dr6); 223 /* Rollback the 4-bytes "mov" */ 224 vcpu_skip_insn(vcpu, -7); 225 } 226 /* Skip the 4-bytes "mov" */ 227 vcpu_skip_insn(vcpu, 7); 228 229 /* Test single step */ 230 target_rip = CAST_TO_RIP(ss_start); 231 target_dr6 = 0xffff4ff0ULL; 232 for (i = 0; i < ARRAY_SIZE(ss_size); i++) { 233 target_rip += ss_size[i]; 234 memset(&debug, 0, sizeof(debug)); 235 debug.control = KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_SINGLESTEP | 236 KVM_GUESTDBG_BLOCKIRQ; 237 debug.arch.debugreg[7] = 0x00000400; 238 vcpu_guest_debug_set(vcpu, &debug); 239 vcpu_run(vcpu); 240 TEST_ASSERT(run->exit_reason == KVM_EXIT_DEBUG && 241 run->debug.arch.exception == DB_VECTOR && 242 run->debug.arch.pc == target_rip && 243 run->debug.arch.dr6 == target_dr6, 244 "SINGLE_STEP[%d]: exit %d exception %d rip 0x%llx " 245 "(should be 0x%llx) dr6 0x%llx (should be 0x%llx)", 246 i, run->exit_reason, run->debug.arch.exception, 247 run->debug.arch.pc, target_rip, run->debug.arch.dr6, 248 target_dr6); 249 } 250 251 /* test global disable */ 252 memset(&debug, 0, sizeof(debug)); 253 debug.control = KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_HW_BP; 254 debug.arch.debugreg[7] = 0x400 | DR7_GD; 255 vcpu_guest_debug_set(vcpu, &debug); 256 vcpu_run(vcpu); 257 target_dr6 = 0xffff0ff0 | DR6_BD; 258 TEST_ASSERT(run->exit_reason == KVM_EXIT_DEBUG && 259 run->debug.arch.exception == DB_VECTOR && 260 run->debug.arch.pc == CAST_TO_RIP(bd_start) && 261 run->debug.arch.dr6 == target_dr6, 262 "DR7.GD: exit %d exception %d rip 0x%llx " 263 "(should be 0x%llx) dr6 0x%llx (should be 0x%llx)", 264 run->exit_reason, run->debug.arch.exception, 265 run->debug.arch.pc, target_rip, run->debug.arch.dr6, 266 target_dr6); 267 268 /* test global disable in emulation */ 269 if (is_forced_emulation_enabled) { 270 /* Skip the 3-bytes "mov dr0" */ 271 vcpu_skip_insn(vcpu, 3); 272 vcpu_run(vcpu); 273 TEST_ASSERT(run->exit_reason == KVM_EXIT_DEBUG && 274 run->debug.arch.exception == DB_VECTOR && 275 run->debug.arch.pc == CAST_TO_RIP(fep_bd_start) && 276 run->debug.arch.dr6 == target_dr6, 277 "DR7.GD: exit %d exception %d rip 0x%llx " 278 "(should be 0x%llx) dr6 0x%llx (should be 0x%llx)", 279 run->exit_reason, run->debug.arch.exception, 280 run->debug.arch.pc, CAST_TO_RIP(fep_bd_start), 281 run->debug.arch.dr6, target_dr6); 282 } 283 284 /* Disable all debug controls, run to the end */ 285 memset(&debug, 0, sizeof(debug)); 286 vcpu_guest_debug_set(vcpu, &debug); 287 288 vm_install_exception_handler(vm, DB_VECTOR, guest_db_handler); 289 vm_install_exception_handler(vm, IRQ_VECTOR, guest_irq_handler); 290 291 vcpu_run(vcpu); 292 TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO); 293 cmd = get_ucall(vcpu, &uc); 294 TEST_ASSERT(cmd == UCALL_DONE, "UCALL_DONE"); 295 296 kvm_vm_free(vm); 297 298 return 0; 299 } 300