Remove building with NOCRYPTO option
[minix3.git] / minix / kernel / arch / i386 / mpx.S
blobfa38697e53246afaa097a79481746b385bcfd8ad
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(). 
6  *
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.
13  *
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. 
18  *
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.
22  */
24 #include "kernel/kernel.h" /* configures the kernel */
26 /* sections */
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"
39 #include "sconst.h"
40 #include <machine/multiboot.h>
42 #include "arch_proto.h" /* K_STACK_SIZE */
44 #ifdef CONFIG_SMP
45 #include "kernel/smp.h"
46 #endif
48 /* Selected 386 tss offsets. */
49 #define TSS3_S_SP0      4
51 IMPORT(usermapped_offset)
52 IMPORT(copr_not_available_handler)
53 IMPORT(params_size)
54 IMPORT(params_offset)
55 IMPORT(switch_to_user)
56 IMPORT(multiboot_init)
58 .text
59 /*===========================================================================*/
60 /*                              interrupt handlers                           */
61 /*              interrupt handlers for 386 32-bit protected mode             */
62 /*===========================================================================*/
64 #define PIC_IRQ_HANDLER(irq)    \
65         push    $irq                                                            ;\
66         call    _C_LABEL(irq_handle)    /* intr_handle(irq_handlers[irq]) */    ;\
67         add     $4, %esp                                                        ;
69 /*===========================================================================*/
70 /*                              hwint00 - 07                                 */
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)                                       ;\
76                                                                         \
77         SAVE_PROCESS_CTX(0, KTS_INT_HARD)                               ;\
78         push    %ebp                                                    ;\
79         movl    $0, %ebp        /* for stack trace */                   ;\
80         call    _C_LABEL(context_stop)                                  ;\
81         add     $4, %esp                                                ;\
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)                                ;\
86                                                                         \
87 0:                                                                      \
88         pusha                                                           ;\
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))                                            ;\
94         popa                                                            ;\
95         iret                                                            ;
97 /* Each of these entry points is an expansion of the hwint_master macro */
98 ENTRY(hwint00)
99 /* Interrupt routine for irq 0 (the clock). */
100         hwint_master(0)
102 ENTRY(hwint01)
103 /* Interrupt routine for irq 1 (keyboard) */
104         hwint_master(1)
106 ENTRY(hwint02)
107 /* Interrupt routine for irq 2 (cascade!) */
108         hwint_master(2)
110 ENTRY(hwint03)
111 /* Interrupt routine for irq 3 (second serial) */
112         hwint_master(3)
114 ENTRY(hwint04)
115 /* Interrupt routine for irq 4 (first serial) */
116         hwint_master(4)
118 ENTRY(hwint05)
119 /* Interrupt routine for irq 5 (XT winchester) */
120         hwint_master(5)
122 ENTRY(hwint06)
123 /* Interrupt routine for irq 6 (floppy) */
124         hwint_master(6)
126 ENTRY(hwint07)
127 /* Interrupt routine for irq 7 (printer) */
128         hwint_master(7)
130 /*===========================================================================*/
131 /*                              hwint08 - 15                                 */
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)                                       ;\
136                                                                         \
137         SAVE_PROCESS_CTX(0, KTS_INT_HARD)                               ;\
138         push    %ebp                                                    ;\
139         movl    $0, %ebp        /* for stack trace */                   ;\
140         call    _C_LABEL(context_stop)                                  ;\
141         add     $4, %esp                                                ;\
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)                                ;\
147                                                                         \
148 0:                                                                      \
149         pusha                                                           ;\
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))                                            ;\
156         popa                                                            ;\
157         iret                                                            ;
159 /* Each of these entry points is an expansion of the hwint_slave macro */
160 ENTRY(hwint08)
161 /* Interrupt routine for irq 8 (realtime clock) */
162         hwint_slave(8)
164 ENTRY(hwint09)
165 /* Interrupt routine for irq 9 (irq 2 redirected) */
166         hwint_slave(9)
168 ENTRY(hwint10)
169 /* Interrupt routine for irq 10 */
170         hwint_slave(10)
172 ENTRY(hwint11)
173 /* Interrupt routine for irq 11 */
174         hwint_slave(11)
176 ENTRY(hwint12)
177 /* Interrupt routine for irq 12 */
178         hwint_slave(12)
180 ENTRY(hwint13)
181 /* Interrupt routine for irq 13 (FPU exception) */
182         hwint_slave(13)
184 ENTRY(hwint14)
185 /* Interrupt routine for irq 14 (AT winchester) */
186         hwint_slave(14)
188 ENTRY(hwint15)
189 /* Interrupt routine for irq 15 */
190         hwint_slave(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
196  *     in %ecx
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
201  */
202 #define ipc_entry_syscall_percpu(cpu)                   ;\
203 ENTRY(ipc_entry_syscall_cpu ## cpu)                     ;\
204         xchg    %ecx, %edx                              ;\
205         mov     k_percpu_stacks+4*cpu, %esi             ;\
206         mov     (%esi), %ebp                            ;\
207         movl    $KTS_SYSCALL, P_KERN_TRAP_STYLE(%ebp)   ;\
208         xchg    %esp, %esi                              ;\
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.
224          *
225          * register usage:
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
229          *
230          * no state is automatically saved; userland does all of that.
231          */
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
236          */
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 */
244         /* save PSW */
245         pushf
246         pop     %edx
247         mov     %edx, PSWREG(%ebp)
249         /* check for call type; do_ipc? */
250         cmp     $IPCVEC_UM, %edi
251         jz      ipc_entry_common
253         /* check for kernel trap */
254         cmp     $KERVEC_UM, %edi
255         jz      kernel_call_entry_common
257         /* unrecognized call number; restore user with error */
258         movl    $-1, AXREG(%ebp)
259         push    %ebp    
260         call    restore_user_context    /* restore_user_context(%ebp); */
263  * IPC is only from a process to kernel
264  */
265 ENTRY(ipc_entry_softint_orig)
266         SAVE_PROCESS_CTX(0, KTS_INT_ORIG)
267         jmp ipc_entry_common
269 ENTRY(ipc_entry_softint_um)
270         SAVE_PROCESS_CTX(0, KTS_INT_UM)
271         jmp ipc_entry_common
273 ENTRY(ipc_entry_common)
274         /* save the pointer to the current process */
275         push    %ebp
277         /*
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
281          */
282         push    %ebx
283         push    %eax
284         push    %ecx
286         /* stop user process cycles */
287         push    %ebp
288         /* for stack trace */
289         movl    $0, %ebp
290         call    _C_LABEL(context_stop)
291         add     $4, %esp
293         call    _C_LABEL(do_ipc)
295         /* restore the current process pointer and save the return value */
296         add     $3 * 4, %esp
297         pop     %esi
298         mov     %eax, AXREG(%esi)
300         jmp     _C_LABEL(switch_to_user)
304  * kernel call is only from a process to kernel
305  */
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 */
316         push    %ebp
318         /*
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
322          */
323         push    %eax
325         /* stop user process cycles */
326         push    %ebp
327         /* for stack trace */
328         movl    $0, %ebp
329         call    _C_LABEL(context_stop)
330         add     $4, %esp
332         call    _C_LABEL(kernel_call)
334         /* restore the current process pointer and save the return value */
335         add     $8, %esp
337         jmp     _C_LABEL(switch_to_user)
340 .balign 16
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
346  */
347 exception_entry:
348         /*
349          * check if it is a nested trap by comparing the saved code segment
350          * descriptor with the kernel CS first
351          */
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 */
358         push    %ebp
359         /* for stack trace clear %ebp */
360         movl    $0, %ebp
361         call    _C_LABEL(context_stop)
362         add     $4, %esp
364         /*
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.
368          */
369         push    %esp
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:
377         pusha
378         mov     %esp, %eax
379         add     $(8 * 4), %eax
380         push    %eax
381         pushl   $1                      /* it's a nested exception */
382         call    _C_LABEL(exception_handler)
383         add     $8, %esp
384         popa
386         /* clear the error code and the exception number */
387         add     $8, %esp
388         /* resume execution at the point of exception */
389         iret
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.
397          */
398         mov     4(%esp), %ebp           /* retrieve proc ptr arg */
399         movw    $USER_DS_SELECTOR, %ax
400         movw    %ax, %ds
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 */
406         /* restore PSW */
407         movl    PSWREG(%ebp), %edi      /* load desired PSW to EDI */
408         push    %edi
409         popf
411         sti                             /* enable interrupts */
412         sysexit                         /* jump to EIP in user */
414 ENTRY(restore_user_context_syscall)
415         /* return to userspace using sysret.
416          * the procedure is very similar to sysexit; it requires
417          * manual %esp restoring, new EIP in ECX, does not require
418          * enabling interrupts, and of course sysret instead of sysexit.
419          */
420         mov     4(%esp), %ebp           /* retrieve proc ptr arg */
422         /* restore PSW (before we switch to user stack!) */
423         movl    PSWREG(%ebp), %edi      /* load desired PSW to EDI */
424         push    %edi
425         popf
427         mov     PCREG(%ebp), %ecx       /* sysret restores EIP using ECX */
428         mov     SPREG(%ebp), %esp       /* restore ESP directly */
429         mov     AXREG(%ebp), %eax       /* trap return value */
430         mov     BXREG(%ebp), %ebx       /* secondary return value */
432         sysret                          /* jump to EIP in user */
434 ENTRY(restore_user_context_int)
435         mov     4(%esp), %ebp   /* will assume P_STACKBASE == 0 */
437         /* reconstruct the stack for iret */
438         push    $USER_DS_SELECTOR       /* ss */
439         movl    SPREG(%ebp), %eax
440         push    %eax
441         movl    PSWREG(%ebp), %eax
442         push    %eax
443         push    $USER_CS_SELECTOR       /* cs */
444         movl    PCREG(%ebp), %eax
445         push    %eax
447         /* Restore segments as the user should see them. */
448         movw    $USER_DS_SELECTOR, %si
449         movw    %si, %ds
450         movw    %si, %es
451         movw    %si, %fs
452         movw    %si, %gs
454         /* Same for general-purpose registers. */
455         RESTORE_GP_REGS(%ebp)
457         movl    BPREG(%ebp), %ebp
459         iret    /* continue process */
461 /*===========================================================================*/
462 /*                              exception handlers                           */
463 /*===========================================================================*/
465 #define EXCEPTION_ERR_CODE(vector)      \
466         push    $vector                 ;\
467         jmp     exception_entry
469 #define EXCEPTION_NO_ERR_CODE(vector)   \
470         pushl   $0              ;\
471         EXCEPTION_ERR_CODE(vector)
473 LABEL(divide_error)
474         EXCEPTION_NO_ERR_CODE(DIVIDE_VECTOR)
476 LABEL(single_step_exception)
477         EXCEPTION_NO_ERR_CODE(DEBUG_VECTOR)
479 LABEL(nmi)
480 #ifndef USE_WATCHDOG
481         EXCEPTION_NO_ERR_CODE(NMI_VECTOR)
482 #else
483         /*
484          * We have to be very careful as this interrupt can occur anytime. On
485          * the other hand, if it interrupts a user process, we will resume the
486          * same process which makes things a little simpler. We know that we are
487          * already on kernel stack whenever it happened and we can be
488          * conservative and save everything as we don't need to be extremely
489          * efficient as the interrupt is infrequent and some overhead is already
490          * expected.
491          */
493         /*
494          * save the important registers. We don't save %cs and %ss and they are
495          * saved and restored by CPU
496          */
497         pushw   %ds
498         pushw   %es
499         pushw   %fs
500         pushw   %gs
501         pusha
503         /*
504          * We cannot be sure about the state of the kernel segment register,
505          * however, we always set %ds and %es to the same as %ss
506          */
507         mov     %ss, %si
508         mov     %si, %ds
509         mov     %si, %es
511         push    %esp
512         call    _C_LABEL(nmi_watchdog_handler)
513         add     $4, %esp
515         /* restore all the important registers as they were before the trap */
516         popa
517         popw    %gs
518         popw    %fs
519         popw    %es
520         popw    %ds
522         iret
523 #endif
525 LABEL(breakpoint_exception)
526         EXCEPTION_NO_ERR_CODE(BREAKPOINT_VECTOR)
528 LABEL(overflow)
529         EXCEPTION_NO_ERR_CODE(OVERFLOW_VECTOR)
531 LABEL(bounds_check)
532         EXCEPTION_NO_ERR_CODE(BOUNDS_VECTOR)
534 LABEL(inval_opcode)
535         EXCEPTION_NO_ERR_CODE(INVAL_OP_VECTOR)
537 LABEL(copr_not_available)
538         TEST_INT_IN_KERNEL(4, copr_not_available_in_kernel)
539         cld                     /* set direction flag to a known value */
540         SAVE_PROCESS_CTX(0, KTS_INT_HARD)
541         /* stop user process cycles */
542         push    %ebp
543         mov     $0, %ebp
544         call    _C_LABEL(context_stop)
545         call    _C_LABEL(copr_not_available_handler)
546         /* reached upon failure only */
547         jmp     _C_LABEL(switch_to_user)
549 copr_not_available_in_kernel:
550         pushl   $0
551         pushl   $COPROC_NOT_VECTOR
552         jmp     exception_entry_nested
554 LABEL(double_fault)
555         EXCEPTION_ERR_CODE(DOUBLE_FAULT_VECTOR)
557 LABEL(copr_seg_overrun)
558         EXCEPTION_NO_ERR_CODE(COPROC_SEG_VECTOR)
560 LABEL(inval_tss)
561         EXCEPTION_ERR_CODE(INVAL_TSS_VECTOR)
563 LABEL(segment_not_present)
564         EXCEPTION_ERR_CODE(SEG_NOT_VECTOR)
566 LABEL(stack_exception)
567         EXCEPTION_ERR_CODE(STACK_FAULT_VECTOR)
569 LABEL(general_protection)
570         EXCEPTION_ERR_CODE(PROTECTION_VECTOR)
572 LABEL(page_fault)
573         EXCEPTION_ERR_CODE(PAGE_FAULT_VECTOR)
575 LABEL(copr_error)
576         EXCEPTION_NO_ERR_CODE(COPROC_ERR_VECTOR)
578 LABEL(alignment_check)
579         EXCEPTION_NO_ERR_CODE(ALIGNMENT_CHECK_VECTOR)
581 LABEL(machine_check)
582         EXCEPTION_NO_ERR_CODE(MACHINE_CHECK_VECTOR)
584 LABEL(simd_exception)
585         EXCEPTION_NO_ERR_CODE(SIMD_EXCEPTION_VECTOR)
587 /*===========================================================================*/
588 /*                              reload_cr3                                   */
589 /*===========================================================================*/
590 /* PUBLIC void reload_cr3(void); */
591 ENTRY(reload_cr3)
592         push    %ebp
593         mov     %esp, %ebp
594         mov     %cr3, %eax
595         mov     %eax, %cr3
596         pop     %ebp
597         ret
599 #ifdef CONFIG_SMP
600 ENTRY(startup_ap_32)
601         /*
602          * we are in protected mode now, %cs is correct and we need to set the
603          * data descriptors before we can touch anything
604          *
605          * first load the regular, highly mapped idt, gdt
606          */
608         /*
609          * use the boot stack for now. The running CPUs are already using their
610          * own stack, the rest is still waiting to be booted
611          */
612         movw    $KERN_DS_SELECTOR, %ax
613         mov     %ax, %ds
614         mov     %ax, %ss
615         mov     $_C_LABEL(k_boot_stktop) - 4, %esp
617         /* load the highly mapped idt, gdt, per-cpu tss */
618         call    _C_LABEL(prot_load_selectors)
620         jmp     _C_LABEL(smp_ap_boot)
621         hlt
622 #endif
624 /*===========================================================================*/
625 /*                              data                                         */
626 /*===========================================================================*/
628 .data
629 .short  0x526F  /* this must be the first data entry (magic #) */
631 .bss
632 k_initial_stack:
633 .space  K_STACK_SIZE
634 LABEL(__k_unpaged_k_initial_stktop)
637  * the kernel stack
638  */
639 k_boot_stack:
640 .space  K_STACK_SIZE    /* kernel stack */ /* FIXME use macro here */
641 LABEL(k_boot_stktop)    /* top of kernel stack */
643 .balign K_STACK_SIZE
644 LABEL(k_stacks_start)
646 /* two pages for each stack, one for data, other as a sandbox */
647 .space  2 * (K_STACK_SIZE * CONFIG_MAX_CPUS)
649 LABEL(k_stacks_end)
651 /* top of kernel stack */