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