|  | /* | 
|  | * Copyright (c) 2019 Intel Corporation. | 
|  | * | 
|  | * SPDX-License-Identifier: Apache-2.0 | 
|  | */ | 
|  |  | 
|  | #include <zephyr/kernel.h> | 
|  |  | 
|  | #include <kernel_internal.h> | 
|  | #include <zephyr/kernel_structs.h> | 
|  | #include <zephyr/sys/__assert.h> | 
|  | #include <zephyr/arch/cpu.h> | 
|  | #include <zephyr/logging/log_ctrl.h> | 
|  | #include <zephyr/logging/log.h> | 
|  | #include <zephyr/fatal.h> | 
|  | #include <zephyr/debug/coredump.h> | 
|  |  | 
|  | LOG_MODULE_DECLARE(os, CONFIG_KERNEL_LOG_LEVEL); | 
|  |  | 
|  | /* LCOV_EXCL_START */ | 
|  | FUNC_NORETURN __weak void arch_system_halt(unsigned int reason) | 
|  | { | 
|  | ARG_UNUSED(reason); | 
|  |  | 
|  | /* TODO: What's the best way to totally halt the system if SMP | 
|  | * is enabled? | 
|  | */ | 
|  |  | 
|  | (void)arch_irq_lock(); | 
|  | for (;;) { | 
|  | /* Spin endlessly */ | 
|  | } | 
|  | } | 
|  | /* LCOV_EXCL_STOP */ | 
|  |  | 
|  | /* LCOV_EXCL_START */ | 
|  | __weak void k_sys_fatal_error_handler(unsigned int reason, | 
|  | const z_arch_esf_t *esf) | 
|  | { | 
|  | ARG_UNUSED(esf); | 
|  |  | 
|  | LOG_PANIC(); | 
|  | LOG_ERR("Halting system"); | 
|  | arch_system_halt(reason); | 
|  | CODE_UNREACHABLE; | 
|  | } | 
|  | /* LCOV_EXCL_STOP */ | 
|  |  | 
|  | static const char *thread_name_get(struct k_thread *thread) | 
|  | { | 
|  | const char *thread_name = (thread != NULL) ? k_thread_name_get(thread) : NULL; | 
|  |  | 
|  | if ((thread_name == NULL) || (thread_name[0] == '\0')) { | 
|  | thread_name = "unknown"; | 
|  | } | 
|  |  | 
|  | return thread_name; | 
|  | } | 
|  |  | 
|  | static const char *reason_to_str(unsigned int reason) | 
|  | { | 
|  | switch (reason) { | 
|  | case K_ERR_CPU_EXCEPTION: | 
|  | return "CPU exception"; | 
|  | case K_ERR_SPURIOUS_IRQ: | 
|  | return "Unhandled interrupt"; | 
|  | case K_ERR_STACK_CHK_FAIL: | 
|  | return "Stack overflow"; | 
|  | case K_ERR_KERNEL_OOPS: | 
|  | return "Kernel oops"; | 
|  | case K_ERR_KERNEL_PANIC: | 
|  | return "Kernel panic"; | 
|  | default: | 
|  | return "Unknown error"; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* LCOV_EXCL_START */ | 
|  | FUNC_NORETURN void k_fatal_halt(unsigned int reason) | 
|  | { | 
|  | arch_system_halt(reason); | 
|  | } | 
|  | /* LCOV_EXCL_STOP */ | 
|  |  | 
|  | void z_fatal_error(unsigned int reason, const z_arch_esf_t *esf) | 
|  | { | 
|  | /* We can't allow this code to be preempted, but don't need to | 
|  | * synchronize between CPUs, so an arch-layer lock is | 
|  | * appropriate. | 
|  | */ | 
|  | unsigned int key = arch_irq_lock(); | 
|  | struct k_thread *thread = IS_ENABLED(CONFIG_MULTITHREADING) ? | 
|  | _current : NULL; | 
|  |  | 
|  | /* twister looks for the "ZEPHYR FATAL ERROR" string, don't | 
|  | * change it without also updating twister | 
|  | */ | 
|  | LOG_ERR(">>> ZEPHYR FATAL ERROR %d: %s on CPU %d", reason, | 
|  | reason_to_str(reason), _current_cpu->id); | 
|  |  | 
|  | /* FIXME: This doesn't seem to work as expected on all arches. | 
|  | * Need a reliable way to determine whether the fault happened when | 
|  | * an IRQ or exception was being handled, or thread context. | 
|  | * | 
|  | * See #17656 | 
|  | */ | 
|  | #if defined(CONFIG_ARCH_HAS_NESTED_EXCEPTION_DETECTION) | 
|  | if ((esf != NULL) && arch_is_in_nested_exception(esf)) { | 
|  | LOG_ERR("Fault during interrupt handling\n"); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | LOG_ERR("Current thread: %p (%s)", thread, | 
|  | thread_name_get(thread)); | 
|  |  | 
|  | coredump(reason, esf, thread); | 
|  |  | 
|  | k_sys_fatal_error_handler(reason, esf); | 
|  |  | 
|  | /* If the system fatal error handler returns, then kill the faulting | 
|  | * thread; a policy decision was made not to hang the system. | 
|  | * | 
|  | * Policy for fatal errors in ISRs: unconditionally panic. | 
|  | * | 
|  | * There is one exception to this policy: a stack sentinel | 
|  | * check may be performed (on behalf of the current thread) | 
|  | * during ISR exit, but in this case the thread should be | 
|  | * aborted. | 
|  | * | 
|  | * Note that k_thread_abort() returns on some architectures but | 
|  | * not others; e.g. on ARC, x86_64, Xtensa with ASM2, ARM | 
|  | */ | 
|  | if (!IS_ENABLED(CONFIG_TEST)) { | 
|  | __ASSERT(reason != K_ERR_KERNEL_PANIC, | 
|  | "Attempted to recover from a kernel panic condition"); | 
|  | /* FIXME: #17656 */ | 
|  | #if defined(CONFIG_ARCH_HAS_NESTED_EXCEPTION_DETECTION) | 
|  | if ((esf != NULL) && arch_is_in_nested_exception(esf)) { | 
|  | #if defined(CONFIG_STACK_SENTINEL) | 
|  | if (reason != K_ERR_STACK_CHK_FAIL) { | 
|  | __ASSERT(0, | 
|  | "Attempted to recover from a fatal error in ISR"); | 
|  | } | 
|  | #endif /* CONFIG_STACK_SENTINEL */ | 
|  | } | 
|  | #endif /* CONFIG_ARCH_HAS_NESTED_EXCEPTION_DETECTION */ | 
|  | } else { | 
|  | /* Test mode */ | 
|  | #if defined(CONFIG_ARCH_HAS_NESTED_EXCEPTION_DETECTION) | 
|  | if ((esf != NULL) && arch_is_in_nested_exception(esf)) { | 
|  | /* Abort the thread only on STACK Sentinel check fail. */ | 
|  | #if defined(CONFIG_STACK_SENTINEL) | 
|  | if (reason != K_ERR_STACK_CHK_FAIL) { | 
|  | arch_irq_unlock(key); | 
|  | return; | 
|  | } | 
|  | #else | 
|  | arch_irq_unlock(key); | 
|  | return; | 
|  | #endif /* CONFIG_STACK_SENTINEL */ | 
|  | } else { | 
|  | /* Abort the thread only if the fault is not due to | 
|  | * a spurious ISR handler triggered. | 
|  | */ | 
|  | if (reason == K_ERR_SPURIOUS_IRQ) { | 
|  | arch_irq_unlock(key); | 
|  | return; | 
|  | } | 
|  | } | 
|  | #endif /*CONFIG_ARCH_HAS_NESTED_EXCEPTION_DETECTION */ | 
|  | } | 
|  |  | 
|  | arch_irq_unlock(key); | 
|  |  | 
|  | if (IS_ENABLED(CONFIG_MULTITHREADING)) { | 
|  | k_thread_abort(thread); | 
|  | } | 
|  | } |