NetBSD/sys/arch/i386/isa/vector.s

341 lines
11 KiB
ArmAsm

/* $NetBSD: vector.s,v 1.46 2000/01/20 03:19:27 enami Exp $ */
/*-
* Copyright (c) 1998 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Charles M. Hannum.
*
* 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 the NetBSD
* Foundation, Inc. and its contributors.
* 4. Neither the name of The NetBSD Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``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 FOUNDATION OR CONTRIBUTORS
* 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 "opt_ddb.h"
#include <i386/isa/icu.h>
#include <dev/isa/isareg.h>
#define ICU_HARDWARE_MASK
/*
* These macros are fairly self explanatory. If ICU_SPECIAL_MASK_MODE is
* defined, we try to take advantage of the ICU's `special mask mode' by only
* EOIing the interrupts on return. This avoids the requirement of masking and
* unmasking. We can't do this without special mask mode, because the ICU
* would also hold interrupts that it thinks are of lower priority.
*
* Many machines do not support special mask mode, so by default we don't try
* to use it.
*/
#define IRQ_BIT(irq_num) (1 << ((irq_num) % 8))
#define IRQ_BYTE(irq_num) ((irq_num) / 8)
#ifdef ICU_SPECIAL_MASK_MODE
#define ACK1(irq_num)
#define ACK2(irq_num) \
movb $(0x60|IRQ_SLAVE),%al /* specific EOI for IRQ2 */ ;\
outb %al,$IO_ICU1
#define MASK(irq_num, icu)
#define UNMASK(irq_num, icu) \
movb $(0x60|(irq_num%8)),%al /* specific EOI */ ;\
outb %al,$icu
#else /* ICU_SPECIAL_MASK_MODE */
#ifndef AUTO_EOI_1
#define ACK1(irq_num) \
movb $(0x60|(irq_num%8)),%al /* specific EOI */ ;\
outb %al,$IO_ICU1
#else
#define ACK1(irq_num)
#endif
#ifndef AUTO_EOI_2
#define ACK2(irq_num) \
movb $(0x60|(irq_num%8)),%al /* specific EOI */ ;\
outb %al,$IO_ICU2 /* do the second ICU first */ ;\
movb $(0x60|IRQ_SLAVE),%al /* specific EOI for IRQ2 */ ;\
outb %al,$IO_ICU1
#else
#define ACK2(irq_num)
#endif
#ifdef ICU_HARDWARE_MASK
#define MASK(irq_num, icu) \
movb _C_LABEL(imen) + IRQ_BYTE(irq_num),%al ;\
orb $IRQ_BIT(irq_num),%al ;\
movb %al,_C_LABEL(imen) + IRQ_BYTE(irq_num) ;\
FASTER_NOP ;\
outb %al,$(icu+1)
#define UNMASK(irq_num, icu) \
cli ;\
movb _C_LABEL(imen) + IRQ_BYTE(irq_num),%al ;\
andb $~IRQ_BIT(irq_num),%al ;\
movb %al,_C_LABEL(imen) + IRQ_BYTE(irq_num) ;\
FASTER_NOP ;\
outb %al,$(icu+1) ;\
sti
#else /* ICU_HARDWARE_MASK */
#define MASK(irq_num, icu)
#define UNMASK(irq_num, icu)
#endif /* ICU_HARDWARE_MASK */
#endif /* ICU_SPECIAL_MASK_MODE */
/*
* 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)
/*
* 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 flags 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 MY_COUNT _C_LABEL(uvmexp)
/* XXX See comment in locore.s */
#ifdef __ELF__
#define XINTR(irq_num) Xintr/**/irq_num
#define XHOLD(irq_num) Xhold/**/irq_num
#define XSTRAY(irq_num) Xstray/**/irq_num
#else
#define XINTR(irq_num) _Xintr/**/irq_num
#define XHOLD(irq_num) _Xhold/**/irq_num
#define XSTRAY(irq_num) _Xstray/**/irq_num
#endif
#define INTR(irq_num, icu, ack) \
IDTVEC(resume/**/irq_num) ;\
cli ;\
jmp 1f ;\
IDTVEC(recurse/**/irq_num) ;\
pushfl ;\
pushl %cs ;\
pushl %esi ;\
cli ;\
XINTR(irq_num): ;\
pushl $0 /* dummy error code */ ;\
pushl $T_ASTFLT /* trap # for doing ASTs */ ;\
INTRENTRY ;\
MAKE_FRAME ;\
MASK(irq_num, icu) /* mask it in hardware */ ;\
ack(irq_num) /* and allow other intrs */ ;\
incl MY_COUNT+V_INTR /* statistical info */ ;\
testb $IRQ_BIT(irq_num),_C_LABEL(cpl) + IRQ_BYTE(irq_num) ;\
jnz XHOLD(irq_num) /* currently masked; hold it */ ;\
1: movl _C_LABEL(cpl),%eax /* cpl to restore on exit */ ;\
pushl %eax ;\
orl _C_LABEL(intrmask) + (irq_num) * 4,%eax ;\
movl %eax,_C_LABEL(cpl) /* add in this intr's mask */ ;\
sti /* safe to take intrs now */ ;\
movl _C_LABEL(intrhand) + (irq_num) * 4,%ebx /* head of chain */ ;\
testl %ebx,%ebx ;\
jz XSTRAY(irq_num) /* no handlers; we're stray */ ;\
STRAY_INITIALIZE /* nobody claimed it yet */ ;\
incl _C_LABEL(intrcnt) + (4*(irq_num)) /* XXX */ ;\
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 */ ;\
incl IH_COUNT(%ebx) /* count the intrs */ ;\
movl IH_NEXT(%ebx),%ebx /* next handler in chain */ ;\
testl %ebx,%ebx ;\
jnz 7b ;\
STRAY_TEST /* see if it's a stray */ ;\
5: UNMASK(irq_num, icu) /* unmask it in hardware */ ;\
jmp _C_LABEL(Xdoreti) /* lower spl and do ASTs */ ;\
IDTVEC(stray/**/irq_num) ;\
pushl $irq_num ;\
call _C_LABEL(isa_strayintr) ;\
addl $4,%esp ;\
incl _C_LABEL(strayintrcnt) + (4*(irq_num)) ;\
jmp 5b ;\
IDTVEC(hold/**/irq_num) ;\
orb $IRQ_BIT(irq_num),_C_LABEL(ipending) + IRQ_BYTE(irq_num) ;\
INTRFASTEXIT
#if defined(DEBUG) && defined(notdef)
#define STRAY_INITIALIZE \
xorl %esi,%esi
#define STRAY_INTEGRATE \
orl %eax,%esi
#define STRAY_TEST \
testl %esi,%esi ;\
jz XSTRAY(irq_num)
#else /* !DEBUG */
#define STRAY_INITIALIZE
#define STRAY_INTEGRATE
#define STRAY_TEST
#endif /* DEBUG */
#ifdef DDB
#define MAKE_FRAME \
leal -8(%esp),%ebp
#else /* !DDB */
#define MAKE_FRAME
#endif /* DDB */
INTR(0, IO_ICU1, ACK1)
INTR(1, IO_ICU1, ACK1)
INTR(2, IO_ICU1, ACK1)
INTR(3, IO_ICU1, ACK1)
INTR(4, IO_ICU1, ACK1)
INTR(5, IO_ICU1, ACK1)
INTR(6, IO_ICU1, ACK1)
INTR(7, IO_ICU1, ACK1)
INTR(8, IO_ICU2, ACK2)
INTR(9, IO_ICU2, ACK2)
INTR(10, IO_ICU2, ACK2)
INTR(11, IO_ICU2, ACK2)
INTR(12, IO_ICU2, ACK2)
INTR(13, IO_ICU2, ACK2)
INTR(14, IO_ICU2, ACK2)
INTR(15, IO_ICU2, ACK2)
/*
* These tables are used by the ISA configuration code.
*/
/* interrupt service routine entry points */
IDTVEC(intr)
.long _C_LABEL(Xintr0), _C_LABEL(Xintr1)
.long _C_LABEL(Xintr2), _C_LABEL(Xintr3)
.long _C_LABEL(Xintr4), _C_LABEL(Xintr5)
.long _C_LABEL(Xintr6), _C_LABEL(Xintr7)
.long _C_LABEL(Xintr8), _C_LABEL(Xintr9)
.long _C_LABEL(Xintr10), _C_LABEL(Xintr11)
.long _C_LABEL(Xintr12), _C_LABEL(Xintr13)
.long _C_LABEL(Xintr14), _C_LABEL(Xintr15)
/*
* These tables are used by Xdoreti() and Xspllower().
*/
/* resume points for suspended interrupts */
IDTVEC(resume)
.long _C_LABEL(Xresume0), _C_LABEL(Xresume1)
.long _C_LABEL(Xresume2), _C_LABEL(Xresume3)
.long _C_LABEL(Xresume4), _C_LABEL(Xresume5)
.long _C_LABEL(Xresume6), _C_LABEL(Xresume7)
.long _C_LABEL(Xresume8), _C_LABEL(Xresume9)
.long _C_LABEL(Xresume10), _C_LABEL(Xresume11)
.long _C_LABEL(Xresume12), _C_LABEL(Xresume13)
.long _C_LABEL(Xresume14), _C_LABEL(Xresume15)
/* for soft interrupts */
.long 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
.long _C_LABEL(Xsoftserial), _C_LABEL(Xsoftnet), _C_LABEL(Xsoftclock)
/* fake interrupts to resume from splx() */
IDTVEC(recurse)
.long _C_LABEL(Xrecurse0), _C_LABEL(Xrecurse1)
.long _C_LABEL(Xrecurse2), _C_LABEL(Xrecurse3)
.long _C_LABEL(Xrecurse4), _C_LABEL(Xrecurse5)
.long _C_LABEL(Xrecurse6), _C_LABEL(Xrecurse7)
.long _C_LABEL(Xrecurse8), _C_LABEL(Xrecurse9)
.long _C_LABEL(Xrecurse10), _C_LABEL(Xrecurse11)
.long _C_LABEL(Xrecurse12), _C_LABEL(Xrecurse13)
.long _C_LABEL(Xrecurse14), _C_LABEL(Xrecurse15)
/* for soft interrupts */
.long 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
.long _C_LABEL(Xsoftserial), _C_LABEL(Xsoftnet), _C_LABEL(Xsoftclock)
/* Old-style vmstat -i interrupt counters. Should be replaced with evcnts. */
.globl _C_LABEL(intrnames), _C_LABEL(eintrnames)
.globl _C_LABEL(intrcnt), _C_LABEL(eintrcnt)
/* Names */
_C_LABEL(intrnames):
.asciz "irq0", "irq1", "irq2", "irq3"
.asciz "irq4", "irq5", "irq6", "irq7"
.asciz "irq8", "irq9", "irq10", "irq11"
.asciz "irq12", "irq13", "irq14", "irq15"
_C_LABEL(strayintrnames):
.asciz "stray0", "stray1", "stray2", "stray3"
.asciz "stray4", "stray5", "stray6", "stray7"
.asciz "stray8", "stray9", "stray10", "stray11"
.asciz "stray12", "stray13", "stray14", "stray15"
_C_LABEL(eintrnames):
/* And counters */
.data
#ifdef __ELF__
.align 16
#else
.align 4
#endif
_C_LABEL(intrcnt):
.long 0, 0, 0, 0, 0, 0, 0, 0
.long 0, 0, 0, 0, 0, 0, 0, 0
_C_LABEL(strayintrcnt):
.long 0, 0, 0, 0, 0, 0, 0, 0
.long 0, 0, 0, 0, 0, 0, 0, 0
_C_LABEL(eintrcnt):
.text