source: umon/main/common/gdb.c @ 87db514

Last change on this file since 87db514 was 87db514, checked in by Amar Takhar <amar@…>, on 04/16/15 at 19:26:21

Initial commit of the umon repository.

Prior to this three changes were made:

  • Remove umon_ prefix from parent directories.
  • Collapse main/target/ into main/
  • Remove ports/template/flashtest.scr.ucon script.
  • Property mode set to 100644
File size: 15.1 KB
Line 
1/**************************************************************************
2 *
3 * Copyright (c) 2013 Alcatel-Lucent
4 *
5 * Alcatel Lucent licenses this file to You under the Apache License,
6 * Version 2.0 (the "License"); you may not use this file except in
7 * compliance with the License.  A copy of the License is contained the
8 * file LICENSE at the top level of this repository.
9 * You may also obtain a copy of the License at:
10 *
11 *      http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 **************************************************************************
20 *
21 * gdb.c:
22 *
23 * The code in this file allows a gdb debugger on a host to connect to the
24 * monitor.  It supports both serial and network (udp) connections.
25 * Note that this is only the cpu-independent portion of the GDB debug
26 * protocol.  The following commands in gdb have been verified to work
27 * with this code:
28 *
29 *      - target remote com1
30 *      - target remote udp:135.222.140.68:1234
31 *      - load and c
32 *      - info registers
33 *      - x/16x 0xff800000
34 *      - print varname
35 *
36 *I'm sure other commands work, but these are the ones I've tested.
37 *
38 * This interface was written from scratch.
39 * References used were:
40 * Bill Gatliff's ESP article and a description of the GDB Remote
41 * Serial Protocol I found at:
42 * http://developer/apple.com/documentation/DeveloperTools/gdb/gdb/gdb_32.htm
43 *
44 * Original author:     Ed Sutter (ed.sutter@alcatel-lucent.com)
45 *
46 */
47#include "config.h"
48#include "genlib.h"
49#include "ether.h"
50#include "stddefs.h"
51#include "endian.h"
52#include "cli.h"
53
54#if INCLUDE_GDB
55
56#include "gdbregs.c"    /* CPU-specific register name table */
57
58#define REGTBL_SIZE                     (sizeof(gdb_regtbl)/sizeof(char *))
59
60#define GDBERR_NOHASH           1       /* no '#' in command */
61#define GDBERR_BADCSUM          2       /* bad checksum */
62#define GDBERR_GENERR           3       /* general confusion */
63#define GDBERR_BADXFMT          4       /* unexpected 'X' command format */
64#define GDBERR_RNUMOOR          5       /* register number out of range */
65#define GDBERR_NOSPACE          6       /* buffer not big enough for response */
66
67/* gdbIbuf[]:
68 * Input buffer used for storage of the incoming command from
69 * the gdb debugger.
70 */
71#if INCLUDE_ETHERNET
72static  uchar gdbIbuf[512];
73#endif
74
75/* gdbRbuf[]:
76 * Response buffer used for the complete response destined
77 * for the gdb host.
78 */
79static  uchar   gdbRbuf[1024];
80static  int             gdbRlen;
81static  void    (*gdbContinueFptr)();
82
83/* gdbUdp:
84 * Set if the gdb interaction is via UDP, else zero.
85 * Obviously this code assumes there is no reentrancy to deal with.
86 */
87static int gdbUdp;
88
89/* gdbTrace():
90 * This is a function pointer that is loaded with either printf
91 * or Mtrace... Use printf if gdb is running via UDP; else use
92 * printf.
93 */
94static int      (*gdbTrace)(char *, ...);
95
96/* gdb_response():
97 * Utility function used by the other response functions to format
98 * the complete response back to the gdb host.  All interaction with
99 * the host is in ASCII.  The response message is preceded by '+$'
100 * and and terminated with '#CC' where 'CC' is a checksum.
101 */
102int
103gdb_response(char *line)
104{
105        uchar   csum, *resp;
106       
107        csum = 0;
108        resp = gdbRbuf;
109        *resp++ = '$';
110        while(*line) {
111                csum += *line;
112                *resp++ = *line++;
113        }
114        resp += sprintf((char *)resp,"#%02x",csum);
115        *resp = 0;
116
117        /* If gdbUdp is clear, then we assume that the gdb host is tied
118         * to the target's serial port, so just use printf to send the
119         * response.
120         * If gdbUdp is set, then we assume the calling function will
121         * send the response (via ethernet).
122         */
123        if (!gdbUdp)
124                printf((char *)gdbRbuf);
125
126        gdbRlen = strlen((char *)gdbRbuf);
127
128        if (gdbRlen < 128)
129                gdbTrace("GDB_RSP: %s\n",gdbRbuf);
130        else
131                gdbTrace("GDB_RSP: BIG\n");
132
133        return(gdbRlen);
134}
135
136int
137gdb_ok(void)
138{
139        return(gdb_response("OK"));
140}
141
142int
143gdb_sig(int signal)
144{
145        char buf[8];
146
147        sprintf(buf,"S%02d",signal);
148        return(gdb_response(buf));
149}
150
151int
152gdb_err(int errno)
153{
154        char buf[8];
155
156        sprintf(buf,"E%02d",errno);
157        return(gdb_response(buf));
158}
159
160/* gdb_m():
161 * GDB memory read command...
162 * Incoming command format is...
163 *
164 *              mADDR,LEN#CC
165 *
166 * where:
167 *      'm'             is the "memory read" request
168 *      'ADDR'  is the address from which the data is to be read
169 *      'LEN'   is the number of bytes to be read
170 *
171 */
172int
173gdb_m(char *line)
174{
175        int             len, i;
176        char    *lp;
177        uchar   *addr, *resp, buf[128];
178       
179        addr = (uchar *)strtol(line+1,&lp,16);
180        len = (int)strtol(lp+1,0,16);
181        if (len) {
182                if (len*2 >= sizeof(buf)) {
183                        gdb_err(GDBERR_NOSPACE);
184                }
185                else {
186                        resp = buf;
187                        for(i=0;i<len;i++,addr++)
188                                resp += sprintf((char *)resp,"%02x",*addr);
189                        gdb_response((char *)buf);
190                }
191        }
192        else
193                gdb_ok();
194        return(0);
195}
196
197/* gdb_M():
198 * GDB memory write command...
199 * Incoming command format is...
200 *
201 *              MADDR,LEN:DATA#CC
202 *
203 * where:
204 *      'M'             is the "memory read" request
205 *      'ADDR'  is the address from which the data is to be read
206 *      'LEN'   is the number of bytes to be read
207 *      'DATA'  is the ascii data
208 *
209 * STATUS: This function has been tested with m68k-elf-gdb (xtools)
210 * and appears to work ok.
211 */
212int
213gdb_M(char *line)
214{
215        int             len, i;
216        char    *lp;
217        uchar   *addr, buf[3];
218       
219        addr = (uchar *)strtol(line+1,&lp,16);
220        len = (int)strtol(lp+1,&lp,16);
221        lp++;
222
223        buf[2] = 0;
224        for(i=0;i<len;i++) {
225                buf[0] = *lp++;
226                buf[1] = *lp++;
227                *addr++ = (uchar)strtol((char *)buf,0,16);
228        }
229        gdb_ok();
230        return(0);
231}
232
233/* gdb_X():
234 * Similar to gdb_M() except that the data is in binary.
235 * The characters '$', '#' and 0x7d are escaped using 0x7d.
236 */
237int
238gdb_X(char *line)
239{
240        int             len, i;
241        char    *lp;
242        uchar   *addr;
243       
244        addr = (uchar *)strtol(line+1,&lp,16);
245        len = (int)strtol(lp+1,&lp,16);
246        lp++;
247
248        for(i=0;i<len;i++) {
249                if ((*lp == 0x7d) &&
250                        ((*(lp+1) == 0x03) || (*(lp+1) == 0x04) || (*(lp+1) == 0x5d))) {
251                        *addr++ = *(lp+1) | 0x20;
252                        lp += 2;
253                }
254                else
255                        *addr++ = *lp++;
256        }
257
258        gdb_ok();
259        return(0);
260}
261
262/* gdb_q():
263 * Query.  Not sure what this is for, but according to Gatliff's
264 * article, just do it...
265 */
266int
267gdb_q(char *line)
268{
269        line++;
270
271        if (strncmp(line,"Offsets",7) == 0)
272                return(gdb_response("Text=0;Data=0;Bss=0"));
273        else
274                return(gdb_ok());
275}
276
277/* gdb_g():
278 * Get all registers.
279 * GDB at the host expects to see a buffer of ASCII-coded hex values
280 * with each register being 8-bytes (forming one 32-bit value).
281 * The order of these registers is defined by the table included
282 * in the file gdbregs.c above.
283 */
284
285int
286gdb_g(char *line)
287{
288        int             i;
289        ulong   reg;
290        char    *resp, buf[(REGTBL_SIZE * 8) + 1];
291
292        resp = buf;
293        for(i=0;i<REGTBL_SIZE;i++) {
294                if (gdb_regtbl[i] != 0)
295                        getreg(gdb_regtbl[i],&reg);
296                else
297                        reg = 0;
298                self_ecl(reg);
299                resp += sprintf(resp,"%08lx",reg);
300        }
301        return(gdb_response(buf));
302}
303
304/* gdb_P():
305 * Store to a register.
306 */
307int
308gdb_P(char *line)
309{
310        char *lp;
311        int rnum;
312        ulong rval;
313
314        line++;
315        rnum = strtol(line,&lp,16);
316        if (rnum >= REGTBL_SIZE) {
317                gdb_err(GDBERR_RNUMOOR);       
318                return(-1);
319        }
320        lp++;
321        rval = strtol(lp,0,16);
322        self_ecl(rval);
323        putreg(gdb_regtbl[rnum],rval);
324
325        gdb_ok();
326        return(0);
327}
328
329/* gdb_c():
330 * This is the function that is called as a result of the 'c' (continue)
331 * command.
332 */
333int
334gdb_c(char *line)
335{
336        ulong   addr;
337        void    (*func)();
338
339        line++;
340        if (*line == '#')
341                getreg(CPU_PC_REG,&addr);
342        else
343                addr = strtol(line,0,16);
344
345        func = (void(*)())addr;
346        func();
347        return(0);
348}
349
350/* gdb_cmd():
351 *      First function called out of the monitor's command interpreter.  It
352 *      does a basic syntax verification and then passes parameters to the
353 *      appropriate handler above.
354 *      Incoming syntax is
355 *
356 *              $ CMD # CSUM (of CMD)
357 *
358 *      where:
359 *              $               is the ascii '$' character (0x24)
360 *              #               is the ascii '#' character (0x23)
361 *              CMD             is some command line consisting of a command and arguments
362 *              CSUM    is the checksum of the characters in CMD
363 *
364 *      for example:
365 *             
366 *              $m4015bc,2#5a
367 *
368 *      Returns...
369 *               0 if command is not processed;
370 *               1 if command is processed;
371 *              -1 if command is processed but has an error;
372 *
373 *      If this code detects an error, then send an error code back to GDB.
374 *      According to the article, there are no defined error codes in GDB so
375 *      we will use the following...
376 *              1       indicates a missing '#' at the end of the incoming cmd string.
377 *              2       indicates a bad checksum calculation.
378 *              3       indicates some command processing error.
379 *              4       indicates bad 'X' command parsing.
380 */
381int
382gdb_cmd(uchar *line)
383{
384        char    *comma, *colon, *cp, *bp, buf[32];
385        int             len, clen, err, i;
386        uchar   mycsum, incsum;
387
388        gdbContinueFptr = (void(*)())0;
389
390        /* If the command is 'X', then we have to treat it "special" because
391         * it contains binary data...
392         */
393        if (line[1] == 'X') {
394                comma = strchr((char *)line,',');
395                colon = strchr((char *)line,':');
396                if ((comma) && (colon)) {
397                        bp = buf;
398                        cp = (char *)line;
399                        while(cp <= colon)
400                                *bp++ = *cp++;
401                        *bp = 0;
402                        gdbTrace("GDB_CMD: '%s'\n",buf);
403                }
404                else {
405                        gdbTrace("GDB_CMD: 'X'\n");
406                        gdb_err(GDBERR_BADXFMT);        /* Unexpected 'X' command format */
407                }
408        }
409        else if (line[0] == 0x03) {
410                gdbTrace("GDB_CTRLC\n");
411                gdb_sig(2);
412                return(1);
413        }
414        else {
415                gdbTrace("GDB_CMD: '%s'\n",line);
416                len = strlen((char *)line);
417
418                if (line[len-3] != '#') {
419                        gdb_err(GDBERR_NOHASH);         /* Missing ending '#' */
420                        return(-1);
421                }
422
423                clen = len - 3;
424                mycsum = 0;
425                for(i=1;i<clen;i++)
426                        mycsum += line[i];
427
428                incsum = (uchar)strtol((char *)line+len-2,(char **)0,16);
429                if (mycsum != incsum) {
430                        gdb_err(GDBERR_BADCSUM);        /* Checksum failure */
431                        return(-1);
432                }
433        }
434
435        err = 0;
436        line++;
437        switch(*line) {
438                case 'm':               /* Memory read */
439                        err = gdb_m((char *)line);
440                        break;
441                case 'M':               /* Memory write (Ascii-coded-hex) */
442                        err = gdb_M((char *)line);
443                        break;
444                case 'X':               /* Memory write (Binary) */
445                        err = gdb_X((char *)line);
446                        break;
447                case 's':               /* Step */
448                        gdb_response("S05");
449                        break;
450                case 'c':               /* Continue */
451                        gdb_c((char *)line);
452                        break;
453                case '?':               /* Last signal */
454                        gdb_response("S05");
455                        break;
456                case 'g':               /* get all registers */
457                        gdb_g((char *)line);
458                        break;
459                case 'q':               /* Query */
460                        gdb_q((char *)line);
461                        break;
462                case 'P':               /* PRR=HHHHHHHH... reg*/
463                        gdb_P((char *)line);
464                        break;
465                case 'H':               /* Thread */
466                        gdb_ok();
467                        break;
468                case 'k':               /* Quit */
469                        gdb_ok();
470                        break;
471                default:                /* Unknown... return empty response. */
472                        gdb_response("");
473                        break;
474        }
475        if (err) {
476                gdb_err(GDBERR_GENERR);                 /* Command processing error */
477        }
478        return(1);
479}
480
481/* Gdb():
482 * This is the command at the CLI that allows the monitor to connect
483 * to a gdb debugger via the serial port.  It currently assumes that
484 * the console port is the same port as is being used for the gdb
485 * connection.  Eventually this needs to be modified to provide an
486 * option for the gdb protocol to run on some serial port other than
487 * the console.
488 *
489 * The connection command in gdb to connect to via serial port (PC)
490 * is:
491 *      target remote com1
492 */
493char *GdbHelp[] = {
494        "Enter gdb mode",
495        "(no options)",
496        0,
497};
498
499int
500Gdb(int argc, char *argv)
501{
502        int             state, quit;
503        uchar   *lp, c;
504        static  uchar   line[1024];
505
506        printf("Entering GDB mode, to exit manually type: '$k#00'\n");
507
508        lp = (uchar *)0;
509        quit = 0;
510        state = 0;
511        gdbUdp = 0;
512        gdbTrace = Mtrace;
513        while(!quit) {
514                c = getchar();
515                switch(state) {
516                        case 0:                         /* Wait for start of message */
517                                if (c == '$') {
518                                        lp = line;
519                                        *lp++ = c;
520                                        state = 1;
521                                }
522                                break;
523                        case 1:
524                                *lp++ = c;              /* This is the command character */
525                                state = 2;
526                                break;
527                        case 2:
528                                if (c == '#') {
529                                        state = 3;
530                                        *lp++ = c;
531                                }
532                                else {
533                                        *lp++ = c;
534                                }
535                                break;
536                        case 3:
537                                *lp++ = c;
538                                state = 4;
539                                break;
540                        case 4:
541                                *lp++ = c;
542                                *lp = 0;
543                                state = 0;
544                                if (line[1] == 'k')
545                                        quit = 1;
546                                gdb_cmd(line);
547                                break;
548                        default:
549                                break;
550                }
551        }
552        putchar('\n');
553        return(CMD_SUCCESS);
554}
555
556#if INCLUDE_ETHERNET
557/* processGDB():
558 * This is the function that allows a remote gdb host to connect to
559 * the monitor with gdb at the udp level.  The connection command in
560 * gdb to do this is:
561 *
562 *      target remote udp:TARGET_IP:TARGET_PORT
563 */
564int
565processGDB(struct ether_header *ehdr,ushort size)
566{
567        char    *gdbp;
568        struct  ip *ihdr, *ti, *ri;
569        struct  Udphdr *uhdr, *tu, *ru;
570        struct  ether_header *te;
571
572        /* If SHOW_GDB is set (via ether -vg), then we dump the trace to
573         * the console; otherwise, we use mtrace.
574         */
575#if INCLUDE_ETHERVERBOSE
576        if (EtherVerbose & SHOW_GDB)
577                gdbTrace = printf;
578        else
579#endif
580                gdbTrace = Mtrace;
581
582        ihdr = (struct ip *)(ehdr + 1);
583        uhdr = (struct Udphdr *)((char *)ihdr + IP_HLEN(ihdr));
584        gdbp = (char *)(uhdr + 1);
585        size = ecs(uhdr->uh_ulen) - sizeof(struct Udphdr);
586
587        /* Check for ACK/NAK here:
588         */
589        if (size == 1) {
590                if ((*gdbp == '+') || (*gdbp == '-')) {
591                        gdbTrace("GDB_%s\n",*gdbp == '+' ? "ACK" : "NAK");
592                        return(0);
593                }
594        }
595
596        /* Copy the incoming udp payload (the gdb command) to gdbIbuf[]
597         * and NULL terminate it...
598         */
599        memcpy((char *)gdbIbuf,(char *)gdbp,size);
600        gdbIbuf[size] = 0;
601
602        /* Now that we've stored away the GDB command request, we
603         * initially respond with the GDB acknowledgement ('+')...
604         */
605        te = EtherCopy(ehdr);
606        ti = (struct ip *) (te + 1);
607        ri = (struct ip *) (ehdr + 1);
608        ti->ip_vhl = ri->ip_vhl;
609        ti->ip_tos = ri->ip_tos;
610        ti->ip_len = ecs((1 + (sizeof(struct ip) + sizeof(struct Udphdr))));
611        ti->ip_id = ipId();
612        ti->ip_off = ri->ip_off;
613        ti->ip_ttl = UDP_TTL;
614        ti->ip_p = IP_UDP;
615        memcpy((char *)&(ti->ip_src.s_addr),(char *)BinIpAddr,
616                sizeof(struct in_addr));
617        memcpy((char *)&(ti->ip_dst.s_addr),(char *)&(ri->ip_src.s_addr),
618                sizeof(struct in_addr));
619        tu = (struct Udphdr *) (ti + 1);
620        ru = (struct Udphdr *) (ri + 1);
621        tu->uh_sport = ru->uh_dport;
622        tu->uh_dport = ru->uh_sport;
623        tu->uh_ulen = ecs((ushort)(sizeof(struct Udphdr) + 1));
624        gdbp = (char *)(tu+1);
625        *gdbp = '+';
626
627        ipChksum(ti);           /* Compute checksum of ip hdr */
628        udpChksum(ti);          /* Compute UDP checksum */
629
630        sendBuffer(sizeof(struct ether_header) + sizeof(struct ip) +
631                sizeof(struct Udphdr) + 1);
632
633        /* Wrap the processing of the incoming packet with a set/clear
634         * of the gdbUdp flag so that the other gdb code can act
635         * accordingly.
636         */
637        gdbUdp = 1;
638        if (gdb_cmd(gdbIbuf) == 0) {
639                gdbUdp = 0;
640                return(0);
641        }
642        gdbUdp = 0;
643
644        /* Add 1 to the gdbRlen to include the NULL termination.
645         */
646        gdbRlen++;     
647
648
649        /* The second respons is only done if gdb_cmd returns non-zero.
650         * It is the response to the gdb command issued by the debugger
651         * on the host.
652         */
653        te = EtherCopy(ehdr);
654        ti = (struct ip *) (te + 1);
655        ri = (struct ip *) (ehdr + 1);
656        ti->ip_vhl = ri->ip_vhl;
657        ti->ip_tos = ri->ip_tos;
658        ti->ip_len = ecs((gdbRlen + (sizeof(struct ip) + sizeof(struct Udphdr))));
659        ti->ip_id = ipId();
660        ti->ip_off = ri->ip_off;
661        ti->ip_ttl = UDP_TTL;
662        ti->ip_p = IP_UDP;
663        memcpy((char *)&(ti->ip_src.s_addr),(char *)BinIpAddr,
664                sizeof(struct in_addr));
665        memcpy((char *)&(ti->ip_dst.s_addr),(char *)&(ri->ip_src.s_addr),
666                sizeof(struct in_addr));
667
668        tu = (struct Udphdr *) (ti + 1);
669        ru = (struct Udphdr *) (ri + 1);
670        tu->uh_sport = ru->uh_dport;
671        tu->uh_dport = ru->uh_sport;
672        tu->uh_ulen = ecs((ushort)(sizeof(struct Udphdr) + gdbRlen));
673        memcpy((char *)(tu+1),(char *)gdbRbuf,gdbRlen);
674
675        ipChksum(ti);           /* Compute checksum of ip hdr */
676        udpChksum(ti);          /* Compute UDP checksum */
677
678        sendBuffer(sizeof(struct ether_header) + sizeof(struct ip) +
679                sizeof(struct Udphdr) + gdbRlen);
680
681        return(1);
682}
683#endif
684
685#endif
686
Note: See TracBrowser for help on using the repository browser.