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 24 static inline void write_dr4(ulong val) 25 { 26 asm volatile ("mov %0, %%dr4" : : "r"(val) : "memory"); 27 } 28 29 static inline ulong read_dr4(void) 30 { 31 ulong val; 32 asm volatile ("mov %%dr4, %0" : "=r"(val)); 33 return val; 34 } 35 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 50 static inline bool is_single_step_db(unsigned long dr6_val) 51 { 52 return dr6_val == (DR6_ACTIVE_LOW | DR6_BS); 53 } 54 55 static inline bool is_general_detect_db(unsigned long dr6_val) 56 { 57 return dr6_val == (DR6_ACTIVE_LOW | DR6_BD); 58 } 59 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 72 static void handle_bp(struct ex_regs *regs) 73 { 74 bp_addr = regs->rip; 75 } 76 77 bool got_ud; 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; 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 381 static void lvtt_handler(isr_regs_t *regs) 382 { 383 eoi(); 384 } 385 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 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 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 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