[GDB] Add copyright header for gdbmach.c
[people/andreif/gpxe.git] / src / arch / i386 / core / gdbmach.c
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 <virtaddr.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 static gdbreg_t dr6;
48
49 static struct hwbp *gdbmach_find_hwbp ( int type, unsigned long addr, size_t len ) {
50         struct hwbp *available = NULL;
51         unsigned int i;
52         for ( i = 0; i < sizeof hwbps / sizeof hwbps [ 0 ]; i++ ) {
53                 if ( hwbps [ i ].type == type && hwbps [ i ].addr == addr && hwbps [ i ].len == len ) {
54                         return &hwbps [ i ];
55                 }
56                 if ( !hwbps [ i ].enabled ) {
57                         available = &hwbps [ i ];
58                 }
59         }
60         return available;
61 }
62
63 static void gdbmach_commit_hwbp ( struct hwbp *bp ) {
64         int regnum = bp - hwbps;
65
66         /* Set breakpoint address */
67         assert ( regnum >= 0 && regnum < sizeof hwbps / sizeof hwbps [ 0 ] );
68         switch ( regnum ) {
69                 case 0:
70                         __asm__ __volatile__ ( "movl %0, %%dr0\n" : : "r" ( bp->addr ) );
71                         break;
72                 case 1:
73                         __asm__ __volatile__ ( "movl %0, %%dr1\n" : : "r" ( bp->addr ) );
74                         break;
75                 case 2:
76                         __asm__ __volatile__ ( "movl %0, %%dr2\n" : : "r" ( bp->addr ) );
77                         break;
78                 case 3:
79                         __asm__ __volatile__ ( "movl %0, %%dr3\n" : : "r" ( bp->addr ) );
80                         break;
81         }
82
83         /* Set type */
84         dr7 &= ~( 0x3 << ( 16 + 4 * regnum ) );
85         dr7 |= bp->type << ( 16 + 4 * regnum );
86
87         /* Set length */
88         dr7 &= ~( 0x3 << ( 18 + 4 * regnum ) );
89         dr7 |= bp->len << ( 18 + 4 * regnum );
90
91         /* Set/clear local enable bit */
92         dr7 &= ~( 0x3 << 2 * regnum );
93         dr7 |= bp->enabled << 2 * regnum;
94 }
95
96 int gdbmach_set_breakpoint ( int type, unsigned long addr, size_t len, int enable ) {
97         struct hwbp *bp;
98         
99         /* Check and convert breakpoint type to x86 type */
100         switch ( type ) {
101                 case GDBMACH_WATCH:
102                         type = 0x1;
103                         break;
104                 case GDBMACH_AWATCH:
105                         type = 0x3;
106                         break;
107                 default:
108                         return 0; /* unsupported breakpoint type */
109         }
110
111         /* Only lengths 1, 2, and 4 are supported */
112         if ( len != 2 && len != 4 ) {
113                 len = 1;
114         }
115         len--; /* convert to x86 breakpoint length bit pattern */
116
117         /* Calculate linear address by adding segment base */
118         addr += virt_offset;
119
120         /* Set up the breakpoint */
121         bp = gdbmach_find_hwbp ( type, addr, len );
122         if ( !bp ) {
123                 return 0; /* ran out of hardware breakpoints */
124         }
125         bp->type = type;
126         bp->addr = addr;
127         bp->len = len;
128         bp->enabled = enable;
129         gdbmach_commit_hwbp ( bp );
130         return 1;
131 }
132
133 static void gdbmach_disable_hwbps ( void ) {
134         /* Store and clear breakpoint status register */
135         __asm__ __volatile__ ( "movl %%dr6, %0\n" "movl %1, %%dr6\n" : "=r" ( dr6 ) : "r" ( DR6_CLEAR ) );
136
137         /* Store and clear hardware breakpoints */
138         __asm__ __volatile__ ( "movl %0, %%dr7\n" : : "r" ( DR7_CLEAR ) );
139 }
140
141 static void gdbmach_enable_hwbps ( void ) {
142         /* Restore hardware breakpoints */
143         __asm__ __volatile__ ( "movl %0, %%dr7\n" : : "r" ( dr7 ) );
144 }
145
146 __cdecl void gdbmach_handler ( int signo, gdbreg_t *regs ) {
147         gdbmach_disable_hwbps();
148         gdbstub_handler ( signo, regs );
149         gdbmach_enable_hwbps();
150 }