xref: /kvm-unit-tests/x86/debug.c (revision b8a4530de3d9e92d86694e3b8a345a313b6da5db)
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"
15*b8a4530dSSean Christopherson #include "usermode.h"
16478027f5SJan Kiszka 
17230db53fSPaolo Bonzini static volatile unsigned long bp_addr;
18230db53fSPaolo Bonzini static volatile unsigned long db_addr[10], dr6[10];
19478027f5SJan Kiszka static volatile unsigned int n;
20478027f5SJan Kiszka static volatile unsigned long value;
21478027f5SJan Kiszka 
227f8f7356SKrish Sadhukhan static inline void write_dr4(ulong val)
232c6863b2SPaolo Bonzini {
247f8f7356SKrish Sadhukhan     asm volatile ("mov %0, %%dr4" : : "r"(val) : "memory");
252c6863b2SPaolo Bonzini }
262c6863b2SPaolo Bonzini 
277f8f7356SKrish Sadhukhan static inline ulong read_dr4(void)
28478027f5SJan Kiszka {
297f8f7356SKrish Sadhukhan     ulong val;
307f8f7356SKrish Sadhukhan     asm volatile ("mov %%dr4, %0" : "=r"(val));
317f8f7356SKrish Sadhukhan     return val;
32478027f5SJan Kiszka }
33478027f5SJan Kiszka 
34478027f5SJan Kiszka static void handle_db(struct ex_regs *regs)
35478027f5SJan Kiszka {
36230db53fSPaolo Bonzini 	db_addr[n] = regs->rip;
377f8f7356SKrish Sadhukhan 	dr6[n] = read_dr6();
38478027f5SJan Kiszka 
39478027f5SJan Kiszka 	if (dr6[n] & 0x1)
409734b423SSean Christopherson 		regs->rflags |= X86_EFLAGS_RF;
41478027f5SJan Kiszka 
42478027f5SJan Kiszka 	if (++n >= 10) {
439734b423SSean Christopherson 		regs->rflags &= ~X86_EFLAGS_TF;
447f8f7356SKrish Sadhukhan 		write_dr7(0x00000400);
45478027f5SJan Kiszka 	}
46478027f5SJan Kiszka }
47478027f5SJan Kiszka 
489734b423SSean Christopherson static inline bool is_single_step_db(unsigned long dr6_val)
499734b423SSean Christopherson {
509734b423SSean Christopherson 	return dr6_val == 0xffff4ff0;
519734b423SSean Christopherson }
529734b423SSean Christopherson 
539734b423SSean Christopherson static inline bool is_icebp_db(unsigned long dr6_val)
549734b423SSean Christopherson {
559734b423SSean Christopherson 	return dr6_val == 0xffff0ff0;
569734b423SSean Christopherson }
579734b423SSean Christopherson 
58a094abddSPaolo Bonzini extern unsigned char handle_db_save_rip;
59a094abddSPaolo Bonzini asm("handle_db_save_rip:\n"
60a094abddSPaolo Bonzini    "stc\n"
61a094abddSPaolo Bonzini    "nop;nop;nop\n"
62a094abddSPaolo Bonzini    "rclq $1, n(%rip)\n"
63a094abddSPaolo Bonzini    "iretq\n");
64a094abddSPaolo Bonzini 
65478027f5SJan Kiszka static void handle_bp(struct ex_regs *regs)
66478027f5SJan Kiszka {
67230db53fSPaolo Bonzini 	bp_addr = regs->rip;
68478027f5SJan Kiszka }
69478027f5SJan Kiszka 
702c6863b2SPaolo Bonzini bool got_ud;
712c6863b2SPaolo Bonzini static void handle_ud(struct ex_regs *regs)
722c6863b2SPaolo Bonzini {
732c6863b2SPaolo Bonzini 	unsigned long cr4 = read_cr4();
742c6863b2SPaolo Bonzini 	write_cr4(cr4 & ~X86_CR4_DE);
752c6863b2SPaolo Bonzini 	got_ud = 1;
762c6863b2SPaolo Bonzini }
772c6863b2SPaolo Bonzini 
789734b423SSean Christopherson typedef unsigned long (*db_test_fn)(void);
79*b8a4530dSSean Christopherson typedef void (*db_report_fn)(unsigned long, const char *);
809734b423SSean Christopherson 
819734b423SSean Christopherson static void __run_single_step_db_test(db_test_fn test, db_report_fn report_fn)
82478027f5SJan Kiszka {
83478027f5SJan Kiszka 	unsigned long start;
84*b8a4530dSSean Christopherson 	bool ign;
859734b423SSean Christopherson 
869734b423SSean Christopherson 	n = 0;
879734b423SSean Christopherson 	write_dr6(0);
889734b423SSean Christopherson 
899734b423SSean Christopherson 	start = test();
90*b8a4530dSSean Christopherson 	report_fn(start, "");
91*b8a4530dSSean Christopherson 
92*b8a4530dSSean Christopherson 	n = 0;
93*b8a4530dSSean Christopherson 	write_dr6(0);
94*b8a4530dSSean Christopherson 	/*
95*b8a4530dSSean Christopherson 	 * Run the test in usermode.  Use the expected start RIP from the first
96*b8a4530dSSean Christopherson 	 * run, the usermode framework doesn't make it easy to get the expected
97*b8a4530dSSean Christopherson 	 * RIP out of the test, and it shouldn't change in any case.  Run the
98*b8a4530dSSean Christopherson 	 * test with IOPL=3 so that it can use OUT, CLI, STI, etc...
99*b8a4530dSSean Christopherson 	 */
100*b8a4530dSSean Christopherson 	set_iopl(3);
101*b8a4530dSSean Christopherson 	run_in_user((usermode_func)test, GP_VECTOR, 0, 0, 0, 0, &ign);
102*b8a4530dSSean Christopherson 	set_iopl(0);
103*b8a4530dSSean Christopherson 
104*b8a4530dSSean Christopherson 	report_fn(start, "Usermode ");
1059734b423SSean Christopherson }
1069734b423SSean Christopherson 
1079734b423SSean Christopherson #define run_ss_db_test(name) __run_single_step_db_test(name, report_##name)
1089734b423SSean Christopherson 
109*b8a4530dSSean Christopherson static void report_singlestep_basic(unsigned long start, const char *usermode)
1109734b423SSean Christopherson {
1119734b423SSean Christopherson 	report(n == 3 &&
1129734b423SSean Christopherson 	       is_single_step_db(dr6[0]) && db_addr[0] == start &&
1139734b423SSean Christopherson 	       is_single_step_db(dr6[1]) && db_addr[1] == start + 1 &&
1149734b423SSean Christopherson 	       is_single_step_db(dr6[2]) && db_addr[2] == start + 1 + 1,
115*b8a4530dSSean Christopherson 	       "%sSingle-step #DB basic test", usermode);
1169734b423SSean Christopherson }
1179734b423SSean Christopherson 
1189734b423SSean Christopherson static unsigned long singlestep_basic(void)
1199734b423SSean Christopherson {
1209734b423SSean Christopherson 	unsigned long start;
1219734b423SSean Christopherson 
1229734b423SSean Christopherson 	/*
1239734b423SSean Christopherson 	 * After being enabled, single-step breakpoints have a one instruction
1249734b423SSean Christopherson 	 * delay before the first #DB is generated.
1259734b423SSean Christopherson 	 */
1269734b423SSean Christopherson 	asm volatile (
1279734b423SSean Christopherson 		"pushf\n\t"
1289734b423SSean Christopherson 		"pop %%rax\n\t"
1299734b423SSean Christopherson 		"or $(1<<8),%%rax\n\t"
1309734b423SSean Christopherson 		"push %%rax\n\t"
1319734b423SSean Christopherson 		"popf\n\t"
1329734b423SSean Christopherson 		"and $~(1<<8),%%rax\n\t"
1339734b423SSean Christopherson 		"1:push %%rax\n\t"
1349734b423SSean Christopherson 		"popf\n\t"
1359734b423SSean Christopherson 		"lea 1b, %0\n\t"
1369734b423SSean Christopherson 		: "=r" (start) : : "rax"
1379734b423SSean Christopherson 	);
1389734b423SSean Christopherson 	return start;
1399734b423SSean Christopherson }
1409734b423SSean Christopherson 
141*b8a4530dSSean Christopherson static void report_singlestep_emulated_instructions(unsigned long start,
142*b8a4530dSSean Christopherson 						    const char *usermode)
1439734b423SSean Christopherson {
1449734b423SSean Christopherson 	report(n == 7 &&
1459734b423SSean Christopherson 	       is_single_step_db(dr6[0]) && db_addr[0] == start &&
1469734b423SSean Christopherson 	       is_single_step_db(dr6[1]) && db_addr[1] == start + 1 &&
1479734b423SSean Christopherson 	       is_single_step_db(dr6[2]) && db_addr[2] == start + 1 + 3 &&
1489734b423SSean Christopherson 	       is_single_step_db(dr6[3]) && db_addr[3] == start + 1 + 3 + 2 &&
1499734b423SSean Christopherson 	       is_single_step_db(dr6[4]) && db_addr[4] == start + 1 + 3 + 2 + 5 &&
1506bfb9572SSean Christopherson 	       is_single_step_db(dr6[5]) && db_addr[5] == start + 1 + 3 + 2 + 5 + 1 &&
1516bfb9572SSean Christopherson 	       is_single_step_db(dr6[6]) && db_addr[6] == start + 1 + 3 + 2 + 5 + 1 + 1,
152*b8a4530dSSean Christopherson 	       "%sSingle-step #DB on emulated instructions", usermode);
1539734b423SSean Christopherson }
1549734b423SSean Christopherson 
1559734b423SSean Christopherson static unsigned long singlestep_emulated_instructions(void)
1569734b423SSean Christopherson {
1579734b423SSean Christopherson 	unsigned long start;
1589734b423SSean Christopherson 
1599734b423SSean Christopherson 	/*
1609734b423SSean Christopherson 	 * Verify single-step #DB are generated correctly on emulated
1619734b423SSean Christopherson 	 * instructions, e.g. CPUID and RDMSR.
1629734b423SSean Christopherson 	 */
1639734b423SSean Christopherson 	asm volatile (
1649734b423SSean Christopherson 		"pushf\n\t"
1659734b423SSean Christopherson 		"pop %%rax\n\t"
1669734b423SSean Christopherson 		"or $(1<<8),%%rax\n\t"
1679734b423SSean Christopherson 		"push %%rax\n\t"
1689734b423SSean Christopherson 		"popf\n\t"
1699734b423SSean Christopherson 		"and $~(1<<8),%%rax\n\t"
1709734b423SSean Christopherson 		"1:push %%rax\n\t"
1719734b423SSean Christopherson 		"xor %%rax,%%rax\n\t"
1729734b423SSean Christopherson 		"cpuid\n\t"
1736bfb9572SSean Christopherson 		"movl $0x3fd, %%edx\n\t"
1746bfb9572SSean Christopherson 		"inb %%dx, %%al\n\t"
1759734b423SSean Christopherson 		"popf\n\t"
1769734b423SSean Christopherson 		"lea 1b,%0\n\t"
1779734b423SSean Christopherson 		: "=r" (start) : : "rax", "ebx", "ecx", "edx"
1789734b423SSean Christopherson 	);
1799734b423SSean Christopherson 	return start;
1809734b423SSean Christopherson }
1819734b423SSean Christopherson 
1829734b423SSean Christopherson int main(int ac, char **av)
1839734b423SSean Christopherson {
1842c6863b2SPaolo Bonzini 	unsigned long cr4;
185478027f5SJan Kiszka 
186478027f5SJan Kiszka 	handle_exception(DB_VECTOR, handle_db);
187478027f5SJan Kiszka 	handle_exception(BP_VECTOR, handle_bp);
1882c6863b2SPaolo Bonzini 	handle_exception(UD_VECTOR, handle_ud);
1892c6863b2SPaolo Bonzini 
1909734b423SSean Christopherson 	/*
1919734b423SSean Christopherson 	 * DR4 is an alias for DR6 (and DR5 aliases DR7) if CR4.DE is NOT set,
1929734b423SSean Christopherson 	 * and is reserved if CR4.DE=1 (Debug Extensions enabled).
1939734b423SSean Christopherson 	 */
1942c6863b2SPaolo Bonzini 	got_ud = 0;
1952c6863b2SPaolo Bonzini 	cr4 = read_cr4();
1962c6863b2SPaolo Bonzini 	write_cr4(cr4 & ~X86_CR4_DE);
1977f8f7356SKrish Sadhukhan 	write_dr4(0);
1987f8f7356SKrish Sadhukhan 	write_dr6(0xffff4ff2);
1997f8f7356SKrish Sadhukhan 	report(read_dr4() == 0xffff4ff2 && !got_ud, "reading DR4 with CR4.DE == 0");
2002c6863b2SPaolo Bonzini 
2012c6863b2SPaolo Bonzini 	cr4 = read_cr4();
2022c6863b2SPaolo Bonzini 	write_cr4(cr4 | X86_CR4_DE);
2037f8f7356SKrish Sadhukhan 	read_dr4();
2049734b423SSean Christopherson 	report(got_ud, "DR4 read got #UD with CR4.DE == 1");
2057f8f7356SKrish Sadhukhan 	write_dr6(0);
206478027f5SJan Kiszka 
207c68a7ff0SPaolo Bonzini 	extern unsigned char sw_bp;
208c68a7ff0SPaolo Bonzini 	asm volatile("int3; sw_bp:");
209a299895bSThomas Huth 	report(bp_addr == (unsigned long)&sw_bp, "#BP");
210478027f5SJan Kiszka 
2119734b423SSean Christopherson 	/*
2129734b423SSean Christopherson 	 * The CPU sets/clears bits 0-3 (trap bits for DR0-3) on #DB based on
2139734b423SSean Christopherson 	 * whether or not the corresponding DR0-3 got a match.  All other bits
2149734b423SSean Christopherson 	 * in DR6 are set if and only if their associated breakpoint condition
2159734b423SSean Christopherson 	 * is active, and are never cleared by the CPU.  Verify a match on DR0
2169734b423SSean Christopherson 	 * is reported correctly, and that DR6.BS is not set when single-step
2179734b423SSean Christopherson 	 * breakpoints are disabled, but is left set (if set by software).
2189734b423SSean Christopherson 	 */
2199e486280SPaolo Bonzini 	n = 0;
220c68a7ff0SPaolo Bonzini 	extern unsigned char hw_bp1;
2217f8f7356SKrish Sadhukhan 	write_dr0(&hw_bp1);
2227f8f7356SKrish Sadhukhan 	write_dr7(0x00000402);
223c68a7ff0SPaolo Bonzini 	asm volatile("hw_bp1: nop");
224a299895bSThomas Huth 	report(n == 1 &&
225a299895bSThomas Huth 	       db_addr[0] == ((unsigned long)&hw_bp1) && dr6[0] == 0xffff0ff1,
226a299895bSThomas Huth 	       "hw breakpoint (test that dr6.BS is not set)");
227478027f5SJan Kiszka 
228478027f5SJan Kiszka 	n = 0;
229c68a7ff0SPaolo Bonzini 	extern unsigned char hw_bp2;
2307f8f7356SKrish Sadhukhan 	write_dr0(&hw_bp2);
2317f8f7356SKrish Sadhukhan 	write_dr6(0x00004002);
232c68a7ff0SPaolo Bonzini 	asm volatile("hw_bp2: nop");
233a299895bSThomas Huth 	report(n == 1 &&
234a299895bSThomas Huth 	       db_addr[0] == ((unsigned long)&hw_bp2) && dr6[0] == 0xffff4ff1,
235a299895bSThomas Huth 	       "hw breakpoint (test that dr6.BS is not cleared)");
2369e486280SPaolo Bonzini 
2379734b423SSean Christopherson 	run_ss_db_test(singlestep_basic);
2389734b423SSean Christopherson 	run_ss_db_test(singlestep_emulated_instructions);
2390a982d78SKyle Huey 
240478027f5SJan Kiszka 	n = 0;
2417f8f7356SKrish Sadhukhan 	write_dr1((void *)&value);
2427f8f7356SKrish Sadhukhan 	write_dr7(0x00d0040a); // 4-byte write
243478027f5SJan Kiszka 
244c68a7ff0SPaolo Bonzini 	extern unsigned char hw_wp1;
245478027f5SJan Kiszka 	asm volatile(
246478027f5SJan Kiszka 		"mov $42,%%rax\n\t"
247c68a7ff0SPaolo Bonzini 		"mov %%rax,%0\n\t; hw_wp1:"
248478027f5SJan Kiszka 		: "=m" (value) : : "rax");
249a299895bSThomas Huth 	report(n == 1 &&
250a299895bSThomas Huth 	       db_addr[0] == ((unsigned long)&hw_wp1) && dr6[0] == 0xffff4ff2,
251a299895bSThomas Huth 	       "hw watchpoint (test that dr6.BS is not cleared)");
2529e486280SPaolo Bonzini 
2539e486280SPaolo Bonzini 	n = 0;
2547f8f7356SKrish Sadhukhan 	write_dr6(0);
2559e486280SPaolo Bonzini 
256c68a7ff0SPaolo Bonzini 	extern unsigned char hw_wp2;
2579e486280SPaolo Bonzini 	asm volatile(
2589e486280SPaolo Bonzini 		"mov $42,%%rax\n\t"
259c68a7ff0SPaolo Bonzini 		"mov %%rax,%0\n\t; hw_wp2:"
2609e486280SPaolo Bonzini 		: "=m" (value) : : "rax");
261a299895bSThomas Huth 	report(n == 1 &&
262a299895bSThomas Huth 	       db_addr[0] == ((unsigned long)&hw_wp2) && dr6[0] == 0xffff0ff2,
263a299895bSThomas Huth 	       "hw watchpoint (test that dr6.BS is not set)");
2649e486280SPaolo Bonzini 
2659e486280SPaolo Bonzini 	n = 0;
2667f8f7356SKrish Sadhukhan 	write_dr6(0);
267c68a7ff0SPaolo Bonzini 	extern unsigned char sw_icebp;
268c68a7ff0SPaolo Bonzini 	asm volatile(".byte 0xf1; sw_icebp:");
269a299895bSThomas Huth 	report(n == 1 &&
270a299895bSThomas Huth 	       db_addr[0] == (unsigned long)&sw_icebp && dr6[0] == 0xffff0ff0,
271a299895bSThomas Huth 	       "icebp");
272478027f5SJan Kiszka 
2737f8f7356SKrish Sadhukhan 	write_dr7(0x400);
274a094abddSPaolo Bonzini 	value = KERNEL_DS;
2757f8f7356SKrish Sadhukhan 	write_dr7(0x00f0040a); // 4-byte read or write
276a094abddSPaolo Bonzini 
277a094abddSPaolo Bonzini 	/*
278a094abddSPaolo Bonzini 	 * Each invocation of the handler should shift n by 1 and set bit 0 to 1.
279a094abddSPaolo Bonzini 	 * We expect a single invocation, so n should become 3.  If the entry
280a094abddSPaolo Bonzini 	 * RIP is wrong, or if the handler is executed more than once, the value
281a094abddSPaolo Bonzini 	 * will not match.
282a094abddSPaolo Bonzini 	 */
283a094abddSPaolo Bonzini 	set_idt_entry(1, &handle_db_save_rip, 0);
284a094abddSPaolo Bonzini 
285a094abddSPaolo Bonzini 	n = 1;
286a094abddSPaolo Bonzini 	asm volatile(
287a094abddSPaolo Bonzini 		"clc\n\t"
288a094abddSPaolo Bonzini 		"mov %0,%%ss\n\t"
289a094abddSPaolo Bonzini 		".byte 0x2e, 0x2e, 0xf1"
290a094abddSPaolo Bonzini 		: "=m" (value) : : "rax");
291a299895bSThomas Huth 	report(n == 3, "MOV SS + watchpoint + ICEBP");
292a094abddSPaolo Bonzini 
293a094abddSPaolo Bonzini 	/*
294a094abddSPaolo Bonzini 	 * Here the #DB handler is invoked twice, once as a software exception
295a094abddSPaolo Bonzini 	 * and once as a software interrupt.
296a094abddSPaolo Bonzini 	 */
297a094abddSPaolo Bonzini 	n = 1;
298a094abddSPaolo Bonzini 	asm volatile(
299a094abddSPaolo Bonzini 		"clc\n\t"
300a094abddSPaolo Bonzini 		"mov %0,%%ss\n\t"
301a094abddSPaolo Bonzini 		"int $1"
302a094abddSPaolo Bonzini 		: "=m" (value) : : "rax");
303a299895bSThomas Huth 	report(n == 7, "MOV SS + watchpoint + int $1");
304a094abddSPaolo Bonzini 
305a094abddSPaolo Bonzini 	/*
306a094abddSPaolo Bonzini 	 * Here the #DB and #BP handlers are invoked once each.
307a094abddSPaolo Bonzini 	 */
308a094abddSPaolo Bonzini 	n = 1;
309a094abddSPaolo Bonzini 	bp_addr = 0;
310a094abddSPaolo Bonzini 	asm volatile(
311a094abddSPaolo Bonzini 		"mov %0,%%ss\n\t"
312a094abddSPaolo Bonzini 		".byte 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0xcc\n\t"
313a094abddSPaolo Bonzini 		"sw_bp2:"
314a094abddSPaolo Bonzini 		: "=m" (value) : : "rax");
315a094abddSPaolo Bonzini 	extern unsigned char sw_bp2;
316a299895bSThomas Huth 	report(n == 3 && bp_addr == (unsigned long)&sw_bp2,
317a299895bSThomas Huth 	       "MOV SS + watchpoint + INT3");
31886065ca2SAndrew Jones 	return report_summary();
319478027f5SJan Kiszka }
320