PatchworkOS
Loading...
Searching...
No Matches
panic.c
Go to the documentation of this file.
1#include <kernel/log/panic.h>
2
3#include <kernel/cpu/cpu.h>
5#include <kernel/cpu/port.h>
6#include <kernel/cpu/regs.h>
7#include <kernel/cpu/smp.h>
8#include <kernel/log/log.h>
9#include <kernel/mem/pmm.h>
10#include <kernel/mem/vmm.h>
11#include <kernel/sched/thread.h>
12#include <kernel/sched/timer.h>
13#include <kernel/version.h>
14
15#include <boot/boot_info.h>
16
17#include <stdarg.h>
18#include <stdint.h>
19#include <stdio.h>
20#include <stdlib.h>
21#include <string.h>
22#include <sys/io.h>
23#include <sys/math.h>
24#include <sys/proc.h>
25
27extern uint64_t _kernelEnd;
28
30static bool symbolsLoaded = false;
31static char panicBuffer[LOG_MAX_BUFFER] = {0};
32
34
35static bool panic_is_valid_stack_frame(void* ptr)
36{
37 if (ptr == NULL)
38 {
39 return false;
40 }
41 if ((uintptr_t)ptr & 0x7)
42 {
43 return false;
44 }
46 {
47 return false;
48 }
50 {
51 return false;
52 }
54 {
55 return false;
56 }
57 if ((uintptr_t)ptr > UINTPTR_MAX - 16)
58 {
59 return false;
60 }
61
62 return true;
63}
64
66{
67 if (addr == 0)
68 {
69 return false;
70 }
71 if (addr < VMM_USER_SPACE_MIN)
72 {
73 return false;
74 }
75 if (addr >= VMM_KERNEL_BINARY_MAX)
76 {
77 return false;
78 }
80 {
81 return false;
82 }
83 if (addr > UINTPTR_MAX - 16)
84 {
85 return false;
86 }
87 return true;
88}
89
90static const char* panic_resolve_symbol(uintptr_t addr, uintptr_t* offset)
91{
92 if (!symbolsLoaded)
93 {
94 return NULL;
95 }
96
97 panic_symbol_t* symbol = NULL;
98 LIST_FOR_EACH(symbol, &symbols, entry)
99 {
100 if (addr >= symbol->start && addr < symbol->end)
101 {
102 if (offset)
103 {
104 *offset = addr - symbol->start;
105 }
106 return symbol->name;
107 }
108 }
109
110 return NULL;
111}
112
113static const char* panic_get_exception_name(uint64_t vector)
114{
115 static const char* names[] = {
116 "divide error",
117 "debug",
118 "nmi",
119 "breakpoint",
120 "overflow",
121 "bound range exceeded",
122 "invalid opcode",
123 "device not available",
124 "double fault",
125 "coprocessor segment overrun",
126 "invalid TSS",
127 "segment not present",
128 "stack fault",
129 "general protection",
130 "page fault",
131 "reserved",
132 "x87 FPU error",
133 "alignment check",
134 "machine check",
135 "SIMD exception",
136 "virtualization exception",
137 "control protection exception",
138 };
139
140 if (vector < sizeof(names) / sizeof(names[0]))
141 {
142 return names[vector];
143 }
144 return "unknown exception";
145}
146
147static void panic_registers(const interrupt_frame_t* frame)
148{
149 LOG_PANIC("rip: %04llx:0x%016llx ", frame->cs, frame->rip);
150
151 uintptr_t offset;
152 const char* symbol = panic_resolve_symbol(frame->rip, &offset);
153 if (symbol != NULL)
154 {
155 LOG_PANIC("<%s+0x%llx>", symbol, offset);
156 }
157 LOG_PANIC("\n");
158
159 LOG_PANIC("rsp: %04llx:0x%016llx rflags: 0x%08llx\n", frame->ss, frame->rsp, frame->rflags & 0xFFFFFFFF);
160
161 LOG_PANIC("rax: 0x%016llx rbx: 0x%016llx rcx: 0x%016llx\n", frame->rax, frame->rbx, frame->rcx);
162 LOG_PANIC("rdx: 0x%016llx rsi: 0x%016llx rdi: 0x%016llx\n", frame->rdx, frame->rsi, frame->rdi);
163 LOG_PANIC("rbp: 0x%016llx r08: 0x%016llx r09: 0x%016llx\n", frame->rbp, frame->r8, frame->r9);
164 LOG_PANIC("r10: 0x%016llx r11: 0x%016llx r12: 0x%016llx\n", frame->r10, frame->r11, frame->r12);
165 LOG_PANIC("r13: 0x%016llx r14: 0x%016llx r15: 0x%016llx\n", frame->r13, frame->r14, frame->r15);
166}
167
169{
170 uint64_t rsp = frame->rsp;
171 LOG_PANIC("stack dump (around rsp=0x%016llx):\n", rsp);
172
173 const int linesToDump = 8;
174 const int bytesPerLine = 16;
175 uint64_t startaAddr = (rsp - (linesToDump / 2 - 1) * bytesPerLine) & ~(bytesPerLine - 1);
176
177 for (int i = 0; i < linesToDump; i++)
178 {
179 uint64_t lineAddr = startaAddr + (i * bytesPerLine);
180
181 LOG_PANIC(" 0x%016llx: ", lineAddr);
182
183 for (int j = 0; j < bytesPerLine; j++)
184 {
185 uintptr_t currentAddr = lineAddr + j;
186 if (panic_is_valid_address(currentAddr))
187 {
188 LOG_PANIC("%02x ", *(uint8_t*)currentAddr);
189 }
190 else
191 {
192 LOG_PANIC(" ");
193 }
194 }
195
196 LOG_PANIC("|");
197
198 for (int j = 0; j < bytesPerLine; j++)
199 {
200 uintptr_t currentAddr = lineAddr + j;
201 if (panic_is_valid_address(currentAddr))
202 {
203 uint8_t c = *(uint8_t*)currentAddr;
204 LOG_PANIC("%c", (c >= 32 && c <= 126) ? c : '.');
205 }
206 else
207 {
208 LOG_PANIC(" ");
209 }
210 }
211 LOG_PANIC("|\n");
212
213 if (rsp >= lineAddr && rsp < lineAddr + bytesPerLine)
214 {
215 LOG_PANIC(" ");
216
217 uint64_t offsetInLine = rsp - lineAddr;
218 for (uint64_t k = 0; k < offsetInLine; k++)
219 {
220 LOG_PANIC(" ");
221 }
222 LOG_PANIC("^^ RSP\n");
223 }
224 }
225}
226
228{
229 if (addr >= VMM_USER_SPACE_MIN && addr < VMM_USER_SPACE_MAX)
230 {
231 LOG_PANIC(" [0x%016llx] <user space address>\n", addr);
232 return;
233 }
234
235 uintptr_t offset;
236 const char* symbol = panic_resolve_symbol(addr, &offset);
237 if (symbol == NULL)
238 {
239 LOG_PANIC(" [0x%016llx] <unknown>\n", addr);
240 return;
241 }
242
243 LOG_PANIC(" [0x%016llx] <%s+0x%llx>\n", addr, symbol, offset);
244}
245
247{
248 LOG_PANIC("stack trace:\n");
249
250 void* currentFrame = __builtin_frame_address(0);
251 void* prevFrame = NULL;
252
253 uint64_t depth = 0;
254 while (currentFrame != NULL && currentFrame != prevFrame)
255 {
256 if (depth >= PANIC_MAX_STACK_FRAMES)
257 {
258 LOG_PANIC(" ...\n");
259 break;
260 }
261
262 if (!panic_is_valid_stack_frame(currentFrame))
263 {
264 break;
265 }
266
267 void* returnAddress = *((void**)currentFrame + 1);
268 if (returnAddress == NULL)
269 {
270 break;
271 }
272
273 panic_print_trace_address((uintptr_t)returnAddress);
274
275 prevFrame = currentFrame;
276 currentFrame = *((void**)currentFrame);
277 depth++;
278 }
279}
280
282{
283 LOG_PANIC("stack trace:\n");
284
285 uint64_t* rbp = (uint64_t*)frame->rbp;
286 uint64_t* prevFrame = NULL;
287
288 uint64_t depth = 0;
289 while (rbp != NULL && rbp != prevFrame)
290 {
291 if (depth >= PANIC_MAX_STACK_FRAMES)
292 {
293 LOG_PANIC(" ...\n");
294 break;
295 }
296
298 {
299 break;
300 }
301
302 if (prevFrame && rbp <= prevFrame)
303 {
304 break;
305 }
306
307 uint64_t returnAddress = rbp[1];
308 if (returnAddress == 0)
309 {
310 break;
311 }
312
313 panic_print_trace_address(returnAddress);
314
315 prevFrame = rbp;
316 rbp = (uint64_t*)rbp[0];
317 depth++;
318 }
319}
320
322{
324
325 for (uint32_t i = 0; i < kernel->symbolCount; i++)
326 {
327 elf_sym_t* sym = &kernel->symbols[i];
328 if (sym->shndx != ELF_SHN_UNDEF && sym->size > 0)
329 {
330 uintptr_t start = sym->value;
331 uintptr_t end = start + (sym->size > 0 ? sym->size : 1);
332
333 panic_symbol_t* symbol = malloc(sizeof(panic_symbol_t));
334 if (symbol == NULL)
335 {
336 // This isent really recoverable, but we can at least continue without the other symbols.
337 symbolsLoaded = true;
338 break;
339 }
340 list_entry_init(&symbol->entry);
341 symbol->start = start;
342 symbol->end = end;
343
344 if (sym->name >= kernel->stringTableSize)
345 {
346 strncpy(symbol->name, "invalid", MAX_NAME - 1);
347 symbol->name[MAX_NAME - 1] = '\0';
348 list_push(&symbols, &symbol->entry);
349 continue;
350 }
351
352 const char* name = &kernel->stringTable[sym->name];
353 strncpy(symbol->name, name, MAX_NAME - 1);
354 symbol->name[MAX_NAME - 1] = '\0';
355 list_push(&symbols, &symbol->entry);
356 }
357 }
358
359 symbolsLoaded = true;
360}
361
362void panic(const interrupt_frame_t* frame, const char* format, ...)
363{
364 asm volatile("cli");
365
366 cpu_t* self = smp_self_unsafe();
367
368 uint32_t expectedCpuId = PANIC_NO_CPU_ID;
369 if (!atomic_compare_exchange_strong(&panicCpudId, &expectedCpuId, self->id))
370 {
371 if (expectedCpuId == self->id)
372 {
373 const char* message = "!!! KERNEL DOUBLE PANIC ON SAME CPU !!!\n";
374 log_write(message, strlen(message));
375 }
376 while (true)
377 {
378 asm volatile("hlt");
379 }
380 }
381
383
385
386 va_list args;
387 va_start(args, format);
388 vsnprintf(panicBuffer, LOG_MAX_BUFFER, format, args);
389 va_end(args);
390
391 LOG_PANIC("!!! KERNEL PANIC (%s version %s) !!!\n", OS_NAME, OS_VERSION);
392 LOG_PANIC("cause: %s\n", panicBuffer);
393
394 thread_t* currentThread = self->sched.runThread;
395 if (currentThread == NULL)
396 {
397 LOG_PANIC("thread: cpu=%d null\n", self->id);
398 }
399 else if (currentThread == self->sched.idleThread)
400 {
401 LOG_PANIC("thread: cpu=%d idle\n", self->id);
402 }
403 else
404 {
405 LOG_PANIC("thread: cpu=%d pid=%d tid=%d\n", self->id, currentThread->process->id, currentThread->id);
406 }
407
408 errno_t lastError = currentThread->error;
409 LOG_PANIC("last errno: %d (%s)\n", lastError, strerror(lastError));
410
411 uint64_t freePages = pmm_free_amount();
412 uint64_t reservedPages = pmm_reserved_amount();
413 uint64_t totalPages = freePages + reservedPages;
414
415 LOG_PANIC("memory: %lluK/%lluK available (%lluK kernel code/data, %lluK reserved)\n",
416 (freePages * PAGE_SIZE) / 1024, (totalPages * PAGE_SIZE) / 1024,
417 (((uintptr_t)&_kernelEnd - (uintptr_t)&_kernelStart) / 1024), (reservedPages * PAGE_SIZE) / 1024);
418
419 uint64_t cr0 = cr0_read();
420 uint64_t cr2 = cr2_read();
421 uint64_t cr3 = cr3_read();
422 uint64_t cr4 = cr4_read();
423
424 LOG_PANIC("cr0=0x%016llx cr2=0x%016llx cr3=0x%016llx cr4=0x%016llx\n", cr0, cr2, cr3, cr4);
425 LOG_PANIC("cr0 flags:");
427 {
428 LOG_PANIC(" pe");
429 }
430 if (cr0 & CR0_MONITOR_CO_PROCESSOR)
431 {
432 LOG_PANIC(" mp");
433 }
434 if (cr0 & CR0_EMULATION)
435 {
436 LOG_PANIC(" em");
437 }
438 if (cr0 & CR0_TASK_SWITCHED)
439 {
440 LOG_PANIC(" ts");
441 }
442 if (cr0 & CR0_EXTENSION_TYPE)
443 {
444 LOG_PANIC(" et");
445 }
446 if (cr0 & CR0_NUMERIC_ERROR_ENABLE)
447 {
448 LOG_PANIC(" ne");
449 }
450 if (cr0 & CR0_WRITE_PROTECT)
451 {
452 LOG_PANIC(" wp");
453 }
454 if (cr0 & CR0_ALIGNMENT_MASK)
455 {
456 LOG_PANIC(" am");
457 }
458 if (cr0 & CR0_NOT_WRITE_THROUGH)
459 {
460 LOG_PANIC(" nw");
461 }
462 if (cr0 & CR0_CACHE_DISABLE)
463 {
464 LOG_PANIC(" cd");
465 }
466 if (cr0 & CR0_PAGING_ENABLE)
467 {
468 LOG_PANIC(" pg");
469 }
470 LOG_PANIC("\n");
471
472 if (frame != NULL)
473 {
474 LOG_PANIC("exception: %s (vector: %lld, error code: 0x%llx)\n", panic_get_exception_name(frame->vector),
475 frame->vector, frame->errorCode);
476
477 if (frame->vector == EXCEPTION_PAGE_FAULT)
478 {
479 LOG_PANIC("page fault details: A %s operation to a %s page caused a %s.\n",
480 (frame->errorCode & 2) ? "write" : "read", (frame->errorCode & 4) ? "user-mode" : "kernel-mode",
481 (frame->errorCode & 1) ? "protection violation" : "non-present page fault");
482 if (frame->errorCode & 8)
483 {
484 LOG_PANIC(" (Reserved bit violation)\n");
485 }
486 if (frame->errorCode & 16)
487 {
488 LOG_PANIC(" (Instruction fetch)\n");
489 }
490
491 if (cr2 == 0)
492 {
493 LOG_PANIC(" (Faulting address is NULL)\n");
494 }
495 else if (cr2 >= VMM_KERNEL_BINARY_MIN && cr2 < VMM_KERNEL_BINARY_MAX)
496 {
497 LOG_PANIC(" (Faulting address is in kernel binary region)\n");
498 }
499 else if (cr2 >= VMM_KERNEL_HEAP_MIN && cr2 < VMM_KERNEL_HEAP_MAX)
500 {
501 LOG_PANIC(" (Faulting address is in kernel heap region)\n");
502 }
503 else if (cr2 >= VMM_KERNEL_STACKS_MIN && cr2 < VMM_KERNEL_STACKS_MAX)
504 {
505 LOG_PANIC(" (Faulting address is in kernel stacks region)\n");
506 }
507 }
508 }
509
510 if (frame != NULL)
511 {
512 panic_registers(frame);
513 if (frame->vector == EXCEPTION_PAGE_FAULT &&
514 (cr2 >= self->sched.runThread->kernelStack.guardBottom && cr2 < self->sched.runThread->kernelStack.top))
515 {
516 LOG_PANIC("skipping stack dump due to page fault in kernel stack region\n");
517 }
518 else
519 {
521 }
522 panic_stack_trace(frame);
523 }
524 else
525 {
527 }
528
529 LOG_PANIC("!!! Please restart your machine !!!\n");
530
531#ifdef QEMU_ISA_DEBUG_EXIT
533#endif
534
535 while (true)
536 {
537 asm volatile("hlt");
538 }
539}
#define MAX_NAME
Maximum length of names.
Definition MAX_NAME.h:11
int errno_t
Definition errno_t.h:4
@ EXCEPTION_PAGE_FAULT
Definition interrupt.h:112
#define QEMU_ISA_DEBUG_EXIT_PORT
Definition port.h:27
static void port_outb(uint16_t port, uint8_t val)
Definition port.h:29
static cpu_t * smp_self_unsafe(void)
Returns a pointer to the cpu_t structure of the current CPU.
Definition smp.h:90
void smp_halt_others(void)
Halts all CPUs except the current one.
Definition smp.c:102
#define PANIC_NO_CPU_ID
Cpu ID indicating no CPU has panicked yet.
Definition panic.h:21
void panic_stack_trace(const interrupt_frame_t *frame)
Print a stack trace from a interrupt frame.
Definition panic.c:281
void panic_symbols_init(const boot_kernel_t *kernel)
Initialize panic symbols from the bootloader-provided kernel information.
Definition panic.c:321
void panic(const interrupt_frame_t *frame, const char *format,...)
Panic the kernel, printing a message and halting.
Definition panic.c:362
#define PANIC_MAX_STACK_FRAMES
Maximum stack frames to capture in a panic.
Definition panic.h:26
#define LOG_MAX_BUFFER
Maximum buffer size for a single log line.
Definition log.h:23
void log_write(const char *string, uint64_t length)
Write directly to the log outputs without any formatting or headers.
Definition log.c:92
#define LOG_PANIC(format,...)
Definition log.h:90
void log_screen_enable(void)
Enable logging to the screen.
Definition log.c:72
uint64_t pmm_free_amount(void)
Retrieves the amount of free physical memory.
Definition pmm.c:238
uint64_t pmm_reserved_amount(void)
Retrieves the amount of reserved physical memory.
Definition pmm.c:244
#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
#define ELF_SHN_UNDEF
Definition elf.h:174
#define LIST_FOR_EACH(elem, list, member)
Iterates over a list.
Definition list.h:65
#define LIST_CREATE(name)
Creates a list initializer.
Definition list.h:176
static void list_push(list_t *list, list_entry_t *entry)
Pushes an entry to the end of the list.
Definition list.h:345
static void list_entry_init(list_entry_t *entry)
Initializes a list entry.
Definition list.h:184
static void list_init(list_t *list)
Initializes a list.
Definition list.h:198
#define PAGE_SIZE
Memory page size.
Definition proc.h:140
#define NULL
Pointer error value.
Definition NULL.h:23
static atomic_uint32_t panicCpudId
Definition panic.c:33
static void panic_direct_stack_trace(void)
Definition panic.c:246
static bool symbolsLoaded
Definition panic.c:30
uint64_t _kernelEnd
static bool panic_is_valid_stack_frame(void *ptr)
Definition panic.c:35
static bool panic_is_valid_address(uintptr_t addr)
Definition panic.c:65
static const char * panic_resolve_symbol(uintptr_t addr, uintptr_t *offset)
Definition panic.c:90
static void panic_print_trace_address(uintptr_t addr)
Definition panic.c:227
uint64_t _kernelStart
static void panic_registers(const interrupt_frame_t *frame)
Definition panic.c:147
static list_t symbols
Definition panic.c:29
static char panicBuffer[LOG_MAX_BUFFER]
Definition panic.c:31
static void panic_print_stack_dump(const interrupt_frame_t *frame)
Definition panic.c:168
static const char * panic_get_exception_name(uint64_t vector)
Definition panic.c:113
static void start()
Definition main.c:542
#define CR0_TASK_SWITCHED
Definition regs.h:42
#define CR0_EXTENSION_TYPE
Definition regs.h:43
#define CR0_NUMERIC_ERROR_ENABLE
Definition regs.h:44
#define CR0_WRITE_PROTECT
Definition regs.h:45
#define CR0_ALIGNMENT_MASK
Definition regs.h:46
#define CR0_EMULATION
Definition regs.h:41
static uint64_t cr2_read()
Definition regs.h:114
#define CR0_NOT_WRITE_THROUGH
Definition regs.h:47
#define CR0_CACHE_DISABLE
Definition regs.h:48
#define CR0_PAGING_ENABLE
Definition regs.h:49
static uint64_t cr3_read()
Definition regs.h:102
#define CR0_MONITOR_CO_PROCESSOR
Definition regs.h:40
#define CR0_PROTECTED_MODE_ENABLE
Definition regs.h:39
static uint64_t cr0_read()
Definition regs.h:126
static uint64_t cr4_read()
Definition regs.h:90
#define va_start(ap, parmN)
Definition stdarg.h:14
#define va_end(ap)
Definition stdarg.h:13
__builtin_va_list va_list
Definition stdarg.h:9
#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:5
#define EXIT_FAILURE
Definition stdlib.h:47
_PUBLIC void * malloc(size_t size)
Definition malloc.c:5
_PUBLIC char * strerror(int errnum)
Definition strerror.c:6
_PUBLIC char * strncpy(char *_RESTRICT s1, const char *_RESTRICT s2, size_t n)
Definition strncpy.c:3
_PUBLIC size_t strlen(const char *s)
Definition strlen.c:3
elf_sym_t * symbols
Definition boot_info.h:84
char * stringTable
Definition boot_info.h:86
uint64_t stringTableSize
Definition boot_info.h:87
uint32_t symbolCount
Definition boot_info.h:85
CPU structure.
Definition cpu.h:42
cpuid_t id
Definition cpu.h:43
sched_cpu_ctx_t sched
Definition cpu.h:51
ELF symbol table entry.
Definition elf.h:335
uint16_t shndx
Definition elf.h:339
uint64_t value
Definition elf.h:340
uint32_t name
Definition elf.h:336
uint64_t size
Definition elf.h:341
Trap Frame Structure.
Definition interrupt.h:42
uint64_t rflags
Definition interrupt.h:64
uint64_t errorCode
Definition interrupt.h:60
uint64_t vector
Definition interrupt.h:59
A doubly linked list.
Definition list.h:51
Panic symbol structure.
Definition panic.h:34
char name[MAX_NAME]
Definition panic.h:38
uintptr_t start
Definition panic.h:36
pid_t id
Definition process.h:55
thread_t * runThread
The currently running thread.
Definition sched.h:119
thread_t * idleThread
The thread that runs when the owner CPU is idling.
Definition sched.h:125
uintptr_t guardBottom
The bottom of the guard page, this address is inclusive.
Thread of execution structure.
Definition thread.h:55
process_t * process
The parent process that the thread executes within.
Definition thread.h:57
stack_pointer_t kernelStack
The kernel stack of the thread.
Definition thread.h:68
errno_t error
Definition thread.h:67
tid_t id
The thread id, unique within a process_t.
Definition thread.h:59