nios2: add base exception handling code

Change-Id: I56b0ec1a3576a77ca7bd6f2c0217de8053406927
Signed-off-by: Andrew Boie <andrew.p.boie@intel.com>
This commit is contained in:
Andrew Boie 2016-06-21 12:15:33 -07:00 committed by Inaky Perez-Gonzalez
commit e2ff2fdd91
5 changed files with 420 additions and 24 deletions

View file

@ -3,7 +3,7 @@ ccflags-y +=-I$(srctree)/arch/$(ARCH)/include
ccflags-y += -I$(srctree)/kernel/microkernel/include ccflags-y += -I$(srctree)/kernel/microkernel/include
obj-y += reset.o irq_manage.o fatal.o swap.o thread.o \ obj-y += reset.o irq_manage.o fatal.o swap.o thread.o \
cpu_idle.o irq_offload.o prep_c.o crt0.o cpu_idle.o irq_offload.o prep_c.o crt0.o \
exception.o
obj-$(CONFIG_IRQ_OFFLOAD) += irq_offload.o obj-$(CONFIG_IRQ_OFFLOAD) += irq_offload.o

244
arch/nios2/core/exception.S Normal file
View file

@ -0,0 +1,244 @@
/*
* Copyright (c) 2016 Intel Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define _ASMLANGUAGE
#include <arch/nios2/asm.h>
#include <nano_private.h>
/* exports */
GTEXT(_exception)
GTEXT(_exception_exit)
GTEXT(_exception_enter_fault)
/* import */
GTEXT(_Fault)
GTEXT(_Swap)
GTEXT(_exception_try_muldiv)
/* Allows use of r1/at register, otherwise reserved for assembler use */
.set noat
/* Placed into special 'exception' section so that the linker can put this code
* at ALT_CPU_EXCEPTION_ADDR defined in system.h
*
* This is the common entry point for processor exceptions and interrupts from
* the Internal Interrupt Controller (IIC).
*
* If the External (EIC) controller is in use, then we will never get here on
* behalf of an interrupt, instead the EIC driver will have set up a vector
* table and the processor will jump directly into the appropriate table
* entry.
*/
SECTION_FUNC(exception.entry, _exception)
/* Reserve thread stack space for saving context */
addi sp, sp, -76
/* Preserve all caller-saved registers onto the thread's stack */
stw ra, 0(sp)
/* Gap here for muldiv handler to store zero register */
stw r1, 8(sp)
stw r2, 12(sp)
stw r3, 16(sp)
stw r4, 20(sp)
stw r5, 24(sp)
stw r6, 28(sp)
stw r7, 32(sp)
stw r8, 36(sp)
stw r9, 40(sp)
stw r10, 44(sp)
stw r11, 48(sp)
stw r12, 52(sp)
stw r13, 56(sp)
stw r14, 60(sp)
stw r15, 64(sp)
/* Store value of estatus control register */
rdctl et, estatus
stw et, 68(sp)
/* ea-4 is the address of the instruction when the exception happened,
* put this in the stack frame as well
*/
addi r15, ea, -4
stw r15, 72(sp)
/* Figure out whether we are here because of an interrupt or an
* exception. If an interrupt, switch stacks and enter IRQ handling
* code. If an exception, remain on current stack and enter exception
* handing code. From the CPU manual, ipending must be nonzero and
* estatis.PIE must be enabled for this to be considered an interrupt.
*
* Stick ipending in r4 since it will be an arg for _enter_irq
*/
rdctl r4, ipending
beq r4, zero, not_interrupt
/* We stashed estatus in et earlier */
andi r15, et, 1
beq r15, zero, not_interrupt
/* If we get here, this is an interrupt */
/* Grab a reference to _nanokernel in r10 so we can determine the
* current irq stack pointer
*/
movhi r10, %hi(_nanokernel)
ori r10, r10, %lo(_nanokernel)
/* Stash a copy of thread's sp in r12 so that we can put it on the IRQ
* stack
*/
mov r12, sp
/* Switch to interrupt stack */
ldw sp, __tNANO_irq_sp_OFFSET(r10)
/* Store thread stack pointer onto IRQ stack */
addi sp, sp, -4
stw r12, 0(sp)
BRANCH_LABEL(on_irq_stack)
#if 0 /* TODO enable interrupt handling code */
/* Enter C interrupt handling code. Value of ipending will be the
* function parameter since we put it in r4
*/
call _enter_irq
#endif
/* Interrupt handler finished and the interrupt should be serviced
* now, the appropriate bits in ipending should be cleared */
/* Get a reference to _nanokernel again in r10 */
movhi r10, %hi(_nanokernel)
ori r10, r10, %lo(_nanokernel)
/* Determine whether the execution of the ISR requires a context
* switch. If the interrupted thread is PREEMPTIBLE (a task) and
* _nanokernel.fiber is non-NULL, a _Swap() needs to occur.
*/
/* Check (_nanokernel.current->flags & PREEMPTIBLE), if not
* goto no_reschedule
*/
ldw r11, __tNANO_current_OFFSET(r10)
ldw r12, __tTCS_flags_OFFSET(r11)
movi r13, PREEMPTIBLE
and r12, r13, r12
beq r12, zero, no_reschedule
/* Check _nanokernel.fiber != NULL, if NULL goto no_reschedule */
ldw r11, __tNANO_fiber_OFFSET(r10)
beq r11, zero, no_reschedule
/*
* A context reschedule is required: keep the volatile registers of
* the interrupted thread on the context's stack. Utilize
* the existing _Swap() primitive to save the remaining
* thread's registers (including floating point) and perform
* a switch to the new thread.
*/
/* We put the thread stack pointer on top of the IRQ stack before
* we switched stacks. Restore it to go back to thread stack
*/
ldw sp, 0(sp)
/* Argument to Swap() is estatus since that's the state of the
* status register before the exception happened. When coming
* out of the context switch we need this info to restore
* IRQ lock state. We put this value in et earlier.
*/
mov r4, et
call _Swap
jmpi _exception_exit
BRANCH_LABEL(not_interrupt)
#if 0 /* TODO enable multiply / divide exception handing */
/* Since this wasn't an interrupt we're not going to restart the
* faulting instruction. If it's an unimplemented math instruction,
* the muldiv code will handle it, else we just give up and _Fault.
*
* We earlier put ea - 4 in the stack frame, replace it with just ea
*/
stw ea, 72(sp)
/* Could be an unimplemented instruction we have to emulate.
* Smaller Nios II cores don't have multiply or divide instructions.
* This code comes back to either _exception_enter_fault or
* _exception_exit
*/
jmpi _exception_try_muldiv
#endif
SECTION_FUNC(exception.entry, _exception_enter_fault)
/* If we get here, the exception wasn't in interrupt or an
* unimplemented math instruction. Let _Fault() handle it in
* C domain
*/
ldw r4, 0(sp)
call _Fault
jmpi _exception_exit
BRANCH_LABEL(no_reschedule)
/* We put the thread stack pointer on top of the IRQ stack before
* we switched stacks. Restore it to go back to thread stack
*/
ldw sp, 0(sp)
/* Fall through */
SECTION_FUNC(exception.entry, _exception_exit)
/* We are on the thread stack. Restore all saved registers
* and return to the interrupted context */
/* Return address from the exception */
ldw ea, 72(sp)
/* Restore estatus
* XXX is this right??? */
ldw r5, 68(sp)
wrctl estatus, r5
/* Restore caller-saved registers */
ldw ra, 0(sp)
ldw r1, 8(sp)
ldw r2, 12(sp)
ldw r3, 16(sp)
ldw r4, 20(sp)
ldw r5, 24(sp)
ldw r6, 28(sp)
ldw r7, 32(sp)
ldw r8, 36(sp)
ldw r9, 40(sp)
ldw r10, 44(sp)
ldw r11, 48(sp)
ldw r12, 52(sp)
ldw r13, 56(sp)
ldw r14, 60(sp)
ldw r15, 64(sp)
/* Put the stack pointer back where it was when we entered
* exception state
*/
addi sp, sp, 76
/* All done, copy estatus into status and transfer to ea */
eret

