Improve context switching and scheduler APIs.
[monolithium.git] / kernel / src / exception.c
1 /*
2  * exception.c
3  *
4  * Copyright (C) 2017 Aleksandar Andrejevic <theflash@sdf.lonestar.org>
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Affero General Public License as
8  * published by the Free Software Foundation, either version 3 of the
9  * License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Affero General Public License for more details.
15  *
16  * You should have received a copy of the GNU Affero General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <common.h>
21 #include <exception.h>
22 #include <syscalls.h>
23 #include <process.h>
24 #include <video.h>
25 #include <vm86.h>
26 #include <heap.h>
27 #include <cpu.h>
28
29 static const char *exception_names[] = {
30     "Breakpoint",
31     "Arithmetic Error",
32     "Assertion Failure",
33     "Bad Operation",
34     "Memory Access Fault",
35 };
36
37 static void raise_exception_internal(registers_t *regs, processor_mode_t mode, exception_info_t *info)
38 {
39     thread_t *thread = get_current_thread();
40     if (thread == NULL) KERNEL_CRASH_WITH_REGS(exception_names[info->number], regs);
41
42     if (mode == USER_MODE)
43     {
44         thread->user_exception_info = *info;
45
46         if (thread->user_handler.eip)
47         {
48             *regs = thread->user_handler;
49             regs->eax = 1;
50             regs->error_code = 0;
51
52             ASSERT((regs->cs & 0xFFFC) == 0 || SEGMENT_RPL(regs->cs) == 3);
53             ASSERT((regs->data_selector & 0xFFFC) == 0 || SEGMENT_RPL(regs->data_selector) == 3);
54
55             registers_ext_t *regs_ext = (registers_ext_t*)regs;
56             regs_ext->esp3 = regs->esp;
57         }
58         else
59         {
60             enable_ints();
61             syscall_terminate(INVALID_HANDLE, 1);
62             ASSERT(FALSE);
63         }
64     }
65     else
66     {
67         thread->kernel_exception_info = *info;
68
69         if (thread->kernel_handler.eip)
70         {
71             thread->kernel_handler.eax = 1;
72             exception_return(thread->kernel_handler);
73         }
74         else
75         {
76             KERNEL_CRASH_WITH_REGS(exception_names[info->number], regs);
77         }
78     }
79 }
80
81 static void exception_handler(registers_t *regs, byte_t int_num)
82 {
83     exception_info_t info;
84     processor_mode_t previous_mode = SEGMENT_RPL(regs->cs) == 0 ? KERNEL_MODE : USER_MODE;
85     void *faulting_address;
86
87     info.state = *regs;
88     memset(info.parameters, 0, sizeof(info.parameters));
89
90     switch (int_num)
91     {
92     case CPU_EXCEPTION_DE:
93     case CPU_EXCEPTION_MF:
94         info.number = EXCEPTION_ARITHMETIC;
95         break;
96
97     case CPU_EXCEPTION_DB:
98     case CPU_EXCEPTION_BP:
99         info.number = EXCEPTION_BREAKPOINT;
100         break;
101
102     case CPU_EXCEPTION_NMI:
103     case CPU_EXCEPTION_MC:
104         return;
105
106     case CPU_EXCEPTION_OF:
107     case CPU_EXCEPTION_BR:
108     case CPU_EXCEPTION_AC:
109         info.number = EXCEPTION_ASSERTION;
110         break;
111
112     case CPU_EXCEPTION_NM:
113         if (cpu_features[0] & CPU_FEATURE_FPU)
114         {
115             thread_lazy_fpu();
116             return;
117         }
118         else
119         {
120             info.number = EXCEPTION_BAD_OPERATION;
121         }
122
123         break;
124
125     case CPU_EXCEPTION_DF:
126         KERNEL_CRASH_WITH_REGS("Double Fault", regs);
127         return;
128
129     case CPU_EXCEPTION_GP:
130         if (regs->eflags & EFLAGS_VM)
131         {
132             vm86_handler((registers_ext_vm86_t*)regs);
133             return;
134         }
135
136     case CPU_EXCEPTION_UD:
137     case CPU_EXCEPTION_TS:
138     case CPU_EXCEPTION_NP:
139     case CPU_EXCEPTION_SS:
140         info.number = EXCEPTION_BAD_OPERATION;
141         break;
142
143     case CPU_EXCEPTION_PF:
144         asm volatile ("movl %%cr2, %0" : "=r"(faulting_address) ::);
145         if (memory_fault_handler(faulting_address, regs)) return;
146
147         info.number = EXCEPTION_MEMORY_ACCESS;
148         memcpy(info.parameters, &faulting_address, sizeof(faulting_address));
149         break;
150
151     default:
152         KERNEL_CRASH_WITH_REGS("Unexpected CPU exception", regs);
153     }
154
155     raise_exception_internal(regs, previous_mode, &info);
156 }
157
158 sysret_t syscall_raise_exception(exception_info_t *info)
159 {
160     exception_info_t safe_info;
161
162     if (get_previous_mode() == USER_MODE)
163     {
164         if (!check_usermode(info, sizeof(exception_info_t))) return ERR_BADPTR;
165
166         EH_TRY safe_info = *info;
167         EH_CATCH EH_ESCAPE(return ERR_BADPTR);
168         EH_DONE;
169     }
170     else
171     {
172         safe_info = *info;
173     }
174
175     thread_t *thread = get_current_thread();
176     raise_exception_internal(&thread->state.regs, get_previous_mode(), &safe_info);
177     return ERR_SUCCESS;
178 }
179
180 sysret_t syscall_get_exception_info(exception_info_t *info)
181 {
182     thread_t *thread = get_current_thread();
183
184     if (get_previous_mode() == USER_MODE)
185     {
186         if (!check_usermode(info, sizeof(exception_info_t))) return ERR_BADPTR;
187
188         EH_TRY memcpy(info, &thread->user_exception_info, sizeof(exception_info_t));
189         EH_CATCH EH_ESCAPE(return ERR_BADPTR);
190         EH_DONE;
191     }
192     else
193     {
194         *info = thread->kernel_exception_info;
195     }
196
197     return ERR_SUCCESS;
198 }
199
200 void set_exception_handler(registers_t *regs, processor_mode_t mode, exception_handler_t *old_handler)
201 {
202     thread_t *thread = get_current_thread();
203
204     if (mode == KERNEL_MODE)
205     {
206         *old_handler = thread->kernel_handler;
207         thread->kernel_handler = *regs;
208     }
209     else
210     {
211         if (!check_usermode(old_handler, sizeof(exception_handler_t))) return;
212
213         EH_TRY
214         {
215             *old_handler = thread->user_handler;
216             thread->user_handler = *regs;
217         }
218         EH_DONE;
219     }
220 }
221
222 sysret_t syscall_save_exception_handler(exception_handler_t *old_handler)
223 {
224     set_exception_handler(old_handler, USER_MODE, get_current_thread()->last_context);
225     return 0;
226 }
227
228 sysret_t syscall_restore_exception_handler(exception_handler_t *old_handler)
229 {
230     thread_t *thread = get_current_thread();
231
232     if (get_previous_mode() == USER_MODE)
233     {
234         exception_handler_t safe_handler;
235         if (!check_usermode(old_handler, sizeof(exception_handler_t))) return ERR_BADPTR;
236
237         EH_TRY
238         {
239             safe_handler = *old_handler;
240         }
241         EH_CATCH
242         {
243             EH_ESCAPE(return ERR_BADPTR);
244         }
245         EH_DONE;
246
247         if (((safe_handler.cs & 0xFFFC) != 0 && SEGMENT_RPL(safe_handler.cs) != 3)
248             || ((safe_handler.data_selector & 0xFFFC) != 0
249             && SEGMENT_RPL(safe_handler.data_selector) != 3))
250         {
251             return ERR_INVALID;
252         }
253
254         thread->user_handler = safe_handler;
255     }
256     else
257     {
258         thread->kernel_handler = *old_handler;
259     }
260
261     return ERR_SUCCESS;
262 }
263
264 void exceptions_init()
265 {
266     byte_t i;
267     for (i = 0; i < CPU_EXCEPTION_MAX; i++) set_int_handler(i, exception_handler, FALSE, FALSE);
268 }