kernel - Fix serious issue w/ smp_invltlb(), plus other issues (3)
[dragonfly.git] / sys / platform / pc32 / isa / ipl.s
1 /*-
2  * Copyright (c) 1989, 1990 William F. Jolitz.
3  * Copyright (c) 1990 The Regents of the University of California.
4  * All rights reserved.
5  *
6  * This code is derived from software contributed to Berkeley by
7  * William Jolitz.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. All advertising materials mentioning features or use of this software
18  *    must display the following acknowledgement:
19  *      This product includes software developed by the University of
20  *      California, Berkeley and its contributors.
21  * 4. Neither the name of the University nor the names of its contributors
22  *    may be used to endorse or promote products derived from this software
23  *    without specific prior written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35  * SUCH DAMAGE.
36  *
37  *      @(#)ipl.s
38  *
39  * $FreeBSD: src/sys/i386/isa/ipl.s,v 1.32.2.3 2002/05/16 16:03:56 bde Exp $
40  * $DragonFly: src/sys/platform/pc32/isa/ipl.s,v 1.28 2007/01/22 19:37:04 corecode Exp $
41  */
42
43 #include "use_npx.h"
44
45 #include <machine/asmacros.h>
46 #include <machine/segments.h>
47 #include <machine/ipl.h>
48 #include <machine/lock.h>
49 #include <machine/psl.h>
50 #include <machine/trap.h>
51  
52 #include "assym.s"
53
54 /*
55  * AT/386
56  * Vector interrupt control section
57  *
58  *  fpending    - Pending interrupts (set when a masked interrupt occurs)
59  *  spending    - Pending software interrupts
60  */
61         .data
62         ALIGN_DATA
63
64         .globl          fastunpend_count
65 fastunpend_count:       .long   0
66
67         .text
68         SUPERALIGN_TEXT
69
70         /*
71          * GENERAL NOTES
72          *
73          *      - fast interrupts are always called with a critical section
74          *        held
75          *
76          *      - we release our critical section when scheduling interrupt
77          *        or softinterrupt threads in order so they can preempt
78          *        (unless we are called manually from a critical section, in
79          *        which case there will still be a critical section and
80          *        they won't preempt anyway).
81          *
82          *      - TD_NEST_COUNT prevents splz from nesting too deeply within
83          *        itself.  It is *not* actually an interrupt nesting count.
84          *        PCPU(intr_nesting_level) is an interrupt nesting count.
85          *
86          *      - We have to be careful in regards to local interrupts
87          *        occuring simultaniously with our doreti and splz 
88          *        processing.
89          */
90
91         /*
92          * DORETI
93          *
94          * Handle return from interrupts, traps and syscalls.  This function
95          * checks the cpl for unmasked pending interrupts (fast, normal, or
96          * soft) and schedules them if appropriate, then irets.
97          *
98          * If we are in a critical section we cannot run any pending ints
99          * nor can be play with mp_lock.
100          *
101          * NOTE: Since SPLs no longer exist, all callers of this function
102          * push $0 for the CPL.  HOWEVER, we *STILL* use the cpl mask within
103          * this function to mark fast interrupts which could not be dispatched
104          * do to the unavailability of the BGL.
105          */
106         SUPERALIGN_TEXT
107         .globl  doreti
108         .type   doreti,@function
109 doreti:
110         FAKE_MCOUNT(bintr)              /* init "from" bintr -> doreti */
111         popl    %eax                    /* cpl to restore XXX */
112         movl    $0,%eax                 /* irq mask unavailable due to BGL */
113         movl    PCPU(curthread),%ebx
114         cli                             /* interlock with td_critcount */
115         cmpl    $0,PCPU(reqflags)       /* short cut if nothing to do */
116         je      5f
117         testl   $-1,TD_CRITCOUNT(%ebx)  /* can't unpend if in critical sec */
118         jne     5f
119         incl    TD_CRITCOUNT(%ebx)      /* force all ints to pending */
120 doreti_next:
121         cli                             /* re-assert cli on loop */
122         movl    %eax,%ecx               /* irq mask unavailable due to BGL */
123         notl    %ecx
124 #ifdef SMP
125         testl   $RQF_IPIQ,PCPU(reqflags)
126         jnz     doreti_ipiq
127         testl   $RQF_TIMER,PCPU(reqflags)
128         jnz     doreti_timer
129 #endif
130         testl   PCPU(fpending),%ecx     /* check for an unmasked fast int */
131         jnz     doreti_fast
132
133         movl    PCPU(spending),%ecx     /* check for a pending software int */
134         cmpl    $0,%ecx
135         jnz     doreti_soft
136
137         testl   $RQF_AST_MASK,PCPU(reqflags) /* any pending ASTs? */
138         jz      2f
139         testl   $PSL_VM,TF_EFLAGS(%esp)
140         jz      1f
141         cmpl    $1,in_vm86call          /* YYY make per 'cpu'? */
142         jnz     doreti_ast
143 1:
144         /* ASTs are only applicable when returning to userland */
145         testb   $SEL_RPL_MASK,TF_CS(%esp)
146         jnz     doreti_ast
147 2:
148         /*
149          * Nothing left to do, finish up.  Interrupts are still disabled.
150          * %eax contains the mask of IRQ's that are not available due to
151          * BGL requirements.  We can only clear RQF_INTPEND if *ALL* pending
152          * interrupts have been processed.
153          */
154         decl    TD_CRITCOUNT(%ebx)      /* interlocked with cli */
155         testl   %eax,%eax
156         jnz     5f
157         andl    $~RQF_INTPEND,PCPU(reqflags)
158 5:
159         MEXITCOUNT
160
161         /*
162          * Restore the segment registers.  Since segment register values
163          * can be set from user mode, this can result in a kernel mode
164          * exception.  The trap code will revector to the *_fault code
165          * which then sets up a T_PROTFLT signal.  If the signal is
166          * sent to userland, sendsig() will automatically clean up all
167          * the segment registers to avoid a loop.
168          */
169         .globl  doreti_popl_gs
170         .globl  doreti_popl_fs
171         .globl  doreti_popl_es
172         .globl  doreti_popl_ds
173         .globl  doreti_iret
174         .globl  doreti_syscall_ret
175 doreti_syscall_ret:
176 doreti_popl_gs:
177         popl    %gs
178 doreti_popl_fs:
179         popl    %fs
180 doreti_popl_es:
181         popl    %es
182 doreti_popl_ds:
183         popl    %ds
184         popal
185         addl    $3*4,%esp       /* xflags, trap, err */
186 doreti_iret:
187         iret
188
189         /*
190          * Interrupts are likely disabled due to the above interlock
191          * between cli/iretq.  We must enable them before calling any
192          * high level function.
193          */
194         ALIGN_TEXT
195         .globl  doreti_iret_fault
196 doreti_iret_fault:
197         subl    $3*4,%esp       /* xflags, trap, err */
198         pushal
199         pushl   %ds
200         .globl  doreti_popl_ds_fault
201 doreti_popl_ds_fault:
202         pushl   %es
203         .globl  doreti_popl_es_fault
204 doreti_popl_es_fault:
205         pushl   %fs
206         .globl  doreti_popl_fs_fault
207 doreti_popl_fs_fault:
208         pushl   %gs
209         .globl  doreti_popl_gs_fault
210 doreti_popl_gs_fault:
211         sti
212         movl    $0,TF_ERR(%esp) /* XXX should be the error code */
213         movl    $T_PROTFLT,TF_TRAPNO(%esp)
214         jmp     alltraps_with_regs_pushed
215
216         /*
217          * FAST interrupt pending.  NOTE: stack context holds frame structure
218          * for fast interrupt procedure, do not do random pushes or pops!
219          */
220         ALIGN_TEXT
221 doreti_fast:
222         andl    PCPU(fpending),%ecx     /* only check fast ints */
223         sti
224         bsfl    %ecx, %ecx              /* locate the next dispatchable int */
225         btrl    %ecx, PCPU(fpending)    /* is it really still pending? */
226         jnc     doreti_next
227         pushl   %eax                    /* save IRQ mask unavailable for BGL */
228                                         /* NOTE: is also CPL in frame */
229         call    dofastunpend            /* unpend fast intr %ecx */
230         popl    %eax
231         jmp     doreti_next
232
233         /*
234          *  SOFT interrupt pending
235          *
236          *  Temporarily back-out our critical section to allow an interrupt
237          *  preempt us when we schedule it.  Bump intr_nesting_level to
238          *  prevent the switch code from recursing via splz too deeply.
239          */
240         ALIGN_TEXT
241 doreti_soft:
242         sti
243         bsfl    %ecx,%ecx               /* locate the next pending softint */
244         btrl    %ecx,PCPU(spending)     /* make sure its still pending */
245         jnc     doreti_next
246         addl    $FIRST_SOFTINT,%ecx     /* actual intr number */
247         pushl   %eax
248         pushl   %ecx
249         incl    TD_NEST_COUNT(%ebx)     /* prevent doreti/splz nesting */
250         decl    TD_CRITCOUNT(%ebx)      /* so we can preempt */
251         call    sched_ithd              /* YYY must pull in imasks */
252         incl    TD_CRITCOUNT(%ebx)
253         decl    TD_NEST_COUNT(%ebx)
254         addl    $4,%esp
255         popl    %eax
256         jmp     doreti_next
257
258         /*
259          * AST pending.  We clear RQF_AST_SIGNAL automatically, the others
260          * are cleared by the trap as they are processed.
261          *
262          * Temporarily back-out our critical section because trap() can be
263          * a long-winded call, and we want to be more syscall-like.  
264          *
265          * YYY theoretically we can call lwkt_switch directly if all we need
266          * to do is a reschedule.
267          */
268 doreti_ast:
269         andl    $~(RQF_AST_SIGNAL|RQF_AST_UPCALL),PCPU(reqflags)
270         sti
271         movl    %eax,%esi               /* save cpl (can't use stack) */
272         movl    $T_ASTFLT,TF_TRAPNO(%esp)
273         pushl   %esp                    /* pass frame by reference */
274         decl    TD_CRITCOUNT(%ebx)
275         call    trap
276         incl    TD_CRITCOUNT(%ebx)
277         addl    $4,%esp
278         movl    %esi,%eax               /* restore cpl for loop */
279         jmp     doreti_next
280
281 #ifdef SMP
282         /*
283          * IPIQ message pending.  We clear RQF_IPIQ automatically.
284          */
285 doreti_ipiq:
286         movl    %eax,%esi               /* save cpl (can't use stack) */
287         incl    PCPU(intr_nesting_level)
288         andl    $~RQF_IPIQ,PCPU(reqflags)
289         sti
290         subl    $8,%esp                 /* add dummy vec and ppl */
291         pushl   %esp                    /* pass frame by reference */
292         call    lwkt_process_ipiq_frame
293         addl    $12,%esp
294         decl    PCPU(intr_nesting_level)
295         movl    %esi,%eax               /* restore cpl for loop */
296         jmp     doreti_next
297
298 doreti_timer:
299         movl    %eax,%esi               /* save cpl (can't use stack) */
300         incl    PCPU(intr_nesting_level)
301         andl    $~RQF_TIMER,PCPU(reqflags)
302         sti
303         subl    $8,%esp                 /* add dummy vec and ppl */
304         pushl   %esp                    /* pass frame by reference */
305         call    lapic_timer_process_frame
306         addl    $12,%esp
307         decl    PCPU(intr_nesting_level)
308         movl    %esi,%eax               /* restore cpl for loop */
309         jmp     doreti_next
310
311 #endif
312
313         /*
314          * SPLZ() a C callable procedure to dispatch any unmasked pending
315          *        interrupts regardless of critical section nesting.  ASTs
316          *        are not dispatched.
317          *
318          *        Use %eax to track those IRQs that could not be processed
319          *        due to BGL requirements.
320          */
321         SUPERALIGN_TEXT
322
323 ENTRY(splz)
324         pushfl
325         pushl   %ebx
326         movl    PCPU(curthread),%ebx
327         incl    TD_CRITCOUNT(%ebx)
328         movl    $0,%eax
329
330 splz_next:
331         cli
332         movl    %eax,%ecx               /* ecx = ~CPL */
333         notl    %ecx
334 #ifdef SMP
335         testl   $RQF_IPIQ,PCPU(reqflags)
336         jnz     splz_ipiq
337         testl   $RQF_TIMER,PCPU(reqflags)
338         jnz     splz_timer
339 #endif
340         testl   PCPU(fpending),%ecx     /* check for an unmasked fast int */
341         jnz     splz_fast
342
343         movl    PCPU(spending),%ecx
344         cmpl    $0,%ecx
345         jnz     splz_soft
346
347         decl    TD_CRITCOUNT(%ebx)
348
349         /*
350          * Nothing left to do, finish up.  Interrupts are still disabled.
351          * If our mask of IRQs we couldn't process due to BGL requirements
352          * is 0 then there are no pending interrupt sources left and we
353          * can clear RQF_INTPEND.
354          */
355         testl   %eax,%eax
356         jnz     5f
357         andl    $~RQF_INTPEND,PCPU(reqflags)
358 5:
359         popl    %ebx
360         popfl
361         ret
362
363         /*
364          * FAST interrupt pending
365          */
366         ALIGN_TEXT
367 splz_fast:
368         andl    PCPU(fpending),%ecx     /* only check fast ints */
369         sti
370         bsfl    %ecx, %ecx              /* locate the next dispatchable int */
371         btrl    %ecx, PCPU(fpending)    /* is it really still pending? */
372         jnc     splz_next
373         pushl   %eax
374         call    dofastunpend            /* unpend fast intr %ecx */
375         popl    %eax
376         jmp     splz_next
377
378         /*
379          *  SOFT interrupt pending
380          *
381          *  Temporarily back-out our critical section to allow the interrupt
382          *  preempt us.
383          */
384         ALIGN_TEXT
385 splz_soft:
386         sti
387         bsfl    %ecx,%ecx               /* locate the next pending softint */
388         btrl    %ecx,PCPU(spending)     /* make sure its still pending */
389         jnc     splz_next
390         addl    $FIRST_SOFTINT,%ecx     /* actual intr number */
391         pushl   %eax
392         pushl   %ecx
393         decl    TD_CRITCOUNT(%ebx)
394         incl    TD_NEST_COUNT(%ebx)     /* prevent doreti/splz nesting */
395         call    sched_ithd              /* YYY must pull in imasks */
396         incl    TD_CRITCOUNT(%ebx)
397         decl    TD_NEST_COUNT(%ebx)     /* prevent doreti/splz nesting */
398         addl    $4,%esp
399         popl    %eax
400         jmp     splz_next
401
402 #ifdef SMP
403 splz_ipiq:
404         andl    $~RQF_IPIQ,PCPU(reqflags)
405         sti
406         pushl   %eax
407         call    lwkt_process_ipiq
408         popl    %eax
409         jmp     splz_next
410
411 splz_timer:
412         andl    $~RQF_TIMER,PCPU(reqflags)
413         sti
414         pushl   %eax
415         call    lapic_timer_process
416         popl    %eax
417         jmp     splz_next
418 #endif
419
420         /*
421          * dofastunpend(%ecx:intr)
422          *
423          * A FAST interrupt previously made pending can now be run,
424          * execute it by pushing a dummy interrupt frame and 
425          * calling ithread_fast_handler to execute or schedule it.
426          * 
427          * ithread_fast_handler() returns 0 if it wants us to unmask
428          * further interrupts.
429          */
430 #define PUSH_DUMMY                                                      \
431         pushfl ;                /* phys int frame / flags */            \
432         pushl   %cs ;           /* phys int frame / cs */               \
433         pushl   12(%esp) ;      /* original caller eip */               \
434         pushl   $0 ;            /* dummy error code */                  \
435         pushl   $0 ;            /* dummy trap type */                   \
436         pushl   $0 ;            /* dummy xflags */                      \
437         subl    $13*4,%esp ;    /* pushal + 4 seg regs (dummy) + CPL */ \
438
439 #define POP_DUMMY                                                       \
440         addl    $19*4,%esp ;                                            \
441
442 dofastunpend:
443         pushl   %ebp                    /* frame for backtrace */
444         movl    %esp,%ebp
445         PUSH_DUMMY
446         pushl   %ecx                    /* last part of intrframe = intr */
447         incl    fastunpend_count
448         pushl   %esp                    /* pass frame by reference */
449         call    ithread_fast_handler    /* returns 0 to unmask */
450         addl    $4,%esp                 /* remove pointer, now intr on top */
451         cmpl    $0,%eax
452         jnz     1f
453         movl    MachIntrABI + MACHINTR_INTREN, %eax
454         call    *%eax                   /* MachIntrABI.intren(intr) */
455 1:
456         addl    $4,%esp
457         POP_DUMMY
458         popl    %ebp
459         ret
460