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