Fix context save logic when called in window ABI configuration. Add an IDLE stack. Don't depend on the mystery stack received from the bootloader.

This commit is contained in:
Gregory Nutt
2016-12-18 10:07:34 -06:00
parent 8ce1fdaab0
commit 586f0aab50
5 changed files with 196 additions and 34 deletions
+29 -5
View File
@@ -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:
+136 -13
View File
@@ -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
+8 -14
View File
@@ -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
}
+1 -1
View File
@@ -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
+22 -1
View File
@@ -34,13 +34,23 @@
#include <nuttx/init.h>
#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 */
}