| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359 |
- // Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- /******************************************************************************
- * Description: A stub to make the ESP32 debuggable by GDB over the serial
- * port, at least enough to do a backtrace on panic. This gdbstub is read-only:
- * it allows inspecting the ESP32 state
- *******************************************************************************/
- #include "rom/ets_sys.h"
- #include "soc/uart_reg.h"
- #include "soc/io_mux_reg.h"
- #include "esp_gdbstub.h"
- #include "driver/gpio.h"
- //Length of buffer used to reserve GDB commands. Has to be at least able to fit the G command, which
- //implies a minimum size of about 320 bytes.
- #define PBUFLEN 512
- static unsigned char cmd[PBUFLEN]; //GDB command input buffer
- static char chsum; //Running checksum of the output packet
- #define ATTR_GDBFN
- //Receive a char from the uart. Uses polling and feeds the watchdog.
- static int ATTR_GDBFN gdbRecvChar() {
- int i;
- while (((READ_PERI_REG(UART_STATUS_REG(0))>>UART_RXFIFO_CNT_S)&UART_RXFIFO_CNT)==0) ;
- i=READ_PERI_REG(UART_FIFO_REG(0));
- return i;
- }
- //Send a char to the uart.
- static void ATTR_GDBFN gdbSendChar(char c) {
- while (((READ_PERI_REG(UART_STATUS_REG(0))>>UART_TXFIFO_CNT_S)&UART_TXFIFO_CNT)>=126) ;
- WRITE_PERI_REG(UART_FIFO_REG(0), c);
- }
- //Send the start of a packet; reset checksum calculation.
- static void ATTR_GDBFN gdbPacketStart() {
- chsum=0;
- gdbSendChar('$');
- }
- //Send a char as part of a packet
- static void ATTR_GDBFN gdbPacketChar(char c) {
- if (c=='#' || c=='$' || c=='}' || c=='*') {
- gdbSendChar('}');
- gdbSendChar(c^0x20);
- chsum+=(c^0x20)+'}';
- } else {
- gdbSendChar(c);
- chsum+=c;
- }
- }
- //Send a string as part of a packet
- static void ATTR_GDBFN gdbPacketStr(char *c) {
- while (*c!=0) {
- gdbPacketChar(*c);
- c++;
- }
- }
- //Send a hex val as part of a packet. 'bits'/4 dictates the number of hex chars sent.
- static void ATTR_GDBFN gdbPacketHex(int val, int bits) {
- char hexChars[]="0123456789abcdef";
- int i;
- for (i=bits; i>0; i-=4) {
- gdbPacketChar(hexChars[(val>>(i-4))&0xf]);
- }
- }
- //Finish sending a packet.
- static void ATTR_GDBFN gdbPacketEnd() {
- gdbSendChar('#');
- gdbPacketHex(chsum, 8);
- }
- //Error states used by the routines that grab stuff from the incoming gdb packet
- #define ST_ENDPACKET -1
- #define ST_ERR -2
- #define ST_OK -3
- #define ST_CONT -4
- //Grab a hex value from the gdb packet. Ptr will get positioned on the end
- //of the hex string, as far as the routine has read into it. Bits/4 indicates
- //the max amount of hex chars it gobbles up. Bits can be -1 to eat up as much
- //hex chars as possible.
- static long ATTR_GDBFN gdbGetHexVal(unsigned char **ptr, int bits) {
- int i;
- int no;
- unsigned int v=0;
- char c;
- no=bits/4;
- if (bits==-1) no=64;
- for (i=0; i<no; i++) {
- c=**ptr;
- (*ptr)++;
- if (c>='0' && c<='9') {
- v<<=4;
- v|=(c-'0');
- } else if (c>='A' && c<='F') {
- v<<=4;
- v|=(c-'A')+10;
- } else if (c>='a' && c<='f') {
- v<<=4;
- v|=(c-'a')+10;
- } else if (c=='#') {
- if (bits==-1) {
- (*ptr)--;
- return v;
- }
- return ST_ENDPACKET;
- } else {
- if (bits==-1) {
- (*ptr)--;
- return v;
- }
- return ST_ERR;
- }
- }
- return v;
- }
- //Swap an int into the form gdb wants it
- static int ATTR_GDBFN iswap(int i) {
- int r;
- r=((i>>24)&0xff);
- r|=((i>>16)&0xff)<<8;
- r|=((i>>8)&0xff)<<16;
- r|=((i>>0)&0xff)<<24;
- return r;
- }
- //Read a byte from ESP32 memory.
- static unsigned char ATTR_GDBFN readbyte(unsigned int p) {
- int *i=(int*)(p&(~3));
- if (p<0x20000000 || p>=0x80000000) return -1;
- return *i>>((p&3)*8);
- }
- //Register file in the format exp108 gdb port expects it.
- //Inspired by gdb/regformats/reg-xtensa.dat
- typedef struct {
- uint32_t pc;
- uint32_t a[64];
- uint32_t lbeg;
- uint32_t lend;
- uint32_t lcount;
- uint32_t sar;
- uint32_t windowbase;
- uint32_t windowstart;
- uint32_t configid0;
- uint32_t configid1;
- uint32_t ps;
- uint32_t threadptr;
- uint32_t br;
- uint32_t scompare1;
- uint32_t acclo;
- uint32_t acchi;
- uint32_t m0;
- uint32_t m1;
- uint32_t m2;
- uint32_t m3;
- uint32_t expstate; //I'm going to assume this is exccause...
- uint32_t f64r_lo;
- uint32_t f64r_hi;
- uint32_t f64s;
- uint32_t f[16];
- uint32_t fcr;
- uint32_t fsr;
- } GdbRegFile;
- GdbRegFile gdbRegFile;
- /*
- //Register format as the Xtensa HAL has it:
- STRUCT_FIELD (long, 4, XT_STK_EXIT, exit)
- STRUCT_FIELD (long, 4, XT_STK_PC, pc)
- STRUCT_FIELD (long, 4, XT_STK_PS, ps)
- STRUCT_FIELD (long, 4, XT_STK_A0, a0)
- [..]
- STRUCT_FIELD (long, 4, XT_STK_A15, a15)
- STRUCT_FIELD (long, 4, XT_STK_SAR, sar)
- STRUCT_FIELD (long, 4, XT_STK_EXCCAUSE, exccause)
- STRUCT_FIELD (long, 4, XT_STK_EXCVADDR, excvaddr)
- STRUCT_FIELD (long, 4, XT_STK_LBEG, lbeg)
- STRUCT_FIELD (long, 4, XT_STK_LEND, lend)
- STRUCT_FIELD (long, 4, XT_STK_LCOUNT, lcount)
- // Temporary space for saving stuff during window spill
- STRUCT_FIELD (long, 4, XT_STK_TMP0, tmp0)
- STRUCT_FIELD (long, 4, XT_STK_TMP1, tmp1)
- STRUCT_FIELD (long, 4, XT_STK_TMP2, tmp2)
- STRUCT_FIELD (long, 4, XT_STK_VPRI, vpri)
- STRUCT_FIELD (long, 4, XT_STK_OVLY, ovly)
- #endif
- STRUCT_END(XtExcFrame)
- */
- static void dumpHwToRegfile(XtExcFrame *frame) {
- int i;
- long *frameAregs=&frame->a0;
- gdbRegFile.pc=frame->pc;
- for (i=0; i<16; i++) gdbRegFile.a[i]=frameAregs[i];
- for (i=16; i<64; i++) gdbRegFile.a[i]=0xDEADBEEF;
- gdbRegFile.lbeg=frame->lbeg;
- gdbRegFile.lend=frame->lend;
- gdbRegFile.lcount=frame->lcount;
- gdbRegFile.sar=frame->sar;
- //All windows have been spilled to the stack by the ISR routines. The following values should indicate that.
- gdbRegFile.sar=frame->sar;
- gdbRegFile.windowbase=0; //0
- gdbRegFile.windowstart=0x1; //1
- gdbRegFile.configid0=0xdeadbeef; //ToDo
- gdbRegFile.configid1=0xdeadbeef; //ToDo
- gdbRegFile.ps=frame->ps-PS_EXCM_MASK;
- gdbRegFile.threadptr=0xdeadbeef; //ToDo
- gdbRegFile.br=0xdeadbeef; //ToDo
- gdbRegFile.scompare1=0xdeadbeef; //ToDo
- gdbRegFile.acclo=0xdeadbeef; //ToDo
- gdbRegFile.acchi=0xdeadbeef; //ToDo
- gdbRegFile.m0=0xdeadbeef; //ToDo
- gdbRegFile.m1=0xdeadbeef; //ToDo
- gdbRegFile.m2=0xdeadbeef; //ToDo
- gdbRegFile.m3=0xdeadbeef; //ToDo
- gdbRegFile.expstate=frame->exccause; //ToDo
- }
- //Send the reason execution is stopped to GDB.
- static void sendReason() {
- //exception-to-signal mapping
- char exceptionSignal[]={4,31,11,11,2,6,8,0,6,7,0,0,7,7,7,7};
- int i=0;
- gdbPacketStart();
- gdbPacketChar('T');
- i=gdbRegFile.expstate&0x7f;
- if (i<sizeof(exceptionSignal)) {
- gdbPacketHex(exceptionSignal[i], 8);
- } else {
- gdbPacketHex(11, 8);
- }
- gdbPacketEnd();
- }
- //Handle a command as received from GDB.
- static int gdbHandleCommand(unsigned char *cmd, int len) {
- //Handle a command
- int i, j, k;
- unsigned char *data=cmd+1;
- if (cmd[0]=='g') { //send all registers to gdb
- int *p=(int*)&gdbRegFile;
- gdbPacketStart();
- for (i=0; i<sizeof(GdbRegFile)/4; i++) gdbPacketHex(iswap(*p++), 32);
- gdbPacketEnd();
- } else if (cmd[0]=='G') { //receive content for all registers from gdb
- int *p=(int*)&gdbRegFile;
- for (i=0; i<sizeof(GdbRegFile)/4; i++) *p++=iswap(gdbGetHexVal(&data, 32));;
- gdbPacketStart();
- gdbPacketStr("OK");
- gdbPacketEnd();
- } else if (cmd[0]=='m') { //read memory to gdb
- i=gdbGetHexVal(&data, -1);
- data++;
- j=gdbGetHexVal(&data, -1);
- gdbPacketStart();
- for (k=0; k<j; k++) {
- gdbPacketHex(readbyte(i++), 8);
- }
- gdbPacketEnd();
- } else if (cmd[0]=='?') { //Reply with stop reason
- sendReason();
- } else {
- //We don't recognize or support whatever GDB just sent us.
- gdbPacketStart();
- gdbPacketEnd();
- return ST_ERR;
- }
- return ST_OK;
- }
- //Lower layer: grab a command packet and check the checksum
- //Calls gdbHandleCommand on the packet if the checksum is OK
- //Returns ST_OK on success, ST_ERR when checksum fails, a
- //character if it is received instead of the GDB packet
- //start char.
- static int gdbReadCommand() {
- unsigned char c;
- unsigned char chsum=0, rchsum;
- unsigned char sentchs[2];
- int p=0;
- unsigned char *ptr;
- c=gdbRecvChar();
- if (c!='$') return c;
- while(1) {
- c=gdbRecvChar();
- if (c=='#') { //end of packet, checksum follows
- cmd[p]=0;
- break;
- }
- chsum+=c;
- if (c=='$') {
- //Wut, restart packet?
- chsum=0;
- p=0;
- continue;
- }
- if (c=='}') { //escape the next char
- c=gdbRecvChar();
- chsum+=c;
- c^=0x20;
- }
- cmd[p++]=c;
- if (p>=PBUFLEN) return ST_ERR;
- }
- //A # has been received. Get and check the received chsum.
- sentchs[0]=gdbRecvChar();
- sentchs[1]=gdbRecvChar();
- ptr=&sentchs[0];
- rchsum=gdbGetHexVal(&ptr, 8);
- if (rchsum!=chsum) {
- gdbSendChar('-');
- return ST_ERR;
- } else {
- gdbSendChar('+');
- return gdbHandleCommand(cmd, p);
- }
- }
- void esp_gdbstub_panic_handler(XtExcFrame *frame) {
- dumpHwToRegfile(frame);
- //Make sure txd/rxd are enabled
- gpio_pullup_dis(1);
- PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0RXD_U, FUNC_U0RXD_U0RXD);
- PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_U0TXD_U0TXD);
- sendReason();
- while(gdbReadCommand()!=ST_CONT);
- while(1);
- }
|