Andrew Boie | 71ce8ce | 2019-07-11 14:18:28 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (c) 2019 Intel Corporation. |
| 3 | * |
| 4 | * SPDX-License-Identifier: Apache-2.0 |
| 5 | */ |
| 6 | |
| 7 | #include <kernel.h> |
| 8 | #include <kernel_internal.h> |
| 9 | #include <sys/printk.h> |
| 10 | #include <sys/__assert.h> |
| 11 | #include <arch/cpu.h> |
| 12 | #include <logging/log_ctrl.h> |
Andrew Boie | 8a9e8e0 | 2019-07-15 22:03:56 -0700 | [diff] [blame] | 13 | #include <logging/log.h> |
Andrew Boie | 71ce8ce | 2019-07-11 14:18:28 -0700 | [diff] [blame] | 14 | #include <fatal.h> |
| 15 | |
Andrew Boie | 8a9e8e0 | 2019-07-15 22:03:56 -0700 | [diff] [blame] | 16 | LOG_MODULE_DECLARE(os); |
| 17 | |
Andrew Boie | 71ce8ce | 2019-07-11 14:18:28 -0700 | [diff] [blame] | 18 | /* LCOV_EXCL_START */ |
| 19 | FUNC_NORETURN __weak void z_arch_system_halt(unsigned int reason) |
| 20 | { |
| 21 | ARG_UNUSED(reason); |
| 22 | |
| 23 | /* TODO: What's the best way to totally halt the system if SMP |
| 24 | * is enabled? |
| 25 | */ |
| 26 | |
| 27 | (void)z_arch_irq_lock(); |
| 28 | for (;;) { |
| 29 | k_cpu_idle(); |
| 30 | } |
| 31 | } |
| 32 | /* LCOV_EXCL_STOP */ |
| 33 | |
| 34 | /* LCOV_EXCL_START */ |
| 35 | __weak void k_sys_fatal_error_handler(unsigned int reason, |
Andrew Boie | 96571a8 | 2019-07-16 15:21:19 -0700 | [diff] [blame] | 36 | const z_arch_esf_t *esf) |
Andrew Boie | 71ce8ce | 2019-07-11 14:18:28 -0700 | [diff] [blame] | 37 | { |
| 38 | ARG_UNUSED(esf); |
| 39 | |
| 40 | LOG_PANIC(); |
Andrew Boie | 8a9e8e0 | 2019-07-15 22:03:56 -0700 | [diff] [blame] | 41 | z_fatal_print("Halting system"); |
Andrew Boie | 71ce8ce | 2019-07-11 14:18:28 -0700 | [diff] [blame] | 42 | z_arch_system_halt(reason); |
| 43 | CODE_UNREACHABLE; |
| 44 | } |
| 45 | /* LCOV_EXCL_STOP */ |
| 46 | |
Andrew Boie | 8a9e8e0 | 2019-07-15 22:03:56 -0700 | [diff] [blame] | 47 | #if defined(CONFIG_LOG) || defined(CONFIG_PRINTK) |
Andrew Boie | 71ce8ce | 2019-07-11 14:18:28 -0700 | [diff] [blame] | 48 | static const char *thread_name_get(struct k_thread *thread) |
| 49 | { |
| 50 | const char *thread_name = k_thread_name_get(thread); |
| 51 | |
| 52 | if (thread_name == NULL || thread_name[0] == '\0') { |
| 53 | thread_name = "unknown"; |
| 54 | } |
| 55 | |
| 56 | return thread_name; |
| 57 | } |
| 58 | |
| 59 | static const char *reason_to_str(unsigned int reason) |
| 60 | { |
| 61 | switch (reason) { |
| 62 | case K_ERR_CPU_EXCEPTION: |
| 63 | return "CPU exception"; |
| 64 | case K_ERR_SPURIOUS_IRQ: |
| 65 | return "Unhandled interrupt"; |
| 66 | case K_ERR_STACK_CHK_FAIL: |
| 67 | return "Stack overflow"; |
| 68 | case K_ERR_KERNEL_OOPS: |
| 69 | return "Kernel oops"; |
| 70 | case K_ERR_KERNEL_PANIC: |
| 71 | return "Kernel panic"; |
| 72 | default: |
| 73 | return "Unknown error"; |
| 74 | } |
| 75 | } |
| 76 | |
Andrew Boie | 8a9e8e0 | 2019-07-15 22:03:56 -0700 | [diff] [blame] | 77 | void z_fatal_print(const char *fmt, ...) |
| 78 | { |
| 79 | va_list ap; |
| 80 | |
| 81 | va_start(ap, fmt); |
| 82 | if (IS_ENABLED(CONFIG_LOG)) { |
| 83 | struct log_msg_ids src_level = { |
| 84 | .level = LOG_LEVEL_ERR, |
| 85 | .source_id = LOG_CURRENT_MODULE_ID(), |
| 86 | .domain_id = CONFIG_LOG_DOMAIN_ID |
| 87 | }; |
| 88 | log_generic(src_level, fmt, ap); |
| 89 | } else { |
| 90 | printk("FATAL: "); |
| 91 | vprintk(fmt, ap); |
| 92 | printk("\n"); |
| 93 | } |
| 94 | va_end(ap); |
| 95 | } |
| 96 | #endif /* CONFIG_LOG || CONFIG_PRINTK */ |
| 97 | |
Andrew Boie | 96571a8 | 2019-07-16 15:21:19 -0700 | [diff] [blame] | 98 | void z_fatal_error(unsigned int reason, const z_arch_esf_t *esf) |
Andrew Boie | 71ce8ce | 2019-07-11 14:18:28 -0700 | [diff] [blame] | 99 | { |
| 100 | struct k_thread *thread = k_current_get(); |
| 101 | |
Andrew Boie | 81ef42d | 2019-07-16 15:29:46 -0700 | [diff] [blame^] | 102 | /* sanitycheck looks for the "ZEPHYR FATAL ERROR" string, don't |
| 103 | * change it without also updating sanitycheck |
| 104 | */ |
Andrew Boie | 8a9e8e0 | 2019-07-15 22:03:56 -0700 | [diff] [blame] | 105 | z_fatal_print(">>> ZEPHYR FATAL ERROR %d: %s", reason, |
| 106 | reason_to_str(reason)); |
Andrew Boie | 71ce8ce | 2019-07-11 14:18:28 -0700 | [diff] [blame] | 107 | |
| 108 | /* FIXME: This doesn't seem to work as expected on all arches. |
| 109 | * Need a reliable way to determine whether the fault happened when |
| 110 | * an IRQ or exception was being handled, or thread context. |
| 111 | * |
| 112 | * See #17656 |
| 113 | * |
| 114 | * if (k_is_in_isr()) { |
| 115 | * printk("Fault during interrupt handling\n"); |
| 116 | * } |
| 117 | */ |
| 118 | |
Andrew Boie | 8a9e8e0 | 2019-07-15 22:03:56 -0700 | [diff] [blame] | 119 | z_fatal_print("Current thread: %p (%s)", thread, |
| 120 | thread_name_get(thread)); |
Andrew Boie | 71ce8ce | 2019-07-11 14:18:28 -0700 | [diff] [blame] | 121 | |
| 122 | k_sys_fatal_error_handler(reason, esf); |
| 123 | |
| 124 | /* If the system fatal error handler returns, then kill the faulting |
| 125 | * thread; a policy decision was made not to hang the system. |
| 126 | * |
| 127 | * Note that k_thread_abort() returns on some architectures but |
| 128 | * not others; e.g. on ARC, x86_64, Xtensa with ASM2, ARM |
| 129 | */ |
| 130 | if (!IS_ENABLED(CONFIG_TEST)) { |
| 131 | __ASSERT(reason != K_ERR_KERNEL_PANIC, |
| 132 | "Attempted to recover from a kernel panic condition"); |
| 133 | /* FIXME: #17656 */ |
| 134 | __ASSERT(!k_is_in_isr(), |
| 135 | "Attempted to recover from a fatal error in ISR"); |
| 136 | } |
| 137 | k_thread_abort(thread); |
| 138 | } |