mirror of
https://github.com/apache/nuttx.git
synced 2026-09-01 22:53:01 +00:00
583 lines
18 KiB
ArmAsm
583 lines
18 KiB
ArmAsm
/****************************************************************************
|
|
* arch/xtensa/src/common/xtensa_context.S
|
|
*
|
|
* Adapted from use in NuttX by:
|
|
*
|
|
* Copyright (C) 2016 Gregory Nutt. All rights reserved.
|
|
* Author: Gregory Nutt <gnutt@nuttx.org>
|
|
*
|
|
* Derives from logic originally provided by Cadence Design Systems Inc.
|
|
*
|
|
* Copyright (c) 2006-2015 Cadence Design Systems Inc.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining
|
|
* a copy of this software and associated documentation files (the
|
|
* "Software"), to deal in the Software without restriction, including
|
|
* without limitation the rights to use, copy, modify, merge, publish,
|
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
|
* permit persons to whom the Software is furnished to do so, subject to
|
|
* the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included
|
|
* in all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
*
|
|
****************************************************************************/
|
|
|
|
.file "xtensa_context.S"
|
|
|
|
/* XTENSA CONTEXT SAVE AND RESTORE ROUTINES
|
|
*
|
|
* Low-level Call0 functions for handling generic context save and restore
|
|
* of registers not specifically addressed by the interrupt vectors and
|
|
* handlers. Those registers (not handled by these functions) are PC, PS,
|
|
* A0, A1 (SP).
|
|
*
|
|
* Note that in Call0 ABI, interrupt handlers are expected to preserve the callee-
|
|
* save regs (A12-A15), which is always the case if the handlers are coded in C.
|
|
* However A12, A13 are made available as scratch registers for interrupt dispatch
|
|
* code, so are presumed saved anyway, and are always restored even in Call0 ABI.
|
|
* Only A14, A15 are truly handled as callee-save regs.
|
|
*
|
|
* Because Xtensa is a configurable architecture, this port supports all user
|
|
* generated configurations (except restrictions stated in the release notes).
|
|
* This is accomplished by conditional compilation using macros and functions
|
|
* defined in the Xtensa HAL (hardware adaptation layer) for your configuration.
|
|
* Only the processor state included in your configuration is saved and restored,
|
|
* including any processor state added by user configuration options or TIE.
|
|
*/
|
|
|
|
/****************************************************************************
|
|
* Included Files
|
|
****************************************************************************/
|
|
|
|
#include <nuttx/config.h>
|
|
|
|
#include <arch/irq.h>
|
|
#include <arch/chip/core-isa.h>
|
|
#include <arch/chip/tie.h>
|
|
#include <arch/xtensa/xtensa_specregs.h>
|
|
|
|
#include "xtensa_abi.h"
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
.text
|
|
|
|
/****************************************************************************
|
|
* Name: _xtensa_context_save
|
|
*
|
|
* Description:
|
|
*
|
|
* NOTE: MUST BE CALLED ONLY BY 'CALL0' INSTRUCTION!
|
|
*
|
|
* This function saves Xtensa processor state: xtensa_context_save
|
|
* saves all registers except PC, PS, A0, A1 (SP), and A2
|
|
*
|
|
* This function is called directly by interrupt handling logic and from
|
|
* xtensa_context_save() below with interrupts disabled. In either calling
|
|
* context, caller saves PC, PS, A0, A1 (SP), and A2. This
|
|
* logic also executes indirectly from xtensa_context_save() by falling
|
|
* through from above.
|
|
*
|
|
* The counterpart to this function is _xtensa_context_restore().
|
|
*
|
|
* Entry Conditions:
|
|
* - A0 = Return address to caller.
|
|
* - A2 = Pointer to the processor state save area
|
|
* - Other processor state except PC, PS, A0, A1 (SP), and A2 are as at
|
|
* the point of interruption.
|
|
*
|
|
* Exit conditions:
|
|
* - A0 = Return address in caller.
|
|
* - A2, A12-A15 as at entry (preserved).
|
|
*
|
|
* Assumptions:
|
|
* - Caller is expected to have saved PC, PS, A0, A1 (SP), and A2.
|
|
* - If windowed ABI, PS.EXCM = 1 (exceptions disabled).
|
|
*
|
|
****************************************************************************/
|
|
|
|
.global _xtensa_context_save
|
|
.type _xtensa_context_save, @function
|
|
|
|
.align 4
|
|
.literal_position
|
|
.align 4
|
|
|
|
_xtensa_context_save:
|
|
|
|
s32i a3, a2, (4 * REG_A3)
|
|
s32i a4, a2, (4 * REG_A4)
|
|
s32i a5, a2, (4 * REG_A5)
|
|
s32i a6, a2, (4 * REG_A6)
|
|
s32i a7, a2, (4 * REG_A7)
|
|
s32i a8, a2, (4 * REG_A8)
|
|
s32i a9, a2, (4 * REG_A9)
|
|
s32i a10, a2, (4 * REG_A10)
|
|
s32i a11, a2, (4 * REG_A11)
|
|
|
|
/* Call0 ABI callee-saved regs a12-15 */
|
|
|
|
s32i a12, a2, (4 * REG_A12)
|
|
s32i a13, a2, (4 * REG_A13)
|
|
s32i a14, a2, (4 * REG_A14)
|
|
s32i a15, a2, (4 * REG_A15)
|
|
|
|
rsr a3, SAR
|
|
s32i a3, a2, (4 * REG_SAR)
|
|
|
|
#ifdef XCHAL_HAVE_LOOPS
|
|
rsr a3, LBEG
|
|
s32i a3, a2, (4 * REG_LBEG)
|
|
rsr a3, LEND
|
|
s32i a3, a2, (4 * REG_LEND)
|
|
rsr a3, LCOUNT
|
|
s32i a3, a2, (4 * REG_LCOUNT)
|
|
#endif
|
|
|
|
#ifndef __XTENSA_CALL0_ABI__
|
|
/* To spill the reg windows, temp. need pre-interrupt stack ptr and
|
|
* a4-15. Interrupts need to be disabled below XCHAL_EXCM_LEVEL and
|
|
* window overflow and underflow exceptions disabled (assured by
|
|
* PS.EXCM == 1).
|
|
*/
|
|
|
|
#ifdef CONFIG_XTENSA_USE_OVLY
|
|
/* Save the overlay state if we are supporting overlays. Since we just
|
|
* saved three registers, we can conveniently use them here. Note that
|
|
* as of now, overlays only work for windowed calling ABI.
|
|
*/
|
|
|
|
#error Overlay support is not implemented
|
|
#endif
|
|
|
|
s32i a0, a2, (4 * REG_TMP0) /* Save return address */
|
|
s32i sp, a2, (4 * REG_TMP1) /* Save current stack pointer */
|
|
wsr a2, EXCSAVE_1 /* Preserve register save area */
|
|
|
|
l32i sp, a2, (4 * REG_A1) /* Restore the interruptee's SP */
|
|
call0 _xtensa_window_spill /* Preserves only a4-a5, a8-a9, a12-a13 */
|
|
|
|
rsr a2, EXCSAVE_1 /* Save interruptee's a0 */
|
|
l32i a0, a2, (4 * REG_TMP0) /* Save return address */
|
|
l32i sp, a2, (4 * REG_TMP1) /* Save current stack pointer */
|
|
#endif
|
|
|
|
ret
|
|
|
|
.size _xtensa_context_save, . - _xtensa_context_save
|
|
|
|
/****************************************************************************
|
|
* Name: xtensa_context_save
|
|
*
|
|
* Description:
|
|
*
|
|
* 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.
|
|
* However, it sets the saved value of the return address (A2) to 1.
|
|
* If the thread is s via _xtensa_context_restore or
|
|
* xtensa_context_restore, it will appear as a second return from
|
|
* xtensa_context_save but with the returned value of 1 to distinguish
|
|
* the two cases.
|
|
*
|
|
* The counterpart to this function is xtensa_context_restore().
|
|
*
|
|
* Entry Conditions:
|
|
* - A0 = Return address to caller.
|
|
* - A2 = Pointer to the processor state save area
|
|
*
|
|
* Exit conditions:
|
|
* - A0 = Return address in caller.
|
|
* - A2 = 0
|
|
*
|
|
* 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
|
|
.type xtensa_context_save, @function
|
|
|
|
.align 4
|
|
.literal_position
|
|
.align 4
|
|
|
|
xtensa_context_save:
|
|
ENTRY(16)
|
|
|
|
/* Set up for (potential) call to _xtensa_context_save() */
|
|
|
|
s32i a3, a2, (4 * REG_A3) /* Get scratch register */
|
|
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 a0, a2, (4 * REG_A0) /* Save callee's a0 */
|
|
s32i sp, a2, (4 * REG_A1) /* Save callee's SP */
|
|
movi a3, 1 /* Set saved A2 to 1 */
|
|
s32i a3, a2, (4 * REG_A2)
|
|
|
|
/* Save the rest of the processor state. For the CALL0 ABI, we can user
|
|
* _xtensa_context_save(), Otherwise we duplicate the context save here
|
|
* to avoid the window spill.
|
|
*/
|
|
|
|
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 address */
|
|
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 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.
|
|
*/
|
|
|
|
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)
|
|
s32i a8, a2, (4 * REG_A8)
|
|
s32i a9, a2, (4 * REG_A9)
|
|
s32i a10, a2, (4 * REG_A10)
|
|
s32i a11, a2, (4 * REG_A11)
|
|
|
|
/* Call0 ABI callee-saved regs a12-15 */
|
|
|
|
s32i a12, a2, (4 * REG_A12)
|
|
s32i a13, a2, (4 * REG_A13)
|
|
s32i a14, a2, (4 * REG_A14)
|
|
s32i a15, a2, (4 * REG_A15)
|
|
|
|
rsr a3, SAR
|
|
s32i a3, a2, (4 * REG_SAR)
|
|
|
|
#ifdef XCHAL_HAVE_LOOPS
|
|
rsr a3, LBEG
|
|
s32i a3, a2, (4 * REG_LBEG)
|
|
rsr a3, LEND
|
|
s32i a3, a2, (4 * REG_LEND)
|
|
rsr a3, LCOUNT
|
|
s32i a3, a2, (4 * REG_LCOUNT)
|
|
#endif
|
|
|
|
/* 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 return 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
|
|
*
|
|
* Description:
|
|
*
|
|
* NOTE: MUST BE CALLED ONLY BY 'CALL0' INSTRUCTION!
|
|
*
|
|
* These functions restores Xtensa processor state and differ in which
|
|
* registers are saved: _xtensa_context_restore() restores all registers
|
|
* except PC, PS, A0, and A2
|
|
*
|
|
* The caller is responsible for restoring PC, PS, A0, and A2.
|
|
*
|
|
* _xtensa_context_save is the counterpart to this function.
|
|
*
|
|
* Entry Conditions:
|
|
* - A0 = Return address in caller.
|
|
* - A2 = Pointer to the processor state save area
|
|
*
|
|
* Exit conditions:
|
|
* - A0 = Return address in caller.
|
|
* - Other registers are restored as detailed above
|
|
* - A2 is preserved
|
|
*
|
|
****************************************************************************/
|
|
|
|
.global _xtensa_context_restore
|
|
.type xtensa_context_restore,@function
|
|
|
|
.align 4
|
|
.literal_position
|
|
.align 4
|
|
|
|
_xtensa_context_restore:
|
|
|
|
#ifdef XCHAL_HAVE_LOOPS
|
|
l32i a3, a2, (4 * REG_LBEG)
|
|
l32i a4, a2, (4 * REG_LEND)
|
|
wsr a3, LBEG
|
|
l32i a3, a2, (4 * REG_LCOUNT)
|
|
wsr a4, LEND
|
|
wsr a3, LCOUNT
|
|
#endif
|
|
|
|
#ifdef CONFIG_XTENSA_USE_OVLY
|
|
/* If we are using overlays, this is a good spot to check if we need
|
|
* to restore an overlay for the incoming task. Here we have a bunch
|
|
* of registers to spare. Note that this step is going to use a few
|
|
* bytes of storage below SP (SP-20 to SP-32) if an overlay is going
|
|
* to be restored.
|
|
*/
|
|
|
|
#error Overly support is not implemented
|
|
#endif
|
|
|
|
l32i a3, a2, (4 * REG_SAR)
|
|
l32i sp, a2, (4 * REG_A1)
|
|
wsr a3, SAR
|
|
l32i a3, a2, (4 * REG_A3)
|
|
l32i a4, a2, (4 * REG_A4)
|
|
l32i a5, a2, (4 * REG_A5)
|
|
l32i a6, a2, (4 * REG_A6)
|
|
l32i a7, a2, (4 * REG_A7)
|
|
l32i a8, a2, (4 * REG_A8)
|
|
l32i a9, a2, (4 * REG_A9)
|
|
l32i a10, a2, (4 * REG_A10)
|
|
l32i a11, a2, (4 * REG_A11)
|
|
|
|
/* Call0 ABI callee-saved regs a12-15 */
|
|
|
|
l32i a12, a2, (4 * REG_A12)
|
|
l32i a13, a2, (4 * REG_A13)
|
|
l32i a14, a2, (4 * REG_A14)
|
|
l32i a15, a2, (4 * REG_A15)
|
|
|
|
ret
|
|
|
|
.size _xtensa_context_restore, . - _xtensa_context_restore
|
|
|
|
/****************************************************************************
|
|
* Name: xtensa_context_restore
|
|
*
|
|
* Description:
|
|
*
|
|
* This functions implements the moral equivalent of longjmp(). It is
|
|
* called from user code (with interrupts disabled) to restore the current
|
|
* state of the running thread. This function always appears to be a
|
|
* second return from xtensa_context_save except that that it returns the
|
|
* value 1 (because the saved value of A2 was set to 1
|
|
* inxtensa_context_save()).
|
|
*
|
|
* The counterpart to this function is xtensa_context_save().
|
|
*
|
|
* Entry Conditions:
|
|
* - A0 = Return address to caller.
|
|
* - A2 = Pointer to the processor state save area
|
|
*
|
|
* Exit conditions:
|
|
* NOTE: That this function does NOT return to the caller but rather
|
|
* to a new threading context. It is not necessary to save any of the
|
|
* caller's registers.
|
|
*
|
|
* Assumptions:
|
|
* - Interrupts are disabled.
|
|
*
|
|
****************************************************************************/
|
|
|
|
.global xtensa_context_restore
|
|
.type xtensa_context_restore, @function
|
|
|
|
.align 4
|
|
.literal_position
|
|
.align 4
|
|
|
|
xtensa_context_restore:
|
|
ENTRY(16)
|
|
|
|
#ifndef __XTENSA_CALL0_ABI__
|
|
/* Force a spill of the live registers of the thread that has been
|
|
* suspended.
|
|
*
|
|
* _xtensa_window_spill return state:
|
|
* a2, a3: clobbered
|
|
* a4,a5,a8,a9,a12,a13: preserved
|
|
* a6,a7,a10,a11,a14,a15 clobbered if they were part of window(s)
|
|
* to be spilled, otherwise they are the same as on entry
|
|
* loop registers: Perserved
|
|
* SAR: clobbered
|
|
*
|
|
* We need to preserve only a2 for _xtensa_context_restore
|
|
*/
|
|
|
|
mov a4, a2 /* Save a2 in a preserved register */
|
|
rsr a5, PS /* Save PS in preserved register */
|
|
|
|
movi a3, ~(PS_WOE_MASK | PS_INTLEVEL_MASK)
|
|
and a2, a5, a3 /* Clear WOE, INTLEVEL */
|
|
addi a2, a2, XCHAL_EXCM_LEVEL /* Set INTLEVEL = XCHAL_EXCM_LEVEL */
|
|
wsr a2, PS /* Apply to PS */
|
|
rsync
|
|
|
|
call0 _xtensa_window_spill
|
|
wsr a5, PS /* Restore PS */
|
|
rsync
|
|
|
|
mov a2, a4 /* Recover a2 */
|
|
#endif
|
|
|
|
/* Restore the processor state for the newly started thread */
|
|
|
|
call0 _xtensa_context_restore /* Restore full register state */
|
|
|
|
/* Restore PC, PS, A0, and A2. */
|
|
|
|
l32i a0, a2, (4 * REG_PS) /* Restore PS */
|
|
wsr a0, PS
|
|
l32i a0, a2, (4 * REG_PC) /* Set up for RFE */
|
|
wsr a0, EPC_1
|
|
l32i a0, a2, (4 * REG_A0) /* Restore a0 */
|
|
l32i a2, a2, (4 * REG_A2) /* Restore A2 */
|
|
|
|
/* Return from exception. RFE returns from either the UserExceptionVector
|
|
* or the KernelExceptionVector. RFE sets PS.EXCM back to 0, and then
|
|
* jumps to the address in EPC[1]. PS.UM and PS.WOE are left unchanged.
|
|
*/
|
|
|
|
rfe /* And return from "exception" */
|
|
|
|
.size xtensa_context_restore, . - xtensa_context_restore
|