1 /* This file is part of the lowest layer of the MINIX kernel. (The other part
2 * is "proc.c".) The lowest layer does process switching and message handling.
3 * Furthermore it contains the assembler startup code for Minix and the 32-bit
4 * interrupt handlers. It cooperates with the code in "start.c" to set up a
5 * good environment for main().
7 * Kernel is entered either because of kernel-calls, ipc-calls, interrupts or
8 * exceptions. TSS is set so that the kernel stack is loaded. The user context is
9 * saved to the proc table and the handler of the event is called. Once the
10 * handler is done, switch_to_user() function is called to pick a new process,
11 * finish what needs to be done for the next process to run, sets its context
12 * and switch to userspace.
14 * For communication with the boot monitor at startup time some constant
15 * data are compiled into the beginning of the text segment. This facilitates
16 * reading the data at the start of the boot process, since only the first
17 * sector of the file needs to be read.
19 * Some data storage is also allocated at the end of this file. This data
20 * will be at the start of the data segment of the kernel and will be read
21 * and modified by the boot monitor before the kernel starts.
24 #include "kernel/kernel.h" /* configures the kernel */
28 #include <machine/vm.h>
29 #include "kernel/kernel.h"
30 #include <minix/config.h>
31 #include <minix/const.h>
32 #include <minix/ipcconst.h>
33 #include <minix/com.h>
34 #include <machine/asm.h>
35 #include <machine/interrupt.h>
36 #include "archconst.h"
37 #include "kernel/const.h"
38 #include "kernel/proc.h"
40 #include <machine/multiboot.h>
42 #include "arch_proto.h" /* K_STACK_SIZE */
45 #include "kernel/smp.h"
48 /* Selected 386 tss offsets. */
51 IMPORT(usermapped_offset)
52 IMPORT(copr_not_available_handler)
55 IMPORT(switch_to_user)
56 IMPORT(multiboot_init)
59 /*===========================================================================*/
60 /* interrupt handlers */
61 /* interrupt handlers for 386 32-bit protected mode */
62 /*===========================================================================*/
64 #define PIC_IRQ_HANDLER(irq) \
66 call _C_LABEL(irq_handle) /* intr_handle(irq_handlers[irq]) */ ;\
69 /*===========================================================================*/
71 /*===========================================================================*/
72 /* Note this is a macro, it just looks like a subroutine. */
74 #define hwint_master(irq) \
75 TEST_INT_IN_KERNEL(4, 0f) ;\
77 SAVE_PROCESS_CTX(0, KTS_INT_HARD) ;\
79 movl $0, %ebp /* for stack trace */ ;\
80 call _C_LABEL(context_stop) ;\
82 PIC_IRQ_HANDLER(irq) ;\
83 movb $END_OF_INT, %al ;\
84 outb $INT_CTL /* reenable interrupts in master pic */ ;\
85 jmp _C_LABEL(switch_to_user) ;\
89 call _C_LABEL(context_stop_idle) ;\
90 PIC_IRQ_HANDLER(irq) ;\
91 movb $END_OF_INT, %al ;\
92 outb $INT_CTL /* reenable interrupts in master pic */ ;\
93 CLEAR_IF(10*4(%esp)) ;\
97 /* Each of these entry points is an expansion of the hwint_master macro */
99 /* Interrupt routine for irq 0 (the clock). */
103 /* Interrupt routine for irq 1 (keyboard) */
107 /* Interrupt routine for irq 2 (cascade!) */
111 /* Interrupt routine for irq 3 (second serial) */
115 /* Interrupt routine for irq 4 (first serial) */
119 /* Interrupt routine for irq 5 (XT winchester) */
123 /* Interrupt routine for irq 6 (floppy) */
127 /* Interrupt routine for irq 7 (printer) */
130 /*===========================================================================*/
132 /*===========================================================================*/
133 /* Note this is a macro, it just looks like a subroutine. */
134 #define hwint_slave(irq) \
135 TEST_INT_IN_KERNEL(4, 0f) ;\
137 SAVE_PROCESS_CTX(0, KTS_INT_HARD) ;\
139 movl $0, %ebp /* for stack trace */ ;\
140 call _C_LABEL(context_stop) ;\
142 PIC_IRQ_HANDLER(irq) ;\
143 movb $END_OF_INT, %al ;\
144 outb $INT_CTL /* reenable interrupts in master pic */ ;\
145 outb $INT2_CTL /* reenable slave 8259 */ ;\
146 jmp _C_LABEL(switch_to_user) ;\
150 call _C_LABEL(context_stop_idle) ;\
151 PIC_IRQ_HANDLER(irq) ;\
152 movb $END_OF_INT, %al ;\
153 outb $INT_CTL /* reenable interrupts in master pic */ ;\
154 outb $INT2_CTL /* reenable slave 8259 */ ;\
155 CLEAR_IF(10*4(%esp)) ;\
159 /* Each of these entry points is an expansion of the hwint_slave macro */
161 /* Interrupt routine for irq 8 (realtime clock) */
165 /* Interrupt routine for irq 9 (irq 2 redirected) */
169 /* Interrupt routine for irq 10 */
173 /* Interrupt routine for irq 11 */
177 /* Interrupt routine for irq 12 */
181 /* Interrupt routine for irq 13 (FPU exception) */
185 /* Interrupt routine for irq 14 (AT winchester) */
189 /* Interrupt routine for irq 15 */
192 /* differences with sysenter:
193 * - we have to find our own per-cpu stack (i.e. post-SYSCALL
194 * %esp is not configured)
195 * - we have to save the post-SYSRET %eip, provided by the cpu
197 * - the system call parameters are passed in %ecx, so we userland
198 * code that executes SYSCALL copies %ecx to %edx. So the roles
199 * of %ecx and %edx are reversed
200 * - we can use %esi as a scratch register
202 #define ipc_entry_syscall_percpu(cpu) ;\
203 ENTRY(ipc_entry_syscall_cpu ## cpu) ;\
205 mov k_percpu_stacks+4*cpu, %esi ;\
207 movl $KTS_SYSCALL, P_KERN_TRAP_STYLE(%ebp) ;\
209 jmp syscall_sysenter_common
211 ipc_entry_syscall_percpu(0)
212 ipc_entry_syscall_percpu(1)
213 ipc_entry_syscall_percpu(2)
214 ipc_entry_syscall_percpu(3)
215 ipc_entry_syscall_percpu(4)
216 ipc_entry_syscall_percpu(5)
217 ipc_entry_syscall_percpu(6)
218 ipc_entry_syscall_percpu(7)
220 ENTRY(ipc_entry_sysenter)
221 /* SYSENTER simply sets kernel segments, EIP to here, and ESP
222 * to tss->sp0 (through MSR). so no automatic context saving is done.
223 * interrupts are disabled.
226 * edi: call type (IPCVEC, KERVEC)
227 * ebx, eax, ecx: syscall params, set by userland
228 * esi, edx: esp, eip to restore, set by userland
230 * no state is automatically saved; userland does all of that.
232 mov (%esp), %ebp /* get proc saved by arch_finish_switch_to_user */
234 /* inform kernel we entered by sysenter and should
235 * therefore exit through restore_user_context_sysenter
237 movl $KTS_SYSENTER, P_KERN_TRAP_STYLE(%ebp)
238 add usermapped_offset, %edx /* compensate for mapping difference */
240 syscall_sysenter_common:
241 mov %esi, SPREG(%ebp) /* esi is return esp */
242 mov %edx, PCREG(%ebp) /* edx is return eip */
247 mov %edx, PSWREG(%ebp)
249 /* check for call type; do_ipc? */
253 /* check for kernel trap */
255 jz kernel_call_entry_common
257 /* unrecognized call number; restore user with error */
258 movl $-1, AXREG(%ebp)
260 call restore_user_context /* restore_user_context(%ebp); */
263 * IPC is only from a process to kernel
265 ENTRY(ipc_entry_softint_orig)
266 SAVE_PROCESS_CTX(0, KTS_INT_ORIG)
269 ENTRY(ipc_entry_softint_um)
270 SAVE_PROCESS_CTX(0, KTS_INT_UM)
273 ENTRY(ipc_entry_common)
274 /* save the pointer to the current process */
278 * pass the syscall arguments from userspace to the handler.
279 * SAVE_PROCESS_CTX() does not clobber these registers, they are still
280 * set as the userspace have set them
286 /* stop user process cycles */
288 /* for stack trace */
290 call _C_LABEL(context_stop)
293 call _C_LABEL(do_ipc)
295 /* restore the current process pointer and save the return value */
298 mov %eax, AXREG(%esi)
300 jmp _C_LABEL(switch_to_user)
304 * kernel call is only from a process to kernel
306 ENTRY(kernel_call_entry_orig)
307 SAVE_PROCESS_CTX(0, KTS_INT_ORIG)
308 jmp kernel_call_entry_common
310 ENTRY(kernel_call_entry_um)
311 SAVE_PROCESS_CTX(0, KTS_INT_UM)
312 jmp kernel_call_entry_common
314 ENTRY(kernel_call_entry_common)
315 /* save the pointer to the current process */
319 * pass the syscall arguments from userspace to the handler.
320 * SAVE_PROCESS_CTX() does not clobber these registers, they are still
321 * set as the userspace have set them
325 /* stop user process cycles */
327 /* for stack trace */
329 call _C_LABEL(context_stop)
332 call _C_LABEL(kernel_call)
334 /* restore the current process pointer and save the return value */
337 jmp _C_LABEL(switch_to_user)
342 * called by the exception interrupt vectors. If the exception does not push
343 * errorcode, we assume that the vector handler pushed 0 instead. Next pushed
344 * thing is the vector number. From this point on we can continue as if every
345 * exception pushes an error code
349 * check if it is a nested trap by comparing the saved code segment
350 * descriptor with the kernel CS first
352 TEST_INT_IN_KERNEL(12, exception_entry_nested)
354 exception_entry_from_user:
355 SAVE_PROCESS_CTX(8, KTS_INT_HARD)
357 /* stop user process cycles */
359 /* for stack trace clear %ebp */
361 call _C_LABEL(context_stop)
365 * push a pointer to the interrupt state pushed by the cpu and the
366 * vector number pushed by the vector handler just before calling
367 * exception_entry and call the exception handler.
370 push $0 /* it's not a nested exception */
371 call _C_LABEL(exception_handler)
373 jmp _C_LABEL(switch_to_user)
375 exception_entry_nested:
381 pushl $1 /* it's a nested exception */
382 call _C_LABEL(exception_handler)
386 /* clear the error code and the exception number */
388 /* resume execution at the point of exception */
391 ENTRY(restore_user_context_sysenter)
392 /* return to userspace using sysexit.
393 * most of the context saving the userspace process is
394 * responsible for, we just have to take care of the right EIP
395 * and ESP restoring here to resume execution, and set EAX and
396 * EBX to the saved status values.
398 mov 4(%esp), %ebp /* retrieve proc ptr arg */
399 movw $USER_DS_SELECTOR, %ax
401 mov PCREG(%ebp), %edx /* sysexit restores EIP using EDX */
402 mov SPREG(%ebp), %ecx /* sysexit restores ESP using ECX */
403 mov AXREG(%ebp), %eax /* trap return value */
404 mov BXREG(%ebp), %ebx /* secondary return value */
405 movl PSWREG(%ebp), %edi /* load desired PSW to EDI */
406 sti /* enable interrupts */
407 sysexit /* jump to EIP in user */
409 ENTRY(restore_user_context_syscall)
410 /* return to userspace using sysret.
411 * the procedure is very similar to sysexit; it requires
412 * manual %esp restoring, new EIP in ECX, does not require
413 * enabling interrupts, and of course sysret instead of sysexit.
415 mov 4(%esp), %ebp /* retrieve proc ptr arg */
416 mov PCREG(%ebp), %ecx /* sysret restores EIP using ECX */
417 mov SPREG(%ebp), %esp /* restore ESP directly */
418 mov AXREG(%ebp), %eax /* trap return value */
419 mov BXREG(%ebp), %ebx /* secondary return value */
420 movl PSWREG(%ebp), %edi /* load desired PSW to EDI */
421 sysret /* jump to EIP in user */
423 ENTRY(restore_user_context_int)
424 mov 4(%esp), %ebp /* will assume P_STACKBASE == 0 */
426 /* reconstruct the stack for iret */
427 push $USER_DS_SELECTOR /* ss */
428 movl SPREG(%ebp), %eax
430 movl PSWREG(%ebp), %eax
432 push $USER_CS_SELECTOR /* cs */
433 movl PCREG(%ebp), %eax
436 /* Restore segments as the user should see them. */
437 movw $USER_DS_SELECTOR, %si
443 /* Same for general-purpose registers. */
444 RESTORE_GP_REGS(%ebp)
446 movl BPREG(%ebp), %ebp
448 iret /* continue process */
450 /*===========================================================================*/
451 /* exception handlers */
452 /*===========================================================================*/
454 #define EXCEPTION_ERR_CODE(vector) \
458 #define EXCEPTION_NO_ERR_CODE(vector) \
460 EXCEPTION_ERR_CODE(vector)
463 EXCEPTION_NO_ERR_CODE(DIVIDE_VECTOR)
465 LABEL(single_step_exception)
466 EXCEPTION_NO_ERR_CODE(DEBUG_VECTOR)
470 EXCEPTION_NO_ERR_CODE(NMI_VECTOR)
473 * We have to be very careful as this interrupt can occur anytime. On
474 * the other hand, if it interrupts a user process, we will resume the
475 * same process which makes things a little simpler. We know that we are
476 * already on kernel stack whenever it happened and we can be
477 * conservative and save everything as we don't need to be extremely
478 * efficient as the interrupt is infrequent and some overhead is already
483 * save the important registers. We don't save %cs and %ss and they are
484 * saved and restored by CPU
493 * We cannot be sure about the state of the kernel segment register,
494 * however, we always set %ds and %es to the same as %ss
501 call _C_LABEL(nmi_watchdog_handler)
504 /* restore all the important registers as they were before the trap */
514 LABEL(breakpoint_exception)
515 EXCEPTION_NO_ERR_CODE(BREAKPOINT_VECTOR)
518 EXCEPTION_NO_ERR_CODE(OVERFLOW_VECTOR)
521 EXCEPTION_NO_ERR_CODE(BOUNDS_VECTOR)
524 EXCEPTION_NO_ERR_CODE(INVAL_OP_VECTOR)
526 LABEL(copr_not_available)
527 TEST_INT_IN_KERNEL(4, copr_not_available_in_kernel)
528 cld /* set direction flag to a known value */
529 SAVE_PROCESS_CTX(0, KTS_INT_HARD)
530 /* stop user process cycles */
533 call _C_LABEL(context_stop)
534 call _C_LABEL(copr_not_available_handler)
535 /* reached upon failure only */
536 jmp _C_LABEL(switch_to_user)
538 copr_not_available_in_kernel:
540 pushl $COPROC_NOT_VECTOR
541 jmp exception_entry_nested
544 EXCEPTION_ERR_CODE(DOUBLE_FAULT_VECTOR)
546 LABEL(copr_seg_overrun)
547 EXCEPTION_NO_ERR_CODE(COPROC_SEG_VECTOR)
550 EXCEPTION_ERR_CODE(INVAL_TSS_VECTOR)
552 LABEL(segment_not_present)
553 EXCEPTION_ERR_CODE(SEG_NOT_VECTOR)
555 LABEL(stack_exception)
556 EXCEPTION_ERR_CODE(STACK_FAULT_VECTOR)
558 LABEL(general_protection)
559 EXCEPTION_ERR_CODE(PROTECTION_VECTOR)
562 EXCEPTION_ERR_CODE(PAGE_FAULT_VECTOR)
565 EXCEPTION_NO_ERR_CODE(COPROC_ERR_VECTOR)
567 LABEL(alignment_check)
568 EXCEPTION_NO_ERR_CODE(ALIGNMENT_CHECK_VECTOR)
571 EXCEPTION_NO_ERR_CODE(MACHINE_CHECK_VECTOR)
573 LABEL(simd_exception)
574 EXCEPTION_NO_ERR_CODE(SIMD_EXCEPTION_VECTOR)
576 /*===========================================================================*/
578 /*===========================================================================*/
579 /* PUBLIC void reload_cr3(void); */
591 * we are in protected mode now, %cs is correct and we need to set the
592 * data descriptors before we can touch anything
594 * first load the regular, highly mapped idt, gdt
598 * use the boot stack for now. The running CPUs are already using their
599 * own stack, the rest is still waiting to be booted
601 movw $KERN_DS_SELECTOR, %ax
604 mov $_C_LABEL(k_boot_stktop) - 4, %esp
606 /* load the highly mapped idt, gdt, per-cpu tss */
607 call _C_LABEL(prot_load_selectors)
609 jmp _C_LABEL(smp_ap_boot)
613 /*===========================================================================*/
615 /*===========================================================================*/
618 .short 0x526F /* this must be the first data entry (magic #) */
623 LABEL(__k_unpaged_k_initial_stktop)
629 .space K_STACK_SIZE /* kernel stack */ /* FIXME use macro here */
630 LABEL(k_boot_stktop) /* top of kernel stack */
633 LABEL(k_stacks_start)
635 /* two pages for each stack, one for data, other as a sandbox */
636 .space 2 * (K_STACK_SIZE * CONFIG_MAX_CPUS)
640 /* top of kernel stack */