ARM: s3c64xx: bring back notes from removed debug-macro.S
[linux-2.6-microblaze.git] / 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 #define DR6_BD          (1 << 13)
15 #define DR7_GD          (1 << 13)
16
17 /* For testing data access debug BP */
18 uint32_t guest_value;
19
20 extern unsigned char sw_bp, hw_bp, write_data, ss_start, bd_start;
21
22 static void guest_code(void)
23 {
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
33         /* Hardware instruction BP test */
34         asm volatile("hw_bp: nop");
35
36         /* Hardware data BP test */
37         asm volatile("mov $1234,%%rax;\n\t"
38                      "mov %%rax,%0;\n\t write_data:"
39                      : "=m" (guest_value) : : "rax");
40
41         /* Single step test, covers 2 basic instructions and 2 emulated */
42         asm volatile("ss_start: "
43                      "xor %%rax,%%rax\n\t"
44                      "cpuid\n\t"
45                      "movl $0x1a0,%%ecx\n\t"
46                      "rdmsr\n\t"
47                      : : : "rax", "ecx");
48
49         /* DR6.BD test */
50         asm volatile("bd_start: mov %%dr0, %%rax" : : : "rax");
51         GUEST_DONE();
52 }
53
54 #define  CLEAR_DEBUG()  memset(&debug, 0, sizeof(debug))
55 #define  APPLY_DEBUG()  vcpu_set_guest_debug(vm, VCPU_ID, &debug)
56 #define  CAST_TO_RIP(v)  ((unsigned long long)&(v))
57 #define  SET_RIP(v)  do {                               \
58                 vcpu_regs_get(vm, VCPU_ID, &regs);      \
59                 regs.rip = (v);                         \
60                 vcpu_regs_set(vm, VCPU_ID, &regs);      \
61         } while (0)
62 #define  MOVE_RIP(v)  SET_RIP(regs.rip + (v));
63
64 int main(void)
65 {
66         struct kvm_guest_debug debug;
67         unsigned long long target_dr6, target_rip;
68         struct kvm_regs regs;
69         struct kvm_run *run;
70         struct kvm_vm *vm;
71         struct ucall uc;
72         uint64_t cmd;
73         int i;
74         /* Instruction lengths starting at ss_start */
75         int ss_size[4] = {
76                 3,              /* xor */
77                 2,              /* cpuid */
78                 5,              /* mov */
79                 2,              /* rdmsr */
80         };
81
82         if (!kvm_check_cap(KVM_CAP_SET_GUEST_DEBUG)) {
83                 print_skip("KVM_CAP_SET_GUEST_DEBUG not supported");
84                 return 0;
85         }
86
87         vm = vm_create_default(VCPU_ID, 0, guest_code);
88         vcpu_set_cpuid(vm, VCPU_ID, kvm_get_supported_cpuid());
89         run = vcpu_state(vm, VCPU_ID);
90
91         /* Test software BPs - int3 */
92         CLEAR_DEBUG();
93         debug.control = KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_SW_BP;
94         APPLY_DEBUG();
95         vcpu_run(vm, VCPU_ID);
96         TEST_ASSERT(run->exit_reason == KVM_EXIT_DEBUG &&
97                     run->debug.arch.exception == BP_VECTOR &&
98                     run->debug.arch.pc == CAST_TO_RIP(sw_bp),
99                     "INT3: exit %d exception %d rip 0x%llx (should be 0x%llx)",
100                     run->exit_reason, run->debug.arch.exception,
101                     run->debug.arch.pc, CAST_TO_RIP(sw_bp));
102         MOVE_RIP(1);
103
104         /* Test instruction HW BP over DR[0-3] */
105         for (i = 0; i < 4; i++) {
106                 CLEAR_DEBUG();
107                 debug.control = KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_HW_BP;
108                 debug.arch.debugreg[i] = CAST_TO_RIP(hw_bp);
109                 debug.arch.debugreg[7] = 0x400 | (1UL << (2*i+1));
110                 APPLY_DEBUG();
111                 vcpu_run(vm, VCPU_ID);
112                 target_dr6 = 0xffff0ff0 | (1UL << i);
113                 TEST_ASSERT(run->exit_reason == KVM_EXIT_DEBUG &&
114                             run->debug.arch.exception == DB_VECTOR &&
115                             run->debug.arch.pc == CAST_TO_RIP(hw_bp) &&
116                             run->debug.arch.dr6 == target_dr6,
117                             "INS_HW_BP (DR%d): exit %d exception %d rip 0x%llx "
118                             "(should be 0x%llx) dr6 0x%llx (should be 0x%llx)",
119                             i, run->exit_reason, run->debug.arch.exception,
120                             run->debug.arch.pc, CAST_TO_RIP(hw_bp),
121                             run->debug.arch.dr6, target_dr6);
122         }
123         /* Skip "nop" */
124         MOVE_RIP(1);
125
126         /* Test data access HW BP over DR[0-3] */
127         for (i = 0; i < 4; i++) {
128                 CLEAR_DEBUG();
129                 debug.control = KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_HW_BP;
130                 debug.arch.debugreg[i] = CAST_TO_RIP(guest_value);
131                 debug.arch.debugreg[7] = 0x00000400 | (1UL << (2*i+1)) |
132                     (0x000d0000UL << (4*i));
133                 APPLY_DEBUG();
134                 vcpu_run(vm, VCPU_ID);
135                 target_dr6 = 0xffff0ff0 | (1UL << i);
136                 TEST_ASSERT(run->exit_reason == KVM_EXIT_DEBUG &&
137                             run->debug.arch.exception == DB_VECTOR &&
138                             run->debug.arch.pc == CAST_TO_RIP(write_data) &&
139                             run->debug.arch.dr6 == target_dr6,
140                             "DATA_HW_BP (DR%d): exit %d exception %d rip 0x%llx "
141                             "(should be 0x%llx) dr6 0x%llx (should be 0x%llx)",
142                             i, run->exit_reason, run->debug.arch.exception,
143                             run->debug.arch.pc, CAST_TO_RIP(write_data),
144                             run->debug.arch.dr6, target_dr6);
145                 /* Rollback the 4-bytes "mov" */
146                 MOVE_RIP(-7);
147         }
148         /* Skip the 4-bytes "mov" */
149         MOVE_RIP(7);
150
151         /* Test single step */
152         target_rip = CAST_TO_RIP(ss_start);
153         target_dr6 = 0xffff4ff0ULL;
154         vcpu_regs_get(vm, VCPU_ID, &regs);
155         for (i = 0; i < (sizeof(ss_size) / sizeof(ss_size[0])); i++) {
156                 target_rip += ss_size[i];
157                 CLEAR_DEBUG();
158                 debug.control = KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_SINGLESTEP;
159                 debug.arch.debugreg[7] = 0x00000400;
160                 APPLY_DEBUG();
161                 vcpu_run(vm, VCPU_ID);
162                 TEST_ASSERT(run->exit_reason == KVM_EXIT_DEBUG &&
163                             run->debug.arch.exception == DB_VECTOR &&
164                             run->debug.arch.pc == target_rip &&
165                             run->debug.arch.dr6 == target_dr6,
166                             "SINGLE_STEP[%d]: exit %d exception %d rip 0x%llx "
167                             "(should be 0x%llx) dr6 0x%llx (should be 0x%llx)",
168                             i, run->exit_reason, run->debug.arch.exception,
169                             run->debug.arch.pc, target_rip, run->debug.arch.dr6,
170                             target_dr6);
171         }
172
173         /* Finally test global disable */
174         CLEAR_DEBUG();
175         debug.control = KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_HW_BP;
176         debug.arch.debugreg[7] = 0x400 | DR7_GD;
177         APPLY_DEBUG();
178         vcpu_run(vm, VCPU_ID);
179         target_dr6 = 0xffff0ff0 | DR6_BD;
180         TEST_ASSERT(run->exit_reason == KVM_EXIT_DEBUG &&
181                     run->debug.arch.exception == DB_VECTOR &&
182                     run->debug.arch.pc == CAST_TO_RIP(bd_start) &&
183                     run->debug.arch.dr6 == target_dr6,
184                             "DR7.GD: exit %d exception %d rip 0x%llx "
185                             "(should be 0x%llx) dr6 0x%llx (should be 0x%llx)",
186                             run->exit_reason, run->debug.arch.exception,
187                             run->debug.arch.pc, target_rip, run->debug.arch.dr6,
188                             target_dr6);
189
190         /* Disable all debug controls, run to the end */
191         CLEAR_DEBUG();
192         APPLY_DEBUG();
193
194         vcpu_run(vm, VCPU_ID);
195         TEST_ASSERT(run->exit_reason == KVM_EXIT_IO, "KVM_EXIT_IO");
196         cmd = get_ucall(vm, VCPU_ID, &uc);
197         TEST_ASSERT(cmd == UCALL_DONE, "UCALL_DONE");
198
199         kvm_vm_free(vm);
200
201         return 0;
202 }