xref: /kvm-unit-tests/x86/debug.c (revision 7f8f73562310d800db1f32085eb3853592d07ff8)
1478027f5SJan Kiszka /*
2478027f5SJan Kiszka  * Test for x86 debugging facilities
3478027f5SJan Kiszka  *
4478027f5SJan Kiszka  * Copyright (c) Siemens AG, 2014
5478027f5SJan Kiszka  *
6478027f5SJan Kiszka  * Authors:
7478027f5SJan Kiszka  *  Jan Kiszka <jan.kiszka@siemens.com>
8478027f5SJan Kiszka  *
9478027f5SJan Kiszka  * This work is licensed under the terms of the GNU GPL, version 2.
10478027f5SJan Kiszka  */
11478027f5SJan Kiszka 
12478027f5SJan Kiszka #include "libcflat.h"
132b934609SXiaoyao Li #include "processor.h"
14478027f5SJan Kiszka #include "desc.h"
15478027f5SJan Kiszka 
16230db53fSPaolo Bonzini static volatile unsigned long bp_addr;
17230db53fSPaolo Bonzini static volatile unsigned long db_addr[10], dr6[10];
18478027f5SJan Kiszka static volatile unsigned int n;
19478027f5SJan Kiszka static volatile unsigned long value;
20478027f5SJan Kiszka 
21*7f8f7356SKrish Sadhukhan static inline void write_dr4(ulong val)
222c6863b2SPaolo Bonzini {
23*7f8f7356SKrish Sadhukhan     asm volatile ("mov %0, %%dr4" : : "r"(val) : "memory");
242c6863b2SPaolo Bonzini }
252c6863b2SPaolo Bonzini 
26*7f8f7356SKrish Sadhukhan static inline ulong read_dr4(void)
27478027f5SJan Kiszka {
28*7f8f7356SKrish Sadhukhan     ulong val;
29*7f8f7356SKrish Sadhukhan     asm volatile ("mov %%dr4, %0" : "=r"(val));
30*7f8f7356SKrish Sadhukhan     return val;
31478027f5SJan Kiszka }
32478027f5SJan Kiszka 
33478027f5SJan Kiszka static void handle_db(struct ex_regs *regs)
34478027f5SJan Kiszka {
35230db53fSPaolo Bonzini 	db_addr[n] = regs->rip;
36*7f8f7356SKrish Sadhukhan 	dr6[n] = read_dr6();
37478027f5SJan Kiszka 
38478027f5SJan Kiszka 	if (dr6[n] & 0x1)
39478027f5SJan Kiszka 		regs->rflags |= (1 << 16);
40478027f5SJan Kiszka 
41478027f5SJan Kiszka 	if (++n >= 10) {
42478027f5SJan Kiszka 		regs->rflags &= ~(1 << 8);
43*7f8f7356SKrish Sadhukhan 		write_dr7(0x00000400);
44478027f5SJan Kiszka 	}
45478027f5SJan Kiszka }
46478027f5SJan Kiszka 
47a094abddSPaolo Bonzini extern unsigned char handle_db_save_rip;
48a094abddSPaolo Bonzini asm("handle_db_save_rip:\n"
49a094abddSPaolo Bonzini    "stc\n"
50a094abddSPaolo Bonzini    "nop;nop;nop\n"
51a094abddSPaolo Bonzini    "rclq $1, n(%rip)\n"
52a094abddSPaolo Bonzini    "iretq\n");
53a094abddSPaolo Bonzini 
54478027f5SJan Kiszka static void handle_bp(struct ex_regs *regs)
55478027f5SJan Kiszka {
56230db53fSPaolo Bonzini 	bp_addr = regs->rip;
57478027f5SJan Kiszka }
58478027f5SJan Kiszka 
592c6863b2SPaolo Bonzini bool got_ud;
602c6863b2SPaolo Bonzini static void handle_ud(struct ex_regs *regs)
612c6863b2SPaolo Bonzini {
622c6863b2SPaolo Bonzini 	unsigned long cr4 = read_cr4();
632c6863b2SPaolo Bonzini 	write_cr4(cr4 & ~X86_CR4_DE);
642c6863b2SPaolo Bonzini 	got_ud = 1;
652c6863b2SPaolo Bonzini }
662c6863b2SPaolo Bonzini 
67478027f5SJan Kiszka int main(int ac, char **av)
68478027f5SJan Kiszka {
69478027f5SJan Kiszka 	unsigned long start;
702c6863b2SPaolo Bonzini 	unsigned long cr4;
71478027f5SJan Kiszka 
72478027f5SJan Kiszka 	handle_exception(DB_VECTOR, handle_db);
73478027f5SJan Kiszka 	handle_exception(BP_VECTOR, handle_bp);
742c6863b2SPaolo Bonzini 	handle_exception(UD_VECTOR, handle_ud);
752c6863b2SPaolo Bonzini 
762c6863b2SPaolo Bonzini 	got_ud = 0;
772c6863b2SPaolo Bonzini 	cr4 = read_cr4();
782c6863b2SPaolo Bonzini 	write_cr4(cr4 & ~X86_CR4_DE);
79*7f8f7356SKrish Sadhukhan 	write_dr4(0);
80*7f8f7356SKrish Sadhukhan 	write_dr6(0xffff4ff2);
81*7f8f7356SKrish Sadhukhan 	report(read_dr4() == 0xffff4ff2 && !got_ud, "reading DR4 with CR4.DE == 0");
822c6863b2SPaolo Bonzini 
832c6863b2SPaolo Bonzini 	cr4 = read_cr4();
842c6863b2SPaolo Bonzini 	write_cr4(cr4 | X86_CR4_DE);
85*7f8f7356SKrish Sadhukhan 	read_dr4();
862c6863b2SPaolo Bonzini 	report(got_ud, "reading DR4 with CR4.DE == 1");
87*7f8f7356SKrish Sadhukhan 	write_dr6(0);
88478027f5SJan Kiszka 
89c68a7ff0SPaolo Bonzini 	extern unsigned char sw_bp;
90c68a7ff0SPaolo Bonzini 	asm volatile("int3; sw_bp:");
91a299895bSThomas Huth 	report(bp_addr == (unsigned long)&sw_bp, "#BP");
92478027f5SJan Kiszka 
939e486280SPaolo Bonzini 	n = 0;
94c68a7ff0SPaolo Bonzini 	extern unsigned char hw_bp1;
95*7f8f7356SKrish Sadhukhan 	write_dr0(&hw_bp1);
96*7f8f7356SKrish Sadhukhan 	write_dr7(0x00000402);
97c68a7ff0SPaolo Bonzini 	asm volatile("hw_bp1: nop");
98a299895bSThomas Huth 	report(n == 1 &&
99a299895bSThomas Huth 	       db_addr[0] == ((unsigned long)&hw_bp1) && dr6[0] == 0xffff0ff1,
100a299895bSThomas Huth 	       "hw breakpoint (test that dr6.BS is not set)");
101478027f5SJan Kiszka 
102478027f5SJan Kiszka 	n = 0;
103c68a7ff0SPaolo Bonzini 	extern unsigned char hw_bp2;
104*7f8f7356SKrish Sadhukhan 	write_dr0(&hw_bp2);
105*7f8f7356SKrish Sadhukhan 	write_dr6(0x00004002);
106c68a7ff0SPaolo Bonzini 	asm volatile("hw_bp2: nop");
107a299895bSThomas Huth 	report(n == 1 &&
108a299895bSThomas Huth 	       db_addr[0] == ((unsigned long)&hw_bp2) && dr6[0] == 0xffff4ff1,
109a299895bSThomas Huth 	       "hw breakpoint (test that dr6.BS is not cleared)");
1109e486280SPaolo Bonzini 
1119e486280SPaolo Bonzini 	n = 0;
112*7f8f7356SKrish Sadhukhan 	write_dr6(0);
113478027f5SJan Kiszka 	asm volatile(
114478027f5SJan Kiszka 		"pushf\n\t"
115478027f5SJan Kiszka 		"pop %%rax\n\t"
116478027f5SJan Kiszka 		"or $(1<<8),%%rax\n\t"
117478027f5SJan Kiszka 		"push %%rax\n\t"
118478027f5SJan Kiszka 		"lea (%%rip),%0\n\t"
119478027f5SJan Kiszka 		"popf\n\t"
120478027f5SJan Kiszka 		"and $~(1<<8),%%rax\n\t"
121478027f5SJan Kiszka 		"push %%rax\n\t"
122478027f5SJan Kiszka 		"popf\n\t"
12375eea510SBill Wendling 		: "=r" (start) : : "rax");
124a299895bSThomas Huth 	report(n == 3 &&
125230db53fSPaolo Bonzini 	       db_addr[0] == start + 1 + 6 && dr6[0] == 0xffff4ff0 &&
126230db53fSPaolo Bonzini 	       db_addr[1] == start + 1 + 6 + 1 && dr6[1] == 0xffff4ff0 &&
127a299895bSThomas Huth 	       db_addr[2] == start + 1 + 6 + 1 + 1 && dr6[2] == 0xffff4ff0,
128a299895bSThomas Huth 	       "single step");
129478027f5SJan Kiszka 
1300a982d78SKyle Huey 	/*
1310a982d78SKyle Huey 	 * cpuid and rdmsr (among others) trigger VM exits and are then
1320a982d78SKyle Huey 	 * emulated. Test that single stepping works on emulated instructions.
1330a982d78SKyle Huey 	 */
1340a982d78SKyle Huey 	n = 0;
135*7f8f7356SKrish Sadhukhan 	write_dr6(0);
1360a982d78SKyle Huey 	asm volatile(
1370a982d78SKyle Huey 		"pushf\n\t"
1380a982d78SKyle Huey 		"pop %%rax\n\t"
1390a982d78SKyle Huey 		"or $(1<<8),%%rax\n\t"
1400a982d78SKyle Huey 		"push %%rax\n\t"
1410a982d78SKyle Huey 		"lea (%%rip),%0\n\t"
1420a982d78SKyle Huey 		"popf\n\t"
1430a982d78SKyle Huey 		"and $~(1<<8),%%rax\n\t"
1440a982d78SKyle Huey 		"push %%rax\n\t"
1450a982d78SKyle Huey 		"xor %%rax,%%rax\n\t"
1460a982d78SKyle Huey 		"cpuid\n\t"
1470a982d78SKyle Huey 		"movl $0x1a0,%%ecx\n\t"
1480a982d78SKyle Huey 		"rdmsr\n\t"
1490a982d78SKyle Huey 		"popf\n\t"
15075eea510SBill Wendling 		: "=r" (start) : : "rax", "ebx", "ecx", "edx");
151a299895bSThomas Huth 	report(n == 7 &&
152230db53fSPaolo Bonzini 	       db_addr[0] == start + 1 + 6 && dr6[0] == 0xffff4ff0 &&
153230db53fSPaolo Bonzini 	       db_addr[1] == start + 1 + 6 + 1 && dr6[1] == 0xffff4ff0 &&
154230db53fSPaolo Bonzini 	       db_addr[2] == start + 1 + 6 + 1 + 3 && dr6[2] == 0xffff4ff0 &&
155230db53fSPaolo Bonzini 	       db_addr[3] == start + 1 + 6 + 1 + 3 + 2 && dr6[3] == 0xffff4ff0 &&
156230db53fSPaolo Bonzini 	       db_addr[4] == start + 1 + 6 + 1 + 3 + 2 + 5 && dr6[4] == 0xffff4ff0 &&
157230db53fSPaolo Bonzini 	       db_addr[5] == start + 1 + 6 + 1 + 3 + 2 + 5 + 2 && dr6[5] == 0xffff4ff0 &&
158a299895bSThomas Huth 	       db_addr[6] == start + 1 + 6 + 1 + 3 + 2 + 5 + 2 + 1 && dr6[6] == 0xffff4ff0,
159a299895bSThomas Huth 	       "single step emulated instructions");
1600a982d78SKyle Huey 
161478027f5SJan Kiszka 	n = 0;
162*7f8f7356SKrish Sadhukhan 	write_dr1((void *)&value);
163*7f8f7356SKrish Sadhukhan 	write_dr7(0x00d0040a); // 4-byte write
164478027f5SJan Kiszka 
165c68a7ff0SPaolo Bonzini 	extern unsigned char hw_wp1;
166478027f5SJan Kiszka 	asm volatile(
167478027f5SJan Kiszka 		"mov $42,%%rax\n\t"
168c68a7ff0SPaolo Bonzini 		"mov %%rax,%0\n\t; hw_wp1:"
169478027f5SJan Kiszka 		: "=m" (value) : : "rax");
170a299895bSThomas Huth 	report(n == 1 &&
171a299895bSThomas Huth 	       db_addr[0] == ((unsigned long)&hw_wp1) && dr6[0] == 0xffff4ff2,
172a299895bSThomas Huth 	       "hw watchpoint (test that dr6.BS is not cleared)");
1739e486280SPaolo Bonzini 
1749e486280SPaolo Bonzini 	n = 0;
175*7f8f7356SKrish Sadhukhan 	write_dr6(0);
1769e486280SPaolo Bonzini 
177c68a7ff0SPaolo Bonzini 	extern unsigned char hw_wp2;
1789e486280SPaolo Bonzini 	asm volatile(
1799e486280SPaolo Bonzini 		"mov $42,%%rax\n\t"
180c68a7ff0SPaolo Bonzini 		"mov %%rax,%0\n\t; hw_wp2:"
1819e486280SPaolo Bonzini 		: "=m" (value) : : "rax");
182a299895bSThomas Huth 	report(n == 1 &&
183a299895bSThomas Huth 	       db_addr[0] == ((unsigned long)&hw_wp2) && dr6[0] == 0xffff0ff2,
184a299895bSThomas Huth 	       "hw watchpoint (test that dr6.BS is not set)");
1859e486280SPaolo Bonzini 
1869e486280SPaolo Bonzini 	n = 0;
187*7f8f7356SKrish Sadhukhan 	write_dr6(0);
188c68a7ff0SPaolo Bonzini 	extern unsigned char sw_icebp;
189c68a7ff0SPaolo Bonzini 	asm volatile(".byte 0xf1; sw_icebp:");
190a299895bSThomas Huth 	report(n == 1 &&
191a299895bSThomas Huth 	       db_addr[0] == (unsigned long)&sw_icebp && dr6[0] == 0xffff0ff0,
192a299895bSThomas Huth 	       "icebp");
193478027f5SJan Kiszka 
194*7f8f7356SKrish Sadhukhan 	write_dr7(0x400);
195a094abddSPaolo Bonzini 	value = KERNEL_DS;
196*7f8f7356SKrish Sadhukhan 	write_dr7(0x00f0040a); // 4-byte read or write
197a094abddSPaolo Bonzini 
198a094abddSPaolo Bonzini 	/*
199a094abddSPaolo Bonzini 	 * Each invocation of the handler should shift n by 1 and set bit 0 to 1.
200a094abddSPaolo Bonzini 	 * We expect a single invocation, so n should become 3.  If the entry
201a094abddSPaolo Bonzini 	 * RIP is wrong, or if the handler is executed more than once, the value
202a094abddSPaolo Bonzini 	 * will not match.
203a094abddSPaolo Bonzini 	 */
204a094abddSPaolo Bonzini 	set_idt_entry(1, &handle_db_save_rip, 0);
205a094abddSPaolo Bonzini 
206a094abddSPaolo Bonzini 	n = 1;
207a094abddSPaolo Bonzini 	asm volatile(
208a094abddSPaolo Bonzini 		"clc\n\t"
209a094abddSPaolo Bonzini 		"mov %0,%%ss\n\t"
210a094abddSPaolo Bonzini 		".byte 0x2e, 0x2e, 0xf1"
211a094abddSPaolo Bonzini 		: "=m" (value) : : "rax");
212a299895bSThomas Huth 	report(n == 3, "MOV SS + watchpoint + ICEBP");
213a094abddSPaolo Bonzini 
214a094abddSPaolo Bonzini 	/*
215a094abddSPaolo Bonzini 	 * Here the #DB handler is invoked twice, once as a software exception
216a094abddSPaolo Bonzini 	 * and once as a software interrupt.
217a094abddSPaolo Bonzini 	 */
218a094abddSPaolo Bonzini 	n = 1;
219a094abddSPaolo Bonzini 	asm volatile(
220a094abddSPaolo Bonzini 		"clc\n\t"
221a094abddSPaolo Bonzini 		"mov %0,%%ss\n\t"
222a094abddSPaolo Bonzini 		"int $1"
223a094abddSPaolo Bonzini 		: "=m" (value) : : "rax");
224a299895bSThomas Huth 	report(n == 7, "MOV SS + watchpoint + int $1");
225a094abddSPaolo Bonzini 
226a094abddSPaolo Bonzini 	/*
227a094abddSPaolo Bonzini 	 * Here the #DB and #BP handlers are invoked once each.
228a094abddSPaolo Bonzini 	 */
229a094abddSPaolo Bonzini 	n = 1;
230a094abddSPaolo Bonzini 	bp_addr = 0;
231a094abddSPaolo Bonzini 	asm volatile(
232a094abddSPaolo Bonzini 		"mov %0,%%ss\n\t"
233a094abddSPaolo Bonzini 		".byte 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0xcc\n\t"
234a094abddSPaolo Bonzini 		"sw_bp2:"
235a094abddSPaolo Bonzini 		: "=m" (value) : : "rax");
236a094abddSPaolo Bonzini 	extern unsigned char sw_bp2;
237a299895bSThomas Huth 	report(n == 3 && bp_addr == (unsigned long)&sw_bp2,
238a299895bSThomas Huth 	       "MOV SS + watchpoint + INT3");
23986065ca2SAndrew Jones 	return report_summary();
240478027f5SJan Kiszka }
241