1 // Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
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
7 // http://www.apache.org/licenses/LICENSE-2.0
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.
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 *******************************************************************************/
21 //ToDo: Clean up includes and sync to real rtos
22 #include "rom/ets_sys.h"
24 #include "soc/uart_register.h"
25 #include "soc/io_mux_reg.h"
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.
33 static unsigned char cmd[PBUFLEN]; //GDB command input buffer
34 static char chsum; //Running checksum of the output packet
38 static void ATTR_GDBFN keepWDTalive() {
43 //Receive a char from the uart. Uses polling and feeds the watchdog.
44 static int ATTR_GDBFN gdbRecvChar() {
46 while (((READ_PERI_REG(UART_STATUS_REG(0))>>UART_RXFIFO_CNT_S)&UART_RXFIFO_CNT)==0) {
49 i=READ_PERI_REG(UART_FIFO_REG(0));
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);
59 //Send the start of a packet; reset checksum calculation.
60 static void ATTR_GDBFN gdbPacketStart() {
65 //Send a char as part of a packet
66 static void ATTR_GDBFN gdbPacketChar(char c) {
67 if (c=='#' || c=='$' || c=='}' || c=='*') {
77 //Send a string as part of a packet
78 static void ATTR_GDBFN gdbPacketStr(char *c) {
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";
89 for (i=bits; i>0; i-=4) {
90 gdbPacketChar(hexChars[(val>>(i-4))&0xf]);
94 //Finish sending a packet.
95 static void ATTR_GDBFN gdbPacketEnd() {
97 gdbPacketHex(chsum, 8);
100 //Error states used by the routines that grab stuff from the incoming gdb packet
101 #define ST_ENDPACKET -1
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) {
117 for (i=0; i<no; i++) {
120 if (c>='0' && c<='9') {
123 } else if (c>='A' && c<='F') {
126 } else if (c>='a' && c<='f') {
146 //Swap an int into the form gdb wants it
147 static int ATTR_GDBFN iswap(int i) {
150 r|=((i>>16)&0xff)<<8;
151 r|=((i>>8)&0xff)<<16;
152 r|=((i>>0)&0xff)<<24;
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);
164 //Register file in the format exp108 gdb port expects it.
165 //Inspired by gdb/regformats/reg-xtensa.dat
174 uint32_t windowstart;
187 uint32_t expstate; //I'm going to assume this is exccause...
197 GdbRegFile gdbRegFile;
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)
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)
220 STRUCT_END(XtExcFrame)
224 static void dumpHwToRegfile(XtExcFrame *frame) {
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
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};
261 i=gdbRegFile.expstate&0x7f;
262 if (i<sizeof(exceptionSignal)) return gdbPacketHex(exceptionSignal[i], 8); else gdbPacketHex(11, 8);
266 //Handle a command as received from GDB.
267 static int gdbHandleCommand(unsigned char *cmd, int len) {
270 unsigned char *data=cmd+1;
271 if (cmd[0]=='g') { //send all registers to gdb
272 int *p=(int*)&gdbRegFile;
274 for (i=0; i<sizeof(GdbRegFile)/4; i++) gdbPacketHex(iswap(*p++), 32);
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));;
282 } else if (cmd[0]=='m') { //read memory to gdb
283 i=gdbGetHexVal(&data, -1);
285 j=gdbGetHexVal(&data, -1);
287 for (k=0; k<j; k++) {
288 gdbPacketHex(readbyte(i++), 8);
291 } else if (cmd[0]=='?') { //Reply with stop reason
294 //We don't recognize or support whatever GDB just sent us.
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
308 static int gdbReadCommand() {
310 unsigned char chsum=0, rchsum;
311 unsigned char sentchs[2];
315 if (c!='$') return c;
318 if (c=='#') { //end of packet, checksum follows
324 //Wut, restart packet?
329 if (c=='}') { //escape the next char
335 if (p>=PBUFLEN) return ST_ERR;
337 //A # has been received. Get and check the received chsum.
338 sentchs[0]=gdbRecvChar();
339 sentchs[1]=gdbRecvChar();
341 rchsum=gdbGetHexVal(&ptr, 8);
342 // ets_printf("c %x r %x\n", chsum, rchsum);
348 return gdbHandleCommand(cmd, p);
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);
362 while(gdbReadCommand()!=ST_CONT);