|  | @@ -0,0 +1,126 @@
 | 
		
	
		
			
			|  | 1 | +#include <stddef.h>
 | 
		
	
		
			
			|  | 2 | +#include <stdio.h>
 | 
		
	
		
			
			|  | 3 | +#include <assert.h>
 | 
		
	
		
			
			|  | 4 | +#include <virtaddr.h>
 | 
		
	
		
			
			|  | 5 | +#include <gpxe/gdbstub.h>
 | 
		
	
		
			
			|  | 6 | +#include <gdbmach.h>
 | 
		
	
		
			
			|  | 7 | +
 | 
		
	
		
			
			|  | 8 | +enum {
 | 
		
	
		
			
			|  | 9 | +	DR7_CLEAR = 0x00000400,    /* disable hardware breakpoints */
 | 
		
	
		
			
			|  | 10 | +	DR6_CLEAR = 0xffff0ff0,    /* clear breakpoint status */
 | 
		
	
		
			
			|  | 11 | +};
 | 
		
	
		
			
			|  | 12 | +
 | 
		
	
		
			
			|  | 13 | +/** Hardware breakpoint, fields stored in x86 bit pattern form */
 | 
		
	
		
			
			|  | 14 | +struct hwbp {
 | 
		
	
		
			
			|  | 15 | +	int type;           /* type (1=write watchpoint, 3=access watchpoint) */
 | 
		
	
		
			
			|  | 16 | +	unsigned long addr; /* linear address */
 | 
		
	
		
			
			|  | 17 | +	size_t len;         /* length (0=1-byte, 1=2-byte, 3=4-byte) */
 | 
		
	
		
			
			|  | 18 | +	int enabled;
 | 
		
	
		
			
			|  | 19 | +};
 | 
		
	
		
			
			|  | 20 | +
 | 
		
	
		
			
			|  | 21 | +static struct hwbp hwbps [ 4 ];
 | 
		
	
		
			
			|  | 22 | +static gdbreg_t dr7 = DR7_CLEAR;
 | 
		
	
		
			
			|  | 23 | +static gdbreg_t dr6;
 | 
		
	
		
			
			|  | 24 | +
 | 
		
	
		
			
			|  | 25 | +static struct hwbp *gdbmach_find_hwbp ( int type, unsigned long addr, size_t len ) {
 | 
		
	
		
			
			|  | 26 | +	struct hwbp *available = NULL;
 | 
		
	
		
			
			|  | 27 | +	unsigned int i;
 | 
		
	
		
			
			|  | 28 | +	for ( i = 0; i < sizeof hwbps / sizeof hwbps [ 0 ]; i++ ) {
 | 
		
	
		
			
			|  | 29 | +		if ( hwbps [ i ].type == type && hwbps [ i ].addr == addr && hwbps [ i ].len == len ) {
 | 
		
	
		
			
			|  | 30 | +			return &hwbps [ i ];
 | 
		
	
		
			
			|  | 31 | +		}
 | 
		
	
		
			
			|  | 32 | +		if ( !hwbps [ i ].enabled ) {
 | 
		
	
		
			
			|  | 33 | +			available = &hwbps [ i ];
 | 
		
	
		
			
			|  | 34 | +		}
 | 
		
	
		
			
			|  | 35 | +	}
 | 
		
	
		
			
			|  | 36 | +	return available;
 | 
		
	
		
			
			|  | 37 | +}
 | 
		
	
		
			
			|  | 38 | +
 | 
		
	
		
			
			|  | 39 | +static void gdbmach_commit_hwbp ( struct hwbp *bp ) {
 | 
		
	
		
			
			|  | 40 | +	int regnum = bp - hwbps;
 | 
		
	
		
			
			|  | 41 | +
 | 
		
	
		
			
			|  | 42 | +	/* Set breakpoint address */
 | 
		
	
		
			
			|  | 43 | +	assert ( regnum >= 0 && regnum < sizeof hwbps / sizeof hwbps [ 0 ] );
 | 
		
	
		
			
			|  | 44 | +	switch ( regnum ) {
 | 
		
	
		
			
			|  | 45 | +		case 0:
 | 
		
	
		
			
			|  | 46 | +			__asm__ __volatile__ ( "movl %0, %%dr0\n" : : "r" ( bp->addr ) );
 | 
		
	
		
			
			|  | 47 | +			break;
 | 
		
	
		
			
			|  | 48 | +		case 1:
 | 
		
	
		
			
			|  | 49 | +			__asm__ __volatile__ ( "movl %0, %%dr1\n" : : "r" ( bp->addr ) );
 | 
		
	
		
			
			|  | 50 | +			break;
 | 
		
	
		
			
			|  | 51 | +		case 2:
 | 
		
	
		
			
			|  | 52 | +			__asm__ __volatile__ ( "movl %0, %%dr2\n" : : "r" ( bp->addr ) );
 | 
		
	
		
			
			|  | 53 | +			break;
 | 
		
	
		
			
			|  | 54 | +		case 3:
 | 
		
	
		
			
			|  | 55 | +			__asm__ __volatile__ ( "movl %0, %%dr3\n" : : "r" ( bp->addr ) );
 | 
		
	
		
			
			|  | 56 | +			break;
 | 
		
	
		
			
			|  | 57 | +	}
 | 
		
	
		
			
			|  | 58 | +
 | 
		
	
		
			
			|  | 59 | +	/* Set type */
 | 
		
	
		
			
			|  | 60 | +	dr7 &= ~( 0x3 << ( 16 + 4 * regnum ) );
 | 
		
	
		
			
			|  | 61 | +	dr7 |= bp->type << ( 16 + 4 * regnum );
 | 
		
	
		
			
			|  | 62 | +
 | 
		
	
		
			
			|  | 63 | +	/* Set length */
 | 
		
	
		
			
			|  | 64 | +	dr7 &= ~( 0x3 << ( 18 + 4 * regnum ) );
 | 
		
	
		
			
			|  | 65 | +	dr7 |= bp->len << ( 18 + 4 * regnum );
 | 
		
	
		
			
			|  | 66 | +
 | 
		
	
		
			
			|  | 67 | +	/* Set/clear local enable bit */
 | 
		
	
		
			
			|  | 68 | +	dr7 &= ~( 0x3 << 2 * regnum );
 | 
		
	
		
			
			|  | 69 | + 	dr7 |= bp->enabled << 2 * regnum;
 | 
		
	
		
			
			|  | 70 | +}
 | 
		
	
		
			
			|  | 71 | +
 | 
		
	
		
			
			|  | 72 | +int gdbmach_set_breakpoint ( int type, unsigned long addr, size_t len, int enable ) {
 | 
		
	
		
			
			|  | 73 | +	struct hwbp *bp;
 | 
		
	
		
			
			|  | 74 | +	
 | 
		
	
		
			
			|  | 75 | +	/* Check and convert breakpoint type to x86 type */
 | 
		
	
		
			
			|  | 76 | +	switch ( type ) {
 | 
		
	
		
			
			|  | 77 | +		case GDBMACH_WATCH:
 | 
		
	
		
			
			|  | 78 | +			type = 0x1;
 | 
		
	
		
			
			|  | 79 | +			break;
 | 
		
	
		
			
			|  | 80 | +		case GDBMACH_AWATCH:
 | 
		
	
		
			
			|  | 81 | +			type = 0x3;
 | 
		
	
		
			
			|  | 82 | +			break;
 | 
		
	
		
			
			|  | 83 | +		default:
 | 
		
	
		
			
			|  | 84 | +			return 0; /* unsupported breakpoint type */
 | 
		
	
		
			
			|  | 85 | +	}
 | 
		
	
		
			
			|  | 86 | +
 | 
		
	
		
			
			|  | 87 | +	/* Only lengths 1, 2, and 4 are supported */
 | 
		
	
		
			
			|  | 88 | +	if ( len != 2 && len != 4 ) {
 | 
		
	
		
			
			|  | 89 | +		len = 1;
 | 
		
	
		
			
			|  | 90 | +	}
 | 
		
	
		
			
			|  | 91 | +	len--; /* convert to x86 breakpoint length bit pattern */
 | 
		
	
		
			
			|  | 92 | +
 | 
		
	
		
			
			|  | 93 | +	/* Calculate linear address by adding segment base */
 | 
		
	
		
			
			|  | 94 | +	addr += virt_offset;
 | 
		
	
		
			
			|  | 95 | +
 | 
		
	
		
			
			|  | 96 | +	/* Set up the breakpoint */
 | 
		
	
		
			
			|  | 97 | +	bp = gdbmach_find_hwbp ( type, addr, len );
 | 
		
	
		
			
			|  | 98 | +	if ( !bp ) {
 | 
		
	
		
			
			|  | 99 | +		return 0; /* ran out of hardware breakpoints */
 | 
		
	
		
			
			|  | 100 | +	}
 | 
		
	
		
			
			|  | 101 | +	bp->type = type;
 | 
		
	
		
			
			|  | 102 | +	bp->addr = addr;
 | 
		
	
		
			
			|  | 103 | +	bp->len = len;
 | 
		
	
		
			
			|  | 104 | +	bp->enabled = enable;
 | 
		
	
		
			
			|  | 105 | +	gdbmach_commit_hwbp ( bp );
 | 
		
	
		
			
			|  | 106 | +	return 1;
 | 
		
	
		
			
			|  | 107 | +}
 | 
		
	
		
			
			|  | 108 | +
 | 
		
	
		
			
			|  | 109 | +static void gdbmach_disable_hwbps ( void ) {
 | 
		
	
		
			
			|  | 110 | +	/* Store and clear breakpoint status register */
 | 
		
	
		
			
			|  | 111 | +	__asm__ __volatile__ ( "movl %%dr6, %0\n" "movl %1, %%dr6\n" : "=r" ( dr6 ) : "r" ( DR6_CLEAR ) );
 | 
		
	
		
			
			|  | 112 | +
 | 
		
	
		
			
			|  | 113 | +	/* Store and clear hardware breakpoints */
 | 
		
	
		
			
			|  | 114 | +	__asm__ __volatile__ ( "movl %0, %%dr7\n" : : "r" ( DR7_CLEAR ) );
 | 
		
	
		
			
			|  | 115 | +}
 | 
		
	
		
			
			|  | 116 | +
 | 
		
	
		
			
			|  | 117 | +static void gdbmach_enable_hwbps ( void ) {
 | 
		
	
		
			
			|  | 118 | +	/* Restore hardware breakpoints */
 | 
		
	
		
			
			|  | 119 | +	__asm__ __volatile__ ( "movl %0, %%dr7\n" : : "r" ( dr7 ) );
 | 
		
	
		
			
			|  | 120 | +}
 | 
		
	
		
			
			|  | 121 | +
 | 
		
	
		
			
			|  | 122 | +__cdecl void gdbmach_handler ( int signo, gdbreg_t *regs ) {
 | 
		
	
		
			
			|  | 123 | +	gdbmach_disable_hwbps();
 | 
		
	
		
			
			|  | 124 | +	gdbstub_handler ( signo, regs );
 | 
		
	
		
			
			|  | 125 | +	gdbmach_enable_hwbps();
 | 
		
	
		
			
			|  | 126 | +}
 |