These changes are the raw update to qemu-2.6.
[kvmfornfv.git] / qemu / roms / ipxe / 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., 51 Franklin Street, Fifth Floor, Boston, MA
17  * 02110-1301, USA.
18  *
19  * You can also choose to distribute this program under the terms of
20  * the Unmodified Binary Distribution Licence (as given in the file
21  * COPYING.UBDL), provided that you have satisfied its requirements.
22  */
23
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25
26 #include <stddef.h>
27 #include <stdio.h>
28 #include <assert.h>
29 #include <ipxe/uaccess.h>
30 #include <ipxe/gdbstub.h>
31 #include <librm.h>
32 #include <gdbmach.h>
33
34 /** @file
35  *
36  * GDB architecture-specific bits for i386
37  *
38  */
39
40 enum {
41         DR7_CLEAR = 0x00000400,    /* disable hardware breakpoints */
42         DR6_CLEAR = 0xffff0ff0,    /* clear breakpoint status */
43 };
44
45 /** Hardware breakpoint, fields stored in x86 bit pattern form */
46 struct hwbp {
47         int type;           /* type (1=write watchpoint, 3=access watchpoint) */
48         unsigned long addr; /* linear address */
49         size_t len;         /* length (0=1-byte, 1=2-byte, 3=4-byte) */
50         int enabled;
51 };
52
53 static struct hwbp hwbps [ 4 ];
54 static gdbreg_t dr7 = DR7_CLEAR;
55
56 static struct hwbp *gdbmach_find_hwbp ( int type, unsigned long addr, size_t len ) {
57         struct hwbp *available = NULL;
58         unsigned int i;
59         for ( i = 0; i < sizeof hwbps / sizeof hwbps [ 0 ]; i++ ) {
60                 if ( hwbps [ i ].type == type && hwbps [ i ].addr == addr && hwbps [ i ].len == len ) {
61                         return &hwbps [ i ];
62                 }
63                 if ( !hwbps [ i ].enabled ) {
64                         available = &hwbps [ i ];
65                 }
66         }
67         return available;
68 }
69
70 static void gdbmach_commit_hwbp ( struct hwbp *bp ) {
71         unsigned int regnum = bp - hwbps;
72
73         /* Set breakpoint address */
74         assert ( regnum < ( sizeof hwbps / sizeof hwbps [ 0 ] ) );
75         switch ( regnum ) {
76                 case 0:
77                         __asm__ __volatile__ ( "movl %0, %%dr0\n" : : "r" ( bp->addr ) );
78                         break;
79                 case 1:
80                         __asm__ __volatile__ ( "movl %0, %%dr1\n" : : "r" ( bp->addr ) );
81                         break;
82                 case 2:
83                         __asm__ __volatile__ ( "movl %0, %%dr2\n" : : "r" ( bp->addr ) );
84                         break;
85                 case 3:
86                         __asm__ __volatile__ ( "movl %0, %%dr3\n" : : "r" ( bp->addr ) );
87                         break;
88         }
89
90         /* Set type */
91         dr7 &= ~( 0x3 << ( 16 + 4 * regnum ) );
92         dr7 |= bp->type << ( 16 + 4 * regnum );
93
94         /* Set length */
95         dr7 &= ~( 0x3 << ( 18 + 4 * regnum ) );
96         dr7 |= bp->len << ( 18 + 4 * regnum );
97
98         /* Set/clear local enable bit */
99         dr7 &= ~( 0x3 << 2 * regnum );
100         dr7 |= bp->enabled << 2 * regnum;
101 }
102
103 int gdbmach_set_breakpoint ( int type, unsigned long addr, size_t len, int enable ) {
104         struct hwbp *bp;
105         
106         /* Check and convert breakpoint type to x86 type */
107         switch ( type ) {
108                 case GDBMACH_WATCH:
109                         type = 0x1;
110                         break;
111                 case GDBMACH_AWATCH:
112                         type = 0x3;
113                         break;
114                 default:
115                         return 0; /* unsupported breakpoint type */
116         }
117
118         /* Only lengths 1, 2, and 4 are supported */
119         if ( len != 2 && len != 4 ) {
120                 len = 1;
121         }
122         len--; /* convert to x86 breakpoint length bit pattern */
123
124         /* Calculate linear address by adding segment base */
125         addr += virt_offset;
126
127         /* Set up the breakpoint */
128         bp = gdbmach_find_hwbp ( type, addr, len );
129         if ( !bp ) {
130                 return 0; /* ran out of hardware breakpoints */
131         }
132         bp->type = type;
133         bp->addr = addr;
134         bp->len = len;
135         bp->enabled = enable;
136         gdbmach_commit_hwbp ( bp );
137         return 1;
138 }
139
140 static void gdbmach_disable_hwbps ( void ) {
141         /* Store and clear hardware breakpoints */
142         __asm__ __volatile__ ( "movl %0, %%dr7\n" : : "r" ( DR7_CLEAR ) );
143 }
144
145 static void gdbmach_enable_hwbps ( void ) {
146         /* Clear breakpoint status register */
147         __asm__ __volatile__ ( "movl %0, %%dr6\n" : : "r" ( DR6_CLEAR ) );
148
149         /* Restore hardware breakpoints */
150         __asm__ __volatile__ ( "movl %0, %%dr7\n" : : "r" ( dr7 ) );
151 }
152
153 __asmcall void gdbmach_handler ( int signo, gdbreg_t *regs ) {
154         gdbmach_disable_hwbps();
155         gdbstub_handler ( signo, regs );
156         gdbmach_enable_hwbps();
157 }
158
159 static void * gdbmach_interrupt_vectors[] = {
160         gdbmach_nocode_sigfpe,          /* Divide by zero */
161         gdbmach_nocode_sigtrap,         /* Debug trap */
162         NULL,                           /* Non-maskable interrupt */
163         gdbmach_nocode_sigtrap,         /* Breakpoint */
164         gdbmach_nocode_sigstkflt,       /* Overflow */
165         gdbmach_nocode_sigstkflt,       /* Bound range exceeded */
166         gdbmach_nocode_sigill,          /* Invalid opcode */
167         NULL,                           /* Device not available */
168         gdbmach_withcode_sigbus,        /* Double fault */
169         NULL,                           /* Coprocessor segment overrun */
170         gdbmach_withcode_sigsegv,       /* Invalid TSS */
171         gdbmach_withcode_sigsegv,       /* Segment not present */
172         gdbmach_withcode_sigsegv,       /* Stack segment fault */
173         gdbmach_withcode_sigsegv,       /* General protection fault */
174         gdbmach_withcode_sigsegv,       /* Page fault */
175 };
176
177 void gdbmach_init ( void ) {
178         unsigned int i;
179
180         for ( i = 0 ; i < ( sizeof ( gdbmach_interrupt_vectors ) /
181                             sizeof ( gdbmach_interrupt_vectors[0] ) ) ; i++ ) {
182                 set_interrupt_vector ( i, gdbmach_interrupt_vectors[i] );
183         }
184 }