kernel - Fix serious issue w/ smp_invltlb(), plus other issues (2)
[dragonfly.git] / sys / platform / pc32 / apic / apic_vector.s
1 /*
2  *      from: vector.s, 386BSD 0.1 unknown origin
3  * $FreeBSD: src/sys/i386/isa/apic_vector.s,v 1.47.2.5 2001/09/01 22:33:38 tegge Exp $
4  * $DragonFly: src/sys/platform/pc32/apic/apic_vector.s,v 1.39 2008/08/02 01:14:43 dillon Exp $
5  */
6
7 #include "use_npx.h"
8 #include "opt_auto_eoi.h"
9
10 #include <machine/asmacros.h>
11 #include <machine/lock.h>
12 #include <machine/psl.h>
13 #include <machine/trap.h>
14
15 #include <machine_base/icu/icu.h>
16 #include <bus/isa/isa.h>
17
18 #include "assym.s"
19
20 #include "apicreg.h"
21 #include "apic_ipl.h"
22 #include <machine/smp.h>
23 #include <machine_base/isa/intr_machdep.h>
24
25 /* convert an absolute IRQ# into a bitmask */
26 #define IRQ_LBIT(irq_num)       (1 << (irq_num))
27
28 /* make an index into the IO APIC from the IRQ# */
29 #define REDTBL_IDX(irq_num)     (0x10 + ((irq_num) * 2))
30
31 #ifdef SMP
32 #define MPLOCKED     lock ;
33 #else
34 #define MPLOCKED
35 #endif
36
37 /*
38  * Push an interrupt frame in a format acceptable to doreti, reload
39  * the segment registers for the kernel.
40  */
41 #define PUSH_FRAME                                                      \
42         pushl   $0 ;            /* dummy error code */                  \
43         pushl   $0 ;            /* dummy trap type */                   \
44         pushl   $0 ;            /* dummy xflags type */                 \
45         pushal ;                                                        \
46         pushl   %ds ;           /* save data and extra segments ... */  \
47         pushl   %es ;                                                   \
48         pushl   %fs ;                                                   \
49         pushl   %gs ;                                                   \
50         cld ;                                                           \
51         mov     $KDSEL,%ax ;                                            \
52         mov     %ax,%ds ;                                               \
53         mov     %ax,%es ;                                               \
54         mov     %ax,%gs ;                                               \
55         mov     $KPSEL,%ax ;                                            \
56         mov     %ax,%fs ;                                               \
57
58 /*
59  * Warning: POP_FRAME can only be used if there is no chance of a
60  * segment register being changed (e.g. by procfs), which is why syscalls
61  * have to use doreti.
62  */
63 #define POP_FRAME                                                       \
64         popl    %gs ;                                                   \
65         popl    %fs ;                                                   \
66         popl    %es ;                                                   \
67         popl    %ds ;                                                   \
68         popal ;                                                         \
69         addl    $3*4,%esp ;     /* dummy xflags, trap & error codes */  \
70
71 #define IOAPICADDR(irq_num) \
72         CNAME(int_to_apicintpin) + IOAPIC_IM_SIZE * (irq_num) + IOAPIC_IM_ADDR
73 #define REDIRIDX(irq_num) \
74         CNAME(int_to_apicintpin) + IOAPIC_IM_SIZE * (irq_num) + IOAPIC_IM_ENTIDX
75 #define IOAPICFLAGS(irq_num) \
76         CNAME(int_to_apicintpin) + IOAPIC_IM_SIZE * (irq_num) + IOAPIC_IM_FLAGS
77
78 #define MASK_IRQ(irq_num)                                               \
79         APIC_IMASK_LOCK ;                       /* into critical reg */ \
80         testl   $IOAPIC_IM_FLAG_MASKED, IOAPICFLAGS(irq_num) ;          \
81         jne     7f ;                    /* masked, don't mask */        \
82         orl     $IOAPIC_IM_FLAG_MASKED, IOAPICFLAGS(irq_num) ;          \
83                                                 /* set the mask bit */  \
84         movl    IOAPICADDR(irq_num), %ecx ;     /* ioapic addr */       \
85         movl    REDIRIDX(irq_num), %eax ;       /* get the index */     \
86         movl    %eax, (%ecx) ;                  /* write the index */   \
87         orl     $IOART_INTMASK,IOAPIC_WINDOW(%ecx) ;/* set the mask */  \
88 7: ;                                            /* already masked */    \
89         APIC_IMASK_UNLOCK ;                                             \
90
91 /*
92  * Test to see whether we are handling an edge or level triggered INT.
93  *  Level-triggered INTs must still be masked as we don't clear the source,
94  *  and the EOI cycle would cause redundant INTs to occur.
95  */
96 #define MASK_LEVEL_IRQ(irq_num)                                         \
97         testl   $IOAPIC_IM_FLAG_LEVEL, IOAPICFLAGS(irq_num) ;           \
98         jz      9f ;                            /* edge, don't mask */  \
99         MASK_IRQ(irq_num) ;                                             \
100 9: ;                                                                    \
101
102 /*
103  * Test to see if the source is currntly masked, clear if so.
104  */
105 #define UNMASK_IRQ(irq_num)                                             \
106         cmpl    $0,%eax ;                                               \
107         jnz     8f ;                                                    \
108         APIC_IMASK_LOCK ;                       /* into critical reg */ \
109         testl   $IOAPIC_IM_FLAG_MASKED, IOAPICFLAGS(irq_num) ;          \
110         je      7f ;                    /* bit clear, not masked */     \
111         andl    $~IOAPIC_IM_FLAG_MASKED, IOAPICFLAGS(irq_num) ;         \
112                                                 /* clear mask bit */    \
113         movl    IOAPICADDR(irq_num),%ecx ;      /* ioapic addr */       \
114         movl    REDIRIDX(irq_num), %eax ;       /* get the index */     \
115         movl    %eax,(%ecx) ;                   /* write the index */   \
116         andl    $~IOART_INTMASK,IOAPIC_WINDOW(%ecx) ;/* clear the mask */ \
117 7: ;                                                                    \
118         APIC_IMASK_UNLOCK ;                                             \
119 8: ;                                                                    \
120
121 #ifdef APIC_IO
122
123 /*
124  * Fast interrupt call handlers run in the following sequence:
125  *
126  *      - Push the trap frame required by doreti
127  *      - Mask the interrupt and reenable its source
128  *      - If we cannot take the interrupt set its fpending bit and
129  *        doreti.  Note that we cannot mess with mp_lock at all
130  *        if we entered from a critical section!
131  *      - If we can take the interrupt clear its fpending bit,
132  *        call the handler, then unmask and doreti.
133  *
134  * YYY can cache gd base opitner instead of using hidden %fs prefixes.
135  */
136
137 #define FAST_INTR(irq_num, vec_name)                                    \
138         .text ;                                                         \
139         SUPERALIGN_TEXT ;                                               \
140 IDTVEC(vec_name) ;                                                      \
141         PUSH_FRAME ;                                                    \
142         FAKE_MCOUNT(15*4(%esp)) ;                                       \
143         MASK_LEVEL_IRQ(irq_num) ;                                       \
144         movl    $0, lapic_eoi ;                                         \
145         movl    PCPU(curthread),%ebx ;                                  \
146         movl    $0,%eax ;       /* CURRENT CPL IN FRAME (REMOVED) */    \
147         pushl   %eax ;                                                  \
148         testl   $-1,TD_NEST_COUNT(%ebx) ;                               \
149         jne     1f ;                                                    \
150         testl   $-1,TD_CRITCOUNT(%ebx) ;                                \
151         je      2f ;                                                    \
152 1: ;                                                                    \
153         /* in critical section, make interrupt pending */               \
154         /* set the pending bit and return, leave interrupt masked */    \
155         orl     $IRQ_LBIT(irq_num),PCPU(fpending) ;                     \
156         orl     $RQF_INTPEND,PCPU(reqflags) ;                           \
157         jmp     5f ;                                                    \
158 2: ;                                                                    \
159         /* clear pending bit, run handler */                            \
160         andl    $~IRQ_LBIT(irq_num),PCPU(fpending) ;                    \
161         pushl   $irq_num ;                                              \
162         pushl   %esp ;                   /* pass frame by reference */  \
163         incl    TD_CRITCOUNT(%ebx) ;                                    \
164         sti ;                                                           \
165         call    ithread_fast_handler ;   /* returns 0 to unmask */      \
166         decl    TD_CRITCOUNT(%ebx) ;                                    \
167         addl    $8, %esp ;                                              \
168         UNMASK_IRQ(irq_num) ;                                           \
169 5: ;                                                                    \
170         MEXITCOUNT ;                                                    \
171         jmp     doreti ;                                                \
172
173 #endif
174
175 /*
176  * Handle "spurious INTerrupts".
177  * Notes:
178  *  This is different than the "spurious INTerrupt" generated by an
179  *   8259 PIC for missing INTs.  See the APIC documentation for details.
180  *  This routine should NOT do an 'EOI' cycle.
181  */
182         .text
183         SUPERALIGN_TEXT
184         .globl Xspuriousint
185 Xspuriousint:
186
187         /* No EOI cycle used here */
188
189         iret
190
191
192 /*
193  * Handle TLB shootdowns.
194  *
195  * NOTE: Interrupts remain disabled.
196  */
197         .text
198         SUPERALIGN_TEXT
199         .globl  Xinvltlb
200 Xinvltlb:
201         PUSH_FRAME
202         movl    $0, lapic_eoi           /* End Of Interrupt to APIC */
203         FAKE_MCOUNT(15*4(%esp))
204
205         subl    $8,%esp                 /* make same as interrupt frame */
206         pushl   %esp                    /* pass frame by reference */
207         call    smp_invltlb_intr
208         addl    $12,%esp
209
210         MEXITCOUNT
211         jmp     doreti_syscall_ret
212
213 /*
214  * Executed by a CPU when it receives an Xcpustop IPI from another CPU,
215  *
216  *  - Signals its receipt.
217  *  - Waits for permission to restart.
218  *  - Processing pending IPIQ events while waiting.
219  *  - Signals its restart.
220  */
221
222         .text
223         SUPERALIGN_TEXT
224         .globl Xcpustop
225 Xcpustop:
226         pushl   %ebp
227         movl    %esp, %ebp
228         pushl   %eax
229         pushl   %ecx
230         pushl   %edx
231         pushl   %ds                     /* save current data segment */
232         pushl   %fs
233
234         movl    $KDSEL, %eax
235         mov     %ax, %ds                /* use KERNEL data segment */
236         movl    $KPSEL, %eax
237         mov     %ax, %fs
238
239         movl    $0, lapic_eoi           /* End Of Interrupt to APIC */
240
241         movl    PCPU(cpuid), %eax
242         imull   $PCB_SIZE, %eax
243         leal    CNAME(stoppcbs)(%eax), %eax
244         pushl   %eax
245         call    CNAME(savectx)          /* Save process context */
246         addl    $4, %esp
247         
248                 
249         movl    PCPU(cpuid), %eax
250
251         /*
252          * Indicate that we have stopped and loop waiting for permission
253          * to start again.  We must still process IPI events while in a
254          * stopped state.
255          *
256          * Interrupts must remain enabled for non-IPI'd per-cpu interrupts
257          * (e.g. Xtimer, Xinvltlb).
258          */
259         MPLOCKED
260         btsl    %eax, stopped_cpus      /* stopped_cpus |= (1<<id) */
261         sti
262 1:
263         andl    $~RQF_IPIQ,PCPU(reqflags)
264         pushl   %eax
265         call    lwkt_smp_stopped
266         popl    %eax
267         btl     %eax, started_cpus      /* while (!(started_cpus & (1<<id))) */
268         jnc     1b
269
270         MPLOCKED
271         btrl    %eax, started_cpus      /* started_cpus &= ~(1<<id) */
272         MPLOCKED
273         btrl    %eax, stopped_cpus      /* stopped_cpus &= ~(1<<id) */
274
275         test    %eax, %eax
276         jnz     2f
277
278         movl    CNAME(cpustop_restartfunc), %eax
279         test    %eax, %eax
280         jz      2f
281         movl    $0, CNAME(cpustop_restartfunc)  /* One-shot */
282
283         call    *%eax
284 2:
285         popl    %fs
286         popl    %ds                     /* restore previous data segment */
287         popl    %edx
288         popl    %ecx
289         popl    %eax
290         movl    %ebp, %esp
291         popl    %ebp
292         iret
293
294         /*
295          * For now just have one ipiq IPI, but what we really want is
296          * to have one for each source cpu to the APICs don't get stalled
297          * backlogging the requests.
298          */
299         .text
300         SUPERALIGN_TEXT
301         .globl Xipiq
302 Xipiq:
303         PUSH_FRAME
304         movl    $0, lapic_eoi           /* End Of Interrupt to APIC */
305         FAKE_MCOUNT(15*4(%esp))
306
307         incl    PCPU(cnt) + V_IPI
308         movl    PCPU(curthread),%ebx
309         testl   $-1,TD_CRITCOUNT(%ebx)
310         jne     1f
311         subl    $8,%esp                 /* make same as interrupt frame */
312         pushl   %esp                    /* pass frame by reference */
313         incl    PCPU(intr_nesting_level)
314         incl    TD_CRITCOUNT(%ebx)
315         sti
316         call    lwkt_process_ipiq_frame
317         decl    TD_CRITCOUNT(%ebx)
318         decl    PCPU(intr_nesting_level)
319         addl    $12,%esp
320         pushl   $0                      /* CPL for frame (REMOVED) */
321         MEXITCOUNT
322         jmp     doreti
323 1:
324         orl     $RQF_IPIQ,PCPU(reqflags)
325         MEXITCOUNT
326         jmp     doreti_syscall_ret
327
328         .text
329         SUPERALIGN_TEXT
330         .globl Xtimer
331 Xtimer:
332         PUSH_FRAME
333         movl    $0, lapic_eoi           /* End Of Interrupt to APIC */
334         FAKE_MCOUNT(15*4(%esp))
335
336         incl    PCPU(cnt) + V_TIMER
337         movl    PCPU(curthread),%ebx
338         testl   $-1,TD_CRITCOUNT(%ebx)
339         jne     1f
340         testl   $-1,TD_NEST_COUNT(%ebx)
341         jne     1f
342         subl    $8,%esp                 /* make same as interrupt frame */
343         pushl   %esp                    /* pass frame by reference */
344         incl    PCPU(intr_nesting_level)
345         incl    TD_CRITCOUNT(%ebx)
346         sti
347         call    lapic_timer_process_frame
348         decl    TD_CRITCOUNT(%ebx)
349         decl    PCPU(intr_nesting_level)
350         addl    $12,%esp
351         pushl   $0                      /* CPL for frame (REMOVED) */
352         MEXITCOUNT
353         jmp     doreti
354 1:
355         orl     $RQF_TIMER,PCPU(reqflags)
356         MEXITCOUNT
357         jmp     doreti_syscall_ret
358
359 #ifdef APIC_IO
360
361 MCOUNT_LABEL(bintr)
362         FAST_INTR(0,apic_fastintr0)
363         FAST_INTR(1,apic_fastintr1)
364         FAST_INTR(2,apic_fastintr2)
365         FAST_INTR(3,apic_fastintr3)
366         FAST_INTR(4,apic_fastintr4)
367         FAST_INTR(5,apic_fastintr5)
368         FAST_INTR(6,apic_fastintr6)
369         FAST_INTR(7,apic_fastintr7)
370         FAST_INTR(8,apic_fastintr8)
371         FAST_INTR(9,apic_fastintr9)
372         FAST_INTR(10,apic_fastintr10)
373         FAST_INTR(11,apic_fastintr11)
374         FAST_INTR(12,apic_fastintr12)
375         FAST_INTR(13,apic_fastintr13)
376         FAST_INTR(14,apic_fastintr14)
377         FAST_INTR(15,apic_fastintr15)
378         FAST_INTR(16,apic_fastintr16)
379         FAST_INTR(17,apic_fastintr17)
380         FAST_INTR(18,apic_fastintr18)
381         FAST_INTR(19,apic_fastintr19)
382         FAST_INTR(20,apic_fastintr20)
383         FAST_INTR(21,apic_fastintr21)
384         FAST_INTR(22,apic_fastintr22)
385         FAST_INTR(23,apic_fastintr23)
386         FAST_INTR(24,apic_fastintr24)
387         FAST_INTR(25,apic_fastintr25)
388         FAST_INTR(26,apic_fastintr26)
389         FAST_INTR(27,apic_fastintr27)
390         FAST_INTR(28,apic_fastintr28)
391         FAST_INTR(29,apic_fastintr29)
392         FAST_INTR(30,apic_fastintr30)
393         FAST_INTR(31,apic_fastintr31)
394 MCOUNT_LABEL(eintr)
395
396 #endif
397
398         .data
399
400 /* variables used by stop_cpus()/restart_cpus()/Xcpustop */
401         .globl stopped_cpus, started_cpus
402 stopped_cpus:
403         .long   0
404 started_cpus:
405         .long   0
406
407         .globl CNAME(cpustop_restartfunc)
408 CNAME(cpustop_restartfunc):
409         .long 0
410                 
411         .text
412