Home | History | Annotate | Download | only in core
      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