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 21*7f8f7356SKrish Sadhukhan static inline void write_dr4(ulong val) 222c6863b2SPaolo Bonzini { 23*7f8f7356SKrish Sadhukhan asm volatile ("mov %0, %%dr4" : : "r"(val) : "memory"); 242c6863b2SPaolo Bonzini } 252c6863b2SPaolo Bonzini 26*7f8f7356SKrish Sadhukhan static inline ulong read_dr4(void) 27478027f5SJan Kiszka { 28*7f8f7356SKrish Sadhukhan ulong val; 29*7f8f7356SKrish Sadhukhan asm volatile ("mov %%dr4, %0" : "=r"(val)); 30*7f8f7356SKrish 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; 36*7f8f7356SKrish Sadhukhan dr6[n] = read_dr6(); 37478027f5SJan Kiszka 38478027f5SJan Kiszka if (dr6[n] & 0x1) 39478027f5SJan Kiszka regs->rflags |= (1 << 16); 40478027f5SJan Kiszka 41478027f5SJan Kiszka if (++n >= 10) { 42478027f5SJan Kiszka regs->rflags &= ~(1 << 8); 43*7f8f7356SKrish Sadhukhan write_dr7(0x00000400); 44478027f5SJan Kiszka } 45478027f5SJan Kiszka } 46478027f5SJan Kiszka 47a094abddSPaolo Bonzini extern unsigned char handle_db_save_rip; 48a094abddSPaolo Bonzini asm("handle_db_save_rip:\n" 49a094abddSPaolo Bonzini "stc\n" 50a094abddSPaolo Bonzini "nop;nop;nop\n" 51a094abddSPaolo Bonzini "rclq $1, n(%rip)\n" 52a094abddSPaolo Bonzini "iretq\n"); 53a094abddSPaolo Bonzini 54478027f5SJan Kiszka static void handle_bp(struct ex_regs *regs) 55478027f5SJan Kiszka { 56230db53fSPaolo Bonzini bp_addr = regs->rip; 57478027f5SJan Kiszka } 58478027f5SJan Kiszka 592c6863b2SPaolo Bonzini bool got_ud; 602c6863b2SPaolo Bonzini static void handle_ud(struct ex_regs *regs) 612c6863b2SPaolo Bonzini { 622c6863b2SPaolo Bonzini unsigned long cr4 = read_cr4(); 632c6863b2SPaolo Bonzini write_cr4(cr4 & ~X86_CR4_DE); 642c6863b2SPaolo Bonzini got_ud = 1; 652c6863b2SPaolo Bonzini } 662c6863b2SPaolo Bonzini 67478027f5SJan Kiszka int main(int ac, char **av) 68478027f5SJan Kiszka { 69478027f5SJan Kiszka unsigned long start; 702c6863b2SPaolo Bonzini unsigned long cr4; 71478027f5SJan Kiszka 72478027f5SJan Kiszka handle_exception(DB_VECTOR, handle_db); 73478027f5SJan Kiszka handle_exception(BP_VECTOR, handle_bp); 742c6863b2SPaolo Bonzini handle_exception(UD_VECTOR, handle_ud); 752c6863b2SPaolo Bonzini 762c6863b2SPaolo Bonzini got_ud = 0; 772c6863b2SPaolo Bonzini cr4 = read_cr4(); 782c6863b2SPaolo Bonzini write_cr4(cr4 & ~X86_CR4_DE); 79*7f8f7356SKrish Sadhukhan write_dr4(0); 80*7f8f7356SKrish Sadhukhan write_dr6(0xffff4ff2); 81*7f8f7356SKrish Sadhukhan report(read_dr4() == 0xffff4ff2 && !got_ud, "reading DR4 with CR4.DE == 0"); 822c6863b2SPaolo Bonzini 832c6863b2SPaolo Bonzini cr4 = read_cr4(); 842c6863b2SPaolo Bonzini write_cr4(cr4 | X86_CR4_DE); 85*7f8f7356SKrish Sadhukhan read_dr4(); 862c6863b2SPaolo Bonzini report(got_ud, "reading DR4 with CR4.DE == 1"); 87*7f8f7356SKrish Sadhukhan write_dr6(0); 88478027f5SJan Kiszka 89c68a7ff0SPaolo Bonzini extern unsigned char sw_bp; 90c68a7ff0SPaolo Bonzini asm volatile("int3; sw_bp:"); 91a299895bSThomas Huth report(bp_addr == (unsigned long)&sw_bp, "#BP"); 92478027f5SJan Kiszka 939e486280SPaolo Bonzini n = 0; 94c68a7ff0SPaolo Bonzini extern unsigned char hw_bp1; 95*7f8f7356SKrish Sadhukhan write_dr0(&hw_bp1); 96*7f8f7356SKrish Sadhukhan write_dr7(0x00000402); 97c68a7ff0SPaolo Bonzini asm volatile("hw_bp1: nop"); 98a299895bSThomas Huth report(n == 1 && 99a299895bSThomas Huth db_addr[0] == ((unsigned long)&hw_bp1) && dr6[0] == 0xffff0ff1, 100a299895bSThomas Huth "hw breakpoint (test that dr6.BS is not set)"); 101478027f5SJan Kiszka 102478027f5SJan Kiszka n = 0; 103c68a7ff0SPaolo Bonzini extern unsigned char hw_bp2; 104*7f8f7356SKrish Sadhukhan write_dr0(&hw_bp2); 105*7f8f7356SKrish Sadhukhan write_dr6(0x00004002); 106c68a7ff0SPaolo Bonzini asm volatile("hw_bp2: nop"); 107a299895bSThomas Huth report(n == 1 && 108a299895bSThomas Huth db_addr[0] == ((unsigned long)&hw_bp2) && dr6[0] == 0xffff4ff1, 109a299895bSThomas Huth "hw breakpoint (test that dr6.BS is not cleared)"); 1109e486280SPaolo Bonzini 1119e486280SPaolo Bonzini n = 0; 112*7f8f7356SKrish Sadhukhan write_dr6(0); 113478027f5SJan Kiszka asm volatile( 114478027f5SJan Kiszka "pushf\n\t" 115478027f5SJan Kiszka "pop %%rax\n\t" 116478027f5SJan Kiszka "or $(1<<8),%%rax\n\t" 117478027f5SJan Kiszka "push %%rax\n\t" 118478027f5SJan Kiszka "lea (%%rip),%0\n\t" 119478027f5SJan Kiszka "popf\n\t" 120478027f5SJan Kiszka "and $~(1<<8),%%rax\n\t" 121478027f5SJan Kiszka "push %%rax\n\t" 122478027f5SJan Kiszka "popf\n\t" 12375eea510SBill Wendling : "=r" (start) : : "rax"); 124a299895bSThomas Huth report(n == 3 && 125230db53fSPaolo Bonzini db_addr[0] == start + 1 + 6 && dr6[0] == 0xffff4ff0 && 126230db53fSPaolo Bonzini db_addr[1] == start + 1 + 6 + 1 && dr6[1] == 0xffff4ff0 && 127a299895bSThomas Huth db_addr[2] == start + 1 + 6 + 1 + 1 && dr6[2] == 0xffff4ff0, 128a299895bSThomas Huth "single step"); 129478027f5SJan Kiszka 1300a982d78SKyle Huey /* 1310a982d78SKyle Huey * cpuid and rdmsr (among others) trigger VM exits and are then 1320a982d78SKyle Huey * emulated. Test that single stepping works on emulated instructions. 1330a982d78SKyle Huey */ 1340a982d78SKyle Huey n = 0; 135*7f8f7356SKrish Sadhukhan write_dr6(0); 1360a982d78SKyle Huey asm volatile( 1370a982d78SKyle Huey "pushf\n\t" 1380a982d78SKyle Huey "pop %%rax\n\t" 1390a982d78SKyle Huey "or $(1<<8),%%rax\n\t" 1400a982d78SKyle Huey "push %%rax\n\t" 1410a982d78SKyle Huey "lea (%%rip),%0\n\t" 1420a982d78SKyle Huey "popf\n\t" 1430a982d78SKyle Huey "and $~(1<<8),%%rax\n\t" 1440a982d78SKyle Huey "push %%rax\n\t" 1450a982d78SKyle Huey "xor %%rax,%%rax\n\t" 1460a982d78SKyle Huey "cpuid\n\t" 1470a982d78SKyle Huey "movl $0x1a0,%%ecx\n\t" 1480a982d78SKyle Huey "rdmsr\n\t" 1490a982d78SKyle Huey "popf\n\t" 15075eea510SBill Wendling : "=r" (start) : : "rax", "ebx", "ecx", "edx"); 151a299895bSThomas Huth report(n == 7 && 152230db53fSPaolo Bonzini db_addr[0] == start + 1 + 6 && dr6[0] == 0xffff4ff0 && 153230db53fSPaolo Bonzini db_addr[1] == start + 1 + 6 + 1 && dr6[1] == 0xffff4ff0 && 154230db53fSPaolo Bonzini db_addr[2] == start + 1 + 6 + 1 + 3 && dr6[2] == 0xffff4ff0 && 155230db53fSPaolo Bonzini db_addr[3] == start + 1 + 6 + 1 + 3 + 2 && dr6[3] == 0xffff4ff0 && 156230db53fSPaolo Bonzini db_addr[4] == start + 1 + 6 + 1 + 3 + 2 + 5 && dr6[4] == 0xffff4ff0 && 157230db53fSPaolo Bonzini db_addr[5] == start + 1 + 6 + 1 + 3 + 2 + 5 + 2 && dr6[5] == 0xffff4ff0 && 158a299895bSThomas Huth db_addr[6] == start + 1 + 6 + 1 + 3 + 2 + 5 + 2 + 1 && dr6[6] == 0xffff4ff0, 159a299895bSThomas Huth "single step emulated instructions"); 1600a982d78SKyle Huey 161478027f5SJan Kiszka n = 0; 162*7f8f7356SKrish Sadhukhan write_dr1((void *)&value); 163*7f8f7356SKrish Sadhukhan write_dr7(0x00d0040a); // 4-byte write 164478027f5SJan Kiszka 165c68a7ff0SPaolo Bonzini extern unsigned char hw_wp1; 166478027f5SJan Kiszka asm volatile( 167478027f5SJan Kiszka "mov $42,%%rax\n\t" 168c68a7ff0SPaolo Bonzini "mov %%rax,%0\n\t; hw_wp1:" 169478027f5SJan Kiszka : "=m" (value) : : "rax"); 170a299895bSThomas Huth report(n == 1 && 171a299895bSThomas Huth db_addr[0] == ((unsigned long)&hw_wp1) && dr6[0] == 0xffff4ff2, 172a299895bSThomas Huth "hw watchpoint (test that dr6.BS is not cleared)"); 1739e486280SPaolo Bonzini 1749e486280SPaolo Bonzini n = 0; 175*7f8f7356SKrish Sadhukhan write_dr6(0); 1769e486280SPaolo Bonzini 177c68a7ff0SPaolo Bonzini extern unsigned char hw_wp2; 1789e486280SPaolo Bonzini asm volatile( 1799e486280SPaolo Bonzini "mov $42,%%rax\n\t" 180c68a7ff0SPaolo Bonzini "mov %%rax,%0\n\t; hw_wp2:" 1819e486280SPaolo Bonzini : "=m" (value) : : "rax"); 182a299895bSThomas Huth report(n == 1 && 183a299895bSThomas Huth db_addr[0] == ((unsigned long)&hw_wp2) && dr6[0] == 0xffff0ff2, 184a299895bSThomas Huth "hw watchpoint (test that dr6.BS is not set)"); 1859e486280SPaolo Bonzini 1869e486280SPaolo Bonzini n = 0; 187*7f8f7356SKrish Sadhukhan write_dr6(0); 188c68a7ff0SPaolo Bonzini extern unsigned char sw_icebp; 189c68a7ff0SPaolo Bonzini asm volatile(".byte 0xf1; sw_icebp:"); 190a299895bSThomas Huth report(n == 1 && 191a299895bSThomas Huth db_addr[0] == (unsigned long)&sw_icebp && dr6[0] == 0xffff0ff0, 192a299895bSThomas Huth "icebp"); 193478027f5SJan Kiszka 194*7f8f7356SKrish Sadhukhan write_dr7(0x400); 195a094abddSPaolo Bonzini value = KERNEL_DS; 196*7f8f7356SKrish Sadhukhan write_dr7(0x00f0040a); // 4-byte read or write 197a094abddSPaolo Bonzini 198a094abddSPaolo Bonzini /* 199a094abddSPaolo Bonzini * Each invocation of the handler should shift n by 1 and set bit 0 to 1. 200a094abddSPaolo Bonzini * We expect a single invocation, so n should become 3. If the entry 201a094abddSPaolo Bonzini * RIP is wrong, or if the handler is executed more than once, the value 202a094abddSPaolo Bonzini * will not match. 203a094abddSPaolo Bonzini */ 204a094abddSPaolo Bonzini set_idt_entry(1, &handle_db_save_rip, 0); 205a094abddSPaolo Bonzini 206a094abddSPaolo Bonzini n = 1; 207a094abddSPaolo Bonzini asm volatile( 208a094abddSPaolo Bonzini "clc\n\t" 209a094abddSPaolo Bonzini "mov %0,%%ss\n\t" 210a094abddSPaolo Bonzini ".byte 0x2e, 0x2e, 0xf1" 211a094abddSPaolo Bonzini : "=m" (value) : : "rax"); 212a299895bSThomas Huth report(n == 3, "MOV SS + watchpoint + ICEBP"); 213a094abddSPaolo Bonzini 214a094abddSPaolo Bonzini /* 215a094abddSPaolo Bonzini * Here the #DB handler is invoked twice, once as a software exception 216a094abddSPaolo Bonzini * and once as a software interrupt. 217a094abddSPaolo Bonzini */ 218a094abddSPaolo Bonzini n = 1; 219a094abddSPaolo Bonzini asm volatile( 220a094abddSPaolo Bonzini "clc\n\t" 221a094abddSPaolo Bonzini "mov %0,%%ss\n\t" 222a094abddSPaolo Bonzini "int $1" 223a094abddSPaolo Bonzini : "=m" (value) : : "rax"); 224a299895bSThomas Huth report(n == 7, "MOV SS + watchpoint + int $1"); 225a094abddSPaolo Bonzini 226a094abddSPaolo Bonzini /* 227a094abddSPaolo Bonzini * Here the #DB and #BP handlers are invoked once each. 228a094abddSPaolo Bonzini */ 229a094abddSPaolo Bonzini n = 1; 230a094abddSPaolo Bonzini bp_addr = 0; 231a094abddSPaolo Bonzini asm volatile( 232a094abddSPaolo Bonzini "mov %0,%%ss\n\t" 233a094abddSPaolo Bonzini ".byte 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0xcc\n\t" 234a094abddSPaolo Bonzini "sw_bp2:" 235a094abddSPaolo Bonzini : "=m" (value) : : "rax"); 236a094abddSPaolo Bonzini extern unsigned char sw_bp2; 237a299895bSThomas Huth report(n == 3 && bp_addr == (unsigned long)&sw_bp2, 238a299895bSThomas Huth "MOV SS + watchpoint + INT3"); 23986065ca2SAndrew Jones return report_summary(); 240478027f5SJan Kiszka } 241