/* $OpenBSD: vector.s,v 1.12 2008/04/26 14:33:27 kettenis Exp $ */ /* $NetBSD: vector.s,v 1.32 1996/01/07 21:29:47 mycroft Exp $ */ /* * Copyright (c) 1993, 1994, 1995 Charles M. Hannum. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Charles M. Hannum. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #define MY_COUNT _C_LABEL(uvmexp) /* * Macros for interrupt entry, call to handler, and exit. * * XXX * The interrupt frame is set up to look like a trap frame. This may be a * waste. The only handler which needs a frame is the clock handler, and it * only needs a few bits. Xdoreti() needs a trap frame for handling ASTs, but * it could easily convert the frame on demand. * * The direct costs of setting up a trap frame are two pushl's (error code and * trap number), an addl to get rid of these, and pushing and popping the * callee-saved registers %esi, %edi, %ebx, and %ebp twice. * * If the interrupt frame is made more flexible, INTR can push %eax first and * decide the ipending case with less overhead, e.g., by avoiding loading the * segment registers. * * XXX * Should we do a cld on every system entry to avoid the requirement for * scattered cld's? */ .globl _C_LABEL(isa_strayintr) #ifdef MULTIPROCESSOR #define LOCK_KERNEL(ipl) pushl ipl; call _C_LABEL(i386_intlock); addl $4,%esp #define UNLOCK_KERNEL(ipl) pushl ipl; call _C_LABEL(i386_intunlock); addl $4,%esp #else #define LOCK_KERNEL(ipl) #define UNLOCK_KERNEL(ipl) #endif #define voidop(num) /* * Normal vectors. * * We cdr down the intrhand chain, calling each handler with its appropriate * argument (0 meaning a pointer to the frame, for clock interrupts). * * The handler returns one of three values: * 0 - This interrupt wasn't for me. * 1 - This interrupt was for me. * -1 - This interrupt might have been for me, but I don't know. * If there are no handlers, or they all return 0, we flag it as a `stray' * interrupt. On a system with level-triggered interrupts, we could terminate * immediately when one of them returns 1; but this is a PC. * * On exit, we jump to Xdoreti(), to process soft interrupts and ASTs. */ #define INTRSTUB(name, num, early_ack, late_ack, mask, unmask, level_mask) \ IDTVEC(resume_/**/name/**/num) ;\ push %ebx ;\ cli ;\ jmp 1f ;\ IDTVEC(recurse_/**/name/**/num) ;\ pushfl ;\ pushl %cs ;\ pushl %esi ;\ pushl $0 /* dummy error code */ ;\ pushl $T_ASTFLT /* trap # for doing ASTs */ ;\ movl %ebx,%esi ;\ INTRENTRY ;\ MAKE_FRAME ;\ push %esi ;\ cli ;\ jmp 1f ;\ _C_LABEL(Xintr_/**/name/**/num): ;\ pushl $0 /* dummy error code */ ;\ pushl $T_ASTFLT /* trap # for doing ASTs */ ;\ INTRENTRY ;\ MAKE_FRAME ;\ mask(num) /* mask it in hardware */ ;\ early_ack(num) /* and allow other intrs */ ;\ incl MY_COUNT+V_INTR /* statistical info */ ;\ movl _C_LABEL(iminlevel) + (num) * 4, %eax ;\ movl CPL,%ebx ;\ cmpl %eax,%ebx ;\ jae _C_LABEL(Xhold_/**/name/**/num)/* currently masked; hold it */;\ pushl %ebx /* cpl to restore on exit */ ;\ 1: ;\ movl _C_LABEL(imaxlevel) + (num) * 4,%eax ;\ movl %eax,CPL /* block enough for this irq */ ;\ sti /* safe to take intrs now */ ;\ movl _C_LABEL(intrhand) + (num) * 4,%ebx /* head of chain */ ;\ testl %ebx,%ebx ;\ jz _C_LABEL(Xstray_/**/name/**/num) /* no handlers; we're stray */ ;\ STRAY_INITIALIZE /* nobody claimed it yet */ ;\ LOCK_KERNEL(IF_PPL(%esp)) ;\ 7: movl IH_ARG(%ebx),%eax /* get handler arg */ ;\ testl %eax,%eax ;\ jnz 4f ;\ movl %esp,%eax /* 0 means frame pointer */ ;\ 4: pushl %eax ;\ call *IH_FUN(%ebx) /* call it */ ;\ addl $4,%esp /* toss the arg */ ;\ STRAY_INTEGRATE /* maybe he claimed it */ ;\ orl %eax,%eax /* should it be counted? */ ;\ jz 5f /* no, skip it */ ;\ addl $1,IH_COUNT(%ebx) /* count the intrs */ ;\ adcl $0,IH_COUNT+4(%ebx) ;\ 5: movl IH_NEXT(%ebx),%ebx /* next handler in chain */ ;\ testl %ebx,%ebx ;\ jnz 7b ;\ UNLOCK_KERNEL(IF_PPL(%esp)) ;\ STRAY_TEST(name,num) /* see if it's a stray */ ;\ 6: unmask(num) /* unmask it in hardware */ ;\ late_ack(num) ;\ jmp _C_LABEL(Xdoreti) /* lower spl and do ASTs */ ;\ IDTVEC(stray_/**/name/**/num) ;\ pushl $num ;\ call _C_LABEL(isa_strayintr) ;\ addl $4,%esp ;\ jmp 6b ;\ IDTVEC(hold_/**/name/**/num) ;\ orb $IRQ_BIT(num),CPUVAR(IPENDING) + IRQ_BYTE(num) ;\ INTRFASTEXIT #if defined(DEBUG) #define STRAY_INITIALIZE \ xorl %esi,%esi #define STRAY_INTEGRATE \ orl %eax,%esi #define STRAY_TEST(name,num) \ testl %esi,%esi ;\ jz _C_LABEL(Xstray_/**/name/**/num) #else /* !DEBUG */ #define STRAY_INITIALIZE #define STRAY_INTEGRATE #define STRAY_TEST(name,num) #endif /* DEBUG */ #ifdef DDB #define MAKE_FRAME \ leal -8(%esp),%ebp #else /* !DDB */ #define MAKE_FRAME #endif /* DDB */ #define ICUADDR IO_ICU1 INTRSTUB(legacy,0, i8259_asm_ack1, voidop, i8259_asm_mask, i8259_asm_unmask, voidop) INTRSTUB(legacy,1, i8259_asm_ack1, voidop, i8259_asm_mask, i8259_asm_unmask, voidop) INTRSTUB(legacy,2, i8259_asm_ack1, voidop, i8259_asm_mask, i8259_asm_unmask, voidop) INTRSTUB(legacy,3, i8259_asm_ack1, voidop, i8259_asm_mask, i8259_asm_unmask, voidop) INTRSTUB(legacy,4, i8259_asm_ack1, voidop, i8259_asm_mask, i8259_asm_unmask, voidop) INTRSTUB(legacy,5, i8259_asm_ack1, voidop, i8259_asm_mask, i8259_asm_unmask, voidop) INTRSTUB(legacy,6, i8259_asm_ack1, voidop, i8259_asm_mask, i8259_asm_unmask, voidop) INTRSTUB(legacy,7, i8259_asm_ack1, voidop, i8259_asm_mask, i8259_asm_unmask, voidop) #undef ICUADDR #define ICUADDR IO_ICU2 INTRSTUB(legacy,8, i8259_asm_ack2, voidop, i8259_asm_mask, i8259_asm_unmask, voidop) INTRSTUB(legacy,9, i8259_asm_ack2, voidop, i8259_asm_mask, i8259_asm_unmask, voidop) INTRSTUB(legacy,10, i8259_asm_ack2, voidop, i8259_asm_mask, i8259_asm_unmask, voidop) INTRSTUB(legacy,11, i8259_asm_ack2, voidop, i8259_asm_mask, i8259_asm_unmask, voidop) INTRSTUB(legacy,12, i8259_asm_ack2, voidop, i8259_asm_mask, i8259_asm_unmask, voidop) INTRSTUB(legacy,13, i8259_asm_ack2, voidop, i8259_asm_mask, i8259_asm_unmask, voidop) INTRSTUB(legacy,14, i8259_asm_ack2, voidop, i8259_asm_mask, i8259_asm_unmask, voidop) INTRSTUB(legacy,15, i8259_asm_ack2, voidop, i8259_asm_mask, i8259_asm_unmask, voidop) /* * These tables are used by the ISA configuration code. */ /* interrupt service routine entry points */ IDTVEC(intr) .long _C_LABEL(Xintr_legacy0), _C_LABEL(Xintr_legacy1) .long _C_LABEL(Xintr_legacy2), _C_LABEL(Xintr_legacy3) .long _C_LABEL(Xintr_legacy4), _C_LABEL(Xintr_legacy5) .long _C_LABEL(Xintr_legacy6), _C_LABEL(Xintr_legacy7) .long _C_LABEL(Xintr_legacy8), _C_LABEL(Xintr_legacy9) .long _C_LABEL(Xintr_legacy10), _C_LABEL(Xintr_legacy11) .long _C_LABEL(Xintr_legacy12), _C_LABEL(Xintr_legacy13) .long _C_LABEL(Xintr_legacy14), _C_LABEL(Xintr_legacy15) /* * These tables are used by Xdoreti() and Xspllower(). */ /* resume points for suspended interrupts */ IDTVEC(resume) .long _C_LABEL(Xresume_legacy0), _C_LABEL(Xresume_legacy1) .long _C_LABEL(Xresume_legacy2), _C_LABEL(Xresume_legacy3) .long _C_LABEL(Xresume_legacy4), _C_LABEL(Xresume_legacy5) .long _C_LABEL(Xresume_legacy6), _C_LABEL(Xresume_legacy7) .long _C_LABEL(Xresume_legacy8), _C_LABEL(Xresume_legacy9) .long _C_LABEL(Xresume_legacy10), _C_LABEL(Xresume_legacy11) .long _C_LABEL(Xresume_legacy12), _C_LABEL(Xresume_legacy13) .long _C_LABEL(Xresume_legacy14), _C_LABEL(Xresume_legacy15) /* for soft interrupts */ .long 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 .long _C_LABEL(Xsofttty), _C_LABEL(Xsoftnet), _C_LABEL(Xsoftclock) .long 0, 0 /* fake interrupts to resume from splx() */ IDTVEC(recurse) .long _C_LABEL(Xrecurse_legacy0), _C_LABEL(Xrecurse_legacy1) .long _C_LABEL(Xrecurse_legacy2), _C_LABEL(Xrecurse_legacy3) .long _C_LABEL(Xrecurse_legacy4), _C_LABEL(Xrecurse_legacy5) .long _C_LABEL(Xrecurse_legacy6), _C_LABEL(Xrecurse_legacy7) .long _C_LABEL(Xrecurse_legacy8), _C_LABEL(Xrecurse_legacy9) .long _C_LABEL(Xrecurse_legacy10), _C_LABEL(Xrecurse_legacy11) .long _C_LABEL(Xrecurse_legacy12), _C_LABEL(Xrecurse_legacy13) .long _C_LABEL(Xrecurse_legacy14), _C_LABEL(Xrecurse_legacy15) /* for soft interrupts */ .long 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 .long _C_LABEL(Xsofttty), _C_LABEL(Xsoftnet), _C_LABEL(Xsoftclock) .long 0, 0