2 * Copyright (C) 2008 Stefan Hajnoczi <stefanha@gmail.com>.
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.
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.
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
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.
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
29 #include <ipxe/uaccess.h>
30 #include <ipxe/gdbstub.h>
36 * GDB architecture-specific bits for i386
41 DR7_CLEAR = 0x00000400, /* disable hardware breakpoints */
42 DR6_CLEAR = 0xffff0ff0, /* clear breakpoint status */
45 /** Hardware breakpoint, fields stored in x86 bit pattern form */
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) */
53 static struct hwbp hwbps [ 4 ];
54 static gdbreg_t dr7 = DR7_CLEAR;
56 static struct hwbp *gdbmach_find_hwbp ( int type, unsigned long addr, size_t len ) {
57 struct hwbp *available = NULL;
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 ) {
63 if ( !hwbps [ i ].enabled ) {
64 available = &hwbps [ i ];
70 static void gdbmach_commit_hwbp ( struct hwbp *bp ) {
71 unsigned int regnum = bp - hwbps;
73 /* Set breakpoint address */
74 assert ( regnum < ( sizeof hwbps / sizeof hwbps [ 0 ] ) );
77 __asm__ __volatile__ ( "movl %0, %%dr0\n" : : "r" ( bp->addr ) );
80 __asm__ __volatile__ ( "movl %0, %%dr1\n" : : "r" ( bp->addr ) );
83 __asm__ __volatile__ ( "movl %0, %%dr2\n" : : "r" ( bp->addr ) );
86 __asm__ __volatile__ ( "movl %0, %%dr3\n" : : "r" ( bp->addr ) );
91 dr7 &= ~( 0x3 << ( 16 + 4 * regnum ) );
92 dr7 |= bp->type << ( 16 + 4 * regnum );
95 dr7 &= ~( 0x3 << ( 18 + 4 * regnum ) );
96 dr7 |= bp->len << ( 18 + 4 * regnum );
98 /* Set/clear local enable bit */
99 dr7 &= ~( 0x3 << 2 * regnum );
100 dr7 |= bp->enabled << 2 * regnum;
103 int gdbmach_set_breakpoint ( int type, unsigned long addr, size_t len, int enable ) {
106 /* Check and convert breakpoint type to x86 type */
115 return 0; /* unsupported breakpoint type */
118 /* Only lengths 1, 2, and 4 are supported */
119 if ( len != 2 && len != 4 ) {
122 len--; /* convert to x86 breakpoint length bit pattern */
124 /* Calculate linear address by adding segment base */
127 /* Set up the breakpoint */
128 bp = gdbmach_find_hwbp ( type, addr, len );
130 return 0; /* ran out of hardware breakpoints */
135 bp->enabled = enable;
136 gdbmach_commit_hwbp ( bp );
140 static void gdbmach_disable_hwbps ( void ) {
141 /* Store and clear hardware breakpoints */
142 __asm__ __volatile__ ( "movl %0, %%dr7\n" : : "r" ( DR7_CLEAR ) );
145 static void gdbmach_enable_hwbps ( void ) {
146 /* Clear breakpoint status register */
147 __asm__ __volatile__ ( "movl %0, %%dr6\n" : : "r" ( DR6_CLEAR ) );
149 /* Restore hardware breakpoints */
150 __asm__ __volatile__ ( "movl %0, %%dr7\n" : : "r" ( dr7 ) );
153 __asmcall void gdbmach_handler ( int signo, gdbreg_t *regs ) {
154 gdbmach_disable_hwbps();
155 gdbstub_handler ( signo, regs );
156 gdbmach_enable_hwbps();
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 */
177 void gdbmach_init ( void ) {
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] );