xref: /kvm-unit-tests/x86/debug.c (revision a299895b7abb54e7ba6bb4108f202acbb484ac65)
1 /*
2  * Test for x86 debugging facilities
3  *
4  * Copyright (c) Siemens AG, 2014
5  *
6  * Authors:
7  *  Jan Kiszka <jan.kiszka@siemens.com>
8  *
9  * This work is licensed under the terms of the GNU GPL, version 2.
10  */
11 
12 #include "libcflat.h"
13 #include "desc.h"
14 
15 static volatile unsigned long bp_addr;
16 static volatile unsigned long db_addr[10], dr6[10];
17 static volatile unsigned int n;
18 static volatile unsigned long value;
19 
20 static unsigned long get_dr6(void)
21 {
22 	unsigned long value;
23 
24 	asm volatile("mov %%dr6,%0" : "=r" (value));
25 	return value;
26 }
27 
28 static void set_dr0(void *value)
29 {
30 	asm volatile("mov %0,%%dr0" : : "r" (value));
31 }
32 
33 static void set_dr1(void *value)
34 {
35 	asm volatile("mov %0,%%dr1" : : "r" (value));
36 }
37 
38 static void set_dr6(unsigned long value)
39 {
40 	asm volatile("mov %0,%%dr6" : : "r" (value));
41 }
42 
43 static void set_dr7(unsigned long value)
44 {
45 	asm volatile("mov %0,%%dr7" : : "r" (value));
46 }
47 
48 static void handle_db(struct ex_regs *regs)
49 {
50 	db_addr[n] = regs->rip;
51 	dr6[n] = get_dr6();
52 
53 	if (dr6[n] & 0x1)
54 		regs->rflags |= (1 << 16);
55 
56 	if (++n >= 10) {
57 		regs->rflags &= ~(1 << 8);
58 		set_dr7(0x00000400);
59 	}
60 }
61 
62 extern unsigned char handle_db_save_rip;
63 asm("handle_db_save_rip:\n"
64    "stc\n"
65    "nop;nop;nop\n"
66    "rclq $1, n(%rip)\n"
67    "iretq\n");
68 
69 static void handle_bp(struct ex_regs *regs)
70 {
71 	bp_addr = regs->rip;
72 }
73 
74 int main(int ac, char **av)
75 {
76 	unsigned long start;
77 
78 	setup_idt();
79 	handle_exception(DB_VECTOR, handle_db);
80 	handle_exception(BP_VECTOR, handle_bp);
81 
82 	extern unsigned char sw_bp;
83 	asm volatile("int3; sw_bp:");
84 	report(bp_addr == (unsigned long)&sw_bp, "#BP");
85 
86 	n = 0;
87 	extern unsigned char hw_bp1;
88 	set_dr0(&hw_bp1);
89 	set_dr7(0x00000402);
90 	asm volatile("hw_bp1: nop");
91 	report(n == 1 &&
92 	       db_addr[0] == ((unsigned long)&hw_bp1) && dr6[0] == 0xffff0ff1,
93 	       "hw breakpoint (test that dr6.BS is not set)");
94 
95 	n = 0;
96 	extern unsigned char hw_bp2;
97 	set_dr0(&hw_bp2);
98 	set_dr6(0x00004002);
99 	asm volatile("hw_bp2: nop");
100 	report(n == 1 &&
101 	       db_addr[0] == ((unsigned long)&hw_bp2) && dr6[0] == 0xffff4ff1,
102 	       "hw breakpoint (test that dr6.BS is not cleared)");
103 
104 	n = 0;
105 	set_dr6(0);
106 	asm volatile(
107 		"pushf\n\t"
108 		"pop %%rax\n\t"
109 		"or $(1<<8),%%rax\n\t"
110 		"push %%rax\n\t"
111 		"lea (%%rip),%0\n\t"
112 		"popf\n\t"
113 		"and $~(1<<8),%%rax\n\t"
114 		"push %%rax\n\t"
115 		"popf\n\t"
116 		: "=r" (start) : : "rax");
117 	report(n == 3 &&
118 	       db_addr[0] == start + 1 + 6 && dr6[0] == 0xffff4ff0 &&
119 	       db_addr[1] == start + 1 + 6 + 1 && dr6[1] == 0xffff4ff0 &&
120 	       db_addr[2] == start + 1 + 6 + 1 + 1 && dr6[2] == 0xffff4ff0,
121 	       "single step");
122 
123 	/*
124 	 * cpuid and rdmsr (among others) trigger VM exits and are then
125 	 * emulated. Test that single stepping works on emulated instructions.
126 	 */
127 	n = 0;
128 	set_dr6(0);
129 	asm volatile(
130 		"pushf\n\t"
131 		"pop %%rax\n\t"
132 		"or $(1<<8),%%rax\n\t"
133 		"push %%rax\n\t"
134 		"lea (%%rip),%0\n\t"
135 		"popf\n\t"
136 		"and $~(1<<8),%%rax\n\t"
137 		"push %%rax\n\t"
138 		"xor %%rax,%%rax\n\t"
139 		"cpuid\n\t"
140 		"movl $0x1a0,%%ecx\n\t"
141 		"rdmsr\n\t"
142 		"popf\n\t"
143 		: "=r" (start) : : "rax", "ebx", "ecx", "edx");
144 	report(n == 7 &&
145 	       db_addr[0] == start + 1 + 6 && dr6[0] == 0xffff4ff0 &&
146 	       db_addr[1] == start + 1 + 6 + 1 && dr6[1] == 0xffff4ff0 &&
147 	       db_addr[2] == start + 1 + 6 + 1 + 3 && dr6[2] == 0xffff4ff0 &&
148 	       db_addr[3] == start + 1 + 6 + 1 + 3 + 2 && dr6[3] == 0xffff4ff0 &&
149 	       db_addr[4] == start + 1 + 6 + 1 + 3 + 2 + 5 && dr6[4] == 0xffff4ff0 &&
150 	       db_addr[5] == start + 1 + 6 + 1 + 3 + 2 + 5 + 2 && dr6[5] == 0xffff4ff0 &&
151 	       db_addr[6] == start + 1 + 6 + 1 + 3 + 2 + 5 + 2 + 1 && dr6[6] == 0xffff4ff0,
152 	       "single step emulated instructions");
153 
154 	n = 0;
155 	set_dr1((void *)&value);
156 	set_dr7(0x00d0040a); // 4-byte write
157 
158 	extern unsigned char hw_wp1;
159 	asm volatile(
160 		"mov $42,%%rax\n\t"
161 		"mov %%rax,%0\n\t; hw_wp1:"
162 		: "=m" (value) : : "rax");
163 	report(n == 1 &&
164 	       db_addr[0] == ((unsigned long)&hw_wp1) && dr6[0] == 0xffff4ff2,
165 	       "hw watchpoint (test that dr6.BS is not cleared)");
166 
167 	n = 0;
168 	set_dr6(0);
169 
170 	extern unsigned char hw_wp2;
171 	asm volatile(
172 		"mov $42,%%rax\n\t"
173 		"mov %%rax,%0\n\t; hw_wp2:"
174 		: "=m" (value) : : "rax");
175 	report(n == 1 &&
176 	       db_addr[0] == ((unsigned long)&hw_wp2) && dr6[0] == 0xffff0ff2,
177 	       "hw watchpoint (test that dr6.BS is not set)");
178 
179 	n = 0;
180 	set_dr6(0);
181 	extern unsigned char sw_icebp;
182 	asm volatile(".byte 0xf1; sw_icebp:");
183 	report(n == 1 &&
184 	       db_addr[0] == (unsigned long)&sw_icebp && dr6[0] == 0xffff0ff0,
185 	       "icebp");
186 
187 	set_dr7(0x400);
188 	value = KERNEL_DS;
189 	set_dr7(0x00f0040a); // 4-byte read or write
190 
191 	/*
192 	 * Each invocation of the handler should shift n by 1 and set bit 0 to 1.
193 	 * We expect a single invocation, so n should become 3.  If the entry
194 	 * RIP is wrong, or if the handler is executed more than once, the value
195 	 * will not match.
196 	 */
197 	set_idt_entry(1, &handle_db_save_rip, 0);
198 
199 	n = 1;
200 	asm volatile(
201 		"clc\n\t"
202 		"mov %0,%%ss\n\t"
203 		".byte 0x2e, 0x2e, 0xf1"
204 		: "=m" (value) : : "rax");
205 	report(n == 3, "MOV SS + watchpoint + ICEBP");
206 
207 	/*
208 	 * Here the #DB handler is invoked twice, once as a software exception
209 	 * and once as a software interrupt.
210 	 */
211 	n = 1;
212 	asm volatile(
213 		"clc\n\t"
214 		"mov %0,%%ss\n\t"
215 		"int $1"
216 		: "=m" (value) : : "rax");
217 	report(n == 7, "MOV SS + watchpoint + int $1");
218 
219 	/*
220 	 * Here the #DB and #BP handlers are invoked once each.
221 	 */
222 	n = 1;
223 	bp_addr = 0;
224 	asm volatile(
225 		"mov %0,%%ss\n\t"
226 		".byte 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0xcc\n\t"
227 		"sw_bp2:"
228 		: "=m" (value) : : "rax");
229 	extern unsigned char sw_bp2;
230 	report(n == 3 && bp_addr == (unsigned long)&sw_bp2,
231 	       "MOV SS + watchpoint + INT3");
232 	return report_summary();
233 }
234