1 /* 2 * Copyright (C) 2008 Stefan Hajnoczi <stefanha (at) gmail.com>. 3 * 4 * This program is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU General Public License as 6 * published by the Free Software Foundation; either version 2 of the 7 * License, or any later version. 8 * 9 * This program is distributed in the hope that it will be useful, but 10 * WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, write to the Free Software 16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 17 */ 18 19 #include <stddef.h> 20 #include <stdio.h> 21 #include <assert.h> 22 #include <gpxe/uaccess.h> 23 #include <gpxe/gdbstub.h> 24 #include <gdbmach.h> 25 26 /** @file 27 * 28 * GDB architecture-specific bits for i386 29 * 30 */ 31 32 enum { 33 DR7_CLEAR = 0x00000400, /* disable hardware breakpoints */ 34 DR6_CLEAR = 0xffff0ff0, /* clear breakpoint status */ 35 }; 36 37 /** Hardware breakpoint, fields stored in x86 bit pattern form */ 38 struct hwbp { 39 int type; /* type (1=write watchpoint, 3=access watchpoint) */ 40 unsigned long addr; /* linear address */ 41 size_t len; /* length (0=1-byte, 1=2-byte, 3=4-byte) */ 42 int enabled; 43 }; 44 45 static struct hwbp hwbps [ 4 ]; 46 static gdbreg_t dr7 = DR7_CLEAR; 47 48 static struct hwbp *gdbmach_find_hwbp ( int type, unsigned long addr, size_t len ) { 49 struct hwbp *available = NULL; 50 unsigned int i; 51 for ( i = 0; i < sizeof hwbps / sizeof hwbps [ 0 ]; i++ ) { 52 if ( hwbps [ i ].type == type && hwbps [ i ].addr == addr && hwbps [ i ].len == len ) { 53 return &hwbps [ i ]; 54 } 55 if ( !hwbps [ i ].enabled ) { 56 available = &hwbps [ i ]; 57 } 58 } 59 return available; 60 } 61 62 static void gdbmach_commit_hwbp ( struct hwbp *bp ) { 63 unsigned int regnum = bp - hwbps; 64 65 /* Set breakpoint address */ 66 assert ( regnum < ( sizeof hwbps / sizeof hwbps [ 0 ] ) ); 67 switch ( regnum ) { 68 case 0: 69 __asm__ __volatile__ ( "movl %0, %%dr0\n" : : "r" ( bp->addr ) ); 70 break; 71 case 1: 72 __asm__ __volatile__ ( "movl %0, %%dr1\n" : : "r" ( bp->addr ) ); 73 break; 74 case 2: 75 __asm__ __volatile__ ( "movl %0, %%dr2\n" : : "r" ( bp->addr ) ); 76 break; 77 case 3: 78 __asm__ __volatile__ ( "movl %0, %%dr3\n" : : "r" ( bp->addr ) ); 79 break; 80 } 81 82 /* Set type */ 83 dr7 &= ~( 0x3 << ( 16 + 4 * regnum ) ); 84 dr7 |= bp->type << ( 16 + 4 * regnum ); 85 86 /* Set length */ 87 dr7 &= ~( 0x3 << ( 18 + 4 * regnum ) ); 88 dr7 |= bp->len << ( 18 + 4 * regnum ); 89 90 /* Set/clear local enable bit */ 91 dr7 &= ~( 0x3 << 2 * regnum ); 92 dr7 |= bp->enabled << 2 * regnum; 93 } 94 95 int gdbmach_set_breakpoint ( int type, unsigned long addr, size_t len, int enable ) { 96 struct hwbp *bp; 97 98 /* Check and convert breakpoint type to x86 type */ 99 switch ( type ) { 100 case GDBMACH_WATCH: 101 type = 0x1; 102 break; 103 case GDBMACH_AWATCH: 104 type = 0x3; 105 break; 106 default: 107 return 0; /* unsupported breakpoint type */ 108 } 109 110 /* Only lengths 1, 2, and 4 are supported */ 111 if ( len != 2 && len != 4 ) { 112 len = 1; 113 } 114 len--; /* convert to x86 breakpoint length bit pattern */ 115 116 /* Calculate linear address by adding segment base */ 117 addr += virt_offset; 118 119 /* Set up the breakpoint */ 120 bp = gdbmach_find_hwbp ( type, addr, len ); 121 if ( !bp ) { 122 return 0; /* ran out of hardware breakpoints */ 123 } 124 bp->type = type; 125 bp->addr = addr; 126 bp->len = len; 127 bp->enabled = enable; 128 gdbmach_commit_hwbp ( bp ); 129 return 1; 130 } 131 132 static void gdbmach_disable_hwbps ( void ) { 133 /* Store and clear hardware breakpoints */ 134 __asm__ __volatile__ ( "movl %0, %%dr7\n" : : "r" ( DR7_CLEAR ) ); 135 } 136 137 static void gdbmach_enable_hwbps ( void ) { 138 /* Clear breakpoint status register */ 139 __asm__ __volatile__ ( "movl %0, %%dr6\n" : : "r" ( DR6_CLEAR ) ); 140 141 /* Restore hardware breakpoints */ 142 __asm__ __volatile__ ( "movl %0, %%dr7\n" : : "r" ( dr7 ) ); 143 } 144 145 __asmcall void gdbmach_handler ( int signo, gdbreg_t *regs ) { 146 gdbmach_disable_hwbps(); 147 gdbstub_handler ( signo, regs ); 148 gdbmach_enable_hwbps(); 149 } 150