From be2a801e309071cb80686fc1c90ae8087a390dbb Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Fri, 28 Oct 2016 10:33:20 -0600 Subject: [PATCH] Xtensa: Add xtensa_coproc.h --- arch/xtensa/Kconfig | 11 -- arch/xtensa/include/irq.h | 7 + arch/xtensa/include/xtensa/xtensa_coproc.h | 152 +++++++++++++++++++ arch/xtensa/src/common/xtensa_coproc.S | 14 +- arch/xtensa/src/common/xtensa_createstack.c | 63 ++++++-- arch/xtensa/src/common/xtensa_initialstate.c | 21 --- arch/xtensa/src/common/xtensa_irqdispatch.c | 10 +- 7 files changed, 219 insertions(+), 59 deletions(-) create mode 100644 arch/xtensa/include/xtensa/xtensa_coproc.h diff --git a/arch/xtensa/Kconfig b/arch/xtensa/Kconfig index e90643c82fa..a163955de3d 100644 --- a/arch/xtensa/Kconfig +++ b/arch/xtensa/Kconfig @@ -38,17 +38,6 @@ config XTENSA_CALL0_ABI The Window ABI is not supported. Only the CALL0 ABI is supported in the current implementation. -config XTENSA_CP_LAZY - bool "Lazy Co-processor Save" - default y - ---help--- - If this option is selected, then the co-processor state will be saved - only on context switches. As a consequency of this, co-processors - may NOT be used within interrupt handlers. If you must use co- - processors in interrupt handling, then disable this option. There - will be a significant interrupt latency performance impact if this - option is disabled. - config XTENSA_USE_OVLY bool default n diff --git a/arch/xtensa/include/irq.h b/arch/xtensa/include/irq.h index c973d0fe459..863b8ddc610 100644 --- a/arch/xtensa/include/irq.h +++ b/arch/xtensa/include/irq.h @@ -52,6 +52,7 @@ #include #include #include +#include /* Include architecture-specific IRQ definitions */ @@ -158,6 +159,12 @@ struct xcptcontext uint32_t regs[XCPTCONTEXT_REGS]; +#if XCHAL_CP_NUM > 0 + /* Co-processor save area */ + + struct struct xtensa_cpstate_s cpstate; +#endif + #ifdef CONFIG_LIB_SYSCALL /* The following array holds the return address and the exc_return value * needed to return from each nested system call. diff --git a/arch/xtensa/include/xtensa/xtensa_coproc.h b/arch/xtensa/include/xtensa/xtensa_coproc.h new file mode 100644 index 00000000000..70bc31e82a1 --- /dev/null +++ b/arch/xtensa/include/xtensa/xtensa_coproc.h @@ -0,0 +1,152 @@ +/**************************************************************************** + * arch/xtensa/include/xtensa/xtensa_coproc.h + * + * 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. + * + ****************************************************************************/ + +#ifndef __ARCH_XTENSA_INCLUDE_XTENSA_XTENSA_COPROC_H +#define __ARCH_XTENSA_INCLUDE_XTENSA_XTENSA_COPROC_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#if XCHAL_CP_NUM > 0 + +/* Align a value up/down to nearest n-byte boundary, where n is a power of 2. */ + +#define _CP_MASK(n) ((n) - 1) +#define _CP_ALIGNUP(n,val) (((val) + _CP_MASK(n)) & ~_CP_MASK(n)) +#define _CP_ALIGNDOWN(n,val) ((val) & ~_CP_MASK(n)) + +/* CO-PROCESSOR STATE SAVE AREA FOR A THREAD + * + * NuttX provides an area per thread to save the state of co-processors when + * that thread does not have control. Co-processors are context-switched + * lazily (on demand) only when a new thread uses a co-processor instruction, + * otherwise a thread retains ownership of the co-processor even when it + * loses control of the processor. An Xtensa co-processor exception is + * triggered when any co-processor instruction is executed by a thread that + * is not the owner, and the context switch of that co-processor is then + * peformed by the handler. Ownership represents which thread's state is + * currently in the co-processor. + * + * Co-processors may not be used by interrupt or exception handlers. If an + * co-processor instruction is executed by an interrupt or exception handler, + * the co-processor exception handler will trigger a kernel panic and freeze. + * This restriction is introduced to reduce the overhead of saving and + * restoring co-processor state (which can be quite large) and in particular + * remove that overhead from interrupt handlers. + * + * The co-processor state save area may be in any convenient per-thread + * location such as in the thread control block or above the thread stack + * area. It need not be in the interrupt stack frame since interrupts don't + * use co-processors. + * + * Along with the save area for each co-processor, two bitmasks with flags + * per co-processor (laid out as in the CPENABLE reg) help manage context- + * switching co-processors as efficiently as possible: + * + * XTENSA_CPENABLE + * The contents of a non-running thread's CPENABLE register. + * It represents the co-processors owned (and whose state is still needed) + * by the thread. When a thread is preempted, its CPENABLE is saved here. + * When a thread solicits a context-swtich, its CPENABLE is cleared - the + * compiler has saved the (caller-saved) co-proc state if it needs to. + * When a non-running thread loses ownership of a CP, its bit is cleared. + * When a thread runs, it's XTENSA_CPENABLE is loaded into the CPENABLE reg. + * Avoids co-processor exceptions when no change of ownership is needed. + * + * XTENSA_CPSTORED + * A bitmask with the same layout as CPENABLE, a bit per co-processor. + * Indicates whether the state of each co-processor is saved in the state + * save area. When a thread enters the kernel, only the state of co-procs + * still enabled in CPENABLE is saved. When the co-processor exception + * handler assigns ownership of a co-processor to a thread, it restores + * the saved state only if this bit is set, and clears this bit. + * + * XTENSA_CPCSST + * A bitmask with the same layout as CPENABLE, a bit per co-processor. + * Indicates whether callee-saved state is saved in the state save area. + * Callee-saved state is saved by itself on a solicited context switch, + * and restored when needed by the coprocessor exception handler. + * Unsolicited switches will cause the entire coprocessor to be saved + * when necessary. + * + * XTENSA_CPASA + * Pointer to the aligned save area. Allows it to be aligned more than + * the overall save area (which might only be stack-aligned or TCB-aligned). + * Especially relevant for Xtensa cores configured with a very large data + * path that requires alignment greater than 16 bytes (ABI stack alignment). + */ + +/* Offsets of each coprocessor save area within the 'aligned save area': */ + +#define XTENSA_CP0_SA 0 +#define XTENSA_CP1_SA _CP_ALIGNUP(XCHAL_CP1_SA_ALIGN, XTENSA_CP0_SA + XCHAL_CP0_SA_SIZE) +#define XTENSA_CP2_SA _CP_ALIGNUP(XCHAL_CP2_SA_ALIGN, XTENSA_CP1_SA + XCHAL_CP1_SA_SIZE) +#define XTENSA_CP3_SA _CP_ALIGNUP(XCHAL_CP3_SA_ALIGN, XTENSA_CP2_SA + XCHAL_CP2_SA_SIZE) +#define XTENSA_CP4_SA _CP_ALIGNUP(XCHAL_CP4_SA_ALIGN, XTENSA_CP3_SA + XCHAL_CP3_SA_SIZE) +#define XTENSA_CP5_SA _CP_ALIGNUP(XCHAL_CP5_SA_ALIGN, XTENSA_CP4_SA + XCHAL_CP4_SA_SIZE) +#define XTENSA_CP6_SA _CP_ALIGNUP(XCHAL_CP6_SA_ALIGN, XTENSA_CP5_SA + XCHAL_CP5_SA_SIZE) +#define XTENSA_CP7_SA _CP_ALIGNUP(XCHAL_CP7_SA_ALIGN, XTENSA_CP6_SA + XCHAL_CP6_SA_SIZE) +#define XTENSA_CP_SA_SIZE _CP_ALIGNUP(16, XTENSA_CP7_SA + XCHAL_CP7_SA_SIZE) + +/* Offsets within the overall save area: */ + +#define XTENSA_CPENABLE 0 /* (2 bytes) coprocessors active for this thread */ +#define XTENSA_CPSTORED 2 /* (2 bytes) coprocessors saved for this thread */ +#define XTENSA_CPCSST 4 /* (2 bytes) coprocessor callee-saved regs stored for this thread */ +#define XTENSA_CPASA 8 /* (4 bytes) ptr to aligned save area */ + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +#ifndef __ASSEMBLY__ + +struct xtensa_cpstate_s +{ + uint16_t cpenable; /* (2 bytes) coprocessors active for this thread */ + uint16_t cpstored; /* (2 bytes) coprocessors saved for this thread */ + uint16_t cpcsst /* (2 bytes) coprocessor callee-saved regs stored for this thread */ + uint16_t unused; /* (2 bytes) unused */ + uint32_t *cpasa; /* (4 bytes) ptr to aligned save area */ +} + +#endif /* __ASSEMBLY__ */ +#endif /* #if XCHAL_CP_NUM > 0 */ +#endif /* __ARCH_XTENSA_INCLUDE_XTENSA_XTENSA_COPROC_H */ diff --git a/arch/xtensa/src/common/xtensa_coproc.S b/arch/xtensa/src/common/xtensa_coproc.S index 18e1efda596..5b0eb0caf40 100644 --- a/arch/xtensa/src/common/xtensa_coproc.S +++ b/arch/xtensa/src/common/xtensa_coproc.S @@ -195,13 +195,15 @@ _xtensa_coproc_savestate: rsr a2, CPENABLE /* a2 = which CPs are enabled */ beqz a2, .Ldone /* Quick exit if none */ + mov a14, a0 /* Save return address */ call0 XT_RTOS_CP_STATE /* Get address of CP save area */ mov a0, a14 /* Restore return address */ beqz a15, .Ldone /* if none then nothing to do */ - s16i a2, a15, XT_CP_CS_ST /* Save mask of CPs being stored */ + + s16i a2, a15, XTENSA_CPCSST /* Save mask of CPs being stored */ movi a13, _xt_coproc_sa_offset /* Array of CP save offsets */ - l32i a15, a15, XT_CP_ASA /* a15 = base of aligned save area */ + l32i a15, a15, XTENSA_CPASA /* a15 = base of aligned save area */ #if XTENSA_CP0_SA_SIZE > 0 bbci.l a2, 0, 2f /* CP 0 not enabled */ @@ -308,11 +310,13 @@ _xtensa_coproc_restorestate: call0 XT_RTOS_CP_STATE /* Get address of CP save area */ mov a0, a14 /* Restore return address */ beqz a15, .Ldone2 /* if none then nothing to do */ - l16ui a3, a15, XT_CP_CS_ST /* a3 = which CPs have been saved */ + + l16ui a3, a15, XTENSA_CPCSST /* a3 = which CPs have been saved */ xor a3, a3, a2 /* Clear the ones being restored */ - s32i a3, a15, XT_CP_CS_ST /* Update saved CP mask */ + s32i a3, a15, XTENSA_CPCSST /* Update saved CP mask */ + movi a13, _xt_coproc_sa_offset /* Array of CP save offsets */ - l32i a15, a15, XT_CP_ASA /* a15 = base of aligned save area */ + l32i a15, a15, XTENSA_CPASA /* a15 = base of aligned save area */ #if XTENSA_CP0_SA_SIZE bbci.l a2, 0, 2f /* CP 0 not enabled */ diff --git a/arch/xtensa/src/common/xtensa_createstack.c b/arch/xtensa/src/common/xtensa_createstack.c index 5e1ca5a4474..88058eb7f11 100644 --- a/arch/xtensa/src/common/xtensa_createstack.c +++ b/arch/xtensa/src/common/xtensa_createstack.c @@ -47,6 +47,8 @@ #include #include #include + +#include #include #include "xtensa.h" @@ -57,13 +59,11 @@ /* XTENSA requires at least a 4-byte stack alignment. For floating point use, * however, the stack must be aligned to 8-byte addresses. + * + * REVIST: Is this true? Comes from ARM EABI */ -#ifdef CONFIG_LIBC_FLOATINGPOINT -# define STACK_ALIGNMENT 8 -#else -# define STACK_ALIGNMENT 4 -#endif +#define STACK_ALIGNMENT 8 /* Stack alignment macros */ @@ -114,6 +114,12 @@ int up_create_stack(FAR struct tcb_s *tcb, size_t stack_size, uint8_t ttype) { +#if XCHAL_CP_NUM > 0 + uintptr_t cpstart; + uintptr_t cpend; + size_t cpsize; +#endif + /* Is there already a stack allocated of a different size? Because of * alignment issues, stack_size might erroneously appear to be of a * different size. Fortunately, this is not a critical operation. @@ -126,6 +132,16 @@ int up_create_stack(FAR struct tcb_s *tcb, size_t stack_size, uint8_t ttype) up_release_stack(tcb, ttype); } +#if XCHAL_CP_NUM > 0 + /* Add the size of the co-processor save area to the stack allocation. + * REVISIT: This may waste memory. Increasing the caller's requested + * stack size should only be necessary if the requested size could not + * hold the co-processor save area. + */ + + stack_size += XTENSA_CP_SA_SIZE; +#endif + /* Do we need to allocate a new stack? */ if (!tcb->stack_alloc_ptr) @@ -163,15 +179,15 @@ int up_create_stack(FAR struct tcb_s *tcb, size_t stack_size, uint8_t ttype) if (tcb->stack_alloc_ptr) { - size_t top_of_stack; + uintptr_t top_of_stack; size_t size_of_stack; +#ifdef CONFIG_STACK_COLORATION /* Yes.. If stack debug is enabled, then fill the stack with a * recognizable value that we can use later to test for high * water marks. */ -#ifdef CONFIG_STACK_COLORATION memset(tcb->stack_alloc_ptr, 0xaa, stack_size); #endif @@ -181,15 +197,36 @@ int up_create_stack(FAR struct tcb_s *tcb, size_t stack_size, uint8_t ttype) * the stack are referenced as positive word offsets from sp. */ - top_of_stack = (uint32_t)tcb->stack_alloc_ptr + stack_size - 4; + top_of_stack = (uintptr_t)tcb->stack_alloc_ptr + stack_size - 4; - /* The XTENSA stack must be aligned at word (4 byte) boundaries; for - * floating point use, the stack must be aligned to 8-byte addresses. - * If necessary top_of_stack must be rounded down to the next - * boundary to meet these alignment requirements. +#if XCHAL_CP_NUM > 0 + /* Allocate the co-processor save area at the top of the (push down) + * stack. + * + * REVISIT: This is not secure. In secure built configurations it + * be more appropriate to use kmm_memalign() to allocte protected + * memory rather than using the stack. */ - top_of_stack = STACK_ALIGN_DOWN(top_of_stack); + cpstart = (uintptr_t)_CP_ALIGNDOWN(top_of_stack - XCHAL_CP1_SA_ALIGN); + top_of_stack = cpstart; + + /* Initialize the coprocessor save area (see xtensa_coproc.h) */ + + xcp->cpstate.cpenable = 0; /* No coprocessors active for this thread */ + xcp->cpstate.cpstored = 0; /* No coprocessors saved for this thread */ + xcp->cpstate.cpcsst = 0; /* No oprocessor callee-saved regs stored for this thread */ + xcp->cpstate.unused = 0; /* unused */ + xcp->cpstate.cpasa = (uint32_t *)cpstart; /* Start of aligned save area */ +#endif + + /* The XTENSA stack must be aligned. If necessary top_of_stack must be + * rounded down to the next boundary to meet this alignment requirement. + * + * NOTE: Co-processor save area not included in the size of the stack. + */ + + top_of_stack = STACK_ALIGN_DOWN(top_of_stack); size_of_stack = top_of_stack - (uint32_t)tcb->stack_alloc_ptr + 4; /* Save the adjusted stack values in the struct tcb_s */ diff --git a/arch/xtensa/src/common/xtensa_initialstate.c b/arch/xtensa/src/common/xtensa_initialstate.c index a5e76565f10..6c029a881e0 100644 --- a/arch/xtensa/src/common/xtensa_initialstate.c +++ b/arch/xtensa/src/common/xtensa_initialstate.c @@ -71,11 +71,6 @@ void up_initial_state(struct tcb_s *tcb) { struct xcptcontext *xcp = &tcb->xcp; -#if 0 /* REVISIT */ -#if XCHAL_CP_NUM > 0 - uint32_t *ptr; -#endif -#endif /* REVISIT */ /* Initialize the initial exception register context structure */ @@ -99,20 +94,4 @@ void up_initial_state(struct tcb_s *tcb) xcp->regs[REG_PS] = PS_UM | PS_EXCM | PS_WOE | PS_CALLINC(1); #endif - -#warning REVISIT co-processor support -#if 0 /* REVISIT */ -#if XCHAL_CP_NUM > 0 - /* Init the coprocessor save area (see xtensa_context.h) - * - * No access to TCB here, so derive indirectly. Stack growth is top to bottom. - * //ptr = (uint32_t *) xMPUSettings->coproc_area; - */ - - ptr = (uint32_t *)(((uint32_t)tcb->adj_stack_ptr - XT_CP_SIZE) & ~0xf); - ptr[0] = 0; - ptr[1] = 0; - ptr[2] = (((uint32_t)ptr) + 12 + XTENSA_TOTAL_SA_ALIGN - 1) & -XTENSA_TOTAL_SA_ALIGN; -#endif -#endif /* REVISIT */ } diff --git a/arch/xtensa/src/common/xtensa_irqdispatch.c b/arch/xtensa/src/common/xtensa_irqdispatch.c index 1ed07feb216..aa43b7a9a3f 100644 --- a/arch/xtensa/src/common/xtensa_irqdispatch.c +++ b/arch/xtensa/src/common/xtensa_irqdispatch.c @@ -81,12 +81,6 @@ uint32_t *xtensa_irq_dispatch(int irq, uint32_t *regs) CURRENT_REGS = regs; -#if XCHAL_CP_NUM > 0 && !defined(CONFIG_XTENSA_CP_LAZY) - /* Save the current co processor state on entry int each interrupt. */ - - esp32_coproc_savestate(tcb->xcp.cpstate); -#endif - /* Deliver the IRQ */ irq_dispatch(irq, regs); @@ -99,7 +93,6 @@ uint32_t *xtensa_irq_dispatch(int irq, uint32_t *regs) if (regs != CURRENT_REGS) { #if XCHAL_CP_NUM > 0 -#ifdef CONFIG_XTENSA_CP_LAZY /* If an interrupt level context switch has occurred, then save the * co-processor state in in the suspended thread's co-processor save * area. @@ -110,9 +103,8 @@ uint32_t *xtensa_irq_dispatch(int irq, uint32_t *regs) */ esp32_coproc_savestate(tcb->xcp.cpstate); -#endif - /* Set up the co-processor state for the to-be-started thread. + /* Then set up the co-processor state for the to-be-started thread. * * NOTE: The current thread for this CPU is the to-be-started * thread.