View file

@ -19,9 +19,26 @@
#include <nano_private.h> #include <nano_private.h>
#include <misc/printk.h> #include <misc/printk.h>
/* TODO initialize with sentinel values */ const NANO_ESF _default_esf = {
const NANO_ESF _default_esf; 0xdeadbaad,
0xdeadbaad,
0xdeadbaad,
0xdeadbaad,
0xdeadbaad,
0xdeadbaad,
0xdeadbaad,
0xdeadbaad,
0xdeadbaad,
0xdeadbaad,
0xdeadbaad,
0xdeadbaad,
0xdeadbaad,
0xdeadbaad,
0xdeadbaad,
0xdeadbaad,
0xdeadbaad,
0xdeadbaad
};
/** /**
* *
@ -42,22 +59,73 @@ const NANO_ESF _default_esf;
FUNC_NORETURN void _NanoFatalErrorHandler(unsigned int reason, FUNC_NORETURN void _NanoFatalErrorHandler(unsigned int reason,
const NANO_ESF *esf) const NANO_ESF *esf)
{ {
/* STUB TODO: dump out reason for the error and any interesting #ifdef CONFIG_PRINTK
* info in esf switch (reason) {
*/ case _NANO_ERR_CPU_EXCEPTION:
case _NANO_ERR_SPURIOUS_INT:
break;
case _NANO_ERR_INVALID_TASK_EXIT:
printk("***** Invalid Exit Software Error! *****\n");
break;
case _NANO_ERR_ALLOCATION_FAIL:
printk("**** Kernel Allocation Failure! ****\n");
break;
default:
printk("**** Unknown Fatal Error %d! ****\n", reason);
break;
}
printk("Current thread ID: 0x%x\n"
"Faulting instruction: 0x%x\n"
" r1: 0x%x r2: 0x%x r3: 0x%x r4: 0x%x\n"
" r5: 0x%x r6: 0x%x r7: 0x%x r8: 0x%x\n"
" r9: 0x%x r10: 0x%x r11: 0x%x r12: 0x%x\n"
"r13: 0x%x r14: 0x%x r15: 0x%x ra: 0x%x\n"
"estatus: %x\n", sys_thread_self_get(), esf->instr,
esf->r1, esf->r2, esf->r3, esf->r4,
esf->r5, esf->r6, esf->r7, esf->r8,
esf->r9, esf->r10, esf->r11, esf->r12,
esf->r13, esf->r14, esf->r15, esf->ra,
esf->estatus);
#endif
_SysFatalErrorHandler(reason, esf); _SysFatalErrorHandler(reason, esf);
} }
FUNC_NORETURN void _Fault(void) FUNC_NORETURN void _Fault(const NANO_ESF *esf)
{ {
/* STUB TODO dump out reason for this exception */ #ifdef CONFIG_PRINTK
/* Unfortunately, completely unavailable on Nios II/e cores */
#ifdef NIOS2_HAS_EXTRA_EXCEPTION_INFO
uint32_t exc_reg, badaddr_reg, eccftl;
enum nios2_exception_cause cause;
_NanoFatalErrorHandler(_NANO_ERR_HW_EXCEPTION, &_default_esf); exc_reg = _nios2_creg_read(NIOS2_CR_EXCEPTION);
/* Bit 31 indicates potentially fatal ECC error */
eccftl = (exc_reg & NIOS2_EXCEPTION_REG_ECCFTL_MASK) != 0;
/* Bits 2-6 contain the cause code */
cause = (exc_reg & NIOS2_EXCEPTION_REG_CAUSE_MASK)
>> NIOS2_EXCEPTION_REG_CAUSE_OFST;
printk("Exception cause: 0x%x ECCFTL: %d\n", exc_reg, eccftl);
if (BIT(cause) & NIOS2_BADADDR_CAUSE_MASK) {
badaddr_reg = _nios2_creg_read(NIOS2_CR_BADADDR);
printk("Badaddr: 0x%x\n", badaddr_reg);
}
#endif /* NIOS2_HAS_EXTRA_EXCEPTION_INFO */
#endif /* CONFIG_PRINTK */
_NanoFatalErrorHandler(_NANO_ERR_CPU_EXCEPTION, esf);
} }
/** /**
* *
* @brief Fatal error handler * @brief Fatal error handler
@ -74,20 +142,42 @@ FUNC_NORETURN void _Fault(void)
* information to a persistent repository and/or rebooting the system. * information to a persistent repository and/or rebooting the system.
* *
* @param reason the fatal error reason * @param reason the fatal error reason
* @param pEsf the pointer to the exception stack frame * @param pEsf pointer to exception stack frame
* *
* @return This function does not return. * @return N/A
*/ */
FUNC_NORETURN void _SysFatalErrorHandler(unsigned int reason, FUNC_NORETURN void _SysFatalErrorHandler(unsigned int reason,
const NANO_ESF *esf) const NANO_ESF *pEsf)
{ {
/* STUB TODO try to abort task/fibers like in the x86 implementation */ nano_context_type_t curCtx = sys_execution_context_type_get();
printk("Fatal error!\n");
while (1) { ARG_UNUSED(reason);
/* whee! */ ARG_UNUSED(pEsf);
if ((curCtx != NANO_CTX_ISR) && !_is_thread_essential()) {
#ifdef CONFIG_MICROKERNEL
if (curCtx == NANO_CTX_TASK) {
extern FUNC_NORETURN void _TaskAbort(void);
printk("Fatal task error! Aborting task.\n");
_TaskAbort();
} else
#endif /* CONFIG_MICROKERNEL */
{
printk("Fatal fiber error! Aborting fiber.\n");
fiber_abort();
}
CODE_UNREACHABLE;
} }
printk("Fatal fault in %s ! Spinning...\n",
curCtx == NANO_CTX_ISR
? "ISR"
: curCtx == NANO_CTX_FIBER ? "essential fiber"
: "essential task");
#ifdef NIOS2_HAS_DEBUG_STUB
_nios2_break();
#endif
for (;;)
; /* Spin forever */
} }

