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