1 /* cstub.c - machine independent portion of remote GDB stub */
3 * Copyright (C) 2006 Lubomir Kundrak
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
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., 675 Mass Ave, Cambridge, MA 02139, USA.
20 #include <grub/misc.h>
21 #include <grub/cpu/gdb.h>
23 #include <grub/serial.h>
24 #include <grub/backtrace.h>
26 static const char hexchars
[] = "0123456789abcdef";
27 int grub_gdb_regs
[GRUB_MACHINE_NR_REGS
];
29 #define GRUB_GDB_COMBUF_SIZE 400 /* At least sizeof(grub_gdb_regs)*2 are needed for
31 static char grub_gdb_inbuf
[GRUB_GDB_COMBUF_SIZE
+ 1];
32 static char grub_gdb_outbuf
[GRUB_GDB_COMBUF_SIZE
+ 1];
34 struct grub_serial_port
*grub_gdb_port
;
39 if ((ch
>= 'a') && (ch
<= 'f'))
40 return (ch
- 'a' + 10);
41 if ((ch
>= '0') && (ch
<= '9'))
43 if ((ch
>= 'A') && (ch
<= 'F'))
44 return (ch
- 'A' + 10);
48 /* Scan for the sequence $<data>#<checksum>. */
50 grub_gdb_getpacket (void)
52 char *buffer
= &grub_gdb_inbuf
[0];
53 unsigned char checksum
;
54 unsigned char xmitcsum
;
60 /* Wait around for the start character, ignore all other
62 while ((ch
= grub_serial_port_fetch (grub_gdb_port
)) != '$');
69 /* Now read until a # or end of buffer is found. */
70 while (count
< GRUB_GDB_COMBUF_SIZE
)
73 ch
= grub_serial_port_fetch (grub_gdb_port
);
87 ch
= grub_serial_port_fetch (grub_gdb_port
);
89 xmitcsum
= hex (ch
) << 4;
91 ch
= grub_serial_port_fetch (grub_gdb_port
);
95 if (checksum
!= xmitcsum
)
96 grub_serial_port_put (grub_gdb_port
, '-'); /* Failed checksum. */
99 grub_serial_port_put (grub_gdb_port
, '+'); /* Successful transfer. */
101 /* If a sequence char is present, reply the sequence ID. */
102 if (buffer
[2] == ':')
104 grub_serial_port_put (grub_gdb_port
, buffer
[0]);
105 grub_serial_port_put (grub_gdb_port
, buffer
[1]);
115 /* Send the packet in buffer. */
117 grub_gdb_putpacket (char *buffer
)
119 grub_uint8_t checksum
;
121 /* $<packet info>#<checksum>. */
125 grub_serial_port_put (grub_gdb_port
, '$');
128 for (ptr
= buffer
; *ptr
; ptr
++)
130 grub_serial_port_put (grub_gdb_port
, *ptr
);
134 grub_serial_port_put (grub_gdb_port
, '#');
135 grub_serial_port_put (grub_gdb_port
, hexchars
[checksum
>> 4]);
136 grub_serial_port_put (grub_gdb_port
, hexchars
[checksum
& 0xf]);
138 while (grub_serial_port_fetch (grub_gdb_port
) != '+');
141 /* Convert the memory pointed to by mem into hex, placing result in buf.
142 Return a pointer to the last char put in buf (NULL). */
144 grub_gdb_mem2hex (char *mem
, char *buf
, grub_size_t count
)
149 for (i
= 0; i
< count
; i
++)
152 *buf
++ = hexchars
[ch
>> 4];
153 *buf
++ = hexchars
[ch
% 16];
159 /* Convert the hex array pointed to by buf into binary to be placed in mem.
160 Return a pointer to the character after the last byte written. */
162 grub_gdb_hex2mem (char *buf
, char *mem
, int count
)
167 for (i
= 0; i
< count
; i
++)
169 ch
= hex (*buf
++) << 4;
170 ch
= ch
+ hex (*buf
++);
176 /* Convert hex characters to int and return the number of characters
179 grub_gdb_hex2int (char **ptr
, grub_uint64_t
*int_value
)
188 hex_value
= hex (**ptr
);
191 *int_value
= (*int_value
<< 4) | hex_value
;
203 /* This function does all command procesing for interfacing to gdb. */
205 grub_gdb_trap (int trap_no
)
210 grub_uint64_t length
;
215 grub_printf ("Unhandled exception 0x%x at ", trap_no
);
216 grub_backtrace_print_address ((void *) grub_gdb_regs
[PC
]);
218 grub_backtrace_pointer ((void *) grub_gdb_regs
[EBP
]);
222 sig_no
= grub_gdb_trap2sig (trap_no
);
224 ptr
= grub_gdb_outbuf
;
226 /* Reply to host that an exception has occurred. */
228 *ptr
++ = 'T'; /* Notify gdb with signo, PC, FP and SP. */
230 *ptr
++ = hexchars
[sig_no
>> 4];
231 *ptr
++ = hexchars
[sig_no
& 0xf];
234 *ptr
++ = hexchars
[SP
];
236 ptr
= grub_gdb_mem2hex ((char *) &grub_gdb_regs
[ESP
], ptr
, 4);
240 *ptr
++ = hexchars
[FP
];
242 ptr
= grub_gdb_mem2hex ((char *) &grub_gdb_regs
[EBP
], ptr
, 4);
245 /* Program counter. */
246 *ptr
++ = hexchars
[PC
];
248 ptr
= grub_gdb_mem2hex ((char *) &grub_gdb_regs
[PC
], ptr
, 4);
253 grub_gdb_putpacket (grub_gdb_outbuf
);
259 grub_gdb_outbuf
[0] = 0;
260 ptr
= grub_gdb_getpacket ();
265 grub_gdb_outbuf
[0] = 'S';
266 grub_gdb_outbuf
[1] = hexchars
[sig_no
>> 4];
267 grub_gdb_outbuf
[2] = hexchars
[sig_no
% 16];
268 grub_gdb_outbuf
[3] = 0;
271 /* Return values of the CPU registers. */
273 grub_gdb_mem2hex ((char *) grub_gdb_regs
, grub_gdb_outbuf
,
274 sizeof (grub_gdb_regs
));
277 /* Set values of the CPU registers -- return OK. */
279 grub_gdb_hex2mem (ptr
, (char *) grub_gdb_regs
,
280 sizeof (grub_gdb_regs
));
281 grub_strcpy (grub_gdb_outbuf
, "OK");
284 /* Set the value of a single CPU register -- return OK. */
289 if (grub_gdb_hex2int (&ptr
, ®no
) && *ptr
++ == '=')
290 if (regno
< GRUB_MACHINE_NR_REGS
)
292 grub_gdb_hex2mem (ptr
, (char *) &grub_gdb_regs
[regno
], 4);
293 grub_strcpy (grub_gdb_outbuf
, "OK");
296 /* FIXME: GDB requires setting orig_eax. I don't know what's
297 this register is about. For now just simulate setting any
299 grub_strcpy (grub_gdb_outbuf
, /*"E01"*/ "OK");
303 /* mAA..AA,LLLL: Read LLLL bytes at address AA..AA. */
305 /* Try to read %x,%x. Set ptr = 0 if successful. */
306 if (grub_gdb_hex2int (&ptr
, &addr
))
308 if (grub_gdb_hex2int (&ptr
, &length
))
311 grub_gdb_mem2hex ((char *) (grub_addr_t
) addr
,
312 grub_gdb_outbuf
, length
);
315 grub_strcpy (grub_gdb_outbuf
, "E01");
318 /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA -- return OK. */
320 /* Try to read %x,%x. Set ptr = 0 if successful. */
321 if (grub_gdb_hex2int (&ptr
, &addr
))
323 if (grub_gdb_hex2int (&ptr
, &length
))
326 grub_gdb_hex2mem (ptr
, (char *) (grub_addr_t
) addr
, length
);
327 grub_strcpy (grub_gdb_outbuf
, "OK");
332 grub_strcpy (grub_gdb_outbuf
, "E02");
336 /* sAA..AA: Step one instruction from AA..AA(optional). */
340 /* cAA..AA: Continue at address AA..AA(optional). */
342 /* try to read optional parameter, pc unchanged if no parm */
343 if (grub_gdb_hex2int (&ptr
, &addr
))
344 grub_gdb_regs
[PC
] = addr
;
346 /* Clear the trace bit. */
347 grub_gdb_regs
[PS
] &= 0xfffffeff;
349 /* Set the trace bit if we're stepping. */
351 grub_gdb_regs
[PS
] |= 0x100;
355 /* Kill the program. */
361 /* Reply to the request. */
362 grub_gdb_putpacket (grub_gdb_outbuf
);