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