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