iPXE
gdbmach.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2008 Stefan Hajnoczi <stefanha@gmail.com>.
3  * Copyright (C) 2016 Michael Brown <mbrown@fensystems.co.uk>.
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License as
7  * published by the Free Software Foundation; either version 2 of the
8  * License, or any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18  * 02110-1301, USA.
19  *
20  * You can also choose to distribute this program under the terms of
21  * the Unmodified Binary Distribution Licence (as given in the file
22  * COPYING.UBDL), provided that you have satisfied its requirements.
23  */
24 
25 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
26 
27 #include <stddef.h>
28 #include <stdio.h>
29 #include <errno.h>
30 #include <assert.h>
31 #include <ipxe/uaccess.h>
32 #include <ipxe/gdbstub.h>
33 #include <librm.h>
34 #include <gdbmach.h>
35 
36 /** @file
37  *
38  * GDB architecture-specific bits for x86
39  *
40  */
41 
42 /** Number of hardware breakpoints */
43 #define NUM_HWBP 4
44 
45 /** Debug register 7: Global breakpoint enable */
46 #define DR7_G( bp ) ( 2 << ( 2 * (bp) ) )
47 
48 /** Debug register 7: Global exact breakpoint enable */
49 #define DR7_GE ( 1 << 9 )
50 
51 /** Debug register 7: Break on data writes */
52 #define DR7_RWLEN_WRITE 0x11110000
53 
54 /** Debug register 7: Break on data access */
55 #define DR7_RWLEN_ACCESS 0x33330000
56 
57 /** Debug register 7: One-byte length */
58 #define DR7_RWLEN_1 0x00000000
59 
60 /** Debug register 7: Two-byte length */
61 #define DR7_RWLEN_2 0x44440000
62 
63 /** Debug register 7: Four-byte length */
64 #define DR7_RWLEN_4 0xcccc0000
65 
66 /** Debug register 7: Eight-byte length */
67 #define DR7_RWLEN_8 0x88880000
68 
69 /** Debug register 7: Breakpoint R/W and length mask */
70 #define DR7_RWLEN_MASK( bp ) ( 0xf0000 << ( 4 * (bp) ) )
71 
72 /** Hardware breakpoint addresses (debug registers 0-3) */
73 static unsigned long dr[NUM_HWBP];
74 
75 /** Active value of debug register 7 */
76 static unsigned long dr7 = DR7_GE;
77 
78 /**
79  * Update debug registers
80  *
81  */
82 static void gdbmach_update ( void ) {
83 
84  /* Set debug registers */
85  __asm__ __volatile__ ( "mov %0, %%dr0" : : "r" ( dr[0] ) );
86  __asm__ __volatile__ ( "mov %0, %%dr1" : : "r" ( dr[1] ) );
87  __asm__ __volatile__ ( "mov %0, %%dr2" : : "r" ( dr[2] ) );
88  __asm__ __volatile__ ( "mov %0, %%dr3" : : "r" ( dr[3] ) );
89  __asm__ __volatile__ ( "mov %0, %%dr7" : : "r" ( dr7 ) );
90 }
91 
92 /**
93  * Find reusable or available hardware breakpoint
94  *
95  * @v addr Linear address
96  * @v rwlen Control bits
97  * @ret bp Hardware breakpoint, or negative error
98  */
99 static int gdbmach_find ( unsigned long addr, unsigned int rwlen ) {
100  unsigned int i;
101  int bp = -ENOENT;
102 
103  /* Look for a reusable or available breakpoint */
104  for ( i = 0 ; i < NUM_HWBP ; i++ ) {
105 
106  /* If breakpoint is not enabled, then it is available */
107  if ( ! ( dr7 & DR7_G ( i ) ) ) {
108  bp = i;
109  continue;
110  }
111 
112  /* If breakpoint is enabled and has the same address
113  * and control bits, then reuse it.
114  */
115  if ( ( dr[i] == addr ) &&
116  ( ( ( dr7 ^ rwlen ) & DR7_RWLEN_MASK ( i ) ) == 0 ) ) {
117  bp = i;
118  break;
119  }
120  }
121 
122  return bp;
123 }
124 
125 /**
126  * Set hardware breakpoint
127  *
128  * @v type GDB breakpoint type
129  * @v addr Virtual address
130  * @v len Length
131  * @v enable Enable (not disable) breakpoint
132  * @ret rc Return status code
133  */
134 int gdbmach_set_breakpoint ( int type, unsigned long addr, size_t len,
135  int enable ) {
136  unsigned int rwlen;
137  unsigned long mask;
138  int bp;
139 
140  /* Parse breakpoint type */
141  switch ( type ) {
142  case GDBMACH_WATCH:
143  rwlen = DR7_RWLEN_WRITE;
144  break;
145  case GDBMACH_AWATCH:
146  rwlen = DR7_RWLEN_ACCESS;
147  break;
148  default:
149  return -ENOTSUP;
150  }
151 
152  /* Parse breakpoint length */
153  switch ( len ) {
154  case 1:
155  rwlen |= DR7_RWLEN_1;
156  break;
157  case 2:
158  rwlen |= DR7_RWLEN_2;
159  break;
160  case 4:
161  rwlen |= DR7_RWLEN_4;
162  break;
163  case 8:
164  rwlen |= DR7_RWLEN_8;
165  break;
166  default:
167  return -ENOTSUP;
168  }
169 
170  /* Convert to linear address */
171  if ( sizeof ( physaddr_t ) <= sizeof ( uint32_t ) )
172  addr = virt_to_phys ( ( void * ) addr );
173 
174  /* Find reusable or available hardware breakpoint */
175  bp = gdbmach_find ( addr, rwlen );
176  if ( bp < 0 )
177  return ( enable ? -ENOBUFS : 0 );
178 
179  /* Configure this breakpoint */
180  DBGC ( &dr[0], "GDB bp %d at %p+%zx type %d (%sabled)\n",
181  bp, ( ( void * ) addr ), len, type, ( enable ? "en" : "dis" ) );
182  dr[bp] = addr;
183  mask = DR7_RWLEN_MASK ( bp );
184  dr7 = ( ( dr7 & ~mask ) | ( rwlen & mask ) );
185  mask = DR7_G ( bp );
186  dr7 &= ~mask;
187  if ( enable )
188  dr7 |= mask;
189 
190  /* Update debug registers */
191  gdbmach_update();
192 
193  return 0;
194 }
195 
196 /**
197  * Handle exception
198  *
199  * @v signo GDB signal number
200  * @v regs Register dump
201  */
202 __asmcall void gdbmach_handler ( int signo, gdbreg_t *regs ) {
203  unsigned long dr7_disabled = DR7_GE;
204  unsigned long dr6_clear = 0;
205 
206  /* Temporarily disable breakpoints */
207  __asm__ __volatile__ ( "mov %0, %%dr7\n" : : "r" ( dr7_disabled ) );
208 
209  /* Handle exception */
210  DBGC ( &dr[0], "GDB signal %d\n", signo );
211  DBGC2_HDA ( &dr[0], 0, regs, ( GDBMACH_NREGS * sizeof ( *regs ) ) );
212  gdbstub_handler ( signo, regs );
213  DBGC ( &dr[0], "GDB signal %d returning\n", signo );
214  DBGC2_HDA ( &dr[0], 0, regs, ( GDBMACH_NREGS * sizeof ( *regs ) ) );
215 
216  /* Clear breakpoint status register */
217  __asm__ __volatile__ ( "mov %0, %%dr6\n" : : "r" ( dr6_clear ) );
218 
219  /* Re-enable breakpoints */
220  __asm__ __volatile__ ( "mov %0, %%dr7\n" : : "r" ( dr7 ) );
221 }
222 
223 /**
224  * CPU exception vectors
225  *
226  * Note that we cannot intercept anything from INT8 (double fault)
227  * upwards, since these overlap by default with IRQ0-7.
228  */
229 static void * gdbmach_vectors[] = {
230  gdbmach_sigfpe, /* Divide by zero */
231  gdbmach_sigtrap, /* Debug trap */
232  NULL, /* Non-maskable interrupt */
233  gdbmach_sigtrap, /* Breakpoint */
234  gdbmach_sigstkflt, /* Overflow */
235  gdbmach_sigstkflt, /* Bound range exceeded */
236  gdbmach_sigill, /* Invalid opcode */
237 };
238 
239 /**
240  * Initialise GDB
241  */
242 void gdbmach_init ( void ) {
243  unsigned int i;
244 
245  /* Hook CPU exception vectors */
246  for ( i = 0 ; i < ( sizeof ( gdbmach_vectors ) /
247  sizeof ( gdbmach_vectors[0] ) ) ; i++ ) {
248  if ( gdbmach_vectors[i] )
250  }
251 }
GDB architecture specifics.
Error codes.
#define DR7_RWLEN_MASK(bp)
Debug register 7: Breakpoint R/W and length mask.
Definition: gdbmach.c:70
uint8_t type
Type.
Definition: ena.h:16
#define DBGC(...)
Definition: compiler.h:505
#define ENOENT
No such file or directory.
Definition: errno.h:514
uint16_t bp
Definition: registers.h:23
int gdbmach_set_breakpoint(int type, unsigned long addr, size_t len, int enable)
Set hardware breakpoint.
Definition: gdbmach.c:134
void gdbmach_init(void)
Initialise GDB.
Definition: gdbmach.c:242
FILE_LICENCE(GPL2_OR_LATER_OR_UBDL)
unsigned long gdbreg_t
Definition: gdbmach.h:15
static __always_inline unsigned long virt_to_phys(volatile const void *addr)
Convert virtual address to a physical address.
Definition: uaccess.h:287
Access to external ("user") memory.
#define DR7_RWLEN_1
Debug register 7: One-byte length.
Definition: gdbmach.c:58
#define DR7_GE
Debug register 7: Global exact breakpoint enable.
Definition: gdbmach.c:49
#define ENOTSUP
Operation not supported.
Definition: errno.h:589
static int gdbmach_find(unsigned long addr, unsigned int rwlen)
Find reusable or available hardware breakpoint.
Definition: gdbmach.c:99
#define __asmcall
Declare a function with standard calling conventions.
Definition: compiler.h:12
Assertions.
#define DBGC2_HDA(...)
Definition: compiler.h:523
#define NUM_HWBP
Number of hardware breakpoints.
Definition: gdbmach.c:43
void gdbmach_sigtrap(void)
__asmcall void gdbmach_handler(int signo, gdbreg_t *regs)
Handle exception.
Definition: gdbmach.c:202
#define DR7_RWLEN_ACCESS
Debug register 7: Break on data access.
Definition: gdbmach.c:55
void gdbstub_handler(int signo, gdbreg_t *regs)
Interrupt handler.
Definition: gdbstub.c:372
u32 addr
Definition: sky2.h:8
void gdbmach_sigstkflt(void)
static unsigned long dr[NUM_HWBP]
Hardware breakpoint addresses (debug registers 0-3)
Definition: gdbmach.c:73
unsigned int uint32_t
Definition: stdint.h:12
struct i386_regs regs
Definition: registers.h:15
__asm__ __volatile__("\n1:\n\t" "movb -1(%2,%1), %%al\n\t" "stosb\n\t" "loop 1b\n\t" "xorl %%eax, %%eax\n\t" "mov %3, %1\n\t" "rep stosb\n\t" :"=&D"(discard_D), "=&c"(discard_c) :"r"(data), "g"(pad_len), "0"(value0), "1"(len) :"eax")
GDB remote debugging.
unsigned long physaddr_t
Definition: stdint.h:20
#define DR7_RWLEN_2
Debug register 7: Two-byte length.
Definition: gdbmach.c:61
static void * gdbmach_vectors[]
CPU exception vectors.
Definition: gdbmach.c:229
__asm__(".section \".rodata\", \"a\", " PROGBITS "\n\t" "\nprivate_key_data:\n\t" ".size private_key_data, ( . - private_key_data )\n\t" ".equ private_key_len, ( . - private_key_data )\n\t" ".previous\n\t")
#define DR7_G(bp)
Debug register 7: Global breakpoint enable.
Definition: gdbmach.c:46
uint32_t len
Length.
Definition: ena.h:14
static void gdbmach_update(void)
Update debug registers.
Definition: gdbmach.c:82
#define ENOBUFS
No buffer space available.
Definition: errno.h:498
void set_interrupt_vector(unsigned int intr, void *vector)
Set interrupt vector.
Definition: librm_mgmt.c:93
void gdbmach_sigfpe(void)
static unsigned long dr7
Active value of debug register 7.
Definition: gdbmach.c:76
void gdbmach_sigill(void)
#define DR7_RWLEN_8
Debug register 7: Eight-byte length.
Definition: gdbmach.c:67
#define NULL
NULL pointer (VOID *)
Definition: Base.h:362
#define DR7_RWLEN_WRITE
Debug register 7: Break on data writes.
Definition: gdbmach.c:52
#define DR7_RWLEN_4
Debug register 7: Four-byte length.
Definition: gdbmach.c:64