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