mirror of
https://github.com/apache/nuttx.git
synced 2026-06-08 10:32:47 +08:00
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:
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user