1// SPDX-License-Identifier: GPL-3.0-or-later
2
3#include "mos/interrupt/interrupt.h"
4#include "mos/ksyscall_entry.h"
5#include "mos/misc/panic.h"
6#include "mos/misc/profiling.h"
7#include "mos/tasks/signal.h"
8
9#include <mos/interrupt/ipi.h>
10#include <mos/lib/structures/list.h>
11#include <mos/mm/cow.h>
12#include <mos/platform/platform.h>
13#include <mos/syscall/dispatcher.h>
14#include <mos/syslog/printk.h>
15#include <mos/x86/cpu/cpu.h>
16#include <mos/x86/devices/port.h>
17#include <mos/x86/interrupt/apic.h>
18#include <mos/x86/tasks/context.h>
19#include <mos/x86/x86_interrupt.h>
20#include <mos/x86/x86_platform.h>
21#include <mos_stdio.h>
22#include <mos_stdlib.h>
23
24static const char *const x86_exception_names[EXCEPTION_COUNT] = {
25 "Divide-By-Zero Error",
26 "Debug",
27 "Non-Maskable Interrupt",
28 "Breakpoint",
29 "Overflow",
30 "Bound Range Exceeded",
31 "Invalid Opcode",
32 "Device Not Available",
33 "Double Fault",
34 "Coprocessor Segment Overrun",
35 "Invalid TSS",
36 "Segment Not Present",
37 "Stack Segment Fault",
38 "General Protection Fault",
39 "Page Fault",
40 "Reserved",
41 "x87 Floating-Point Error",
42 "Alignment Check",
43 "Machine Check",
44 "SIMD Floating-Point Exception",
45 "Virtualization Exception",
46 "Control Protection Exception",
47 "Reserved",
48 "Reserved",
49 "Reserved",
50 "Reserved",
51 "Reserved",
52 "Reserved",
53 "Hypervisor Injection Exception",
54 "VMM Communication Exception",
55 "Security Exception",
56 "Reserved",
57};
58
59static void x86_handle_nmi(platform_regs_t *regs)
60{
61 pr_emph("cpu %d: NMI received", lapic_get_id());
62
63 u8 scp1 = port_inb(port: 0x92);
64 u8 scp2 = port_inb(port: 0x61);
65
66 static const char *const scp1_names[] = { "Alternate Hot Reset", "Alternate A20 Gate", "[RESERVED]", "Security Lock",
67 "Watchdog Timer", "[RESERVED]", "HDD 2 Activity", "HDD 1 Activity" };
68
69 static const char *const scp2_names[] = { "Timer 2 Tied to Speaker", "Speaker Data Enable", "Parity Check Enable", "Channel Check Enable",
70 "Refresh Request", "Timer 2 Output", "Channel Check", "Parity Check" };
71
72 for (int bit = 0; bit < 8; bit++)
73 if (scp1 & (1 << bit))
74 pr_emph(" %s", scp1_names[bit]);
75
76 for (int bit = 0; bit < 8; bit++)
77 if (scp2 & (1 << bit))
78 pr_emph(" %s", scp2_names[bit]);
79
80 platform_dump_regs(regs);
81 mos_panic("NMI received");
82}
83
84static void x86_handle_exception(platform_regs_t *regs)
85{
86 MOS_ASSERT(regs->interrupt_number < EXCEPTION_COUNT);
87
88 const char *name = x86_exception_names[regs->interrupt_number];
89 const char *intr_type = "";
90
91 // Faults: These can be corrected and the program may continue as if nothing happened.
92 // Traps: Traps are reported immediately after the execution of the trapping instruction.
93 // Aborts: Some severe unrecoverable error.
94 switch ((x86_exception_enum_t) regs->interrupt_number)
95 {
96 case EXCEPTION_NMI:
97 {
98 x86_handle_nmi(regs);
99 break;
100 }
101 case EXCEPTION_DEBUG:
102 {
103 ptr_t drx[6]; // DR0, DR1, DR2, DR3 and DR6, DR7
104 __asm__ volatile("mov %%dr0, %0\n"
105 "mov %%dr1, %1\n"
106 "mov %%dr2, %2\n"
107 "mov %%dr3, %3\n"
108 "mov %%dr6, %4\n"
109 "mov %%dr7, %5\n"
110 : "=r"(drx[0]), "=r"(drx[1]), "=r"(drx[2]), "=r"(drx[3]), "=r"(drx[4]), "=r"(drx[5]));
111
112 pr_emerg("cpu %d: %s (%lu) at " PTR_FMT " (DR0: " PTR_FMT " DR1: " PTR_FMT " DR2: " PTR_FMT " DR3: " PTR_FMT " DR6: " PTR_FMT " DR7: " PTR_FMT ")",
113 lapic_get_id(), name, regs->interrupt_number, regs->ip, drx[0], drx[1], drx[2], drx[3], drx[4], drx[5]);
114
115 return;
116 }
117 case EXCEPTION_INVALID_OPCODE:
118 {
119 intr_type = "fault";
120
121 if (MOS_IN_RANGE(regs->ip, (ptr_t) __MOS_KERNEL_CODE_START, (ptr_t) __MOS_KERNEL_CODE_END))
122 {
123 // kernel mode invalid opcode, search for a bug entry
124 try_handle_kernel_panics(ip: regs->ip);
125 mos_panic("Invalid opcode in kernel mode");
126 }
127 break;
128 }
129 case EXCEPTION_DIVIDE_ERROR:
130 case EXCEPTION_OVERFLOW:
131 case EXCEPTION_BOUND_RANGE_EXCEEDED:
132 case EXCEPTION_DEVICE_NOT_AVAILABLE:
133 case EXCEPTION_COPROCESSOR_SEGMENT_OVERRUN:
134 case EXCEPTION_INVALID_TSS:
135 case EXCEPTION_SEGMENT_NOT_PRESENT:
136 case EXCEPTION_STACK_SEGMENT_FAULT:
137 case EXCEPTION_GENERAL_PROTECTION_FAULT:
138 case EXCEPTION_FPU_ERROR:
139 case EXCEPTION_ALIGNMENT_CHECK:
140 case EXCEPTION_SIMD_ERROR:
141 case EXCEPTION_VIRTUALIZATION_EXCEPTION:
142 case EXCEPTION_CONTROL_PROTECTION_EXCEPTION:
143 case EXCEPTION_HYPERVISOR_EXCEPTION:
144 case EXCEPTION_VMM_COMMUNICATION_EXCEPTION:
145 case EXCEPTION_SECURITY_EXCEPTION:
146 {
147 intr_type = "fault";
148 break;
149 }
150
151 case EXCEPTION_BREAKPOINT:
152 {
153 mos_warn("Breakpoint not handled.");
154 return;
155 }
156
157 case EXCEPTION_PAGE_FAULT:
158 {
159 intr_type = "page fault";
160
161 pagefault_t info = {
162 .is_present = (regs->error_code & 0x1) != 0,
163 .is_write = (regs->error_code & 0x2) != 0,
164 .is_user = (regs->error_code & 0x4) != 0,
165 .is_exec = (regs->error_code & 0x10) != 0,
166 .ip = regs->ip,
167 .regs = regs,
168 };
169
170 mm_handle_fault(x86_cpu_get_cr2(), info: &info);
171 goto done;
172 }
173
174 case EXCEPTION_DOUBLE_FAULT:
175 case EXCEPTION_MACHINE_CHECK:
176 {
177 intr_type = "abort";
178 break;
179 }
180 case EXCEPTION_MAX:
181 case EXCEPTION_COUNT:
182 {
183 MOS_UNREACHABLE();
184 }
185 }
186
187 if (current_thread)
188 {
189 pr_emerg("cpu %d: %s (%lu) at " PTR_FMT " (error code %lu)", lapic_get_id(), name, regs->interrupt_number, regs->ip, regs->error_code);
190 signal_send_to_thread(current_thread, SIGKILL);
191 platform_dump_regs(regs);
192 platform_dump_current_stack();
193 platform_dump_stack(regs);
194 }
195 else
196 {
197 platform_dump_regs(regs);
198 mos_panic("x86 %s:\nInterrupt #%lu ('%s', error code %lu)", intr_type, regs->interrupt_number, name, regs->error_code);
199 }
200
201done:
202 return;
203}
204
205static void x86_handle_irq(platform_regs_t *frame)
206{
207 lapic_eoi();
208 const int irq = frame->interrupt_number - IRQ_BASE;
209 interrupt_entry(irq);
210}
211
212void x86_interrupt_entry(ptr_t rsp)
213{
214 platform_regs_t *frame = (platform_regs_t *) rsp;
215 current_cpu->interrupt_regs = frame;
216
217 reg_t syscall_ret = 0, syscall_nr = 0;
218
219 const pf_point_t ev = profile_enter();
220
221 if (frame->interrupt_number < IRQ_BASE)
222 x86_handle_exception(regs: frame);
223 else if (frame->interrupt_number >= IRQ_BASE && frame->interrupt_number < IRQ_BASE + IRQ_MAX)
224 x86_handle_irq(frame);
225 else if (frame->interrupt_number >= IPI_BASE && frame->interrupt_number < IPI_BASE + IPI_TYPE_MAX)
226 ipi_do_handle(type: (ipi_type_t) (frame->interrupt_number - IPI_BASE)), lapic_eoi();
227 else if (frame->interrupt_number == MOS_SYSCALL_INTR)
228 syscall_nr = frame->ax, syscall_ret = ksyscall_enter(number: frame->ax, arg1: frame->bx, arg2: frame->cx, arg3: frame->dx, arg4: frame->si, arg5: frame->di, arg6: frame->r9);
229 else
230 pr_warn("Unknown interrupt number: %lu", frame->interrupt_number);
231
232 profile_leave(ev, "x86.int.%lu", frame->interrupt_number);
233
234 if (unlikely(!current_thread))
235 {
236 x86_interrupt_return_impl(regs: frame);
237 MOS_UNREACHABLE();
238 }
239
240 // jump to signal handler if there is a pending signal, and if we are coming from userspace
241 if (frame->cs & 0x3)
242 {
243 if (frame->interrupt_number == MOS_SYSCALL_INTR)
244 signal_exit_to_user_prepare_syscall(regs: frame, syscall_nr, syscall_ret);
245 else
246 signal_exit_to_user_prepare(regs: frame);
247 }
248
249 x86_interrupt_return_impl(regs: frame);
250 MOS_UNREACHABLE();
251}
252