diff --git a/app/include/rom.h b/app/include/rom.h index 88d7c81d..51d3fbe1 100644 --- a/app/include/rom.h +++ b/app/include/rom.h @@ -37,3 +37,57 @@ extern unsigned char * base64_decode(const unsigned char *src, size_t len, size_ // initialized because it uses mem_malloc extern void mem_init(void * start_addr); + + +// Hardware exception handling +struct exception_frame +{ + uint32_t epc; + uint32_t ps; + uint32_t sar; + uint32_t unused; + union { + struct { + uint32_t a0; + // note: no a1 here! + uint32_t a2; + uint32_t a3; + uint32_t a4; + uint32_t a5; + uint32_t a6; + uint32_t a7; + uint32_t a8; + uint32_t a9; + uint32_t a10; + uint32_t a11; + uint32_t a12; + uint32_t a13; + uint32_t a14; + uint32_t a15; + }; + uint32_t a_reg[15]; + }; + uint32_t cause; +}; + +/** + * C-level exception handler callback prototype. + * + * Does not need an RFE instruction - it is called through a wrapper which + * performs state capture & restore, as well as the actual RFE. + * + * @param ef An exception frame containing the relevant state from the + * exception triggering. This state may be manipulated and will + * be applied on return. + * @param cause The exception cause number. + */ +typedef void (*exception_handler_fn) (struct exception_frame *ef, uint32_t cause); + +/** + * Sets the exception handler function for a particular exception cause. + * @param handler The exception handler to install. + * If NULL, reverts to the XTOS default handler. + * @returns The previous exception handler, or NULL if none existed prior. + */ +exception_handler_fn _xtos_set_exception_handler (uint32_t cause, exception_handler_fn handler); + diff --git a/app/user/user_exceptions.c b/app/user/user_exceptions.c new file mode 100644 index 00000000..d20f0622 --- /dev/null +++ b/app/user/user_exceptions.c @@ -0,0 +1,97 @@ +/* + * Copyright 2015 Dius Computing Pty Ltd. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * - Neither the name of the copyright holders nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Johny Mattsson + */ + +#include "user_exceptions.h" + +#define LOAD_MASK 0x00f00fu +#define L8UI_MATCH 0x000002u +#define L16UI_MATCH 0x001002u +#define L16SI_MATCH 0x009002u + + +void load_non_32_wide_handler (struct exception_frame *ef, uint32_t cause) +{ + /* If this is not EXCCAUSE_LOAD_STORE_ERROR you're doing it wrong! */ + (void)cause; + + uint32_t epc1 = ef->epc; + uint32_t excvaddr; + uint32_t insn; + asm ( + "rsr %0, EXCVADDR;" /* read out the faulting address */ + "movi a4, ~3;" /* prepare a mask for the EPC */ + "and a4, a4, %2;" /* apply mask for 32bit aligned base */ + "l32i a5, a4, 0;" /* load part 1 */ + "l32i a6, a4, 4;" /* load part 2 */ + "ssa8l %2;" /* set up shift register for src op */ + "src %1, a6, a5;" /* right shift to get faulting instruction */ + :"=r"(excvaddr), "=r"(insn) + :"r"(epc1) + :"a4", "a5", "a6" + ); + + uint32_t valmask = 0; + uint32_t what = insn & LOAD_MASK; + + if (what == L8UI_MATCH) + valmask = 0xffu; + else if (what == L16UI_MATCH || what == L16SI_MATCH) + valmask = 0xffffu; + else + { +die: + /* Turns out we couldn't fix this, trigger a system break instead + * and hang if the break doesn't get handled. This is effectively + * what would happen if the default handler was installed. */ + asm ("break 1, 1"); + while (1) {} + } + + /* Load, shift and mask down to correct size */ + uint32_t val = (*(uint32_t *)(excvaddr & ~0x3)); + val >>= (excvaddr & 0x3) * 8; + val &= valmask; + + /* Sign-extend for L16SI, if applicable */ + if (what == L16SI_MATCH && (val & 0x8000)) + val |= 0xffff0000; + + int regno = (insn & 0x0000f0u) >> 4; + if (regno == 1) + goto die; /* we can't support loading into a1, just die */ + else if (regno != 0) + --regno; /* account for skipped a1 in exception_frame */ + + ef->a_reg[regno] = val; /* carry out the load */ + ef->epc += 3; /* resume at following instruction */ +} diff --git a/app/user/user_exceptions.h b/app/user/user_exceptions.h new file mode 100644 index 00000000..d2cf5166 --- /dev/null +++ b/app/user/user_exceptions.h @@ -0,0 +1,5 @@ +#include "c_types.h" +#include "rom.h" +#include + +void load_non_32_wide_handler (struct exception_frame *ef, uint32_t cause) TEXT_SECTION_ATTR; diff --git a/app/user/user_main.c b/app/user/user_main.c index 88938da8..6064937e 100644 --- a/app/user/user_main.c +++ b/app/user/user_main.c @@ -16,6 +16,7 @@ #include "flash_fs.h" #include "user_interface.h" +#include "user_exceptions.h" #include "ets_sys.h" #include "driver/uart.h" @@ -125,6 +126,9 @@ void nodemcu_init(void) *******************************************************************************/ void user_init(void) { + _xtos_set_exception_handler ( + EXCCAUSE_LOAD_STORE_ERROR, load_non_32_wide_handler); + // NODE_DBG("SDK version:%s\n", system_get_sdk_version()); // system_print_meminfo(); // os_printf("Heap size::%d.\n",system_get_free_heap_size()); diff --git a/include/c_types.h b/include/c_types.h index 55bffd46..34da0503 100644 --- a/include/c_types.h +++ b/include/c_types.h @@ -84,6 +84,8 @@ typedef enum { #define ICACHE_FLASH_ATTR #endif /* ICACHE_FLASH */ +#define TEXT_SECTION_ATTR __attribute__((section(".text"))) + #ifndef __cplusplus typedef unsigned char bool; #define BOOL bool