xref: /kvm-unit-tests/x86/debug.c (revision cc6931d350dd761c2b56996c2e01fba617f82a47)
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  */
11f1dcfd54SSean Christopherson #include <asm/debugreg.h>
12478027f5SJan Kiszka 
13478027f5SJan Kiszka #include "libcflat.h"
142b934609SXiaoyao Li #include "processor.h"
15478027f5SJan Kiszka #include "desc.h"
16b8a4530dSSean Christopherson #include "usermode.h"
17478027f5SJan Kiszka 
18230db53fSPaolo Bonzini static volatile unsigned long bp_addr;
19230db53fSPaolo Bonzini static volatile unsigned long db_addr[10], dr6[10];
20478027f5SJan Kiszka static volatile unsigned int n;
21478027f5SJan Kiszka static volatile unsigned long value;
22478027f5SJan Kiszka 
237f8f7356SKrish Sadhukhan static inline void write_dr4(ulong val)
242c6863b2SPaolo Bonzini {
257f8f7356SKrish Sadhukhan     asm volatile ("mov %0, %%dr4" : : "r"(val) : "memory");
262c6863b2SPaolo Bonzini }
272c6863b2SPaolo Bonzini 
287f8f7356SKrish Sadhukhan static inline ulong read_dr4(void)
29478027f5SJan Kiszka {
307f8f7356SKrish Sadhukhan     ulong val;
317f8f7356SKrish Sadhukhan     asm volatile ("mov %%dr4, %0" : "=r"(val));
327f8f7356SKrish Sadhukhan     return val;
33478027f5SJan Kiszka }
34478027f5SJan Kiszka 
35478027f5SJan Kiszka static void handle_db(struct ex_regs *regs)
36478027f5SJan Kiszka {
37230db53fSPaolo Bonzini 	db_addr[n] = regs->rip;
387f8f7356SKrish Sadhukhan 	dr6[n] = read_dr6();
39478027f5SJan Kiszka 
40478027f5SJan Kiszka 	if (dr6[n] & 0x1)
419734b423SSean Christopherson 		regs->rflags |= X86_EFLAGS_RF;
42478027f5SJan Kiszka 
43478027f5SJan Kiszka 	if (++n >= 10) {
449734b423SSean Christopherson 		regs->rflags &= ~X86_EFLAGS_TF;
457f8f7356SKrish Sadhukhan 		write_dr7(0x00000400);
46478027f5SJan Kiszka 	}
47478027f5SJan Kiszka }
48478027f5SJan Kiszka 
499734b423SSean Christopherson static inline bool is_single_step_db(unsigned long dr6_val)
509734b423SSean Christopherson {
51f1dcfd54SSean Christopherson 	return dr6_val == (DR6_ACTIVE_LOW | DR6_BS);
529734b423SSean Christopherson }
539734b423SSean Christopherson 
54bc0dd8bdSSean Christopherson static inline bool is_general_detect_db(unsigned long dr6_val)
55bc0dd8bdSSean Christopherson {
56bc0dd8bdSSean Christopherson 	return dr6_val == (DR6_ACTIVE_LOW | DR6_BD);
57bc0dd8bdSSean Christopherson }
58bc0dd8bdSSean Christopherson 
599734b423SSean Christopherson static inline bool is_icebp_db(unsigned long dr6_val)
609734b423SSean Christopherson {
61f1dcfd54SSean Christopherson 	return dr6_val == DR6_ACTIVE_LOW;
629734b423SSean Christopherson }
639734b423SSean Christopherson 
64a094abddSPaolo Bonzini extern unsigned char handle_db_save_rip;
65a094abddSPaolo Bonzini asm("handle_db_save_rip:\n"
66a094abddSPaolo Bonzini    "stc\n"
67a094abddSPaolo Bonzini    "nop;nop;nop\n"
68a094abddSPaolo Bonzini    "rclq $1, n(%rip)\n"
69a094abddSPaolo Bonzini    "iretq\n");
70a094abddSPaolo Bonzini 
71478027f5SJan Kiszka static void handle_bp(struct ex_regs *regs)
72478027f5SJan Kiszka {
73230db53fSPaolo Bonzini 	bp_addr = regs->rip;
74478027f5SJan Kiszka }
75478027f5SJan Kiszka 
762c6863b2SPaolo Bonzini bool got_ud;
772c6863b2SPaolo Bonzini static void handle_ud(struct ex_regs *regs)
782c6863b2SPaolo Bonzini {
792c6863b2SPaolo Bonzini 	unsigned long cr4 = read_cr4();
802c6863b2SPaolo Bonzini 	write_cr4(cr4 & ~X86_CR4_DE);
812c6863b2SPaolo Bonzini 	got_ud = 1;
822c6863b2SPaolo Bonzini }
832c6863b2SPaolo Bonzini 
849734b423SSean Christopherson typedef unsigned long (*db_test_fn)(void);
85b8a4530dSSean Christopherson typedef void (*db_report_fn)(unsigned long, const char *);
869734b423SSean Christopherson 
87bc0dd8bdSSean Christopherson static unsigned long singlestep_with_movss_blocking_and_dr7_gd(void);
88bc0dd8bdSSean Christopherson 
899734b423SSean Christopherson static void __run_single_step_db_test(db_test_fn test, db_report_fn report_fn)
90478027f5SJan Kiszka {
91478027f5SJan Kiszka 	unsigned long start;
92b8a4530dSSean Christopherson 	bool ign;
939734b423SSean Christopherson 
949734b423SSean Christopherson 	n = 0;
959734b423SSean Christopherson 	write_dr6(0);
969734b423SSean Christopherson 
979734b423SSean Christopherson 	start = test();
98b8a4530dSSean Christopherson 	report_fn(start, "");
99b8a4530dSSean Christopherson 
100bc0dd8bdSSean Christopherson 	/* MOV DR #GPs at CPL>0, don't try to run the DR7.GD test in usermode. */
101bc0dd8bdSSean Christopherson 	if (test == singlestep_with_movss_blocking_and_dr7_gd)
102bc0dd8bdSSean Christopherson 		return;
103bc0dd8bdSSean Christopherson 
104b8a4530dSSean Christopherson 	n = 0;
105b8a4530dSSean Christopherson 	write_dr6(0);
106bc0dd8bdSSean Christopherson 
107b8a4530dSSean Christopherson 	/*
108b8a4530dSSean Christopherson 	 * Run the test in usermode.  Use the expected start RIP from the first
109b8a4530dSSean Christopherson 	 * run, the usermode framework doesn't make it easy to get the expected
110b8a4530dSSean Christopherson 	 * RIP out of the test, and it shouldn't change in any case.  Run the
111b8a4530dSSean Christopherson 	 * test with IOPL=3 so that it can use OUT, CLI, STI, etc...
112b8a4530dSSean Christopherson 	 */
113b8a4530dSSean Christopherson 	set_iopl(3);
114b8a4530dSSean Christopherson 	run_in_user((usermode_func)test, GP_VECTOR, 0, 0, 0, 0, &ign);
115b8a4530dSSean Christopherson 	set_iopl(0);
116b8a4530dSSean Christopherson 
117b8a4530dSSean Christopherson 	report_fn(start, "Usermode ");
1189734b423SSean Christopherson }
1199734b423SSean Christopherson 
1209734b423SSean Christopherson #define run_ss_db_test(name) __run_single_step_db_test(name, report_##name)
1219734b423SSean Christopherson 
122b8a4530dSSean Christopherson static void report_singlestep_basic(unsigned long start, const char *usermode)
1239734b423SSean Christopherson {
1249734b423SSean Christopherson 	report(n == 3 &&
1259734b423SSean Christopherson 	       is_single_step_db(dr6[0]) && db_addr[0] == start &&
1269734b423SSean Christopherson 	       is_single_step_db(dr6[1]) && db_addr[1] == start + 1 &&
1279734b423SSean Christopherson 	       is_single_step_db(dr6[2]) && db_addr[2] == start + 1 + 1,
128b8a4530dSSean Christopherson 	       "%sSingle-step #DB basic test", usermode);
1299734b423SSean Christopherson }
1309734b423SSean Christopherson 
1319734b423SSean Christopherson static unsigned long singlestep_basic(void)
1329734b423SSean Christopherson {
1339734b423SSean Christopherson 	unsigned long start;
1349734b423SSean Christopherson 
1359734b423SSean Christopherson 	/*
1369734b423SSean Christopherson 	 * After being enabled, single-step breakpoints have a one instruction
1379734b423SSean Christopherson 	 * delay before the first #DB is generated.
1389734b423SSean Christopherson 	 */
1399734b423SSean Christopherson 	asm volatile (
1409734b423SSean Christopherson 		"pushf\n\t"
1419734b423SSean Christopherson 		"pop %%rax\n\t"
1429734b423SSean Christopherson 		"or $(1<<8),%%rax\n\t"
1439734b423SSean Christopherson 		"push %%rax\n\t"
1449734b423SSean Christopherson 		"popf\n\t"
1459734b423SSean Christopherson 		"and $~(1<<8),%%rax\n\t"
1469734b423SSean Christopherson 		"1:push %%rax\n\t"
1479734b423SSean Christopherson 		"popf\n\t"
148*cc6931d3SZhenzhong Duan 		"lea 1b(%%rip), %0\n\t"
1499734b423SSean Christopherson 		: "=r" (start) : : "rax"
1509734b423SSean Christopherson 	);
1519734b423SSean Christopherson 	return start;
1529734b423SSean Christopherson }
1539734b423SSean Christopherson 
154b8a4530dSSean Christopherson static void report_singlestep_emulated_instructions(unsigned long start,
155b8a4530dSSean Christopherson 						    const char *usermode)
1569734b423SSean Christopherson {
1579734b423SSean Christopherson 	report(n == 7 &&
1589734b423SSean Christopherson 	       is_single_step_db(dr6[0]) && db_addr[0] == start &&
1599734b423SSean Christopherson 	       is_single_step_db(dr6[1]) && db_addr[1] == start + 1 &&
1609734b423SSean Christopherson 	       is_single_step_db(dr6[2]) && db_addr[2] == start + 1 + 3 &&
1619734b423SSean Christopherson 	       is_single_step_db(dr6[3]) && db_addr[3] == start + 1 + 3 + 2 &&
1629734b423SSean Christopherson 	       is_single_step_db(dr6[4]) && db_addr[4] == start + 1 + 3 + 2 + 5 &&
1636bfb9572SSean Christopherson 	       is_single_step_db(dr6[5]) && db_addr[5] == start + 1 + 3 + 2 + 5 + 1 &&
1646bfb9572SSean Christopherson 	       is_single_step_db(dr6[6]) && db_addr[6] == start + 1 + 3 + 2 + 5 + 1 + 1,
165b8a4530dSSean Christopherson 	       "%sSingle-step #DB on emulated instructions", usermode);
1669734b423SSean Christopherson }
1679734b423SSean Christopherson 
1689734b423SSean Christopherson static unsigned long singlestep_emulated_instructions(void)
1699734b423SSean Christopherson {
1709734b423SSean Christopherson 	unsigned long start;
1719734b423SSean Christopherson 
1729734b423SSean Christopherson 	/*
1739734b423SSean Christopherson 	 * Verify single-step #DB are generated correctly on emulated
1749734b423SSean Christopherson 	 * instructions, e.g. CPUID and RDMSR.
1759734b423SSean Christopherson 	 */
1769734b423SSean Christopherson 	asm volatile (
1779734b423SSean Christopherson 		"pushf\n\t"
1789734b423SSean Christopherson 		"pop %%rax\n\t"
1799734b423SSean Christopherson 		"or $(1<<8),%%rax\n\t"
1809734b423SSean Christopherson 		"push %%rax\n\t"
1819734b423SSean Christopherson 		"popf\n\t"
1829734b423SSean Christopherson 		"and $~(1<<8),%%rax\n\t"
1839734b423SSean Christopherson 		"1:push %%rax\n\t"
1849734b423SSean Christopherson 		"xor %%rax,%%rax\n\t"
1859734b423SSean Christopherson 		"cpuid\n\t"
1866bfb9572SSean Christopherson 		"movl $0x3fd, %%edx\n\t"
1876bfb9572SSean Christopherson 		"inb %%dx, %%al\n\t"
1889734b423SSean Christopherson 		"popf\n\t"
189*cc6931d3SZhenzhong Duan 		"lea 1b(%%rip),%0\n\t"
1909734b423SSean Christopherson 		: "=r" (start) : : "rax", "ebx", "ecx", "edx"
1919734b423SSean Christopherson 	);
1929734b423SSean Christopherson 	return start;
1939734b423SSean Christopherson }
1949734b423SSean Christopherson 
195bc0dd8bdSSean Christopherson static void report_singlestep_with_sti_blocking(unsigned long start,
196bc0dd8bdSSean Christopherson 						const char *usermode)
197bc0dd8bdSSean Christopherson {
198bc0dd8bdSSean Christopherson 	report(n == 4 &&
199bc0dd8bdSSean Christopherson 	       is_single_step_db(dr6[0]) && db_addr[0] == start &&
200bc0dd8bdSSean Christopherson 	       is_single_step_db(dr6[1]) && db_addr[1] == start + 6 &&
201bc0dd8bdSSean Christopherson 	       is_single_step_db(dr6[2]) && db_addr[2] == start + 6 + 1 &&
202bc0dd8bdSSean Christopherson 	       is_single_step_db(dr6[3]) && db_addr[3] == start + 6 + 1 + 1,
203bc0dd8bdSSean Christopherson 	       "%sSingle-step #DB w/ STI blocking", usermode);
204bc0dd8bdSSean Christopherson }
205bc0dd8bdSSean Christopherson 
206bc0dd8bdSSean Christopherson 
207bc0dd8bdSSean Christopherson static unsigned long singlestep_with_sti_blocking(void)
208bc0dd8bdSSean Christopherson {
209bc0dd8bdSSean Christopherson 	unsigned long start_rip;
210bc0dd8bdSSean Christopherson 
211bc0dd8bdSSean Christopherson 	/*
212bc0dd8bdSSean Christopherson 	 * STI blocking doesn't suppress #DBs, thus the first single-step #DB
213bc0dd8bdSSean Christopherson 	 * should arrive after the standard one instruction delay.
214bc0dd8bdSSean Christopherson 	 */
215bc0dd8bdSSean Christopherson 	asm volatile(
216bc0dd8bdSSean Christopherson 		"cli\n\t"
217bc0dd8bdSSean Christopherson 		"pushf\n\t"
218bc0dd8bdSSean Christopherson 		"pop %%rax\n\t"
219bc0dd8bdSSean Christopherson 		"or $(1<<8),%%rax\n\t"
220bc0dd8bdSSean Christopherson 		"push %%rax\n\t"
221bc0dd8bdSSean Christopherson 		"popf\n\t"
222bc0dd8bdSSean Christopherson 		"sti\n\t"
223bc0dd8bdSSean Christopherson 		"1:and $~(1<<8),%%rax\n\t"
224bc0dd8bdSSean Christopherson 		"push %%rax\n\t"
225bc0dd8bdSSean Christopherson 		"popf\n\t"
226*cc6931d3SZhenzhong Duan 		"lea 1b(%%rip),%0\n\t"
227bc0dd8bdSSean Christopherson 		: "=r" (start_rip) : : "rax"
228bc0dd8bdSSean Christopherson 	);
229bc0dd8bdSSean Christopherson 	return start_rip;
230bc0dd8bdSSean Christopherson }
231bc0dd8bdSSean Christopherson 
232bc0dd8bdSSean Christopherson static void report_singlestep_with_movss_blocking(unsigned long start,
233bc0dd8bdSSean Christopherson 						  const char *usermode)
234bc0dd8bdSSean Christopherson {
235bc0dd8bdSSean Christopherson 	report(n == 3 &&
236bc0dd8bdSSean Christopherson 	       is_single_step_db(dr6[0]) && db_addr[0] == start &&
237bc0dd8bdSSean Christopherson 	       is_single_step_db(dr6[1]) && db_addr[1] == start + 1 &&
238bc0dd8bdSSean Christopherson 	       is_single_step_db(dr6[2]) && db_addr[2] == start + 1 + 1,
239bc0dd8bdSSean Christopherson 	       "%sSingle-step #DB w/ MOVSS blocking", usermode);
240bc0dd8bdSSean Christopherson }
241bc0dd8bdSSean Christopherson 
242bc0dd8bdSSean Christopherson static unsigned long singlestep_with_movss_blocking(void)
243bc0dd8bdSSean Christopherson {
244bc0dd8bdSSean Christopherson 	unsigned long start_rip;
245bc0dd8bdSSean Christopherson 
246bc0dd8bdSSean Christopherson 	/*
247bc0dd8bdSSean Christopherson 	 * MOVSS blocking suppresses single-step #DBs (and select other #DBs),
248bc0dd8bdSSean Christopherson 	 * thus the first single-step #DB should occur after MOVSS blocking
249bc0dd8bdSSean Christopherson 	 * expires, i.e. two instructions after #DBs are enabled in this case.
250bc0dd8bdSSean Christopherson 	 */
251bc0dd8bdSSean Christopherson 	asm volatile(
252bc0dd8bdSSean Christopherson 		"pushf\n\t"
253bc0dd8bdSSean Christopherson 		"pop %%rax\n\t"
254bc0dd8bdSSean Christopherson 		"or $(1<<8),%%rax\n\t"
255bc0dd8bdSSean Christopherson 		"push %%rax\n\t"
256bc0dd8bdSSean Christopherson 		"mov %%ss, %%ax\n\t"
257bc0dd8bdSSean Christopherson 		"popf\n\t"
258bc0dd8bdSSean Christopherson 		"mov %%ax, %%ss\n\t"
259bc0dd8bdSSean Christopherson 		"and $~(1<<8),%%rax\n\t"
260bc0dd8bdSSean Christopherson 		"1: push %%rax\n\t"
261bc0dd8bdSSean Christopherson 		"popf\n\t"
262*cc6931d3SZhenzhong Duan 		"lea 1b(%%rip),%0\n\t"
263bc0dd8bdSSean Christopherson 		: "=r" (start_rip) : : "rax"
264bc0dd8bdSSean Christopherson 	);
265bc0dd8bdSSean Christopherson 	return start_rip;
266bc0dd8bdSSean Christopherson }
267bc0dd8bdSSean Christopherson 
268bc0dd8bdSSean Christopherson 
269bc0dd8bdSSean Christopherson static void report_singlestep_with_movss_blocking_and_icebp(unsigned long start,
270bc0dd8bdSSean Christopherson 							    const char *usermode)
271bc0dd8bdSSean Christopherson {
272bc0dd8bdSSean Christopherson 	report(n == 4 &&
273bc0dd8bdSSean Christopherson 	       is_icebp_db(dr6[0]) && db_addr[0] == start &&
274bc0dd8bdSSean Christopherson 	       is_single_step_db(dr6[1]) && db_addr[1] == start + 6 &&
275bc0dd8bdSSean Christopherson 	       is_single_step_db(dr6[2]) && db_addr[2] == start + 6 + 1 &&
276bc0dd8bdSSean Christopherson 	       is_single_step_db(dr6[3]) && db_addr[3] == start + 6 + 1 + 1,
277bc0dd8bdSSean Christopherson 	       "%sSingle-Step + ICEBP #DB w/ MOVSS blocking", usermode);
278bc0dd8bdSSean Christopherson }
279bc0dd8bdSSean Christopherson 
280bc0dd8bdSSean Christopherson static unsigned long singlestep_with_movss_blocking_and_icebp(void)
281bc0dd8bdSSean Christopherson {
282bc0dd8bdSSean Christopherson 	unsigned long start;
283bc0dd8bdSSean Christopherson 
284bc0dd8bdSSean Christopherson 	/*
285bc0dd8bdSSean Christopherson 	 * ICEBP, a.k.a. INT1 or int1icebrk, is an oddball.  It generates a
286bc0dd8bdSSean Christopherson 	 * trap-like #DB, is intercepted if #DBs are intercepted, and manifests
287bc0dd8bdSSean Christopherson 	 * as a #DB VM-Exit, but the VM-Exit occurs on the ICEBP itself, i.e.
288bc0dd8bdSSean Christopherson 	 * it's treated as an instruction intercept.  Verify that ICEBP is
289bc0dd8bdSSean Christopherson 	 * correctly emulated as a trap-like #DB when intercepted, and that
290bc0dd8bdSSean Christopherson 	 * MOVSS blocking is handled correctly with respect to single-step
291bc0dd8bdSSean Christopherson 	 * breakpoints being enabled.
292bc0dd8bdSSean Christopherson 	 */
293bc0dd8bdSSean Christopherson 	asm volatile(
294bc0dd8bdSSean Christopherson 		"pushf\n\t"
295bc0dd8bdSSean Christopherson 		"pop %%rax\n\t"
296bc0dd8bdSSean Christopherson 		"or $(1<<8),%%rax\n\t"
297bc0dd8bdSSean Christopherson 		"push %%rax\n\t"
298bc0dd8bdSSean Christopherson 		"mov %%ss, %%ax\n\t"
299bc0dd8bdSSean Christopherson 		"popf\n\t"
300bc0dd8bdSSean Christopherson 		"mov %%ax, %%ss\n\t"
301bc0dd8bdSSean Christopherson 		".byte 0xf1;"
302bc0dd8bdSSean Christopherson 		"1:and $~(1<<8),%%rax\n\t"
303bc0dd8bdSSean Christopherson 		"push %%rax\n\t"
304bc0dd8bdSSean Christopherson 		"popf\n\t"
305*cc6931d3SZhenzhong Duan 		"lea 1b(%%rip),%0\n\t"
306bc0dd8bdSSean Christopherson 		: "=r" (start) : : "rax"
307bc0dd8bdSSean Christopherson 	);
308bc0dd8bdSSean Christopherson 	return start;
309bc0dd8bdSSean Christopherson }
310bc0dd8bdSSean Christopherson 
311bc0dd8bdSSean Christopherson static void report_singlestep_with_movss_blocking_and_dr7_gd(unsigned long start,
312bc0dd8bdSSean Christopherson 							     const char *ign)
313bc0dd8bdSSean Christopherson {
314bc0dd8bdSSean Christopherson 	report(n == 5 &&
315bc0dd8bdSSean Christopherson 	       is_general_detect_db(dr6[0]) && db_addr[0] == start &&
316bc0dd8bdSSean Christopherson 	       is_single_step_db(dr6[1]) && db_addr[1] == start + 3 &&
317bc0dd8bdSSean Christopherson 	       is_single_step_db(dr6[2]) && db_addr[2] == start + 3 + 6 &&
318bc0dd8bdSSean Christopherson 	       is_single_step_db(dr6[3]) && db_addr[3] == start + 3 + 6 + 1 &&
319bc0dd8bdSSean Christopherson 	       is_single_step_db(dr6[4]) && db_addr[4] == start + 3 + 6 + 1 + 1,
320bc0dd8bdSSean Christopherson 	       "Single-step #DB w/ MOVSS blocking and DR7.GD=1");
321bc0dd8bdSSean Christopherson }
322bc0dd8bdSSean Christopherson 
323bc0dd8bdSSean Christopherson static unsigned long singlestep_with_movss_blocking_and_dr7_gd(void)
324bc0dd8bdSSean Christopherson {
325bc0dd8bdSSean Christopherson 	unsigned long start_rip;
326bc0dd8bdSSean Christopherson 
327bc0dd8bdSSean Christopherson 	write_dr7(DR7_GD);
328bc0dd8bdSSean Christopherson 
329bc0dd8bdSSean Christopherson 	/*
330bc0dd8bdSSean Christopherson 	 * MOVSS blocking does NOT suppress General Detect #DBs, which have
331bc0dd8bdSSean Christopherson 	 * fault-like behavior.  Note, DR7.GD is cleared by the CPU upon
332bc0dd8bdSSean Christopherson 	 * successful delivery of the #DB.  DR6.BD is NOT cleared by the CPU,
333bc0dd8bdSSean Christopherson 	 * but the MOV DR6 below will be re-executed after handling the
334bc0dd8bdSSean Christopherson 	 * General Detect #DB.
335bc0dd8bdSSean Christopherson 	 */
336bc0dd8bdSSean Christopherson 	asm volatile(
337bc0dd8bdSSean Christopherson 		"xor %0, %0\n\t"
338bc0dd8bdSSean Christopherson 		"pushf\n\t"
339bc0dd8bdSSean Christopherson 		"pop %%rax\n\t"
340bc0dd8bdSSean Christopherson 		"or $(1<<8),%%rax\n\t"
341bc0dd8bdSSean Christopherson 		"push %%rax\n\t"
342bc0dd8bdSSean Christopherson 		"mov %%ss, %%ax\n\t"
343bc0dd8bdSSean Christopherson 		"popf\n\t"
344bc0dd8bdSSean Christopherson 		"mov %%ax, %%ss\n\t"
345bc0dd8bdSSean Christopherson 		"1: mov %0, %%dr6\n\t"
346bc0dd8bdSSean Christopherson 		"and $~(1<<8),%%rax\n\t"
347bc0dd8bdSSean Christopherson 		"push %%rax\n\t"
348bc0dd8bdSSean Christopherson 		"popf\n\t"
349*cc6931d3SZhenzhong Duan 		"lea 1b(%%rip),%0\n\t"
350bc0dd8bdSSean Christopherson 		: "=r" (start_rip) : : "rax"
351bc0dd8bdSSean Christopherson 	);
352bc0dd8bdSSean Christopherson 	return start_rip;
353bc0dd8bdSSean Christopherson }
354bc0dd8bdSSean Christopherson 
3559734b423SSean Christopherson int main(int ac, char **av)
3569734b423SSean Christopherson {
3572c6863b2SPaolo Bonzini 	unsigned long cr4;
358478027f5SJan Kiszka 
359478027f5SJan Kiszka 	handle_exception(DB_VECTOR, handle_db);
360478027f5SJan Kiszka 	handle_exception(BP_VECTOR, handle_bp);
3612c6863b2SPaolo Bonzini 	handle_exception(UD_VECTOR, handle_ud);
3622c6863b2SPaolo Bonzini 
3639734b423SSean Christopherson 	/*
3649734b423SSean Christopherson 	 * DR4 is an alias for DR6 (and DR5 aliases DR7) if CR4.DE is NOT set,
3659734b423SSean Christopherson 	 * and is reserved if CR4.DE=1 (Debug Extensions enabled).
3669734b423SSean Christopherson 	 */
3672c6863b2SPaolo Bonzini 	got_ud = 0;
3682c6863b2SPaolo Bonzini 	cr4 = read_cr4();
3692c6863b2SPaolo Bonzini 	write_cr4(cr4 & ~X86_CR4_DE);
3707f8f7356SKrish Sadhukhan 	write_dr4(0);
371f1dcfd54SSean Christopherson 	write_dr6(DR6_ACTIVE_LOW | DR6_BS | DR6_TRAP1);
372f1dcfd54SSean Christopherson 	report(read_dr4() == (DR6_ACTIVE_LOW | DR6_BS | DR6_TRAP1) && !got_ud,
373f1dcfd54SSean Christopherson 	       "DR4==DR6 with CR4.DE == 0");
3742c6863b2SPaolo Bonzini 
3752c6863b2SPaolo Bonzini 	cr4 = read_cr4();
3762c6863b2SPaolo Bonzini 	write_cr4(cr4 | X86_CR4_DE);
3777f8f7356SKrish Sadhukhan 	read_dr4();
3789734b423SSean Christopherson 	report(got_ud, "DR4 read got #UD with CR4.DE == 1");
3797f8f7356SKrish Sadhukhan 	write_dr6(0);
380478027f5SJan Kiszka 
381c68a7ff0SPaolo Bonzini 	extern unsigned char sw_bp;
382c68a7ff0SPaolo Bonzini 	asm volatile("int3; sw_bp:");
383a299895bSThomas Huth 	report(bp_addr == (unsigned long)&sw_bp, "#BP");
384478027f5SJan Kiszka 
3859734b423SSean Christopherson 	/*
3869734b423SSean Christopherson 	 * The CPU sets/clears bits 0-3 (trap bits for DR0-3) on #DB based on
3879734b423SSean Christopherson 	 * whether or not the corresponding DR0-3 got a match.  All other bits
3889734b423SSean Christopherson 	 * in DR6 are set if and only if their associated breakpoint condition
3899734b423SSean Christopherson 	 * is active, and are never cleared by the CPU.  Verify a match on DR0
3909734b423SSean Christopherson 	 * is reported correctly, and that DR6.BS is not set when single-step
3919734b423SSean Christopherson 	 * breakpoints are disabled, but is left set (if set by software).
3929734b423SSean Christopherson 	 */
3939e486280SPaolo Bonzini 	n = 0;
394c68a7ff0SPaolo Bonzini 	extern unsigned char hw_bp1;
3957f8f7356SKrish Sadhukhan 	write_dr0(&hw_bp1);
396f1dcfd54SSean Christopherson 	write_dr7(DR7_FIXED_1 | DR7_GLOBAL_ENABLE_DR0);
397c68a7ff0SPaolo Bonzini 	asm volatile("hw_bp1: nop");
398a299895bSThomas Huth 	report(n == 1 &&
399f1dcfd54SSean Christopherson 	       db_addr[0] == ((unsigned long)&hw_bp1) &&
400f1dcfd54SSean Christopherson 	       dr6[0] == (DR6_ACTIVE_LOW | DR6_TRAP0),
401a299895bSThomas Huth 	       "hw breakpoint (test that dr6.BS is not set)");
402478027f5SJan Kiszka 
403478027f5SJan Kiszka 	n = 0;
404c68a7ff0SPaolo Bonzini 	extern unsigned char hw_bp2;
4057f8f7356SKrish Sadhukhan 	write_dr0(&hw_bp2);
406f1dcfd54SSean Christopherson 	write_dr6(DR6_BS | DR6_TRAP1);
407c68a7ff0SPaolo Bonzini 	asm volatile("hw_bp2: nop");
408a299895bSThomas Huth 	report(n == 1 &&
409f1dcfd54SSean Christopherson 	       db_addr[0] == ((unsigned long)&hw_bp2) &&
410f1dcfd54SSean Christopherson 	       dr6[0] == (DR6_ACTIVE_LOW | DR6_BS | DR6_TRAP0),
411a299895bSThomas Huth 	       "hw breakpoint (test that dr6.BS is not cleared)");
4129e486280SPaolo Bonzini 
4139734b423SSean Christopherson 	run_ss_db_test(singlestep_basic);
4149734b423SSean Christopherson 	run_ss_db_test(singlestep_emulated_instructions);
415bc0dd8bdSSean Christopherson 	run_ss_db_test(singlestep_with_sti_blocking);
416bc0dd8bdSSean Christopherson 	run_ss_db_test(singlestep_with_movss_blocking);
417bc0dd8bdSSean Christopherson 	run_ss_db_test(singlestep_with_movss_blocking_and_icebp);
418bc0dd8bdSSean Christopherson 	run_ss_db_test(singlestep_with_movss_blocking_and_dr7_gd);
4190a982d78SKyle Huey 
420478027f5SJan Kiszka 	n = 0;
4217f8f7356SKrish Sadhukhan 	write_dr1((void *)&value);
4224879ca8bSSean Christopherson 	write_dr6(DR6_BS);
4237f8f7356SKrish Sadhukhan 	write_dr7(0x00d0040a); // 4-byte write
424478027f5SJan Kiszka 
425c68a7ff0SPaolo Bonzini 	extern unsigned char hw_wp1;
426478027f5SJan Kiszka 	asm volatile(
427478027f5SJan Kiszka 		"mov $42,%%rax\n\t"
428c68a7ff0SPaolo Bonzini 		"mov %%rax,%0\n\t; hw_wp1:"
429478027f5SJan Kiszka 		: "=m" (value) : : "rax");
430a299895bSThomas Huth 	report(n == 1 &&
431f1dcfd54SSean Christopherson 	       db_addr[0] == ((unsigned long)&hw_wp1) &&
432f1dcfd54SSean Christopherson 	       dr6[0] == (DR6_ACTIVE_LOW | DR6_BS | DR6_TRAP1),
433a299895bSThomas Huth 	       "hw watchpoint (test that dr6.BS is not cleared)");
4349e486280SPaolo Bonzini 
4359e486280SPaolo Bonzini 	n = 0;
4367f8f7356SKrish Sadhukhan 	write_dr6(0);
4379e486280SPaolo Bonzini 
438c68a7ff0SPaolo Bonzini 	extern unsigned char hw_wp2;
4399e486280SPaolo Bonzini 	asm volatile(
4409e486280SPaolo Bonzini 		"mov $42,%%rax\n\t"
441c68a7ff0SPaolo Bonzini 		"mov %%rax,%0\n\t; hw_wp2:"
4429e486280SPaolo Bonzini 		: "=m" (value) : : "rax");
443a299895bSThomas Huth 	report(n == 1 &&
444f1dcfd54SSean Christopherson 	       db_addr[0] == ((unsigned long)&hw_wp2) &&
445f1dcfd54SSean Christopherson 	       dr6[0] == (DR6_ACTIVE_LOW | DR6_TRAP1),
446a299895bSThomas Huth 	       "hw watchpoint (test that dr6.BS is not set)");
4479e486280SPaolo Bonzini 
4489e486280SPaolo Bonzini 	n = 0;
4497f8f7356SKrish Sadhukhan 	write_dr6(0);
450c68a7ff0SPaolo Bonzini 	extern unsigned char sw_icebp;
451c68a7ff0SPaolo Bonzini 	asm volatile(".byte 0xf1; sw_icebp:");
452a299895bSThomas Huth 	report(n == 1 &&
453f1dcfd54SSean Christopherson 	       db_addr[0] == (unsigned long)&sw_icebp && dr6[0] == DR6_ACTIVE_LOW,
454a299895bSThomas Huth 	       "icebp");
455478027f5SJan Kiszka 
4567f8f7356SKrish Sadhukhan 	write_dr7(0x400);
457a094abddSPaolo Bonzini 	value = KERNEL_DS;
4587f8f7356SKrish Sadhukhan 	write_dr7(0x00f0040a); // 4-byte read or write
459a094abddSPaolo Bonzini 
460a094abddSPaolo Bonzini 	/*
461a094abddSPaolo Bonzini 	 * Each invocation of the handler should shift n by 1 and set bit 0 to 1.
462a094abddSPaolo Bonzini 	 * We expect a single invocation, so n should become 3.  If the entry
463a094abddSPaolo Bonzini 	 * RIP is wrong, or if the handler is executed more than once, the value
464a094abddSPaolo Bonzini 	 * will not match.
465a094abddSPaolo Bonzini 	 */
466a094abddSPaolo Bonzini 	set_idt_entry(1, &handle_db_save_rip, 0);
467a094abddSPaolo Bonzini 
468a094abddSPaolo Bonzini 	n = 1;
469a094abddSPaolo Bonzini 	asm volatile(
470a094abddSPaolo Bonzini 		"clc\n\t"
471a094abddSPaolo Bonzini 		"mov %0,%%ss\n\t"
472a094abddSPaolo Bonzini 		".byte 0x2e, 0x2e, 0xf1"
473a094abddSPaolo Bonzini 		: "=m" (value) : : "rax");
474a299895bSThomas Huth 	report(n == 3, "MOV SS + watchpoint + ICEBP");
475a094abddSPaolo Bonzini 
476a094abddSPaolo Bonzini 	/*
477a094abddSPaolo Bonzini 	 * Here the #DB handler is invoked twice, once as a software exception
478a094abddSPaolo Bonzini 	 * and once as a software interrupt.
479a094abddSPaolo Bonzini 	 */
480a094abddSPaolo Bonzini 	n = 1;
481a094abddSPaolo Bonzini 	asm volatile(
482a094abddSPaolo Bonzini 		"clc\n\t"
483a094abddSPaolo Bonzini 		"mov %0,%%ss\n\t"
484a094abddSPaolo Bonzini 		"int $1"
485a094abddSPaolo Bonzini 		: "=m" (value) : : "rax");
486a299895bSThomas Huth 	report(n == 7, "MOV SS + watchpoint + int $1");
487a094abddSPaolo Bonzini 
488a094abddSPaolo Bonzini 	/*
489a094abddSPaolo Bonzini 	 * Here the #DB and #BP handlers are invoked once each.
490a094abddSPaolo Bonzini 	 */
491a094abddSPaolo Bonzini 	n = 1;
492a094abddSPaolo Bonzini 	bp_addr = 0;
493a094abddSPaolo Bonzini 	asm volatile(
494a094abddSPaolo Bonzini 		"mov %0,%%ss\n\t"
495a094abddSPaolo Bonzini 		".byte 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0xcc\n\t"
496a094abddSPaolo Bonzini 		"sw_bp2:"
497a094abddSPaolo Bonzini 		: "=m" (value) : : "rax");
498a094abddSPaolo Bonzini 	extern unsigned char sw_bp2;
499a299895bSThomas Huth 	report(n == 3 && bp_addr == (unsigned long)&sw_bp2,
500a299895bSThomas Huth 	       "MOV SS + watchpoint + INT3");
50186065ca2SAndrew Jones 	return report_summary();
502478027f5SJan Kiszka }
503