| 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 | |
| 25 | static 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 | |
| 82 | MAKE_HANDLER(divbyz, "Division by zero" ); |
| 83 | MAKE_HANDLER(debug, "Debug signal" ); |
| 84 | MAKE_HANDLER(breakpoint, "Breakpoint" ); |
| 85 | MAKE_HANDLER(ss, "STACK SEGMENT FAULT" ); |
| 86 | MAKE_HANDLER(double_fault, "DOUBLE FAULT" ); |
| 87 | |
| 88 | static 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 | |
| 100 | static 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 | |
| 121 | static enum irq_result nop_handler(struct irq_context *ictx, void *ctx) { |
| 122 | cc_ignore(ctx, ictx); |
| 123 | return IRQ_HANDLED; |
| 124 | } |
| 125 | |
| 126 | static 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 | |