From 026c0787e91bd1a4bd2a4dd9a9ea1e69911b02a5 Mon Sep 17 00:00:00 2001 From: M Hightower <27247790+mhightower83@users.noreply.github.com> Date: Wed, 11 Nov 2020 22:19:55 -0800 Subject: [PATCH 1/9] Handle breakpoints via postmortem when GDB is not running. In the absence of GDB, install a small breakpoint handler that clears EXCM and executes an illegal instruction. This leverages the existing exception handling code in the SDK for `ill` instruction. At postmortem processing, the real event is identified and reported. This allows for alerting of embedded breakpoints. In the stack trace, `epc1` is updated with the value of `epc2` so that the "ESP Exception Decoder" will identify the line where the BP occurred. The SDKs default behavior was to loop on breakpoints waiting for interrupts >2 until the HWDT kicks in. The current "C++" compiler will embed breakpoints when it detects a divide by zero at compile time. Expanded technique to preserve more of the current state and to also report Kernel and Double exceptions through postmortem. --- cores/esp8266/core_esp8266_main.cpp | 5 + cores/esp8266/core_esp8266_postmortem.cpp | 285 +++++++++++++++++++++- 2 files changed, 285 insertions(+), 5 deletions(-) diff --git a/cores/esp8266/core_esp8266_main.cpp b/cores/esp8266/core_esp8266_main.cpp index 21724e2eb4..8b0e7f0afa 100644 --- a/cores/esp8266/core_esp8266_main.cpp +++ b/cores/esp8266/core_esp8266_main.cpp @@ -327,6 +327,7 @@ extern "C" void preinit (void) { /* do nothing by default */ } +extern "C" void postmortem_init(void); extern "C" void user_init(void) { struct rst_info *rtc_info_ptr = system_get_rst_info(); @@ -342,6 +343,10 @@ extern "C" void user_init(void) { cont_init(g_pcont); +#if defined(DEBUG_ESP_PORT) || defined(DEBUG_ESP_EXCEPTIONS) + postmortem_init(); +#endif + preinit(); // Prior to C++ Dynamic Init (not related to above init() ). Meant to be user redefinable. ets_task(loop_task, diff --git a/cores/esp8266/core_esp8266_postmortem.cpp b/cores/esp8266/core_esp8266_postmortem.cpp index 3e75342d05..47887acb04 100644 --- a/cores/esp8266/core_esp8266_postmortem.cpp +++ b/cores/esp8266/core_esp8266_postmortem.cpp @@ -33,9 +33,32 @@ #include "gdb_hooks.h" #include "StackThunk.h" #include "coredecls.h" +#include "esp8266_undocumented.h" +#include "core_esp8266_features.h" extern "C" { +#if defined(DEBUG_ESP_PORT) || defined(DEBUG_ESP_EXCEPTIONS) +extern void _DebugExceptionVector(void); +extern void _KernelExceptionVector(void); +extern void _DoubleExceptionVector(void); + +// Context structure used by Debug, Double, Kernel Exception and unhandled +// exception stubs to build a stack frame with potentially useful addresses for +// EXP Exception Decoder's use. Also used to save some special registers +// that were overwritten by using the `ill` instruction. +struct EXC_CONTEXT{ + uint32_t ps; // +0 + uint32_t pad1; // +4 + uint32_t pad2; // +8 + uint32_t excsave2; // +12 + uint32_t exccause; // +16 + uint32_t epc1; // +20 + uint32_t a1; // +24 + uint32_t excsave1; // +28, a0 at the time of the event +}; +#endif + // These will be pointers to PROGMEM const strings static const char* s_panic_file = 0; static int s_panic_line = 0; @@ -128,6 +151,67 @@ void __wrap_system_restart_local() { ets_install_putc1(&uart_write_char_d); + /* + Check for Exceptions mapped into User Exception. + */ + if (rst_info.reason == REASON_EXCEPTION_RST && rst_info.exccause == 0) { +#if defined(DEBUG_ESP_PORT) || defined(DEBUG_ESP_EXCEPTIONS) + if ((rst_info.epc1 - 11u) == (uint32_t)_DoubleExceptionVector) { + struct EXC_CONTEXT *exc_context; + asm volatile("rsr.excsave2 %0\n\t" : "=a" (exc_context) :: "memory"); + // Use Special Register values from before the `ill` instruction. + // Note, for a double exception the exccause is that of the second exception. + // epc1 is still the address of the 1st exception, its cause is now unknown. + rst_info.exccause = exc_context->exccause; + rst_info.epc1 = exc_context->epc1; + // Double Exception + if (rst_info.depc) { + ets_printf_P(PSTR("\nDouble Exception")); + // The current "ESP Exception Decoder" does not process the value in depc + // Set a fake epc1 so the decoder identifies the line of the Double Exception + rst_info.epc1 = rst_info.depc; + // The "ESP Exception Decoder" is also helped by updating the + // context saved on the stack. + exc_context->epc1 = rst_info.depc; + } + // BP - Debug Exception + else if (rst_info.epc2) { + if (rst_info.epc2 == 0x4000dc4bu) { + // Unhandled Exceptions land here. 'break 1, 1' in _xtos_unhandled_exception + if (20u == exc_context->exccause) { + // This could be the result from a jump or call; a call + // is more likely. eg. calling a null callback function + // For the call case, a0 is likely the return address. + rst_info.epc1 = exc_context->excsave1; + } + ets_printf_P(PSTR("\nXTOS Unhandled exception - BP")); + } + else if (rst_info.epc2 == 0x4000dc3cu) { + // Unhandled interrupts land here. 'break 1, 15' in _xtos_unhandled_interrupt + ets_printf_P(PSTR("\nXTOS Unhandled interrupt - BP")); + // Don't know what should be done here if anything. + } + else { + ets_printf_P(PSTR("\nDebug Exception")); + // The current Exception Decoder does not process the value in epc2 + // Set a fake epc1 so the Exception Decoder identifies the correct BP line + rst_info.epc1 = rst_info.epc2; + } + } + // Kernel Exception + else { + ets_printf_P(PSTR("\nKernel Exception")); + } + ets_printf_P(PSTR(" - excsave1=0x%08x excsave2=0x%08x epc1=0x%08x\n"), + exc_context->excsave1, exc_context->excsave2, exc_context->epc1 ); + } +#endif + // The GCC divide routine in ROM jumps to the address below and executes ILL (00 00 00) on div-by-zero + // In that case, print the exception as (6) which is IntegerDivZero + if (rst_info.epc1 == 0x4000dce5u) { + rst_info.exccause = 6; + } + } cut_here(); if (s_panic_line) { @@ -144,11 +228,8 @@ void __wrap_system_restart_local() { ets_printf_P(PSTR("\nAbort called\n")); } else if (rst_info.reason == REASON_EXCEPTION_RST) { - // The GCC divide routine in ROM jumps to the address below and executes ILL (00 00 00) on div-by-zero - // In that case, print the exception as (6) which is IntegerDivZero - bool div_zero = (rst_info.exccause == 0) && (rst_info.epc1 == 0x4000dce5); ets_printf_P(PSTR("\nException (%d):\nepc1=0x%08x epc2=0x%08x epc3=0x%08x excvaddr=0x%08x depc=0x%08x\n"), - div_zero ? 6 : rst_info.exccause, rst_info.epc1, rst_info.epc2, rst_info.epc3, rst_info.excvaddr, rst_info.depc); + rst_info.exccause, rst_info.epc1, rst_info.epc2, rst_info.epc3, rst_info.excvaddr, rst_info.depc); } else if (rst_info.reason == REASON_SOFT_WDT_RST) { ets_printf_P(PSTR("\nSoft WDT reset\n")); @@ -157,7 +238,7 @@ void __wrap_system_restart_local() { ets_printf_P(PSTR("\nStack overflow detected.\n")); ets_printf_P(PSTR("\nException (%d):\nepc1=0x%08x epc2=0x%08x epc3=0x%08x excvaddr=0x%08x depc=0x%08x\n"), 5 /* Alloca exception, closest thing to stack fault*/, s_stacksmash_addr, 0, 0, 0, 0); - } + } else { ets_printf_P(PSTR("\nGeneric Reset\n")); } @@ -322,5 +403,199 @@ void __stack_chk_fail(void) { while (1); // never reached, needed to satisfy "noreturn" attribute } +#if defined(DEBUG_ESP_PORT) || defined(DEBUG_ESP_EXCEPTIONS) +/* + Handle breakpoints via postmortem when GDB is not running. + + Objective: Add support for Debug, Kernel, and Double Exception reporting + with minimum impact on IRAM usage. Additionally, improve reporting of + Unhandled Exception causes for the disabled interrupt case. + + Overview: In the absence of GDB, install a small breakpoint handler that + clears EXCM and executes an illegal instruction. This leverages the existing + exception handling code in the SDK for the `ill` instruction. At postmortem + processing, identify the event and report. This allows for alerting of + embedded breakpoints. In the stack trace, EPC1 is updated with the value of + EPC2. This helps "Exception Decoder" identify the line where the BP + occurred. By using the SDK's handling for `ill`, we get support for handling + the process of getting out of the Cache_Read_Disable state free, without + consuming IRAM. + + For breakpoints (BP), the SDK's default behavior is to loop, waiting for an + interrupt >2 until the HWDT kicks in. The current "C++" compiler will embed + breakpoints when it detects a divide by zero at compile time. Are there + other situations where the compiler does this? + + Expand technique to preserve more of the current context and to also report + Kernel and Double exceptions through postmortem. As well as enhanced + reporting for XTOS Unhandled Exceptions. + + To avoid having to deal with literals and to minimize the use of IRAM, some + jumping around is done to use pockets of available IRAM in each vector + handler's allotted space. Regrettably, the relative jumps needed to ties + these pockets of memory together were computed by hand. + + To assist the "ESP Exception Decoder", create a stack frame to store + various special registers for inspection. The expectation is that some of + these values may point to code. This can give insight into what was running + at the time of the crash. +*/ + +// +// Stage Exception Vector Stubs to be copied into respective vector address +// space in ICACHE/PROGMEM address space. +// +void postmortem_debug_exception_vector(void); +asm( // 16 bytes MAX, using 16 + ".section .text.postmortem_debug_exception_vector,\"ax\",@progbits\n\t" + ".align 4\n\t" + ".type postmortem_debug_exception_vector, @function\n\t" + "\n" +"postmortem_debug_exception_vector:\n\t" // Copy destination _DebugExceptionVector + "wsr.excsave2 a0\n\t" + "j . + 32\n\t" // _KernelExceptionVector + 3 + + // continue here from _KernelExceptionVector +"postmortem_debug_exception_vector_part2:\n\t" + "s32i a0, a1, 16\n\t" + "rsr.ps a0\n\t" + "s32i a0, a1, 0\n\t" + "j . + 86\n\t" // finish with postmortem_double_exception_handler_part2 + ".size postmortem_debug_exception_vector, .-postmortem_debug_exception_vector\n\t" +); + +void postmortem_kernel_exception_handler(void); +asm( // 32 bytes MAX, using 31 + ".section .text.postmortem_kernel_exception_handler,\"ax\",@progbits\n\t" + ".align 4\n\t" + ".type postmortem_kernel_exception_handler, @function\n\t" + "\n" +"postmortem_kernel_exception_handler:\n\t" // Copy destination _KernelExceptionVector + "wsr.excsave1 a0\n\t" + +"postmortem_kernel_exception_handler_p3:\n\t" + "addi a0, a1, -32\n\t" + "s32i a1, a0, 24\n\t" + "mov.n a1, a0\n\t" + "xsr.excsave2 a0\n\t" // stash pointer to stack data in excsave2 + "s32i a0, a1, 12\n\t" // and save previous + "rsr.excsave1 a0\n\t" + "s32i a0, a1, 28\n\t" + "rsr.epc1 a0\n\t" + "s32i a0, a1, 20\n\t" + // save current exccause + "rsr.exccause a0\n\t" + "j . - 54\n\t" // continue at postmortem_debug_exception_vector_part2 + + ".size postmortem_kernel_exception_handler, .-postmortem_kernel_exception_handler\n\t" +); + +void postmortem_double_exception_handler(void); +asm( // 16 byte MAX, using 14 + ".section .text.postmortem_double_exception_handler,\"ax\",@progbits\n\t" + ".align 4\n\t" + ".type postmortem_double_exception_handler, @function\n\t" + "\n" +"postmortem_double_exception_handler:\n\t" // Copy destination _DoubleExceptionVector + // Common code at _KernelExceptionVector, which is -64 bytes from + // _DoubleExceptionVector. + "j . - 64\n\t" // continue at _KernelExceptionVector + + // finish here from _DebugExceptionVector part 2 + // _DoubleExceptionVector part 2 +"postmortem_double_exception_handler_part2:\n\t" + // User mode, Clear EXCM, and block all interrupts + "movi.n a0, 0x02F\n\t" + "wsr.ps a0\n\t" + "rsync\n\t" + // Let _UserExceptionVector finish processing the exception as an + // exception(0). Adjust results at __wrap_system_restart_local so that `ESP + // Exception Decoder` reports the state before this `ill` instruction. + "ill\n\t" + ".size postmortem_double_exception_handler, .-postmortem_double_exception_handler\n\t" +); + +/* + Of the Exception Cause table's 64 entries, most are set to + `_xtos_unhandled_exception`. And I assume most of these never occur; however, + at lesat one does exccause 20. This one occurs on a call to a NULL pointer. This + can happen when a function prototype is given the weak attribute but never + defined, or forgetting to test a call back function pointer for NULL before + calling, etc. + + A problem with the `_xtos_unhandled_exception` function is that it handles the + event with a Debug Exception BP. If you don't have GDB running you see a HWDT + reset. If interrupts are at INTLEVEL 2 or higher (a BP instruction becomes a + NOP), you still see a HWDT reset regardless of running GDB. +*/ +typedef void (* _xtos_handler)(void); +static _xtos_handler * const _xtos_exc_handler_table = (_xtos_handler *)0x3FFFC000u; +#define ROM_xtos_unhandled_exception (reinterpret_cast<_xtos_handler>(0x4000dc44)) + +void postmortem_xtos_unhandled_exception(void); +asm( + ".section .iram.text.postmortem_xtos_unhandled_exception,\"ax\",@progbits\n\t" + ".literal_position\n\t" + ".align 4\n\t" + ".type postmortem_xtos_unhandled_exception, @function\n\t" + "\n" +"postmortem_xtos_unhandled_exception:\n\t" + // Rewind Boot ROM User Exception prologue and continue to + // _KernelExceptionVector such that we look a Debug BP Exception sitting at + // 'break 1, 1' in the _xtos_unhandled_exception handler. + "movi a2, 0x4000dc4bu\n\t" + "wsr.epc2 a2\n\t" + "l32i a2, a1, 20\n\t" + "l32i a3, a1, 24\n\t" + "addmi a1, a1, 0x100\n\t" + "j _KernelExceptionVector\n\t" + ".size postmortem_xtos_unhandled_exception, .-postmortem_xtos_unhandled_exception\n\t" +); + + +static void replace_exception_handler_on_match( + uint32_t cause, + _xtos_handler match, + _xtos_handler replacement) { + + _xtos_handler old_wrapper = _xtos_exc_handler_table[cause]; + if (old_wrapper == match || NULL == match) { + _xtos_exc_handler_table[cause] = replacement; + } +} + +static void install_unhandled_exception_handler(void) { + // Only replace Exception Table entries still using the orignal Boot ROM + // _xtos_unhandled_exception handler. + for (size_t i = 0; i < 64; i++) { + replace_exception_handler_on_match( + i, + ROM_xtos_unhandled_exception, + (_xtos_handler)postmortem_xtos_unhandled_exception); + } +} + +void postmortem_init(void) { + if (!gdb_present()) { + // Install all three (interdependent) exception handler stubs. + // Length of copy must be rounded up to copy memory in aligned 4 byte + // increments to comply with IRAM memory access requirements. + uint32_t save_ps = xt_rsil(15); + ets_memcpy((void*)_DebugExceptionVector, (void*)postmortem_debug_exception_vector, 16); + ets_memcpy((void*)_KernelExceptionVector, (void*)postmortem_kernel_exception_handler, 32); + ets_memcpy((void*)_DoubleExceptionVector, (void*)postmortem_double_exception_handler, 16); + asm volatile( + "movi.n a2, 0\n\t" + "wsr.epc2 a2\n\t" + "wsr.excsave2 a2\n\t" + "wsr.depc a2\n\t" + ::: "a2" + ); + install_unhandled_exception_handler(); + xt_wsr_ps(save_ps); + } +} + +#endif }; From fb3fe9dc2abe70c27561ac2f2d0d5981fc66246d Mon Sep 17 00:00:00 2001 From: M Hightower <27247790+mhightower83@users.noreply.github.com> Date: Fri, 13 Nov 2020 12:39:06 -0800 Subject: [PATCH 2/9] comment corrections --- cores/esp8266/core_esp8266_postmortem.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cores/esp8266/core_esp8266_postmortem.cpp b/cores/esp8266/core_esp8266_postmortem.cpp index 47887acb04..47fc428958 100644 --- a/cores/esp8266/core_esp8266_postmortem.cpp +++ b/cores/esp8266/core_esp8266_postmortem.cpp @@ -160,7 +160,7 @@ void __wrap_system_restart_local() { struct EXC_CONTEXT *exc_context; asm volatile("rsr.excsave2 %0\n\t" : "=a" (exc_context) :: "memory"); // Use Special Register values from before the `ill` instruction. - // Note, for a double exception the exccause is that of the second exception. + // Note, for a double exception, the exccause is that of the second exception. // epc1 is still the address of the 1st exception, its cause is now unknown. rst_info.exccause = exc_context->exccause; rst_info.epc1 = exc_context->epc1; @@ -541,8 +541,8 @@ asm( "\n" "postmortem_xtos_unhandled_exception:\n\t" // Rewind Boot ROM User Exception prologue and continue to - // _KernelExceptionVector such that we look a Debug BP Exception sitting at - // 'break 1, 1' in the _xtos_unhandled_exception handler. + // _KernelExceptionVector such that we look like a Debug BP Exception + // sitting at 'break 1, 1' in the _xtos_unhandled_exception handler. "movi a2, 0x4000dc4bu\n\t" "wsr.epc2 a2\n\t" "l32i a2, a1, 20\n\t" From c58baaa0f3e67cb2d91d01bf76512a4e5b8b6b4a Mon Sep 17 00:00:00 2001 From: M Hightower <27247790+mhightower83@users.noreply.github.com> Date: Sun, 15 Nov 2020 18:18:55 -0800 Subject: [PATCH 3/9] Reviewer requested changes. Additional - moved postmortem_init to run after preinit. This allows a user replacement exception handler to be installed before postmortem_init which will replace the old exception handlers. --- cores/esp8266/core_esp8266_main.cpp | 4 +- cores/esp8266/core_esp8266_postmortem.cpp | 142 ++++++++++++++++++---- 2 files changed, 117 insertions(+), 29 deletions(-) diff --git a/cores/esp8266/core_esp8266_main.cpp b/cores/esp8266/core_esp8266_main.cpp index 8b0e7f0afa..a0b7604a2d 100644 --- a/cores/esp8266/core_esp8266_main.cpp +++ b/cores/esp8266/core_esp8266_main.cpp @@ -343,12 +343,12 @@ extern "C" void user_init(void) { cont_init(g_pcont); + preinit(); // Prior to C++ Dynamic Init (not related to above init() ). Meant to be user redefinable. + #if defined(DEBUG_ESP_PORT) || defined(DEBUG_ESP_EXCEPTIONS) postmortem_init(); #endif - preinit(); // Prior to C++ Dynamic Init (not related to above init() ). Meant to be user redefinable. - ets_task(loop_task, LOOP_TASK_PRIORITY, s_loop_queue, LOOP_QUEUE_SIZE); diff --git a/cores/esp8266/core_esp8266_postmortem.cpp b/cores/esp8266/core_esp8266_postmortem.cpp index 47fc428958..a2a92a46db 100644 --- a/cores/esp8266/core_esp8266_postmortem.cpp +++ b/cores/esp8266/core_esp8266_postmortem.cpp @@ -39,10 +39,79 @@ extern "C" { #if defined(DEBUG_ESP_PORT) || defined(DEBUG_ESP_EXCEPTIONS) +#define ALIGN_UP(a, s) ((decltype(a))((((uintptr_t)(a)) + (s-1)) & ~(s-1))) + +// Famous locations in Boot ROM +constexpr uint32_t _xtos_unhandled_exception__bp_address = 0x4000DC4Bu; +constexpr uint32_t _xtos_unhandled_interrupt__bp_address = 0x4000DC3Cu; + +/* + The Boot ROM `__divsi3` function handles a divide by 0 by branching to the + `ill` instruction at address 0x4000dce5. By looking for this address in epc1 + we can separate the divide by zero event from other `ill` instruction events. +*/ +constexpr uint32_t divide_by_0_exception = 0x4000dce5u; + +/* + For exceptions that are directed to one of the general exception vectors + (UserExceptionVector, KernelExceptionVector, or DoubleExceptionVector) there + are 64 possible causes. The exception cause register EXCCAUSE has the value. + The value is in the lower 6 bits (5 ... 0). Other bits in the register are + reserved and may need to be truncated before use. +*/ +constexpr size_t max_num_exccause_values = 64u; + +/* + The Boot ROM sets up a table of dispatch handlers at 0x3FFFC000. + This table has an entry for each of the EXCCAUSE values, 0 through 63. + + Entries that do not have a specific handler are set to + `_xtos_unhandled_exception`. This handler will execute a `break 1, 1` + (0x4000DC4Bu) before doing a `rfe` (return from exception). Since the PC has + not been changed, the event that caused the 1st exception will likely keep + repeating until the HWDT kicks in. + + This table is normally managed through calls to _xtos_set_exception_handler() +*/ +using _xtos_handler = void (*)(void); +constexpr uint32_t exception_dispatch_table = 0x3FFFC000u; +static _xtos_handler * const _xtos_exc_handler_table = (_xtos_handler *)exception_dispatch_table; + +/* + Xtensa ® Instruction Set Architecture (ISA) Reference Manual, p90: + EXCCAUSE value 20, InstFetchProhibitedCause - An instruction fetch referenced + a page mapped with an attribute that does not permit instruction fetch. +*/ +constexpr uint32_t instFetchProhibitedCause = 20u; + extern void _DebugExceptionVector(void); extern void _KernelExceptionVector(void); extern void _DoubleExceptionVector(void); +void postmortem_debug_exception_vector(void); +void postmortem_kernel_exception_handler(void); +void postmortem_double_exception_handler(void); + +/* + I cannot find official documentation for the size and offset addresse for + the exception vectors. These values were inferred from one of the Boot ROM + listings out on the Internet. +*/ +constexpr size_t max_debug_exc_vector_size = 16u; +constexpr size_t max_kernel_exc_vector_size = 32u; +constexpr size_t max_double_exc_vector_size = 16u; + +// These function entry points are for computation ONLY they are NOT aligned as functions +extern void *postmortem_debug_exception_vector_last; +extern void *postmortem_kernel_exception_handler_last; +extern void *postmortem_double_exception_handler_last; +extern void *postmortem_double_exception_handler_ill; + +// Calculate the location address of where the `ill` instruction that is +// used/leveraged to flag exceptions: Debug, Kerne, and Double Exceptions. +// See "Handle breakpoints via postmortem ..." below for more details. +const uint32_t bp_ill_address = (uintptr_t)_DoubleExceptionVector + ((uintptr_t)&postmortem_double_exception_handler_ill - (uintptr_t)postmortem_double_exception_handler); + // Context structure used by Debug, Double, Kernel Exception and unhandled // exception stubs to build a stack frame with potentially useful addresses for // EXP Exception Decoder's use. Also used to save some special registers @@ -57,6 +126,7 @@ struct EXC_CONTEXT{ uint32_t a1; // +24 uint32_t excsave1; // +28, a0 at the time of the event }; + #endif // These will be pointers to PROGMEM const strings @@ -156,7 +226,7 @@ void __wrap_system_restart_local() { */ if (rst_info.reason == REASON_EXCEPTION_RST && rst_info.exccause == 0) { #if defined(DEBUG_ESP_PORT) || defined(DEBUG_ESP_EXCEPTIONS) - if ((rst_info.epc1 - 11u) == (uint32_t)_DoubleExceptionVector) { + if (rst_info.epc1 == bp_ill_address) { struct EXC_CONTEXT *exc_context; asm volatile("rsr.excsave2 %0\n\t" : "=a" (exc_context) :: "memory"); // Use Special Register values from before the `ill` instruction. @@ -176,9 +246,9 @@ void __wrap_system_restart_local() { } // BP - Debug Exception else if (rst_info.epc2) { - if (rst_info.epc2 == 0x4000dc4bu) { + if (rst_info.epc2 == _xtos_unhandled_exception__bp_address) { // Unhandled Exceptions land here. 'break 1, 1' in _xtos_unhandled_exception - if (20u == exc_context->exccause) { + if (instFetchProhibitedCause == exc_context->exccause) { // This could be the result from a jump or call; a call // is more likely. eg. calling a null callback function // For the call case, a0 is likely the return address. @@ -186,7 +256,7 @@ void __wrap_system_restart_local() { } ets_printf_P(PSTR("\nXTOS Unhandled exception - BP")); } - else if (rst_info.epc2 == 0x4000dc3cu) { + else if (rst_info.epc2 == _xtos_unhandled_interrupt__bp_address) { // Unhandled interrupts land here. 'break 1, 15' in _xtos_unhandled_interrupt ets_printf_P(PSTR("\nXTOS Unhandled interrupt - BP")); // Don't know what should be done here if anything. @@ -208,7 +278,7 @@ void __wrap_system_restart_local() { #endif // The GCC divide routine in ROM jumps to the address below and executes ILL (00 00 00) on div-by-zero // In that case, print the exception as (6) which is IntegerDivZero - if (rst_info.epc1 == 0x4000dce5u) { + if (rst_info.epc1 == divide_by_0_exception) { rst_info.exccause = 6; } } @@ -445,7 +515,6 @@ void __stack_chk_fail(void) { // Stage Exception Vector Stubs to be copied into respective vector address // space in ICACHE/PROGMEM address space. // -void postmortem_debug_exception_vector(void); asm( // 16 bytes MAX, using 16 ".section .text.postmortem_debug_exception_vector,\"ax\",@progbits\n\t" ".align 4\n\t" @@ -461,10 +530,10 @@ asm( // 16 bytes MAX, using 16 "rsr.ps a0\n\t" "s32i a0, a1, 0\n\t" "j . + 86\n\t" // finish with postmortem_double_exception_handler_part2 +"postmortem_debug_exception_vector_last:\n\t" ".size postmortem_debug_exception_vector, .-postmortem_debug_exception_vector\n\t" ); -void postmortem_kernel_exception_handler(void); asm( // 32 bytes MAX, using 31 ".section .text.postmortem_kernel_exception_handler,\"ax\",@progbits\n\t" ".align 4\n\t" @@ -486,11 +555,10 @@ asm( // 32 bytes MAX, using 31 // save current exccause "rsr.exccause a0\n\t" "j . - 54\n\t" // continue at postmortem_debug_exception_vector_part2 - +"postmortem_kernel_exception_handler_last:\n\t" ".size postmortem_kernel_exception_handler, .-postmortem_kernel_exception_handler\n\t" ); -void postmortem_double_exception_handler(void); asm( // 16 byte MAX, using 14 ".section .text.postmortem_double_exception_handler,\"ax\",@progbits\n\t" ".align 4\n\t" @@ -511,7 +579,9 @@ asm( // 16 byte MAX, using 14 // Let _UserExceptionVector finish processing the exception as an // exception(0). Adjust results at __wrap_system_restart_local so that `ESP // Exception Decoder` reports the state before this `ill` instruction. +"postmortem_double_exception_handler_ill:\n\t" "ill\n\t" +"postmortem_double_exception_handler_last:\n\t" ".size postmortem_double_exception_handler, .-postmortem_double_exception_handler\n\t" ); @@ -528,9 +598,6 @@ asm( // 16 byte MAX, using 14 reset. If interrupts are at INTLEVEL 2 or higher (a BP instruction becomes a NOP), you still see a HWDT reset regardless of running GDB. */ -typedef void (* _xtos_handler)(void); -static _xtos_handler * const _xtos_exc_handler_table = (_xtos_handler *)0x3FFFC000u; -#define ROM_xtos_unhandled_exception (reinterpret_cast<_xtos_handler>(0x4000dc44)) void postmortem_xtos_unhandled_exception(void); asm( @@ -543,7 +610,7 @@ asm( // Rewind Boot ROM User Exception prologue and continue to // _KernelExceptionVector such that we look like a Debug BP Exception // sitting at 'break 1, 1' in the _xtos_unhandled_exception handler. - "movi a2, 0x4000dc4bu\n\t" + "movi a2, 0x4000dc4bu\n\t" // _xtos_unhandled_exception__bp_address "wsr.epc2 a2\n\t" "l32i a2, a1, 20\n\t" "l32i a3, a1, 24\n\t" @@ -564,10 +631,15 @@ static void replace_exception_handler_on_match( } } +// While _xtos_unhandled_exception is in the linker .ld file, it may have been +// overridden. We require the original Boot ROM function address to limit our +// override to those old values in the table. +const _xtos_handler ROM_xtos_unhandled_exception = (reinterpret_cast<_xtos_handler>(0x4000dc44)); + static void install_unhandled_exception_handler(void) { // Only replace Exception Table entries still using the orignal Boot ROM // _xtos_unhandled_exception handler. - for (size_t i = 0; i < 64; i++) { + for (size_t i = 0; i < max_num_exccause_values; i++) { replace_exception_handler_on_match( i, ROM_xtos_unhandled_exception, @@ -580,19 +652,35 @@ void postmortem_init(void) { // Install all three (interdependent) exception handler stubs. // Length of copy must be rounded up to copy memory in aligned 4 byte // increments to comply with IRAM memory access requirements. - uint32_t save_ps = xt_rsil(15); - ets_memcpy((void*)_DebugExceptionVector, (void*)postmortem_debug_exception_vector, 16); - ets_memcpy((void*)_KernelExceptionVector, (void*)postmortem_kernel_exception_handler, 32); - ets_memcpy((void*)_DoubleExceptionVector, (void*)postmortem_double_exception_handler, 16); - asm volatile( - "movi.n a2, 0\n\t" - "wsr.epc2 a2\n\t" - "wsr.excsave2 a2\n\t" - "wsr.depc a2\n\t" - ::: "a2" - ); - install_unhandled_exception_handler(); - xt_wsr_ps(save_ps); + const size_t debug_vector_sz = ALIGN_UP((uintptr_t)&postmortem_debug_exception_vector_last - (uintptr_t)postmortem_debug_exception_vector, 4); + const size_t kernel_vector_sz = ALIGN_UP((uintptr_t)&postmortem_kernel_exception_handler_last - (uintptr_t)postmortem_kernel_exception_handler, 4); + const size_t double_vector_sz = ALIGN_UP((uintptr_t)&postmortem_double_exception_handler_last - (uintptr_t)postmortem_double_exception_handler, 4); +#ifdef DEBUG_POSTMORTEM_EXCEPTION_VERIFY_SIZES + if (max_debug_exc_vector_size < debug_vector_sz || + max_kernel_exc_vector_size < kernel_vector_sz || + max_double_exc_vector_size < double_vector_sz) + { + ets_printf_P(PSTR("\npostmortem_init: Internal Error: exception vector size(s) too big\n")); + ets_printf_P(PSTR(" debug_vector_sz(%u), kernel_vector_sz(%u), double_vector_sz(%u)\n"), + debug_vector_sz, kernel_vector_sz, double_vector_sz); + } else +#endif + { + + uint32_t save_ps = xt_rsil(15); + ets_memcpy((void*)_DebugExceptionVector, (void*)postmortem_debug_exception_vector, debug_vector_sz); + ets_memcpy((void*)_KernelExceptionVector, (void*)postmortem_kernel_exception_handler, kernel_vector_sz); + ets_memcpy((void*)_DoubleExceptionVector, (void*)postmortem_double_exception_handler, double_vector_sz); + asm volatile( + "movi.n a2, 0\n\t" + "wsr.epc2 a2\n\t" + "wsr.excsave2 a2\n\t" + "wsr.depc a2\n\t" + ::: "a2" + ); + install_unhandled_exception_handler(); + xt_wsr_ps(save_ps); + } } } From dd80b84af9456397c534cfc3a603cad536314b26 Mon Sep 17 00:00:00 2001 From: M Hightower <27247790+mhightower83@users.noreply.github.com> Date: Sun, 15 Nov 2020 22:23:00 -0800 Subject: [PATCH 4/9] Corrected build error. --- cores/esp8266/core_esp8266_postmortem.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/cores/esp8266/core_esp8266_postmortem.cpp b/cores/esp8266/core_esp8266_postmortem.cpp index a2a92a46db..c1fc6573b9 100644 --- a/cores/esp8266/core_esp8266_postmortem.cpp +++ b/cores/esp8266/core_esp8266_postmortem.cpp @@ -38,13 +38,6 @@ extern "C" { -#if defined(DEBUG_ESP_PORT) || defined(DEBUG_ESP_EXCEPTIONS) -#define ALIGN_UP(a, s) ((decltype(a))((((uintptr_t)(a)) + (s-1)) & ~(s-1))) - -// Famous locations in Boot ROM -constexpr uint32_t _xtos_unhandled_exception__bp_address = 0x4000DC4Bu; -constexpr uint32_t _xtos_unhandled_interrupt__bp_address = 0x4000DC3Cu; - /* The Boot ROM `__divsi3` function handles a divide by 0 by branching to the `ill` instruction at address 0x4000dce5. By looking for this address in epc1 @@ -52,6 +45,13 @@ constexpr uint32_t _xtos_unhandled_interrupt__bp_address = 0x4000DC3Cu; */ constexpr uint32_t divide_by_0_exception = 0x4000dce5u; +#if defined(DEBUG_ESP_PORT) || defined(DEBUG_ESP_EXCEPTIONS) +#define ALIGN_UP(a, s) ((decltype(a))((((uintptr_t)(a)) + (s-1)) & ~(s-1))) + +// Famous locations in Boot ROM +constexpr uint32_t _xtos_unhandled_exception__bp_address = 0x4000DC4Bu; +constexpr uint32_t _xtos_unhandled_interrupt__bp_address = 0x4000DC3Cu; + /* For exceptions that are directed to one of the general exception vectors (UserExceptionVector, KernelExceptionVector, or DoubleExceptionVector) there From c8d0da1d4d8c0e51299508c99c9457b17e8d0560 Mon Sep 17 00:00:00 2001 From: M Hightower <27247790+mhightower83@users.noreply.github.com> Date: Thu, 19 Nov 2020 19:48:56 -0800 Subject: [PATCH 5/9] Moved definitions around to aid in future merges where _xtos_exc_handler_table is referenced. --- cores/esp8266/core_esp8266_postmortem.cpp | 18 +--------------- cores/esp8266/esp8266_undocumented.h | 25 +++++++++++++++++++++++ tools/sdk/ld/eagle.rom.addr.v6.ld | 4 ++++ 3 files changed, 30 insertions(+), 17 deletions(-) diff --git a/cores/esp8266/core_esp8266_postmortem.cpp b/cores/esp8266/core_esp8266_postmortem.cpp index c1fc6573b9..27f7c0f883 100644 --- a/cores/esp8266/core_esp8266_postmortem.cpp +++ b/cores/esp8266/core_esp8266_postmortem.cpp @@ -62,23 +62,7 @@ constexpr uint32_t _xtos_unhandled_interrupt__bp_address = 0x4000DC3Cu; constexpr size_t max_num_exccause_values = 64u; /* - The Boot ROM sets up a table of dispatch handlers at 0x3FFFC000. - This table has an entry for each of the EXCCAUSE values, 0 through 63. - - Entries that do not have a specific handler are set to - `_xtos_unhandled_exception`. This handler will execute a `break 1, 1` - (0x4000DC4Bu) before doing a `rfe` (return from exception). Since the PC has - not been changed, the event that caused the 1st exception will likely keep - repeating until the HWDT kicks in. - - This table is normally managed through calls to _xtos_set_exception_handler() -*/ -using _xtos_handler = void (*)(void); -constexpr uint32_t exception_dispatch_table = 0x3FFFC000u; -static _xtos_handler * const _xtos_exc_handler_table = (_xtos_handler *)exception_dispatch_table; - -/* - Xtensa ® Instruction Set Architecture (ISA) Reference Manual, p90: + Xtensa (R) Instruction Set Architecture (ISA) Reference Manual, p90: EXCCAUSE value 20, InstFetchProhibitedCause - An instruction fetch referenced a page mapped with an attribute that does not permit instruction fetch. */ diff --git a/cores/esp8266/esp8266_undocumented.h b/cores/esp8266/esp8266_undocumented.h index 22216d962a..875911b4c6 100644 --- a/cores/esp8266/esp8266_undocumented.h +++ b/cores/esp8266/esp8266_undocumented.h @@ -1,5 +1,8 @@ // ROM and blob calls without official headers available +#ifndef __ESP8266_UNDOCUMENTED_H +#define __ESP8266_UNDOCUMENTED_H + #ifdef __cplusplus extern "C" { #endif @@ -34,6 +37,28 @@ extern int ets_uart_printf(const char *format, ...) __attribute__ ((format (prin extern void ets_delay_us(uint32_t us); +/* + The Boot ROM sets up a table of dispatch handlers at 0x3FFFC000. + This table has an entry for each of the EXCCAUSE values, 0 through 63. + + Entries that do not have a specific handler are set to + `_xtos_unhandled_exception`. This handler will execute a `break 1, 1` + (0x4000DC4Bu) before doing a `rfe` (return from exception). Since the PC has + not been changed, the event that caused the 1st exception will likely keep + repeating until the HWDT kicks in. + + This table is normally managed through calls to _xtos_set_exception_handler() +*/ + +/* + Added to eagle.rom.addr.v6.ld + PROVIDE ( _xtos_exc_handler_table = 0x3fffc000 ); +*/ +using _xtos_handler = void (*)(void); +extern _xtos_handler _xtos_exc_handler_table[]; + #ifdef __cplusplus }; #endif + +#endif diff --git a/tools/sdk/ld/eagle.rom.addr.v6.ld b/tools/sdk/ld/eagle.rom.addr.v6.ld index 84c5e3acf5..ca54aedae7 100644 --- a/tools/sdk/ld/eagle.rom.addr.v6.ld +++ b/tools/sdk/ld/eagle.rom.addr.v6.ld @@ -79,6 +79,10 @@ PROVIDE ( _xtos_l1int_handler = 0x4000048c ); PROVIDE ( _xtos_p_none = 0x4000dbf8 ); PROVIDE ( _xtos_restore_intlevel = 0x4000056c ); PROVIDE ( _xtos_return_from_exc = 0x4000dc54 ); + +/* Added to support replacing the ROM _xtos_unhandled_exception */ +PROVIDE ( _xtos_exc_handler_table = 0x3fffc000 ); + PROVIDE ( _xtos_set_exception_handler = 0x40000454 ); PROVIDE ( _xtos_set_interrupt_handler = 0x4000bd70 ); PROVIDE ( _xtos_set_interrupt_handler_arg = 0x4000bd28 ); From b52648c3512009d8e60bdd10981e48507701d471 Mon Sep 17 00:00:00 2001 From: M Hightower <27247790+mhightower83@users.noreply.github.com> Date: Thu, 19 Nov 2020 21:09:50 -0800 Subject: [PATCH 6/9] Correction for `using _xtos_handler =` in .c files --- cores/esp8266/esp8266_undocumented.h | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/cores/esp8266/esp8266_undocumented.h b/cores/esp8266/esp8266_undocumented.h index 875911b4c6..c5cd6ad948 100644 --- a/cores/esp8266/esp8266_undocumented.h +++ b/cores/esp8266/esp8266_undocumented.h @@ -51,10 +51,15 @@ extern void ets_delay_us(uint32_t us); */ /* - Added to eagle.rom.addr.v6.ld - PROVIDE ( _xtos_exc_handler_table = 0x3fffc000 ); + Added to eagle.rom.addr.v6.ld + PROVIDE ( _xtos_exc_handler_table = 0x3fffc000 ); */ -using _xtos_handler = void (*)(void); +#ifdef __cplusplus +using _xtos_handler = void (*)(); +#else +typedef void (*_xtos_handler)(); +#endif + extern _xtos_handler _xtos_exc_handler_table[]; #ifdef __cplusplus From dc484cfc71eb20ab18537f8e68f287982bc1751c Mon Sep 17 00:00:00 2001 From: M Hightower <27247790+mhightower83@users.noreply.github.com> Date: Sun, 22 Nov 2020 11:35:28 -0800 Subject: [PATCH 7/9] Improved comments. At postmortem disable breakpoints. For ASMs let the compiler pick tmp register. Changed _xtos_handler to conform with xtensa/xtruntime.h definition. I am trying to not conflick with their usage in case the file someday finds its way into this code base; however, unless I am confused, I think I see issues with its reuse of _xtos_handler, where it does not strickly fit. At this time, these concerns are outside the scope of this PR. --- cores/esp8266/core_esp8266_postmortem.cpp | 34 +++++++++++++++----- cores/esp8266/esp8266_undocumented.h | 38 +++++++++++++++++------ 2 files changed, 54 insertions(+), 18 deletions(-) diff --git a/cores/esp8266/core_esp8266_postmortem.cpp b/cores/esp8266/core_esp8266_postmortem.cpp index 27f7c0f883..981900d5e0 100644 --- a/cores/esp8266/core_esp8266_postmortem.cpp +++ b/cores/esp8266/core_esp8266_postmortem.cpp @@ -205,6 +205,20 @@ void __wrap_system_restart_local() { ets_install_putc1(&uart_write_char_d); +#if defined(DEBUG_ESP_PORT) || defined(DEBUG_ESP_EXCEPTIONS) + if (!gdb_present()) { + // Clear breakpoint registers so ESP doesn't get a HWDT crash at reboot. + uint32_t tmp; // Let the compiler select the optimum scratch register + asm volatile( + "movi.n %0, 0\n\t" + "wsr.dbreakc0 %0\n\t" + "wsr.ibreakenable %0\n\t" + "wsr.icount %0\n\t" + :"=r"(tmp) :: + ); + } +#endif + /* Check for Exceptions mapped into User Exception. */ @@ -583,7 +597,7 @@ asm( // 16 byte MAX, using 14 NOP), you still see a HWDT reset regardless of running GDB. */ -void postmortem_xtos_unhandled_exception(void); +void postmortem_xtos_unhandled_exception(...); asm( ".section .iram.text.postmortem_xtos_unhandled_exception,\"ax\",@progbits\n\t" ".literal_position\n\t" @@ -627,7 +641,7 @@ static void install_unhandled_exception_handler(void) { replace_exception_handler_on_match( i, ROM_xtos_unhandled_exception, - (_xtos_handler)postmortem_xtos_unhandled_exception); + postmortem_xtos_unhandled_exception); } } @@ -650,17 +664,21 @@ void postmortem_init(void) { } else #endif { - uint32_t save_ps = xt_rsil(15); ets_memcpy((void*)_DebugExceptionVector, (void*)postmortem_debug_exception_vector, debug_vector_sz); ets_memcpy((void*)_KernelExceptionVector, (void*)postmortem_kernel_exception_handler, kernel_vector_sz); ets_memcpy((void*)_DoubleExceptionVector, (void*)postmortem_double_exception_handler, double_vector_sz); + // A little asm optimization. No need to zero exccause, epc1 and + // excsave1 these will be quickly set on the 1st timer tick for the + // Soft WDT. Set the rest to zero. + uint32_t tmp; asm volatile( - "movi.n a2, 0\n\t" - "wsr.epc2 a2\n\t" - "wsr.excsave2 a2\n\t" - "wsr.depc a2\n\t" - ::: "a2" + "movi.n %0, 0\n\t" + "wsr.epc2 %0\n\t" + "wsr.epc3 %0\n\t" + "wsr.excsave2 %0\n\t" + "wsr.depc %0\n\t" + :"=r"(tmp) :: ); install_unhandled_exception_handler(); xt_wsr_ps(save_ps); diff --git a/cores/esp8266/esp8266_undocumented.h b/cores/esp8266/esp8266_undocumented.h index c5cd6ad948..b16f56faa3 100644 --- a/cores/esp8266/esp8266_undocumented.h +++ b/cores/esp8266/esp8266_undocumented.h @@ -38,28 +38,46 @@ extern int ets_uart_printf(const char *format, ...) __attribute__ ((format (prin extern void ets_delay_us(uint32_t us); /* - The Boot ROM sets up a table of dispatch handlers at 0x3FFFC000. - This table has an entry for each of the EXCCAUSE values, 0 through 63. + The Boot ROM sets up a table of dispatch handlers at 0x3FFFC000. This table + has an entry for each of the EXCCAUSE values, 0 through 63. The exception + handler at the `User Exception Vector` uses EXCCAUSE with the base address + 0x3FFFC000 to build a jump address to the respective cause handler. Of the + cause handle functions `_xtos_c_wrapper_handler` and `_xtos_unhandled_exception` + are of interest. Entries that do not have a specific handler are set to `_xtos_unhandled_exception`. This handler will execute a `break 1, 1` (0x4000DC4Bu) before doing a `rfe` (return from exception). Since the PC has - not been changed, the event that caused the 1st exception will likely keep + not changed, the event that caused the 1st exception will likely keep repeating until the HWDT kicks in. - This table is normally managed through calls to _xtos_set_exception_handler() + These exception handling functions are in assembly, and do not conform to the + typical "C" function conventions. However, some form of prototype is needed to + reference these function addresses in "C" code. In + `RTOS_SDK/components/esp8266/include/xtensa/xtruntime.h`, it uses a compounded + definition that equates to `void (*)(...)` for .cpp modules to use. I have + noticed this creates sufficient confusion at compilation to get your attention + when used in the wrong place. I have copied that definition here. + + Unfortunately RTOS_SDK and other ESP projects on the Internet also reuses + this loose definition, _xtos_handler, for a function table of "C" callable + functions, `_xtos_c_handler_table`. This issue falls into a different PR so I + will leave it for that PR where there is more context. +//C TODO: Update this last paragraph when merged with related PR. */ -/* - Added to eagle.rom.addr.v6.ld - PROVIDE ( _xtos_exc_handler_table = 0x3fffc000 ); -*/ +// This is copy/paste from RTOS_SDK/components/esp8266/include/xtensa/xtruntime.h #ifdef __cplusplus -using _xtos_handler = void (*)(); +typedef void (_xtos_handler_func)(...); #else -typedef void (*_xtos_handler)(); +typedef void (_xtos_handler_func)(); #endif +typedef _xtos_handler_func *_xtos_handler; +/* + Added to eagle.rom.addr.v6.ld + PROVIDE ( _xtos_exc_handler_table = 0x3fffc000 ); +*/ extern _xtos_handler _xtos_exc_handler_table[]; #ifdef __cplusplus From 01cc4157f516ff590f4e445328ae1ed3561048c7 Mon Sep 17 00:00:00 2001 From: M Hightower <27247790+mhightower83@users.noreply.github.com> Date: Sat, 28 Nov 2020 21:56:21 -0800 Subject: [PATCH 8/9] Cleanup comment. Added some externs and comments for reference. --- cores/esp8266/core_esp8266_postmortem.cpp | 10 +++--- cores/esp8266/esp8266_undocumented.h | 42 ++++++++++++++--------- 2 files changed, 31 insertions(+), 21 deletions(-) diff --git a/cores/esp8266/core_esp8266_postmortem.cpp b/cores/esp8266/core_esp8266_postmortem.cpp index 981900d5e0..cd8f7ee925 100644 --- a/cores/esp8266/core_esp8266_postmortem.cpp +++ b/cores/esp8266/core_esp8266_postmortem.cpp @@ -586,10 +586,10 @@ asm( // 16 byte MAX, using 14 /* Of the Exception Cause table's 64 entries, most are set to `_xtos_unhandled_exception`. And I assume most of these never occur; however, - at lesat one does exccause 20. This one occurs on a call to a NULL pointer. This - can happen when a function prototype is given the weak attribute but never - defined, or forgetting to test a call back function pointer for NULL before - calling, etc. + at lesat one does, exccause 20. This one occurs on a call to a NULL pointer. + This can happen when a function prototype is given the weak attribute but + never defined, or forgetting to test a call back function pointer for NULL + before calling, etc. A problem with the `_xtos_unhandled_exception` function is that it handles the event with a Debug Exception BP. If you don't have GDB running you see a HWDT @@ -631,7 +631,7 @@ static void replace_exception_handler_on_match( // While _xtos_unhandled_exception is in the linker .ld file, it may have been // overridden. We require the original Boot ROM function address to limit our -// override to those old values in the table. +// override to the default values still in the table. const _xtos_handler ROM_xtos_unhandled_exception = (reinterpret_cast<_xtos_handler>(0x4000dc44)); static void install_unhandled_exception_handler(void) { diff --git a/cores/esp8266/esp8266_undocumented.h b/cores/esp8266/esp8266_undocumented.h index b16f56faa3..5a8e18726c 100644 --- a/cores/esp8266/esp8266_undocumented.h +++ b/cores/esp8266/esp8266_undocumented.h @@ -7,6 +7,11 @@ extern "C" { #endif +#ifndef XCHAL_EXCCAUSE_NUM +// from tools/xtensa-lx106-elf/include/xtensa/config/core.h:629:#define XCHAL_EXCCAUSE_NUM 64 +#define XCHAL_EXCCAUSE_NUM 64 +#endif + // ROM extern void rom_i2c_writeReg_Mask(int, int, int, int, int, int); @@ -42,30 +47,26 @@ extern void ets_delay_us(uint32_t us); has an entry for each of the EXCCAUSE values, 0 through 63. The exception handler at the `User Exception Vector` uses EXCCAUSE with the base address 0x3FFFC000 to build a jump address to the respective cause handler. Of the - cause handle functions `_xtos_c_wrapper_handler` and `_xtos_unhandled_exception` + cause handle functions, `_xtos_c_wrapper_handler` and `_xtos_unhandled_exception` are of interest. - Entries that do not have a specific handler are set to + Exception handler entries that do not have a specific handler are set to `_xtos_unhandled_exception`. This handler will execute a `break 1, 1` (0x4000DC4Bu) before doing a `rfe` (return from exception). Since the PC has not changed, the event that caused the 1st exception will likely keep repeating until the HWDT kicks in. These exception handling functions are in assembly, and do not conform to the - typical "C" function conventions. However, some form of prototype is needed to - reference these function addresses in "C" code. In + typical "C" function conventions. However, some form of prototype/typedef is + needed to reference these function addresses in "C" code. In `RTOS_SDK/components/esp8266/include/xtensa/xtruntime.h`, it uses a compounded definition that equates to `void (*)(...)` for .cpp modules to use. I have - noticed this creates sufficient confusion at compilation to get your attention - when used in the wrong place. I have copied that definition here. - - Unfortunately RTOS_SDK and other ESP projects on the Internet also reuses - this loose definition, _xtos_handler, for a function table of "C" callable - functions, `_xtos_c_handler_table`. This issue falls into a different PR so I - will leave it for that PR where there is more context. -//C TODO: Update this last paragraph when merged with related PR. -*/ + copied that definition here. + Added to eagle.rom.addr.v6.ld: + PROVIDE ( _xtos_exc_handler_table = 0x3fffc000 ); +*/ +#ifndef XTRUNTIME_H // This is copy/paste from RTOS_SDK/components/esp8266/include/xtensa/xtruntime.h #ifdef __cplusplus typedef void (_xtos_handler_func)(...); @@ -74,11 +75,20 @@ typedef void (_xtos_handler_func)(); #endif typedef _xtos_handler_func *_xtos_handler; +extern _xtos_handler _xtos_exc_handler_table[XCHAL_EXCCAUSE_NUM]; + /* - Added to eagle.rom.addr.v6.ld - PROVIDE ( _xtos_exc_handler_table = 0x3fffc000 ); + Assembly-level handler, used in the _xtos_exc_handler_table[]. It is a wrapper + for calling registered "C" exception handlers. */ -extern _xtos_handler _xtos_exc_handler_table[]; +extern _xtos_handler_func _xtos_c_wrapper_handler; + +/* + Assembly-level handler, used in the _xtos_exc_handler_table[]. It is the + default handler, for exceptions without a registered handler. +*/ +extern _xtos_handler_func _xtos_unhandled_exception; +#endif #ifdef __cplusplus }; From 79c0d01bc3feed631d7ef2ffde81bed40de2627b Mon Sep 17 00:00:00 2001 From: M Hightower <27247790+mhightower83@users.noreply.github.com> Date: Fri, 11 Dec 2020 10:46:15 -0800 Subject: [PATCH 9/9] replace typdef with using For esp8266_undocumented.cpp, in the __cplusplus path replace `typedef` with `using`. --- cores/esp8266/esp8266_undocumented.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/cores/esp8266/esp8266_undocumented.h b/cores/esp8266/esp8266_undocumented.h index a19835218b..d44b1fa9cc 100644 --- a/cores/esp8266/esp8266_undocumented.h +++ b/cores/esp8266/esp8266_undocumented.h @@ -121,12 +121,14 @@ struct __exception_frame */ #ifndef XTRUNTIME_H // This is copy/paste from RTOS_SDK/components/esp8266/include/xtensa/xtruntime.h +// With edits in the __cplusplus path to use `using` instead of `typedef`. #ifdef __cplusplus -typedef void (_xtos_handler_func)(...); +using _xtos_handler_func = void (...); +using _xtos_handler = _xtos_handler_func *; #else typedef void (_xtos_handler_func)(); -#endif typedef _xtos_handler_func *_xtos_handler; +#endif extern _xtos_handler _xtos_exc_handler_table[XCHAL_EXCCAUSE_NUM]; @@ -152,7 +154,7 @@ _xtos_handler_func _xtos_unhandled_exception; `_xtos_c_handler_table[]` More details in `_xtos_set_exception_handler` comments below. */ -typedef void (*fn_c_exception_handler_t)(struct __exception_frame *ef, int cause); +using fn_c_exception_handler_t = void (*)(struct __exception_frame *ef, int cause); /* TMI maybe? However, it may be useful for a deep debugging session.