Initial code release
[external/syslinux.git] / gpxe / 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 <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
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
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
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
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
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
145 __asmcall void gdbmach_handler ( int signo, gdbreg_t *regs ) {
146         gdbmach_disable_hwbps();
147         gdbstub_handler ( signo, regs );
148         gdbmach_enable_hwbps();
149 }