| 1 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | |
| 3 | #include "mos/misc/power.hpp" |
| 4 | #include "mos/syslog/syslog.hpp" |
| 5 | |
| 6 | #include <mos/interrupt/ipi.hpp> |
| 7 | #include <mos/lib/structures/list.hpp> |
| 8 | #include <mos/misc/cmdline.hpp> |
| 9 | #include <mos/misc/panic.hpp> |
| 10 | #include <mos/misc/setup.hpp> |
| 11 | #include <mos/platform/platform.hpp> |
| 12 | #include <mos/syslog/printk.hpp> |
| 13 | #include <mos_stdio.hpp> |
| 14 | |
| 15 | // stack smashing protector |
| 16 | u64 __stack_chk_guard = 0; |
| 17 | |
| 18 | [[noreturn]] void __stack_chk_fail(void) |
| 19 | { |
| 20 | mos_panic("Stack smashing detected!" ); |
| 21 | } |
| 22 | |
| 23 | void __stack_chk_fail_local(void) |
| 24 | { |
| 25 | __stack_chk_fail(); |
| 26 | } |
| 27 | |
| 28 | static kmsg_handler_t *kwarn_handler = NULL; |
| 29 | static bool poweroff_on_panic = false; |
| 30 | |
| 31 | MOS_EARLY_SETUP("poweroff_on_panic" , setup_poweroff_on_panic) |
| 32 | { |
| 33 | poweroff_on_panic = cmdline_string_truthiness(arg, default_value: true); |
| 34 | return true; |
| 35 | } |
| 36 | |
| 37 | void kwarn_handler_set(kmsg_handler_t *handler) |
| 38 | { |
| 39 | pr_warn("installing a new warning handler..." ); |
| 40 | kwarn_handler = handler; |
| 41 | } |
| 42 | |
| 43 | void kwarn_handler_remove(void) |
| 44 | { |
| 45 | pr_warn("removing warning handler..." ); |
| 46 | if (!kwarn_handler) |
| 47 | mos_warn("no previous warning handler installed" ); |
| 48 | kwarn_handler = NULL; |
| 49 | } |
| 50 | |
| 51 | extern const panic_point_t __MOS_PANIC_LIST_START[], __MOS_PANIC_LIST_END[]; |
| 52 | extern const panic_hook_t __MOS_PANIC_HOOKS_START[], __MOS_PANIC_HOOKS_END[]; |
| 53 | |
| 54 | static const panic_point_t *find_panic_point(ptr_t ip) |
| 55 | { |
| 56 | const panic_point_t *point = NULL; |
| 57 | for (const panic_point_t *p = __MOS_PANIC_LIST_START; p < __MOS_PANIC_LIST_END; p++) |
| 58 | { |
| 59 | if (p->ip == ip) |
| 60 | { |
| 61 | point = p; |
| 62 | break; |
| 63 | } |
| 64 | } |
| 65 | return point; |
| 66 | } |
| 67 | |
| 68 | void try_handle_kernel_panics(ptr_t ip) |
| 69 | { |
| 70 | const panic_point_t *point = find_panic_point(ip); |
| 71 | if (!point) |
| 72 | { |
| 73 | pr_dwarn(panic, "no panic point found for " PTR_FMT, ip); |
| 74 | return; |
| 75 | } |
| 76 | handle_kernel_panic(point); |
| 77 | } |
| 78 | |
| 79 | void handle_kernel_panic(const panic_point_t *point) |
| 80 | { |
| 81 | platform_interrupt_disable(); |
| 82 | |
| 83 | if (!once()) |
| 84 | { |
| 85 | pr_fatal("recursive panic detected, aborting..." ); |
| 86 | pr_info("" ); |
| 87 | if (unlikely(poweroff_on_panic)) |
| 88 | { |
| 89 | pr_emerg("Powering off..." ); |
| 90 | power_shutdown(); |
| 91 | } |
| 92 | |
| 93 | while (true) |
| 94 | ; |
| 95 | } |
| 96 | |
| 97 | if (printk_unquiet()) |
| 98 | pr_info("quiet mode disabled" ); // was quiet |
| 99 | |
| 100 | pr_emerg("" ); |
| 101 | pr_fatal("!!!!!!!!!!!!!!!!!!!!!!!!" ); |
| 102 | pr_fatal("!!!!! KERNEL PANIC !!!!!" ); |
| 103 | pr_fatal("!!!!!!!!!!!!!!!!!!!!!!!!" ); |
| 104 | pr_emerg("" ); |
| 105 | pr_emerg("file: %s:%llu" , point->file, point->line); |
| 106 | pr_emerg("function: %s" , point->func); |
| 107 | if (point->ip) |
| 108 | pr_emerg("instruction: %ps (" PTR_FMT ")" , (void *) point->ip, point->ip); |
| 109 | else |
| 110 | pr_emerg("instruction: see backtrace" ); |
| 111 | pr_emerg("" ); |
| 112 | |
| 113 | pr_cont("\n" ); |
| 114 | |
| 115 | if (point->ip == 0) |
| 116 | { |
| 117 | // inline panic point |
| 118 | pr_emph("Current stack trace:" ); |
| 119 | platform_dump_current_stack(); |
| 120 | } |
| 121 | else |
| 122 | { |
| 123 | if (current_cpu->interrupt_regs) |
| 124 | { |
| 125 | pr_emph("Register states before interrupt:" ); |
| 126 | platform_dump_regs(current_cpu->interrupt_regs); |
| 127 | pr_cont("\n" ); |
| 128 | pr_emph("Stack trace before interrupt" ); |
| 129 | platform_dump_stack(current_cpu->interrupt_regs); |
| 130 | pr_cont("\n" ); |
| 131 | } |
| 132 | else |
| 133 | { |
| 134 | pr_emph("No interrupt context available" ); |
| 135 | } |
| 136 | } |
| 137 | |
| 138 | pr_cont("\n" ); |
| 139 | |
| 140 | for (const panic_hook_t *hook = __MOS_PANIC_HOOKS_START; hook < __MOS_PANIC_HOOKS_END; hook++) |
| 141 | { |
| 142 | if (hook->enabled && !*hook->enabled) |
| 143 | continue; |
| 144 | |
| 145 | pr_dinfo2(panic, "invoking panic hook '%s' at %ps" , hook->name, (void *) hook); |
| 146 | hook->hook(); |
| 147 | } |
| 148 | |
| 149 | ipi_send_all(type: IPI_TYPE_HALT); |
| 150 | |
| 151 | if (unlikely(poweroff_on_panic)) |
| 152 | { |
| 153 | pr_emerg("Powering off..." ); |
| 154 | power_shutdown(); |
| 155 | } |
| 156 | |
| 157 | pr_emerg("Halting..." ); |
| 158 | platform_halt_cpu(); |
| 159 | |
| 160 | while (1) |
| 161 | ; |
| 162 | } |
| 163 | |
| 164 | void mos_kwarn(const char *func, u32 line, const char *fmt, ...) |
| 165 | { |
| 166 | va_list args; |
| 167 | if (kwarn_handler) |
| 168 | { |
| 169 | va_start(args, fmt); |
| 170 | kwarn_handler(func, line, fmt, args); |
| 171 | va_end(args); |
| 172 | return; |
| 173 | } |
| 174 | |
| 175 | char message[MOS_PRINTK_BUFFER_SIZE]; |
| 176 | va_start(args, fmt); |
| 177 | vsnprintf(buf: message, MOS_PRINTK_BUFFER_SIZE, format: fmt, args); |
| 178 | va_end(args); |
| 179 | |
| 180 | lprintk(loglevel: LogLevel::WARN, format: "\n%s" , message); |
| 181 | lprintk(loglevel: LogLevel::WARN, format: " in function: %s (line %u)\n" , func, line); |
| 182 | } |
| 183 | |