]> granicus.if.org Git - esp-idf/blob - components/freertos/gdbstub.c
Initial public version
[esp-idf] / components / freertos / gdbstub.c
1 // Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 /******************************************************************************
16  * Description: A stub to make the ESP32 debuggable by GDB over the serial 
17  * port, at least enough to do a backtrace on panic. This gdbstub is read-only:
18  * it allows inspecting the ESP32 state 
19  *******************************************************************************/
20
21 //ToDo: Clean up includes and sync to real rtos
22 #include "rom/ets_sys.h"
23
24 #include "soc/uart_register.h"
25 #include "soc/io_mux_reg.h"
26
27 #include "gdbstub.h"
28
29 //Length of buffer used to reserve GDB commands. Has to be at least able to fit the G command, which
30 //implies a minimum size of about 320 bytes.
31 #define PBUFLEN 512
32
33 static unsigned char cmd[PBUFLEN];              //GDB command input buffer
34 static char chsum;                                              //Running checksum of the output packet
35
36 #define ATTR_GDBFN
37
38 static void ATTR_GDBFN keepWDTalive() {
39         //ToDo for esp31/32
40 }
41
42
43 //Receive a char from the uart. Uses polling and feeds the watchdog.
44 static int ATTR_GDBFN gdbRecvChar() {
45         int i;
46         while (((READ_PERI_REG(UART_STATUS_REG(0))>>UART_RXFIFO_CNT_S)&UART_RXFIFO_CNT)==0) {
47                 keepWDTalive();
48         }
49         i=READ_PERI_REG(UART_FIFO_REG(0));
50         return i;
51 }
52
53 //Send a char to the uart.
54 static void ATTR_GDBFN gdbSendChar(char c) {
55         while (((READ_PERI_REG(UART_STATUS_REG(0))>>UART_TXFIFO_CNT_S)&UART_TXFIFO_CNT)>=126) ;
56         WRITE_PERI_REG(UART_FIFO_REG(0), c);
57 }
58
59 //Send the start of a packet; reset checksum calculation.
60 static void ATTR_GDBFN gdbPacketStart() {
61         chsum=0;
62         gdbSendChar('$');
63 }
64
65 //Send a char as part of a packet
66 static void ATTR_GDBFN gdbPacketChar(char c) {
67         if (c=='#' || c=='$' || c=='}' || c=='*') {
68                 gdbSendChar('}');
69                 gdbSendChar(c^0x20);
70                 chsum+=(c^0x20)+'}';
71         } else {
72                 gdbSendChar(c);
73                 chsum+=c;
74         }
75 }
76
77 //Send a string as part of a packet
78 static void ATTR_GDBFN gdbPacketStr(char *c) {
79         while (*c!=0) {
80                 gdbPacketChar(*c);
81                 c++;
82         }
83 }
84
85 //Send a hex val as part of a packet. 'bits'/4 dictates the number of hex chars sent.
86 static void ATTR_GDBFN gdbPacketHex(int val, int bits) {
87         char hexChars[]="0123456789abcdef";
88         int i;
89         for (i=bits; i>0; i-=4) {
90                 gdbPacketChar(hexChars[(val>>(i-4))&0xf]);
91         }
92 }
93
94 //Finish sending a packet.
95 static void ATTR_GDBFN gdbPacketEnd() {
96         gdbSendChar('#');
97         gdbPacketHex(chsum, 8);
98 }
99
100 //Error states used by the routines that grab stuff from the incoming gdb packet
101 #define ST_ENDPACKET -1
102 #define ST_ERR -2
103 #define ST_OK -3
104 #define ST_CONT -4
105
106 //Grab a hex value from the gdb packet. Ptr will get positioned on the end
107 //of the hex string, as far as the routine has read into it. Bits/4 indicates
108 //the max amount of hex chars it gobbles up. Bits can be -1 to eat up as much
109 //hex chars as possible.
110 static long ATTR_GDBFN gdbGetHexVal(unsigned char **ptr, int bits) {
111         int i;
112         int no;
113         unsigned int v=0;
114         char c;
115         no=bits/4;
116         if (bits==-1) no=64;
117         for (i=0; i<no; i++) {
118                 c=**ptr;
119                 (*ptr)++;
120                 if (c>='0' && c<='9') {
121                         v<<=4;
122                         v|=(c-'0');
123                 } else if (c>='A' && c<='F') {
124                         v<<=4;
125                         v|=(c-'A')+10;
126                 } else if (c>='a' && c<='f') {
127                         v<<=4;
128                         v|=(c-'a')+10;
129                 } else if (c=='#') {
130                         if (bits==-1) {
131                                 (*ptr)--;
132                                 return v;
133                         }
134                         return ST_ENDPACKET;
135                 } else {
136                         if (bits==-1) {
137                                 (*ptr)--;
138                                 return v;
139                         }
140                         return ST_ERR;
141                 }
142         }
143         return v;
144 }
145
146 //Swap an int into the form gdb wants it
147 static int ATTR_GDBFN iswap(int i) {
148         int r;
149         r=((i>>24)&0xff);
150         r|=((i>>16)&0xff)<<8;
151         r|=((i>>8)&0xff)<<16;
152         r|=((i>>0)&0xff)<<24;
153         return r;
154 }
155
156 //Read a byte from ESP32 memory.
157 static unsigned char ATTR_GDBFN readbyte(unsigned int p) {
158         int *i=(int*)(p&(~3));
159         if (p<0x20000000 || p>=0x80000000) return -1;
160         return *i>>((p&3)*8);
161 }
162
163
164 //Register file in the format exp108 gdb port expects it.
165 //Inspired by gdb/regformats/reg-xtensa.dat
166 typedef struct {
167         uint32_t pc;
168         uint32_t a[64];
169         uint32_t lbeg;
170         uint32_t lend;
171         uint32_t lcount;
172         uint32_t sar;
173         uint32_t windowbase;
174         uint32_t windowstart;
175         uint32_t configid0;
176         uint32_t configid1;
177         uint32_t ps;
178         uint32_t threadptr;
179         uint32_t br;
180         uint32_t scompare1;
181         uint32_t acclo;
182         uint32_t acchi;
183         uint32_t m0;
184         uint32_t m1;
185         uint32_t m2;
186         uint32_t m3;
187         uint32_t expstate;  //I'm going to assume this is exccause...
188         uint32_t f64r_lo;
189         uint32_t f64r_hi;
190         uint32_t f64s;
191         uint32_t f[16];
192         uint32_t fcr;
193         uint32_t fsr;
194 } GdbRegFile;
195
196
197 GdbRegFile gdbRegFile;
198
199 /* 
200 //Register format as the Xtensa HAL has it:
201 STRUCT_FIELD (long, 4, XT_STK_EXIT,     exit)
202 STRUCT_FIELD (long, 4, XT_STK_PC,       pc)
203 STRUCT_FIELD (long, 4, XT_STK_PS,       ps)
204 STRUCT_FIELD (long, 4, XT_STK_A0,       a0)
205 [..]
206 STRUCT_FIELD (long, 4, XT_STK_A15,      a15)
207 STRUCT_FIELD (long, 4, XT_STK_SAR,      sar)
208 STRUCT_FIELD (long, 4, XT_STK_EXCCAUSE, exccause)
209 STRUCT_FIELD (long, 4, XT_STK_EXCVADDR, excvaddr)
210 STRUCT_FIELD (long, 4, XT_STK_LBEG,   lbeg)
211 STRUCT_FIELD (long, 4, XT_STK_LEND,   lend)
212 STRUCT_FIELD (long, 4, XT_STK_LCOUNT, lcount)
213 // Temporary space for saving stuff during window spill 
214 STRUCT_FIELD (long, 4, XT_STK_TMP0,   tmp0)
215 STRUCT_FIELD (long, 4, XT_STK_TMP1,   tmp1)
216 STRUCT_FIELD (long, 4, XT_STK_TMP2,   tmp2)
217 STRUCT_FIELD (long, 4, XT_STK_VPRI,   vpri)
218 STRUCT_FIELD (long, 4, XT_STK_OVLY,   ovly)
219 #endif
220 STRUCT_END(XtExcFrame)
221 */
222
223
224 static void dumpHwToRegfile(XtExcFrame *frame) {
225         int i;
226         long *frameAregs=&frame->a0;
227         gdbRegFile.pc=frame->pc;
228         for (i=0; i<16; i++) gdbRegFile.a[i]=frameAregs[i];
229         for (i=16; i<64; i++) gdbRegFile.a[i]=0xDEADBEEF;
230         gdbRegFile.lbeg=frame->lbeg;
231         gdbRegFile.lend=frame->lend;
232         gdbRegFile.lcount=frame->lcount;
233         gdbRegFile.sar=frame->sar;
234         //All windows have been spilled to the stack by the ISR routines. The following values should indicate that.
235         gdbRegFile.sar=frame->sar;
236         gdbRegFile.windowbase=0; //0
237         gdbRegFile.windowstart=0x1; //1
238         gdbRegFile.configid0=0xdeadbeef; //ToDo
239         gdbRegFile.configid1=0xdeadbeef; //ToDo
240         gdbRegFile.ps=frame->ps-PS_EXCM_MASK;
241         gdbRegFile.threadptr=0xdeadbeef; //ToDo
242         gdbRegFile.br=0xdeadbeef; //ToDo
243         gdbRegFile.scompare1=0xdeadbeef; //ToDo
244         gdbRegFile.acclo=0xdeadbeef; //ToDo
245         gdbRegFile.acchi=0xdeadbeef; //ToDo
246         gdbRegFile.m0=0xdeadbeef; //ToDo
247         gdbRegFile.m1=0xdeadbeef; //ToDo
248         gdbRegFile.m2=0xdeadbeef; //ToDo
249         gdbRegFile.m3=0xdeadbeef; //ToDo
250         gdbRegFile.expstate=frame->exccause; //ToDo
251 }
252
253
254 //Send the reason execution is stopped to GDB.
255 static void sendReason() {
256         //exception-to-signal mapping
257         char exceptionSignal[]={4,31,11,11,2,6,8,0,6,7,0,0,7,7,7,7};
258         int i=0;
259         gdbPacketStart();
260         gdbPacketChar('T');
261         i=gdbRegFile.expstate&0x7f;
262         if (i<sizeof(exceptionSignal)) return gdbPacketHex(exceptionSignal[i], 8); else gdbPacketHex(11, 8);
263         gdbPacketEnd();
264 }
265
266 //Handle a command as received from GDB.
267 static int gdbHandleCommand(unsigned char *cmd, int len) {
268         //Handle a command
269         int i, j, k;
270         unsigned char *data=cmd+1;
271         if (cmd[0]=='g') {              //send all registers to gdb
272                 int *p=(int*)&gdbRegFile;
273                 gdbPacketStart();
274                 for (i=0; i<sizeof(GdbRegFile)/4; i++) gdbPacketHex(iswap(*p++), 32);
275                 gdbPacketEnd();
276         } else if (cmd[0]=='G') {       //receive content for all registers from gdb
277                 int *p=(int*)&gdbRegFile;
278                 for (i=0; i<sizeof(GdbRegFile)/4; i++) *p++=iswap(gdbGetHexVal(&data, 32));;
279                 gdbPacketStart();
280                 gdbPacketStr("OK");
281                 gdbPacketEnd();
282         } else if (cmd[0]=='m') {       //read memory to gdb
283                 i=gdbGetHexVal(&data, -1);
284                 data++;
285                 j=gdbGetHexVal(&data, -1);
286                 gdbPacketStart();
287                 for (k=0; k<j; k++) {
288                         gdbPacketHex(readbyte(i++), 8);
289                 }
290                 gdbPacketEnd();
291         } else if (cmd[0]=='?') {       //Reply with stop reason
292                 sendReason();
293         } else {
294                 //We don't recognize or support whatever GDB just sent us.
295                 gdbPacketStart();
296                 gdbPacketEnd();
297                 return ST_ERR;
298         }
299         return ST_OK;
300 }
301
302
303 //Lower layer: grab a command packet and check the checksum
304 //Calls gdbHandleCommand on the packet if the checksum is OK
305 //Returns ST_OK on success, ST_ERR when checksum fails, a 
306 //character if it is received instead of the GDB packet
307 //start char.
308 static int gdbReadCommand() {
309         unsigned char c;
310         unsigned char chsum=0, rchsum;
311         unsigned char sentchs[2];
312         int p=0;
313         unsigned char *ptr;
314         c=gdbRecvChar();
315         if (c!='$') return c;
316         while(1) {
317                 c=gdbRecvChar();
318                 if (c=='#') {   //end of packet, checksum follows
319                         cmd[p]=0;
320                         break;
321                 }
322                 chsum+=c;
323                 if (c=='$') {
324                         //Wut, restart packet?
325                         chsum=0;
326                         p=0;
327                         continue;
328                 }
329                 if (c=='}') {           //escape the next char
330                         c=gdbRecvChar();
331                         chsum+=c;
332                         c^=0x20;
333                 }
334                 cmd[p++]=c;
335                 if (p>=PBUFLEN) return ST_ERR;
336         }
337         //A # has been received. Get and check the received chsum.
338         sentchs[0]=gdbRecvChar();
339         sentchs[1]=gdbRecvChar();
340         ptr=&sentchs[0];
341         rchsum=gdbGetHexVal(&ptr, 8);
342 //      ets_printf("c %x r %x\n", chsum, rchsum);
343         if (rchsum!=chsum) {
344                 gdbSendChar('-');
345                 return ST_ERR;
346         } else {
347                 gdbSendChar('+');
348                 return gdbHandleCommand(cmd, p);
349         }
350 }
351
352
353
354 void gdbstubPanicHandler(XtExcFrame *frame) {
355         dumpHwToRegfile(frame);
356         //Make sure txd/rxd are enabled
357         PIN_PULLUP_DIS(PERIPHS_IO_MUX_U0TXD_U);
358         PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0RXD_U, FUNC_U0RXD_U0RXD);
359         PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_U0TXD_U0TXD);
360
361         sendReason();
362         while(gdbReadCommand()!=ST_CONT);
363         while(1);
364 }
365