PatchworkOS  19e446b
A non-POSIX operating system.
Loading...
Searching...
No Matches
panic.c
Go to the documentation of this file.
1#include <kernel/cpu/irq.h>
2#include <kernel/log/panic.h>
3#include <kernel/log/screen.h>
4
5#include <kernel/cpu/cpu.h>
7#include <kernel/cpu/port.h>
8#include <kernel/cpu/regs.h>
9#include <kernel/init/init.h>
10#include <kernel/log/log.h>
11#include <kernel/mem/pmm.h>
12#include <kernel/mem/vmm.h>
14#include <kernel/proc/process.h>
15#include <kernel/sched/thread.h>
16#include <kernel/sched/timer.h>
17#include <kernel/version.h>
18
19#include <boot/boot_info.h>
20
21#include <stdarg.h>
22#include <stdint.h>
23#include <stdio.h>
24#include <string.h>
25#include <sys/fs.h>
26#include <sys/math.h>
27#include <sys/proc.h>
28
31
32static char panicBuffer[LOG_MAX_BUFFER] = {0};
33
34static atomic_uint32_t panicCpuId = ATOMIC_VAR_INIT(PANIC_NO_CPU_ID);
35
36static const char* panic_get_exception_name(uint64_t vector)
37{
38 static const char* names[] = {
39 "divide error",
40 "debug",
41 "nmi",
42 "breakpoint",
43 "overflow",
44 "bound range exceeded",
45 "invalid opcode",
46 "device not available",
47 "double fault",
48 "coprocessor segment overrun",
49 "invalid TSS",
50 "segment not present",
51 "stack fault",
52 "general protection",
53 "page fault",
54 "reserved",
55 "x87 FPU error",
56 "alignment check",
57 "machine check",
58 "SIMD exception",
59 "virtualization exception",
60 "control protection exception",
61 };
62
63 if (vector < sizeof(names) / sizeof(names[0]))
64 {
65 return names[vector];
66 }
67 return "unknown exception";
68}
69
70static void panic_registers(const interrupt_frame_t* frame)
71{
72 LOG_PANIC("rip: %04llx:0x%016llx ", frame->cs, frame->rip);
73
74 symbol_info_t symbol;
75 if (symbol_resolve_addr(&symbol, (void*)frame->rip) != ERR)
76 {
77 LOG_PANIC("<%s+0x%llx>", symbol.name, frame->rip - (uintptr_t)symbol.addr);
78 }
79 LOG_PANIC("\n");
80
81 LOG_PANIC("rsp: %04llx:0x%016llx rflags: 0x%08llx\n", frame->ss, frame->rsp, frame->rflags & 0xFFFFFFFF);
82
83 LOG_PANIC("rax: 0x%016llx rbx: 0x%016llx rcx: 0x%016llx\n", frame->rax, frame->rbx, frame->rcx);
84 LOG_PANIC("rdx: 0x%016llx rsi: 0x%016llx rdi: 0x%016llx\n", frame->rdx, frame->rsi, frame->rdi);
85 LOG_PANIC("rbp: 0x%016llx r08: 0x%016llx r09: 0x%016llx\n", frame->rbp, frame->r8, frame->r9);
86 LOG_PANIC("r10: 0x%016llx r11: 0x%016llx r12: 0x%016llx\n", frame->r10, frame->r11, frame->r12);
87 LOG_PANIC("r13: 0x%016llx r14: 0x%016llx r15: 0x%016llx\n", frame->r13, frame->r14, frame->r15);
88
92 LOG_PANIC("fsbase: 0x%016llx gsbase: 0x%016llx kgsbase: 0x%016llx\n", fsBase, gsBase, kGsBase);
93}
94
96{
97 if (addr == 0)
98 {
99 return false;
100 }
101 if (addr < VMM_USER_SPACE_MIN)
102 {
103 return false;
104 }
105 if (addr >= VMM_KERNEL_BINARY_MAX)
106 {
107 return false;
108 }
109 if (addr > VMM_USER_SPACE_MAX && addr < VMM_IDENTITY_MAPPED_MIN)
110 {
111 return false;
112 }
113 if (addr >= UINTPTR_MAX - sizeof(uint64_t) * 2)
114 {
115 return false;
116 }
117 if (addr >= VMM_KERNEL_STACKS_MAX - sizeof(uint64_t) * 4 && addr <= VMM_KERNEL_STACKS_MAX)
118 {
119 return false;
120 }
121 return true;
122}
123
125{
126 if (ptr == 0)
127 {
128 return false;
129 }
130 if (ptr & 0x7)
131 {
132 return false;
133 }
134 if (!panic_is_valid_address(ptr))
135 {
136 return false;
137 }
138 if (!panic_is_valid_address(ptr + sizeof(uintptr_t)))
139 {
140 return false;
141 }
142
143 return true;
144}
145
147{
148 uint64_t rsp = frame->rsp;
149 LOG_PANIC("stack dump (around rsp=0x%016llx):\n", rsp);
150
151 const int linesToDump = 8;
152 const int bytesPerLine = 16;
153 uint64_t startAddr = (rsp - (linesToDump / 2 - 1) * bytesPerLine) & ~(bytesPerLine - 1);
154
155 for (int i = 0; i < linesToDump; i++)
156 {
157 uint64_t lineAddr = startAddr + (i * bytesPerLine);
158
159 LOG_PANIC(" 0x%016llx: ", lineAddr);
160
161 for (int j = 0; j < bytesPerLine; j++)
162 {
163 uintptr_t currentAddr = lineAddr + j;
164 if (panic_is_valid_address(currentAddr))
165 {
166 LOG_PANIC("%02x ", *(uint8_t*)currentAddr);
167 }
168 else
169 {
170 LOG_PANIC(" ");
171 }
172 }
173
174 LOG_PANIC("|");
175
176 for (int j = 0; j < bytesPerLine; j++)
177 {
178 uintptr_t currentAddr = lineAddr + j;
179 if (panic_is_valid_address(currentAddr))
180 {
181 uint8_t c = *(uint8_t*)currentAddr;
182 LOG_PANIC("%c", (c >= 32 && c <= 126) ? c : '.');
183 }
184 else
185 {
186 LOG_PANIC(" ");
187 }
188 }
189 LOG_PANIC("|\n");
190
191 if (rsp >= lineAddr && rsp < lineAddr + bytesPerLine)
192 {
193 LOG_PANIC(" ");
194
195 uint64_t offsetInLine = rsp - lineAddr;
196 for (uint64_t k = 0; k < offsetInLine; k++)
197 {
198 LOG_PANIC(" ");
199 }
200 LOG_PANIC("^^ RSP\n");
201 }
202 }
203}
204
206{
207 if (addr >= VMM_USER_SPACE_MIN && addr < VMM_USER_SPACE_MAX)
208 {
209 LOG_PANIC(" [0x%016llx] <user space address>\n", addr);
210 return ERR;
211 }
212
213 symbol_info_t symbol;
214 if (symbol_resolve_addr(&symbol, (void*)addr) == ERR)
215 {
216 LOG_PANIC(" [0x%016llx] <unknown>\n", addr);
217 return ERR;
218 }
219
220 LOG_PANIC(" [0x%016llx] <%s+0x%llx>\n", addr, symbol.name, addr - (uintptr_t)symbol.addr);
221 return 0;
222}
223
225{
226 LOG_PANIC("stack trace:\n");
227 uintptr_t* prevFrame = NULL;
228 uint64_t depth = 0;
229
230 while (rbp != NULL && rbp != prevFrame)
231 {
232 if (depth >= PANIC_MAX_STACK_FRAMES)
233 {
234 LOG_PANIC(" ...\n");
235 break;
236 }
237
239 {
240 LOG_PANIC(" [0x%016llx] <invalid frame pointer>\n", (uintptr_t)rbp);
241 break;
242 }
243
244 uintptr_t returnAddress = rbp[1];
245 if (returnAddress == 0)
246 {
247 LOG_PANIC(" [0x%016llx] <null return address>\n", returnAddress);
248 break;
249 }
250
251 if (panic_print_trace_address(returnAddress) == ERR)
252 {
253 LOG_PANIC(" [0x%016llx] <failed to resolve>\n", returnAddress);
254 break;
255 }
256
257 prevFrame = rbp;
258 rbp = (uintptr_t*)rbp[0];
259 depth++;
260 }
261}
262
264{
265 panic_unwind_stack(__builtin_frame_address(0));
266}
267
269{
271}
272
273static void panic_print_code(const interrupt_frame_t* frame)
274{
275 if (!panic_is_valid_address(frame->rip))
276 {
277 return;
278 }
279
280 LOG_PANIC("code: ");
281 uint8_t* ip = (uint8_t*)frame->rip;
282 for (int i = 0; i < 16; i++)
283 {
284 if (panic_is_valid_address((uintptr_t)(ip + i)))
285 {
286 LOG_PANIC("%02x ", ip[i]);
287 }
288 }
289 LOG_PANIC("\n");
290}
291
292void panic(const interrupt_frame_t* frame, const char* format, ...)
293{
294 ASM("cli");
295
296 uint32_t expectedCpuId = PANIC_NO_CPU_ID;
297 if (!atomic_compare_exchange_strong(&panicCpuId, &expectedCpuId, SELF->id))
298 {
299 if (expectedCpuId == SELF->id)
300 {
301 // Print basic message for double panic on same CPU but avoid using the full panic stuff again.
302 const char* message = "!!! KERNEL DOUBLE PANIC ON SAME CPU !!!\n";
303 log_nprint(LOG_LEVEL_PANIC, message, strlen(message));
304 }
305 while (true)
306 {
307 ASM("hlt");
308 }
309 }
310
311 thread_t* currentThread = thread_current();
312 process_t* currentProcess = process_current();
313
314 errno_t err = currentThread != NULL ? currentThread->error : 0;
315
316 va_list args;
317 va_start(args, format);
318 vsnprintf(panicBuffer, sizeof(panicBuffer), format, args);
319 va_end(args);
320
321 if (cpu_halt_others() == ERR)
322 {
323 LOG_PANIC("failed to halt other CPUs due to '%s'\n", strerror(errno));
324 }
325
326 screen_panic();
327
328 LOG_PANIC("!!! KERNEL PANIC (%s version %s) !!!\n", OS_NAME, OS_VERSION);
329 LOG_PANIC("cause: %s\n", panicBuffer);
330
331 if (currentThread == NULL)
332 {
333 LOG_PANIC("thread: cpu=%d null\n", SELF->id);
334 }
335 else if (currentThread == thread_idle())
336 {
337 LOG_PANIC("thread: cpu=%d idle\n", SELF->id);
338 }
339 else
340 {
341 LOG_PANIC("thread: cpu=%d pid=%d tid=%d\n", SELF->id, currentProcess->id, currentThread->id);
342 }
343
344 LOG_PANIC("last errno: %d (%s)\n", err, strerror(err));
345
346 uint64_t freePages = pmm_avail_pages();
347 uint64_t reservedPages = pmm_used_pages();
348 uint64_t totalPages = freePages + reservedPages;
349
350 LOG_PANIC("memory: %lluK/%lluK available (%lluK kernel code/data, %lluK reserved)\n",
351 (freePages * PAGE_SIZE) / 1024, (totalPages * PAGE_SIZE) / 1024,
352 (((uintptr_t)&_kernel_end - (uintptr_t)&_kernel_start) / 1024), (reservedPages * PAGE_SIZE) / 1024);
353
354 uint64_t cr0 = cr0_read();
355 uint64_t cr2 = cr2_read();
356 uint64_t cr3 = cr3_read();
357 uint64_t cr4 = cr4_read();
358
359 LOG_PANIC("cr0=0x%016llx cr2=0x%016llx cr3=0x%016llx cr4=0x%016llx\n", cr0, cr2, cr3, cr4);
360 LOG_PANIC("cr0 flags:");
362 {
363 LOG_PANIC(" pe");
364 }
365 if (cr0 & CR0_MONITOR_CO_PROCESSOR)
366 {
367 LOG_PANIC(" mp");
368 }
369 if (cr0 & CR0_EMULATION)
370 {
371 LOG_PANIC(" em");
372 }
373 if (cr0 & CR0_TASK_SWITCHED)
374 {
375 LOG_PANIC(" ts");
376 }
377 if (cr0 & CR0_EXTENSION_TYPE)
378 {
379 LOG_PANIC(" et");
380 }
381 if (cr0 & CR0_NUMERIC_ERROR_ENABLE)
382 {
383 LOG_PANIC(" ne");
384 }
385 if (cr0 & CR0_WRITE_PROTECT)
386 {
387 LOG_PANIC(" wp");
388 }
389 if (cr0 & CR0_ALIGNMENT_MASK)
390 {
391 LOG_PANIC(" am");
392 }
393 if (cr0 & CR0_NOT_WRITE_THROUGH)
394 {
395 LOG_PANIC(" nw");
396 }
397 if (cr0 & CR0_CACHE_DISABLE)
398 {
399 LOG_PANIC(" cd");
400 }
401 if (cr0 & CR0_PAGING_ENABLE)
402 {
403 LOG_PANIC(" pg");
404 }
405 LOG_PANIC("\n");
406
407 if (frame != NULL)
408 {
409 LOG_PANIC("exception: %s (vector: %lld, error code: 0x%llx)\n", panic_get_exception_name(frame->vector),
410 frame->vector, frame->errorCode);
411
412 if (frame->vector == VECTOR_PAGE_FAULT)
413 {
414 LOG_PANIC("page fault details: A %s operation to a %s page caused a %s.\n",
415 (frame->errorCode & 2) ? "write" : "read", (frame->errorCode & 4) ? "user-mode" : "kernel-mode",
416 (frame->errorCode & 1) ? "protection violation" : "non-present page fault");
417 if (frame->errorCode & 8)
418 {
419 LOG_PANIC(" (Reserved bit violation)\n");
420 }
421 if (frame->errorCode & 16)
422 {
423 LOG_PANIC(" (Instruction fetch)\n");
424 }
425
426 if (cr2 == 0)
427 {
428 LOG_PANIC(" (Faulting address is NULL)\n");
429 }
430 else if (cr2 >= VMM_KERNEL_BINARY_MIN && cr2 < VMM_KERNEL_BINARY_MAX)
431 {
432 LOG_PANIC(" (Faulting address is in kernel binary region)\n");
433 }
434 else if (cr2 >= VMM_KERNEL_HEAP_MIN && cr2 < VMM_KERNEL_HEAP_MAX)
435 {
436 LOG_PANIC(" (Faulting address is in kernel heap region)\n");
437 }
438 else if (cr2 >= VMM_KERNEL_STACKS_MIN && cr2 < VMM_KERNEL_STACKS_MAX)
439 {
440 LOG_PANIC(" (Faulting address is in kernel stacks region)\n");
441 }
442 }
443 }
444
445 if (frame != NULL)
446 {
447 panic_registers(frame);
448 panic_print_code(frame);
450 panic_stack_trace(frame);
451 }
452 else
453 {
455 }
456
457 LOG_PANIC("!!! Please restart your machine !!!\n");
458
459#ifdef QEMU_EXIT_ON_PANIC
461#endif
462
463 while (true)
464 {
465 ASM("hlt");
466 }
467}
static char format[MAX_NAME]
Definition screen.c:17
int errno_t
Definition errno_t.h:4
@ VECTOR_PAGE_FAULT
Definition interrupt.h:159
#define SELF
Macro to access data in the current cpu.
Definition percpu.h:85
static void out8(port_t port, uint8_t val)
Write an 8-bit value to an I/O port.
Definition port.h:72
uint64_t cpu_halt_others(void)
Halts all other CPUs.
Definition cpu.c:115
#define QEMU_EXIT_ON_PANIC_PORT
QEMU exit port for panic.
Definition panic.h:33
#define PANIC_NO_CPU_ID
Cpu ID indicating no CPU has panicked yet.
Definition panic.h:23
void panic_stack_trace(const interrupt_frame_t *frame)
Print a stack trace from a interrupt frame.
Definition panic.c:268
void panic(const interrupt_frame_t *frame, const char *format,...)
Panic the kernel, printing a message and halting.
Definition panic.c:292
#define PANIC_MAX_STACK_FRAMES
Maximum stack frames to capture in a panic.
Definition panic.h:28
void screen_panic(void)
Show the screen without locking, for panic situations.
Definition screen.c:201
#define LOG_MAX_BUFFER
Maximum buffer size for various logging buffers.
Definition log.h:25
void log_nprint(log_level_t level, const char *string, uint64_t length)
Print a unformatted log message.
Definition log.c:194
#define LOG_PANIC(format,...)
Definition log.h:94
@ LOG_LEVEL_PANIC
Definition log.h:37
size_t pmm_used_pages(void)
Get the number of used physical pages.
Definition pmm.c:403
size_t pmm_avail_pages(void)
Get the number of available physical pages.
Definition pmm.c:395
#define VMM_KERNEL_HEAP_MAX
The maximum address for the kernel heap.
Definition vmm.h:68
#define VMM_KERNEL_STACKS_MAX
The maximum address for kernel stacks.
Definition vmm.h:65
#define VMM_KERNEL_BINARY_MAX
The maximum address for the content of the kernel binary.
Definition vmm.h:61
#define VMM_KERNEL_STACKS_MIN
The minimum address for kernel stacks.
Definition vmm.h:66
#define VMM_IDENTITY_MAPPED_MIN
The minimum address for the identity mapped physical memory.
Definition vmm.h:72
#define VMM_USER_SPACE_MAX
The maximum address for user space.
Definition vmm.h:74
#define VMM_KERNEL_BINARY_MIN
The minimum address for the content of the kernel binary./*#end#*‍/.
Definition vmm.h:62
#define VMM_USER_SPACE_MIN
The minimum address for user space.
Definition vmm.h:75
#define VMM_KERNEL_HEAP_MIN
The minimum address for the kernel heap.
Definition vmm.h:69
uint64_t symbol_resolve_addr(symbol_info_t *outSymbol, void *addr)
Resolve a symbol by address.
Definition symbol.c:346
static process_t * process_current(void)
Retrieves the process of the currently running thread.
Definition process.h:131
static thread_t * thread_idle(void)
Retrieves the idle thread for the current CPU.
Definition thread.h:147
static thread_t * thread_current(void)
Retrieves the currently running thread.
Definition thread.h:126
#define errno
Error number variable.
Definition errno.h:27
#define ASM(...)
Inline assembly macro.
Definition defs.h:160
#define NULL
Pointer error value.
Definition NULL.h:25
#define ERR
Integer error value.
Definition ERR.h:17
#define PAGE_SIZE
The size of a memory page in bytes.
Definition PAGE_SIZE.h:8
static void panic_direct_stack_trace(void)
Definition panic.c:263
static atomic_uint32_t panicCpuId
Definition panic.c:34
static bool panic_is_valid_address(uintptr_t addr)
Definition panic.c:95
static void panic_unwind_stack(uintptr_t *rbp)
Definition panic.c:224
static void panic_registers(const interrupt_frame_t *frame)
Definition panic.c:70
uint64_t _kernel_end
static bool panic_is_valid_stack_frame(uintptr_t ptr)
Definition panic.c:124
uint64_t _kernel_start
static uint64_t panic_print_trace_address(uintptr_t addr)
Definition panic.c:205
static char panicBuffer[LOG_MAX_BUFFER]
Definition panic.c:32
static void panic_print_stack_dump(const interrupt_frame_t *frame)
Definition panic.c:146
static void panic_print_code(const interrupt_frame_t *frame)
Definition panic.c:273
static const char * panic_get_exception_name(uint64_t vector)
Definition panic.c:36
#define CR0_TASK_SWITCHED
Definition regs.h:44
#define CR0_EXTENSION_TYPE
Definition regs.h:45
static uint64_t cr0_read(void)
Definition regs.h:128
#define CR0_NUMERIC_ERROR_ENABLE
Definition regs.h:46
#define CR0_WRITE_PROTECT
Definition regs.h:47
#define CR0_ALIGNMENT_MASK
Definition regs.h:48
#define CR0_EMULATION
Definition regs.h:43
static uint64_t cr4_read(void)
Definition regs.h:92
static uint64_t cr3_read(void)
Definition regs.h:104
static uint64_t msr_read(uint32_t msr)
Definition regs.h:65
#define CR0_NOT_WRITE_THROUGH
Definition regs.h:49
#define CR0_CACHE_DISABLE
Definition regs.h:50
#define CR0_PAGING_ENABLE
Definition regs.h:51
#define MSR_GS_BASE
Definition regs.h:20
#define MSR_FS_BASE
Definition regs.h:19
#define MSR_KERNEL_GS_BASE
Definition regs.h:21
#define CR0_MONITOR_CO_PROCESSOR
Definition regs.h:42
#define CR0_PROTECTED_MODE_ENABLE
Definition regs.h:41
static uint64_t cr2_read(void)
Definition regs.h:116
#define va_start(ap, parmN)
Definition stdarg.h:16
#define va_end(ap)
Definition stdarg.h:15
__builtin_va_list va_list
Definition stdarg.h:11
#define atomic_compare_exchange_strong(object, expected, desired)
Definition stdatomic.h:278
#define ATOMIC_VAR_INIT(value)
Definition stdatomic.h:74
__UINT32_TYPE__ uint32_t
Definition stdint.h:15
__UINT64_TYPE__ uint64_t
Definition stdint.h:17
__UINT8_TYPE__ uint8_t
Definition stdint.h:11
__UINTPTR_TYPE__ uintptr_t
Definition stdint.h:43
#define UINTPTR_MAX
Definition stdint.h:113
_PUBLIC int vsnprintf(char *_RESTRICT s, size_t n, const char *_RESTRICT format, va_list arg)
Definition vsnprintf.c:31
_PUBLIC char * strerror(int errnum)
Definition strerror.c:6
_PUBLIC size_t strlen(const char *s)
Definition strlen.c:3
Trap Frame Structure.
Definition interrupt.h:195
uint64_t errorCode
Definition interrupt.h:213
Process structure.
Definition process.h:76
pid_t id
Definition process.h:81
Symbol information structure.
Definition symbol.h:120
void * addr
Definition symbol.h:122
char name[SYMBOL_MAX_NAME]
Definition symbol.h:121
Thread of execution structure.
Definition thread.h:61
errno_t error
Definition thread.h:72
tid_t id
The thread id, unique within a process_t.
Definition thread.h:64