/* * Test for x86 debugging facilities * * Copyright (c) Siemens AG, 2014 * * Authors: * Jan Kiszka * * This work is licensed under the terms of the GNU GPL, version 2. */ #include "libcflat.h" #include "desc.h" static volatile unsigned long bp_addr[10], dr6[10]; static volatile unsigned int n; static volatile unsigned long value; static unsigned long get_dr6(void) { unsigned long value; asm volatile("mov %%dr6,%0" : "=r" (value)); return value; } static void set_dr0(void *value) { asm volatile("mov %0,%%dr0" : : "r" (value)); } static void set_dr1(void *value) { asm volatile("mov %0,%%dr1" : : "r" (value)); } static void set_dr6(unsigned long value) { asm volatile("mov %0,%%dr6" : : "r" (value)); } static void set_dr7(unsigned long value) { asm volatile("mov %0,%%dr7" : : "r" (value)); } static void handle_db(struct ex_regs *regs) { bp_addr[n] = regs->rip; dr6[n] = get_dr6(); if (dr6[n] & 0x1) regs->rflags |= (1 << 16); if (++n >= 10) { regs->rflags &= ~(1 << 8); set_dr7(0x00000400); } } static void handle_bp(struct ex_regs *regs) { bp_addr[0] = regs->rip; } int main(int ac, char **av) { unsigned long start; setup_idt(); handle_exception(DB_VECTOR, handle_db); handle_exception(BP_VECTOR, handle_bp); extern unsigned char sw_bp; asm volatile("int3; sw_bp:"); report("#BP", bp_addr[0] == (unsigned long)&sw_bp); n = 0; extern unsigned char hw_bp1; set_dr0(&hw_bp1); set_dr7(0x00000402); asm volatile("hw_bp1: nop"); report("hw breakpoint (test that dr6.BS is not set)", n == 1 && bp_addr[0] == ((unsigned long)&hw_bp1) && dr6[0] == 0xffff0ff1); n = 0; extern unsigned char hw_bp2; set_dr0(&hw_bp2); set_dr6(0x00004002); asm volatile("hw_bp2: nop"); report("hw breakpoint (test that dr6.BS is not cleared)", n == 1 && bp_addr[0] == ((unsigned long)&hw_bp2) && dr6[0] == 0xffff4ff1); n = 0; set_dr6(0); asm volatile( "pushf\n\t" "pop %%rax\n\t" "or $(1<<8),%%rax\n\t" "push %%rax\n\t" "lea (%%rip),%0\n\t" "popf\n\t" "and $~(1<<8),%%rax\n\t" "push %%rax\n\t" "popf\n\t" : "=g" (start) : : "rax"); report("single step", n == 3 && bp_addr[0] == start+1+6 && dr6[0] == 0xffff4ff0 && bp_addr[1] == start+1+6+1 && dr6[1] == 0xffff4ff0 && bp_addr[2] == start+1+6+1+1 && dr6[2] == 0xffff4ff0); /* * cpuid and rdmsr (among others) trigger VM exits and are then * emulated. Test that single stepping works on emulated instructions. */ n = 0; set_dr6(0); asm volatile( "pushf\n\t" "pop %%rax\n\t" "or $(1<<8),%%rax\n\t" "push %%rax\n\t" "lea (%%rip),%0\n\t" "popf\n\t" "and $~(1<<8),%%rax\n\t" "push %%rax\n\t" "xor %%rax,%%rax\n\t" "cpuid\n\t" "movl $0x1a0,%%ecx\n\t" "rdmsr\n\t" "popf\n\t" : "=g" (start) : : "rax", "ebx", "ecx", "edx"); report("single step emulated instructions", n == 7 && bp_addr[0] == start+1+6 && dr6[0] == 0xffff4ff0 && bp_addr[1] == start+1+6+1 && dr6[1] == 0xffff4ff0 && bp_addr[2] == start+1+6+1+3 && dr6[2] == 0xffff4ff0 && bp_addr[3] == start+1+6+1+3+2 && dr6[3] == 0xffff4ff0 && bp_addr[4] == start+1+6+1+3+2+5 && dr6[4] == 0xffff4ff0 && bp_addr[5] == start+1+6+1+3+2+5+2 && dr6[5] == 0xffff4ff0 && bp_addr[6] == start+1+6+1+3+2+5+2+1 && dr6[6] == 0xffff4ff0); n = 0; set_dr1((void *)&value); set_dr7(0x00d0040a); extern unsigned char hw_wp1; asm volatile( "mov $42,%%rax\n\t" "mov %%rax,%0\n\t; hw_wp1:" : "=m" (value) : : "rax"); report("hw watchpoint (test that dr6.BS is not cleared)", n == 1 && bp_addr[0] == ((unsigned long)&hw_wp1) && dr6[0] == 0xffff4ff2); n = 0; set_dr6(0); extern unsigned char hw_wp2; asm volatile( "mov $42,%%rax\n\t" "mov %%rax,%0\n\t; hw_wp2:" : "=m" (value) : : "rax"); report("hw watchpoint (test that dr6.BS is not set)", n == 1 && bp_addr[0] == ((unsigned long)&hw_wp2) && dr6[0] == 0xffff0ff2); n = 0; set_dr6(0); extern unsigned char sw_icebp; asm volatile(".byte 0xf1; sw_icebp:"); report("icebp", n == 1 && bp_addr[0] == (unsigned long)&sw_icebp && dr6[0] == 0xffff0ff0); return report_summary(); }