mirror of
https://github.com/espressif/esp-idf.git
synced 2026-05-05 12:25:03 +02:00
add detection of invalid cache access
- fix level 4 interrupt vectors to produce correct backtrace - initialize invalid cache access interrupt on startup - handle invalid cache access in panic handler
This commit is contained in:
committed by
Ivan Grokhotkov
parent
3c6c1e36ec
commit
0b79d07d34
@@ -0,0 +1,99 @@
|
||||
// Copyright 2015-2017 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.
|
||||
|
||||
/*
|
||||
The cache has an interrupt that can be raised as soon as an access to a cached
|
||||
region (flash, psram) is done without the cache being enabled. We use that here
|
||||
to panic the CPU, which from a debugging perspective is better than grabbing bad
|
||||
data from the bus.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_intr.h"
|
||||
#include "esp_attr.h"
|
||||
#include "soc/dport_reg.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
void esp_cache_err_int_init()
|
||||
{
|
||||
uint32_t core_id = xPortGetCoreID();
|
||||
ESP_INTR_DISABLE(ETS_CACHEERR_INUM);
|
||||
|
||||
// We do not register a handler for the interrupt because it is interrupt
|
||||
// level 4 which is not serviceable from C. Instead, xtensa_vectors.S has
|
||||
// a call to the panic handler for
|
||||
// this interrupt.
|
||||
intr_matrix_set(core_id, ETS_CACHE_IA_INTR_SOURCE, ETS_CACHEERR_INUM);
|
||||
|
||||
// Enable invalid cache access interrupt when the cache is disabled.
|
||||
// When the interrupt happens, we can not determine the CPU where the
|
||||
// invalid cache access has occurred. We enable the interrupt to catch
|
||||
// invalid access on both CPUs, but the interrupt is connected to the
|
||||
// CPU which happens to call this function.
|
||||
// For this reason, panic handler backtrace will not be correct if the
|
||||
// interrupt is connected to PRO CPU and invalid access happens on the APP
|
||||
// CPU.
|
||||
|
||||
if (core_id == PRO_CPU_NUM) {
|
||||
SET_PERI_REG_MASK(DPORT_CACHE_IA_INT_EN_REG,
|
||||
DPORT_CACHE_IA_INT_PRO_OPPOSITE |
|
||||
DPORT_CACHE_IA_INT_PRO_DRAM1 |
|
||||
DPORT_CACHE_IA_INT_PRO_DROM0 |
|
||||
DPORT_CACHE_IA_INT_PRO_IROM0 |
|
||||
DPORT_CACHE_IA_INT_PRO_IRAM0 |
|
||||
DPORT_CACHE_IA_INT_PRO_IRAM1);
|
||||
} else {
|
||||
SET_PERI_REG_MASK(DPORT_CACHE_IA_INT_EN_REG,
|
||||
DPORT_CACHE_IA_INT_APP_OPPOSITE |
|
||||
DPORT_CACHE_IA_INT_APP_DRAM1 |
|
||||
DPORT_CACHE_IA_INT_APP_DROM0 |
|
||||
DPORT_CACHE_IA_INT_APP_IROM0 |
|
||||
DPORT_CACHE_IA_INT_APP_IRAM0 |
|
||||
DPORT_CACHE_IA_INT_APP_IRAM1);
|
||||
}
|
||||
ESP_INTR_ENABLE(ETS_CACHEERR_INUM);
|
||||
}
|
||||
|
||||
int IRAM_ATTR esp_cache_err_get_cpuid()
|
||||
{
|
||||
const uint32_t pro_mask =
|
||||
DPORT_PRO_CPU_DISABLED_CACHE_IA_DRAM1 |
|
||||
DPORT_PRO_CPU_DISABLED_CACHE_IA_DROM0 |
|
||||
DPORT_PRO_CPU_DISABLED_CACHE_IA_IROM0 |
|
||||
DPORT_PRO_CPU_DISABLED_CACHE_IA_IRAM0 |
|
||||
DPORT_PRO_CPU_DISABLED_CACHE_IA_IRAM1 |
|
||||
DPORT_APP_CPU_DISABLED_CACHE_IA_OPPOSITE;
|
||||
|
||||
if (GET_PERI_REG_MASK(DPORT_PRO_DCACHE_DBUG3_REG, pro_mask)) {
|
||||
return PRO_CPU_NUM;
|
||||
}
|
||||
|
||||
const uint32_t app_mask =
|
||||
DPORT_APP_CPU_DISABLED_CACHE_IA_DRAM1 |
|
||||
DPORT_APP_CPU_DISABLED_CACHE_IA_DROM0 |
|
||||
DPORT_APP_CPU_DISABLED_CACHE_IA_IROM0 |
|
||||
DPORT_APP_CPU_DISABLED_CACHE_IA_IRAM0 |
|
||||
DPORT_APP_CPU_DISABLED_CACHE_IA_IRAM1 |
|
||||
DPORT_PRO_CPU_DISABLED_CACHE_IA_OPPOSITE;
|
||||
|
||||
if (GET_PERI_REG_MASK(DPORT_APP_DCACHE_DBUG3_REG, app_mask)) {
|
||||
return APP_CPU_NUM;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
@@ -55,6 +55,7 @@
|
||||
#include "esp_int_wdt.h"
|
||||
#include "esp_task_wdt.h"
|
||||
#include "esp_phy_init.h"
|
||||
#include "esp_cache_err_int.h"
|
||||
#include "esp_coexist.h"
|
||||
#include "esp_panic.h"
|
||||
#include "esp_core_dump.h"
|
||||
@@ -228,6 +229,7 @@ void start_cpu0_default(void)
|
||||
#if CONFIG_TASK_WDT
|
||||
esp_task_wdt_init();
|
||||
#endif
|
||||
esp_cache_err_int_init();
|
||||
esp_crosscore_int_init();
|
||||
esp_ipc_init();
|
||||
spi_flash_init();
|
||||
@@ -257,6 +259,7 @@ void start_cpu1_default(void)
|
||||
}
|
||||
//Take care putting stuff here: if asked, FreeRTOS will happily tell you the scheduler
|
||||
//has started, but it isn't active *on this CPU* yet.
|
||||
esp_cache_err_int_init();
|
||||
esp_crosscore_int_init();
|
||||
|
||||
ESP_EARLY_LOGI(TAG, "Starting scheduler on APP CPU.");
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
// Copyright 2015-2017 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.
|
||||
|
||||
|
||||
/**
|
||||
* @brief initialize cache invalid access interrupt
|
||||
*
|
||||
* This function enables cache invalid access interrupt source and connects it
|
||||
* to interrupt input number ETS_CACHEERR_INUM (see soc/soc.h). It is called
|
||||
* from the startup code.
|
||||
*/
|
||||
void esp_cache_err_int_init();
|
||||
|
||||
|
||||
/**
|
||||
* @brief get the CPU which caused cache invalid access interrupt
|
||||
* @return
|
||||
* - PRO_CPU_NUM, if PRO_CPU has caused cache IA interrupt
|
||||
* - APP_CPU_NUM, if APP_CPU has caused cache IA interrupt
|
||||
* - (-1) otherwise
|
||||
*/
|
||||
int esp_cache_err_get_cpuid();
|
||||
@@ -13,7 +13,8 @@ extern "C"
|
||||
#define PANIC_RSN_COPROCEXCEPTION 4
|
||||
#define PANIC_RSN_INTWDT_CPU0 5
|
||||
#define PANIC_RSN_INTWDT_CPU1 6
|
||||
#define PANIC_RSN_MAX 6
|
||||
#define PANIC_RSN_CACHEERR 7
|
||||
#define PANIC_RSN_MAX 7
|
||||
|
||||
|
||||
#ifndef __ASSEMBLER__
|
||||
|
||||
+49
-15
@@ -16,6 +16,7 @@
|
||||
#include <xtensa/config/core.h>
|
||||
|
||||
#include "rom/rtc.h"
|
||||
#include "rom/uart.h"
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
@@ -28,6 +29,7 @@
|
||||
#include "soc/timer_group_struct.h"
|
||||
#include "soc/timer_group_reg.h"
|
||||
#include "soc/cpu.h"
|
||||
#include "soc/rtc.h"
|
||||
|
||||
#include "esp_gdbstub.h"
|
||||
#include "esp_panic.h"
|
||||
@@ -35,6 +37,7 @@
|
||||
#include "esp_err.h"
|
||||
#include "esp_core_dump.h"
|
||||
#include "esp_spi_flash.h"
|
||||
#include "esp_cache_err_int.h"
|
||||
|
||||
/*
|
||||
Panic handlers; these get called when an unhandled exception occurs or the assembly-level
|
||||
@@ -120,7 +123,7 @@ static __attribute__((noreturn)) inline void invoke_abort()
|
||||
void abort()
|
||||
{
|
||||
#if !CONFIG_ESP32_PANIC_SILENT_REBOOT
|
||||
ets_printf("abort() was called at PC 0x%08x\n", (intptr_t)__builtin_return_address(0) - 3);
|
||||
ets_printf("abort() was called at PC 0x%08x on core %d\n", (intptr_t)__builtin_return_address(0) - 3, xPortGetCoreID());
|
||||
#endif
|
||||
invoke_abort();
|
||||
}
|
||||
@@ -149,9 +152,23 @@ static void haltOtherCore()
|
||||
esp_cpu_stall( xPortGetCoreID() == 0 ? 1 : 0 );
|
||||
}
|
||||
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
|
||||
void panicHandler(XtExcFrame *frame)
|
||||
{
|
||||
int *regs = (int *)frame;
|
||||
int core_id = xPortGetCoreID();
|
||||
//Please keep in sync with PANIC_RSN_* defines
|
||||
const char *reasons[] = {
|
||||
"Unknown reason",
|
||||
@@ -161,15 +178,21 @@ void panicHandler(XtExcFrame *frame)
|
||||
"Coprocessor exception",
|
||||
"Interrupt wdt timeout on CPU0",
|
||||
"Interrupt wdt timeout on CPU1",
|
||||
"Cache disabled but cached memory region accessed",
|
||||
};
|
||||
const char *reason = reasons[0];
|
||||
//The panic reason is stored in the EXCCAUSE register.
|
||||
if (regs[20] <= PANIC_RSN_MAX) {
|
||||
reason = reasons[regs[20]];
|
||||
}
|
||||
if (frame->exccause == PANIC_RSN_CACHEERR && esp_cache_err_get_cpuid() != core_id) {
|
||||
// Cache error interrupt will be handled by the panic handler
|
||||
// on the other CPU.
|
||||
return;
|
||||
}
|
||||
haltOtherCore();
|
||||
panicPutStr("Guru Meditation Error: Core ");
|
||||
panicPutDec(xPortGetCoreID());
|
||||
panicPutDec(core_id);
|
||||
panicPutStr(" panic'ed (");
|
||||
if (!abort_called) {
|
||||
panicPutStr(reason);
|
||||
@@ -204,22 +227,12 @@ void panicHandler(XtExcFrame *frame)
|
||||
}
|
||||
|
||||
if (esp_cpu_in_ocd_debug_mode()) {
|
||||
asm("break.n 1");
|
||||
setFirstBreakpoint(regs[1]);
|
||||
return;
|
||||
}
|
||||
commonErrorHandler(frame);
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
void xt_unhandled_exception(XtExcFrame *frame)
|
||||
{
|
||||
int *regs = (int *)frame;
|
||||
@@ -315,6 +328,22 @@ void esp_panic_wdt_stop()
|
||||
WRITE_PERI_REG(RTC_CNTL_WDTWPROTECT_REG, 0);
|
||||
}
|
||||
|
||||
static void esp_panic_dig_reset() __attribute__((noreturn));
|
||||
|
||||
static void esp_panic_dig_reset()
|
||||
{
|
||||
// make sure all the panic handler output is sent from UART FIFO
|
||||
uart_tx_wait_idle(CONFIG_CONSOLE_UART_NUM);
|
||||
// switch to XTAL (otherwise we will keep running from the PLL)
|
||||
rtc_clk_cpu_freq_set(RTC_CPU_FREQ_XTAL);
|
||||
// reset the digital part
|
||||
esp_cpu_unstall(PRO_CPU_NUM);
|
||||
SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_SW_SYS_RST);
|
||||
while (true) {
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool stackPointerIsSane(uint32_t sp)
|
||||
{
|
||||
return !(sp < 0x3ffae010 || sp > 0x3ffffff0 || ((sp & 0xf) != 0));
|
||||
@@ -416,7 +445,12 @@ static void commonErrorHandler(XtExcFrame *frame)
|
||||
esp_panic_wdt_stop();
|
||||
#if CONFIG_ESP32_PANIC_PRINT_REBOOT || CONFIG_ESP32_PANIC_SILENT_REBOOT
|
||||
panicPutStr("Rebooting...\r\n");
|
||||
esp_restart_noos();
|
||||
if (frame->exccause != PANIC_RSN_CACHEERR) {
|
||||
esp_restart_noos();
|
||||
} else {
|
||||
// The only way to clear invalid cache access interrupt is to reset the digital part
|
||||
esp_panic_dig_reset();
|
||||
}
|
||||
#else
|
||||
disableAllWdts();
|
||||
panicPutStr("CPU halted.\r\n");
|
||||
|
||||
Reference in New Issue
Block a user