Move panic handler and gdbstub into esp32 component, clean up wdt according to merge req suggestions

This commit is contained in:
Jeroen Domburg
2016-10-26 12:23:01 +08:00
parent 1ca97f5adb
commit 7d254eb3f0
15 changed files with 161 additions and 146 deletions
-39
View File
@@ -93,45 +93,6 @@ config FREERTOS_THREAD_LOCAL_STORAGE_POINTERS
If using the WiFi stack, this value must be at least 1.
#This still needs to be implemented.
choice FREERTOS_PANIC
prompt "Panic handler behaviour"
default FREERTOS_PANIC_PRINT_REBOOT
help
If FreeRTOS detects unexpected behaviour or an unhandled exception, the panic handler is
invoked. Configure the panic handlers action here.
config FREERTOS_PANIC_PRINT_HALT
bool "Print registers and halt"
help
Outputs the relevant registers over the serial port and halt the
processor. Needs a manual reset to restart.
config FREERTOS_PANIC_PRINT_REBOOT
bool "Print registers and reboot"
help
Outputs the relevant registers over the serial port and immediately
reset the processor.
config FREERTOS_PANIC_SILENT_REBOOT
bool "Silent reboot"
help
Just resets the processor without outputting anything
config FREERTOS_PANIC_GDBSTUB
bool "Invoke GDBStub"
help
Invoke gdbstub on the serial port, allowing for gdb to attach to it to do a postmortem
of the crash.
endchoice
config FREERTOS_DEBUG_OCDAWARE
bool "Make exception and panic handlers JTAG/OCD aware"
default y
help
The FreeRTOS panic and unhandled exception handers can detect a JTAG OCD debugger and
instead of panicking, have the debugger stop on the offending instruction.
choice FREERTOS_ASSERT
prompt "FreeRTOS assertions"
default FREERTOS_ASSERT_FAIL_ABORT
-365
View File
@@ -1,365 +0,0 @@
// 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
*******************************************************************************/
//ToDo: Clean up includes and sync to real rtos
#include "rom/ets_sys.h"
#include "soc/uart_reg.h"
#include "soc/io_mux_reg.h"
#include "gdbstub.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
static void ATTR_GDBFN keepWDTalive() {
//ToDo for esp31/32
}
//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) {
keepWDTalive();
}
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)) return 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);
// ets_printf("c %x r %x\n", chsum, rchsum);
if (rchsum!=chsum) {
gdbSendChar('-');
return ST_ERR;
} else {
gdbSendChar('+');
return gdbHandleCommand(cmd, p);
}
}
void gdbstubPanicHandler(XtExcFrame *frame) {
dumpHwToRegfile(frame);
//Make sure txd/rxd are enabled
PIN_PULLUP_DIS(PERIPHS_IO_MUX_U0TXD_U);
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);
}
-22
View File
@@ -1,22 +0,0 @@
// 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.
#ifndef GDBSTUB_H
#define GDBSTUB_H
#include <xtensa/config/core.h>
#include "freertos/xtensa_api.h"
void gdbstubPanicHandler(XtExcFrame *frame);
#endif
+2 -2
View File
@@ -101,7 +101,7 @@
#include "FreeRTOS.h"
#include "task.h"
#include "panic.h"
#include "esp_panic.h"
/* Defined in portasm.h */
extern void _frxt_tick_timer_init(void);
@@ -375,7 +375,7 @@ portBASE_TYPE vPortCPUReleaseMutex(portMUX_TYPE *mux) {
#if CONFIG_FREERTOS_BREAK_ON_SCHEDULER_START_JTAG
void vPortFirstTaskHook(TaskFunction_t function) {
setBreakpointIfJtag(function);
esp_set_breakpoint_if_jtag(function);
}
#endif
+50 -50
View File
@@ -91,7 +91,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*******************************************************************************/
#include "xtensa_rtos.h"
#include "panic.h"
#include "esp_panic.h"
#include "sdkconfig.h"
/*
Define for workaround: pin no-cpu-affinity tasks to a cpu when fpu is used.
@@ -303,12 +303,12 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
.section .iram1,"ax"
.global panicHandler
.global panicHandler
.global _xt_panic
.type _xt_panic,@function
.align 4
.literal_position
.literal_position
.align 4
_xt_panic:
@@ -344,41 +344,41 @@ _xt_panic:
movi a0, PS_INTLEVEL(7) | PS_UM | PS_WOE
wsr a0, PS
//Call panic handler
mov a6,sp
call4 panicHandler
//Call panic handler
mov a6,sp
call4 panicHandler
1: j 1b /* loop infinitely */
retw
retw
.align 4
.align 4
//Call using call0. Prints the hex char in a2. Kills a3, a4, a5
panic_print_hex:
movi a3,0x60000000
movi a4,8
movi a3,0x60000000
movi a4,8
panic_print_hex_loop:
l32i a5, a3, 0x1c
extui a5, a5, 16, 8
bgei a5,64,panic_print_hex_loop
l32i a5, a3, 0x1c
extui a5, a5, 16, 8
bgei a5,64,panic_print_hex_loop
srli a5,a2,28
bgei a5,10,panic_print_hex_a
addi a5,a5,'0'
j panic_print_hex_ok
srli a5,a2,28
bgei a5,10,panic_print_hex_a
addi a5,a5,'0'
j panic_print_hex_ok
panic_print_hex_a:
addi a5,a5,'A'-10
addi a5,a5,'A'-10
panic_print_hex_ok:
s32i a5,a3,0
slli a2,a2,4
addi a4,a4,-1
bnei a4,0,panic_print_hex_loop
movi a5,' '
s32i a5,a3,0
s32i a5,a3,0
slli a2,a2,4
addi a4,a4,-1
bnei a4,0,panic_print_hex_loop
movi a5,' '
s32i a5,a3,0
ret
ret
@@ -463,8 +463,8 @@ _DebugExceptionVector:
jx a3
#else
wsr a0, EXCSAVE+XCHAL_DEBUGLEVEL /* save original a0 somewhere */
movi a0,PANIC_RSN_DEBUGEXCEPTION
wsr a0,EXCCAUSE
movi a0,PANIC_RSN_DEBUGEXCEPTION
wsr a0,EXCCAUSE
call0 _xt_panic /* does not return */
rfi XCHAL_DEBUGLEVEL /* make a0 point here not later */
#endif
@@ -492,8 +492,8 @@ _DoubleExceptionVector:
#if XCHAL_HAVE_DEBUG
break 1, 4 /* unhandled double exception */
#endif
movi a0,PANIC_RSN_DOUBLEEXCEPTION
wsr a0,EXCCAUSE
movi a0,PANIC_RSN_DOUBLEEXCEPTION
wsr a0,EXCCAUSE
call0 _xt_panic /* does not return */
rfde /* make a0 point here not later */
@@ -527,8 +527,8 @@ _xt_kernel_exc:
#if XCHAL_HAVE_DEBUG
break 1, 0 /* unhandled kernel exception */
#endif
movi a0,PANIC_RSN_KERNELEXCEPTION
wsr a0,EXCCAUSE
movi a0,PANIC_RSN_KERNELEXCEPTION
wsr a0,EXCCAUSE
call0 _xt_panic /* does not return */
rfe /* make a0 point here not there */
@@ -916,11 +916,11 @@ _xt_coproc_exc:
addi a2, a2, TASKTCB_XCOREID_OFFSET /* offset to xCoreID in tcb struct */
s32i a3, a2, 0 /* store current cpuid */
/* Grab correct xt_coproc_owner_sa for this core */
movi a2, XCHAL_CP_MAX << 2
mull a2, a2, a3
/* Grab correct xt_coproc_owner_sa for this core */
movi a2, XCHAL_CP_MAX << 2
mull a2, a2, a3
movi a3, _xt_coproc_owner_sa /* a3 = base of owner array */
add a3, a3, a2
add a3, a3, a2
extui a2, a0, 0, 16 /* coprocessor bitmask portion */
or a4, a4, a2 /* a4 = CPENABLE | (1 << n) */
@@ -1031,8 +1031,8 @@ _xt_coproc_exc:
#if XCHAL_HAVE_DEBUG
break 1, 1 /* unhandled user exception */
#endif
movi a0,PANIC_RSN_COPROCEXCEPTION
wsr a0,EXCCAUSE
movi a0,PANIC_RSN_COPROCEXCEPTION
wsr a0,EXCCAUSE
call0 _xt_panic /* not in a thread (invalid) */
/* never returns */
@@ -1620,19 +1620,19 @@ _xt_highint4:
/* On the ESP32, this level is used for the INT_WDT handler. If that triggers, the program is stuck with interrupts
off and the CPU should panic. */
rsr a0, EXCSAVE_4
wsr a0, EXCSAVE_1 /* panic handler reads this register */
/* Set EXCCAUSE to reflect cause of the wdt int trigger */
movi a0,PANIC_RSN_INTWDT_CPU0
wsr a0,EXCCAUSE
rsr a0, EXCSAVE_4
wsr a0, EXCSAVE_1 /* panic handler reads this register */
/* Set EXCCAUSE to reflect cause of the wdt int trigger */
movi a0,PANIC_RSN_INTWDT_CPU0
wsr a0,EXCCAUSE
#if CONFIG_INT_WDT_CHECK_CPU1
/* Check if the cause is the app cpu failing to tick.*/
movi a0, int_wdt_app_cpu_ticked
l32i a0, a0, 0
bnez a0, 1f
/* It is. Modify cause. */
movi a0,PANIC_RSN_INTWDT_CPU1
wsr a0,EXCCAUSE
/* Check if the cause is the app cpu failing to tick.*/
movi a0, int_wdt_app_cpu_ticked
l32i a0, a0, 0
bnez a0, 1f
/* It is. Modify cause. */
movi a0,PANIC_RSN_INTWDT_CPU1
wsr a0,EXCCAUSE
1:
#endif
call0 _xt_panic