diff --git a/arch/xtensa/include/esp32/irq.h b/arch/xtensa/include/esp32/irq.h index 98ce3c08db6..4c8e6c47ca5 100644 --- a/arch/xtensa/include/esp32/irq.h +++ b/arch/xtensa/include/esp32/irq.h @@ -50,19 +50,6 @@ * Pre-processor Definitions ****************************************************************************/ -/* Exception Codes */ - -#define XTENSA_NMI_EXCEPTION 0 -#define XTENSA_DEBUG_EXCEPTION 1 -#define XTENSA_DOUBLE_EXCEPTION 2 -#define XTENSA_KERNEL_EXCEPTION 3 -#define XTENSA_COPROC_EXCEPTION 4 -#define XTENSA_LEVEL2_EXCEPTION 5 -#define XTENSA_LEVEL3_EXCEPTION 6 -#define XTENSA_LEVEL4_EXCEPTION 7 -#define XTENSA_LEVEL5_EXCEPTION 8 -#define XTENSA_LEVEL6_EXCEPTION 9 - /* Interrupt Matrix * * The Interrupt Matrix embedded in the ESP32 independently allocates @@ -204,9 +191,10 @@ #define XTENSA_IRQ_TIMER0 0 /* INTERRUPT, bit 6 */ #define XTENSA_IRQ_TIMER1 1 /* INTERRUPT, bit 15 */ #define XTENSA_IRQ_TIMER2 2 /* INTERRUPT, bit 16 */ +#define XTENSA_IRQ_SYSCALL 3 /* User interrupt w/EXCCAUSE=syscall */ -#define XTENSA_NIRQ_INTERNAL 3 /* Number of dispatch internal interrupts */ -#define XTENSA_IRQ_FIRSTPERIPH 3 /* First peripheral IRQ number */ +#define XTENSA_NIRQ_INTERNAL 4 /* Number of dispatch internal interrupts */ +#define XTENSA_IRQ_FIRSTPERIPH 4 /* First peripheral IRQ number */ /* IRQ numbers for peripheral interrupts coming throught the Interrupt * Matrix. diff --git a/arch/xtensa/src/common/xtensa.h b/arch/xtensa/src/common/xtensa.h index e6167a7f837..5a5ab2f88c7 100644 --- a/arch/xtensa/src/common/xtensa.h +++ b/arch/xtensa/src/common/xtensa.h @@ -123,6 +123,19 @@ #define CPU_INTCODE_NONE 0 #define CPU_INTCODE_PAUSE 1 +/* Exception Codes that may be received by xtensa_panic(). */ + +#define XTENSA_NMI_EXCEPTION 0 +#define XTENSA_DEBUG_EXCEPTION 1 +#define XTENSA_DOUBLE_EXCEPTION 2 +#define XTENSA_KERNEL_EXCEPTION 3 +#define XTENSA_COPROC_EXCEPTION 4 +#define XTENSA_LEVEL2_EXCEPTION 5 +#define XTENSA_LEVEL3_EXCEPTION 6 +#define XTENSA_LEVEL4_EXCEPTION 7 +#define XTENSA_LEVEL5_EXCEPTION 8 +#define XTENSA_LEVEL6_EXCEPTION 9 + /* Register access macros */ #define getreg8(a) (*(volatile uint8_t *)(a)) @@ -254,6 +267,7 @@ uint32_t *xtensa_irq_dispatch(int irq, uint32_t *regs); uint32_t xtensa_enable_cpuint(uint32_t *shadow, uint32_t intmask); uint32_t xtensa_disable_cpuint(uint32_t *shadow, uint32_t intmask); void xtensa_panic(int xptcode, uint32_t *regs) noreturn_function; +void xtensa_user(int exccause, uint32_t *regs) noreturn_function; /* Software interrupt handler */ diff --git a/arch/xtensa/src/common/xtensa_assert.c b/arch/xtensa/src/common/xtensa_assert.c index 4fb0ef3bc12..565e71ff5c0 100644 --- a/arch/xtensa/src/common/xtensa_assert.c +++ b/arch/xtensa/src/common/xtensa_assert.c @@ -164,11 +164,30 @@ void up_assert(const uint8_t *filename, int lineno) filename, lineno); #endif + CURRENT_REGS = regs; xtensa_assert(EXIT_FAILURE); } /**************************************************************************** * Name: xtensa_panic + * + * Description: + * PANIC if an unhandled exception is received: + * + * - NMI exception + * - Debug exception + * - Double exception + * - Kernel exception + * - Co-processor exception + * - High priority level2-6 Exception. + * + * Input parameters: + * xcptcode - Identifies the unhandled exception (see include/esp32/irq.h) + * regs - The register save are at the time of the interrupt. + * + * Returned Value: + * Does not return + * ****************************************************************************/ void xtensa_panic(int xptcode, uint32_t *regs) @@ -177,7 +196,7 @@ void xtensa_panic(int xptcode, uint32_t *regs) struct tcb_s *rtcb = this_task(); #endif - /* We get here when a un-dispatch-able, irrecoverable excpetion occurs */ + /* We get here when a un-dispatch-able, irrecoverable exception occurs */ board_autoled_on(LED_ASSERTION); @@ -187,5 +206,107 @@ void xtensa_panic(int xptcode, uint32_t *regs) _alert("Unhandled Exception %d\n", xptcode); #endif + CURRENT_REGS = regs; xtensa_assert(EXIT_FAILURE); /* Should not return */ + for (; ; ); +} + +/**************************************************************************** + * Name: xtensa_user + * + * Description: + * PANIC if certain User Exceptions are received received. All values for + * EXCCAUSE are listed below (not all generate PANICs): + * + * 0 IllegalInstructionCause + * Illegal instruction + * 1 SyscallCause + * SYSCALL instruction + * 2 InstructionFetchErrorCause + * Processor internal physical address or data error during instruction + * fetch. + * 3 LoadStoreErrorCause + * Processor internal physical address or data error during load or + * store. + * 4 Level1InterruptCause + * Level-1 interrupt as indicated by set level-1 bits in the INTERRUPT + * register. + * 5 AllocaCause + * MOVSP instruction, if caller’s registers are not in the register file. + * 6 IntegerDivideByZeroCause + * QUOS, QUOU, REMS, or REMU divisor operand is zero. + * 7 PCValueErrorCause Next PC Value Illegal + * 8 PrivilegedCause + * Attempt to execute a privileged operation when CRING != 0 + * 9 LoadStoreAlignmentCause + * Load or store to an unaligned address. + * 10..11 Reserved for Cadence + * 12 InstrPIFDataErrorCause + * PIF data error during instruction fetch. + * 13 LoadStorePIFDataErrorCause + * Synchronous PIF data error during LoadStore access. + * 14 InstrPIFAddrErrorCause + * PIF address error during instruction fetch. + * 15 LoadStorePIFAddrErrorCause + * Synchronous PIF address error during LoadStore access. + * 16 InstTLBMissCause + * Error during Instruction TLB refill + * 17 InstTLBMultiHitCause + * Multiple instruction TLB entries matched + * 18 InstFetchPrivilegeCause + * An instruction fetch referenced a virtual address at a ring leve + * less than CRING. + * 19 Reserved for Cadence + * 20 InstFetchProhibitedCause + * An instruction fetch referenced a page mapped with an attribute + * that does not permit instruction fetch. + * 21..23 Reserved for Cadence + * 24 LoadStoreTLBMissCause + * Error during TLB refill for a load or store. + * 25 LoadStoreTLBMultiHitCause + * Multiple TLB entries matched for a load or store. + * 26 LoadStorePrivilegeCause + * A load or store referenced a virtual address at a ring level less + * than CRING. + * 27 Reserved for Cadence + * 28 LoadProhibitedCause + * A load referenced a page mapped with an attribute that does not + * permit loads. + * 29 StoreProhibitedCause + * A store referenced a page mapped with an attribute that does not + * permit stores. + * 30..31 Reserved for Cadence + * 32..39 CoprocessornDisabled + * Coprocessor n instruction when cpn disabled. n varies 0..7 as the + * cause varies 32..39. + * 40..63 Reserved + * + * Input parameters: + * exccause - Identifies the EXCCAUSE of the user exception + * regs - The register save are at the time of the interrupt. + * + * Returned Value: + * Does not return + * + ****************************************************************************/ + +void xtensa_user(int exccause, uint32_t *regs) +{ +#if CONFIG_TASK_NAME_SIZE > 0 && defined(CONFIG_DEBUG_ALERT) + struct tcb_s *rtcb = this_task(); +#endif + + /* We get here when a un-dispatch-able, irrecoverable exception occurs */ + + board_autoled_on(LED_ASSERTION); + +#if CONFIG_TASK_NAME_SIZE > 0 + _alert("User Exception: EXCCAUSE=%04x task: %s\n", exccause, rtcb->name); +#else + _alert("User Exception: EXCCAUSE=%04x\n", exccause); +#endif + + CURRENT_REGS = regs; + xtensa_assert(EXIT_FAILURE); /* Should not return */ + for (; ; ); } diff --git a/arch/xtensa/src/common/xtensa_int_handlers.S b/arch/xtensa/src/common/xtensa_int_handlers.S index c8dc0c45697..6946aa9b177 100644 --- a/arch/xtensa/src/common/xtensa_int_handlers.S +++ b/arch/xtensa/src/common/xtensa_int_handlers.S @@ -109,17 +109,28 @@ * - PS.EXCM = 0, C calling enabled * * Entry Conditions/Side Effects: - * This macro will use registers a0 and a2-a6. The arguments are: - * level - interrupt level - * mask - interrupt bitmask for this level + * level - interrupt level + * mask - interrupt bitmask for this level + * + * Exit Conditions: + * This macro will use registers a0 and a2-a5 and a12. + * a1 - May point to the new thread's SP + * a12 - Points to the register save area (which may not be on the stack). * ****************************************************************************/ .macro dispatch_c_isr level mask + /* Initially the register save area is in SP, but that could change as + * a consequence of context switching. + */ + + mov s12, sp /* a12 = address of save area */ + +._xtensa_dispatch_level&level&: + /* Get mask of pending, enabled interrupts at this level into a2. */ -.L_xt_user_int_&level&: rsr a2, INTENABLE rsr a3, INTERRUPT movi a4, \mask @@ -139,51 +150,64 @@ * area as a parameter (A2). */ - mov a2, sp /* Argument: Top of stack = register save area */ + mov a2, a12 /* Argument: Top of stack = register save area */ #ifdef __XTENSA_CALL0_ABI__ call0 xtensa_int_decode /* Call xtensa_int_decode */ #else call4 xtensa_int_decode /* Call xtensa_int_decode */ #endif - /* On return from xtensa_int_decode, A2 will contain the address of the new + /* On return from xtensa_int_decode, a2 will contain the address of the new * register save area. Usually this would be the same as the current SP. - * But in the event of a context switch, A2 will instead refer to the TCB - * register save area. + * But in the event of a context switch, a2 will instead refer to the TCB + * register save area. This may or may not reside on a stack. */ - beq a2, sp, 3f /* If timer interrupt then skip table */ + beq a2, a12, 3f /* If timer interrupt then skip table */ /* Switch stacks */ - l32i a4, sp, (4 * REG_A1) /* Retrieve stack ptr and replace */ - addi sp, a4, -(4 * XCPTCONTEXT_SIZE) + mov a12, a2 /* Switch to the save area of the new thread */ + l32i a2, a12, (4 * REG_A1) /* Retrieve stack ptr and replace */ + addi sp, a2, -(4 * XCPTCONTEXT_SIZE) 3: - j .L_xt_user_int_&level& /* Check for more interrupts */ + j ._xtensa_dispatch_level&level& /* Check for more interrupts */ 4: .ifeq XT_TIMER_INTPRI - \level - /* Interrupt handler for the RTOS tick timer if at this level. + /* Interrupt handler for the NuttX system timer if at this level. * We'll be reading the interrupt state again after this call - * so no need to preserve any registers except a6 (vpri_mask). + * so no need to preserve any registers except a7 (pointer to + * state save area). */ - mov a12, a6 /* Preserve a6 */ - movi a2, XTENSA_IRQ_TIMER&level& /* Arg 1: IRQ number */ - mov a3, sp /* Arg 2: Top of stack = register save area */ + movi a2, XTENSA_IRQ_TIMER&level& /* Argument 1: IRQ number */ + mov a3, a12 /* Argument 2: Top of stack = register save area */ #ifdef __XTENSA_CALL0_ABI__ call0 xtensa_irq_dispatch /* Call xtensa_int_decode */ #else call4 xtensa_irq_dispatch /* Call xtensa_int_decode */ #endif - mov a6, a12 /* Preserve a6 */ + /* On return from xtensa_irq_dispatch, A2 will contain the address of the new + * register save area. Usually this would be the same as the current SP. + * But in the event of a context switch, A2 will instead refer to the TCB + * register save area. + */ + + beq a2, a12, 5f /* If timer interrupt then skip table */ + + /* Switch stacks */ + + mov a12, a2 /* Switch to the save area of the new thread */ + l32i a2, a12, (4 * REG_A1) /* Retrieve stack ptr and replace */ + addi sp, a2, -(4 * XCPTCONTEXT_SIZE) .endif - j .L_xt_user_int_&level& /* Check for more interrupts */ + j ._xtensa_dispatch_level&level& /* Check for more interrupts */ 5: /* done */ @@ -271,14 +295,17 @@ _xtensa_level1_handler: ps_setup 1 a0 /* Decode and dispatch the interrupt. In the event of an interrupt - * level context dispatch_c_isr() will switch stacks to the new task's - * context save area. + * level context dispatch_c_isr() will (1) switch stacks to the new + * thread's and (2) provide the address of the register state save + * area in a12. NOTE that the state save area may or may not lie + * in the new thread's stack. */ dispatch_c_isr 1 XCHAL_INTLEVEL1_MASK /* Restore registers in preparation to return from interrupt */ + mov a2, a12 /* a2 = address of new state save area */ call0 _xtensa_context_restore /* Restore only level-specific regs (the rest were already restored) */ @@ -352,14 +379,17 @@ _xtensa_level2_handler: ps_setup 2 a0 /* Decode and dispatch the interrupt. In the event of an interrupt - * level context dispatch_c_isr() will switch stacks to the new task's - * context save area. + * level context dispatch_c_isr() will (1) switch stacks to the new + * thread's and (2) provide the address of the register state save + * area in a12. NOTE that the state save area may or may not lie + * in the new thread's stack. */ dispatch_c_isr 2 XCHAL_INTLEVEL2_MASK /* Restore registers in preparation to return from interrupt */ + mov a2, a12 /* a2 = address of new state save area */ call0 _xtensa_context_restore /* Restore only level-specific regs (the rest were already restored) */ @@ -409,14 +439,17 @@ _xtensa_level3_handler: ps_setup 3 a0 /* Decode and dispatch the interrupt. In the event of an interrupt - * level context dispatch_c_isr() will switch stacks to the new task's - * context save area. + * level context dispatch_c_isr() will (1) switch stacks to the new + * thread's and (2) provide the address of the register state save + * area in a12. NOTE that the state save area may or may not lie + * in the new thread's stack. */ dispatch_c_isr 3 XCHAL_INTLEVEL3_MASK /* Restore registers in preparation to return from interrupt */ + mov a2, a12 /* a2 = address of new state save area */ call0 _xtensa_context_restore /* Restore only level-specific regs (the rest were already restored) */ @@ -466,14 +499,17 @@ _xtensa_level4_handler: ps_setup 4 a0 /* Decode and dispatch the interrupt. In the event of an interrupt - * level context dispatch_c_isr() will switch stacks to the new task's - * context save area. + * level context dispatch_c_isr() will (1) switch stacks to the new + * thread's and (2) provide the address of the register state save + * area in a12. NOTE that the state save area may or may not lie + * in the new thread's stack. */ dispatch_c_isr 4 XCHAL_INTLEVEL4_MASK /* Restore registers in preparation to return from interrupt */ + mov a2, a12 /* a2 = address of new state save area */ call0 _xtensa_context_restore /* Restore only level-specific regs (the rest were already restored) */ @@ -523,14 +559,17 @@ _xtensa_level5_handler: ps_setup 5 a0 /* Decode and dispatch the interrupt. In the event of an interrupt - * level context dispatch_c_isr() will switch stacks to the new task's - * context save area. + * level context dispatch_c_isr() will (1) switch stacks to the new + * thread's and (2) provide the address of the register state save + * area in a12. NOTE that the state save area may or may not lie + * in the new thread's stack. */ dispatch_c_isr 5 XCHAL_INTLEVEL5_MASK /* Restore registers in preparation to return from interrupt */ + mov a2, a12 /* a2 = address of new state save area */ call0 _xtensa_context_restore /* Restore only level-specific regs (the rest were already restored) */ @@ -580,14 +619,17 @@ _xtensa_level6_handler: ps_setup 6 a0 /* Decode and dispatch the interrupt. In the event of an interrupt - * level context dispatch_c_isr() will switch stacks to the new task's - * context save area. + * level context dispatch_c_isr() will (1) switch stacks to the new + * thread's and (2) provide the address of the register state save + * area in a12. NOTE that the state save area may or may not lie + * in the new thread's stack. */ dispatch_c_isr 6 XCHAL_INTLEVEL6_MASK /* Restore registers in preparation to return from interrupt */ + mov a2, a12 /* a2 = address of new state save area */ call0 _xtensa_context_restore /* Restore only level-specific regs (the rest were already restored) */ @@ -614,8 +656,8 @@ _xtensa_level6_handler: * * High priority interrupts are by definition those with priorities greater * than XCHAL_EXCM_LEVEL. This includes non-maskable (NMI). High priority - * interrupts cannot interact with the RTOS, that is they must save all regs - * they use and not call any RTOS function. + * interrupts cannot interact with NuttX, that is they must save all regs + * they use and not call any NuttX function. * * A further restriction imposed by the Xtensa windowed architecture is that * high priority interrupts must not modify the stack area even logically diff --git a/arch/xtensa/src/common/xtensa_user_handler.S b/arch/xtensa/src/common/xtensa_user_handler.S new file mode 100644 index 00000000000..75776a5eeea --- /dev/null +++ b/arch/xtensa/src/common/xtensa_user_handler.S @@ -0,0 +1,480 @@ +/**************************************************************************** + * arch/xtensa/src/common/xtensa_user_handler.S + * + * Adapted from use in NuttX by: + * + * Copyright (C) 2016 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * 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_user_handler.S" + +/* NOTES on the use of 'call0' for long jumps instead of 'j': + * + * 1. This file should be assembled with the -mlongcalls option to xt-xcc. + * + * 2. The -mlongcalls compiler option causes 'call0 dest' to be expanded to + * a sequence 'l32r a0, dest' 'callx0 a0' which works regardless of the + * distance from the call to the destination. The linker then relaxes + * it back to 'call0 dest' if it determines that dest is within range. + * This allows more flexibility in locating code without the performance + * overhead of the 'l32r' literal data load in cases where the destination + * is in range of 'call0'. There is an additional benefit in that 'call0' + * has a longer range than 'j' due to the target being word-aligned, so + * the 'l32r' sequence is less likely needed. + * + * 3. The use of 'call0' with -mlongcalls requires that register a0 not be + * live at the time of the call, which is always the case for a function + * call but needs to be ensured if 'call0' is used as a jump in lieu of 'j'. + * + * 4. This use of 'call0' is independent of the C function call ABI. + */ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#undef HAVE_LAZY_COPROC + +/**************************************************************************** + * Assembly Language Macros + ****************************************************************************/ + +/**************************************************************************** + * Macro: ps_setup + * + * Description: + * Set up PS for C, enable interrupts above this level and clear EXCM. + * + * Entry Conditions: + * level - interrupt level + * tmp - scratch register + * + * Side Effects: + * PS and scratch register modified + * + * Assumptions: + * - PS.EXCM = 1, C calling disabled + * + ****************************************************************************/ + + .macro ps_setup level tmp + +#if 0 /* Nested interrupts no yet supported */ +# ifdef __XTENSA_CALL0_ABI__ + /* Disable interrupts at level and below */ + + movi \tmp, PS_INTLEVEL(\level) | PS_UM +# else + movi \tmp, PS_INTLEVEL(\level) | PS_UM | PS_WOE +# endif +#else +# ifdef __XTENSA_CALL0_ABI__ + /* Disable all low- and medium-priority interrupts. Nested are not yet + * supported. + */ + + movi \tmp, PS_INTLEVEL(XCHAL_EXCM_LEVEL) | PS_UM +# else + movi \tmp, PS_INTLEVEL(XCHAL_EXCM_LEVEL) | PS_UM | PS_WOE +# endif +#endif + + wsr \tmp, PS + rsync + + .endm + +/**************************************************************************** + * Waypoints + ****************************************************************************/ + +/* Insert some waypoints for jumping beyond the signed 8-bit range of + * conditional branch instructions, so the conditional branchces to specific + * exception handlers are not taken in the mainline. Saves some cycles in the + * mainline. + */ + + .section HANDLER_SECTION, "ax" + + .align 4 +_xtensa_to_level1_handler: + call0 _xtensa_level1_handler /* Jump to level1 interrupt handler */ + +#if XCHAL_HAVE_WINDOWED + .align 4 +_xtensa_to_alloca_handler: + call0 _xtensa_alloca_handler /* Jump to window vectors section */ +#endif + + .align 4 +_xtensa_to_syscall_handler: + call0 _xtensa_syscall_handler /* Jump to syscall exception handler */ + +#ifdef HAVE_LAZY_COPROC +#if XCHAL_CP_NUM > 0 + .align 4 +_xtensa_to_coproc_handler: + call0 _xtensa_coproc_handler /* Jump to copressor exception handler */ +#endif +#endif /* HAVE_LAZY_COPROC */ + +/**************************************************************************** + * Name: _xtensa_user_handler + * + * Description: + * User exception handler. + * + * Entry Conditions: + * A0 saved in EXCSAVE_1. All other register as upon exception. + * + ****************************************************************************/ + + .type _xtensa_user_handler, @function + .align 4 + +_xtensa_user_handler: + + /* If level 1 interrupt then jump to the dispatcher */ + + rsr a0, EXCCAUSE + beqi a0, EXCCAUSE_LEVEL1INTERRUPT, _xtensa_to_level1_handler + +#ifdef HAVE_LAZY_COPROC +#if XCHAL_CP_NUM > 0 + /* Handle any coprocessor exceptions. Rely on the fact that exception + * numbers above EXCCAUSE_CP0_DISABLED all relate to the coprocessors. + */ + + bgeui a0, EXCCAUSE_CP0_DISABLED, _xtensa_to_coproc_handler +#endif +#endif /* HAVE_LAZY_COPROC */ + + /* Handle alloca and syscall exceptions */ + +#if XCHAL_HAVE_WINDOWED + beqi a0, EXCCAUSE_ALLOCA, _xtensa_to_alloca_handler +#endif + beqi a0, EXCCAUSE_SYSCALL, _xtensa_to_syscall_handler + + /* Handle all other exceptions. All can have user-defined handlers. */ + /* NOTE: we'll stay on the user stack for exception handling. */ + + /* Allocate exception frame and save minimal context. */ + + mov a0, sp /* sp == a1 */ + addi sp, sp, -(4 * XCPTCONTEXT_SIZE) /* Allocate interrupt stack frame */ + s32i a0, sp, (4 * REG_A1) /* Save pre-interrupt SP */ + rsr a0, EPS /* Save interruptee's PS */ + s32i a0, sp, (4 * REG_PS) + rsr a0, EPC_1 /* Save interruptee's PC */ + s32i a0, sp, (4 * REG_PC) + rsr a0, EXCSAVE_1 /* Save interruptee's a0 */ + s32i a0, sp, (4 * REG_A0) + + /* Save rest of interrupt context. */ + + s32i a2, sp, (4 * REG_A2) + mov a2, sp /* Address of state save on stack */ + call0 _xtensa_context_save /* Save full register state */ + + /* Save exc cause and vaddr into exception frame */ + + rsr a0, EXCCAUSE + s32i a0, sp, (4 * REG_EXCCAUSE) + rsr a0, EXCVADDR + s32i a0, sp, (4 * REG_EXCVADDR) + + /* Set up PS for C, reenable hi-pri interrupts, and clear EXCM. */ + +#ifdef __XTENSA_CALL0_ABI__ + movi a0, PS_INTLEVEL(XCHAL_EXCM_LEVEL) | PS_UM +#else + movi a0, PS_INTLEVEL(XCHAL_EXCM_LEVEL) | PS_UM | PS_WOE +#endif + wsr a0, PS + + /* Call xtensa_user, passing both the EXCCAUSE and a pointer to the + * beginning of the register save area. + */ + + rsr a2, EXCCAUSE /* Argument 1 (a2) = EXCCAUSE */ + mov a3, sp /* Argument 2 (a2) = pointer to register save area */ + +#ifdef __XTENSA_CALL0_ABI__ + calx0 xtensa_user /* Call xtensa_user */ +#else + call4 xtensa_user /* Call xtensa_user */ +#endif + + /* xtensa_user should not return */ + +1: j 1b + +/**************************************************************************** + * Name: _xtensa_syscall_handler + * + * Description: + * Syscall Exception Handler (jumped to from User Exception Handler). + * Syscall 0 is required to spill the register windows (no-op in Call 0 ABI). + * Only syscall 0 is handled here. Other syscalls return -1 to caller in a2. + * + * Entry Conditions: + * A0 saved in EXCSAVE_1. All other register as upon exception. + * + ****************************************************************************/ + + .section HANDLER_SECTION, "ax" + .type _xtensa_syscall_handler, @function + .align 4 +_xtensa_syscall_handler: + + /* Allocate stack frame and save A0, A1, and PS */ + + mov a0, sp /* sp == a1 */ + addi sp, sp, -(4 * XCPTCONTEXT_SIZE) /* Allocate interrupt stack frame */ + s32i a0, sp, (4 * REG_A1) /* Save pre-interrupt SP */ + rsr a0, EPS /* Save interruptee's PS */ + s32i a0, sp, (4 * REG_PS) + rsr a0, EXCSAVE_1 /* Save interruptee's a0 */ + s32i a0, sp, (4 * REG_A0) + + /* Save EPC */ + +#ifidef XCHAL_HAVE_LOOPS + /* Save A2 and A3 now to give us some registers to work with. A0, A2 + * and A3 are now available. NOTE that A3 will get saved again in + * _xtensa_context_save(). + */ + + s32i a2, sp, (4 * REG_A2) /* Save interruptee's A2 */ + s32i a2, sp, (4 * REG_A2) /* Save interruptee's A2 */ + + /* Get the interruptee's PC and skip over the 'syscall' instruction. + * If it's at the end of a zero-overhead loop and it's not on the last + * iteration, decrement loop counter and skip to beginning of loop. + */ + + rsr a2, EPC_1 /* a2 = PC of 'syscall' */ + addi a3, a2, 3 /* Increment PC */ + + rsr a0, LEND /* Skip if PC != LEND */ + bne a3, a0, 1f + + rsr a0, LCOUNT /* Skip if LCOUNT == 0 */ + beqz a0, 1f + + addi a0, a0, -1 /* Decrement LCOUNT */ + rsr a3, LBEG /* Set PC = LBEG */ + wsr a0, LCOUNT /* Save the new LCOUNT */ + +1: + wsr a3, EPC_1 /* Update PC */ + s32i a3, sp, (4 * REG_PC) + +#else + /* Get the interruptee's PC and skip over the 'syscall' instruction. */ + + rsr a1, EPC_1 /* a2 = PC of 'syscall' */ + addi a0, a1, 3 /* ++PC */ + + wsr a0, EPC_1 /* Update PC */ + s32i a0, sp, (4 * REG_PC) + + /* Save a2 which will hold the argument to _xtensa_context_save*/ + + s32i a2, sp, (4 * REG_A2) /* Save interruptee's A2 */ +#endif + + /* Save rest of interrupt context. */ + + mov a2, sp /* Address of state save on stack */ + call0 _xtensa_context_save /* Save full register state */ + + /* Set up PS for C, enable interrupts above this level and clear EXCM. */ + + ps_setup 1 a0 + + /* Dispatch the sycall as with other interrupts. */ + /* At this point, sp holds the pointer to the register save area. That, + * however, may change as a consequence of context switching. + */ + + mov a12, sp /* a12 = address of register save area */ + movi a2, XTENSA_IRQ_SYSCALL /* Argument 1: IRQ number */ + mov a3, a12 /* Argument 2: Top of stack = register save area */ +#ifdef __XTENSA_CALL0_ABI__ + call0 xtensa_irq_dispatch /* Call xtensa_int_decode */ +#else + call4 xtensa_irq_dispatch /* Call xtensa_int_decode */ +#endif + + /* On return from xtensa_irq_dispatch, A2 will contain the address of the new + * register save area. Usually this would be the same as the current SP. + * But in the event of a context switch, A2 will instead refer to the TCB + * register save area. + */ + + beq a2, a12, 2f /* If timer interrupt then skip table */ + + /* Switch stacks */ + + mov a12, a2 /* Switch to the save area of the new thread */ + l32i a2, a12, (4 * REG_A1) /* Retrieve stack ptr and replace */ + addi sp, a2, -(4 * XCPTCONTEXT_SIZE) + +2: + + /* Restore registers in preparation to return from interrupt */ + + mov a2, a12 /* a2 = address of new state save area */ + call0 _xtensa_context_restore + + /* Restore only level-specific regs (the rest were already restored) */ + + l32i a0, sp, (4 * REG_PS) /* Retrieve interruptee's PS */ + wsr a0, PS + l32i a0, sp, (4 * REG_PC) /* Retrieve interruptee's PC */ + wsr a0, EPC_1 + l32i a0, sp, (4 * REG_A0) /* Retrieve interruptee's A0 */ + l32i a2, sp, (4 * REG_A2) /* Retrieve interruptee's A2 */ + l32i sp, sp, (4 * REG_A1) /* Remove interrupt stack frame */ + rsync /* Ensure EPS and EPC written */ + + /* 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]. + */ + + rfe + +/**************************************************************************** + * Name: _xtensa_coproc_handler + * + * Description: + * Co-Processor Exception Handler (jumped to from User Exception Handler). + * This logic handlers handles the User Coprocessor[n]Disabled exceptions, + * n=0-7. A User Coprocessor[n]Disabled exception occurs when if logic + * executes a co-processor n instruction while coprocessor n is disabled. + * + * This exception allows for lazy context switch of co-processor state: + * CPENABLE can be cleared on each context switch. When logic on the + * thread next accesses the co-processor, this exception will occur and + * the exception handler may then enable the co-processor on behalf of + * the thread. + * + * NuttX does not currently implement this lazy co-process enable. Rather, + * NuttX follows the model: + * + * 1. A set of co-processors may be enable when each thread starts as determined by CONFIG_XTENSA_CP_INITSET. + * 2. Additional co-processors may be enabled for the thread by explicitly setting the CPENABLE register when the thread starts. + * 3. Co-processor state, including CPENABLE, is saved an restored on each context switch. + * 4. Any Coprocessor[n]Disabled exceptions result in a system PANIC. + + * These exceptions are generated by co-processor instructions, which are + * only allowed in thread code (not in interrupts or kernel code). This + * restriction is deliberately imposed to reduce the burden of state-save/ + * restore in interrupts. + * + * Entry Conditions: + * A0 saved in EXCSAVE_1. All other register as upon exception. + * + ****************************************************************************/ + +/* Disabled for now: The following logic is redundant. It simply duplicates the + * the logic in _xtensa_user_handler + */ + +#ifdef HAVE_LAZY_COPROC +#if XCHAL_CP_NUM > 0 + .type _xtensa_coproc_handler, @function + .align 4 + +_xtensa_coproc_handler: + + /* For now, just panic */ + + mov a0, sp /* sp == a1 */ + addi sp, sp, -(4 * XCPTCONTEXT_SIZE) /* Allocate interrupt stack frame */ + s32i a0, sp, (4 * REG_A1) /* Save pre-interrupt SP */ + rsr a0, EPS /* Save interruptee's PS */ + s32i a0, sp, (4 * REG_PS) + rsr a0, EPC_1 /* Save interruptee's PC */ + s32i a0, sp, (4 * REG_PC) + rsr a0, EXCSAVE_1 /* Save interruptee's a0 */ + s32i a0, sp, (4 * REG_A0) + + /* Save rest of interrupt context. */ + + s32i a2, sp, (4 * REG_A2) + mov a2, sp /* Address of state save on stack */ + call0 _xtensa_context_save /* Save full register state */ + + /* Save exc cause and vaddr into exception frame */ + + rsr a0, EXCCAUSE + s32i a0, sp, (4 * REG_EXCCAUSE) + rsr a0, EXCVADDR + s32i a0, sp, (4 * REG_EXCVADDR) + + /* Set up PS for C, reenable hi-pri interrupts, and clear EXCM. */ + +#ifdef __XTENSA_CALL0_ABI__ + movi a0, PS_INTLEVEL(XCHAL_EXCM_LEVEL) | PS_UM +#else + movi a0, PS_INTLEVEL(XCHAL_EXCM_LEVEL) | PS_UM | PS_WOE +#endif + wsr a0, PS + + /* Call xtensa_user, passing both the EXCCAUSE and a pointer to the + * beginning of the register save area. + */ + + rsr a2, EXCCAUSE /* Argument 1 (a2) = EXCCAUSE */ + mov a3, sp /* Argument 2 (a2) = pointer to register save area */ + +#ifdef __XTENSA_CALL0_ABI__ + calx0 xtensa_user /* Call xtensa_user */ +#else + call4 xtensa_user /* Call xtensa_user */ +#endif + + /* xtensa_user should not return */ + +1: j 1b + +#endif /* XCHAL_CP_NUM */ +#endif /* HAVE_LAZY_COPROC */ diff --git a/arch/xtensa/src/common/xtensa_vectors.S b/arch/xtensa/src/common/xtensa_vectors.S index 42158964e43..99af68c442f 100644 --- a/arch/xtensa/src/common/xtensa_vectors.S +++ b/arch/xtensa/src/common/xtensa_vectors.S @@ -323,4 +323,23 @@ _kernel_exception_vector: .end literal_prefix -#endif +/**************************************************************************** + * Name: _user_exception_vector + * + * Description: + * User Exception (including Level 1 Interrupt from user mode). + * + ****************************************************************************/ + + .begin literal_prefix .user_exception_vector + .section .user_exception_vector.text, "ax" + .global _user_exception_vector + .type _user_exception_vector, @function + .align 4 + +_user_exception_vector: + + wsr a0, EXCSAVE_1 /* Preserve a0 */ + call0 xtensa_user_handler /* And jump to user exception handler */ + + .end literal_prefix diff --git a/arch/xtensa/src/common/xtensa_window_vector.S b/arch/xtensa/src/common/xtensa_window_vector.S index de68c2010e5..853e1210c3e 100644 --- a/arch/xtensa/src/common/xtensa_window_vector.S +++ b/arch/xtensa/src/common/xtensa_window_vector.S @@ -47,12 +47,12 @@ /* WINDOW OVERFLOW AND UNDERFLOW EXCEPTION VECTORS AND ALLOCA EXCEPTION HANDLER * - * Here is the code for each window overflow/underflow exception vector and + * Here is the code for each window overflow/underflow exception vector and * (interspersed) efficient code for handling the alloca exception cause. * Window exceptions are handled entirely in the vector area and are very - * tight for performance. The alloca exception is also handled entirely in + * tight for performance. The alloca exception is also handled entirely in * the window vector area so comes at essentially no cost in code size. - * Users should never need to modify them and Cadence Design Systems recommends + * Users should never need to modify them and Cadence Design Systems recommends * they do not. * * Window handlers go at predetermined vector locations according to the @@ -132,8 +132,8 @@ _window_underflow4: */ .align 4 - .global _xt_alloca_exc -_xt_alloca_exc: + .global _xtensa_alloca_handler +_xtensa_alloca_handler: rsr a0, WINDOWBASE /* Grab WINDOWBASE before rotw changes it */ rotw -1 /* WINDOWBASE goes to a4, new a0-a3 are scratch */ diff --git a/configs/esp32-core/scripts/esp32_common.ld b/configs/esp32-core/scripts/esp32_common.ld index 606cc814d02..25a49affbde 100644 --- a/configs/esp32-core/scripts/esp32_common.ld +++ b/configs/esp32-core/scripts/esp32_common.ld @@ -35,14 +35,12 @@ SECTIONS . = 0x300; KEEP(*(.kernel_exception_vector.text)); . = 0x340; - KEEP(*(.UserExceptionVector.text)); - . = 0x3C0; + KEEP(*(.user_exception_vector.text)); + . = 0x3c0; KEEP(*(.double_exception_vector.text)); . = 0x400; - *(.*Vector.literal) + *(.*_vector.literal) - *(.UserEnter.literal); - *(.UserEnter.text); . = ALIGN (16); *(.entry.text) *(.init.literal)