2016-08-17 15:08:22 +00:00
|
|
|
// 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.
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
|
|
#include <xtensa/config/core.h>
|
|
|
|
|
|
|
|
#include "rom/rtc.h"
|
|
|
|
|
|
|
|
#include "freertos/FreeRTOS.h"
|
|
|
|
#include "freertos/task.h"
|
|
|
|
#include "freertos/xtensa_api.h"
|
|
|
|
|
2016-09-13 15:02:03 +00:00
|
|
|
#include "soc/uart_reg.h"
|
2016-08-17 15:08:22 +00:00
|
|
|
#include "soc/io_mux_reg.h"
|
|
|
|
#include "soc/dport_reg.h"
|
|
|
|
#include "soc/rtc_cntl_reg.h"
|
2016-10-25 09:05:13 +00:00
|
|
|
#include "soc/timer_group_struct.h"
|
2016-10-26 04:23:01 +00:00
|
|
|
#include "soc/timer_group_reg.h"
|
2016-11-21 09:15:37 +00:00
|
|
|
#include "soc/cpu.h"
|
2016-08-17 15:08:22 +00:00
|
|
|
|
2016-10-26 04:23:01 +00:00
|
|
|
#include "esp_gdbstub.h"
|
|
|
|
#include "esp_panic.h"
|
2016-10-28 06:32:11 +00:00
|
|
|
#include "esp_attr.h"
|
2016-08-17 15:08:22 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
Panic handlers; these get called when an unhandled exception occurs or the assembly-level
|
|
|
|
task switching / interrupt code runs into an unrecoverable error. The default task stack
|
|
|
|
overflow handler also is in here.
|
|
|
|
*/
|
|
|
|
|
2016-10-28 06:32:11 +00:00
|
|
|
/*
|
|
|
|
Note: The linker script will put everything in this file in IRAM/DRAM, so it also works with flash cache disabled.
|
|
|
|
*/
|
|
|
|
|
2016-10-27 03:17:24 +00:00
|
|
|
#if !CONFIG_ESP32_PANIC_SILENT_REBOOT
|
2016-08-23 12:14:54 +00:00
|
|
|
//printf may be broken, so we fix our own printing fns...
|
2016-08-17 15:08:22 +00:00
|
|
|
inline static void panicPutchar(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);
|
|
|
|
}
|
|
|
|
|
|
|
|
inline static void panicPutStr(const char *c) {
|
|
|
|
int x=0;
|
|
|
|
while (c[x]!=0) {
|
|
|
|
panicPutchar(c[x]);
|
|
|
|
x++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
inline static void panicPutHex(int a) {
|
|
|
|
int x;
|
|
|
|
int c;
|
|
|
|
panicPutchar(' ');
|
|
|
|
for (x=0; x<8; x++) {
|
|
|
|
c=(a>>28)&0xf;
|
|
|
|
if (c<10) panicPutchar('0'+c); else panicPutchar('a'+c-10);
|
|
|
|
a<<=4;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
inline static void panicPutDec(int a) {
|
|
|
|
int n1, n2;
|
|
|
|
n1=a%10;
|
|
|
|
n2=a/10;
|
|
|
|
panicPutchar(' ');
|
|
|
|
if (n2==0) panicPutchar(' '); else panicPutchar(n2+'0');
|
|
|
|
panicPutchar(n1+'0');
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
//No printing wanted. Stub out these functions.
|
|
|
|
inline static void panicPutchar(char c) { }
|
|
|
|
inline static void panicPutStr(const char *c) { }
|
|
|
|
inline static void panicPutHex(int a) { }
|
|
|
|
inline static void panicPutDec(int a) { }
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
void __attribute__((weak)) vApplicationStackOverflowHook( TaskHandle_t xTask, signed char *pcTaskName ) {
|
2016-10-02 00:04:09 +00:00
|
|
|
panicPutStr("***ERROR*** A stack overflow in task ");
|
2016-08-17 15:08:22 +00:00
|
|
|
panicPutStr((char*)pcTaskName);
|
2016-10-02 00:04:09 +00:00
|
|
|
panicPutStr(" has been detected.\r\n");
|
2016-08-17 15:08:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static const char *edesc[]={
|
|
|
|
"IllegalInstruction", "Syscall", "InstructionFetchError", "LoadStoreError",
|
|
|
|
"Level1Interrupt", "Alloca", "IntegerDivideByZero", "PCValue",
|
|
|
|
"Privileged", "LoadStoreAlignment", "res", "res",
|
|
|
|
"InstrPDAddrError", "LoadStorePIFDataError", "InstrPIFAddrError", "LoadStorePIFAddrError",
|
|
|
|
"InstTLBMiss", "InstTLBMultiHit", "InstFetchPrivilege", "res",
|
|
|
|
"InstrFetchProhibited", "res", "res", "res",
|
|
|
|
"LoadStoreTLBMiss", "LoadStoreTLBMultihit", "LoadStorePrivilege", "res",
|
|
|
|
"LoadProhibited", "StoreProhibited", "res", "res",
|
|
|
|
"Cp0Dis", "Cp1Dis", "Cp2Dis", "Cp3Dis",
|
|
|
|
"Cp4Dis", "Cp5Dis", "Cp6Dis", "Cp7Dis"
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
void commonErrorHandler(XtExcFrame *frame);
|
|
|
|
|
|
|
|
//The fact that we've panic'ed probably means the other CPU is now running wild, possibly
|
2016-11-21 09:15:37 +00:00
|
|
|
//messing up the serial output, so we stall it here.
|
|
|
|
static void haltOtherCore()
|
|
|
|
{
|
|
|
|
esp_cpu_stall( xPortGetCoreID() == 0 ? 1 : 0 );
|
2016-08-17 15:08:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//Returns true when a debugger is attached using JTAG.
|
|
|
|
static int inOCDMode() {
|
2016-10-27 03:17:24 +00:00
|
|
|
#if CONFIG_ESP32_DEBUG_OCDAWARE
|
2016-08-17 15:08:22 +00:00
|
|
|
int dcr;
|
|
|
|
int reg=0x10200C; //DSRSET register
|
|
|
|
asm("rer %0,%1":"=r"(dcr):"r"(reg));
|
|
|
|
return (dcr&0x1);
|
|
|
|
#else
|
|
|
|
return 0; //Always return no debugger is attached.
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void panicHandler(XtExcFrame *frame) {
|
2016-10-25 09:05:13 +00:00
|
|
|
int *regs=(int*)frame;
|
|
|
|
//Please keep in sync with PANIC_RSN_* defines
|
|
|
|
const char *reasons[]={
|
|
|
|
"Unknown reason",
|
|
|
|
"Unhandled debug exception",
|
|
|
|
"Double exception",
|
|
|
|
"Unhandled kernel exception",
|
|
|
|
"Coprocessor exception",
|
2016-10-25 10:08:55 +00:00
|
|
|
"Interrupt wdt timeout on CPU0",
|
|
|
|
"Interrupt wdt timeout on CPU1",
|
2016-10-25 09:05:13 +00:00
|
|
|
};
|
|
|
|
const char *reason=reasons[0];
|
|
|
|
//The panic reason is stored in the EXCCAUSE register.
|
|
|
|
if (regs[20]<=PANIC_RSN_MAX) reason=reasons[regs[20]];
|
2016-08-17 15:08:22 +00:00
|
|
|
haltOtherCore();
|
|
|
|
panicPutStr("Guru Meditation Error: Core ");
|
|
|
|
panicPutDec(xPortGetCoreID());
|
2016-10-25 09:05:13 +00:00
|
|
|
panicPutStr(" panic'ed (");
|
|
|
|
panicPutStr(reason);
|
|
|
|
panicPutStr(")\r\n");
|
2016-08-17 15:08:22 +00:00
|
|
|
|
|
|
|
if (inOCDMode()) {
|
|
|
|
asm("break.n 1");
|
|
|
|
}
|
|
|
|
commonErrorHandler(frame);
|
|
|
|
}
|
|
|
|
|
2016-08-24 04:23:58 +00:00
|
|
|
static void setFirstBreakpoint(uint32_t pc) {
|
|
|
|
asm(
|
|
|
|
"wsr.ibreaka0 %0\n" \
|
|
|
|
"rsr.ibreakenable a3\n" \
|
|
|
|
"movi a4,1\n" \
|
|
|
|
"or a4, a4, a3\n" \
|
|
|
|
"wsr.ibreakenable a4\n" \
|
|
|
|
::"r"(pc):"a3","a4");
|
|
|
|
}
|
|
|
|
|
2016-08-17 15:08:22 +00:00
|
|
|
void xt_unhandled_exception(XtExcFrame *frame) {
|
|
|
|
int *regs=(int*)frame;
|
|
|
|
int x;
|
|
|
|
|
|
|
|
haltOtherCore();
|
|
|
|
panicPutStr("Guru Meditation Error of type ");
|
|
|
|
x=regs[20];
|
|
|
|
if (x<40) panicPutStr(edesc[x]); else panicPutStr("Unknown");
|
2016-09-26 03:18:43 +00:00
|
|
|
panicPutStr(" occurred on core ");
|
2016-08-17 15:08:22 +00:00
|
|
|
panicPutDec(xPortGetCoreID());
|
|
|
|
if (inOCDMode()) {
|
|
|
|
panicPutStr(" at pc=");
|
|
|
|
panicPutHex(regs[1]);
|
|
|
|
panicPutStr(". Setting bp and returning..\r\n");
|
|
|
|
//Stick a hardware breakpoint on the address the handler returns to. This way, the OCD debugger
|
|
|
|
//will kick in exactly at the context the error happened.
|
2016-08-24 04:23:58 +00:00
|
|
|
setFirstBreakpoint(regs[1]);
|
2016-08-30 09:55:20 +00:00
|
|
|
return;
|
2016-08-17 15:08:22 +00:00
|
|
|
}
|
|
|
|
panicPutStr(". Exception was unhandled.\r\n");
|
|
|
|
commonErrorHandler(frame);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-10-26 04:23:01 +00:00
|
|
|
/*
|
|
|
|
If watchdogs are enabled, the panic handler runs the risk of getting aborted pre-emptively because
|
|
|
|
an overzealous watchdog decides to reset it. On the other hand, if we disable all watchdogs, we run
|
|
|
|
the risk of somehow halting in the panic handler and not resetting. That is why this routine kills
|
|
|
|
all watchdogs except the timer group 0 watchdog, and it reconfigures that to reset the chip after
|
|
|
|
one second.
|
|
|
|
*/
|
2016-10-25 09:05:13 +00:00
|
|
|
static void reconfigureAllWdts() {
|
2016-10-26 06:54:50 +00:00
|
|
|
TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE;
|
2016-10-25 09:05:13 +00:00
|
|
|
TIMERG0.wdt_feed=1;
|
|
|
|
TIMERG0.wdt_config0.sys_reset_length=7; //3.2uS
|
|
|
|
TIMERG0.wdt_config0.cpu_reset_length=7; //3.2uS
|
2016-10-26 06:54:50 +00:00
|
|
|
TIMERG0.wdt_config0.stg0=TIMG_WDT_STG_SEL_RESET_SYSTEM; //1st stage timeout: reset system
|
2016-10-25 09:05:13 +00:00
|
|
|
TIMERG0.wdt_config1.clk_prescale=80*500; //Prescaler: wdt counts in ticks of 0.5mS
|
|
|
|
TIMERG0.wdt_config2=2000; //1 second before reset
|
|
|
|
TIMERG0.wdt_config0.en=1;
|
|
|
|
TIMERG0.wdt_wprotect=0;
|
|
|
|
//Disable wdt 1
|
2016-10-26 06:54:50 +00:00
|
|
|
TIMERG1.wdt_wprotect=TIMG_WDT_WKEY_VALUE;
|
2016-10-25 09:05:13 +00:00
|
|
|
TIMERG1.wdt_config0.en=0;
|
|
|
|
TIMERG1.wdt_wprotect=0;
|
|
|
|
}
|
|
|
|
|
2016-10-27 03:17:24 +00:00
|
|
|
#if CONFIG_ESP32_PANIC_GDBSTUB || CONFIG_ESP32_PANIC_PRINT_HALT
|
2016-10-26 04:23:01 +00:00
|
|
|
/*
|
|
|
|
This disables all the watchdogs for when we call the gdbstub.
|
|
|
|
*/
|
2016-10-25 09:05:13 +00:00
|
|
|
static void disableAllWdts() {
|
2016-10-26 06:54:50 +00:00
|
|
|
TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE;
|
2016-10-25 09:05:13 +00:00
|
|
|
TIMERG0.wdt_config0.en=0;
|
|
|
|
TIMERG0.wdt_wprotect=0;
|
2016-10-26 06:54:50 +00:00
|
|
|
TIMERG1.wdt_wprotect=TIMG_WDT_WKEY_VALUE;
|
2016-10-25 09:05:13 +00:00
|
|
|
TIMERG1.wdt_config0.en=0;
|
|
|
|
TIMERG0.wdt_wprotect=0;
|
|
|
|
}
|
|
|
|
|
2016-10-27 03:17:24 +00:00
|
|
|
#endif
|
2016-10-25 09:05:13 +00:00
|
|
|
|
2016-08-17 15:08:22 +00:00
|
|
|
/*
|
|
|
|
We arrive here after a panic or unhandled exception, when no OCD is detected. Dump the registers to the
|
|
|
|
serial port and either jump to the gdb stub, halt the CPU or reboot.
|
|
|
|
*/
|
|
|
|
void commonErrorHandler(XtExcFrame *frame) {
|
|
|
|
int *regs=(int*)frame;
|
|
|
|
int x, y;
|
|
|
|
const char *sdesc[]={
|
|
|
|
"PC ","PS ","A0 ","A1 ","A2 ","A3 ","A4 ","A5 ",
|
|
|
|
"A6 ","A7 ","A8 ","A9 ","A10 ","A11 ","A12 ","A13 ",
|
|
|
|
"A14 ","A15 ","SAR ","EXCCAUSE","EXCVADDR","LBEG ","LEND ","LCOUNT "};
|
|
|
|
|
2016-10-25 09:05:13 +00:00
|
|
|
//Feed the watchdogs, so they will give us time to print out debug info
|
|
|
|
reconfigureAllWdts();
|
|
|
|
|
2016-08-17 15:08:22 +00:00
|
|
|
panicPutStr("Register dump:\r\n");
|
|
|
|
|
|
|
|
for (x=0; x<24; x+=4) {
|
|
|
|
for (y=0; y<4; y++) {
|
|
|
|
if (sdesc[x+y][0]!=0) {
|
|
|
|
panicPutStr(sdesc[x+y]);
|
|
|
|
panicPutStr(": ");
|
|
|
|
panicPutHex(regs[x+y+1]);
|
|
|
|
panicPutStr(" ");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
panicPutStr("\r\n");
|
|
|
|
}
|
2016-10-27 03:17:24 +00:00
|
|
|
#if CONFIG_ESP32_PANIC_GDBSTUB
|
2016-10-25 09:05:13 +00:00
|
|
|
disableAllWdts();
|
2016-08-17 15:08:22 +00:00
|
|
|
panicPutStr("Entering gdb stub now.\r\n");
|
2016-10-26 04:23:01 +00:00
|
|
|
esp_gdbstub_panic_handler(frame);
|
2016-10-27 03:17:24 +00:00
|
|
|
#elif CONFIG_ESP32_PANIC_PRINT_REBOOT || CONFIG_ESP32_PANIC_SILENT_REBOOT
|
2016-08-17 15:08:22 +00:00
|
|
|
panicPutStr("Rebooting...\r\n");
|
|
|
|
for (x=0; x<100; x++) ets_delay_us(1000);
|
|
|
|
software_reset();
|
|
|
|
#else
|
2016-10-25 09:05:13 +00:00
|
|
|
disableAllWdts();
|
2016-08-17 15:08:22 +00:00
|
|
|
panicPutStr("CPU halted.\r\n");
|
|
|
|
while(1);
|
|
|
|
#endif
|
|
|
|
}
|
2016-08-24 04:23:58 +00:00
|
|
|
|
|
|
|
|
2016-10-26 04:23:01 +00:00
|
|
|
void esp_set_breakpoint_if_jtag(void *fn) {
|
2016-08-24 04:23:58 +00:00
|
|
|
if (!inOCDMode()) return;
|
|
|
|
setFirstBreakpoint((uint32_t)fn);
|
|
|
|
}
|