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