• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 Stefan Hajnoczi <stefanha@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 
gdbmach_find_hwbp(int type,unsigned long addr,size_t len)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 
gdbmach_commit_hwbp(struct hwbp * bp)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 
gdbmach_set_breakpoint(int type,unsigned long addr,size_t len,int enable)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 
gdbmach_disable_hwbps(void)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 
gdbmach_enable_hwbps(void)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 
gdbmach_handler(int signo,gdbreg_t * regs)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