Browse Source

[GDB] Add watch and rwatch hardware watchpoints

tags/v0.9.4
Stefan Hajnoczi 16 years ago
parent
commit
19386ec2c8

+ 1
- 1
src/arch/i386/core/gdbidt.S View File

@@ -184,7 +184,7 @@ do_interrupt:
184 184
 	/* Call GDB stub exception handler */
185 185
 	pushl	%esp
186 186
 	pushl	(IH_OFFSET_SIGNO + 4)(%esp)
187
-	call	gdbstub_handler
187
+	call	gdbmach_handler
188 188
 	addl	$8, %esp
189 189
 
190 190
 	/* Restore CPU state from GDB register snapshot */

+ 126
- 0
src/arch/i386/core/gdbmach.c View File

@@ -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
+}

+ 13
- 0
src/arch/i386/include/gdbmach.h View File

@@ -10,6 +10,8 @@
10 10
  *
11 11
  */
12 12
 
13
+#include <stdint.h>
14
+
13 15
 typedef uint32_t gdbreg_t;
14 16
 
15 17
 /* The register snapshot, this must be in sync with interrupt handler and the
@@ -35,6 +37,15 @@ enum {
35 37
 	GDBMACH_SIZEOF_REGS = GDBMACH_NREGS * sizeof ( gdbreg_t )
36 38
 };
37 39
 
40
+/* Breakpoint types */
41
+enum {
42
+	GDBMACH_BPMEM,
43
+	GDBMACH_BPHW,
44
+	GDBMACH_WATCH,
45
+	GDBMACH_RWATCH,
46
+	GDBMACH_AWATCH,
47
+};
48
+
38 49
 static inline void gdbmach_set_pc ( gdbreg_t *regs, gdbreg_t pc ) {
39 50
 	regs [ GDBMACH_EIP ] = pc;
40 51
 }
@@ -48,4 +59,6 @@ static inline void gdbmach_breakpoint ( void ) {
48 59
 	__asm__ __volatile__ ( "int $3\n" );
49 60
 }
50 61
 
62
+extern int gdbmach_set_breakpoint ( int type, unsigned long addr, size_t len, int enable );
63
+
51 64
 #endif /* GDBMACH_H */

+ 21
- 1
src/core/gdbstub.c View File

@@ -249,6 +249,22 @@ static void gdbstub_continue ( struct gdbstub *stub, int single_step ) {
249 249
 	/* Reply will be sent when we hit the next breakpoint or interrupt */
250 250
 }
251 251
 
