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