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