source: grub-pc/trunk/fuentes/grub-core/gdb/cstub.c @ 22

Last change on this file since 22 was 22, checked in by mabarracus, 4 years ago

updated version and apply net.ifnames=0 into debian/rules

File size: 8.7 KB
Line 
1/* cstub.c - machine independent portion of remote GDB stub */
2/*
3 *  Copyright (C) 2006  Lubomir Kundrak
4 *
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.
9 *
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.
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., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20#include <grub/misc.h>
21#include <grub/cpu/gdb.h>
22#include <grub/gdb.h>
23#include <grub/serial.h>
24#include <grub/backtrace.h>
25
26static const char hexchars[] = "0123456789abcdef";
27int grub_gdb_regs[GRUB_MACHINE_NR_REGS];
28
29#define GRUB_GDB_COMBUF_SIZE 400        /* At least sizeof(grub_gdb_regs)*2 are needed for
30                                           register packets.  */
31static char grub_gdb_inbuf[GRUB_GDB_COMBUF_SIZE + 1];
32static char grub_gdb_outbuf[GRUB_GDB_COMBUF_SIZE + 1];
33
34struct grub_serial_port *grub_gdb_port;
35
36static int
37hex (char ch)
38{
39  if ((ch >= 'a') && (ch <= 'f'))
40    return (ch - 'a' + 10);
41  if ((ch >= '0') && (ch <= '9'))
42    return (ch - '0');
43  if ((ch >= 'A') && (ch <= 'F'))
44    return (ch - 'A' + 10);
45  return (-1);
46}
47
48/* Scan for the sequence $<data>#<checksum>.  */
49static char *
50grub_gdb_getpacket (void)
51{
52  char *buffer = &grub_gdb_inbuf[0];
53  unsigned char checksum;
54  unsigned char xmitcsum;
55  int count;
56  int ch;
57
58  while (1)
59    {
60      /* Wait around for the start character, ignore all other
61         characters.  */
62      while ((ch = grub_serial_port_fetch (grub_gdb_port)) != '$');
63
64    retry:
65      checksum = 0;
66      xmitcsum = -1;
67      count = 0;
68
69      /* Now read until a # or end of buffer is found.  */
70      while (count < GRUB_GDB_COMBUF_SIZE)
71        {
72          do
73            ch = grub_serial_port_fetch (grub_gdb_port);
74          while (ch < 0);
75          if (ch == '$')
76            goto retry;
77          if (ch == '#')
78            break;
79          checksum += ch;
80          buffer[count] = ch;
81          count = count + 1;
82        }
83      buffer[count] = 0;
84      if (ch == '#')
85        {
86          do
87            ch = grub_serial_port_fetch (grub_gdb_port);
88          while (ch < 0);
89          xmitcsum = hex (ch) << 4;
90          do
91            ch = grub_serial_port_fetch (grub_gdb_port);
92          while (ch < 0);
93          xmitcsum += hex (ch);
94
95          if (checksum != xmitcsum)
96            grub_serial_port_put (grub_gdb_port, '-');  /* Failed checksum.  */
97          else
98            {
99              grub_serial_port_put (grub_gdb_port, '+');        /* Successful transfer.  */
100
101              /* If a sequence char is present, reply the sequence ID.  */
102              if (buffer[2] == ':')
103                {
104                  grub_serial_port_put (grub_gdb_port, buffer[0]);
105                  grub_serial_port_put (grub_gdb_port, buffer[1]);
106
107                  return &buffer[3];
108                }
109              return &buffer[0];
110            }
111        }
112    }
113}
114
115/* Send the packet in buffer.  */
116static void
117grub_gdb_putpacket (char *buffer)
118{
119  grub_uint8_t checksum;
120
121  /* $<packet info>#<checksum>.  */
122  do
123    {
124      char *ptr;
125      grub_serial_port_put (grub_gdb_port, '$');
126      checksum = 0;
127
128      for (ptr = buffer; *ptr; ptr++)
129        {
130          grub_serial_port_put (grub_gdb_port, *ptr);
131          checksum += *ptr;
132        }
133
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]);
137    }
138  while (grub_serial_port_fetch (grub_gdb_port) != '+');
139}
140
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).  */
143static char *
144grub_gdb_mem2hex (char *mem, char *buf, grub_size_t count)
145{
146  grub_size_t i;
147  unsigned char ch;
148
149  for (i = 0; i < count; i++)
150    {
151      ch = *mem++;
152      *buf++ = hexchars[ch >> 4];
153      *buf++ = hexchars[ch % 16];
154    }
155  *buf = 0;
156  return (buf);
157}
158
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.  */
161static char *
162grub_gdb_hex2mem (char *buf, char *mem, int count)
163{
164  int i;
165  unsigned char ch;
166
167  for (i = 0; i < count; i++)
168    {
169      ch = hex (*buf++) << 4;
170      ch = ch + hex (*buf++);
171      *mem++ = ch;
172    }
173  return (mem);
174}
175
176/* Convert hex characters to int and return the number of characters
177   processed.  */
178static int
179grub_gdb_hex2int (char **ptr, grub_uint64_t *int_value)
180{
181  int num_chars = 0;
182  int hex_value;
183
184  *int_value = 0;
185
186  while (**ptr)
187    {
188      hex_value = hex (**ptr);
189      if (hex_value >= 0)
190        {
191          *int_value = (*int_value << 4) | hex_value;
192          num_chars++;
193        }
194      else
195        break;
196
197      (*ptr)++;
198    }
199
200  return (num_chars);
201}
202
203/* This function does all command procesing for interfacing to gdb.  */
204void __attribute__ ((regparm(3)))
205grub_gdb_trap (int trap_no)
206{
207  unsigned int sig_no;
208  int stepping;
209  grub_uint64_t addr;
210  grub_uint64_t length;
211  char *ptr;
212
213  if (!grub_gdb_port)
214    {
215      grub_printf ("Unhandled exception 0x%x at ", trap_no);
216      grub_backtrace_print_address ((void *) grub_gdb_regs[PC]);
217      grub_printf ("\n");
218      grub_backtrace_pointer ((void *) grub_gdb_regs[EBP]);
219      grub_fatal ("Unhandled exception");
220    }
221
222  sig_no = grub_gdb_trap2sig (trap_no);
223
224  ptr = grub_gdb_outbuf;
225
226  /* Reply to host that an exception has occurred.  */
227
228  *ptr++ = 'T'; /* Notify gdb with signo, PC, FP and SP.  */
229
230  *ptr++ = hexchars[sig_no >> 4];
231  *ptr++ = hexchars[sig_no & 0xf];
232
233  /* Stack pointer.  */
234  *ptr++ = hexchars[SP];
235  *ptr++ = ':';
236  ptr = grub_gdb_mem2hex ((char *) &grub_gdb_regs[ESP], ptr, 4);
237  *ptr++ = ';';
238
239  /* Frame pointer.  */
240  *ptr++ = hexchars[FP];
241  *ptr++ = ':';
242  ptr = grub_gdb_mem2hex ((char *) &grub_gdb_regs[EBP], ptr, 4);
243  *ptr++ = ';';
244
245  /* Program counter.  */
246  *ptr++ = hexchars[PC];
247  *ptr++ = ':';
248  ptr = grub_gdb_mem2hex ((char *) &grub_gdb_regs[PC], ptr, 4);
249  *ptr++ = ';';
250
251  *ptr = '\0';
252
253  grub_gdb_putpacket (grub_gdb_outbuf);
254
255  stepping = 0;
256
257  while (1)
258    {
259      grub_gdb_outbuf[0] = 0;
260      ptr = grub_gdb_getpacket ();
261
262      switch (*ptr++)
263        {
264        case '?':
265          grub_gdb_outbuf[0] = 'S';
266          grub_gdb_outbuf[1] = hexchars[sig_no >> 4];
267          grub_gdb_outbuf[2] = hexchars[sig_no & 0xf];
268          grub_gdb_outbuf[3] = 0;
269          break;
270
271        /* Return values of the CPU registers.  */
272        case 'g':
273          grub_gdb_mem2hex ((char *) grub_gdb_regs, grub_gdb_outbuf,
274                            sizeof (grub_gdb_regs));
275          break;
276
277        /* Set values of the CPU registers -- return OK.  */
278        case 'G':
279          grub_gdb_hex2mem (ptr, (char *) grub_gdb_regs,
280                            sizeof (grub_gdb_regs));
281          grub_strcpy (grub_gdb_outbuf, "OK");
282          break;
283
284        /* Set the value of a single CPU register -- return OK.  */
285        case 'P':
286          {
287            grub_uint64_t regno;
288
289            if (grub_gdb_hex2int (&ptr, &regno) && *ptr++ == '=')
290              if (regno < GRUB_MACHINE_NR_REGS)
291                {
292                  grub_gdb_hex2mem (ptr, (char *) &grub_gdb_regs[regno], 4);
293                  grub_strcpy (grub_gdb_outbuf, "OK");
294                  break;
295                }
296            /* FIXME: GDB requires setting orig_eax. I don't know what's
297               this register is about. For now just simulate setting any
298               registers.  */
299            grub_strcpy (grub_gdb_outbuf, /*"E01"*/ "OK");
300            break;
301          }
302
303        /* mAA..AA,LLLL: Read LLLL bytes at address AA..AA.  */
304        case 'm':
305          /* Try to read %x,%x.  Set ptr = 0 if successful.  */
306          if (grub_gdb_hex2int (&ptr, &addr))
307            if (*(ptr++) == ',')
308              if (grub_gdb_hex2int (&ptr, &length))
309                {
310                  ptr = 0;
311                  grub_gdb_mem2hex ((char *) (grub_addr_t) addr,
312                                    grub_gdb_outbuf, length);
313                }
314          if (ptr)
315            grub_strcpy (grub_gdb_outbuf, "E01");
316          break;
317
318        /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA -- return OK.  */
319        case 'M':
320          /* Try to read %x,%x.  Set ptr = 0 if successful.  */
321          if (grub_gdb_hex2int (&ptr, &addr))
322            if (*(ptr++) == ',')
323              if (grub_gdb_hex2int (&ptr, &length))
324                if (*(ptr++) == ':')
325                  {
326                    grub_gdb_hex2mem (ptr, (char *) (grub_addr_t) addr, length);
327                    grub_strcpy (grub_gdb_outbuf, "OK");
328                    ptr = 0;
329                  }
330          if (ptr)
331            {
332              grub_strcpy (grub_gdb_outbuf, "E02");
333            }
334          break;
335
336        /* sAA..AA: Step one instruction from AA..AA(optional).  */
337        case 's':
338          stepping = 1;
339
340        /* cAA..AA: Continue at address AA..AA(optional).  */
341        case 'c':
342          /* try to read optional parameter, pc unchanged if no parm */
343          if (grub_gdb_hex2int (&ptr, &addr))
344            grub_gdb_regs[PC] = addr;
345
346          /* Clear the trace bit.  */
347          grub_gdb_regs[PS] &= 0xfffffeff;
348
349          /* Set the trace bit if we're stepping.  */
350          if (stepping)
351            grub_gdb_regs[PS] |= 0x100;
352
353          return;
354
355        /* Kill the program.  */
356        case 'k':
357          /* Do nothing.  */
358          return;
359        }
360
361      /* Reply to the request.  */
362      grub_gdb_putpacket (grub_gdb_outbuf);
363    }
364}
365
Note: See TracBrowser for help on using the repository browser.