2 * Copyright (c) 1997, by Steve Passe
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
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/i386/apic/Attic/apic_ipl.s,v 1.4 2003/06/22 08:54:22 dillon Exp $
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.
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.
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
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'.
60 .p2align 2 /* MUST be 32bit aligned */
73 * splz() - dispatch pending interrupts after cpl reduced
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
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.
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.
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
99 * NOTE: "bsfl %ecx,%ecx" is undefined when %ecx is 0 so we can't
100 * rely on the secondary btrl tests.
104 movl TD_MACH+MTD_CPL(%ebx),%eax
107 * We don't need any locking here. (ipending & ~cpl) cannot grow
108 * while we're looking at it - any interrupt will shrink it to 0.
112 notl %ecx /* set bit = unmasked level */
113 andl _ipending,%ecx /* set bit = unmasked pending INT */
127 * We would prefer to call the intr handler directly here but that
128 * doesn't work for badly behaved handlers that want the interrupt
129 * frame. Also, there's a problem determining the unit number.
130 * We should change the interface so that the unit number is not
131 * determined at config time.
133 * The vec[] routines build the proper frame on the stack so
134 * the interrupt will eventually return to the caller or splz,
135 * then calls one of _Xintr0 thru _XintrNN.
142 pushl %eax /* save cpl across call */
143 orl imasks(,%ecx,4),%eax
144 movl %eax,TD_MACH+MTD_CPL(%ebx) /* set cpl for SWI */
145 call *_ihandlers(,%ecx,4)
147 movl %eax,TD_MACH+MTD_CPL(%ebx) /* restore cpl and loop */
151 * Fake clock interrupt(s) so that they appear to come from our caller instead
152 * of from here, so that system profiling works.
153 * XXX do this more generally (for all vectors; look up the C entry point).
154 * XXX frame bogusness stops us from just jumping to the C entry point.
155 * We have to clear iactive since this is an unpend call, and it will be
156 * set from the time of the original INT.
160 * The 'generic' vector stubs.
163 #define BUILD_VEC(irq_num) \
165 __CONCAT(vec,irq_num): ; \
171 lock ; /* MP-safe */ \
172 andl $~IRQ_BIT(irq_num), iactive ; /* lazy masking */ \
174 APIC_ITRACE(apic_itrace_splz, irq_num, APIC_ITRACE_SPLZ) ; \
175 jmp __CONCAT(_Xintr,irq_num)
194 BUILD_VEC(16) /* 8 additional INTs in IO APIC */
204 /******************************************************************************
205 * XXX FIXME: figure out where these belong.
208 /* this nonsense is to verify that masks ALWAYS have 1 and only 1 bit set */
209 #define QUALIFY_MASKS_NOT
212 #define QUALIFY_MASK \
220 bad_mask: .asciz "bad mask"
226 * (soon to be) MP-safe function to clear ONE INT mask bit.
227 * The passed arg is a 32bit u_int MASK.
228 * It sets the associated bit in _apic_imen.
229 * It sets the mask bit of the associated IO APIC register.
232 pushfl /* save state of EI flag */
233 cli /* prevent recursion */
234 IMASK_LOCK /* enter critical reg */
236 movl 8(%esp), %eax /* mask into %eax */
237 bsfl %eax, %ecx /* get pin index */
238 btrl %ecx, _apic_imen /* update _apic_imen */
243 movl CNAME(int_to_apicintpin) + 8(%ecx), %edx
244 movl CNAME(int_to_apicintpin) + 12(%ecx), %ecx
248 movl %ecx, (%edx) /* write the target register index */
249 movl 16(%edx), %eax /* read the target register data */
250 andl $~IOART_INTMASK, %eax /* clear mask bit */
251 movl %eax, 16(%edx) /* write the APIC register data */
253 IMASK_UNLOCK /* exit critical reg */
254 popfl /* restore old state of EI flag */
258 * (soon to be) MP-safe function to set ONE INT mask bit.
259 * The passed arg is a 32bit u_int MASK.
260 * It clears the associated bit in _apic_imen.
261 * It clears the mask bit of the associated IO APIC register.
264 pushfl /* save state of EI flag */
265 cli /* prevent recursion */
266 IMASK_LOCK /* enter critical reg */
268 movl 8(%esp), %eax /* mask into %eax */
269 bsfl %eax, %ecx /* get pin index */
270 btsl %ecx, _apic_imen /* update _apic_imen */
275 movl CNAME(int_to_apicintpin) + 8(%ecx), %edx
276 movl CNAME(int_to_apicintpin) + 12(%ecx), %ecx
280 movl %ecx, (%edx) /* write the target register index */
281 movl 16(%edx), %eax /* read the target register data */
282 orl $IOART_INTMASK, %eax /* set mask bit */
283 movl %eax, 16(%edx) /* write the APIC register data */
285 IMASK_UNLOCK /* exit critical reg */
286 popfl /* restore old state of EI flag */
290 /******************************************************************************
296 * void write_ioapic_mask(int apic, u_int mask);
299 #define _INT_MASK 0x00010000
300 #define _PIN_MASK 0x00ffffff
302 #define _OLD_ESI 0(%esp)
303 #define _OLD_EBX 4(%esp)
304 #define _RETADDR 8(%esp)
305 #define _APIC 12(%esp)
306 #define _MASK 16(%esp)
310 pushl %ebx /* scratch */
311 pushl %esi /* scratch */
313 movl _apic_imen, %ebx
314 xorl _MASK, %ebx /* %ebx = _apic_imen ^ mask */
315 andl $_PIN_MASK, %ebx /* %ebx = _apic_imen & 0x00ffffff */
316 jz all_done /* no change, return */
318 movl _APIC, %esi /* APIC # */
320 movl (%ecx,%esi,4), %esi /* %esi holds APIC base address */
322 next_loop: /* %ebx = diffs, %esi = APIC base */
323 bsfl %ebx, %ecx /* %ecx = index if 1st/next set bit */
326 btrl %ecx, %ebx /* clear this bit in diffs */
327 leal 16(,%ecx,2), %edx /* calculate register index */
329 movl %edx, (%esi) /* write the target register index */
330 movl 16(%esi), %eax /* read the target register data */
332 btl %ecx, _MASK /* test for mask or unmask */
333 jnc clear /* bit is clear */
334 orl $_INT_MASK, %eax /* set mask bit */
336 clear: andl $~_INT_MASK, %eax /* clear mask bit */
338 write: movl %eax, 16(%esi) /* write the APIC register data */
340 jmp next_loop /* try another pass */
359 movl _apic_imen, %eax
360 notl %eax /* mask = ~mask */
361 andl _apic_imen, %eax /* %eax = _apic_imen & ~mask */
363 pushl %eax /* new (future) _apic_imen value */
364 pushl $0 /* APIC# arg */
365 call write_ioapic_mask /* modify the APIC registers */
367 addl $4, %esp /* remove APIC# arg from stack */
368 popl _apic_imen /* _apic_imen |= mask */
372 movl _apic_imen, %eax
373 orl 4(%esp), %eax /* %eax = _apic_imen | mask */
375 pushl %eax /* new (future) _apic_imen value */
376 pushl $0 /* APIC# arg */
377 call write_ioapic_mask /* modify the APIC registers */
379 addl $4, %esp /* remove APIC# arg from stack */
380 popl _apic_imen /* _apic_imen |= mask */
389 * u_int read_io_apic_mask(int apic);
396 * Set INT mask bit for each bit set in 'mask'.
397 * Ignore INT mask bit for all others.
399 * void set_io_apic_mask(apic, u_int32_t bits);
406 * void set_ioapic_maskbit(int apic, int bit);
413 * Clear INT mask bit for each bit set in 'mask'.
414 * Ignore INT mask bit for all others.
416 * void clr_io_apic_mask(int apic, u_int32_t bits);
423 * void clr_ioapic_maskbit(int apic, int bit);
431 /******************************************************************************
436 * u_int io_apic_write(int apic, int select);
439 movl 4(%esp), %ecx /* APIC # */
441 movl (%eax,%ecx,4), %edx /* APIC base register address */
442 movl 8(%esp), %eax /* target register index */
443 movl %eax, (%edx) /* write the target register index */
444 movl 16(%edx), %eax /* read the APIC register data */
445 ret /* %eax = register value */
448 * void io_apic_write(int apic, int select, int value);
451 movl 4(%esp), %ecx /* APIC # */
453 movl (%eax,%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 */
461 * Send an EOI to the local APIC.