make vfs & filesystems use failable copying
[minix3.git] / kernel / arch / i386 / mpx.S
blob4617bfdc2bdaeea0ea6772f41bee7d8a44970262
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 */
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.
414          */
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
429         push    %eax
430         movl    PSWREG(%ebp), %eax
431         push    %eax
432         push    $USER_CS_SELECTOR       /* cs */
433         movl    PCREG(%ebp), %eax
434         push    %eax
436         /* Restore segments as the user should see them. */
437         movw    $USER_DS_SELECTOR, %si
438         movw    %si, %ds
439         movw    %si, %es
440         movw    %si, %fs
441         movw    %si, %gs
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)      \
455         push    $vector                 ;\
456         jmp     exception_entry
458 #define EXCEPTION_NO_ERR_CODE(vector)   \
459         pushl   $0              ;\
460         EXCEPTION_ERR_CODE(vector)
462 LABEL(divide_error)
463         EXCEPTION_NO_ERR_CODE(DIVIDE_VECTOR)
465 LABEL(single_step_exception)
466         EXCEPTION_NO_ERR_CODE(DEBUG_VECTOR)
468 LABEL(nmi)
469 #ifndef USE_WATCHDOG
470         EXCEPTION_NO_ERR_CODE(NMI_VECTOR)
471 #else
472         /*
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
479          * expected.
480          */
482         /*
483          * save the important registers. We don't save %cs and %ss and they are
484          * saved and restored by CPU
485          */
486         pushw   %ds
487         pushw   %es
488         pushw   %fs
489         pushw   %gs
490         pusha
492         /*
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
495          */
496         mov     %ss, %si
497         mov     %si, %ds
498         mov     %si, %es
500         push    %esp
501         call    _C_LABEL(nmi_watchdog_handler)
502         add     $4, %esp
504         /* restore all the important registers as they were before the trap */
505         popa
506         popw    %gs
507         popw    %fs
508         popw    %es
509         popw    %ds
511         iret
512 #endif
514 LABEL(breakpoint_exception)
515         EXCEPTION_NO_ERR_CODE(BREAKPOINT_VECTOR)
517 LABEL(overflow)
518         EXCEPTION_NO_ERR_CODE(OVERFLOW_VECTOR)
520 LABEL(bounds_check)
521         EXCEPTION_NO_ERR_CODE(BOUNDS_VECTOR)
523 LABEL(inval_opcode)
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 */
531         push    %ebp
532         mov     $0, %ebp
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:
539         pushl   $0
540         pushl   $COPROC_NOT_VECTOR
541         jmp     exception_entry_nested
543 LABEL(double_fault)
544         EXCEPTION_ERR_CODE(DOUBLE_FAULT_VECTOR)
546 LABEL(copr_seg_overrun)
547         EXCEPTION_NO_ERR_CODE(COPROC_SEG_VECTOR)
549 LABEL(inval_tss)
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)
561 LABEL(page_fault)
562         EXCEPTION_ERR_CODE(PAGE_FAULT_VECTOR)
564 LABEL(copr_error)
565         EXCEPTION_NO_ERR_CODE(COPROC_ERR_VECTOR)
567 LABEL(alignment_check)
568         EXCEPTION_NO_ERR_CODE(ALIGNMENT_CHECK_VECTOR)
570 LABEL(machine_check)
571         EXCEPTION_NO_ERR_CODE(MACHINE_CHECK_VECTOR)
573 LABEL(simd_exception)
574         EXCEPTION_NO_ERR_CODE(SIMD_EXCEPTION_VECTOR)
576 /*===========================================================================*/
577 /*                              reload_cr3                                   */
578 /*===========================================================================*/
579 /* PUBLIC void reload_cr3(void); */
580 ENTRY(reload_cr3)
581         push    %ebp
582         mov     %esp, %ebp
583         mov     %cr3, %eax
584         mov     %eax, %cr3
585         pop     %ebp
586         ret
588 #ifdef CONFIG_SMP
589 ENTRY(startup_ap_32)
590         /*
591          * we are in protected mode now, %cs is correct and we need to set the
592          * data descriptors before we can touch anything
593          *
594          * first load the regular, highly mapped idt, gdt
595          */
597         /*
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
600          */
601         movw    $KERN_DS_SELECTOR, %ax
602         mov     %ax, %ds
603         mov     %ax, %ss
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)
610         hlt
611 #endif
613 /*===========================================================================*/
614 /*                              data                                         */
615 /*===========================================================================*/
617 .data
618 .short  0x526F  /* this must be the first data entry (magic #) */
620 .bss
621 k_initial_stack:
622 .space  K_STACK_SIZE
623 LABEL(__k_unpaged_k_initial_stktop)
626  * the kernel stack
627  */
628 k_boot_stack:
629 .space  K_STACK_SIZE    /* kernel stack */ /* FIXME use macro here */
630 LABEL(k_boot_stktop)    /* top of kernel stack */
632 .balign K_STACK_SIZE
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)
638 LABEL(k_stacks_end)
640 /* top of kernel stack */