252
+static void gdbstub_breakpoint ( struct gdbstub *stub ) {
253
+	unsigned long args [ 3 ];
254
+	int enable = stub->payload [ 0 ] == 'Z' ? 1 : 0;
255
+	if ( !gdbstub_get_packet_args ( stub, args, sizeof args / sizeof args [ 0 ], NULL ) ) {
256
+		gdbstub_send_errno ( stub, POSIX_EINVAL );
257
+		return;
258
+	}
259
+	if ( gdbmach_set_breakpoint ( args [ 0 ], args [ 1 ], args [ 2 ], enable ) ) {
260
+		gdbstub_send_ok ( stub );
261
+	} else {
262
+		/* Not supported */
263
+		stub->len = 0;
264
+		gdbstub_tx_packet ( stub );
265
+	}
266
+}
267
+
252 268
 static void gdbstub_rx_packet ( struct gdbstub *stub ) {
253 269
 	switch ( stub->payload [ 0 ] ) {
254 270
 		case '?':
@@ -275,6 +291,10 @@ static void gdbstub_rx_packet ( struct gdbstub *stub ) {
275 291
 				gdbstub_send_ok ( stub );
276 292
 			}
277 293
 			break;
294
+		case 'Z': /* Insert breakpoint */
295
+		case 'z': /* Remove breakpoint */
296
+			gdbstub_breakpoint ( stub );
297
+			break;
278 298
 		default:
279 299
 			stub->len = 0;
280 300
 			gdbstub_tx_packet ( stub );
@@ -341,7 +361,7 @@ static struct gdbstub stub = {
341 361
 	.parse = gdbstub_state_new
342 362
 };
343 363
 
344
-__cdecl void gdbstub_handler ( int signo, gdbreg_t *regs ) {
364
+void gdbstub_handler ( int signo, gdbreg_t *regs ) {
345 365
 	char packet [ SIZEOF_PAYLOAD + 4 ];
346 366
 	size_t len, i;
347 367
 

+ 9
- 0
src/include/gpxe/gdbstub.h View File

@@ -9,6 +9,7 @@
9 9
 
10 10
 #include <stdint.h>
11 11
 #include <gpxe/tables.h>
12
+#include <gdbmach.h>
12 13
 
13 14
 /**
14 15
  * A transport mechanism for the GDB protocol
@@ -61,4 +62,12 @@ extern struct gdb_transport *find_gdb_transport ( const char *name );
61 62
  */
62 63
 extern void gdbstub_start ( struct gdb_transport *trans );
63 64
 
65
+/**
66
+ * Interrupt handler
67
+ *
68
+ * @signo POSIX signal number
69
+ * @regs CPU register snapshot
70
+ **/
71
+extern void gdbstub_handler ( int signo, gdbreg_t *regs );
72
+
64 73
 #endif /* _GPXE_GDBSTUB_H */

+ 21
- 0
src/tests/gdbstub_test.S View File

@@ -1,4 +1,9 @@
1 1
 	.arch i386
2
+
3
+	.section ".data"
4
+watch_me:
5
+	.long 0xfeedbeef
6
+
2 7
 	.section ".text"
3 8
 	.code32
4 9
 gdbstub_test:
@@ -29,5 +34,21 @@ gdbstub_test:
29 34
 	int	$3
30 35
 	nop
31 36
 
37
+	/* 6. Access watch test */
38
+	movl	$0x600d0000, %ecx
39
+	movl	watch_me, %eax
40
+	movl	$0xbad00000, %ecx
41
+	int	$3
42
+	movl	$0x600d0001, %ecx
43
+	movl	%eax, watch_me
44
+	movl	$0xbad00001, %ecx
45
+	int	$3
46
+
47
+	/* 7. Write watch test */
48
+	movl	$0x600d0002, %ecx
49
+	movl	%eax, watch_me
50
+	movl	$0xbad00002, %ecx
51
+	int	$3
52
+
32 53
 1:
33 54
 	jmp	1b

+ 30
- 0
src/tests/gdbstub_test.gdb View File

@@ -77,6 +77,34 @@ define gpxe_test_step
77 77
 	gpxe_assert ({char}($eip-1)) (char)0x90 "gpxe_test_step" # nop = 0x90
78 78
 end
79 79
 
80
+define gpxe_test_awatch
81
+	awatch watch_me
82
+
83
+	c
84
+	gpxe_assert $ecx 0x600d0000 "gpxe_test_awatch"
85
+	if $ecx == 0x600d0000
86
+		c
87
+	end
88
+
89
+	c
90
+	gpxe_assert $ecx 0x600d0001 "gpxe_test_awatch"
91
+	if $ecx == 0x600d0001
92
+		c
93
+	end
94
+
95
+	delete
96
+end
97
+
98
+define gpxe_test_watch
99
+	watch watch_me
100
+	c
101
+	gpxe_assert $ecx 0x600d0002 "gpxe_test_watch"
102
+	if $ecx == 0x600d0002
103
+		c
104
+	end
105
+	delete
106
+end
107
+
80 108
 gpxe_load_symbols
81 109
 gpxe_start_tests
82 110
 gpxe_test_regs_read
@@ -84,3 +112,5 @@ gpxe_test_regs_write
84 112
 gpxe_test_mem_read
85 113
 gpxe_test_mem_write
86 114
 gpxe_test_step
115
+gpxe_test_awatch
116
+gpxe_test_watch

Loading…
Cancel
Save