1 /*
2 * Test for x86 debugging facilities
3 *
4 * Copyright (c) Siemens AG, 2014
5 *
6 * Authors:
7 * Jan Kiszka <jan.kiszka@siemens.com>
8 *
9 * This work is licensed under the terms of the GNU GPL, version 2.
10 */
11 #include <asm/debugreg.h>
12
13 #include "atomic.h"
14 #include "libcflat.h"
15 #include "processor.h"
16 #include "desc.h"
17 #include "usermode.h"
18
19 static volatile unsigned long bp_addr;
20 static volatile unsigned long db_addr[10], dr6[10];
21 static volatile unsigned int n;
22 static volatile unsigned long value;
23
write_dr4(ulong val)24 static inline void write_dr4(ulong val)
25 {
26 asm volatile ("mov %0, %%dr4" : : "r"(val) : "memory");
27 }
28
read_dr4(void)29 static inline ulong read_dr4(void)
30 {
31 ulong val;
32 asm volatile ("mov %%dr4, %0" : "=r"(val));
33 return val;
34 }
35
handle_db(struct ex_regs * regs)36 static void handle_db(struct ex_regs *regs)
37 {
38 db_addr[n] = regs->rip;
39 dr6[n] = read_dr6();
40
41 if (dr6[n] & 0x1)
42 regs->rflags |= X86_EFLAGS_RF;
43
44 if (++n >= 10) {
45 regs->rflags &= ~X86_EFLAGS_TF;
46 write_dr7(0x00000400);
47 }
48 }
49
is_single_step_db(unsigned long dr6_val)50 static inline bool is_single_step_db(unsigned long dr6_val)
51 {
52 return dr6_val == (DR6_ACTIVE_LOW | DR6_BS);
53 }
54
is_general_detect_db(unsigned long dr6_val)55 static inline bool is_general_detect_db(unsigned long dr6_val)
56 {
57 return dr6_val == (DR6_ACTIVE_LOW | DR6_BD);
58 }
59
is_icebp_db(unsigned long dr6_val)60 static inline bool is_icebp_db(unsigned long dr6_val)
61 {
62 return dr6_val == DR6_ACTIVE_LOW;
63 }
64
65 extern unsigned char handle_db_save_rip;
66 asm("handle_db_save_rip:\n"
67 "stc\n"
68 "nop;nop;nop\n"
69 "rclq $1, n(%rip)\n"
70 "iretq\n");
71
handle_bp(struct ex_regs * regs)72 static void handle_bp(struct ex_regs *regs)
73 {
74 bp_addr = regs->rip;
75 }
76
77 bool got_ud;
handle_ud(struct ex_regs * regs)78 static void handle_ud(struct ex_regs *regs)
79 {
80 unsigned long cr4 = read_cr4();
81 write_cr4(cr4 & ~X86_CR4_DE);
82 got_ud = 1;
83 }
84
85 static bool got_ac;
handle_ac(struct ex_regs * regs)86 static void handle_ac(struct ex_regs *regs)
87 {
88 got_ac = true;
89 }
90
91 typedef unsigned long (*db_test_fn)(void);
92 typedef void (*db_report_fn)(unsigned long, const char *);
93
94 static unsigned long singlestep_with_movss_blocking_and_dr7_gd(void);
95 static unsigned long singlestep_with_sti_hlt(void);
96
__run_single_step_db_test(db_test_fn test,db_report_fn report_fn)97 static void __run_single_step_db_test(db_test_fn test, db_report_fn report_fn)
98 {
99 unsigned long start;
100 bool ign;
101
102 n = 0;
103 write_dr6(0);
104
105 start = test();
106 report_fn(start, "");
107
108 /*
109 * MOV DR #GPs at CPL>0, don't try to run the DR7.GD test in usermode.
110 * Likewise for HLT.
111 */
112 if (test == singlestep_with_movss_blocking_and_dr7_gd
113 || test == singlestep_with_sti_hlt)
114 return;
115
116 n = 0;
117 write_dr6(0);
118
119 /*
120 * Run the test in usermode. Use the expected start RIP from the first
121 * run, the usermode framework doesn't make it easy to get the expected
122 * RIP out of the test, and it shouldn't change in any case. Run the
123 * test with IOPL=3 so that it can use OUT, CLI, STI, etc...
124 */
125 set_iopl(3);
126 run_in_user((usermode_func)test, GP_VECTOR, 0, 0, 0, 0, &ign);
127 set_iopl(0);
128
129 report_fn(start, "Usermode ");
130 }
131
132 #define run_ss_db_test(name) __run_single_step_db_test(name, report_##name)
133
report_singlestep_basic(unsigned long start,const char * usermode)134 static void report_singlestep_basic(unsigned long start, const char *usermode)
135 {
136 report(n == 3 &&
137 is_single_step_db(dr6[0]) && db_addr[0] == start &&
138 is_single_step_db(dr6[1]) && db_addr[1] == start + 1 &&
139 is_single_step_db(dr6[2]) && db_addr[2] == start + 1 + 1,
140 "%sSingle-step #DB basic test", usermode);
141 }
142
singlestep_basic(void)143 static noinline unsigned long singlestep_basic(void)
144 {
145 unsigned long start;
146
147 /*
148 * After being enabled, single-step breakpoints have a one instruction
149 * delay before the first #DB is generated.
150 */
151 asm volatile (
152 "pushf\n\t"
153 "pop %%rax\n\t"
154 "or $(1<<8),%%rax\n\t"
155 "push %%rax\n\t"
156 "popf\n\t"
157 "and $~(1<<8),%%rax\n\t"
158 "1:push %%rax\n\t"
159 "popf\n\t"
160 "lea 1b(%%rip), %0\n\t"
161 : "=r" (start) : : "rax"
162 );
163 return start;
164 }
165
report_singlestep_emulated_instructions(unsigned long start,const char * usermode)166 static void report_singlestep_emulated_instructions(unsigned long start,
167 const char *usermode)
168 {
169 report(n == 7 &&
170 is_single_step_db(dr6[0]) && db_addr[0] == start &&
171 is_single_step_db(dr6[1]) && db_addr[1] == start + 1 &&
172 is_single_step_db(dr6[2]) && db_addr[2] == start + 1 + 3 &&
173 is_single_step_db(dr6[3]) && db_addr[3] == start + 1 + 3 + 2 &&
174 is_single_step_db(dr6[4]) && db_addr[4] == start + 1 + 3 + 2 + 5 &&
175 is_single_step_db(dr6[5]) && db_addr[5] == start + 1 + 3 + 2 + 5 + 1 &&
176 is_single_step_db(dr6[6]) && db_addr[6] == start + 1 + 3 + 2 + 5 + 1 + 1,
177 "%sSingle-step #DB on emulated instructions", usermode);
178 }
179
singlestep_emulated_instructions(void)180 static noinline unsigned long singlestep_emulated_instructions(void)
181 {
182 unsigned long start;
183
184 /*
185 * Verify single-step #DB are generated correctly on emulated
186 * instructions, e.g. CPUID and RDMSR.
187 */
188 asm volatile (
189 "pushf\n\t"
190 "pop %%rax\n\t"
191 "or $(1<<8),%%rax\n\t"
192 "push %%rax\n\t"
193 "popf\n\t"
194 "and $~(1<<8),%%rax\n\t"
195 "1:push %%rax\n\t"
196 "xor %%rax,%%rax\n\t"
197 "cpuid\n\t"
198 "movl $0x3fd, %%edx\n\t"
199 "inb %%dx, %%al\n\t"
200 "popf\n\t"
201 "lea 1b(%%rip),%0\n\t"
202 : "=r" (start) : : "rax", "ebx", "ecx", "edx"
203 );
204 return start;
205 }
206
report_singlestep_with_sti_blocking(unsigned long start,const char * usermode)207 static void report_singlestep_with_sti_blocking(unsigned long start,
208 const char *usermode)
209 {
210 report(n == 4 &&
211 is_single_step_db(dr6[0]) && db_addr[0] == start &&
212 is_single_step_db(dr6[1]) && db_addr[1] == start + 6 &&
213 is_single_step_db(dr6[2]) && db_addr[2] == start + 6 + 1 &&
214 is_single_step_db(dr6[3]) && db_addr[3] == start + 6 + 1 + 1,
215 "%sSingle-step #DB w/ STI blocking", usermode);
216 }
217
218
singlestep_with_sti_blocking(void)219 static noinline unsigned long singlestep_with_sti_blocking(void)
220 {
221 unsigned long start_rip;
222
223 /*
224 * STI blocking doesn't suppress #DBs, thus the first single-step #DB
225 * should arrive after the standard one instruction delay.
226 */
227 asm volatile(
228 "cli\n\t"
229 "pushf\n\t"
230 "pop %%rax\n\t"
231 "or $(1<<8),%%rax\n\t"
232 "push %%rax\n\t"
233 "popf\n\t"
234 "sti\n\t"
235 "1:and $~(1<<8),%%rax\n\t"
236 "push %%rax\n\t"
237 "popf\n\t"
238 "lea 1b(%%rip),%0\n\t"
239 : "=r" (start_rip) : : "rax"
240 );
241 return start_rip;
242 }
243
report_singlestep_with_movss_blocking(unsigned long start,const char * usermode)244 static void report_singlestep_with_movss_blocking(unsigned long start,
245 const char *usermode)
246 {
247 report(n == 3 &&
248 is_single_step_db(dr6[0]) && db_addr[0] == start &&
249 is_single_step_db(dr6[1]) && db_addr[1] == start + 1 &&
250 is_single_step_db(dr6[2]) && db_addr[2] == start + 1 + 1,
251 "%sSingle-step #DB w/ MOVSS blocking", usermode);
252 }
253
singlestep_with_movss_blocking(void)254 static noinline unsigned long singlestep_with_movss_blocking(void)
255 {
256 unsigned long start_rip;
257
258 /*
259 * MOVSS blocking suppresses single-step #DBs (and select other #DBs),
260 * thus the first single-step #DB should occur after MOVSS blocking
261 * expires, i.e. two instructions after #DBs are enabled in this case.
262 */
263 asm volatile(
264 "pushf\n\t"
265 "pop %%rax\n\t"
266 "or $(1<<8),%%rax\n\t"
267 "push %%rax\n\t"
268 "mov %%ss, %%ax\n\t"
269 "popf\n\t"
270 "mov %%ax, %%ss\n\t"
271 "and $~(1<<8),%%rax\n\t"
272 "1: push %%rax\n\t"
273 "popf\n\t"
274 "lea 1b(%%rip),%0\n\t"
275 : "=r" (start_rip) : : "rax"
276 );
277 return start_rip;
278 }
279
280
report_singlestep_with_movss_blocking_and_icebp(unsigned long start,const char * usermode)281 static void report_singlestep_with_movss_blocking_and_icebp(unsigned long start,
282 const char *usermode)
283 {
284 report(n == 4 &&
285 is_icebp_db(dr6[0]) && db_addr[0] == start &&
286 is_single_step_db(dr6[1]) && db_addr[1] == start + 6 &&
287 is_single_step_db(dr6[2]) && db_addr[2] == start + 6 + 1 &&
288 is_single_step_db(dr6[3]) && db_addr[3] == start + 6 + 1 + 1,
289 "%sSingle-Step + ICEBP #DB w/ MOVSS blocking", usermode);
290 }
291
singlestep_with_movss_blocking_and_icebp(void)292 static noinline unsigned long singlestep_with_movss_blocking_and_icebp(void)
293 {
294 unsigned long start;
295
296 /*
297 * ICEBP, a.k.a. INT1 or int1icebrk, is an oddball. It generates a
298 * trap-like #DB, is intercepted if #DBs are intercepted, and manifests
299 * as a #DB VM-Exit, but the VM-Exit occurs on the ICEBP itself, i.e.
300 * it's treated as an instruction intercept. Verify that ICEBP is
301 * correctly emulated as a trap-like #DB when intercepted, and that
302 * MOVSS blocking is handled correctly with respect to single-step
303 * breakpoints being enabled.
304 */
305 asm volatile(
306 "pushf\n\t"
307 "pop %%rax\n\t"
308 "or $(1<<8),%%rax\n\t"
309 "push %%rax\n\t"
310 "mov %%ss, %%ax\n\t"
311 "popf\n\t"
312 "mov %%ax, %%ss\n\t"
313 ".byte 0xf1;"
314 "1:and $~(1<<8),%%rax\n\t"
315 "push %%rax\n\t"
316 "popf\n\t"
317 "lea 1b(%%rip),%0\n\t"
318 : "=r" (start) : : "rax"
319 );
320 return start;
321 }
322
report_singlestep_with_movss_blocking_and_dr7_gd(unsigned long start,const char * ign)323 static void report_singlestep_with_movss_blocking_and_dr7_gd(unsigned long start,
324 const char *ign)
325 {
326 report(n == 5 &&
327 is_general_detect_db(dr6[0]) && db_addr[0] == start &&
328 is_single_step_db(dr6[1]) && db_addr[1] == start + 3 &&
329 is_single_step_db(dr6[2]) && db_addr[2] == start + 3 + 6 &&
330 is_single_step_db(dr6[3]) && db_addr[3] == start + 3 + 6 + 1 &&
331 is_single_step_db(dr6[4]) && db_addr[4] == start + 3 + 6 + 1 + 1,
332 "Single-step #DB w/ MOVSS blocking and DR7.GD=1");
333 }
334
singlestep_with_movss_blocking_and_dr7_gd(void)335 static noinline unsigned long singlestep_with_movss_blocking_and_dr7_gd(void)
336 {
337 unsigned long start_rip;
338
339 write_dr7(DR7_GD);
340
341 /*
342 * MOVSS blocking does NOT suppress General Detect #DBs, which have
343 * fault-like behavior. Note, DR7.GD is cleared by the CPU upon
344 * successful delivery of the #DB. DR6.BD is NOT cleared by the CPU,
345 * but the MOV DR6 below will be re-executed after handling the
346 * General Detect #DB.
347 */
348 asm volatile(
349 "xor %0, %0\n\t"
350 "pushf\n\t"
351 "pop %%rax\n\t"
352 "or $(1<<8),%%rax\n\t"
353 "push %%rax\n\t"
354 "mov %%ss, %%ax\n\t"
355 "popf\n\t"
356 "mov %%ax, %%ss\n\t"
357 "1: mov %0, %%dr6\n\t"
358 "and $~(1<<8),%%rax\n\t"
359 "push %%rax\n\t"
360 "popf\n\t"
361 "lea 1b(%%rip),%0\n\t"
362 : "=r" (start_rip) : : "rax"
363 );
364 return start_rip;
365 }
366
report_singlestep_with_sti_hlt(unsigned long start,const char * usermode)367 static void report_singlestep_with_sti_hlt(unsigned long start,
368 const char *usermode)
369 {
370 report(n == 5 &&
371 is_single_step_db(dr6[0]) && db_addr[0] == start &&
372 is_single_step_db(dr6[1]) && db_addr[1] == start + 1 &&
373 is_single_step_db(dr6[2]) && db_addr[2] == start + 1 + 6 &&
374 is_single_step_db(dr6[3]) && db_addr[3] == start + 1 + 6 + 1 &&
375 is_single_step_db(dr6[4]) && db_addr[4] == start + 1 + 6 + 1 + 1,
376 "%sSingle-step #DB w/ STI;HLT", usermode);
377 }
378
379 #define APIC_LVT_TIMER_VECTOR (0xee)
380
lvtt_handler(isr_regs_t * regs)381 static void lvtt_handler(isr_regs_t *regs)
382 {
383 eoi();
384 }
385
singlestep_with_sti_hlt(void)386 static noinline unsigned long singlestep_with_sti_hlt(void)
387 {
388 unsigned long start_rip;
389
390 cli();
391
392 handle_irq(APIC_LVT_TIMER_VECTOR, lvtt_handler);
393 apic_write(APIC_LVTT, APIC_LVT_TIMER_ONESHOT |
394 APIC_LVT_TIMER_VECTOR);
395 apic_write(APIC_TDCR, 0x0000000b);
396 apic_write(APIC_TMICT, 1000000);
397
398 /*
399 * STI blocking doesn't suppress #DBs, thus the first single-step #DB
400 * should arrive after the standard one instruction delay.
401 */
402 asm volatile(
403 "pushf\n\t"
404 "pop %%rax\n\t"
405 "or $(1<<8),%%rax\n\t"
406 "push %%rax\n\t"
407 "popf\n\t"
408 "sti\n\t"
409 "1:hlt;\n\t"
410 "and $~(1<<8),%%rax\n\t"
411 "push %%rax\n\t"
412 "popf\n\t"
413 "lea 1b(%%rip),%0\n\t"
414 : "=r" (start_rip) : : "rax"
415 );
416 return start_rip;
417 }
418
bus_lock(uint64_t magic)419 static noinline uint64_t bus_lock(uint64_t magic)
420 {
421 uint8_t buffer[128] __attribute__((aligned(64))) = { };
422 atomic64_t *val = (atomic64_t *)&buffer[60];
423
424 atomic64_cmpxchg(val, 0, magic);
425 return READ_ONCE(*(uint64_t *)val);
426 }
427
bus_lock_test(void)428 static void bus_lock_test(void)
429 {
430 const uint64_t magic = 0xdeadbeefdeadbeefull;
431 bool bus_lock_db = false;
432 uint64_t val;
433
434 /*
435 * Generate a bus lock (via a locked access that splits cache lines)
436 * in CPL0 and again in CPL3 (Bus Lock Detect only affects CPL3), and
437 * verify that no #AC or #DB is generated (the relevant features are
438 * not enabled).
439 */
440 val = bus_lock(magic);
441 report(!got_ac && !n && val == magic,
442 "CPL0 Split Lock #AC = %u (#DB = %u), val = %lx (wanted %lx)",
443 got_ac, n, val, magic);
444
445 val = run_in_user((usermode_func)bus_lock, DB_VECTOR, magic, 0, 0, 0, &bus_lock_db);
446 report(!bus_lock_db && val == magic,
447 "CPL3 Bus Lock #DB = %u, val = %lx (wanted %lx)",
448 bus_lock_db, val, magic);
449
450 n = 0;
451 got_ac = false;
452 }
453
main(int ac,char ** av)454 int main(int ac, char **av)
455 {
456 unsigned long cr4;
457
458 handle_exception(DB_VECTOR, handle_db);
459 handle_exception(BP_VECTOR, handle_bp);
460 handle_exception(UD_VECTOR, handle_ud);
461 handle_exception(AC_VECTOR, handle_ac);
462
463 bus_lock_test();
464
465 /*
466 * DR4 is an alias for DR6 (and DR5 aliases DR7) if CR4.DE is NOT set,
467 * and is reserved if CR4.DE=1 (Debug Extensions enabled).
468 */
469 got_ud = 0;
470 cr4 = read_cr4();
471 write_cr4(cr4 & ~X86_CR4_DE);
472 write_dr4(0);
473 write_dr6(DR6_ACTIVE_LOW | DR6_BS | DR6_TRAP1);
474 report(read_dr4() == (DR6_ACTIVE_LOW | DR6_BS | DR6_TRAP1) && !got_ud,
475 "DR4==DR6 with CR4.DE == 0");
476
477 cr4 = read_cr4();
478 write_cr4(cr4 | X86_CR4_DE);
479 read_dr4();
480 report(got_ud, "DR4 read got #UD with CR4.DE == 1");
481 write_dr6(0);
482
483 extern unsigned char sw_bp;
484 asm volatile("int3; sw_bp:");
485 report(bp_addr == (unsigned long)&sw_bp, "#BP");
486
487 /*
488 * The CPU sets/clears bits 0-3 (trap bits for DR0-3) on #DB based on
489 * whether or not the corresponding DR0-3 got a match. All other bits
490 * in DR6 are set if and only if their associated breakpoint condition
491 * is active, and are never cleared by the CPU. Verify a match on DR0
492 * is reported correctly, and that DR6.BS is not set when single-step
493 * breakpoints are disabled, but is left set (if set by software).
494 */
495 n = 0;
496 extern unsigned char hw_bp1;
497 write_dr0(&hw_bp1);
498 write_dr7(DR7_FIXED_1 | DR7_GLOBAL_ENABLE_DR0);
499 asm volatile("hw_bp1: nop");
500 report(n == 1 &&
501 db_addr[0] == ((unsigned long)&hw_bp1) &&
502 dr6[0] == (DR6_ACTIVE_LOW | DR6_TRAP0),
503 "hw breakpoint (test that dr6.BS is not set)");
504
505 n = 0;
506 extern unsigned char hw_bp2;
507 write_dr0(&hw_bp2);
508 write_dr6(DR6_BS | DR6_TRAP1);
509 asm volatile("hw_bp2: nop");
510 report(n == 1 &&
511 db_addr[0] == ((unsigned long)&hw_bp2) &&
512 dr6[0] == (DR6_ACTIVE_LOW | DR6_BS | DR6_TRAP0),
513 "hw breakpoint (test that dr6.BS is not cleared)");
514
515 run_ss_db_test(singlestep_basic);
516 run_ss_db_test(singlestep_emulated_instructions);
517 run_ss_db_test(singlestep_with_sti_blocking);
518 run_ss_db_test(singlestep_with_movss_blocking);
519 run_ss_db_test(singlestep_with_movss_blocking_and_icebp);
520 run_ss_db_test(singlestep_with_movss_blocking_and_dr7_gd);
521 run_ss_db_test(singlestep_with_sti_hlt);
522
523 n = 0;
524 write_dr1((void *)&value);
525 write_dr6(DR6_BS);
526 write_dr7(0x00d0040a); // 4-byte write
527
528 extern unsigned char hw_wp1;
529 asm volatile(
530 "mov $42,%%rax\n\t"
531 "mov %%rax,%0\n\t; hw_wp1:"
532 : "=m" (value) : : "rax");
533 report(n == 1 &&
534 db_addr[0] == ((unsigned long)&hw_wp1) &&
535 dr6[0] == (DR6_ACTIVE_LOW | DR6_BS | DR6_TRAP1),
536 "hw watchpoint (test that dr6.BS is not cleared)");
537
538 n = 0;
539 write_dr6(0);
540
541 extern unsigned char hw_wp2;
542 asm volatile(
543 "mov $42,%%rax\n\t"
544 "mov %%rax,%0\n\t; hw_wp2:"
545 : "=m" (value) : : "rax");
546 report(n == 1 &&
547 db_addr[0] == ((unsigned long)&hw_wp2) &&
548 dr6[0] == (DR6_ACTIVE_LOW | DR6_TRAP1),
549 "hw watchpoint (test that dr6.BS is not set)");
550
551 n = 0;
552 write_dr6(0);
553 extern unsigned char sw_icebp;
554 asm volatile(".byte 0xf1; sw_icebp:");
555 report(n == 1 &&
556 db_addr[0] == (unsigned long)&sw_icebp && dr6[0] == DR6_ACTIVE_LOW,
557 "icebp");
558
559 write_dr7(0x400);
560 value = KERNEL_DS;
561 write_dr7(0x00f0040a); // 4-byte read or write
562
563 /*
564 * Each invocation of the handler should shift n by 1 and set bit 0 to 1.
565 * We expect a single invocation, so n should become 3. If the entry
566 * RIP is wrong, or if the handler is executed more than once, the value
567 * will not match.
568 */
569 set_idt_entry(1, &handle_db_save_rip, 0);
570
571 n = 1;
572 asm volatile(
573 "clc\n\t"
574 "mov %0,%%ss\n\t"
575 ".byte 0x2e, 0x2e, 0xf1"
576 : "=m" (value) : : "rax");
577 report(n == 3, "MOV SS + watchpoint + ICEBP");
578
579 /*
580 * Here the #DB handler is invoked twice, once as a software exception
581 * and once as a software interrupt.
582 */
583 n = 1;
584 asm volatile(
585 "clc\n\t"
586 "mov %0,%%ss\n\t"
587 "int $1"
588 : "=m" (value) : : "rax");
589 report(n == 7, "MOV SS + watchpoint + int $1");
590
591 /*
592 * Here the #DB and #BP handlers are invoked once each.
593 */
594 n = 1;
595 bp_addr = 0;
596 asm volatile(
597 "mov %0,%%ss\n\t"
598 ".byte 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0xcc\n\t"
599 "sw_bp2:"
600 : "=m" (value) : : "rax");
601 extern unsigned char sw_bp2;
602 report(n == 3 && bp_addr == (unsigned long)&sw_bp2,
603 "MOV SS + watchpoint + INT3");
604 return report_summary();
605 }
606