1#include <console/crash.h>
2#include <console/panic.h>
3
4#define MAKE_HANDLER(handler_name, message) \
5 static enum irq_result handler_name##_handler(struct irq_context *rsp, \
6 void *ctx) { \
7 cc_ignore(ctx); \
8 struct crash_regs pregs; \
9 irq_context_to_crash_regs(rsp->regs, &pregs); \
10 char msg[CRASH_MSG_MAX]; \
11 snprintf(msg, sizeof(msg), "CPU %u fault: " message " at %p", \
12 (uint32_t) smp_id_raw(), (void *) rsp->regs->rip); \
13 crash_full(&(struct crash_context) { \
14 .source = CRASH_SOURCE_CPU_EXCEPTION, \
15 .formats = CRASH_FMT_DEFAULT, \
16 .file = __FILE__, \
17 .line = __LINE__, \
18 .func = #handler_name "_handler", \
19 .msg = msg, \
20 .regs = &pregs, \
21 }); \
22 return IRQ_HANDLED; \
23 }
24
25static enum irq_result gpf_handler(struct irq_context *ictx, void *ctx) {
26 cc_ignore(ctx);
27
28 uint64_t core = smp_id_raw();
29 struct irq_registers *rsp = ictx->regs;
30 uint64_t ec = rsp->error_code;
31
32 printf(format: "\n=== General Protection Fault ===\n");
33 printf(format: "Core: %u\n", (uint32_t) core);
34 printf(format: "RIP: %p\n", (void *) rsp->rip);
35 printf(format: "RSP: %p\n", (void *) rsp->rsp);
36 printf(format: "RBP: %p\n", (void *) rsp->rbp);
37 printf(format: "CS: 0x%04x\n", (uint16_t) rsp->cs);
38 printf(format: "SS: 0x%04x\n", (uint16_t) rsp->ss);
39 printf(format: "RFLAGS: 0x%016lx\n", rsp->rflags);
40
41 printf(format: "\nError code: 0x%04lx\n", ec);
42 if (ec == 0) {
43 printf(format: " (no selector; likely a non-canonical access, "
44 "null deref, or privilege violation)\n");
45 } else {
46 bool ext = ec & 1;
47 uint8_t tbl = (ec >> 1) & 0x3;
48 uint16_t index = (ec >> 3) & 0x1FFF;
49
50 const char *tbl_name;
51 switch (tbl) {
52 case 0: tbl_name = "GDT"; break;
53 case 1: tbl_name = "IDT"; break;
54 case 2: tbl_name = "LDT"; break;
55 case 3: tbl_name = "IDT"; break;
56 default: tbl_name = "unknown"; break;
57 }
58
59 printf(format: " External: %s\n", ext ? "yes (hardware)" : "no (software)");
60 printf(format: " Table: %s\n", tbl_name);
61 printf(format: " Selector: %u (0x%x)\n", index, index);
62 }
63
64 struct crash_regs pregs;
65 irq_context_to_crash_regs(ictx: rsp, out: &pregs);
66 char msg[CRASH_MSG_MAX];
67 snprintf(msg, sizeof(msg), "GPF on core %u at %p (error code 0x%lx)",
68 (uint32_t) core, (void *) rsp->rip, ec);
69 crash_full(ctx: &(struct crash_context) {
70 .source = CRASH_SOURCE_CPU_EXCEPTION,
71 .formats = CRASH_FMT_DEFAULT,
72 .file = __FILE__,
73 .line = __LINE__,
74 .func = __func__,
75 .msg = msg,
76 .regs = &pregs,
77 });
78
79 return IRQ_HANDLED;
80}
81
82MAKE_HANDLER(divbyz, "Division by zero");
83MAKE_HANDLER(debug, "Debug signal");
84MAKE_HANDLER(breakpoint, "Breakpoint");
85MAKE_HANDLER(ss, "STACK SEGMENT FAULT");
86MAKE_HANDLER(double_fault, "DOUBLE FAULT");
87
88static enum irq_result panic_nmi_isr(struct irq_context *rsp, void *ctx) {
89 cc_ignore(rsp, ctx);
90 if (atomic_load(&global.panicked)) {
91 if (crash_cpu_is_owner(id: smp_id_raw()))
92 return IRQ_HANDLED;
93
94 crash_nmi_handoff(p: ctx, irqc: rsp->regs);
95 }
96
97 return IRQ_NONE;
98}
99
100static enum irq_result hw_error_nmi_isr(struct irq_context *ictx, void *ctx) {
101 cc_ignore(ctx, ictx);
102 uint8_t port61 = inb(port: 0x61);
103 if (port61 & 0xC0) {
104 char msg[CRASH_MSG_MAX];
105 snprintf(msg, sizeof(msg),
106 "Hardware / Memory Parity NMI Error (Port 0x61 = 0x%02x)",
107 port61);
108 crash_full(ctx: &(struct crash_context) {
109 .source = CRASH_SOURCE_CPU_EXCEPTION,
110 .formats = CRASH_FMT_DEFAULT,
111 .file = __FILE__,
112 .line = __LINE__,
113 .func = __func__,
114 .msg = msg,
115 });
116 }
117
118 return IRQ_NONE;
119}
120
121static enum irq_result nop_handler(struct irq_context *ictx, void *ctx) {
122 cc_ignore(ctx, ictx);
123 return IRQ_HANDLED;
124}
125
126static enum irq_result dpc_handler(struct irq_context *rsp, void *ctx) {
127 scheduler_mark_self_needs_run_dpcs(true);
128 cc_ignore(ctx, rsp);
129 return IRQ_HANDLED;
130}
131