diff --git a/arch/arc/core/fatal.c b/arch/arc/core/fatal.c index 1a8fbd6353f..3fb91560c1f 100644 --- a/arch/arc/core/fatal.c +++ b/arch/arc/core/fatal.c @@ -81,11 +81,6 @@ void _NanoFatalErrorHandler(unsigned int reason, const NANO_ESF *pEsf) _SysFatalErrorHandler(reason, pEsf); } -void _do_kernel_oops(const NANO_ESF *esf) -{ - _NanoFatalErrorHandler(esf->r0, esf); -} - FUNC_NORETURN void _arch_syscall_oops(void *ssf_ptr) { _SysFatalErrorHandler(_NANO_ERR_KERNEL_OOPS, ssf_ptr); diff --git a/arch/arc/core/fault.c b/arch/arc/core/fault.c index 78ca94c71f7..ea3e0865200 100644 --- a/arch/arc/core/fault.c +++ b/arch/arc/core/fault.c @@ -39,6 +39,13 @@ void _Fault(const NANO_ESF *esf) code = _ARC_V2_ECR_CODE(ecr); parameter = _ARC_V2_ECR_PARAMETER(ecr); + + /* exception raised by kernel */ + if (vector == 0x9 && parameter == _TRAP_S_CALL_RUNTIME_EXCEPT) { + _NanoFatalErrorHandler(esf->r0, esf); + return; + } + printk("Exception vector: 0x%x, cause code: 0x%x, parameter 0x%x\n", vector, code, parameter); printk("Address 0x%x\n", exc_addr); diff --git a/arch/arc/core/fault_s.S b/arch/arc/core/fault_s.S index 7bf2c83096c..c8034b30e7f 100644 --- a/arch/arc/core/fault_s.S +++ b/arch/arc/core/fault_s.S @@ -33,6 +33,9 @@ GTEXT(__ev_extension) GTEXT(__ev_div_zero) GTEXT(__ev_dc_error) GTEXT(__ev_maligned) +#ifdef CONFIG_IRQ_OFFLOAD +GTEXT(_irq_do_offload); +#endif GDATA(exc_nest_count) @@ -40,10 +43,8 @@ GDATA(exc_nest_count) SECTION_VAR(BSS, saved_value) .word 0 -#define EXCEPTION_STACK_SIZE 256 - -SECTION_VAR(NOINIT, _exception_stack) - .space EXCEPTION_STACK_SIZE +/* the necessary stack size for exception handling */ +#define EXCEPTION_STACK_SIZE 384 /* * @brief Fault handler installed in the fault and reserved vectors @@ -62,6 +63,7 @@ SECTION_SUBSEC_FUNC(TEXT,__fault,__ev_div_zero) SECTION_SUBSEC_FUNC(TEXT,__fault,__ev_dc_error) SECTION_SUBSEC_FUNC(TEXT,__fault,__ev_maligned) +_exc_entry: #ifdef CONFIG_ARC_STACK_CHECKING st r0, [saved_value] @@ -72,10 +74,24 @@ SECTION_SUBSEC_FUNC(TEXT,__fault,__ev_maligned) ld r0, [saved_value] #endif st sp, [saved_value] - mov_s sp, _exception_stack + /* + * re-use the top part of interrupt stack as exception + * stack. If this top part is used by interrupt handling, + * and exception is raised, then here it's guaranteed that + * exception handling has necessary stack to use + */ + mov_s sp, _interrupt_stack add sp, sp, EXCEPTION_STACK_SIZE - /* save caller saved registers */ + /* + * save caller saved registers + * this stack frame is set up in exception stack, + * not in the original sp (thread stack or interrupt stack). + * Because the exception may be raised by stack checking or + * mpu protect violation related to stack. If this stack frame + * is setup in original sp, double exception may be raised during + * _create_irq_stack_frame, which is unrecoverable. + */ _create_irq_stack_frame #ifdef CONFIG_ARC_HAS_SECURE @@ -87,8 +103,8 @@ SECTION_SUBSEC_FUNC(TEXT,__fault,__ev_maligned) lr r0,[_ARC_V2_ERET] st_s r0, [sp, ___isf_t_pc_OFFSET] /* eret into pc */ + /* sp is parameter of _Fault */ mov r0, sp - jl _Fault _exc_return: @@ -138,18 +154,13 @@ _exc_return_from_exc: rtie -#ifdef CONFIG_IRQ_OFFLOAD -GTEXT(_irq_do_offload); -#endif - - SECTION_SUBSEC_FUNC(TEXT,__fault,__ev_trap) /* get the id of trap_s */ lr ilink, [_ARC_V2_ECR] and ilink, ilink, 0x3f #ifdef CONFIG_USERSPACE cmp ilink, _TRAP_S_CALL_SYSTEM_CALL - bne _do_other_trap + bne _do_non_syscall_trap /* do sys_call */ mov ilink, _SYSCALL_LIMIT cmp r6, ilink @@ -177,27 +188,16 @@ valid_syscall_id: rtie - /* - * Before invoking exception handler, the kernel switches to an exception - * stack to save the faulting thread's registers. - * The exception is fatal and all the kernel can do is just print - * a diagnostic message and halt. - */ - -_do_other_trap: +_do_non_syscall_trap: #endif /* CONFIG_USERSPACE */ -#ifdef CONFIG_ARC_STACK_CHECKING - st r0, [saved_value] - /* disable stack checking */ - lr r0, [_ARC_V2_STATUS32] - bclr r0, r0, _ARC_V2_STATUS32_SC_BIT - kflag r0 - ld r0, [saved_value] -#endif - st sp, [saved_value] - mov_s sp, _exception_stack - add sp, sp, EXCEPTION_STACK_SIZE - +#ifdef CONFIG_IRQ_OFFLOAD + /* + * IRQ_OFFLOAD is to simulate interrupt handling through exception, + * so its entry is different with normal exception handling, it is + * handled in isr stack + */ + cmp ilink, _TRAP_S_SCALL_IRQ_OFFLOAD + bne _exc_entry /* save caller saved registers */ _create_irq_stack_frame @@ -210,21 +210,83 @@ _do_other_trap: lr r0,[_ARC_V2_ERET] st_s r0, [sp, ___isf_t_pc_OFFSET] /* eret into pc */ - mov r0, sp - - mov blink, _exc_return - - cmp ilink, _TRAP_S_CALL_RUNTIME_EXCEPT - beq _oops - -#ifdef CONFIG_IRQ_OFFLOAD - cmp ilink, _TRAP_S_SCALL_IRQ_OFFLOAD - bne _trap_fault - j _irq_do_offload +#ifdef CONFIG_ARC_STACK_CHECKING + /* disable stack checking */ + lr r0, [_ARC_V2_STATUS32] + bclr r0, r0, _ARC_V2_STATUS32_SC_BIT + kflag r0 #endif -_trap_fault: - j _Fault + ld r1, [exc_nest_count] + add r0, r1, 1 + st r0, [exc_nest_count] + cmp r1, 0 -_oops: - j _do_kernel_oops + bgt.d exc_nest_handle + mov r0, sp + + mov r1, _kernel + ld sp, [r1, _kernel_offset_to_irq_stack] +exc_nest_handle: + push_s r0 + + jl _irq_do_offload + + pop sp + + mov r1, exc_nest_count + ld r0, [r1] + sub r0, r0, 1 + cmp r0, 0 + bne.d _exc_return_from_exc + st r0, [r1] + +#ifdef CONFIG_PREEMPT_ENABLED + mov_s r1, _kernel + ld_s r2, [r1, _kernel_offset_to_current] + + /* check if the current thread needs to be rescheduled */ + ld_s r0, [r1, _kernel_offset_to_ready_q_cache] + breq r0, r2, _exc_return_from_irqoffload_trap + + _save_callee_saved_regs + + st _CAUSE_RIRQ, [r2, _thread_offset_to_relinquish_cause] + /* note: Ok to use _CAUSE_RIRQ since everything is saved */ + + ld_s r2, [r1, _kernel_offset_to_ready_q_cache] + st_s r2, [r1, _kernel_offset_to_current] + +#ifdef CONFIG_ARC_HAS_SECURE + /* + * sync up the ERSEC_STAT.ERM and SEC_STAT.IRM. + * use a fake interrupt return to simulate an exception turn. + * ERM and IRM record which mode the cpu should return, 1: secure + * 0: normal + */ + lr r3,[_ARC_V2_ERSEC_STAT] + btst r3, 31 + bset.nz r3, r3, 3 + bclr.z r3, r3, 3 + /* sflag r3 */ + /* sflag instruction is not supported in current ARC GNU */ + .long 0x00ff302f +#endif + /* clear AE bit to forget this was an exception */ + lr r3, [_ARC_V2_STATUS32] + and r3,r3,(~_ARC_V2_STATUS32_AE) + kflag r3 + /* pretend lowest priority interrupt happened to use common handler */ + lr r3, [_ARC_V2_AUX_IRQ_ACT] + or r3,r3,(1<<(CONFIG_NUM_IRQ_PRIO_LEVELS-1)) /* use lowest */ + sr r3, [_ARC_V2_AUX_IRQ_ACT] + + /* Assumption: r2 has current thread */ + b _rirq_common_interrupt_swap +#endif + +_exc_return_from_irqoffload_trap: + _pop_irq_stack_frame + rtie +#endif /* CONFIG_IRQ_OFFLOAD */ + b _exc_entry