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