View file

@ -154,6 +154,7 @@ struct s_NANO {
typedef struct s_NANO tNANO; typedef struct s_NANO tNANO;
extern tNANO _nanokernel; extern tNANO _nanokernel;
extern char _interrupt_stack[CONFIG_ISR_STACK_SIZE];
/* Arch-specific nanokernel APIs */ /* Arch-specific nanokernel APIs */
@ -162,7 +163,8 @@ void nano_cpu_atomic_idle(unsigned int key);
static ALWAYS_INLINE void nanoArchInit(void) static ALWAYS_INLINE void nanoArchInit(void)
{ {
/* STUB */ _nanokernel.irq_sp = (char *)STACK_ROUND_DOWN(_interrupt_stack +
CONFIG_ISR_STACK_SIZE);
} }
static ALWAYS_INLINE void fiberRtnValueSet(struct tcs *fiber, static ALWAYS_INLINE void fiberRtnValueSet(struct tcs *fiber,

View file

@ -34,10 +34,11 @@ extern "C" {
#define STACK_ALIGN 4 #define STACK_ALIGN 4
#define _NANO_ERR_HW_EXCEPTION (0) /* MPU/Bus/Usage fault */ #define _NANO_ERR_CPU_EXCEPTION (0) /* Any unhandled exception */
#define _NANO_ERR_INVALID_TASK_EXIT (1) /* Invalid task exit */ #define _NANO_ERR_INVALID_TASK_EXIT (1) /* Invalid task exit */
#define _NANO_ERR_STACK_CHK_FAIL (2) /* Stack corruption detected */ #define _NANO_ERR_STACK_CHK_FAIL (2) /* Stack corruption detected */
#define _NANO_ERR_ALLOCATION_FAIL (3) /* Kernel Allocation Failure */ #define _NANO_ERR_ALLOCATION_FAIL (3) /* Kernel Allocation Failure */
#define _NANO_ERR_SPURIOUS_INT (4) /* Spurious interrupt */
#ifndef _ASMLANGUAGE #ifndef _ASMLANGUAGE
#include <stdint.h> #include <stdint.h>
@ -93,8 +94,25 @@ void _arch_irq_enable(unsigned int irq);
void _arch_irq_disable(unsigned int irq); void _arch_irq_disable(unsigned int irq);
struct __esf { struct __esf {
/* XXX - not defined yet */ uint32_t ra; /* return address r31 */
uint32_t placeholder; uint32_t r0; /* zero register */
uint32_t r1; /* at */
uint32_t r2; /* return value */
uint32_t r3; /* return value */
uint32_t r4; /* register args */
uint32_t r5; /* register args */
uint32_t r6; /* register args */
uint32_t r7; /* register args */
uint32_t r8; /* Caller-saved general purpose */
uint32_t r9; /* Caller-saved general purpose */
uint32_t r10; /* Caller-saved general purpose */
uint32_t r11; /* Caller-saved general purpose */
uint32_t r12; /* Caller-saved general purpose */
uint32_t r13; /* Caller-saved general purpose */
uint32_t r14; /* Caller-saved general purpose */
uint32_t r15; /* Caller-saved general purpose */
uint32_t estatus;
uint32_t instr; /* Instruction being executed when exc occurred */
}; };
typedef struct __esf NANO_ESF; typedef struct __esf NANO_ESF;
@ -103,6 +121,48 @@ extern const NANO_ESF _default_esf;
FUNC_NORETURN void _SysFatalErrorHandler(unsigned int reason, FUNC_NORETURN void _SysFatalErrorHandler(unsigned int reason,
const NANO_ESF *esf); const NANO_ESF *esf);
enum nios2_exception_cause {
NIOS2_EXCEPTION_UNKNOWN = -1,
NIOS2_EXCEPTION_RESET = 0,
NIOS2_EXCEPTION_CPU_ONLY_RESET_REQUEST = 1,
NIOS2_EXCEPTION_INTERRUPT = 2,
NIOS2_EXCEPTION_TRAP_INST = 3,
NIOS2_EXCEPTION_UNIMPLEMENTED_INST = 4,
NIOS2_EXCEPTION_ILLEGAL_INST = 5,
NIOS2_EXCEPTION_MISALIGNED_DATA_ADDR = 6,
NIOS2_EXCEPTION_MISALIGNED_TARGET_PC = 7,
NIOS2_EXCEPTION_DIVISION_ERROR = 8,
NIOS2_EXCEPTION_SUPERVISOR_ONLY_INST_ADDR = 9,
NIOS2_EXCEPTION_SUPERVISOR_ONLY_INST = 10,
NIOS2_EXCEPTION_SUPERVISOR_ONLY_DATA_ADDR = 11,
NIOS2_EXCEPTION_TLB_MISS = 12,
NIOS2_EXCEPTION_TLB_EXECUTE_PERM_VIOLATION = 13,
NIOS2_EXCEPTION_TLB_READ_PERM_VIOLATION = 14,
NIOS2_EXCEPTION_TLB_WRITE_PERM_VIOLATION = 15,
NIOS2_EXCEPTION_MPU_INST_REGION_VIOLATION = 16,
NIOS2_EXCEPTION_MPU_DATA_REGION_VIOLATION = 17,
NIOS2_EXCEPTION_ECC_TLB_ERR = 18,
NIOS2_EXCEPTION_ECC_FETCH_ERR = 19,
NIOS2_EXCEPTION_ECC_REGISTER_FILE_ERR = 20,
NIOS2_EXCEPTION_ECC_DATA_ERR = 21,
NIOS2_EXCEPTION_ECC_DATA_CACHE_WRITEBACK_ERR = 22
};
/* Bitfield indicating which exception cause codes report a valid
* badaddr register. NIOS2_EXCEPTION_TLB_MISS and NIOS2_EXCEPTION_ECC_TLB_ERR
* are deliberately not included here, you need to check if TLBMISC.D=1
*/
#define NIOS2_BADADDR_CAUSE_MASK \
(BIT(NIOS2_EXCEPTION_SUPERVISOR_ONLY_DATA_ADDR) | \
BIT(NIOS2_EXCEPTION_MISALIGNED_DATA_ADDR) | \
BIT(NIOS2_EXCEPTION_MISALIGNED_TARGET_PC) | \
BIT(NIOS2_EXCEPTION_TLB_READ_PERM_VIOLATION) | \
BIT(NIOS2_EXCEPTION_TLB_WRITE_PERM_VIOLATION) | \
BIT(NIOS2_EXCEPTION_MPU_DATA_REGION_VIOLATION) | \
BIT(NIOS2_EXCEPTION_ECC_DATA_ERR))
#endif /* _ASMLANGUAGE */ #endif /* _ASMLANGUAGE */
#ifdef __cplusplus #ifdef __cplusplus