MP Implementation 2/4: Implement a poor-man's IPI messaging subsystem,
[dragonfly.git] / sys / i386 / i386 / exception.s
1 /*-
2  * Copyright (c) 1990 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by the University of
16  *      California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * $FreeBSD: src/sys/i386/i386/exception.s,v 1.65.2.3 2001/08/15 01:23:49 peter Exp $
34  * $DragonFly: src/sys/i386/i386/Attic/exception.s,v 1.12 2003/07/08 06:27:26 dillon Exp $
35  */
36
37 #include "npx.h"
38
39 #include <machine/asmacros.h>
40 #include <machine/ipl.h>
41 #include <machine/lock.h>
42 #include <machine/psl.h>
43 #include <machine/trap.h>
44 #ifdef SMP
45 #include <machine/smptests.h>           /** various SMP options */
46 #endif
47
48 #include "assym.s"
49
50 #define SEL_RPL_MASK    0x0003
51
52         .text
53
54 /*****************************************************************************/
55 /* Trap handling                                                             */
56 /*****************************************************************************/
57 /*
58  * Trap and fault vector routines.
59  *
60  * Most traps are 'trap gates', SDT_SYS386TGT.  A trap gate pushes state on
61  * the stack that mostly looks like an interrupt, but does not disable 
62  * interrupts.  A few of the traps we are use are interrupt gates, 
63  * SDT_SYS386IGT, which are nearly the same thing except interrupts are
64  * disabled on entry.
65  *
66  * The cpu will push a certain amount of state onto the kernel stack for
67  * the current process.  The amount of state depends on the type of trap 
68  * and whether the trap crossed rings or not.  See i386/include/frame.h.  
69  * At the very least the current EFLAGS (status register, which includes 
70  * the interrupt disable state prior to the trap), the code segment register,
71  * and the return instruction pointer are pushed by the cpu.  The cpu 
72  * will also push an 'error' code for certain traps.  We push a dummy 
73  * error code for those traps where the cpu doesn't in order to maintain 
74  * a consistent frame.  We also push a contrived 'trap number'.
75  *
76  * The cpu does not push the general registers, we must do that, and we 
77  * must restore them prior to calling 'iret'.  The cpu adjusts the %cs and
78  * %ss segment registers, but does not mess with %ds, %es, or %fs.  Thus we
79  * must load them with appropriate values for supervisor mode operation.
80  *
81  * On entry to a trap or interrupt WE DO NOT OWN THE MP LOCK.  This means
82  * that we must be careful in regards to accessing global variables.  We
83  * save (push) the current cpl (our software interrupt disable mask), call
84  * the trap function, then jump to doreti to restore the cpl and deal with
85  * ASTs (software interrupts).  doreti will determine if the restoration
86  * of the cpl unmasked any pending interrupts and will issue those interrupts
87  * synchronously prior to doing the iret.
88  */
89 #define IDTVEC(name)    ALIGN_TEXT; .globl __CONCAT(X,name); \
90                         .type __CONCAT(X,name),@function; __CONCAT(X,name):
91 #define TRAP(a)         pushl $(a) ; jmp alltraps
92
93 #ifdef BDE_DEBUGGER
94 #define BDBTRAP(name) \
95         ss ; \
96         cmpb    $0,bdb_exists ; \
97         je      1f ; \
98         testb   $SEL_RPL_MASK,4(%esp) ; \
99         jne     1f ; \
100         ss ; \
101         .globl  __CONCAT(__CONCAT(bdb_,name),_ljmp); \
102 __CONCAT(__CONCAT(bdb_,name),_ljmp): \
103         ljmp    $0,$0 ; \
104 1:
105 #else
106 #define BDBTRAP(name)
107 #endif
108
109 #define BPTTRAP(a)      testl $PSL_I,4+8(%esp) ; je 1f ; sti ; 1: ; TRAP(a)
110
111 MCOUNT_LABEL(user)
112 MCOUNT_LABEL(btrap)
113
114 IDTVEC(div)
115         pushl $0; TRAP(T_DIVIDE)
116 IDTVEC(dbg)
117         BDBTRAP(dbg)
118         pushl $0; BPTTRAP(T_TRCTRAP)
119 IDTVEC(nmi)
120         pushl $0; TRAP(T_NMI)
121 IDTVEC(bpt)
122         BDBTRAP(bpt)
123         pushl $0; BPTTRAP(T_BPTFLT)
124 IDTVEC(ofl)
125         pushl $0; TRAP(T_OFLOW)
126 IDTVEC(bnd)
127         pushl $0; TRAP(T_BOUND)
128 IDTVEC(ill)
129         pushl $0; TRAP(T_PRIVINFLT)
130 IDTVEC(dna)
131         pushl $0; TRAP(T_DNA)
132 IDTVEC(fpusegm)
133         pushl $0; TRAP(T_FPOPFLT)
134 IDTVEC(tss)
135         TRAP(T_TSSFLT)
136 IDTVEC(missing)
137         TRAP(T_SEGNPFLT)
138 IDTVEC(stk)
139         TRAP(T_STKFLT)
140 IDTVEC(prot)
141         TRAP(T_PROTFLT)
142 IDTVEC(page)
143         TRAP(T_PAGEFLT)
144 IDTVEC(mchk)
145         pushl $0; TRAP(T_MCHK)
146 IDTVEC(rsvd)
147         pushl $0; TRAP(T_RESERVED)
148
149 IDTVEC(fpu)
150 #if NNPX > 0
151         /*
152          * Handle like an interrupt (except for accounting) so that we can
153          * call npx_intr to clear the error.  It would be better to handle
154          * npx interrupts as traps.  Nested interrupts would probably have
155          * to be converted to ASTs.
156          */
157         pushl   $0                      /* dummy error code */
158         pushl   $0                      /* dummy trap type */
159         pushal
160         pushl   %ds
161         pushl   %es                     /* now stack frame is a trap frame */
162         pushl   %fs
163         mov     $KDSEL,%ax
164         mov     %ax,%ds
165         mov     %ax,%es
166         mov     $KPSEL,%ax
167         mov     %ax,%fs
168         FAKE_MCOUNT(13*4(%esp))
169
170         movl    PCPU(curthread),%ebx            /* save original cpl */
171         movl    TD_CPL(%ebx), %ebx
172         pushl   %ebx
173         incl    PCPU(cnt)+V_TRAP
174
175         call    npx_intr                /* note: call might mess w/ argument */
176
177         movl    %ebx, (%esp)            /* save cpl for doreti */
178         incl    PCPU(intr_nesting_level)
179         MEXITCOUNT
180         jmp     doreti
181 #else   /* NNPX > 0 */
182         pushl $0; TRAP(T_ARITHTRAP)
183 #endif  /* NNPX > 0 */
184
185 IDTVEC(align)
186         TRAP(T_ALIGNFLT)
187
188 IDTVEC(xmm)
189         pushl $0; TRAP(T_XMMFLT)
190         
191         /*
192          * _alltraps entry point.  Interrupts are enabled if this was a trap
193          * gate (TGT), else disabled if this was an interrupt gate (IGT).
194          * Note that int0x80_syscall is a trap gate.  Only page faults
195          * use an interrupt gate.
196          *
197          * Note that we are MP through to the call to trap().
198          */
199
200         SUPERALIGN_TEXT
201         .globl  alltraps
202         .type   alltraps,@function
203 alltraps:
204         pushal
205         pushl   %ds
206         pushl   %es
207         pushl   %fs
208 alltraps_with_regs_pushed:
209         mov     $KDSEL,%ax
210         mov     %ax,%ds
211         mov     %ax,%es
212         mov     $KPSEL,%ax
213         mov     %ax,%fs
214         FAKE_MCOUNT(13*4(%esp))
215 calltrap:
216         FAKE_MCOUNT(btrap)              /* init "from" _btrap -> calltrap */
217         incl    PCPU(cnt)+V_TRAP
218         movl    PCPU(curthread),%eax    /* keep orig cpl here during call */
219         movl    TD_CPL(%eax),%ebx
220         call    trap
221
222         /*
223          * Return via doreti to handle ASTs.  Have to change trap frame
224          * to interrupt frame.
225          */
226         pushl   %ebx                    /* cpl to restore */
227         incl    PCPU(intr_nesting_level)
228         MEXITCOUNT
229         jmp     doreti
230
231 /*
232  * SYSCALL CALL GATE (old entry point for a.out binaries)
233  *
234  * The intersegment call has been set up to specify one dummy parameter.
235  *
236  * This leaves a place to put eflags so that the call frame can be
237  * converted to a trap frame. Note that the eflags is (semi-)bogusly
238  * pushed into (what will be) tf_err and then copied later into the
239  * final spot. It has to be done this way because esp can't be just
240  * temporarily altered for the pushfl - an interrupt might come in
241  * and clobber the saved cs/eip.
242  *
243  * We do not obtain the MP lock, but the call to syscall2 might.  If it
244  * does it will release the lock prior to returning.
245  */
246         SUPERALIGN_TEXT
247 IDTVEC(syscall)
248         pushfl                          /* save eflags in tf_err for now */
249         subl    $4,%esp                 /* skip over tf_trapno */
250         pushal
251         pushl   %ds
252         pushl   %es
253         pushl   %fs
254         mov     $KDSEL,%ax              /* switch to kernel segments */
255         mov     %ax,%ds
256         mov     %ax,%es
257         mov     $KPSEL,%ax
258         mov     %ax,%fs
259         movl    TF_ERR(%esp),%eax       /* copy saved eflags to final spot */
260         movl    %eax,TF_EFLAGS(%esp)
261         movl    $7,TF_ERR(%esp)         /* sizeof "lcall 7,0" */
262         FAKE_MCOUNT(13*4(%esp))
263         incl    PCPU(cnt)+V_SYSCALL     /* YYY per-cpu */
264         call    syscall2
265         MEXITCOUNT
266         cli                             /* atomic astpending access */
267         cmpl    $0,PCPU(astpending)
268         je      doreti_syscall_ret
269         pushl   $0                      /* cpl to restore */
270         movl    $1,PCPU(intr_nesting_level)
271         jmp     doreti
272
273 /*
274  * Call gate entry for FreeBSD ELF and Linux/NetBSD syscall (int 0x80)
275  *
276  * Even though the name says 'int0x80', this is actually a TGT (trap gate)
277  * rather then an IGT (interrupt gate).  Thus interrupts are enabled on
278  * entry just as they are for a normal syscall.
279  *
280  * We do not obtain the MP lock, but the call to syscall2 might.  If it
281  * does it will release the lock prior to returning.
282  */
283         SUPERALIGN_TEXT
284 IDTVEC(int0x80_syscall)
285         subl    $8,%esp                 /* skip over tf_trapno and tf_err */
286         pushal
287         pushl   %ds
288         pushl   %es
289         pushl   %fs
290         mov     $KDSEL,%ax              /* switch to kernel segments */
291         mov     %ax,%ds
292         mov     %ax,%es
293         mov     $KPSEL,%ax
294         mov     %ax,%fs
295         movl    $2,TF_ERR(%esp)         /* sizeof "int 0x80" */
296         FAKE_MCOUNT(13*4(%esp))
297         incl    PCPU(cnt)+V_SYSCALL     /* YYY per-cpu */
298         call    syscall2
299         MEXITCOUNT
300         cli                             /* atomic astpending access */
301         cmpl    $0,PCPU(astpending)
302         je      doreti_syscall_ret
303         pushl   $0                      /* cpl to restore */
304         movl    $1,PCPU(intr_nesting_level)
305         jmp     doreti
306
307 /*
308  * This function is what cpu_heavy_restore jumps to after a new process
309  * is created.  We are in a critical section in order to prevent
310  * cpu_heavy_restore from being interrupted (especially since it stores
311  * its context in a static place!).
312  *
313  * The MP lock is held on entry, but for processes fork_return (esi)
314  * releases it.  'doreti' always runs without the MP lock.
315  *
316  * We need to be careful to hold interrupts disabled through until
317  * doreti iret's YYY this is due to the PCB_ storage in the heavy switcher,
318  * fixme!
319  */
320 ENTRY(fork_trampoline)
321         cli
322         movl    PCPU(curthread),%eax
323         subl    $TDPRI_CRIT,TD_PRI(%eax)
324
325         /*
326          * cpu_set_fork_handler intercepts this function call to
327          * have this call a non-return function to stay in kernel mode.
328          *
329          * initproc has its own fork handler, start_init(), which DOES
330          * return.
331          */
332         pushl   %ebx                    /* arg1 */
333         call    *%esi                   /* function */
334         addl    $4,%esp
335         /* cut from syscall */
336
337         call    spl0
338         call    splz
339
340 #if defined(INVARIANTS) && defined(SMP)
341         movl    PCPU(curthread),%eax
342         cmpl    $0,TD_MPCOUNT(%eax)
343         je      1f
344         pushl   %esi
345         pushl   TD_MPCOUNT(%eax)
346         pushl   $pmsg4
347         call    panic
348 pmsg4:  .asciz  "fork_trampoline mpcount %d after calling %p"
349 1:
350 #endif
351         /*
352          * Return via doreti to handle ASTs.
353          */
354         pushl   $0                      /* cpl to restore */
355         movl    $1,PCPU(intr_nesting_level)
356         MEXITCOUNT
357         jmp     doreti
358
359
360 /*
361  * Include vm86 call routines, which want to call doreti.
362  */
363 #include "i386/i386/vm86bios.s"
364
365 /*
366  * Include what was once config+isa-dependent code.
367  * XXX it should be in a stand-alone file.  It's still icu-dependent and
368  * belongs in i386/isa.
369  */
370 #include "i386/isa/vector.s"
371
372 /*
373  * Include what was once icu-dependent code.
374  * XXX it should be merged into this file (also move the definition of
375  * imen to vector.s or isa.c).
376  * Before including it, set up a normal asm environment so that vector.s
377  * doesn't have to know that stuff is included after it.
378  */
379         .data
380         ALIGN_DATA
381         .text
382         SUPERALIGN_TEXT
383 #include "i386/isa/ipl.s"