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