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
16u64 __stack_chk_guard = 0;
17
18[[noreturn]] void __stack_chk_fail(void)
19{
20 mos_panic("Stack smashing detected!");
21}
22
23void __stack_chk_fail_local(void)
24{
25 __stack_chk_fail();
26}
27
28static kmsg_handler_t *kwarn_handler = NULL;
29static bool poweroff_on_panic = false;
30
31MOS_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
37void kwarn_handler_set(kmsg_handler_t *handler)
38{
39 pr_warn("installing a new warning handler...");
40 kwarn_handler = handler;
41}
42
43void 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
51extern const panic_point_t __MOS_PANIC_LIST_START[], __MOS_PANIC_LIST_END[];
52extern const panic_hook_t __MOS_PANIC_HOOKS_START[], __MOS_PANIC_HOOKS_END[];
53
54static 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
68void 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
79void 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
164void 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