Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

KVM: selftests: Add KVM_SET_GUEST_DEBUG test

Covers fundamental tests for KVM_SET_GUEST_DEBUG. It is very close to the debug
test in kvm-unit-test, but doing it from outside the guest.

Signed-off-by: Peter Xu <peterx@redhat.com>
Message-Id: <20200505205000.188252-4-peterx@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>

authored by

Peter Xu and committed by
Paolo Bonzini
449aa906 d5d260c5

+192
+1
tools/testing/selftests/kvm/Makefile
··· 28 28 TEST_GEN_PROGS_x86_64 += x86_64/vmx_set_nested_state_test 29 29 TEST_GEN_PROGS_x86_64 += x86_64/vmx_tsc_adjust_test 30 30 TEST_GEN_PROGS_x86_64 += x86_64/xss_msr_test 31 + TEST_GEN_PROGS_x86_64 += x86_64/debug_regs 31 32 TEST_GEN_PROGS_x86_64 += clear_dirty_log_test 32 33 TEST_GEN_PROGS_x86_64 += demand_paging_test 33 34 TEST_GEN_PROGS_x86_64 += dirty_log_test
+2
tools/testing/selftests/kvm/include/kvm_util.h
··· 143 143 void vcpu_run(struct kvm_vm *vm, uint32_t vcpuid); 144 144 int _vcpu_run(struct kvm_vm *vm, uint32_t vcpuid); 145 145 void vcpu_run_complete_io(struct kvm_vm *vm, uint32_t vcpuid); 146 + void vcpu_set_guest_debug(struct kvm_vm *vm, uint32_t vcpuid, 147 + struct kvm_guest_debug *debug); 146 148 void vcpu_set_mp_state(struct kvm_vm *vm, uint32_t vcpuid, 147 149 struct kvm_mp_state *mp_state); 148 150 void vcpu_regs_get(struct kvm_vm *vm, uint32_t vcpuid, struct kvm_regs *regs);
+9
tools/testing/selftests/kvm/lib/kvm_util.c
··· 1201 1201 ret, errno); 1202 1202 } 1203 1203 1204 + void vcpu_set_guest_debug(struct kvm_vm *vm, uint32_t vcpuid, 1205 + struct kvm_guest_debug *debug) 1206 + { 1207 + struct vcpu *vcpu = vcpu_find(vm, vcpuid); 1208 + int ret = ioctl(vcpu->fd, KVM_SET_GUEST_DEBUG, debug); 1209 + 1210 + TEST_ASSERT(ret == 0, "KVM_SET_GUEST_DEBUG failed: %d", ret); 1211 + } 1212 + 1204 1213 /* 1205 1214 * VM VCPU Set MP State 1206 1215 *
+180
tools/testing/selftests/kvm/x86_64/debug_regs.c
··· 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 + 12 + #define VCPU_ID 0 13 + 14 + /* For testing data access debug BP */ 15 + uint32_t guest_value; 16 + 17 + extern unsigned char sw_bp, hw_bp, write_data, ss_start; 18 + 19 + static void guest_code(void) 20 + { 21 + /* 22 + * Software BP tests. 23 + * 24 + * NOTE: sw_bp need to be before the cmd here, because int3 is an 25 + * exception rather than a normal trap for KVM_SET_GUEST_DEBUG (we 26 + * capture it using the vcpu exception bitmap). 27 + */ 28 + asm volatile("sw_bp: int3"); 29 + 30 + /* Hardware instruction BP test */ 31 + asm volatile("hw_bp: nop"); 32 + 33 + /* Hardware data BP test */ 34 + asm volatile("mov $1234,%%rax;\n\t" 35 + "mov %%rax,%0;\n\t write_data:" 36 + : "=m" (guest_value) : : "rax"); 37 + 38 + /* Single step test, covers 2 basic instructions and 2 emulated */ 39 + asm volatile("ss_start: " 40 + "xor %%rax,%%rax\n\t" 41 + "cpuid\n\t" 42 + "movl $0x1a0,%%ecx\n\t" 43 + "rdmsr\n\t" 44 + : : : "rax", "ecx"); 45 + 46 + GUEST_DONE(); 47 + } 48 + 49 + #define CLEAR_DEBUG() memset(&debug, 0, sizeof(debug)) 50 + #define APPLY_DEBUG() vcpu_set_guest_debug(vm, VCPU_ID, &debug) 51 + #define CAST_TO_RIP(v) ((unsigned long long)&(v)) 52 + #define SET_RIP(v) do { \ 53 + vcpu_regs_get(vm, VCPU_ID, &regs); \ 54 + regs.rip = (v); \ 55 + vcpu_regs_set(vm, VCPU_ID, &regs); \ 56 + } while (0) 57 + #define MOVE_RIP(v) SET_RIP(regs.rip + (v)); 58 + 59 + int main(void) 60 + { 61 + struct kvm_guest_debug debug; 62 + unsigned long long target_dr6, target_rip; 63 + struct kvm_regs regs; 64 + struct kvm_run *run; 65 + struct kvm_vm *vm; 66 + struct ucall uc; 67 + uint64_t cmd; 68 + int i; 69 + /* Instruction lengths starting at ss_start */ 70 + int ss_size[4] = { 71 + 3, /* xor */ 72 + 2, /* cpuid */ 73 + 5, /* mov */ 74 + 2, /* rdmsr */ 75 + }; 76 + 77 + if (!kvm_check_cap(KVM_CAP_SET_GUEST_DEBUG)) { 78 + print_skip("KVM_CAP_SET_GUEST_DEBUG not supported"); 79 + return 0; 80 + } 81 + 82 + vm = vm_create_default(VCPU_ID, 0, guest_code); 83 + vcpu_set_cpuid(vm, VCPU_ID, kvm_get_supported_cpuid()); 84 + run = vcpu_state(vm, VCPU_ID); 85 + 86 + /* Test software BPs - int3 */ 87 + CLEAR_DEBUG(); 88 + debug.control = KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_SW_BP; 89 + APPLY_DEBUG(); 90 + vcpu_run(vm, VCPU_ID); 91 + TEST_ASSERT(run->exit_reason == KVM_EXIT_DEBUG && 92 + run->debug.arch.exception == BP_VECTOR && 93 + run->debug.arch.pc == CAST_TO_RIP(sw_bp), 94 + "INT3: exit %d exception %d rip 0x%llx (should be 0x%llx)", 95 + run->exit_reason, run->debug.arch.exception, 96 + run->debug.arch.pc, CAST_TO_RIP(sw_bp)); 97 + MOVE_RIP(1); 98 + 99 + /* Test instruction HW BP over DR[0-3] */ 100 + for (i = 0; i < 4; i++) { 101 + CLEAR_DEBUG(); 102 + debug.control = KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_HW_BP; 103 + debug.arch.debugreg[i] = CAST_TO_RIP(hw_bp); 104 + debug.arch.debugreg[7] = 0x400 | (1UL << (2*i+1)); 105 + APPLY_DEBUG(); 106 + vcpu_run(vm, VCPU_ID); 107 + target_dr6 = 0xffff0ff0 | (1UL << i); 108 + TEST_ASSERT(run->exit_reason == KVM_EXIT_DEBUG && 109 + run->debug.arch.exception == DB_VECTOR && 110 + run->debug.arch.pc == CAST_TO_RIP(hw_bp) && 111 + run->debug.arch.dr6 == target_dr6, 112 + "INS_HW_BP (DR%d): exit %d exception %d rip 0x%llx " 113 + "(should be 0x%llx) dr6 0x%llx (should be 0x%llx)", 114 + i, run->exit_reason, run->debug.arch.exception, 115 + run->debug.arch.pc, CAST_TO_RIP(hw_bp), 116 + run->debug.arch.dr6, target_dr6); 117 + } 118 + /* Skip "nop" */ 119 + MOVE_RIP(1); 120 + 121 + /* Test data access HW BP over DR[0-3] */ 122 + for (i = 0; i < 4; i++) { 123 + CLEAR_DEBUG(); 124 + debug.control = KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_HW_BP; 125 + debug.arch.debugreg[i] = CAST_TO_RIP(guest_value); 126 + debug.arch.debugreg[7] = 0x00000400 | (1UL << (2*i+1)) | 127 + (0x000d0000UL << (4*i)); 128 + APPLY_DEBUG(); 129 + vcpu_run(vm, VCPU_ID); 130 + target_dr6 = 0xffff0ff0 | (1UL << i); 131 + TEST_ASSERT(run->exit_reason == KVM_EXIT_DEBUG && 132 + run->debug.arch.exception == DB_VECTOR && 133 + run->debug.arch.pc == CAST_TO_RIP(write_data) && 134 + run->debug.arch.dr6 == target_dr6, 135 + "DATA_HW_BP (DR%d): exit %d exception %d rip 0x%llx " 136 + "(should be 0x%llx) dr6 0x%llx (should be 0x%llx)", 137 + i, run->exit_reason, run->debug.arch.exception, 138 + run->debug.arch.pc, CAST_TO_RIP(write_data), 139 + run->debug.arch.dr6, target_dr6); 140 + /* Rollback the 4-bytes "mov" */ 141 + MOVE_RIP(-7); 142 + } 143 + /* Skip the 4-bytes "mov" */ 144 + MOVE_RIP(7); 145 + 146 + /* Test single step */ 147 + target_rip = CAST_TO_RIP(ss_start); 148 + target_dr6 = 0xffff4ff0ULL; 149 + vcpu_regs_get(vm, VCPU_ID, &regs); 150 + for (i = 0; i < (sizeof(ss_size) / sizeof(ss_size[0])); i++) { 151 + target_rip += ss_size[i]; 152 + CLEAR_DEBUG(); 153 + debug.control = KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_SINGLESTEP; 154 + debug.arch.debugreg[7] = 0x00000400; 155 + APPLY_DEBUG(); 156 + vcpu_run(vm, VCPU_ID); 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 + 168 + /* Disable all debug controls, run to the end */ 169 + CLEAR_DEBUG(); 170 + APPLY_DEBUG(); 171 + 172 + vcpu_run(vm, VCPU_ID); 173 + TEST_ASSERT(run->exit_reason == KVM_EXIT_IO, "KVM_EXIT_IO"); 174 + cmd = get_ucall(vm, VCPU_ID, &uc); 175 + TEST_ASSERT(cmd == UCALL_DONE, "UCALL_DONE"); 176 + 177 + kvm_vm_free(vm); 178 + 179 + return 0; 180 + }