Merge branch 'misc.namei' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
[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 #include "apic.h"
12
13 #define VCPU_ID 0
14
15 #define DR6_BD          (1 << 13)
16 #define DR7_GD          (1 << 13)
17
18 #define IRQ_VECTOR 0xAA
19
20 /* For testing data access debug BP */
21 uint32_t guest_value;
22
23 extern unsigned char sw_bp, hw_bp, write_data, ss_start, bd_start;
24
25 static void guest_code(void)
26 {
27         /* Create a pending interrupt on current vCPU */
28         x2apic_enable();
29         x2apic_write_reg(APIC_ICR, APIC_DEST_SELF | APIC_INT_ASSERT |
30                          APIC_DM_FIXED | IRQ_VECTOR);
31
32         /*
33          * Software BP tests.
34          *
35          * NOTE: sw_bp need to be before the cmd here, because int3 is an
36          * exception rather than a normal trap for KVM_SET_GUEST_DEBUG (we
37          * capture it using the vcpu exception bitmap).
38          */
39         asm volatile("sw_bp: int3");
40
41         /* Hardware instruction BP test */
42         asm volatile("hw_bp: nop");
43
44         /* Hardware data BP test */
45         asm volatile("mov $1234,%%rax;\n\t"
46                      "mov %%rax,%0;\n\t write_data:"
47                      : "=m" (guest_value) : : "rax");
48
49         /*
50          * Single step test, covers 2 basic instructions and 2 emulated
51          *
52          * Enable interrupts during the single stepping to see that
53          * pending interrupt we raised is not handled due to KVM_GUESTDBG_BLOCKIRQ
54          */
55         asm volatile("ss_start: "
56                      "sti\n\t"
57                      "xor %%eax,%%eax\n\t"
58                      "cpuid\n\t"
59                      "movl $0x1a0,%%ecx\n\t"
60                      "rdmsr\n\t"
61                      "cli\n\t"
62                      : : : "eax", "ebx", "ecx", "edx");
63
64         /* DR6.BD test */
65         asm volatile("bd_start: mov %%dr0, %%rax" : : : "rax");
66         GUEST_DONE();
67 }
68
69 #define  CLEAR_DEBUG()  memset(&debug, 0, sizeof(debug))
70 #define  APPLY_DEBUG()  vcpu_set_guest_debug(vm, VCPU_ID, &debug)
71 #define  CAST_TO_RIP(v)  ((unsigned long long)&(v))
72 #define  SET_RIP(v)  do {                               \
73                 vcpu_regs_get(vm, VCPU_ID, &regs);      \
74                 regs.rip = (v);                         \
75                 vcpu_regs_set(vm, VCPU_ID, &regs);      \
76         } while (0)
77 #define  MOVE_RIP(v)  SET_RIP(regs.rip + (v));
78
79 int main(void)
80 {
81         struct kvm_guest_debug debug;
82         unsigned long long target_dr6, target_rip;
83         struct kvm_regs regs;
84         struct kvm_run *run;
85         struct kvm_vm *vm;
86         struct ucall uc;
87         uint64_t cmd;
88         int i;
89         /* Instruction lengths starting at ss_start */
90         int ss_size[6] = {
91                 1,              /* sti*/
92                 2,              /* xor */
93                 2,              /* cpuid */
94                 5,              /* mov */
95                 2,              /* rdmsr */
96                 1,              /* cli */
97         };
98
99         if (!kvm_check_cap(KVM_CAP_SET_GUEST_DEBUG)) {
100                 print_skip("KVM_CAP_SET_GUEST_DEBUG not supported");
101                 return 0;
102         }
103
104         vm = vm_create_default(VCPU_ID, 0, guest_code);
105         run = vcpu_state(vm, VCPU_ID);
106
107         /* Test software BPs - int3 */
108         CLEAR_DEBUG();
109         debug.control = KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_SW_BP;
110         APPLY_DEBUG();
111         vcpu_run(vm, VCPU_ID);
112         TEST_ASSERT(run->exit_reason == KVM_EXIT_DEBUG &&
113                     run->debug.arch.exception == BP_VECTOR &&
114                     run->debug.arch.pc == CAST_TO_RIP(sw_bp),
115                     "INT3: exit %d exception %d rip 0x%llx (should be 0x%llx)",
116                     run->exit_reason, run->debug.arch.exception,
117                     run->debug.arch.pc, CAST_TO_RIP(sw_bp));
118         MOVE_RIP(1);
119
120         /* Test instruction HW BP over DR[0-3] */
121         for (i = 0; i < 4; i++) {
122                 CLEAR_DEBUG();
123                 debug.control = KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_HW_BP;
124                 debug.arch.debugreg[i] = CAST_TO_RIP(hw_bp);
125                 debug.arch.debugreg[7] = 0x400 | (1UL << (2*i+1));
126                 APPLY_DEBUG();
127                 vcpu_run(vm, VCPU_ID);
128                 target_dr6 = 0xffff0ff0 | (1UL << i);
129                 TEST_ASSERT(run->exit_reason == KVM_EXIT_DEBUG &&
130                             run->debug.arch.exception == DB_VECTOR &&
131                             run->debug.arch.pc == CAST_TO_RIP(hw_bp) &&
132                             run->debug.arch.dr6 == target_dr6,
133                             "INS_HW_BP (DR%d): exit %d exception %d rip 0x%llx "
134                             "(should be 0x%llx) dr6 0x%llx (should be 0x%llx)",
135                             i, run->exit_reason, run->debug.arch.exception,
136                             run->debug.arch.pc, CAST_TO_RIP(hw_bp),
137                             run->debug.arch.dr6, target_dr6);
138         }
139         /* Skip "nop" */
140         MOVE_RIP(1);
141
142         /* Test data access HW BP over DR[0-3] */
143         for (i = 0; i < 4; i++) {
144                 CLEAR_DEBUG();
145                 debug.control = KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_HW_BP;
146                 debug.arch.debugreg[i] = CAST_TO_RIP(guest_value);
147                 debug.arch.debugreg[7] = 0x00000400 | (1UL << (2*i+1)) |
148                     (0x000d0000UL << (4*i));
149                 APPLY_DEBUG();
150                 vcpu_run(vm, VCPU_ID);
151                 target_dr6 = 0xffff0ff0 | (1UL << i);
152                 TEST_ASSERT(run->exit_reason == KVM_EXIT_DEBUG &&
153                             run->debug.arch.exception == DB_VECTOR &&
154                             run->debug.arch.pc == CAST_TO_RIP(write_data) &&
155                             run->debug.arch.dr6 == target_dr6,
156                             "DATA_HW_BP (DR%d): exit %d exception %d rip 0x%llx "
157                             "(should be 0x%llx) dr6 0x%llx (should be 0x%llx)",
158                             i, run->exit_reason, run->debug.arch.exception,
159                             run->debug.arch.pc, CAST_TO_RIP(write_data),
160                             run->debug.arch.dr6, target_dr6);
161                 /* Rollback the 4-bytes "mov" */
162                 MOVE_RIP(-7);
163         }
164         /* Skip the 4-bytes "mov" */
165         MOVE_RIP(7);
166
167         /* Test single step */
168         target_rip = CAST_TO_RIP(ss_start);
169         target_dr6 = 0xffff4ff0ULL;
170         vcpu_regs_get(vm, VCPU_ID, &regs);
171         for (i = 0; i < (sizeof(ss_size) / sizeof(ss_size[0])); i++) {
172                 target_rip += ss_size[i];
173                 CLEAR_DEBUG();
174                 debug.control = KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_SINGLESTEP |
175                                 KVM_GUESTDBG_BLOCKIRQ;
176                 debug.arch.debugreg[7] = 0x00000400;
177                 APPLY_DEBUG();
178                 vcpu_run(vm, VCPU_ID);
179                 TEST_ASSERT(run->exit_reason == KVM_EXIT_DEBUG &&
180                             run->debug.arch.exception == DB_VECTOR &&
181                             run->debug.arch.pc == target_rip &&
182                             run->debug.arch.dr6 == target_dr6,
183                             "SINGLE_STEP[%d]: exit %d exception %d rip 0x%llx "
184                             "(should be 0x%llx) dr6 0x%llx (should be 0x%llx)",
185                             i, run->exit_reason, run->debug.arch.exception,
186                             run->debug.arch.pc, target_rip, run->debug.arch.dr6,
187                             target_dr6);
188         }
189
190         /* Finally test global disable */
191         CLEAR_DEBUG();
192         debug.control = KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_HW_BP;
193         debug.arch.debugreg[7] = 0x400 | DR7_GD;
194         APPLY_DEBUG();
195         vcpu_run(vm, VCPU_ID);
196         target_dr6 = 0xffff0ff0 | DR6_BD;
197         TEST_ASSERT(run->exit_reason == KVM_EXIT_DEBUG &&
198                     run->debug.arch.exception == DB_VECTOR &&
199                     run->debug.arch.pc == CAST_TO_RIP(bd_start) &&
200                     run->debug.arch.dr6 == target_dr6,
201                             "DR7.GD: exit %d exception %d rip 0x%llx "
202                             "(should be 0x%llx) dr6 0x%llx (should be 0x%llx)",
203                             run->exit_reason, run->debug.arch.exception,
204                             run->debug.arch.pc, target_rip, run->debug.arch.dr6,
205                             target_dr6);
206
207         /* Disable all debug controls, run to the end */
208         CLEAR_DEBUG();
209         APPLY_DEBUG();
210
211         vcpu_run(vm, VCPU_ID);
212         TEST_ASSERT(run->exit_reason == KVM_EXIT_IO, "KVM_EXIT_IO");
213         cmd = get_ucall(vm, VCPU_ID, &uc);
214         TEST_ASSERT(cmd == UCALL_DONE, "UCALL_DONE");
215
216         kvm_vm_free(vm);
217
218         return 0;
219 }