xref: /kvm-unit-tests/x86/debug.c (revision 0a982d78f62a1ff77ef05cd053b00712d77c8508)
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[10], dr6[10];
16 static volatile unsigned int n;
17 static volatile unsigned long value;
18 
19 static unsigned long get_dr6(void)
20 {
21 	unsigned long value;
22 
23 	asm volatile("mov %%dr6,%0" : "=r" (value));
24 	return value;
25 }
26 
27 static void set_dr0(void *value)
28 {
29 	asm volatile("mov %0,%%dr0" : : "r" (value));
30 }
31 
32 static void set_dr1(void *value)
33 {
34 	asm volatile("mov %0,%%dr1" : : "r" (value));
35 }
36 
37 static void set_dr6(unsigned long value)
38 {
39 	asm volatile("mov %0,%%dr6" : : "r" (value));
40 }
41 
42 static void set_dr7(unsigned long value)
43 {
44 	asm volatile("mov %0,%%dr7" : : "r" (value));
45 }
46 
47 static void handle_db(struct ex_regs *regs)
48 {
49 	bp_addr[n] = regs->rip;
50 	dr6[n] = get_dr6();
51 
52 	if (dr6[n] & 0x1)
53 		regs->rflags |= (1 << 16);
54 
55 	if (++n >= 10) {
56 		regs->rflags &= ~(1 << 8);
57 		set_dr7(0x00000400);
58 	}
59 }
60 
61 static void handle_bp(struct ex_regs *regs)
62 {
63 	bp_addr[0] = regs->rip;
64 }
65 
66 int main(int ac, char **av)
67 {
68 	unsigned long start;
69 
70 	setup_idt();
71 	handle_exception(DB_VECTOR, handle_db);
72 	handle_exception(BP_VECTOR, handle_bp);
73 
74 sw_bp:
75 	asm volatile("int3");
76 	report("#BP", bp_addr[0] == (unsigned long)&&sw_bp + 1);
77 
78 	n = 0;
79 	set_dr0(&&hw_bp1);
80 	set_dr7(0x00000402);
81 hw_bp1:
82 	asm volatile("nop");
83 	report("hw breakpoint (test that dr6.BS is not set)",
84 	       n == 1 &&
85 	       bp_addr[0] == ((unsigned long)&&hw_bp1) && dr6[0] == 0xffff0ff1);
86 
87 	n = 0;
88 	set_dr0(&&hw_bp2);
89 	set_dr6(0x00004002);
90 hw_bp2:
91 	asm volatile("nop");
92 	report("hw breakpoint (test that dr6.BS is not cleared)",
93 	       n == 1 &&
94 	       bp_addr[0] == ((unsigned long)&&hw_bp2) && dr6[0] == 0xffff4ff1);
95 
96 	n = 0;
97 	set_dr6(0);
98 	asm volatile(
99 		"pushf\n\t"
100 		"pop %%rax\n\t"
101 		"or $(1<<8),%%rax\n\t"
102 		"push %%rax\n\t"
103 		"lea (%%rip),%0\n\t"
104 		"popf\n\t"
105 		"and $~(1<<8),%%rax\n\t"
106 		"push %%rax\n\t"
107 		"popf\n\t"
108 		: "=g" (start) : : "rax");
109 	report("single step",
110 	       n == 3 &&
111 	       bp_addr[0] == start+1+6 && dr6[0] == 0xffff4ff0 &&
112 	       bp_addr[1] == start+1+6+1 && dr6[1] == 0xffff4ff0 &&
113 	       bp_addr[2] == start+1+6+1+1 && dr6[2] == 0xffff4ff0);
114 
115 	/*
116 	 * cpuid and rdmsr (among others) trigger VM exits and are then
117 	 * emulated. Test that single stepping works on emulated instructions.
118 	 */
119 	n = 0;
120 	set_dr6(0);
121 	asm volatile(
122 		"pushf\n\t"
123 		"pop %%rax\n\t"
124 		"or $(1<<8),%%rax\n\t"
125 		"push %%rax\n\t"
126 		"lea (%%rip),%0\n\t"
127 		"popf\n\t"
128 		"and $~(1<<8),%%rax\n\t"
129 		"push %%rax\n\t"
130 		"xor %%rax,%%rax\n\t"
131 		"cpuid\n\t"
132 		"movl $0x1a0,%%ecx\n\t"
133 		"rdmsr\n\t"
134 		"popf\n\t"
135 		: "=g" (start) : : "rax", "ebx", "ecx", "edx");
136 	report("single step emulated instructions",
137 	       n == 7 &&
138 	       bp_addr[0] == start+1+6 && dr6[0] == 0xffff4ff0 &&
139 	       bp_addr[1] == start+1+6+1 && dr6[1] == 0xffff4ff0 &&
140 	       bp_addr[2] == start+1+6+1+3 && dr6[2] == 0xffff4ff0 &&
141 	       bp_addr[3] == start+1+6+1+3+2 && dr6[3] == 0xffff4ff0 &&
142 	       bp_addr[4] == start+1+6+1+3+2+5 && dr6[4] == 0xffff4ff0 &&
143 	       bp_addr[5] == start+1+6+1+3+2+5+2 && dr6[5] == 0xffff4ff0 &&
144 	       bp_addr[6] == start+1+6+1+3+2+5+2+1 && dr6[6] == 0xffff4ff0);
145 
146 	n = 0;
147 	set_dr1((void *)&value);
148 	set_dr7(0x00d0040a);
149 
150 	asm volatile(
151 		"mov $42,%%rax\n\t"
152 		"mov %%rax,%0\n\t"
153 		: "=m" (value) : : "rax");
154 hw_wp1:
155 	report("hw watchpoint (test that dr6.BS is not cleared)",
156 	       n == 1 &&
157 	       bp_addr[0] == ((unsigned long)&&hw_wp1) && dr6[0] == 0xffff4ff2);
158 
159 	n = 0;
160 	set_dr6(0);
161 
162 	asm volatile(
163 		"mov $42,%%rax\n\t"
164 		"mov %%rax,%0\n\t"
165 		: "=m" (value) : : "rax");
166 hw_wp2:
167 	report("hw watchpoint (test that dr6.BS is not set)",
168 	       n == 1 &&
169 	       bp_addr[0] == ((unsigned long)&&hw_wp2) && dr6[0] == 0xffff0ff2);
170 
171 	n = 0;
172 	set_dr6(0);
173 sw_icebp:
174 	asm volatile(".byte 0xf1");
175 	report("icebp",
176 	       n == 1 &&
177 	       bp_addr[0] == (unsigned long)&&sw_icebp + 1 &&
178 	       dr6[0] == 0xffff0ff0);
179 
180 	return report_summary();
181 }
182