diff --git a/arch/xtensa/src/common/xtensa.h b/arch/xtensa/src/common/xtensa.h index 9cf4778e54a..20c67fc6d09 100644 --- a/arch/xtensa/src/common/xtensa.h +++ b/arch/xtensa/src/common/xtensa.h @@ -104,10 +104,30 @@ /* Check if an interrupt stack size is configured */ -#ifndef CONFIG_ARCH_INTERRUPTSTACK -# define CONFIG_ARCH_INTERRUPTSTACK 0 +#define HAVE_INTERRUPTSTACK 1 + +#if !defined(CONFIG_ARCH_INTERRUPTSTACK) +# define CONFIG_ARCH_INTERRUPTSTACK 0 +# undef HAVE_INTERRUPTSTACK +#elif CONFIG_ARCH_INTERRUPTSTACK < 16 +# warning CONFIG_ARCH_INTERRUPTSTACK is to small +# undef HAVE_INTERRUPTSTACK #endif +#define INTERRUPTSTACK_SIZE ((CONFIG_ARCH_INTERRUPTSTACK + 15) & ~15) +#define INTERRUPT_STACKWORDS (INTERRUPTSTACK_SIZE >> 2) + +/* An IDLE thread stack size for CPU0 must be defined */ + +#if !defined(CONFIG_IDLETHREAD_STACKSIZE) +# error CONFIG_IDLETHREAD_STACKSIZE is not defined +#elif CONFIG_IDLETHREAD_STACKSIZE < 16 +# error CONFIG_IDLETHREAD_STACKSIZE is to small +#endif + +#define IDLETHREAD_STACKSIZE ((CONFIG_IDLETHREAD_STACKSIZE + 15) & ~15) +#define IDLETHREAD_STACKWORDS (IDLETHREAD_STACKSIZE >> 2) + /* Used for stack usage measurements */ #define STACK_COLOR 0xdeadbeef @@ -180,12 +200,16 @@ extern volatile uint32_t *g_current_regs[1]; #endif -/* Address of the saved user stack pointer */ +#ifdef HAVE_INTERRUPTSTACK +/* The (optional) interrupt stack */ -#if CONFIG_ARCH_INTERRUPTSTACK > 3 -extern void g_intstackbase; +extern uint32_t g_intstack[INTERRUPT_STACKWORDS]; #endif +/* Address of the CPU0 IDLE thread */ + +extern uint32_t g_idlestack[IDLETHREAD_STACKWORDS]; + /* These 'addresses' of these values are setup by the linker script. They are * not actual uint32_t storage locations! They are only used meaningfully in the * following way: diff --git a/arch/xtensa/src/common/xtensa_context.S b/arch/xtensa/src/common/xtensa_context.S index 0eb9a468a21..7f6d074d6d7 100644 --- a/arch/xtensa/src/common/xtensa_context.S +++ b/arch/xtensa/src/common/xtensa_context.S @@ -182,8 +182,6 @@ _xtensa_context_save: * * Description: * - * NOTE: MUST BE CALLED ONLY BY 'CALL0' INSTRUCTION! - * * This functions implements the moral equivalent of setjmp(). It is * called from user code (with interrupts disabled) to save the current * state of the running thread. This function always returns zero. @@ -206,6 +204,24 @@ _xtensa_context_save: * Assumptions: * - Interrupts are disabled. * + ****************************************************************************/ + +#ifdef __XTENSA_CALL0_ABI__ + +/**************************************************************************** + * Name: xtensa_context_save: + * + * Description: + * This implementation of xtensa_context_save for the case of the CALL0 ABI + * + * Input State: + * a0 = The return value to the caller. + * a2 = The address of the register state state structure + * + * Return state: + * a0 = The return value to the caller. + * a2, a12-a15 preserved as at entry + * ****************************************************************************/ .global xtensa_context_save @@ -218,7 +234,6 @@ _xtensa_context_save: xtensa_context_save: ENTRY(16) - /* Set up for (potential) call to _xtensa_context_save() */ s32i a3, a2, (4 * REG_A3) /* Get scratch register */ @@ -236,24 +251,79 @@ xtensa_context_save: * to avoid the window spill. */ -#ifdef __XTENSA_CALL0_ABI__ l32i r3, a2, (4 * REG_A3) /* Recover original a3 */ call0 _xtensa_context_save /* Save full register state */ /* Recover the return address and return zero */ l32i a0, a2, (4 * REG_A0) /* Recover return addess */ -#else - /* REVISIT: We could save a lot here. It should not be necessary to + movi a2, 0 /* Return zero */ + RET(16) + + .size xtensa_context_save, . - xtensa_context_save +#endif + +/**************************************************************************** + * This implementation of xtensa_context_save for the case of the window ABI. + * This case is more complex. For the Window ABI, there is a "hook" that + * performs the low level state state. xtensa_context_save() is a simply + * trampoline function that performs the window oeprations in that + * configuration. + ****************************************************************************/ + +#ifndef __XTENSA_CALL0_ABI__ + +/**************************************************************************** + * Name: _xtensa_save_hook: + * + * Input State: + * True return value has already been saved + * a0 = The return value into xtensa_context_save() + * a2 = The address of the register state state structure + * + * Return state: + * a0, a3 modified. + * Other values as on entry + * Returned value is in a3 (non-stanadard) + * + ****************************************************************************/ + + .type _xtensa_save_hook, @function + + .align 4 + .literal_position + .align 4 + +_xtensa_save_hook: + + /* Save the return value of 1 that will be used when returning from a + * context switch. NOTE that the returned value from this function is + * expected in a3 (not the usual a2). This also frees up a3 for a use + * as a scratch register. + */ + + movi a3, 1 /* Set saved a3 to 1 */ + s32i a3, a2, (4 * REG_A3) + + /* Save the rest of the processor state. + * + * REVISIT: We could save a lot here. It should not be necessary to * preserve all of these registers. The ABI permits volatile, callee- * saved, registers to be clobbered on function calls. We save the * whole tamale here mostly for debug purposes. * - * NOTE that PS, PC and registers 0-3 were saved above. a0 is not - * modified. + * NOTE that a3 was saved above. The true a0 return value was saved + * in xtensa_context_save. The a0 value saved below is the return into + * xtensa_context_save. */ - s32i a4, a2, (4 * REG_A4) + rsr a3, PS /* Save callee's PS */ + s32i a3, a2, (4 * REG_PS) + s32i a0, a2, (4 * REG_PC) /* Save Return address as PC */ + + s32i sp, a2, (4 * REG_A1) /* Save callee's SP */ + s32i a2, a2, (4 * REG_A2) + s32i a4, a2, (4 * REG_A4) /* Save remaining registers */ s32i a5, a2, (4 * REG_A5) s32i a6, a2, (4 * REG_A6) s32i a7, a2, (4 * REG_A7) @@ -279,13 +349,68 @@ xtensa_context_save: s32i a3, a2, (4 * REG_LEND) rsr a3, LCOUNT s32i a3, a2, (4 * REG_LCOUNT) -#endif #endif - movi a2, 0 /* Return zero */ + /* NOTE that the returned value is through a3 */ + + movi a3, 0 /* Return zero, no context switch */ + ret + + .size _xtensa_save_hook, . - _xtensa_save_hook + +/**************************************************************************** + * Name: xtensa_context_save: + * + * Description: + * This is the implementation of xtensa_context_save for the case of the + * window ABI. In the window ABI configuration, xtensa_context_save is a + * thin "trampoline" layer. It performs the ENTRY window operations on + * entry and the exit. A call0 is used to force the retun from the context + * switch to the window return within this trampoline. + * + * Input State: + * a0 = The true return value to the caller. + * a2 = The address of the register state state structure + * + * Return state: + * a0, a2, and a3 modified. + * Returned value is in a2 + * + ****************************************************************************/ + + .global xtensa_context_save + .type xtensa_context_save, @function + + .align 4 + .literal_position + .align 4 + +xtensa_context_save: + ENTRY(16) + + /* Save the true return address in the register save structure (a0). */ + + s32i a0, a2, (4 * REG_A0) /* Save true return address (a0) */ + + /* Then perform the actual state save in _xtensa_save_hook. The saved + * EPC will be set to the return from this function then we will do the + * RET(16) window fix-up. + */ + + call0 _xtensa_save_hook /* Save full register state */ + + /* a0 and a2 will be automatically restored in the context switch case + * with a3=1. In the non-context switch return with a2=0, a2 will still + * be valid, but we have to restore a0 ourself. The following should + * work in either case. + */ + + l32i a0, a2, (4 * REG_A0) /* Recover the true return address (a0) */ + mov a2, a3 /* Move a3 to the correct register for return */ RET(16) .size xtensa_context_save, . - xtensa_context_save +#endif /**************************************************************************** * Name: _xtensa_context_restore @@ -371,8 +496,6 @@ _xtensa_context_restore: * * Description: * - * NOTE: MUST BE CALLED ONLY BY 'CALL0' INSTRUCTION! - * * This functions implements the moral equivalent of longjmp(). It is * called from user code (with interrupts disabled) to restor the current * state of the running thread. This function always appears to be a diff --git a/arch/xtensa/src/common/xtensa_dumpstate.c b/arch/xtensa/src/common/xtensa_dumpstate.c index 9e0af50e812..d470c937ea2 100644 --- a/arch/xtensa/src/common/xtensa_dumpstate.c +++ b/arch/xtensa/src/common/xtensa_dumpstate.c @@ -150,7 +150,7 @@ void xtensa_dumpstate(void) uint32_t sp = xtensa_getsp(); uint32_t ustackbase; uint32_t ustacksize; -#if CONFIG_ARCH_INTERRUPTSTACK > 3 +#ifdef HAVE_INTERRUPTSTACK uint32_t istackbase; uint32_t istacksize; #endif @@ -165,14 +165,8 @@ void xtensa_dumpstate(void) if (rtcb->pid == 0) { -#warning REVISIT: Need top of IDLE stack -#if 0 - ustackbase = g_idle_topstack - 4; - ustacksize = CONFIG_IDLETHREAD_STACKSIZE; -#else - ustackbase = sp + 128; - ustacksize = 256; -#endif + ustackbase = (uint32_t)&g_idlestack[IDLETHREAD_STACKWORDS-1]; + ustacksize = IDLETHREAD_STACKSIZE; } else { @@ -183,9 +177,9 @@ void xtensa_dumpstate(void) /* Get the limits on the interrupt stack memory */ #warning REVISIT interrupt stack -#if CONFIG_ARCH_INTERRUPTSTACK > 3 - istackbase = (uint32_t)&g_intstackbase; - istacksize = (CONFIG_ARCH_INTERRUPTSTACK & ~3) - 4; +#ifdef HAVE_INTERRUPTSTACK + istackbase = (uint32_t)&g_intstack[INTERRUPT_STACKWORDS-1]; + istacksize = INTERRUPTSTACK_SIZE; /* Show interrupt stack info */ @@ -208,7 +202,7 @@ void xtensa_dumpstate(void) * at the base of the interrupt stack. */ - sp = g_intstackbase; + sp = &g_instack[INTERRUPTSTACK_SIZE - sizeof(uint32_t)]; _alert("sp: %08x\n", sp); } @@ -229,7 +223,7 @@ void xtensa_dumpstate(void) if (sp > ustackbase || sp <= ustackbase - ustacksize) { -#if !defined(CONFIG_ARCH_INTERRUPTSTACK) || CONFIG_ARCH_INTERRUPTSTACK < 4 +#ifdef HAVE_INTERRUPTSTACK _alert("ERROR: Stack pointer is not within allocated stack\n"); #endif } diff --git a/arch/xtensa/src/esp32/esp32_irq.c b/arch/xtensa/src/esp32/esp32_irq.c index 351e50b5686..11c43a4a1ce 100644 --- a/arch/xtensa/src/esp32/esp32_irq.c +++ b/arch/xtensa/src/esp32/esp32_irq.c @@ -141,7 +141,7 @@ void xtensa_irq_initialize(void) (void)esp32_cpuint_initialize(); -#if defined(CONFIG_STACK_COLORATION) && CONFIG_ARCH_INTERRUPTSTACK > 3 +#if defined(CONFIG_STACK_COLORATION) && defined(HAVE_INTERRUPTSTACK) /* Colorize the interrupt stack for debug purposes */ #warning Missing logic diff --git a/arch/xtensa/src/esp32/esp32_start.c b/arch/xtensa/src/esp32/esp32_start.c index a468e83a9c8..0bf47460d61 100644 --- a/arch/xtensa/src/esp32/esp32_start.c +++ b/arch/xtensa/src/esp32/esp32_start.c @@ -34,13 +34,23 @@ #include +#include "xtensa.h" #include "xtensa_attr.h" + #include "chip/esp32_dport.h" #include "chip/esp32_rtccntl.h" #include "esp32_clockconfig.h" #include "esp32_region.h" #include "esp32_start.h" -#include "xtensa.h" + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* Address of the CPU0 IDLE thread */ + +uint32_t g_idlestack[IDLETHREAD_STACKWORDS] + __attribute__((aligned(16) section(".noinit"))); /**************************************************************************** * Public Functions @@ -62,6 +72,7 @@ void IRAM_ATTR __start(void) { uint32_t regval; + uint32_t sp; /* Kill the watchdog timer */ @@ -73,6 +84,15 @@ void IRAM_ATTR __start(void) regval &= ~(1 << 14); putreg32(regval, 0x6001f048); + /* Move the stack to a known location. Although we were give a stack + * pointer at start-up, we don't know where that stack pointer is positioned + * respect to our memory map. The only safe option is to switch to a well- + * known IDLE thread stack. + */ + + sp = (uint32_t)g_idlestack + IDLETHREAD_STACKSIZE; + __asm__ __volatile__("mov sp, %0\n" : : "r"(sp)); + /* Make page 0 access raise an exception */ esp32_region_protection(); @@ -108,4 +128,5 @@ void IRAM_ATTR __start(void) /* Bring up NuttX */ os_start(); + for(; ; ); /* Should not return */ }