xref: /kvm-unit-tests/x86/debug.c (revision dca3f4c041143c8e8dc70c6890a19a5730310230)
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 
13*8d9218bbSSean Christopherson #include "atomic.h"
14478027f5SJan Kiszka #include "libcflat.h"
152b934609SXiaoyao Li #include "processor.h"
16478027f5SJan Kiszka #include "desc.h"
17b8a4530dSSean Christopherson #include "usermode.h"
18478027f5SJan Kiszka 
19230db53fSPaolo Bonzini static volatile unsigned long bp_addr;
20230db53fSPaolo Bonzini static volatile unsigned long db_addr[10], dr6[10];
21478027f5SJan Kiszka static volatile unsigned int n;
22478027f5SJan Kiszka static volatile unsigned long value;
23478027f5SJan Kiszka 
write_dr4(ulong val)247f8f7356SKrish Sadhukhan static inline void write_dr4(ulong val)
252c6863b2SPaolo Bonzini {
267f8f7356SKrish Sadhukhan     asm volatile ("mov %0, %%dr4" : : "r"(val) : "memory");
272c6863b2SPaolo Bonzini }
282c6863b2SPaolo Bonzini 
read_dr4(void)297f8f7356SKrish Sadhukhan static inline ulong read_dr4(void)
30478027f5SJan Kiszka {
317f8f7356SKrish Sadhukhan     ulong val;
327f8f7356SKrish Sadhukhan     asm volatile ("mov %%dr4, %0" : "=r"(val));
337f8f7356SKrish Sadhukhan     return val;
34478027f5SJan Kiszka }
35478027f5SJan Kiszka 
handle_db(struct ex_regs * regs)36478027f5SJan Kiszka static void handle_db(struct ex_regs *regs)
37478027f5SJan Kiszka {
38230db53fSPaolo Bonzini 	db_addr[n] = regs->rip;
397f8f7356SKrish Sadhukhan 	dr6[n] = read_dr6();
40478027f5SJan Kiszka 
41478027f5SJan Kiszka 	if (dr6[n] & 0x1)
429734b423SSean Christopherson 		regs->rflags |= X86_EFLAGS_RF;
43478027f5SJan Kiszka 
44478027f5SJan Kiszka 	if (++n >= 10) {
459734b423SSean Christopherson 		regs->rflags &= ~X86_EFLAGS_TF;
467f8f7356SKrish Sadhukhan 		write_dr7(0x00000400);
47478027f5SJan Kiszka 	}
48478027f5SJan Kiszka }
49478027f5SJan Kiszka 
is_single_step_db(unsigned long dr6_val)509734b423SSean Christopherson static inline bool is_single_step_db(unsigned long dr6_val)
519734b423SSean Christopherson {
52f1dcfd54SSean Christopherson 	return dr6_val == (DR6_ACTIVE_LOW | DR6_BS);
539734b423SSean Christopherson }
549734b423SSean Christopherson 
is_general_detect_db(unsigned long dr6_val)55bc0dd8bdSSean Christopherson static inline bool is_general_detect_db(unsigned long dr6_val)
56bc0dd8bdSSean Christopherson {
57bc0dd8bdSSean Christopherson 	return dr6_val == (DR6_ACTIVE_LOW | DR6_BD);
58bc0dd8bdSSean Christopherson }
59bc0dd8bdSSean Christopherson 
is_icebp_db(unsigned long dr6_val)609734b423SSean Christopherson static inline bool is_icebp_db(unsigned long dr6_val)
619734b423SSean Christopherson {
62f1dcfd54SSean Christopherson 	return dr6_val == DR6_ACTIVE_LOW;
639734b423SSean Christopherson }
649734b423SSean Christopherson 
65a094abddSPaolo Bonzini extern unsigned char handle_db_save_rip;
66a094abddSPaolo Bonzini asm("handle_db_save_rip:\n"
67a094abddSPaolo Bonzini    "stc\n"
68a094abddSPaolo Bonzini    "nop;nop;nop\n"
69a094abddSPaolo Bonzini    "rclq $1, n(%rip)\n"
70a094abddSPaolo Bonzini    "iretq\n");
71a094abddSPaolo Bonzini 
handle_bp(struct ex_regs * regs)72478027f5SJan Kiszka static void handle_bp(struct ex_regs *regs)
73478027f5SJan Kiszka {
74230db53fSPaolo Bonzini 	bp_addr = regs->rip;
75478027f5SJan Kiszka }
76478027f5SJan Kiszka 
772c6863b2SPaolo Bonzini bool got_ud;
handle_ud(struct ex_regs * regs)782c6863b2SPaolo Bonzini static void handle_ud(struct ex_regs *regs)
792c6863b2SPaolo Bonzini {
802c6863b2SPaolo Bonzini 	unsigned long cr4 = read_cr4();
812c6863b2SPaolo Bonzini 	write_cr4(cr4 & ~X86_CR4_DE);
822c6863b2SPaolo Bonzini 	got_ud = 1;
832c6863b2SPaolo Bonzini }
842c6863b2SPaolo Bonzini 
85*8d9218bbSSean Christopherson static bool got_ac;
handle_ac(struct ex_regs * regs)86*8d9218bbSSean Christopherson static void handle_ac(struct ex_regs *regs)
87*8d9218bbSSean Christopherson {
88*8d9218bbSSean Christopherson 	got_ac = true;
89*8d9218bbSSean Christopherson }
90*8d9218bbSSean Christopherson 
919734b423SSean Christopherson typedef unsigned long (*db_test_fn)(void);
92b8a4530dSSean Christopherson typedef void (*db_report_fn)(unsigned long, const char *);
939734b423SSean Christopherson 
94bc0dd8bdSSean Christopherson static unsigned long singlestep_with_movss_blocking_and_dr7_gd(void);
95302fbd56SPaolo Bonzini static unsigned long singlestep_with_sti_hlt(void);
96bc0dd8bdSSean Christopherson 
__run_single_step_db_test(db_test_fn test,db_report_fn report_fn)979734b423SSean Christopherson static void __run_single_step_db_test(db_test_fn test, db_report_fn report_fn)
98478027f5SJan Kiszka {
99478027f5SJan Kiszka 	unsigned long start;
100b8a4530dSSean Christopherson 	bool ign;
1019734b423SSean Christopherson 
1029734b423SSean Christopherson 	n = 0;
1039734b423SSean Christopherson 	write_dr6(0);
1049734b423SSean Christopherson 
1059734b423SSean Christopherson 	start = test();
106b8a4530dSSean Christopherson 	report_fn(start, "");
107b8a4530dSSean Christopherson 
108302fbd56SPaolo Bonzini 	/*
109302fbd56SPaolo Bonzini 	 * MOV DR #GPs at CPL>0, don't try to run the DR7.GD test in usermode.
110302fbd56SPaolo Bonzini 	 * Likewise for HLT.
111302fbd56SPaolo Bonzini 	 */
112302fbd56SPaolo Bonzini 	if (test == singlestep_with_movss_blocking_and_dr7_gd
113302fbd56SPaolo Bonzini 	    || test == singlestep_with_sti_hlt)
114bc0dd8bdSSean Christopherson 		return;
115bc0dd8bdSSean Christopherson 
116b8a4530dSSean Christopherson 	n = 0;
117b8a4530dSSean Christopherson 	write_dr6(0);
118bc0dd8bdSSean Christopherson 
119b8a4530dSSean Christopherson 	/*
120b8a4530dSSean Christopherson 	 * Run the test in usermode.  Use the expected start RIP from the first
121b8a4530dSSean Christopherson 	 * run, the usermode framework doesn't make it easy to get the expected
122b8a4530dSSean Christopherson 	 * RIP out of the test, and it shouldn't change in any case.  Run the
123b8a4530dSSean Christopherson 	 * test with IOPL=3 so that it can use OUT, CLI, STI, etc...
124b8a4530dSSean Christopherson 	 */
125b8a4530dSSean Christopherson 	set_iopl(3);
126b8a4530dSSean Christopherson 	run_in_user((usermode_func)test, GP_VECTOR, 0, 0, 0, 0, &ign);
127b8a4530dSSean Christopherson 	set_iopl(0);
128b8a4530dSSean Christopherson 
129b8a4530dSSean Christopherson 	report_fn(start, "Usermode ");
1309734b423SSean Christopherson }
1319734b423SSean Christopherson 
1329734b423SSean Christopherson #define run_ss_db_test(name) __run_single_step_db_test(name, report_##name)
1339734b423SSean Christopherson 
report_singlestep_basic(unsigned long start,const char * usermode)134b8a4530dSSean Christopherson static void report_singlestep_basic(unsigned long start, const char *usermode)
1359734b423SSean Christopherson {
1369734b423SSean Christopherson 	report(n == 3 &&
1379734b423SSean Christopherson 	       is_single_step_db(dr6[0]) && db_addr[0] == start &&
1389734b423SSean Christopherson 	       is_single_step_db(dr6[1]) && db_addr[1] == start + 1 &&
1399734b423SSean Christopherson 	       is_single_step_db(dr6[2]) && db_addr[2] == start + 1 + 1,
140b8a4530dSSean Christopherson 	       "%sSingle-step #DB basic test", usermode);
1419734b423SSean Christopherson }
1429734b423SSean Christopherson 
singlestep_basic(void)14320de8c3bSAaron Lewis static noinline unsigned long singlestep_basic(void)
1449734b423SSean Christopherson {
1459734b423SSean Christopherson 	unsigned long start;
1469734b423SSean Christopherson 
1479734b423SSean Christopherson 	/*
1489734b423SSean Christopherson 	 * After being enabled, single-step breakpoints have a one instruction
1499734b423SSean Christopherson 	 * delay before the first #DB is generated.
1509734b423SSean Christopherson 	 */
1519734b423SSean Christopherson 	asm volatile (
1529734b423SSean Christopherson 		"pushf\n\t"
1539734b423SSean Christopherson 		"pop %%rax\n\t"
1549734b423SSean Christopherson 		"or $(1<<8),%%rax\n\t"
1559734b423SSean Christopherson 		"push %%rax\n\t"
1569734b423SSean Christopherson 		"popf\n\t"
1579734b423SSean Christopherson 		"and $~(1<<8),%%rax\n\t"
1589734b423SSean Christopherson 		"1:push %%rax\n\t"
1599734b423SSean Christopherson 		"popf\n\t"
160cc6931d3SZhenzhong Duan 		"lea 1b(%%rip), %0\n\t"
1619734b423SSean Christopherson 		: "=r" (start) : : "rax"
1629734b423SSean Christopherson 	);
1639734b423SSean Christopherson 	return start;
1649734b423SSean Christopherson }
1659734b423SSean Christopherson 
report_singlestep_emulated_instructions(unsigned long start,const char * usermode)166b8a4530dSSean Christopherson static void report_singlestep_emulated_instructions(unsigned long start,
167b8a4530dSSean Christopherson 						    const char *usermode)
1689734b423SSean Christopherson {
1699734b423SSean Christopherson 	report(n == 7 &&
1709734b423SSean Christopherson 	       is_single_step_db(dr6[0]) && db_addr[0] == start &&
1719734b423SSean Christopherson 	       is_single_step_db(dr6[1]) && db_addr[1] == start + 1 &&
1729734b423SSean Christopherson 	       is_single_step_db(dr6[2]) && db_addr[2] == start + 1 + 3 &&
1739734b423SSean Christopherson 	       is_single_step_db(dr6[3]) && db_addr[3] == start + 1 + 3 + 2 &&
1749734b423SSean Christopherson 	       is_single_step_db(dr6[4]) && db_addr[4] == start + 1 + 3 + 2 + 5 &&
1756bfb9572SSean Christopherson 	       is_single_step_db(dr6[5]) && db_addr[5] == start + 1 + 3 + 2 + 5 + 1 &&
1766bfb9572SSean Christopherson 	       is_single_step_db(dr6[6]) && db_addr[6] == start + 1 + 3 + 2 + 5 + 1 + 1,
177b8a4530dSSean Christopherson 	       "%sSingle-step #DB on emulated instructions", usermode);
1789734b423SSean Christopherson }
1799734b423SSean Christopherson 
singlestep_emulated_instructions(void)18020de8c3bSAaron Lewis static noinline unsigned long singlestep_emulated_instructions(void)
1819734b423SSean Christopherson {
1829734b423SSean Christopherson 	unsigned long start;
1839734b423SSean Christopherson 
1849734b423SSean Christopherson 	/*
1859734b423SSean Christopherson 	 * Verify single-step #DB are generated correctly on emulated
1869734b423SSean Christopherson 	 * instructions, e.g. CPUID and RDMSR.
1879734b423SSean Christopherson 	 */
1889734b423SSean Christopherson 	asm volatile (
1899734b423SSean Christopherson 		"pushf\n\t"
1909734b423SSean Christopherson 		"pop %%rax\n\t"
1919734b423SSean Christopherson 		"or $(1<<8),%%rax\n\t"
1929734b423SSean Christopherson 		"push %%rax\n\t"
1939734b423SSean Christopherson 		"popf\n\t"
1949734b423SSean Christopherson 		"and $~(1<<8),%%rax\n\t"
1959734b423SSean Christopherson 		"1:push %%rax\n\t"
1969734b423SSean Christopherson 		"xor %%rax,%%rax\n\t"
1979734b423SSean Christopherson 		"cpuid\n\t"
1986bfb9572SSean Christopherson 		"movl $0x3fd, %%edx\n\t"
1996bfb9572SSean Christopherson 		"inb %%dx, %%al\n\t"
2009734b423SSean Christopherson 		"popf\n\t"
201cc6931d3SZhenzhong Duan 		"lea 1b(%%rip),%0\n\t"
2029734b423SSean Christopherson 		: "=r" (start) : : "rax", "ebx", "ecx", "edx"
2039734b423SSean Christopherson 	);
2049734b423SSean Christopherson 	return start;
2059734b423SSean Christopherson }
2069734b423SSean Christopherson 
report_singlestep_with_sti_blocking(unsigned long start,const char * usermode)207bc0dd8bdSSean Christopherson static void report_singlestep_with_sti_blocking(unsigned long start,
208bc0dd8bdSSean Christopherson 						const char *usermode)
209bc0dd8bdSSean Christopherson {
210bc0dd8bdSSean Christopherson 	report(n == 4 &&
211bc0dd8bdSSean Christopherson 	       is_single_step_db(dr6[0]) && db_addr[0] == start &&
212bc0dd8bdSSean Christopherson 	       is_single_step_db(dr6[1]) && db_addr[1] == start + 6 &&
213bc0dd8bdSSean Christopherson 	       is_single_step_db(dr6[2]) && db_addr[2] == start + 6 + 1 &&
214bc0dd8bdSSean Christopherson 	       is_single_step_db(dr6[3]) && db_addr[3] == start + 6 + 1 + 1,
215bc0dd8bdSSean Christopherson 	       "%sSingle-step #DB w/ STI blocking", usermode);
216bc0dd8bdSSean Christopherson }
217bc0dd8bdSSean Christopherson 
218bc0dd8bdSSean Christopherson 
singlestep_with_sti_blocking(void)21920de8c3bSAaron Lewis static noinline unsigned long singlestep_with_sti_blocking(void)
220bc0dd8bdSSean Christopherson {
221bc0dd8bdSSean Christopherson 	unsigned long start_rip;
222bc0dd8bdSSean Christopherson 
223bc0dd8bdSSean Christopherson 	/*
224bc0dd8bdSSean Christopherson 	 * STI blocking doesn't suppress #DBs, thus the first single-step #DB
225bc0dd8bdSSean Christopherson 	 * should arrive after the standard one instruction delay.
226bc0dd8bdSSean Christopherson 	 */
227bc0dd8bdSSean Christopherson 	asm volatile(
228bc0dd8bdSSean Christopherson 		"cli\n\t"
229bc0dd8bdSSean Christopherson 		"pushf\n\t"
230bc0dd8bdSSean Christopherson 		"pop %%rax\n\t"
231bc0dd8bdSSean Christopherson 		"or $(1<<8),%%rax\n\t"
232bc0dd8bdSSean Christopherson 		"push %%rax\n\t"
233bc0dd8bdSSean Christopherson 		"popf\n\t"
234bc0dd8bdSSean Christopherson 		"sti\n\t"
235bc0dd8bdSSean Christopherson 		"1:and $~(1<<8),%%rax\n\t"
236bc0dd8bdSSean Christopherson 		"push %%rax\n\t"
237bc0dd8bdSSean Christopherson 		"popf\n\t"
238cc6931d3SZhenzhong Duan 		"lea 1b(%%rip),%0\n\t"
239bc0dd8bdSSean Christopherson 		: "=r" (start_rip) : : "rax"
240bc0dd8bdSSean Christopherson 	);
241bc0dd8bdSSean Christopherson 	return start_rip;
242bc0dd8bdSSean Christopherson }
243bc0dd8bdSSean Christopherson 
report_singlestep_with_movss_blocking(unsigned long start,const char * usermode)244bc0dd8bdSSean Christopherson static void report_singlestep_with_movss_blocking(unsigned long start,
245bc0dd8bdSSean Christopherson 						  const char *usermode)
246bc0dd8bdSSean Christopherson {
247bc0dd8bdSSean Christopherson 	report(n == 3 &&
248bc0dd8bdSSean Christopherson 	       is_single_step_db(dr6[0]) && db_addr[0] == start &&
249bc0dd8bdSSean Christopherson 	       is_single_step_db(dr6[1]) && db_addr[1] == start + 1 &&
250bc0dd8bdSSean Christopherson 	       is_single_step_db(dr6[2]) && db_addr[2] == start + 1 + 1,
251bc0dd8bdSSean Christopherson 	       "%sSingle-step #DB w/ MOVSS blocking", usermode);
252bc0dd8bdSSean Christopherson }
253bc0dd8bdSSean Christopherson 
singlestep_with_movss_blocking(void)25420de8c3bSAaron Lewis static noinline unsigned long singlestep_with_movss_blocking(void)
255bc0dd8bdSSean Christopherson {
256bc0dd8bdSSean Christopherson 	unsigned long start_rip;
257bc0dd8bdSSean Christopherson 
258bc0dd8bdSSean Christopherson 	/*
259bc0dd8bdSSean Christopherson 	 * MOVSS blocking suppresses single-step #DBs (and select other #DBs),
260bc0dd8bdSSean Christopherson 	 * thus the first single-step #DB should occur after MOVSS blocking
261bc0dd8bdSSean Christopherson 	 * expires, i.e. two instructions after #DBs are enabled in this case.
262bc0dd8bdSSean Christopherson 	 */
263bc0dd8bdSSean Christopherson 	asm volatile(
264bc0dd8bdSSean Christopherson 		"pushf\n\t"
265bc0dd8bdSSean Christopherson 		"pop %%rax\n\t"
266bc0dd8bdSSean Christopherson 		"or $(1<<8),%%rax\n\t"
267bc0dd8bdSSean Christopherson 		"push %%rax\n\t"
268bc0dd8bdSSean Christopherson 		"mov %%ss, %%ax\n\t"
269bc0dd8bdSSean Christopherson 		"popf\n\t"
270bc0dd8bdSSean Christopherson 		"mov %%ax, %%ss\n\t"
271bc0dd8bdSSean Christopherson 		"and $~(1<<8),%%rax\n\t"
272bc0dd8bdSSean Christopherson 		"1: push %%rax\n\t"
273bc0dd8bdSSean Christopherson 		"popf\n\t"
274cc6931d3SZhenzhong Duan 		"lea 1b(%%rip),%0\n\t"
275bc0dd8bdSSean Christopherson 		: "=r" (start_rip) : : "rax"
276bc0dd8bdSSean Christopherson 	);
277bc0dd8bdSSean Christopherson 	return start_rip;
278bc0dd8bdSSean Christopherson }
279bc0dd8bdSSean Christopherson 
280bc0dd8bdSSean Christopherson 
report_singlestep_with_movss_blocking_and_icebp(unsigned long start,const char * usermode)281bc0dd8bdSSean Christopherson static void report_singlestep_with_movss_blocking_and_icebp(unsigned long start,
282bc0dd8bdSSean Christopherson 							    const char *usermode)
283bc0dd8bdSSean Christopherson {
284bc0dd8bdSSean Christopherson 	report(n == 4 &&
285bc0dd8bdSSean Christopherson 	       is_icebp_db(dr6[0]) && db_addr[0] == start &&
286bc0dd8bdSSean Christopherson 	       is_single_step_db(dr6[1]) && db_addr[1] == start + 6 &&
287bc0dd8bdSSean Christopherson 	       is_single_step_db(dr6[2]) && db_addr[2] == start + 6 + 1 &&
288bc0dd8bdSSean Christopherson 	       is_single_step_db(dr6[3]) && db_addr[3] == start + 6 + 1 + 1,
289bc0dd8bdSSean Christopherson 	       "%sSingle-Step + ICEBP #DB w/ MOVSS blocking", usermode);
290bc0dd8bdSSean Christopherson }
291bc0dd8bdSSean Christopherson 
singlestep_with_movss_blocking_and_icebp(void)29220de8c3bSAaron Lewis static noinline unsigned long singlestep_with_movss_blocking_and_icebp(void)
293bc0dd8bdSSean Christopherson {
294bc0dd8bdSSean Christopherson 	unsigned long start;
295bc0dd8bdSSean Christopherson 
296bc0dd8bdSSean Christopherson 	/*
297bc0dd8bdSSean Christopherson 	 * ICEBP, a.k.a. INT1 or int1icebrk, is an oddball.  It generates a
298bc0dd8bdSSean Christopherson 	 * trap-like #DB, is intercepted if #DBs are intercepted, and manifests
299bc0dd8bdSSean Christopherson 	 * as a #DB VM-Exit, but the VM-Exit occurs on the ICEBP itself, i.e.
300bc0dd8bdSSean Christopherson 	 * it's treated as an instruction intercept.  Verify that ICEBP is
301bc0dd8bdSSean Christopherson 	 * correctly emulated as a trap-like #DB when intercepted, and that
302bc0dd8bdSSean Christopherson 	 * MOVSS blocking is handled correctly with respect to single-step
303bc0dd8bdSSean Christopherson 	 * breakpoints being enabled.
304bc0dd8bdSSean Christopherson 	 */
305bc0dd8bdSSean Christopherson 	asm volatile(
306bc0dd8bdSSean Christopherson 		"pushf\n\t"
307bc0dd8bdSSean Christopherson 		"pop %%rax\n\t"
308bc0dd8bdSSean Christopherson 		"or $(1<<8),%%rax\n\t"
309bc0dd8bdSSean Christopherson 		"push %%rax\n\t"
310bc0dd8bdSSean Christopherson 		"mov %%ss, %%ax\n\t"
311bc0dd8bdSSean Christopherson 		"popf\n\t"
312bc0dd8bdSSean Christopherson 		"mov %%ax, %%ss\n\t"
313bc0dd8bdSSean Christopherson 		".byte 0xf1;"
314bc0dd8bdSSean Christopherson 		"1:and $~(1<<8),%%rax\n\t"
315bc0dd8bdSSean Christopherson 		"push %%rax\n\t"
316bc0dd8bdSSean Christopherson 		"popf\n\t"
317cc6931d3SZhenzhong Duan 		"lea 1b(%%rip),%0\n\t"
318bc0dd8bdSSean Christopherson 		: "=r" (start) : : "rax"
319bc0dd8bdSSean Christopherson 	);
320bc0dd8bdSSean Christopherson 	return start;
321bc0dd8bdSSean Christopherson }
322bc0dd8bdSSean Christopherson 
report_singlestep_with_movss_blocking_and_dr7_gd(unsigned long start,const char * ign)323bc0dd8bdSSean Christopherson static void report_singlestep_with_movss_blocking_and_dr7_gd(unsigned long start,
324bc0dd8bdSSean Christopherson 							     const char *ign)
325bc0dd8bdSSean Christopherson {
326bc0dd8bdSSean Christopherson 	report(n == 5 &&
327bc0dd8bdSSean Christopherson 	       is_general_detect_db(dr6[0]) && db_addr[0] == start &&
328bc0dd8bdSSean Christopherson 	       is_single_step_db(dr6[1]) && db_addr[1] == start + 3 &&
329bc0dd8bdSSean Christopherson 	       is_single_step_db(dr6[2]) && db_addr[2] == start + 3 + 6 &&
330bc0dd8bdSSean Christopherson 	       is_single_step_db(dr6[3]) && db_addr[3] == start + 3 + 6 + 1 &&
331bc0dd8bdSSean Christopherson 	       is_single_step_db(dr6[4]) && db_addr[4] == start + 3 + 6 + 1 + 1,
332bc0dd8bdSSean Christopherson 	       "Single-step #DB w/ MOVSS blocking and DR7.GD=1");
333bc0dd8bdSSean Christopherson }
334bc0dd8bdSSean Christopherson 
singlestep_with_movss_blocking_and_dr7_gd(void)33520de8c3bSAaron Lewis static noinline unsigned long singlestep_with_movss_blocking_and_dr7_gd(void)
336bc0dd8bdSSean Christopherson {
337bc0dd8bdSSean Christopherson 	unsigned long start_rip;
338bc0dd8bdSSean Christopherson 
339bc0dd8bdSSean Christopherson 	write_dr7(DR7_GD);
340bc0dd8bdSSean Christopherson 
341bc0dd8bdSSean Christopherson 	/*
342bc0dd8bdSSean Christopherson 	 * MOVSS blocking does NOT suppress General Detect #DBs, which have
343bc0dd8bdSSean Christopherson 	 * fault-like behavior.  Note, DR7.GD is cleared by the CPU upon
344bc0dd8bdSSean Christopherson 	 * successful delivery of the #DB.  DR6.BD is NOT cleared by the CPU,
345bc0dd8bdSSean Christopherson 	 * but the MOV DR6 below will be re-executed after handling the
346bc0dd8bdSSean Christopherson 	 * General Detect #DB.
347bc0dd8bdSSean Christopherson 	 */
348bc0dd8bdSSean Christopherson 	asm volatile(
349bc0dd8bdSSean Christopherson 		"xor %0, %0\n\t"
350bc0dd8bdSSean Christopherson 		"pushf\n\t"
351bc0dd8bdSSean Christopherson 		"pop %%rax\n\t"
352bc0dd8bdSSean Christopherson 		"or $(1<<8),%%rax\n\t"
353bc0dd8bdSSean Christopherson 		"push %%rax\n\t"
354bc0dd8bdSSean Christopherson 		"mov %%ss, %%ax\n\t"
355bc0dd8bdSSean Christopherson 		"popf\n\t"
356bc0dd8bdSSean Christopherson 		"mov %%ax, %%ss\n\t"
357bc0dd8bdSSean Christopherson 		"1: mov %0, %%dr6\n\t"
358bc0dd8bdSSean Christopherson 		"and $~(1<<8),%%rax\n\t"
359bc0dd8bdSSean Christopherson 		"push %%rax\n\t"
360bc0dd8bdSSean Christopherson 		"popf\n\t"
361cc6931d3SZhenzhong Duan 		"lea 1b(%%rip),%0\n\t"
362bc0dd8bdSSean Christopherson 		: "=r" (start_rip) : : "rax"
363bc0dd8bdSSean Christopherson 	);
364bc0dd8bdSSean Christopherson 	return start_rip;
365bc0dd8bdSSean Christopherson }
366bc0dd8bdSSean Christopherson 
report_singlestep_with_sti_hlt(unsigned long start,const char * usermode)367302fbd56SPaolo Bonzini static void report_singlestep_with_sti_hlt(unsigned long start,
368302fbd56SPaolo Bonzini 						const char *usermode)
369302fbd56SPaolo Bonzini {
370302fbd56SPaolo Bonzini 	report(n == 5 &&
371302fbd56SPaolo Bonzini 	       is_single_step_db(dr6[0]) && db_addr[0] == start &&
372302fbd56SPaolo Bonzini 	       is_single_step_db(dr6[1]) && db_addr[1] == start + 1 &&
373302fbd56SPaolo Bonzini 	       is_single_step_db(dr6[2]) && db_addr[2] == start + 1 + 6 &&
374302fbd56SPaolo Bonzini 	       is_single_step_db(dr6[3]) && db_addr[3] == start + 1 + 6 + 1 &&
375302fbd56SPaolo Bonzini 	       is_single_step_db(dr6[4]) && db_addr[4] == start + 1 + 6 + 1 + 1,
376302fbd56SPaolo Bonzini 	       "%sSingle-step #DB w/ STI;HLT", usermode);
377302fbd56SPaolo Bonzini }
378302fbd56SPaolo Bonzini 
379302fbd56SPaolo Bonzini #define APIC_LVT_TIMER_VECTOR    (0xee)
380302fbd56SPaolo Bonzini 
lvtt_handler(isr_regs_t * regs)381302fbd56SPaolo Bonzini static void lvtt_handler(isr_regs_t *regs)
382302fbd56SPaolo Bonzini {
383302fbd56SPaolo Bonzini         eoi();
384302fbd56SPaolo Bonzini }
385302fbd56SPaolo Bonzini 
singlestep_with_sti_hlt(void)386302fbd56SPaolo Bonzini static noinline unsigned long singlestep_with_sti_hlt(void)
387302fbd56SPaolo Bonzini {
388302fbd56SPaolo Bonzini 	unsigned long start_rip;
389302fbd56SPaolo Bonzini 
390302fbd56SPaolo Bonzini 	cli();
391302fbd56SPaolo Bonzini 
392302fbd56SPaolo Bonzini 	handle_irq(APIC_LVT_TIMER_VECTOR, lvtt_handler);
393302fbd56SPaolo Bonzini 	apic_write(APIC_LVTT, APIC_LVT_TIMER_ONESHOT |
394302fbd56SPaolo Bonzini 		   APIC_LVT_TIMER_VECTOR);
395302fbd56SPaolo Bonzini 	apic_write(APIC_TDCR, 0x0000000b);
396302fbd56SPaolo Bonzini 	apic_write(APIC_TMICT, 1000000);
397302fbd56SPaolo Bonzini 
398302fbd56SPaolo Bonzini 	/*
399302fbd56SPaolo Bonzini 	 * STI blocking doesn't suppress #DBs, thus the first single-step #DB
400302fbd56SPaolo Bonzini 	 * should arrive after the standard one instruction delay.
401302fbd56SPaolo Bonzini 	 */
402302fbd56SPaolo Bonzini 	asm volatile(
403302fbd56SPaolo Bonzini 		"pushf\n\t"
404302fbd56SPaolo Bonzini 		"pop %%rax\n\t"
405302fbd56SPaolo Bonzini 		"or $(1<<8),%%rax\n\t"
406302fbd56SPaolo Bonzini 		"push %%rax\n\t"
407302fbd56SPaolo Bonzini 		"popf\n\t"
408302fbd56SPaolo Bonzini 		"sti\n\t"
409302fbd56SPaolo Bonzini 		"1:hlt;\n\t"
410302fbd56SPaolo Bonzini 		"and $~(1<<8),%%rax\n\t"
411302fbd56SPaolo Bonzini 		"push %%rax\n\t"
412302fbd56SPaolo Bonzini 		"popf\n\t"
413302fbd56SPaolo Bonzini 		"lea 1b(%%rip),%0\n\t"
414302fbd56SPaolo Bonzini 		: "=r" (start_rip) : : "rax"
415302fbd56SPaolo Bonzini 	);
416302fbd56SPaolo Bonzini 	return start_rip;
417302fbd56SPaolo Bonzini }
418302fbd56SPaolo Bonzini 
bus_lock(uint64_t magic)419*8d9218bbSSean Christopherson static noinline uint64_t bus_lock(uint64_t magic)
420*8d9218bbSSean Christopherson {
421*8d9218bbSSean Christopherson 	uint8_t buffer[128] __attribute__((aligned(64))) = { };
422*8d9218bbSSean Christopherson 	atomic64_t *val = (atomic64_t *)&buffer[60];
423*8d9218bbSSean Christopherson 
424*8d9218bbSSean Christopherson 	atomic64_cmpxchg(val, 0, magic);
425*8d9218bbSSean Christopherson 	return READ_ONCE(*(uint64_t *)val);
426*8d9218bbSSean Christopherson }
427*8d9218bbSSean Christopherson 
bus_lock_test(void)428*8d9218bbSSean Christopherson static void bus_lock_test(void)
429*8d9218bbSSean Christopherson {
430*8d9218bbSSean Christopherson 	const uint64_t magic = 0xdeadbeefdeadbeefull;
431*8d9218bbSSean Christopherson 	bool bus_lock_db = false;
432*8d9218bbSSean Christopherson 	uint64_t val;
433*8d9218bbSSean Christopherson 
434*8d9218bbSSean Christopherson 	/*
435*8d9218bbSSean Christopherson 	 * Generate a bus lock (via a locked access that splits cache lines)
436*8d9218bbSSean Christopherson 	 * in CPL0 and again in CPL3 (Bus Lock Detect only affects CPL3), and
437*8d9218bbSSean Christopherson 	 * verify that no #AC or #DB is generated (the relevant features are
438*8d9218bbSSean Christopherson 	 * not enabled).
439*8d9218bbSSean Christopherson 	 */
440*8d9218bbSSean Christopherson 	val = bus_lock(magic);
441*8d9218bbSSean Christopherson 	report(!got_ac && !n && val == magic,
442*8d9218bbSSean Christopherson 	       "CPL0 Split Lock #AC = %u (#DB = %u), val = %lx (wanted %lx)",
443*8d9218bbSSean Christopherson 	       got_ac, n, val, magic);
444*8d9218bbSSean Christopherson 
445*8d9218bbSSean Christopherson 	val = run_in_user((usermode_func)bus_lock, DB_VECTOR, magic, 0, 0, 0, &bus_lock_db);
446*8d9218bbSSean Christopherson 	report(!bus_lock_db && val == magic,
447*8d9218bbSSean Christopherson 	       "CPL3 Bus Lock #DB = %u, val = %lx (wanted %lx)",
448*8d9218bbSSean Christopherson 	       bus_lock_db, val, magic);
449*8d9218bbSSean Christopherson 
450*8d9218bbSSean Christopherson 	n = 0;
451*8d9218bbSSean Christopherson 	got_ac = false;
452*8d9218bbSSean Christopherson }
453*8d9218bbSSean Christopherson 
main(int ac,char ** av)4549734b423SSean Christopherson int main(int ac, char **av)
4559734b423SSean Christopherson {
4562c6863b2SPaolo Bonzini 	unsigned long cr4;
457478027f5SJan Kiszka 
458478027f5SJan Kiszka 	handle_exception(DB_VECTOR, handle_db);
459478027f5SJan Kiszka 	handle_exception(BP_VECTOR, handle_bp);
4602c6863b2SPaolo Bonzini 	handle_exception(UD_VECTOR, handle_ud);
461*8d9218bbSSean Christopherson 	handle_exception(AC_VECTOR, handle_ac);
462*8d9218bbSSean Christopherson 
463*8d9218bbSSean Christopherson 	bus_lock_test();
4642c6863b2SPaolo Bonzini 
4659734b423SSean Christopherson 	/*
4669734b423SSean Christopherson 	 * DR4 is an alias for DR6 (and DR5 aliases DR7) if CR4.DE is NOT set,
4679734b423SSean Christopherson 	 * and is reserved if CR4.DE=1 (Debug Extensions enabled).
4689734b423SSean Christopherson 	 */
4692c6863b2SPaolo Bonzini 	got_ud = 0;
4702c6863b2SPaolo Bonzini 	cr4 = read_cr4();
4712c6863b2SPaolo Bonzini 	write_cr4(cr4 & ~X86_CR4_DE);
4727f8f7356SKrish Sadhukhan 	write_dr4(0);
473f1dcfd54SSean Christopherson 	write_dr6(DR6_ACTIVE_LOW | DR6_BS | DR6_TRAP1);
474f1dcfd54SSean Christopherson 	report(read_dr4() == (DR6_ACTIVE_LOW | DR6_BS | DR6_TRAP1) && !got_ud,
475f1dcfd54SSean Christopherson 	       "DR4==DR6 with CR4.DE == 0");
4762c6863b2SPaolo Bonzini 
4772c6863b2SPaolo Bonzini 	cr4 = read_cr4();
4782c6863b2SPaolo Bonzini 	write_cr4(cr4 | X86_CR4_DE);
4797f8f7356SKrish Sadhukhan 	read_dr4();
4809734b423SSean Christopherson 	report(got_ud, "DR4 read got #UD with CR4.DE == 1");
4817f8f7356SKrish Sadhukhan 	write_dr6(0);
482478027f5SJan Kiszka 
483c68a7ff0SPaolo Bonzini 	extern unsigned char sw_bp;
484c68a7ff0SPaolo Bonzini 	asm volatile("int3; sw_bp:");
485a299895bSThomas Huth 	report(bp_addr == (unsigned long)&sw_bp, "#BP");
486478027f5SJan Kiszka 
4879734b423SSean Christopherson 	/*
4889734b423SSean Christopherson 	 * The CPU sets/clears bits 0-3 (trap bits for DR0-3) on #DB based on
4899734b423SSean Christopherson 	 * whether or not the corresponding DR0-3 got a match.  All other bits
4909734b423SSean Christopherson 	 * in DR6 are set if and only if their associated breakpoint condition
4919734b423SSean Christopherson 	 * is active, and are never cleared by the CPU.  Verify a match on DR0
4929734b423SSean Christopherson 	 * is reported correctly, and that DR6.BS is not set when single-step
4939734b423SSean Christopherson 	 * breakpoints are disabled, but is left set (if set by software).
4949734b423SSean Christopherson 	 */
4959e486280SPaolo Bonzini 	n = 0;
496c68a7ff0SPaolo Bonzini 	extern unsigned char hw_bp1;
4977f8f7356SKrish Sadhukhan 	write_dr0(&hw_bp1);
498f1dcfd54SSean Christopherson 	write_dr7(DR7_FIXED_1 | DR7_GLOBAL_ENABLE_DR0);
499c68a7ff0SPaolo Bonzini 	asm volatile("hw_bp1: nop");
500a299895bSThomas Huth 	report(n == 1 &&
501f1dcfd54SSean Christopherson 	       db_addr[0] == ((unsigned long)&hw_bp1) &&
502f1dcfd54SSean Christopherson 	       dr6[0] == (DR6_ACTIVE_LOW | DR6_TRAP0),
503a299895bSThomas Huth 	       "hw breakpoint (test that dr6.BS is not set)");
504478027f5SJan Kiszka 
505478027f5SJan Kiszka 	n = 0;
506c68a7ff0SPaolo Bonzini 	extern unsigned char hw_bp2;
5077f8f7356SKrish Sadhukhan 	write_dr0(&hw_bp2);
508f1dcfd54SSean Christopherson 	write_dr6(DR6_BS | DR6_TRAP1);
509c68a7ff0SPaolo Bonzini 	asm volatile("hw_bp2: nop");
510a299895bSThomas Huth 	report(n == 1 &&
511f1dcfd54SSean Christopherson 	       db_addr[0] == ((unsigned long)&hw_bp2) &&
512f1dcfd54SSean Christopherson 	       dr6[0] == (DR6_ACTIVE_LOW | DR6_BS | DR6_TRAP0),
513a299895bSThomas Huth 	       "hw breakpoint (test that dr6.BS is not cleared)");
5149e486280SPaolo Bonzini 
5159734b423SSean Christopherson 	run_ss_db_test(singlestep_basic);
5169734b423SSean Christopherson 	run_ss_db_test(singlestep_emulated_instructions);
517bc0dd8bdSSean Christopherson 	run_ss_db_test(singlestep_with_sti_blocking);
518bc0dd8bdSSean Christopherson 	run_ss_db_test(singlestep_with_movss_blocking);
519bc0dd8bdSSean Christopherson 	run_ss_db_test(singlestep_with_movss_blocking_and_icebp);
520bc0dd8bdSSean Christopherson 	run_ss_db_test(singlestep_with_movss_blocking_and_dr7_gd);
521302fbd56SPaolo Bonzini 	run_ss_db_test(singlestep_with_sti_hlt);
5220a982d78SKyle Huey 
523478027f5SJan Kiszka 	n = 0;
5247f8f7356SKrish Sadhukhan 	write_dr1((void *)&value);
5254879ca8bSSean Christopherson 	write_dr6(DR6_BS);
5267f8f7356SKrish Sadhukhan 	write_dr7(0x00d0040a); // 4-byte write
527478027f5SJan Kiszka 
528c68a7ff0SPaolo Bonzini 	extern unsigned char hw_wp1;
529478027f5SJan Kiszka 	asm volatile(
530478027f5SJan Kiszka 		"mov $42,%%rax\n\t"
531c68a7ff0SPaolo Bonzini 		"mov %%rax,%0\n\t; hw_wp1:"
532478027f5SJan Kiszka 		: "=m" (value) : : "rax");
533a299895bSThomas Huth 	report(n == 1 &&
534f1dcfd54SSean Christopherson 	       db_addr[0] == ((unsigned long)&hw_wp1) &&
535f1dcfd54SSean Christopherson 	       dr6[0] == (DR6_ACTIVE_LOW | DR6_BS | DR6_TRAP1),
536a299895bSThomas Huth 	       "hw watchpoint (test that dr6.BS is not cleared)");
5379e486280SPaolo Bonzini 
5389e486280SPaolo Bonzini 	n = 0;
5397f8f7356SKrish Sadhukhan 	write_dr6(0);
5409e486280SPaolo Bonzini 
541c68a7ff0SPaolo Bonzini 	extern unsigned char hw_wp2;
5429e486280SPaolo Bonzini 	asm volatile(
5439e486280SPaolo Bonzini 		"mov $42,%%rax\n\t"
544c68a7ff0SPaolo Bonzini 		"mov %%rax,%0\n\t; hw_wp2:"
5459e486280SPaolo Bonzini 		: "=m" (value) : : "rax");
546a299895bSThomas Huth 	report(n == 1 &&
547f1dcfd54SSean Christopherson 	       db_addr[0] == ((unsigned long)&hw_wp2) &&
548f1dcfd54SSean Christopherson 	       dr6[0] == (DR6_ACTIVE_LOW | DR6_TRAP1),
549a299895bSThomas Huth 	       "hw watchpoint (test that dr6.BS is not set)");
5509e486280SPaolo Bonzini 
5519e486280SPaolo Bonzini 	n = 0;
5527f8f7356SKrish Sadhukhan 	write_dr6(0);
553c68a7ff0SPaolo Bonzini 	extern unsigned char sw_icebp;
554c68a7ff0SPaolo Bonzini 	asm volatile(".byte 0xf1; sw_icebp:");
555a299895bSThomas Huth 	report(n == 1 &&
556f1dcfd54SSean Christopherson 	       db_addr[0] == (unsigned long)&sw_icebp && dr6[0] == DR6_ACTIVE_LOW,
557a299895bSThomas Huth 	       "icebp");
558478027f5SJan Kiszka 
5597f8f7356SKrish Sadhukhan 	write_dr7(0x400);
560a094abddSPaolo Bonzini 	value = KERNEL_DS;
5617f8f7356SKrish Sadhukhan 	write_dr7(0x00f0040a); // 4-byte read or write
562a094abddSPaolo Bonzini 
563a094abddSPaolo Bonzini 	/*
564a094abddSPaolo Bonzini 	 * Each invocation of the handler should shift n by 1 and set bit 0 to 1.
565a094abddSPaolo Bonzini 	 * We expect a single invocation, so n should become 3.  If the entry
566a094abddSPaolo Bonzini 	 * RIP is wrong, or if the handler is executed more than once, the value
567a094abddSPaolo Bonzini 	 * will not match.
568a094abddSPaolo Bonzini 	 */
569a094abddSPaolo Bonzini 	set_idt_entry(1, &handle_db_save_rip, 0);
570a094abddSPaolo Bonzini 
571a094abddSPaolo Bonzini 	n = 1;
572a094abddSPaolo Bonzini 	asm volatile(
573a094abddSPaolo Bonzini 		"clc\n\t"
574a094abddSPaolo Bonzini 		"mov %0,%%ss\n\t"
575a094abddSPaolo Bonzini 		".byte 0x2e, 0x2e, 0xf1"
576a094abddSPaolo Bonzini 		: "=m" (value) : : "rax");
577a299895bSThomas Huth 	report(n == 3, "MOV SS + watchpoint + ICEBP");
578a094abddSPaolo Bonzini 
579a094abddSPaolo Bonzini 	/*
580a094abddSPaolo Bonzini 	 * Here the #DB handler is invoked twice, once as a software exception
581a094abddSPaolo Bonzini 	 * and once as a software interrupt.
582a094abddSPaolo Bonzini 	 */
583a094abddSPaolo Bonzini 	n = 1;
584a094abddSPaolo Bonzini 	asm volatile(
585a094abddSPaolo Bonzini 		"clc\n\t"
586a094abddSPaolo Bonzini 		"mov %0,%%ss\n\t"
587a094abddSPaolo Bonzini 		"int $1"
588a094abddSPaolo Bonzini 		: "=m" (value) : : "rax");
589a299895bSThomas Huth 	report(n == 7, "MOV SS + watchpoint + int $1");
590a094abddSPaolo Bonzini 
591a094abddSPaolo Bonzini 	/*
592a094abddSPaolo Bonzini 	 * Here the #DB and #BP handlers are invoked once each.
593a094abddSPaolo Bonzini 	 */
594a094abddSPaolo Bonzini 	n = 1;
595a094abddSPaolo Bonzini 	bp_addr = 0;
596a094abddSPaolo Bonzini 	asm volatile(
597a094abddSPaolo Bonzini 		"mov %0,%%ss\n\t"
598a094abddSPaolo Bonzini 		".byte 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0xcc\n\t"
599a094abddSPaolo Bonzini 		"sw_bp2:"
600a094abddSPaolo Bonzini 		: "=m" (value) : : "rax");
601a094abddSPaolo Bonzini 	extern unsigned char sw_bp2;
602a299895bSThomas Huth 	report(n == 3 && bp_addr == (unsigned long)&sw_bp2,
603a299895bSThomas Huth 	       "MOV SS + watchpoint + INT3");
60486065ca2SAndrew Jones 	return report_summary();
605478027f5SJan Kiszka }
606