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.6 2003/07/01 20:31:38 dillon Exp $
35 * Routines used by splz_unpend to build an interrupt frame from a
36 * trap frame. The _vec[] routines build the proper frame on the stack,
37 * then call one of _Xintr0 thru _XintrNN.
40 * i386/isa/apic_ipl.s (this file): splz_unpend JUMPs to HWIs.
41 * i386/isa/clock.c: setup _vec[clock] to point at _vec8254.
45 .long vec0, vec1, vec2, vec3, vec4, vec5, vec6, vec7
46 .long vec8, vec9, vec10, vec11, vec12, vec13, vec14, vec15
47 .long vec16, vec17, vec18, vec19, vec20, vec21, vec22, vec23
51 * This is the UP equivilant of _imen.
52 * It is OPAQUE, and must NOT be accessed directly.
53 * It MUST be accessed along with the IO APIC as a 'critical region'.
61 .p2align 2 /* MUST be 32bit aligned */
74 * splz() - dispatch pending interrupts after cpl reduced
76 * Interrupt priority mechanism
77 * -- soft splXX masks with group mechanism (cpl)
78 * -- h/w masks for currently active or unused interrupts (imen)
79 * -- ipending = active interrupts currently masked by cpl
84 * The caller has restored cpl and checked that (ipending & ~cpl)
85 * is nonzero. However, since ipending can change at any time
86 * (by an interrupt or, with SMP, by another cpu), we have to
87 * repeat the check. At the moment we must own the MP lock in
88 * the SMP case because the interruput handlers require it. We
89 * loop until no unmasked pending interrupts remain.
91 * No new unmaksed pending interrupts will be added during the
92 * loop because, being unmasked, the interrupt code will be able
93 * to execute the interrupts.
95 * Interrupts come in two flavors: Hardware interrupts and software
96 * interrupts. We have to detect the type of interrupt (based on the
97 * position of the interrupt bit) and call the appropriate dispatch
100 * NOTE: "bsfl %ecx,%ecx" is undefined when %ecx is 0 so we can't
101 * rely on the secondary btrl tests.
105 movl TD_CPL(%ebx),%eax
108 * We don't need any locking here. (ipending & ~cpl) cannot grow
109 * while we're looking at it - any interrupt will shrink it to 0.
113 notl %ecx /* set bit = unmasked level */
114 andl _ipending,%ecx /* set bit = unmasked pending INT */
128 * We would prefer to call the intr handler directly here but that
129 * doesn't work for badly behaved handlers that want the interrupt
130 * frame. Also, there's a problem determining the unit number.
131 * We should change the interface so that the unit number is not
132 * determined at config time.
134 * The vec[] routines build the proper frame on the stack so
135 * the interrupt will eventually return to the caller or splz,
136 * then calls one of _Xintr0 thru _XintrNN.
143 pushl %eax /* save cpl across call */
144 orl imasks(,%ecx,4),%eax
145 movl %eax,TD_CPL(%ebx) /* set cpl for SWI */
146 call *_ihandlers(,%ecx,4)
148 movl %eax,TD_CPL(%ebx) /* restore cpl and loop */
152 * Fake clock interrupt(s) so that they appear to come from our caller instead
153 * of from here, so that system profiling works.
154 * XXX do this more generally (for all vectors; look up the C entry point).
155 * XXX frame bogusness stops us from just jumping to the C entry point.
156 * We have to clear iactive since this is an unpend call, and it will be
157 * set from the time of the original INT.
161 * The 'generic' vector stubs.
164 #define BUILD_VEC(irq_num) \
166 __CONCAT(vec,irq_num): ; \
172 lock ; /* MP-safe */ \
173 andl $~IRQ_BIT(irq_num), iactive ; /* lazy masking */ \
175 APIC_ITRACE(apic_itrace_splz, irq_num, APIC_ITRACE_SPLZ) ; \
176 jmp __CONCAT(_Xintr,irq_num)
195 BUILD_VEC(16) /* 8 additional INTs in IO APIC */
205 /******************************************************************************
206 * XXX FIXME: figure out where these belong.
209 /* this nonsense is to verify that masks ALWAYS have 1 and only 1 bit set */
210 #define QUALIFY_MASKS_NOT
213 #define QUALIFY_MASK \
221 bad_mask: .asciz "bad mask"
227 * (soon to be) MP-safe function to clear ONE INT mask bit.
228 * The passed arg is a 32bit u_int MASK.
229 * It sets the associated bit in _apic_imen.
230 * It sets the mask bit of the associated IO APIC register.
233 pushfl /* save state of EI flag */
234 cli /* prevent recursion */
235 IMASK_LOCK /* enter critical reg */
237 movl 8(%esp), %eax /* mask into %eax */
238 bsfl %eax, %ecx /* get pin index */
239 btrl %ecx, apic_imen /* update apic_imen */
244 movl CNAME(int_to_apicintpin) + 8(%ecx), %edx
245 movl CNAME(int_to_apicintpin) + 12(%ecx), %ecx
249 movl %ecx, (%edx) /* write the target register index */
250 movl 16(%edx), %eax /* read the target register data */
251 andl $~IOART_INTMASK, %eax /* clear mask bit */
252 movl %eax, 16(%edx) /* write the APIC register data */
254 IMASK_UNLOCK /* exit critical reg */
255 popfl /* restore old state of EI flag */
259 * (soon to be) MP-safe function to set ONE INT mask bit.
260 * The passed arg is a 32bit u_int MASK.
261 * It clears the associated bit in apic_imen.
262 * It clears the mask bit of the associated IO APIC register.
265 pushfl /* save state of EI flag */
266 cli /* prevent recursion */
267 IMASK_LOCK /* enter critical reg */
269 movl 8(%esp), %eax /* mask into %eax */
270 bsfl %eax, %ecx /* get pin index */
271 btsl %ecx, apic_imen /* update _apic_imen */
276 movl CNAME(int_to_apicintpin) + 8(%ecx), %edx
277 movl CNAME(int_to_apicintpin) + 12(%ecx), %ecx
281 movl %ecx, (%edx) /* write the target register index */
282 movl 16(%edx), %eax /* read the target register data */
283 orl $IOART_INTMASK, %eax /* set mask bit */
284 movl %eax, 16(%edx) /* write the APIC register data */
286 IMASK_UNLOCK /* exit critical reg */
287 popfl /* restore old state of EI flag */
291 /******************************************************************************
297 * void write_ioapic_mask(int apic, u_int mask);
300 #define _INT_MASK 0x00010000
301 #define _PIN_MASK 0x00ffffff
303 #define _OLD_ESI 0(%esp)
304 #define _OLD_EBX 4(%esp)
305 #define _RETADDR 8(%esp)
306 #define _APIC 12(%esp)
307 #define _MASK 16(%esp)
311 pushl %ebx /* scratch */
312 pushl %esi /* scratch */
315 xorl _MASK, %ebx /* %ebx = _apic_imen ^ mask */
316 andl $_PIN_MASK, %ebx /* %ebx = _apic_imen & 0x00ffffff */
317 jz all_done /* no change, return */
319 movl _APIC, %esi /* APIC # */
321 movl (%ecx,%esi,4), %esi /* %esi holds APIC base address */
323 next_loop: /* %ebx = diffs, %esi = APIC base */
324 bsfl %ebx, %ecx /* %ecx = index if 1st/next set bit */
327 btrl %ecx, %ebx /* clear this bit in diffs */
328 leal 16(,%ecx,2), %edx /* calculate register index */
330 movl %edx, (%esi) /* write the target register index */
331 movl 16(%esi), %eax /* read the target register data */
333 btl %ecx, _MASK /* test for mask or unmask */
334 jnc clear /* bit is clear */
335 orl $_INT_MASK, %eax /* set mask bit */
337 clear: andl $~_INT_MASK, %eax /* clear mask bit */
339 write: movl %eax, 16(%esi) /* write the APIC register data */
341 jmp next_loop /* try another pass */
361 notl %eax /* mask = ~mask */
362 andl apic_imen, %eax /* %eax = _apic_imen & ~mask */
364 pushl %eax /* new (future) _apic_imen value */
365 pushl $0 /* APIC# arg */
366 call write_ioapic_mask /* modify the APIC registers */
368 addl $4, %esp /* remove APIC# arg from stack */
369 popl apic_imen /* _apic_imen |= mask */
373 movl _apic_imen, %eax
374 orl 4(%esp), %eax /* %eax = _apic_imen | mask */
376 pushl %eax /* new (future) _apic_imen value */
377 pushl $0 /* APIC# arg */
378 call write_ioapic_mask /* modify the APIC registers */
380 addl $4, %esp /* remove APIC# arg from stack */
381 popl apic_imen /* _apic_imen |= mask */
390 * u_int read_io_apic_mask(int apic);
397 * Set INT mask bit for each bit set in 'mask'.
398 * Ignore INT mask bit for all others.
400 * void set_io_apic_mask(apic, u_int32_t bits);
407 * void set_ioapic_maskbit(int apic, int bit);
414 * Clear INT mask bit for each bit set in 'mask'.
415 * Ignore INT mask bit for all others.
417 * void clr_io_apic_mask(int apic, u_int32_t bits);
424 * void clr_ioapic_maskbit(int apic, int bit);
432 /******************************************************************************
437 * u_int io_apic_write(int apic, int select);
440 movl 4(%esp), %ecx /* APIC # */
442 movl (%eax,%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 */
449 * void io_apic_write(int apic, int select, int value);
452 movl 4(%esp), %ecx /* APIC # */
454 movl (%eax,%ecx,4), %edx /* APIC base register address */
455 movl 8(%esp), %eax /* target register index */
456 movl %eax, (%edx) /* write the target register index */
457 movl 12(%esp), %eax /* target register value */
458 movl %eax, 16(%edx) /* write the APIC register data */
459 ret /* %eax = void */
462 * Send an EOI to the local APIC.