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
25FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
26FILE_SECBOOT ( FORBIDDEN );
27
28#include <stddef.h>
29#include <stdio.h>
30#include <errno.h>
31#include <assert.h>
32#include <ipxe/uaccess.h>
33#include <ipxe/gdbstub.h>
34#include <librm.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) */
73static unsigned long dr[NUM_HWBP];
74
75/** Active value of debug register 7 */
76static unsigned long dr7 = DR7_GE;
77
78/**
79 * Update debug registers
80 *
81 */
82static 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 */
99static 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 */
134int 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 */
192
193 return 0;
194}
195
196/**
197 * Handle exception
198 *
199 * @v signo GDB signal number
200 * @v regs Register dump
201 */
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 */
229static 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 */
242void 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}
#define NULL
NULL pointer (VOID *)
Definition Base.h:322
#define __asmcall
Declare a function with standard calling conventions.
Definition compiler.h:15
void gdbmach_sigstkflt(void)
@ GDBMACH_WATCH
Definition gdbmach.h:46
@ GDBMACH_AWATCH
Definition gdbmach.h:48
void gdbmach_sigill(void)
void gdbmach_sigtrap(void)
unsigned long gdbreg_t
Definition gdbmach.h:17
@ GDBMACH_NREGS
Definition gdbmach.h:38
void gdbmach_sigfpe(void)
__asm__ __volatile__("call *%9" :"=a"(result), "=c"(discard_ecx), "=d"(discard_edx) :"d"(0), "a"(code), "b"(0), "c"(in_phys), "D"(0), "S"(out_phys), "m"(hypercall))
unsigned int uint32_t
Definition stdint.h:12
unsigned long physaddr_t
Definition stdint.h:20
Assertions.
ring len
Length.
Definition dwmac.h:226
uint32_t addr
Buffer address.
Definition dwmac.h:9
uint32_t type
Operating system type.
Definition ena.h:1
Error codes.
static void gdbmach_update(void)
Update debug registers.
Definition gdbmach.c:82
static void * gdbmach_vectors[]
CPU exception vectors.
Definition gdbmach.c:229
#define DR7_RWLEN_2
Debug register 7: Two-byte length.
Definition gdbmach.c:61
static int gdbmach_find(unsigned long addr, unsigned int rwlen)
Find reusable or available hardware breakpoint.
Definition gdbmach.c:99
static unsigned long dr7
Active value of debug register 7.
Definition gdbmach.c:76
static unsigned long dr[NUM_HWBP]
Hardware breakpoint addresses (debug registers 0-3)
Definition gdbmach.c:73
#define NUM_HWBP
Number of hardware breakpoints.
Definition gdbmach.c:43
#define DR7_RWLEN_4
Debug register 7: Four-byte length.
Definition gdbmach.c:64
#define DR7_RWLEN_1
Debug register 7: One-byte length.
Definition gdbmach.c:58
int gdbmach_set_breakpoint(int type, unsigned long addr, size_t len, int enable)
Set hardware breakpoint.
Definition gdbmach.c:134
#define DR7_G(bp)
Debug register 7: Global breakpoint enable.
Definition gdbmach.c:46
void gdbmach_init(void)
Initialise GDB.
Definition gdbmach.c:242
__asmcall void gdbmach_handler(int signo, gdbreg_t *regs)
Handle exception.
Definition gdbmach.c:202
#define DR7_RWLEN_8
Debug register 7: Eight-byte length.
Definition gdbmach.c:67
#define DR7_GE
Debug register 7: Global exact breakpoint enable.
Definition gdbmach.c:49
#define DR7_RWLEN_ACCESS
Debug register 7: Break on data access.
Definition gdbmach.c:55
#define DR7_RWLEN_MASK(bp)
Debug register 7: Breakpoint R/W and length mask.
Definition gdbmach.c:70
#define DR7_RWLEN_WRITE
Debug register 7: Break on data writes.
Definition gdbmach.c:52
void gdbstub_handler(int signo, gdbreg_t *regs)
Interrupt handler.
Definition gdbstub.c:372
GDB remote debugging.
#define DBGC2_HDA(...)
Definition compiler.h:523
#define DBGC(...)
Definition compiler.h:505
#define FILE_LICENCE(_licence)
Declare a particular licence as applying to a file.
Definition compiler.h:896
#define ENOENT
No such file or directory.
Definition errno.h:515
#define ENOTSUP
Operation not supported.
Definition errno.h:590
#define ENOBUFS
No buffer space available.
Definition errno.h:499
#define FILE_SECBOOT(_status)
Declare a file's UEFI Secure Boot permission status.
Definition compiler.h:926
Access to external ("user") memory.
void set_interrupt_vector(unsigned int intr, void *vector)
Set interrupt vector.
Definition librm_mgmt.c:98
__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")
struct i386_regs regs
Definition registers.h:1
uint16_t bp
Definition registers.h:9