thread stage 8: add crit_enter(), per-thread cpl handling, fix deferred
[dragonfly.git] / sys / platform / pc32 / apic / apic_ipl.s
1 /*-
2  * Copyright (c) 1997, by Steve Passe
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. The name of the developer may NOT be used to endorse or promote products
11  *    derived from this software without specific prior written permission.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  *
25  * $FreeBSD: src/sys/i386/isa/apic_ipl.s,v 1.27.2.2 2000/09/30 02:49:35 ps Exp $
26  * $DragonFly: src/sys/platform/pc32/apic/apic_ipl.s,v 1.3 2003/06/21 07:54:56 dillon Exp $
27  */
28
29
30         .data
31         ALIGN_DATA
32
33 /*
34  * Routines used by splz_unpend to build an interrupt frame from a
35  * trap frame.  The _vec[] routines build the proper frame on the stack,
36  * then call one of _Xintr0 thru _XintrNN.
37  *
38  * used by:
39  *   i386/isa/apic_ipl.s (this file):   splz_unpend JUMPs to HWIs.
40  *   i386/isa/clock.c:                  setup _vec[clock] to point at _vec8254.
41  */
42         .globl _vec
43 _vec:
44         .long    vec0,  vec1,  vec2,  vec3,  vec4,  vec5,  vec6,  vec7
45         .long    vec8,  vec9, vec10, vec11, vec12, vec13, vec14, vec15
46         .long   vec16, vec17, vec18, vec19, vec20, vec21, vec22, vec23
47
48 /*
49  * Note:
50  *      This is the UP equivilant of _imen.
51  *      It is OPAQUE, and must NOT be accessed directly.
52  *      It MUST be accessed along with the IO APIC as a 'critical region'.
53  *      Accessed by:
54  *              INTREN()
55  *              INTRDIS()
56  *              MAYBE_MASK_IRQ
57  *              MAYBE_UNMASK_IRQ
58  *              imen_dump()
59  */
60         .p2align 2                              /* MUST be 32bit aligned */
61         .globl _apic_imen
62 _apic_imen:
63         .long   HWI_MASK
64
65
66 /*
67  * 
68  */
69         .text
70         SUPERALIGN_TEXT
71
72 /*
73  * splz() -     dispatch pending interrupts after cpl reduced
74  *
75  * Interrupt priority mechanism
76  *      -- soft splXX masks with group mechanism (cpl)
77  *      -- h/w masks for currently active or unused interrupts (imen)
78  *      -- ipending = active interrupts currently masked by cpl
79  */
80
81 ENTRY(splz)
82         /*
83          * The caller has restored cpl and checked that (ipending & ~cpl)
84          * is nonzero.  However, since ipending can change at any time
85          * (by an interrupt or, with SMP, by another cpu), we have to
86          * repeat the check.  At the moment we must own the MP lock in
87          * the SMP case because the interruput handlers require it.  We
88          * loop until no unmasked pending interrupts remain.  
89          *
90          * No new unmaksed pending interrupts will be added during the
91          * loop because, being unmasked, the interrupt code will be able
92          * to execute the interrupts.
93          *
94          * Interrupts come in two flavors:  Hardware interrupts and software
95          * interrupts.  We have to detect the type of interrupt (based on the
96          * position of the interrupt bit) and call the appropriate dispatch
97          * routine.
98          * 
99          * NOTE: "bsfl %ecx,%ecx" is undefined when %ecx is 0 so we can't
100          * rely on the secondary btrl tests.
101          */
102         movl    _cpl,%eax
103 splz_next:
104         /*
105          * We don't need any locking here.  (ipending & ~cpl) cannot grow 
106          * while we're looking at it - any interrupt will shrink it to 0.
107          */
108         movl    $0,_reqpri
109         movl    %eax,%ecx
110         notl    %ecx                    /* set bit = unmasked level */
111         andl    _ipending,%ecx          /* set bit = unmasked pending INT */
112         jne     splz_unpend
113         ret
114
115         ALIGN_TEXT
116 splz_unpend:
117         bsfl    %ecx,%ecx
118         lock
119         btrl    %ecx,_ipending
120         jnc     splz_next
121         cmpl    $NHWI,%ecx
122         jae     splz_swi
123         /*
124          * We would prefer to call the intr handler directly here but that
125          * doesn't work for badly behaved handlers that want the interrupt
126          * frame.  Also, there's a problem determining the unit number.
127          * We should change the interface so that the unit number is not
128          * determined at config time.
129          *
130          * The vec[] routines build the proper frame on the stack,
131          * then call one of _Xintr0 thru _XintrNN.
132          */
133         jmp     *_vec(,%ecx,4)
134
135         ALIGN_TEXT
136 splz_swi:
137         pushl   %eax
138         orl     imasks(,%ecx,4),%eax
139         movl    %eax,_cpl
140         call    *_ihandlers(,%ecx,4)
141         popl    %eax
142         movl    %eax,_cpl
143         jmp     splz_next
144
145 /*
146  * Fake clock interrupt(s) so that they appear to come from our caller instead
147  * of from here, so that system profiling works.
148  * XXX do this more generally (for all vectors; look up the C entry point).
149  * XXX frame bogusness stops us from just jumping to the C entry point.
150  * We have to clear iactive since this is an unpend call, and it will be
151  * set from the time of the original INT.
152  */
153
154 /*
155  * The 'generic' vector stubs.
156  */
157
158 #define BUILD_VEC(irq_num)                                              \
159         ALIGN_TEXT ;                                                    \
160 __CONCAT(vec,irq_num): ;                                                \
161         popl    %eax ;                                                  \
162         pushfl ;                                                        \
163         pushl   $KCSEL ;                                                \
164         pushl   %eax ;                                                  \
165         cli ;                                                           \
166         lock ;                                  /* MP-safe */           \
167         andl    $~IRQ_BIT(irq_num), iactive ;   /* lazy masking */      \
168         MEXITCOUNT ;                                                    \
169         APIC_ITRACE(apic_itrace_splz, irq_num, APIC_ITRACE_SPLZ) ;      \
170         jmp     __CONCAT(_Xintr,irq_num)
171
172
173         BUILD_VEC(0)
174         BUILD_VEC(1)
175         BUILD_VEC(2)
176         BUILD_VEC(3)
177         BUILD_VEC(4)
178         BUILD_VEC(5)
179         BUILD_VEC(6)
180         BUILD_VEC(7)
181         BUILD_VEC(8)
182         BUILD_VEC(9)
183         BUILD_VEC(10)
184         BUILD_VEC(11)
185         BUILD_VEC(12)
186         BUILD_VEC(13)
187         BUILD_VEC(14)
188         BUILD_VEC(15)
189         BUILD_VEC(16)                   /* 8 additional INTs in IO APIC */
190         BUILD_VEC(17)
191         BUILD_VEC(18)
192         BUILD_VEC(19)
193         BUILD_VEC(20)
194         BUILD_VEC(21)
195         BUILD_VEC(22)
196         BUILD_VEC(23)
197
198
199 /******************************************************************************
200  * XXX FIXME: figure out where these belong.
201  */
202
203 /* this nonsense is to verify that masks ALWAYS have 1 and only 1 bit set */
204 #define QUALIFY_MASKS_NOT
205
206 #ifdef QUALIFY_MASKS
207 #define QUALIFY_MASK            \
208         btrl    %ecx, %eax ;    \
209         andl    %eax, %eax ;    \
210         jz      1f ;            \
211         pushl   $bad_mask ;     \
212         call    _panic ;        \
213 1:
214
215 bad_mask:       .asciz  "bad mask"
216 #else
217 #define QUALIFY_MASK
218 #endif
219
220 /*
221  * (soon to be) MP-safe function to clear ONE INT mask bit.
222  * The passed arg is a 32bit u_int MASK.
223  * It sets the associated bit in _apic_imen.
224  * It sets the mask bit of the associated IO APIC register.
225  */
226 ENTRY(INTREN)
227         pushfl                          /* save state of EI flag */
228         cli                             /* prevent recursion */
229         IMASK_LOCK                      /* enter critical reg */
230
231         movl    8(%esp), %eax           /* mask into %eax */
232         bsfl    %eax, %ecx              /* get pin index */
233         btrl    %ecx, _apic_imen        /* update _apic_imen */
234
235         QUALIFY_MASK
236
237         shll    $4, %ecx
238         movl    CNAME(int_to_apicintpin) + 8(%ecx), %edx
239         movl    CNAME(int_to_apicintpin) + 12(%ecx), %ecx
240         testl   %edx, %edx
241         jz      1f
242
243         movl    %ecx, (%edx)            /* write the target register index */
244         movl    16(%edx), %eax          /* read the target register data */
245         andl    $~IOART_INTMASK, %eax   /* clear mask bit */
246         movl    %eax, 16(%edx)          /* write the APIC register data */
247 1:      
248         IMASK_UNLOCK                    /* exit critical reg */
249         popfl                           /* restore old state of EI flag */
250         ret
251
252 /*
253  * (soon to be) MP-safe function to set ONE INT mask bit.
254  * The passed arg is a 32bit u_int MASK.
255  * It clears the associated bit in _apic_imen.
256  * It clears the mask bit of the associated IO APIC register.
257  */
258 ENTRY(INTRDIS)
259         pushfl                          /* save state of EI flag */
260         cli                             /* prevent recursion */
261         IMASK_LOCK                      /* enter critical reg */
262
263         movl    8(%esp), %eax           /* mask into %eax */
264         bsfl    %eax, %ecx              /* get pin index */
265         btsl    %ecx, _apic_imen        /* update _apic_imen */
266
267         QUALIFY_MASK
268
269         shll    $4, %ecx
270         movl    CNAME(int_to_apicintpin) + 8(%ecx), %edx
271         movl    CNAME(int_to_apicintpin) + 12(%ecx), %ecx
272         testl   %edx, %edx
273         jz      1f
274
275         movl    %ecx, (%edx)            /* write the target register index */
276         movl    16(%edx), %eax          /* read the target register data */
277         orl     $IOART_INTMASK, %eax    /* set mask bit */
278         movl    %eax, 16(%edx)          /* write the APIC register data */
279 1:      
280         IMASK_UNLOCK                    /* exit critical reg */
281         popfl                           /* restore old state of EI flag */
282         ret
283
284
285 /******************************************************************************
286  *
287  */
288
289
290 /*
291  * void write_ioapic_mask(int apic, u_int mask); 
292  */
293
294 #define _INT_MASK       0x00010000
295 #define _PIN_MASK       0x00ffffff
296
297 #define _OLD_ESI          0(%esp)
298 #define _OLD_EBX          4(%esp)
299 #define _RETADDR          8(%esp)
300 #define _APIC            12(%esp)
301 #define _MASK            16(%esp)
302
303         ALIGN_TEXT
304 write_ioapic_mask:
305         pushl %ebx                      /* scratch */
306         pushl %esi                      /* scratch */
307
308         movl    _apic_imen, %ebx
309         xorl    _MASK, %ebx             /* %ebx = _apic_imen ^ mask */
310         andl    $_PIN_MASK, %ebx        /* %ebx = _apic_imen & 0x00ffffff */
311         jz      all_done                /* no change, return */
312
313         movl    _APIC, %esi             /* APIC # */
314         movl    _ioapic, %ecx
315         movl    (%ecx,%esi,4), %esi     /* %esi holds APIC base address */
316
317 next_loop:                              /* %ebx = diffs, %esi = APIC base */
318         bsfl    %ebx, %ecx              /* %ecx = index if 1st/next set bit */
319         jz      all_done
320
321         btrl    %ecx, %ebx              /* clear this bit in diffs */
322         leal    16(,%ecx,2), %edx       /* calculate register index */
323
324         movl    %edx, (%esi)            /* write the target register index */
325         movl    16(%esi), %eax          /* read the target register data */
326
327         btl     %ecx, _MASK             /* test for mask or unmask */
328         jnc     clear                   /* bit is clear */
329         orl     $_INT_MASK, %eax        /* set mask bit */
330         jmp     write
331 clear:  andl    $~_INT_MASK, %eax       /* clear mask bit */
332
333 write:  movl    %eax, 16(%esi)          /* write the APIC register data */
334
335         jmp     next_loop               /* try another pass */
336
337 all_done:
338         popl    %esi
339         popl    %ebx
340         ret
341
342 #undef _OLD_ESI
343 #undef _OLD_EBX
344 #undef _RETADDR
345 #undef _APIC
346 #undef _MASK
347
348 #undef _PIN_MASK
349 #undef _INT_MASK
350
351 #ifdef oldcode
352
353 _INTREN:
354         movl _apic_imen, %eax
355         notl %eax                       /* mask = ~mask */
356         andl _apic_imen, %eax           /* %eax = _apic_imen & ~mask */
357
358         pushl %eax                      /* new (future) _apic_imen value */
359         pushl $0                        /* APIC# arg */
360         call write_ioapic_mask          /* modify the APIC registers */
361
362         addl $4, %esp                   /* remove APIC# arg from stack */
363         popl _apic_imen                 /* _apic_imen |= mask */
364         ret
365
366 _INTRDIS:
367         movl _apic_imen, %eax
368         orl 4(%esp), %eax               /* %eax = _apic_imen | mask */
369
370         pushl %eax                      /* new (future) _apic_imen value */
371         pushl $0                        /* APIC# arg */
372         call write_ioapic_mask          /* modify the APIC registers */
373
374         addl $4, %esp                   /* remove APIC# arg from stack */
375         popl _apic_imen                 /* _apic_imen |= mask */
376         ret
377
378 #endif /* oldcode */
379
380
381 #ifdef ready
382
383 /*
384  * u_int read_io_apic_mask(int apic); 
385  */
386         ALIGN_TEXT
387 read_io_apic_mask:
388         ret
389
390 /*
391  * Set INT mask bit for each bit set in 'mask'.
392  * Ignore INT mask bit for all others.
393  *
394  * void set_io_apic_mask(apic, u_int32_t bits); 
395  */
396         ALIGN_TEXT
397 set_io_apic_mask:
398         ret
399
400 /*
401  * void set_ioapic_maskbit(int apic, int bit); 
402  */
403         ALIGN_TEXT
404 set_ioapic_maskbit:
405         ret
406
407 /*
408  * Clear INT mask bit for each bit set in 'mask'.
409  * Ignore INT mask bit for all others.
410  *
411  * void clr_io_apic_mask(int apic, u_int32_t bits); 
412  */
413         ALIGN_TEXT
414 clr_io_apic_mask:
415         ret
416
417 /*
418  * void clr_ioapic_maskbit(int apic, int bit); 
419  */
420         ALIGN_TEXT
421 clr_ioapic_maskbit:
422         ret
423
424 #endif /** ready */
425
426 /******************************************************************************
427  * 
428  */
429
430 /*
431  * u_int io_apic_write(int apic, int select);
432  */
433 ENTRY(io_apic_read)
434         movl    4(%esp), %ecx           /* APIC # */
435         movl    _ioapic, %eax
436         movl    (%eax,%ecx,4), %edx     /* APIC base register address */
437         movl    8(%esp), %eax           /* target register index */
438         movl    %eax, (%edx)            /* write the target register index */
439         movl    16(%edx), %eax          /* read the APIC register data */
440         ret                             /* %eax = register value */
441
442 /*
443  * void io_apic_write(int apic, int select, int value);
444  */
445 ENTRY(io_apic_write)
446         movl    4(%esp), %ecx           /* APIC # */
447         movl    _ioapic, %eax
448         movl    (%eax,%ecx,4), %edx     /* APIC base register address */
449         movl    8(%esp), %eax           /* target register index */
450         movl    %eax, (%edx)            /* write the target register index */
451         movl    12(%esp), %eax          /* target register value */
452         movl    %eax, 16(%edx)          /* write the APIC register data */
453         ret                             /* %eax = void */
454
455 /*
456  * Send an EOI to the local APIC.
457  */
458 ENTRY(apic_eoi)
459         movl    $0, _lapic+0xb0
460         ret