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 */ 11478027f5SJan Kiszka 12478027f5SJan Kiszka #include "libcflat.h" 132b934609SXiaoyao Li #include "processor.h" 14478027f5SJan Kiszka #include "desc.h" 15478027f5SJan Kiszka 16230db53fSPaolo Bonzini static volatile unsigned long bp_addr; 17230db53fSPaolo Bonzini static volatile unsigned long db_addr[10], dr6[10]; 18478027f5SJan Kiszka static volatile unsigned int n; 19478027f5SJan Kiszka static volatile unsigned long value; 20478027f5SJan Kiszka 217f8f7356SKrish Sadhukhan static inline void write_dr4(ulong val) 222c6863b2SPaolo Bonzini { 237f8f7356SKrish Sadhukhan asm volatile ("mov %0, %%dr4" : : "r"(val) : "memory"); 242c6863b2SPaolo Bonzini } 252c6863b2SPaolo Bonzini 267f8f7356SKrish Sadhukhan static inline ulong read_dr4(void) 27478027f5SJan Kiszka { 287f8f7356SKrish Sadhukhan ulong val; 297f8f7356SKrish Sadhukhan asm volatile ("mov %%dr4, %0" : "=r"(val)); 307f8f7356SKrish Sadhukhan return val; 31478027f5SJan Kiszka } 32478027f5SJan Kiszka 33478027f5SJan Kiszka static void handle_db(struct ex_regs *regs) 34478027f5SJan Kiszka { 35230db53fSPaolo Bonzini db_addr[n] = regs->rip; 367f8f7356SKrish Sadhukhan dr6[n] = read_dr6(); 37478027f5SJan Kiszka 38478027f5SJan Kiszka if (dr6[n] & 0x1) 39*9734b423SSean Christopherson regs->rflags |= X86_EFLAGS_RF; 40478027f5SJan Kiszka 41478027f5SJan Kiszka if (++n >= 10) { 42*9734b423SSean Christopherson regs->rflags &= ~X86_EFLAGS_TF; 437f8f7356SKrish Sadhukhan write_dr7(0x00000400); 44478027f5SJan Kiszka } 45478027f5SJan Kiszka } 46478027f5SJan Kiszka 47*9734b423SSean Christopherson static inline bool is_single_step_db(unsigned long dr6_val) 48*9734b423SSean Christopherson { 49*9734b423SSean Christopherson return dr6_val == 0xffff4ff0; 50*9734b423SSean Christopherson } 51*9734b423SSean Christopherson 52*9734b423SSean Christopherson static inline bool is_icebp_db(unsigned long dr6_val) 53*9734b423SSean Christopherson { 54*9734b423SSean Christopherson return dr6_val == 0xffff0ff0; 55*9734b423SSean Christopherson } 56*9734b423SSean Christopherson 57a094abddSPaolo Bonzini extern unsigned char handle_db_save_rip; 58a094abddSPaolo Bonzini asm("handle_db_save_rip:\n" 59a094abddSPaolo Bonzini "stc\n" 60a094abddSPaolo Bonzini "nop;nop;nop\n" 61a094abddSPaolo Bonzini "rclq $1, n(%rip)\n" 62a094abddSPaolo Bonzini "iretq\n"); 63a094abddSPaolo Bonzini 64478027f5SJan Kiszka static void handle_bp(struct ex_regs *regs) 65478027f5SJan Kiszka { 66230db53fSPaolo Bonzini bp_addr = regs->rip; 67478027f5SJan Kiszka } 68478027f5SJan Kiszka 692c6863b2SPaolo Bonzini bool got_ud; 702c6863b2SPaolo Bonzini static void handle_ud(struct ex_regs *regs) 712c6863b2SPaolo Bonzini { 722c6863b2SPaolo Bonzini unsigned long cr4 = read_cr4(); 732c6863b2SPaolo Bonzini write_cr4(cr4 & ~X86_CR4_DE); 742c6863b2SPaolo Bonzini got_ud = 1; 752c6863b2SPaolo Bonzini } 762c6863b2SPaolo Bonzini 77*9734b423SSean Christopherson typedef unsigned long (*db_test_fn)(void); 78*9734b423SSean Christopherson typedef void (*db_report_fn)(unsigned long); 79*9734b423SSean Christopherson 80*9734b423SSean Christopherson static void __run_single_step_db_test(db_test_fn test, db_report_fn report_fn) 81478027f5SJan Kiszka { 82478027f5SJan Kiszka unsigned long start; 83*9734b423SSean Christopherson 84*9734b423SSean Christopherson n = 0; 85*9734b423SSean Christopherson write_dr6(0); 86*9734b423SSean Christopherson 87*9734b423SSean Christopherson start = test(); 88*9734b423SSean Christopherson report_fn(start); 89*9734b423SSean Christopherson } 90*9734b423SSean Christopherson 91*9734b423SSean Christopherson #define run_ss_db_test(name) __run_single_step_db_test(name, report_##name) 92*9734b423SSean Christopherson 93*9734b423SSean Christopherson static void report_singlestep_basic(unsigned long start) 94*9734b423SSean Christopherson { 95*9734b423SSean Christopherson report(n == 3 && 96*9734b423SSean Christopherson is_single_step_db(dr6[0]) && db_addr[0] == start && 97*9734b423SSean Christopherson is_single_step_db(dr6[1]) && db_addr[1] == start + 1 && 98*9734b423SSean Christopherson is_single_step_db(dr6[2]) && db_addr[2] == start + 1 + 1, 99*9734b423SSean Christopherson "Single-step #DB basic test"); 100*9734b423SSean Christopherson } 101*9734b423SSean Christopherson 102*9734b423SSean Christopherson static unsigned long singlestep_basic(void) 103*9734b423SSean Christopherson { 104*9734b423SSean Christopherson unsigned long start; 105*9734b423SSean Christopherson 106*9734b423SSean Christopherson /* 107*9734b423SSean Christopherson * After being enabled, single-step breakpoints have a one instruction 108*9734b423SSean Christopherson * delay before the first #DB is generated. 109*9734b423SSean Christopherson */ 110*9734b423SSean Christopherson asm volatile ( 111*9734b423SSean Christopherson "pushf\n\t" 112*9734b423SSean Christopherson "pop %%rax\n\t" 113*9734b423SSean Christopherson "or $(1<<8),%%rax\n\t" 114*9734b423SSean Christopherson "push %%rax\n\t" 115*9734b423SSean Christopherson "popf\n\t" 116*9734b423SSean Christopherson "and $~(1<<8),%%rax\n\t" 117*9734b423SSean Christopherson "1:push %%rax\n\t" 118*9734b423SSean Christopherson "popf\n\t" 119*9734b423SSean Christopherson "lea 1b, %0\n\t" 120*9734b423SSean Christopherson : "=r" (start) : : "rax" 121*9734b423SSean Christopherson ); 122*9734b423SSean Christopherson return start; 123*9734b423SSean Christopherson } 124*9734b423SSean Christopherson 125*9734b423SSean Christopherson static void report_singlestep_emulated_instructions(unsigned long start) 126*9734b423SSean Christopherson { 127*9734b423SSean Christopherson report(n == 7 && 128*9734b423SSean Christopherson is_single_step_db(dr6[0]) && db_addr[0] == start && 129*9734b423SSean Christopherson is_single_step_db(dr6[1]) && db_addr[1] == start + 1 && 130*9734b423SSean Christopherson is_single_step_db(dr6[2]) && db_addr[2] == start + 1 + 3 && 131*9734b423SSean Christopherson is_single_step_db(dr6[3]) && db_addr[3] == start + 1 + 3 + 2 && 132*9734b423SSean Christopherson is_single_step_db(dr6[4]) && db_addr[4] == start + 1 + 3 + 2 + 5 && 133*9734b423SSean Christopherson is_single_step_db(dr6[5]) && db_addr[5] == start + 1 + 3 + 2 + 5 + 2 && 134*9734b423SSean Christopherson is_single_step_db(dr6[6]) && db_addr[6] == start + 1 + 3 + 2 + 5 + 2 + 1, 135*9734b423SSean Christopherson "Single-step #DB on emulated instructions"); 136*9734b423SSean Christopherson } 137*9734b423SSean Christopherson 138*9734b423SSean Christopherson static unsigned long singlestep_emulated_instructions(void) 139*9734b423SSean Christopherson { 140*9734b423SSean Christopherson unsigned long start; 141*9734b423SSean Christopherson 142*9734b423SSean Christopherson /* 143*9734b423SSean Christopherson * Verify single-step #DB are generated correctly on emulated 144*9734b423SSean Christopherson * instructions, e.g. CPUID and RDMSR. 145*9734b423SSean Christopherson */ 146*9734b423SSean Christopherson asm volatile ( 147*9734b423SSean Christopherson "pushf\n\t" 148*9734b423SSean Christopherson "pop %%rax\n\t" 149*9734b423SSean Christopherson "or $(1<<8),%%rax\n\t" 150*9734b423SSean Christopherson "push %%rax\n\t" 151*9734b423SSean Christopherson "popf\n\t" 152*9734b423SSean Christopherson "and $~(1<<8),%%rax\n\t" 153*9734b423SSean Christopherson "1:push %%rax\n\t" 154*9734b423SSean Christopherson "xor %%rax,%%rax\n\t" 155*9734b423SSean Christopherson "cpuid\n\t" 156*9734b423SSean Christopherson "movl $0x1a0,%%ecx\n\t" 157*9734b423SSean Christopherson "rdmsr\n\t" 158*9734b423SSean Christopherson "popf\n\t" 159*9734b423SSean Christopherson "lea 1b,%0\n\t" 160*9734b423SSean Christopherson : "=r" (start) : : "rax", "ebx", "ecx", "edx" 161*9734b423SSean Christopherson ); 162*9734b423SSean Christopherson return start; 163*9734b423SSean Christopherson } 164*9734b423SSean Christopherson 165*9734b423SSean Christopherson int main(int ac, char **av) 166*9734b423SSean Christopherson { 1672c6863b2SPaolo Bonzini unsigned long cr4; 168478027f5SJan Kiszka 169478027f5SJan Kiszka handle_exception(DB_VECTOR, handle_db); 170478027f5SJan Kiszka handle_exception(BP_VECTOR, handle_bp); 1712c6863b2SPaolo Bonzini handle_exception(UD_VECTOR, handle_ud); 1722c6863b2SPaolo Bonzini 173*9734b423SSean Christopherson /* 174*9734b423SSean Christopherson * DR4 is an alias for DR6 (and DR5 aliases DR7) if CR4.DE is NOT set, 175*9734b423SSean Christopherson * and is reserved if CR4.DE=1 (Debug Extensions enabled). 176*9734b423SSean Christopherson */ 1772c6863b2SPaolo Bonzini got_ud = 0; 1782c6863b2SPaolo Bonzini cr4 = read_cr4(); 1792c6863b2SPaolo Bonzini write_cr4(cr4 & ~X86_CR4_DE); 1807f8f7356SKrish Sadhukhan write_dr4(0); 1817f8f7356SKrish Sadhukhan write_dr6(0xffff4ff2); 1827f8f7356SKrish Sadhukhan report(read_dr4() == 0xffff4ff2 && !got_ud, "reading DR4 with CR4.DE == 0"); 1832c6863b2SPaolo Bonzini 1842c6863b2SPaolo Bonzini cr4 = read_cr4(); 1852c6863b2SPaolo Bonzini write_cr4(cr4 | X86_CR4_DE); 1867f8f7356SKrish Sadhukhan read_dr4(); 187*9734b423SSean Christopherson report(got_ud, "DR4 read got #UD with CR4.DE == 1"); 1887f8f7356SKrish Sadhukhan write_dr6(0); 189478027f5SJan Kiszka 190c68a7ff0SPaolo Bonzini extern unsigned char sw_bp; 191c68a7ff0SPaolo Bonzini asm volatile("int3; sw_bp:"); 192a299895bSThomas Huth report(bp_addr == (unsigned long)&sw_bp, "#BP"); 193478027f5SJan Kiszka 194*9734b423SSean Christopherson /* 195*9734b423SSean Christopherson * The CPU sets/clears bits 0-3 (trap bits for DR0-3) on #DB based on 196*9734b423SSean Christopherson * whether or not the corresponding DR0-3 got a match. All other bits 197*9734b423SSean Christopherson * in DR6 are set if and only if their associated breakpoint condition 198*9734b423SSean Christopherson * is active, and are never cleared by the CPU. Verify a match on DR0 199*9734b423SSean Christopherson * is reported correctly, and that DR6.BS is not set when single-step 200*9734b423SSean Christopherson * breakpoints are disabled, but is left set (if set by software). 201*9734b423SSean Christopherson */ 2029e486280SPaolo Bonzini n = 0; 203c68a7ff0SPaolo Bonzini extern unsigned char hw_bp1; 2047f8f7356SKrish Sadhukhan write_dr0(&hw_bp1); 2057f8f7356SKrish Sadhukhan write_dr7(0x00000402); 206c68a7ff0SPaolo Bonzini asm volatile("hw_bp1: nop"); 207a299895bSThomas Huth report(n == 1 && 208a299895bSThomas Huth db_addr[0] == ((unsigned long)&hw_bp1) && dr6[0] == 0xffff0ff1, 209a299895bSThomas Huth "hw breakpoint (test that dr6.BS is not set)"); 210478027f5SJan Kiszka 211478027f5SJan Kiszka n = 0; 212c68a7ff0SPaolo Bonzini extern unsigned char hw_bp2; 2137f8f7356SKrish Sadhukhan write_dr0(&hw_bp2); 2147f8f7356SKrish Sadhukhan write_dr6(0x00004002); 215c68a7ff0SPaolo Bonzini asm volatile("hw_bp2: nop"); 216a299895bSThomas Huth report(n == 1 && 217a299895bSThomas Huth db_addr[0] == ((unsigned long)&hw_bp2) && dr6[0] == 0xffff4ff1, 218a299895bSThomas Huth "hw breakpoint (test that dr6.BS is not cleared)"); 2199e486280SPaolo Bonzini 220*9734b423SSean Christopherson run_ss_db_test(singlestep_basic); 221*9734b423SSean Christopherson run_ss_db_test(singlestep_emulated_instructions); 2220a982d78SKyle Huey 223478027f5SJan Kiszka n = 0; 2247f8f7356SKrish Sadhukhan write_dr1((void *)&value); 2257f8f7356SKrish Sadhukhan write_dr7(0x00d0040a); // 4-byte write 226478027f5SJan Kiszka 227c68a7ff0SPaolo Bonzini extern unsigned char hw_wp1; 228478027f5SJan Kiszka asm volatile( 229478027f5SJan Kiszka "mov $42,%%rax\n\t" 230c68a7ff0SPaolo Bonzini "mov %%rax,%0\n\t; hw_wp1:" 231478027f5SJan Kiszka : "=m" (value) : : "rax"); 232a299895bSThomas Huth report(n == 1 && 233a299895bSThomas Huth db_addr[0] == ((unsigned long)&hw_wp1) && dr6[0] == 0xffff4ff2, 234a299895bSThomas Huth "hw watchpoint (test that dr6.BS is not cleared)"); 2359e486280SPaolo Bonzini 2369e486280SPaolo Bonzini n = 0; 2377f8f7356SKrish Sadhukhan write_dr6(0); 2389e486280SPaolo Bonzini 239c68a7ff0SPaolo Bonzini extern unsigned char hw_wp2; 2409e486280SPaolo Bonzini asm volatile( 2419e486280SPaolo Bonzini "mov $42,%%rax\n\t" 242c68a7ff0SPaolo Bonzini "mov %%rax,%0\n\t; hw_wp2:" 2439e486280SPaolo Bonzini : "=m" (value) : : "rax"); 244a299895bSThomas Huth report(n == 1 && 245a299895bSThomas Huth db_addr[0] == ((unsigned long)&hw_wp2) && dr6[0] == 0xffff0ff2, 246a299895bSThomas Huth "hw watchpoint (test that dr6.BS is not set)"); 2479e486280SPaolo Bonzini 2489e486280SPaolo Bonzini n = 0; 2497f8f7356SKrish Sadhukhan write_dr6(0); 250c68a7ff0SPaolo Bonzini extern unsigned char sw_icebp; 251c68a7ff0SPaolo Bonzini asm volatile(".byte 0xf1; sw_icebp:"); 252a299895bSThomas Huth report(n == 1 && 253a299895bSThomas Huth db_addr[0] == (unsigned long)&sw_icebp && dr6[0] == 0xffff0ff0, 254a299895bSThomas Huth "icebp"); 255478027f5SJan Kiszka 2567f8f7356SKrish Sadhukhan write_dr7(0x400); 257a094abddSPaolo Bonzini value = KERNEL_DS; 2587f8f7356SKrish Sadhukhan write_dr7(0x00f0040a); // 4-byte read or write 259a094abddSPaolo Bonzini 260a094abddSPaolo Bonzini /* 261a094abddSPaolo Bonzini * Each invocation of the handler should shift n by 1 and set bit 0 to 1. 262a094abddSPaolo Bonzini * We expect a single invocation, so n should become 3. If the entry 263a094abddSPaolo Bonzini * RIP is wrong, or if the handler is executed more than once, the value 264a094abddSPaolo Bonzini * will not match. 265a094abddSPaolo Bonzini */ 266a094abddSPaolo Bonzini set_idt_entry(1, &handle_db_save_rip, 0); 267a094abddSPaolo Bonzini 268a094abddSPaolo Bonzini n = 1; 269a094abddSPaolo Bonzini asm volatile( 270a094abddSPaolo Bonzini "clc\n\t" 271a094abddSPaolo Bonzini "mov %0,%%ss\n\t" 272a094abddSPaolo Bonzini ".byte 0x2e, 0x2e, 0xf1" 273a094abddSPaolo Bonzini : "=m" (value) : : "rax"); 274a299895bSThomas Huth report(n == 3, "MOV SS + watchpoint + ICEBP"); 275a094abddSPaolo Bonzini 276a094abddSPaolo Bonzini /* 277a094abddSPaolo Bonzini * Here the #DB handler is invoked twice, once as a software exception 278a094abddSPaolo Bonzini * and once as a software interrupt. 279a094abddSPaolo Bonzini */ 280a094abddSPaolo Bonzini n = 1; 281a094abddSPaolo Bonzini asm volatile( 282a094abddSPaolo Bonzini "clc\n\t" 283a094abddSPaolo Bonzini "mov %0,%%ss\n\t" 284a094abddSPaolo Bonzini "int $1" 285a094abddSPaolo Bonzini : "=m" (value) : : "rax"); 286a299895bSThomas Huth report(n == 7, "MOV SS + watchpoint + int $1"); 287a094abddSPaolo Bonzini 288a094abddSPaolo Bonzini /* 289a094abddSPaolo Bonzini * Here the #DB and #BP handlers are invoked once each. 290a094abddSPaolo Bonzini */ 291a094abddSPaolo Bonzini n = 1; 292a094abddSPaolo Bonzini bp_addr = 0; 293a094abddSPaolo Bonzini asm volatile( 294a094abddSPaolo Bonzini "mov %0,%%ss\n\t" 295a094abddSPaolo Bonzini ".byte 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0xcc\n\t" 296a094abddSPaolo Bonzini "sw_bp2:" 297a094abddSPaolo Bonzini : "=m" (value) : : "rax"); 298a094abddSPaolo Bonzini extern unsigned char sw_bp2; 299a299895bSThomas Huth report(n == 3 && bp_addr == (unsigned long)&sw_bp2, 300a299895bSThomas Huth "MOV SS + watchpoint + INT3"); 30186065ca2SAndrew Jones return report_summary(); 302478027f5SJan Kiszka } 303