| /* |
| * Copyright (c) 2014 Wind River Systems, Inc. |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| /** |
| * @file |
| * @brief Common fault handler for ARCv2 |
| * |
| * Common fault handler for ARCv2 processors. |
| */ |
| |
| #include <toolchain.h> |
| #include <linker/sections.h> |
| #include <inttypes.h> |
| |
| #include <kernel.h> |
| #include <kernel_structs.h> |
| #include <misc/printk.h> |
| #include <exc_handle.h> |
| #include <logging/log_ctrl.h> |
| |
| u32_t arc_exc_saved_sp; |
| |
| #ifdef CONFIG_USERSPACE |
| Z_EXC_DECLARE(z_arch_user_string_nlen); |
| |
| static const struct z_exc_handle exceptions[] = { |
| Z_EXC_HANDLE(z_arch_user_string_nlen) |
| }; |
| #endif |
| |
| #if defined(CONFIG_MPU_STACK_GUARD) |
| |
| #define IS_MPU_GUARD_VIOLATION(guard_start, fault_addr, stack_ptr) \ |
| ((fault_addr >= guard_start) && \ |
| (fault_addr < (guard_start + STACK_GUARD_SIZE)) && \ |
| (stack_ptr <= (guard_start + STACK_GUARD_SIZE))) |
| |
| /** |
| * @brief Assess occurrence of current thread's stack corruption |
| * |
| * This function performs an assessment whether a memory fault (on a |
| * given memory address) is the result of stack memory corruption of |
| * the current thread. |
| * |
| * Thread stack corruption for supervisor threads or user threads in |
| * privilege mode (when User Space is supported) is reported upon an |
| * attempt to access the stack guard area (if MPU Stack Guard feature |
| * is supported). Additionally the current thread stack pointer |
| * must be pointing inside or below the guard area. |
| * |
| * Thread stack corruption for user threads in user mode is reported, |
| * if the current stack pointer is pointing below the start of the current |
| * thread's stack. |
| * |
| * Notes: |
| * - we assume a fully descending stack, |
| * - we assume a stacking error has occurred, |
| * - the function shall be called when handling MPU privilege violation |
| * |
| * If stack corruption is detected, the function returns the lowest |
| * allowed address where the Stack Pointer can safely point to, to |
| * prevent from errors when un-stacking the corrupted stack frame |
| * upon exception return. |
| * |
| * @param fault_addr memory address on which memory access violation |
| * has been reported. |
| * @param sp stack pointer when exception comes out |
| * |
| * @return The lowest allowed stack frame pointer, if error is a |
| * thread stack corruption, otherwise return 0. |
| */ |
| static u32_t z_check_thread_stack_fail(const u32_t fault_addr, u32_t sp) |
| { |
| const struct k_thread *thread = _current; |
| |
| if (!thread) { |
| return 0; |
| } |
| #if defined(CONFIG_USERSPACE) |
| if (thread->arch.priv_stack_start) { |
| /* User thread */ |
| if (z_arc_v2_aux_reg_read(_ARC_V2_ERSTATUS) |
| & _ARC_V2_STATUS32_U) { |
| /* Thread's user stack corruption */ |
| #ifdef CONFIG_ARC_HAS_SECURE |
| sp = z_arc_v2_aux_reg_read(_ARC_V2_SEC_U_SP); |
| #else |
| sp = z_arc_v2_aux_reg_read(_ARC_V2_USER_SP); |
| #endif |
| if (sp <= (u32_t)thread->stack_obj) { |
| return (u32_t)thread->stack_obj; |
| } |
| } else { |
| /* User thread in privilege mode */ |
| if (IS_MPU_GUARD_VIOLATION( |
| thread->arch.priv_stack_start - STACK_GUARD_SIZE, |
| fault_addr, sp)) { |
| /* Thread's privilege stack corruption */ |
| return thread->arch.priv_stack_start; |
| } |
| } |
| } else { |
| /* Supervisor thread */ |
| if (IS_MPU_GUARD_VIOLATION((u32_t)thread->stack_obj, |
| fault_addr, sp)) { |
| /* Supervisor thread stack corruption */ |
| return (u32_t)thread->stack_obj + STACK_GUARD_SIZE; |
| } |
| } |
| #else /* CONFIG_USERSPACE */ |
| if (IS_MPU_GUARD_VIOLATION(thread->stack_info.start, |
| fault_addr, sp)) { |
| /* Thread stack corruption */ |
| return thread->stack_info.start + STACK_GUARD_SIZE; |
| } |
| #endif /* CONFIG_USERSPACE */ |
| |
| return 0; |
| } |
| |
| #endif |
| |
| /* |
| * @brief Fault handler |
| * |
| * This routine is called when fatal error conditions are detected by hardware |
| * and is responsible only for reporting the error. Once reported, it then |
| * invokes the user provided routine z_SysFatalErrorHandler() which is |
| * responsible for implementing the error handling policy. |
| */ |
| void _Fault(NANO_ESF *esf) |
| { |
| u32_t vector, code, parameter; |
| u32_t exc_addr = z_arc_v2_aux_reg_read(_ARC_V2_EFA); |
| u32_t ecr = z_arc_v2_aux_reg_read(_ARC_V2_ECR); |
| |
| LOG_PANIC(); |
| |
| #ifdef CONFIG_USERSPACE |
| for (int i = 0; i < ARRAY_SIZE(exceptions); i++) { |
| u32_t start = (u32_t)exceptions[i].start; |
| u32_t end = (u32_t)exceptions[i].end; |
| |
| if (esf->pc >= start && esf->pc < end) { |
| esf->pc = (u32_t)(exceptions[i].fixup); |
| return; |
| } |
| } |
| #endif |
| |
| vector = Z_ARC_V2_ECR_VECTOR(ecr); |
| code = Z_ARC_V2_ECR_CODE(ecr); |
| parameter = Z_ARC_V2_ECR_PARAMETER(ecr); |
| |
| |
| /* exception raised by kernel */ |
| if (vector == 0x9 && parameter == _TRAP_S_CALL_RUNTIME_EXCEPT) { |
| z_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); |
| #ifdef CONFIG_ARC_STACK_CHECKING |
| /* Vector 6 = EV_ProV. Regardless of code, parameter 2 means stack |
| * check violation |
| * stack check and mpu violation can come out together, then |
| * parameter = 0x2 | [0x4 | 0x8 | 0x1] |
| */ |
| if (vector == 6U && parameter & 0x2) { |
| z_NanoFatalErrorHandler(_NANO_ERR_STACK_CHK_FAIL, esf); |
| return; |
| } |
| #endif |
| |
| #ifdef CONFIG_MPU_STACK_GUARD |
| if (vector == 0x6 && ((parameter == 0x4) || (parameter == 0x24))) { |
| if (z_check_thread_stack_fail(exc_addr, arc_exc_saved_sp)) { |
| z_NanoFatalErrorHandler(_NANO_ERR_STACK_CHK_FAIL, esf); |
| return; |
| } |
| } |
| #endif |
| z_NanoFatalErrorHandler(_NANO_ERR_HW_EXCEPTION, esf); |
| } |