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 $
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.
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.
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
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'.
59 .p2align 2 /* MUST be 32bit aligned */
72 * splz() - dispatch pending interrupts after cpl reduced
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
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.
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.
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
98 * NOTE: "bsfl %ecx,%ecx" is undefined when %ecx is 0 so we can't
99 * rely on the secondary btrl tests.
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.
108 notl %ecx /* set bit = unmasked level */
109 andl _ipending,%ecx /* set bit = unmasked pending INT */
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.
128 * The vec[] routines build the proper frame on the stack,
129 * then call one of _Xintr0 thru _XintrNN.
136 orl imasks(,%ecx,4),%eax
138 call *_ihandlers(,%ecx,4)
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.
153 * The 'generic' vector stubs.
156 #define BUILD_VEC(irq_num) \
158 __CONCAT(vec,irq_num): ; \
164 lock ; /* MP-safe */ \
165 andl $~IRQ_BIT(irq_num), iactive ; /* lazy masking */ \
167 APIC_ITRACE(apic_itrace_splz, irq_num, APIC_ITRACE_SPLZ) ; \
168 jmp __CONCAT(_Xintr,irq_num)
187 BUILD_VEC(16) /* 8 additional INTs in IO APIC */
197 /******************************************************************************
198 * XXX FIXME: figure out where these belong.
201 /* this nonsense is to verify that masks ALWAYS have 1 and only 1 bit set */
202 #define QUALIFY_MASKS_NOT
205 #define QUALIFY_MASK \
213 bad_mask: .asciz "bad mask"
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.
225 pushfl /* save state of EI flag */
226 cli /* prevent recursion */
227 IMASK_LOCK /* enter critical reg */
229 movl 8(%esp), %eax /* mask into %eax */
230 bsfl %eax, %ecx /* get pin index */
231 btrl %ecx, _apic_imen /* update _apic_imen */
236 movl CNAME(int_to_apicintpin) + 8(%ecx), %edx
237 movl CNAME(int_to_apicintpin) + 12(%ecx), %ecx
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 */
246 IMASK_UNLOCK /* exit critical reg */
247 popfl /* restore old state of EI flag */
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.
257 pushfl /* save state of EI flag */
258 cli /* prevent recursion */
259 IMASK_LOCK /* enter critical reg */
261 movl 8(%esp), %eax /* mask into %eax */
262 bsfl %eax, %ecx /* get pin index */
263 btsl %ecx, _apic_imen /* update _apic_imen */
268 movl CNAME(int_to_apicintpin) + 8(%ecx), %edx
269 movl CNAME(int_to_apicintpin) + 12(%ecx), %ecx
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 */
278 IMASK_UNLOCK /* exit critical reg */
279 popfl /* restore old state of EI flag */
283 /******************************************************************************
289 * void write_ioapic_mask(int apic, u_int mask);
292 #define _INT_MASK 0x00010000
293 #define _PIN_MASK 0x00ffffff
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)
303 pushl %ebx /* scratch */
304 pushl %esi /* scratch */
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 */
311 movl _APIC, %esi /* APIC # */
313 movl (%ecx,%esi,4), %esi /* %esi holds APIC base address */
315 next_loop: /* %ebx = diffs, %esi = APIC base */
316 bsfl %ebx, %ecx /* %ecx = index if 1st/next set bit */
319 btrl %ecx, %ebx /* clear this bit in diffs */
320 leal 16(,%ecx,2), %edx /* calculate register index */
322 movl %edx, (%esi) /* write the target register index */
323 movl 16(%esi), %eax /* read the target register data */
325 btl %ecx, _MASK /* test for mask or unmask */
326 jnc clear /* bit is clear */
327 orl $_INT_MASK, %eax /* set mask bit */
329 clear: andl $~_INT_MASK, %eax /* clear mask bit */
331 write: movl %eax, 16(%esi) /* write the APIC register data */
333 jmp next_loop /* try another pass */
352 movl _apic_imen, %eax
353 notl %eax /* mask = ~mask */
354 andl _apic_imen, %eax /* %eax = _apic_imen & ~mask */
356 pushl %eax /* new (future) _apic_imen value */
357 pushl $0 /* APIC# arg */
358 call write_ioapic_mask /* modify the APIC registers */
360 addl $4, %esp /* remove APIC# arg from stack */
361 popl _apic_imen /* _apic_imen |= mask */
365 movl _apic_imen, %eax
366 orl 4(%esp), %eax /* %eax = _apic_imen | mask */
368 pushl %eax /* new (future) _apic_imen value */
369 pushl $0 /* APIC# arg */
370 call write_ioapic_mask /* modify the APIC registers */
372 addl $4, %esp /* remove APIC# arg from stack */
373 popl _apic_imen /* _apic_imen |= mask */
382 * u_int read_io_apic_mask(int apic);
389 * Set INT mask bit for each bit set in 'mask'.
390 * Ignore INT mask bit for all others.
392 * void set_io_apic_mask(apic, u_int32_t bits);
399 * void set_ioapic_maskbit(int apic, int bit);
406 * Clear INT mask bit for each bit set in 'mask'.
407 * Ignore INT mask bit for all others.
409 * void clr_io_apic_mask(int apic, u_int32_t bits);
416 * void clr_ioapic_maskbit(int apic, int bit);
424 /******************************************************************************
429 * u_int io_apic_write(int apic, int select);
432 movl 4(%esp), %ecx /* APIC # */
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 */
441 * void io_apic_write(int apic, int select, int value);
444 movl 4(%esp), %ecx /* APIC # */
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 */
454 * Send an EOI to the local APIC.