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 12 #include "libcflat.h" 13 #include "processor.h" 14 #include "desc.h" 15 16 static volatile unsigned long bp_addr; 17 static volatile unsigned long db_addr[10], dr6[10]; 18 static volatile unsigned int n; 19 static volatile unsigned long value; 20 21 static inline void write_dr4(ulong val) 22 { 23 asm volatile ("mov %0, %%dr4" : : "r"(val) : "memory"); 24 } 25 26 static inline ulong read_dr4(void) 27 { 28 ulong val; 29 asm volatile ("mov %%dr4, %0" : "=r"(val)); 30 return val; 31 } 32 33 static void handle_db(struct ex_regs *regs) 34 { 35 db_addr[n] = regs->rip; 36 dr6[n] = read_dr6(); 37 38 if (dr6[n] & 0x1) 39 regs->rflags |= (1 << 16); 40 41 if (++n >= 10) { 42 regs->rflags &= ~(1 << 8); 43 write_dr7(0x00000400); 44 } 45 } 46 47 extern unsigned char handle_db_save_rip; 48 asm("handle_db_save_rip:\n" 49 "stc\n" 50 "nop;nop;nop\n" 51 "rclq $1, n(%rip)\n" 52 "iretq\n"); 53 54 static void handle_bp(struct ex_regs *regs) 55 { 56 bp_addr = regs->rip; 57 } 58 59 bool got_ud; 60 static void handle_ud(struct ex_regs *regs) 61 { 62 unsigned long cr4 = read_cr4(); 63 write_cr4(cr4 & ~X86_CR4_DE); 64 got_ud = 1; 65 } 66 67 int main(int ac, char **av) 68 { 69 unsigned long start; 70 unsigned long cr4; 71 72 handle_exception(DB_VECTOR, handle_db); 73 handle_exception(BP_VECTOR, handle_bp); 74 handle_exception(UD_VECTOR, handle_ud); 75 76 got_ud = 0; 77 cr4 = read_cr4(); 78 write_cr4(cr4 & ~X86_CR4_DE); 79 write_dr4(0); 80 write_dr6(0xffff4ff2); 81 report(read_dr4() == 0xffff4ff2 && !got_ud, "reading DR4 with CR4.DE == 0"); 82 83 cr4 = read_cr4(); 84 write_cr4(cr4 | X86_CR4_DE); 85 read_dr4(); 86 report(got_ud, "reading DR4 with CR4.DE == 1"); 87 write_dr6(0); 88 89 extern unsigned char sw_bp; 90 asm volatile("int3; sw_bp:"); 91 report(bp_addr == (unsigned long)&sw_bp, "#BP"); 92 93 n = 0; 94 extern unsigned char hw_bp1; 95 write_dr0(&hw_bp1); 96 write_dr7(0x00000402); 97 asm volatile("hw_bp1: nop"); 98 report(n == 1 && 99 db_addr[0] == ((unsigned long)&hw_bp1) && dr6[0] == 0xffff0ff1, 100 "hw breakpoint (test that dr6.BS is not set)"); 101 102 n = 0; 103 extern unsigned char hw_bp2; 104 write_dr0(&hw_bp2); 105 write_dr6(0x00004002); 106 asm volatile("hw_bp2: nop"); 107 report(n == 1 && 108 db_addr[0] == ((unsigned long)&hw_bp2) && dr6[0] == 0xffff4ff1, 109 "hw breakpoint (test that dr6.BS is not cleared)"); 110 111 n = 0; 112 write_dr6(0); 113 asm volatile( 114 "pushf\n\t" 115 "pop %%rax\n\t" 116 "or $(1<<8),%%rax\n\t" 117 "push %%rax\n\t" 118 "lea (%%rip),%0\n\t" 119 "popf\n\t" 120 "and $~(1<<8),%%rax\n\t" 121 "push %%rax\n\t" 122 "popf\n\t" 123 : "=r" (start) : : "rax"); 124 report(n == 3 && 125 db_addr[0] == start + 1 + 6 && dr6[0] == 0xffff4ff0 && 126 db_addr[1] == start + 1 + 6 + 1 && dr6[1] == 0xffff4ff0 && 127 db_addr[2] == start + 1 + 6 + 1 + 1 && dr6[2] == 0xffff4ff0, 128 "single step"); 129 130 /* 131 * cpuid and rdmsr (among others) trigger VM exits and are then 132 * emulated. Test that single stepping works on emulated instructions. 133 */ 134 n = 0; 135 write_dr6(0); 136 asm volatile( 137 "pushf\n\t" 138 "pop %%rax\n\t" 139 "or $(1<<8),%%rax\n\t" 140 "push %%rax\n\t" 141 "lea (%%rip),%0\n\t" 142 "popf\n\t" 143 "and $~(1<<8),%%rax\n\t" 144 "push %%rax\n\t" 145 "xor %%rax,%%rax\n\t" 146 "cpuid\n\t" 147 "movl $0x1a0,%%ecx\n\t" 148 "rdmsr\n\t" 149 "popf\n\t" 150 : "=r" (start) : : "rax", "ebx", "ecx", "edx"); 151 report(n == 7 && 152 db_addr[0] == start + 1 + 6 && dr6[0] == 0xffff4ff0 && 153 db_addr[1] == start + 1 + 6 + 1 && dr6[1] == 0xffff4ff0 && 154 db_addr[2] == start + 1 + 6 + 1 + 3 && dr6[2] == 0xffff4ff0 && 155 db_addr[3] == start + 1 + 6 + 1 + 3 + 2 && dr6[3] == 0xffff4ff0 && 156 db_addr[4] == start + 1 + 6 + 1 + 3 + 2 + 5 && dr6[4] == 0xffff4ff0 && 157 db_addr[5] == start + 1 + 6 + 1 + 3 + 2 + 5 + 2 && dr6[5] == 0xffff4ff0 && 158 db_addr[6] == start + 1 + 6 + 1 + 3 + 2 + 5 + 2 + 1 && dr6[6] == 0xffff4ff0, 159 "single step emulated instructions"); 160 161 n = 0; 162 write_dr1((void *)&value); 163 write_dr7(0x00d0040a); // 4-byte write 164 165 extern unsigned char hw_wp1; 166 asm volatile( 167 "mov $42,%%rax\n\t" 168 "mov %%rax,%0\n\t; hw_wp1:" 169 : "=m" (value) : : "rax"); 170 report(n == 1 && 171 db_addr[0] == ((unsigned long)&hw_wp1) && dr6[0] == 0xffff4ff2, 172 "hw watchpoint (test that dr6.BS is not cleared)"); 173 174 n = 0; 175 write_dr6(0); 176 177 extern unsigned char hw_wp2; 178 asm volatile( 179 "mov $42,%%rax\n\t" 180 "mov %%rax,%0\n\t; hw_wp2:" 181 : "=m" (value) : : "rax"); 182 report(n == 1 && 183 db_addr[0] == ((unsigned long)&hw_wp2) && dr6[0] == 0xffff0ff2, 184 "hw watchpoint (test that dr6.BS is not set)"); 185 186 n = 0; 187 write_dr6(0); 188 extern unsigned char sw_icebp; 189 asm volatile(".byte 0xf1; sw_icebp:"); 190 report(n == 1 && 191 db_addr[0] == (unsigned long)&sw_icebp && dr6[0] == 0xffff0ff0, 192 "icebp"); 193 194 write_dr7(0x400); 195 value = KERNEL_DS; 196 write_dr7(0x00f0040a); // 4-byte read or write 197 198 /* 199 * Each invocation of the handler should shift n by 1 and set bit 0 to 1. 200 * We expect a single invocation, so n should become 3. If the entry 201 * RIP is wrong, or if the handler is executed more than once, the value 202 * will not match. 203 */ 204 set_idt_entry(1, &handle_db_save_rip, 0); 205 206 n = 1; 207 asm volatile( 208 "clc\n\t" 209 "mov %0,%%ss\n\t" 210 ".byte 0x2e, 0x2e, 0xf1" 211 : "=m" (value) : : "rax"); 212 report(n == 3, "MOV SS + watchpoint + ICEBP"); 213 214 /* 215 * Here the #DB handler is invoked twice, once as a software exception 216 * and once as a software interrupt. 217 */ 218 n = 1; 219 asm volatile( 220 "clc\n\t" 221 "mov %0,%%ss\n\t" 222 "int $1" 223 : "=m" (value) : : "rax"); 224 report(n == 7, "MOV SS + watchpoint + int $1"); 225 226 /* 227 * Here the #DB and #BP handlers are invoked once each. 228 */ 229 n = 1; 230 bp_addr = 0; 231 asm volatile( 232 "mov %0,%%ss\n\t" 233 ".byte 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0xcc\n\t" 234 "sw_bp2:" 235 : "=m" (value) : : "rax"); 236 extern unsigned char sw_bp2; 237 report(n == 3 && bp_addr == (unsigned long)&sw_bp2, 238 "MOV SS + watchpoint + INT3"); 239 return report_summary(); 240 } 241