/************************************************************* * File: mon/debug.c * Purpose: Part of core Monitor * Author: Phil Bunce (pjb@carmel.com) * Revision History: * 970304 Start of revision history * 970311 Changed dbxmode to gdbmode * 970331 Added 'b', 'a' and 'v' commands. * 970410 Added code to set reg a1 to zero * 970520 Added code to B and A to return error codes * 980619 Removed 'dbx' from help message * 990304 Added close of hostfd on 'x'. Fixes fd leak w sde3. * 990305 Added code to catch ^C when in debug mode. */ /************************************************************* * This module provides dbx support, permitting PMON to be used as * an execution vehicle when source-level debugging with MIPS' dbx. * It also permits operation with gdb using the 'target mips' command. To use this feature perform the following steps: 1. Create the file /etc/remote.pdbx which should contain the following single line. pmon:dv=/dev/tty1:br#9600 2. Create the file ~/.dbxinit which should contain the following, set $pdbxport="pmon" set $usesockets=0 set $manual_load=1 3. Download the program to the target system 4. Invoke dbx using the following command, dbx -prom 5. Optionally set breakpoints e.g., "bp main". 6. Start execution using the dbx command "run". 7. Type "debug" on the target system's console. *************************************************************/ /* * Messages are transmitted over an RS232 serial link, and are of the * form: * ^V type+len sequence# data checkSum * These messages are transmitted by the routine putpkt and received by * getpkt. */ #include #include #include #include #include #define DATA 0 #define ACKPKT 0x20 #define nextseq(x) (((x)>=63)?0:x+1) #define TIMEOUT 300000 Optdesc debug_opts[] = { {"[-svV] [-c args]","enter gdb mode"}, {"-s","don't set client sp"}, {"-c ","args to be passed to client"}, {"-v","report protocol errors"}, {"-V","verbose mode"}, {0}}; int myseq; int hisseq; int hostfd; int Vflag; jmp_buf pktstart; jmp_buf timeout; int gdbmode; jmp_buf dbx_jmpbuf; jmp_buf debugintrbuf; extern char clientcmd[LINESZ]; extern FILE *dfp; extern int verbose; static void do_req(); static putmsg(); static getmsg(); static putpkt(); static getpkt(); static readc(); /************************************************************* * debug(ac,av) * The 'debug' command */ debug(ac,av) int ac; char *av[]; { char *hostport; int i,j,sflag; strcpy(clientcmd,av[0]); strcat(clientcmd," "); if (dfp == 0) dfp = stdout; vflag = Vflag = verbose; sflag = 0; for (i=1;i 0) break; for (i=0;i<1000000;i++) ; if (Vflag) putchar('.'); } #else write(hostfd,"\r",1); #endif myseq = hisseq = 0; if (!sflag) putGpr(29,mkRV(clienttos())); putGpr(5,mkRV(0)); /* set a1 to zero */ /* This stops a crash in crt1.s when attempting to see * if timing was requested. */ gdbmode = 1; dbgmode(0); } /************************************************************* * dbgmode(type) * enter dbx mode * dbgmode(0) -- initial condition * dbgmode(1) -- after a continue * dbgmode(2) -- after a single-step */ dbgmode(type) int type; { char rxstr[80],*rxarg[8]; int ac,n; if (n=setjmp(dbx_jmpbuf)) type = n; switch (type) { case 0 : putmsg(hostfd,&myseq,"0x1 b 0x0 0x57F"); break; case 1 : putmsg(hostfd,&myseq,"0x1 c 0x0 0x57f"); break; case 2 : putmsg(hostfd,&myseq,"0x1 s 0x0 0x57f"); break; case 3 : putmsg(hostfd,&myseq,"0x1 c 0x0 0x57f 0x1"); break; /* wasbda */ } for (;;) { getmsg(hostfd,&hisseq,rxstr); ac = argvize(rxarg,rxstr); do_req(hostfd,ac,rxarg); } } /************************************************************* * void gdbstop(int n) * n should be 1, 2 or 3. */ gdbstop(n) int n; { longjmp(dbx_jmpbuf,n); } /************************************************************* * static putmsg(fd,seq,msg) * send msg, including wait for the ACK */ static putmsg(fd,seq,msg) int fd,*seq; char *msg; { int type,ts,ns; ns = nextseq(*seq); for (;;) { setjmp(timeout); putpkt(fd,*seq,msg); if ((type = getpkt(fd,&ts,0)) == ACKPKT && ts == ns) break; if (vflag) fprintf(dfp,"bad ACK type=%02x got seq=%d wanted seq=%d\n", type,ts,ns); if (ts != ns) putpkt(fd,nextseq(ts),0); } *seq = ns; if (Vflag) fprintf(dfp,"\n"); } /************************************************************* * static getmsg(fd,seq,msg) * get msg, including send the ACK */ static getmsg(fd,seq,msg) int fd,*seq; char *msg; { int type,ts; for (;;) { if ((type = getpkt(fd,&ts,msg)) == DATA && ts == *seq) break; if (vflag) fprintf(dfp,"bad DATA type=%02x seq=%d msg=%s\n",type,ts,msg); } *seq = nextseq(*seq); putpkt(fd,*seq,0); } /************************************************************* * static putpkt(fd,seq,msg) * send a packet, if msg == 0, type = ACK */ static putpkt(fd,seq,msg) int fd; /* file descriptor */ int seq; /* sequence number to be sent */ char *msg; /* message to be sent */ { int len,type,type_len,i; int csum,csum1,csum2,csum3; char tmp[80]; if (Vflag) fprintf(dfp,"putpkt: fd=%d seq=%d msg=%s\n",fd,seq,msg); if (msg == 0) type = ACKPKT; else type = DATA; if (msg) len = strlen(msg); else len = 0; type_len = type | (len>>6); type_len |= 0x40; csum = type_len; len &= 0x3f; len |= 0x40; csum += len; seq |= 0x40; csum += seq; if (msg) for (i=0;msg[i] != 0;i++) csum += msg[i]; csum1 = csum>>12; csum1 |= 0x40; csum2 = (csum>>6)&0x3f; csum2 |= 0x40; csum3 = csum&0x3f; csum3 |= 0x40; if (msg) sprintf(tmp,"%c%c%c%c%s%c%c%c",CNTRL('v'),type_len,len,seq,msg, csum1,csum2,csum3); else sprintf(tmp,"%c%c%c%c%c%c%c",CNTRL('v'),type_len,len,seq, csum1,csum2,csum3); write(fd,tmp,strlen(tmp)); } /************************************************************* * static getpkt(fd,seq,msg) * get a packet as a string, returns type */ static getpkt(fd,seq,msg) int fd; /* file descriptor */ int *seq; /* received sequence number */ char *msg; /* destination for message */ { int len,type,csum,rsum,n,i; char ch; type = DATA; setjmp(pktstart); ch = readc(fd,msg); csum = ch; if (ch&ACKPKT) { type = ACKPKT; ch &= ~ACKPKT; } len = ch - '@'; ch = readc(fd,msg); csum += ch; len = (len<<6) + (ch - '@'); ch = readc(fd,msg); csum += ch; *seq = ch - '@'; for (i=0;i TIMEOUT) { if (vflag) fprintf(dfp,"timeout\n"); longjmp(timeout,1); } n = ioctl_fionread(fd); if (n > 0) break; } read(fd,&ch,1); if (ch == CNTRL('V')) longjmp(pktstart,1); return(ch); } /************************************************************* * static void do_req(fd,ac,av) handle a dbx request * av[0]=pid av[1]=req av[2]=addr av[3]=data */ static void do_req(fd,ac,av) int fd,ac; char *av[]; { ADDR adr,adr2; RegRec *rp; int i,code,rtn; char msg[80],*rname; U64 val,datrv; char tmp[REGVALMAX]; Ulong dat; if (ac < 4 || ac > 5) { fprintf(dfp,"ac=%d: "); for (i=0;i>= 1; } break; case 'I' : /* write Ispace */ case 'D' : /* write Dspace */ val = read_target(XT_MEM,adr,4); write_target(XT_MEM,adr,datrv,4); sprintf(msg,"%s %s 0x0 0x%s",av[0],av[1], sprintRV(tmp,val)); putmsg(fd,&myseq,msg); #if 0 /* it would be nice. But gdb only uses 'D' */ if (av[1][0] == 'I') flush_target(ICACHE); #else flush_target(ICACHE); #endif break; case 'H' : /* write half-word */ val = read_target(XT_MEM,adr,2); write_target(XT_MEM,adr,datrv,2); sprintf(msg,"%s %s 0x0 0x%s",av[0],av[1], sprintRV(tmp,val)); putmsg(fd,&myseq,msg); break; case 'P' : /* write byte */ val = read_target(XT_MEM,adr,1); write_target(XT_MEM,adr,datrv,1); sprintf(msg,"%s %s 0x0 0x%s",av[0],av[1], sprintRV(tmp,val)); putmsg(fd,&myseq,msg); break; case 'R' : /* write reg */ if (adr <= 31) { val = getGpr(adr); putGpr(adr,datrv); } else if (adr <= 63) val = mkRV(0); /* Fpr */ else { switch (adr) { case 96 : rname="pc"; break; case 97 : rname="cause"; break; case 98 : rname="hi"; break; case 99 : rname="lo"; break; case 100 : rname="0"; break; /* fcsr */ case 101 : rname="0"; break; /* feir */ default : rname="0"; break; } rp = findRegRec(rname,0); val = getU64(rp); putU64(rp,datrv); } sprintf(msg,"%s %s 0x0 0x%s",av[0],av[1], sprintRV(tmp,val)); putmsg(fd,&myseq,msg); break; case 's' : /* step */ /* optional addr */ if (adr != 1) putPc(adr); run_target(4,0,1); break; case 'c' : /* continue */ /* optional addr */ if (adr != 1) putPc(adr); run_target(3,0,0); break; case 'x' : /* exit */ gdbmode = 0; close(hostfd); /* 990304 */ if (verbose) fprintf(dfp,"exiting debug mode\n"); printf(" break!\n"); longjmp(intrbuf,2); break; /* addr */ case 'B' : /* set ibpt */ code = 0; rtn = setbp_target(-1,BPTYPE_PC,adr,0,0); if (rtn < 0) { code = 0-rtn; val = mkRV(0); } else {val = mkRV(rtn&0xffff); code = rtn>>16;} sprintf(msg,"%s %s 0x%s 0x%x",av[0],av[1], sprintRV(tmp,val),code); putmsg(fd,&myseq,msg); break; case 'b' : /* 0x0 clear bpt */ clrbp_target(adr); code = 0; sprintf(msg,"%s %s 0x0 0x%x",av[0],av[1],code); putmsg(fd,&myseq,msg); break; case 'A' : /* set daccess bpt */ /* [ []] */ /* adr dat av[4] av[5] */ if (ac >= 5) atob((unsigned int *)&adr2,av[4],0); else adr2 = 0; if (ac >= 6) { atobRV(&val,av[5],0); dat |= 4; } dat <<= 4; rtn = setbp_target(-1,dat|BPTYPE_DATA,adr,adr2,val.lo); if (rtn < 0) { code = 0-rtn; val = mkRV(0); } else {val = mkRV(rtn&0xffff); code = rtn>>16;} sprintf(msg,"%s %s 0x%s 0x%x",av[0],av[1], sprintRV(tmp,val),code); putmsg(fd,&myseq,msg); break; default : fprintf(dfp,"unknown request type '%s %s %s %s'\n", av[0],av[1],av[2],av[3]); } } #if 0 Hardware Breakpoint Support --------------------------- Hardware breakpoint support uses three new commands: 'B', 'A', and 'b', which are used for setting instruction breakpoints, setting data breakpoints (Access bpts), or clearing breakpoints respectively. Set Instruction Breakpoint -------------------------- gdb should use the following command whenever an instruction breakpoint is to be set. The underlying monitor (eg. PMON) will be responsible for figuring out what actual breakpoint mechanism to use. Sometimes it will use a software breakpoint mechanism, sometimes a icache locking mechanism, and sometimes an actual hardware breakpoint register. 'B' 0x0 reply: 'B' The reply returns two values: bptn - a breakpoint number, which is a small integer with possible values of zero through 255. code - an error return code, a value of zero indicates a succesful completion, other values indicate various errors and warnings. Possible return codes: OK, W_QAL, E_QAL, E_OUT, E_NON Set Data Breakpoint ------------------- gdb should use the following command whenever a data breakpoint is to be set. The underlying monitor (eg. PMON) will be responsible for figuring out what actual breakpoint mechanism to use. 'A' [ []] where: type= "0x1" = read "0x2" = write "0x3" = access (read or write) For example, 0x1 'A' 0x80020004 0x1 any read from address 0x1 'A' 0x80032140 0x2 any write to address 0x1 'A' 0x80032140 0x3 any access to address 0x1 'A' 0x80042160 0x2 0x0 0x12345678 write of value to address. -- the 0x0 is a placeholder for addr2 not used here 0x1 'A' 0x80050000 0x3 0x8006ffff any access within range Each of these commands would return a message of the following format. 'A' Where: bptn - a breakpoint number, which is a small integer with possible values of zero through 255. code - an error return code, a value of zero indicates a succesful completion, other values indicate various errors and warnings. Possible return codes: OK, W_MSK, W_VAL, W_QAL, E_RGE, E_QAL, E_OUT, E_NON Clear Breakpoint ---------------- gdb should use the following command whenever a breakpoint is to be deleted. This command uses the "Breakpoint Number" returned by either the 'B' or 'A' commands. 'b' 0x0 reply: 'b' 0x0 Possible return codes: OK, E_BPT Return codes: ------------- Note that it is possible to get more than one warning value. For example, a return value of 0x103 would indicate that "Range feature is supported via mask" and "Value check is not supported in hardware". OK 0 -- OK W_MSK 0x101 -- warning: Range feature is supported via mask W_VAL 0x102 -- warning: Value check is not supported in hardware W_QAL 0x104 -- warning: Requested qualifiers are not supported in hardware E_BPT 0x200 -- error: No such breakpoint number E_RGE 0x201 -- error: Range is not supported E_QAL 0x202 -- error: The requested qualifiers can not be used E_OUT 0x203 -- error: Out of hardware resources E_NON 0x204 -- error: Hardware breakpoint not supported E_VAL 0x205 -- error: value feature not supported E_ERR 0x206 -- error: requested bpt can not be set #endif