xref: /kvm-unit-tests/x86/debug.c (revision 230db53f9c26b44633b41393e9b600eb087814b3)
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"
13478027f5SJan Kiszka #include "desc.h"
14478027f5SJan Kiszka 
15*230db53fSPaolo Bonzini static volatile unsigned long bp_addr;
16*230db53fSPaolo Bonzini static volatile unsigned long db_addr[10], dr6[10];
17478027f5SJan Kiszka static volatile unsigned int n;
18478027f5SJan Kiszka static volatile unsigned long value;
19478027f5SJan Kiszka 
20478027f5SJan Kiszka static unsigned long get_dr6(void)
21478027f5SJan Kiszka {
22478027f5SJan Kiszka 	unsigned long value;
23478027f5SJan Kiszka 
24478027f5SJan Kiszka 	asm volatile("mov %%dr6,%0" : "=r" (value));
25478027f5SJan Kiszka 	return value;
26478027f5SJan Kiszka }
27478027f5SJan Kiszka 
28478027f5SJan Kiszka static void set_dr0(void *value)
29478027f5SJan Kiszka {
30478027f5SJan Kiszka 	asm volatile("mov %0,%%dr0" : : "r" (value));
31478027f5SJan Kiszka }
32478027f5SJan Kiszka 
33478027f5SJan Kiszka static void set_dr1(void *value)
34478027f5SJan Kiszka {
35478027f5SJan Kiszka 	asm volatile("mov %0,%%dr1" : : "r" (value));
36478027f5SJan Kiszka }
37478027f5SJan Kiszka 
389e486280SPaolo Bonzini static void set_dr6(unsigned long value)
399e486280SPaolo Bonzini {
409e486280SPaolo Bonzini 	asm volatile("mov %0,%%dr6" : : "r" (value));
419e486280SPaolo Bonzini }
429e486280SPaolo Bonzini 
43478027f5SJan Kiszka static void set_dr7(unsigned long value)
44478027f5SJan Kiszka {
45478027f5SJan Kiszka 	asm volatile("mov %0,%%dr7" : : "r" (value));
46478027f5SJan Kiszka }
47478027f5SJan Kiszka 
48478027f5SJan Kiszka static void handle_db(struct ex_regs *regs)
49478027f5SJan Kiszka {
50*230db53fSPaolo Bonzini 	db_addr[n] = regs->rip;
51478027f5SJan Kiszka 	dr6[n] = get_dr6();
52478027f5SJan Kiszka 
53478027f5SJan Kiszka 	if (dr6[n] & 0x1)
54478027f5SJan Kiszka 		regs->rflags |= (1 << 16);
55478027f5SJan Kiszka 
56478027f5SJan Kiszka 	if (++n >= 10) {
57478027f5SJan Kiszka 		regs->rflags &= ~(1 << 8);
58478027f5SJan Kiszka 		set_dr7(0x00000400);
59478027f5SJan Kiszka 	}
60478027f5SJan Kiszka }
61478027f5SJan Kiszka 
62478027f5SJan Kiszka static void handle_bp(struct ex_regs *regs)
63478027f5SJan Kiszka {
64*230db53fSPaolo Bonzini 	bp_addr = regs->rip;
65478027f5SJan Kiszka }
66478027f5SJan Kiszka 
67478027f5SJan Kiszka int main(int ac, char **av)
68478027f5SJan Kiszka {
69478027f5SJan Kiszka 	unsigned long start;
70478027f5SJan Kiszka 
71478027f5SJan Kiszka 	setup_idt();
72478027f5SJan Kiszka 	handle_exception(DB_VECTOR, handle_db);
73478027f5SJan Kiszka 	handle_exception(BP_VECTOR, handle_bp);
74478027f5SJan Kiszka 
75c68a7ff0SPaolo Bonzini 	extern unsigned char sw_bp;
76c68a7ff0SPaolo Bonzini 	asm volatile("int3; sw_bp:");
77*230db53fSPaolo Bonzini 	report("#BP", bp_addr == (unsigned long)&sw_bp);
78478027f5SJan Kiszka 
799e486280SPaolo Bonzini 	n = 0;
80c68a7ff0SPaolo Bonzini 	extern unsigned char hw_bp1;
81c68a7ff0SPaolo Bonzini 	set_dr0(&hw_bp1);
82478027f5SJan Kiszka 	set_dr7(0x00000402);
83c68a7ff0SPaolo Bonzini 	asm volatile("hw_bp1: nop");
849e486280SPaolo Bonzini 	report("hw breakpoint (test that dr6.BS is not set)",
85478027f5SJan Kiszka 	       n == 1 &&
86*230db53fSPaolo Bonzini 	       db_addr[0] == ((unsigned long)&hw_bp1) && dr6[0] == 0xffff0ff1);
87478027f5SJan Kiszka 
88478027f5SJan Kiszka 	n = 0;
89c68a7ff0SPaolo Bonzini 	extern unsigned char hw_bp2;
90c68a7ff0SPaolo Bonzini 	set_dr0(&hw_bp2);
919e486280SPaolo Bonzini 	set_dr6(0x00004002);
92c68a7ff0SPaolo Bonzini 	asm volatile("hw_bp2: nop");
939e486280SPaolo Bonzini 	report("hw breakpoint (test that dr6.BS is not cleared)",
949e486280SPaolo Bonzini 	       n == 1 &&
95*230db53fSPaolo Bonzini 	       db_addr[0] == ((unsigned long)&hw_bp2) && dr6[0] == 0xffff4ff1);
969e486280SPaolo Bonzini 
979e486280SPaolo Bonzini 	n = 0;
989e486280SPaolo Bonzini 	set_dr6(0);
99478027f5SJan Kiszka 	asm volatile(
100478027f5SJan Kiszka 		"pushf\n\t"
101478027f5SJan Kiszka 		"pop %%rax\n\t"
102478027f5SJan Kiszka 		"or $(1<<8),%%rax\n\t"
103478027f5SJan Kiszka 		"push %%rax\n\t"
104478027f5SJan Kiszka 		"lea (%%rip),%0\n\t"
105478027f5SJan Kiszka 		"popf\n\t"
106478027f5SJan Kiszka 		"and $~(1<<8),%%rax\n\t"
107478027f5SJan Kiszka 		"push %%rax\n\t"
108478027f5SJan Kiszka 		"popf\n\t"
109478027f5SJan Kiszka 		: "=g" (start) : : "rax");
110478027f5SJan Kiszka 	report("single step",
111478027f5SJan Kiszka 	       n == 3 &&
112*230db53fSPaolo Bonzini 	       db_addr[0] == start+1+6 && dr6[0] == 0xffff4ff0 &&
113*230db53fSPaolo Bonzini 	       db_addr[1] == start+1+6+1 && dr6[1] == 0xffff4ff0 &&
114*230db53fSPaolo Bonzini 	       db_addr[2] == start+1+6+1+1 && dr6[2] == 0xffff4ff0);
115478027f5SJan Kiszka 
1160a982d78SKyle Huey 	/*
1170a982d78SKyle Huey 	 * cpuid and rdmsr (among others) trigger VM exits and are then
1180a982d78SKyle Huey 	 * emulated. Test that single stepping works on emulated instructions.
1190a982d78SKyle Huey 	 */
1200a982d78SKyle Huey 	n = 0;
1210a982d78SKyle Huey 	set_dr6(0);
1220a982d78SKyle Huey 	asm volatile(
1230a982d78SKyle Huey 		"pushf\n\t"
1240a982d78SKyle Huey 		"pop %%rax\n\t"
1250a982d78SKyle Huey 		"or $(1<<8),%%rax\n\t"
1260a982d78SKyle Huey 		"push %%rax\n\t"
1270a982d78SKyle Huey 		"lea (%%rip),%0\n\t"
1280a982d78SKyle Huey 		"popf\n\t"
1290a982d78SKyle Huey 		"and $~(1<<8),%%rax\n\t"
1300a982d78SKyle Huey 		"push %%rax\n\t"
1310a982d78SKyle Huey 		"xor %%rax,%%rax\n\t"
1320a982d78SKyle Huey 		"cpuid\n\t"
1330a982d78SKyle Huey 		"movl $0x1a0,%%ecx\n\t"
1340a982d78SKyle Huey 		"rdmsr\n\t"
1350a982d78SKyle Huey 		"popf\n\t"
1360a982d78SKyle Huey 		: "=g" (start) : : "rax", "ebx", "ecx", "edx");
1370a982d78SKyle Huey 	report("single step emulated instructions",
1380a982d78SKyle Huey 	       n == 7 &&
139*230db53fSPaolo Bonzini 	       db_addr[0] == start+1+6 && dr6[0] == 0xffff4ff0 &&
140*230db53fSPaolo Bonzini 	       db_addr[1] == start+1+6+1 && dr6[1] == 0xffff4ff0 &&
141*230db53fSPaolo Bonzini 	       db_addr[2] == start+1+6+1+3 && dr6[2] == 0xffff4ff0 &&
142*230db53fSPaolo Bonzini 	       db_addr[3] == start+1+6+1+3+2 && dr6[3] == 0xffff4ff0 &&
143*230db53fSPaolo Bonzini 	       db_addr[4] == start+1+6+1+3+2+5 && dr6[4] == 0xffff4ff0 &&
144*230db53fSPaolo Bonzini 	       db_addr[5] == start+1+6+1+3+2+5+2 && dr6[5] == 0xffff4ff0 &&
145*230db53fSPaolo Bonzini 	       db_addr[6] == start+1+6+1+3+2+5+2+1 && dr6[6] == 0xffff4ff0);
1460a982d78SKyle Huey 
147478027f5SJan Kiszka 	n = 0;
148478027f5SJan Kiszka 	set_dr1((void *)&value);
149*230db53fSPaolo Bonzini 	set_dr7(0x00d0040a); // 4-byte write
150478027f5SJan Kiszka 
151c68a7ff0SPaolo Bonzini 	extern unsigned char hw_wp1;
152478027f5SJan Kiszka 	asm volatile(
153478027f5SJan Kiszka 		"mov $42,%%rax\n\t"
154c68a7ff0SPaolo Bonzini 		"mov %%rax,%0\n\t; hw_wp1:"
155478027f5SJan Kiszka 		: "=m" (value) : : "rax");
1569e486280SPaolo Bonzini 	report("hw watchpoint (test that dr6.BS is not cleared)",
157478027f5SJan Kiszka 	       n == 1 &&
158*230db53fSPaolo Bonzini 	       db_addr[0] == ((unsigned long)&hw_wp1) && dr6[0] == 0xffff4ff2);
1599e486280SPaolo Bonzini 
1609e486280SPaolo Bonzini 	n = 0;
1619e486280SPaolo Bonzini 	set_dr6(0);
1629e486280SPaolo Bonzini 
163c68a7ff0SPaolo Bonzini 	extern unsigned char hw_wp2;
1649e486280SPaolo Bonzini 	asm volatile(
1659e486280SPaolo Bonzini 		"mov $42,%%rax\n\t"
166c68a7ff0SPaolo Bonzini 		"mov %%rax,%0\n\t; hw_wp2:"
1679e486280SPaolo Bonzini 		: "=m" (value) : : "rax");
1689e486280SPaolo Bonzini 	report("hw watchpoint (test that dr6.BS is not set)",
1699e486280SPaolo Bonzini 	       n == 1 &&
170*230db53fSPaolo Bonzini 	       db_addr[0] == ((unsigned long)&hw_wp2) && dr6[0] == 0xffff0ff2);
1719e486280SPaolo Bonzini 
1729e486280SPaolo Bonzini 	n = 0;
1739e486280SPaolo Bonzini 	set_dr6(0);
174c68a7ff0SPaolo Bonzini 	extern unsigned char sw_icebp;
175c68a7ff0SPaolo Bonzini 	asm volatile(".byte 0xf1; sw_icebp:");
1769e486280SPaolo Bonzini 	report("icebp",
1779e486280SPaolo Bonzini 	       n == 1 &&
178*230db53fSPaolo Bonzini 	       db_addr[0] == (unsigned long)&sw_icebp &&
1799e486280SPaolo Bonzini 	       dr6[0] == 0xffff0ff0);
180478027f5SJan Kiszka 
18186065ca2SAndrew Jones 	return report_summary();
182478027f5SJan Kiszka }
183