1/*	$OpenBSD: vector.s,v 1.16 2003/04/17 03:42:14 drahn Exp $	*/
2/*	$NetBSD: vector.s,v 1.32 1996/01/07 21:29:47 mycroft Exp $	*/
3
4/*
5 * Copyright (c) 1993, 1994, 1995 Charles M. Hannum.  All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *	notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *	notice, this list of conditions and the following disclaimer in the
14 *	documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 *	must display the following acknowledgement:
17 *	This product includes software developed by Charles M. Hannum.
18 * 4. The name of the author may not be used to endorse or promote products
19 *	derived from this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33#include <i386/isa/icu.h>
34#include <dev/isa/isareg.h>
35
36#define ICU_HARDWARE_MASK
37
38#define MY_COUNT _C_LABEL(uvmexp)
39
40/*
41 * These macros are fairly self explanatory.  If ICU_SPECIAL_MASK_MODE is
42 * defined, we try to take advantage of the ICU's `special mask mode' by only
43 * EOIing the interrupts on return.  This avoids the requirement of masking and
44 * unmasking.  We can't do this without special mask mode, because the ICU
45 * would also hold interrupts that it thinks are of lower priority.
46 *
47 * Many machines do not support special mask mode, so by default we don't try
48 * to use it.
49 */
50
51#define	IRQ_BIT(irq_num)	(1 << ((irq_num) % 8))
52#define	IRQ_BYTE(irq_num)	((irq_num) / 8)
53
54#ifdef ICU_SPECIAL_MASK_MODE
55
56#define	ACK1(irq_num)
57#define	ACK2(irq_num) \
58	movb	$(0x60|IRQ_SLAVE),%al	/* specific EOI for IRQ2 */	;\
59	outb	%al,$IO_ICU1
60#define	MASK(irq_num, icu)
61#define	UNMASK(irq_num, icu) \
62	movb	$(0x60|(irq_num%8)),%al	/* specific EOI */		;\
63	outb	%al,$icu
64
65#else /* ICU_SPECIAL_MASK_MODE */
66
67#ifndef	AUTO_EOI_1
68#define	ACK1(irq_num) \
69	movb	$(0x60|(irq_num%8)),%al	/* specific EOI */		;\
70	outb	%al,$IO_ICU1
71#else
72#define	ACK1(irq_num)
73#endif
74
75#ifndef AUTO_EOI_2
76#define	ACK2(irq_num) \
77	movb	$(0x60|(irq_num%8)),%al	/* specific EOI */		;\
78	outb	%al,$IO_ICU2		/* do the second ICU first */	;\
79	movb	$(0x60|IRQ_SLAVE),%al	/* specific EOI for IRQ2 */	;\
80	outb	%al,$IO_ICU1
81#else
82#define	ACK2(irq_num)
83#endif
84
85#ifdef ICU_HARDWARE_MASK
86
87#define	MASK(irq_num, icu) \
88	movb	_C_LABEL(imen) + IRQ_BYTE(irq_num),%al				;\
89	orb	$IRQ_BIT(irq_num),%al					;\
90	movb	%al,_C_LABEL(imen) + IRQ_BYTE(irq_num)				;\
91	FASTER_NOP							;\
92	outb	%al,$(icu+1)
93#define	UNMASK(irq_num, icu) \
94	cli								;\
95	movb	_C_LABEL(imen) + IRQ_BYTE(irq_num),%al				;\
96	andb	$~IRQ_BIT(irq_num),%al					;\
97	movb	%al,_C_LABEL(imen) + IRQ_BYTE(irq_num)				;\
98	FASTER_NOP							;\
99	outb	%al,$(icu+1)						;\
100	sti
101
102#else /* ICU_HARDWARE_MASK */
103
104#define	MASK(irq_num, icu)
105#define	UNMASK(irq_num, icu)
106
107#endif /* ICU_HARDWARE_MASK */
108
109#endif /* ICU_SPECIAL_MASK_MODE */
110
111/*
112 * Macros for interrupt entry, call to handler, and exit.
113 *
114 * XXX
115 * The interrupt frame is set up to look like a trap frame.  This may be a
116 * waste.  The only handler which needs a frame is the clock handler, and it
117 * only needs a few bits.  Xdoreti() needs a trap frame for handling ASTs, but
118 * it could easily convert the frame on demand.
119 *
120 * The direct costs of setting up a trap frame are two pushl's (error code and
121 * trap number), an addl to get rid of these, and pushing and popping the
122 * callee-saved registers %esi, %edi, %ebx, and %ebp twice.
123 *
124 * If the interrupt frame is made more flexible,  INTR can push %eax first and
125 * decide the ipending case with less overhead, e.g., by avoiding loading the
126 * segment registers.
127 *
128 * XXX
129 * Should we do a cld on every system entry to avoid the requirement for
130 * scattered cld's?
131 */
132
133	.globl	_C_LABEL(isa_strayintr)
134
135/*
136 * Normal vectors.
137 *
138 * We cdr down the intrhand chain, calling each handler with its appropriate
139 * argument (0 meaning a pointer to the frame, for clock interrupts).
140 *
141 * The handler returns one of three values:
142 *   0 - This interrupt wasn't for me.
143 *   1 - This interrupt was for me.
144 *  -1 - This interrupt might have been for me, but I don't know.
145 * If there are no handlers, or they all return 0, we flags it as a `stray'
146 * interrupt.  On a system with level-triggered interrupts, we could terminate
147 * immediately when one of them returns 1; but this is a PC.
148 *
149 * On exit, we jump to Xdoreti(), to process soft interrupts and ASTs.
150 */
151#define	INTR(irq_num, icu, ack) \
152IDTVEC(recurse/**/irq_num)						;\
153	pushfl								;\
154	pushl	%cs							;\
155	pushl	%esi							;\
156	cli								;\
157_C_LABEL(Xintr)/**/irq_num/**/:						;\
158	pushl	$0			/* dummy error code */		;\
159	pushl	$T_ASTFLT		/* trap # for doing ASTs */	;\
160	INTRENTRY							;\
161	MAKE_FRAME							;\
162	MASK(irq_num, icu)		/* mask it in hardware */	;\
163	ack(irq_num)			/* and allow other intrs */	;\
164	incl	MY_COUNT+V_INTR		/* statistical info */		;\
165	movl	_C_LABEL(iminlevel) + (irq_num) * 4, %eax		;\
166	movzbl	_C_LABEL(cpl),%ebx					;\
167	cmpl	%eax,%ebx						;\
168	jae	_C_LABEL(Xhold/**/irq_num)/* currently masked; hold it */;\
169_C_LABEL(Xresume)/**/irq_num/**/:					;\
170	movzbl	_C_LABEL(cpl),%eax	/* cpl to restore on exit */	;\
171	pushl	%eax							;\
172	movl	_C_LABEL(imaxlevel) + (irq_num) * 4,%eax		;\
173	movl	%eax,_C_LABEL(cpl)	/* block enough for this irq */	;\
174	sti				/* safe to take intrs now */	;\
175	movl	_C_LABEL(intrhand) + (irq_num) * 4,%ebx	/* head of chain */	;\
176	testl	%ebx,%ebx						;\
177	jz	_C_LABEL(Xstray)/**/irq_num	/* no handlears; we're stray */	;\
178	STRAY_INITIALIZE		/* nobody claimed it yet */	;\
1797:	movl	IH_ARG(%ebx),%eax	/* get handler arg */		;\
180	testl	%eax,%eax						;\
181	jnz	4f							;\
182	movl	%esp,%eax		/* 0 means frame pointer */	;\
1834:	pushl	%eax							;\
184	call	*IH_FUN(%ebx)		/* call it */			;\
185	addl	$4,%esp			/* toss the arg */		;\
186	STRAY_INTEGRATE			/* maybe he claimed it */	;\
187	orl	%eax,%eax		/* should it be counted? */	;\
188	jz	5f			/* no, skip it */		;\
189	incl	IH_COUNT(%ebx)		/* count the intrs */		;\
1905:	movl	IH_NEXT(%ebx),%ebx	/* next handler in chain */	;\
191	testl	%ebx,%ebx						;\
192	jnz	7b							;\
193	STRAY_TEST			/* see if it's a stray */	;\
1946:	UNMASK(irq_num, icu)		/* unmask it in hardware */	;\
195	jmp	_C_LABEL(Xdoreti)	/* lower spl and do ASTs */	;\
196IDTVEC(stray/**/irq_num)						;\
197	pushl	$irq_num						;\
198	call	_C_LABEL(isa_strayintr)					;\
199	addl	$4,%esp							;\
200	jmp	6b							;\
201IDTVEC(hold/**/irq_num)							;\
202	orb	$IRQ_BIT(irq_num),_C_LABEL(ipending) + IRQ_BYTE(irq_num)	;\
203	INTRFASTEXIT
204
205#if defined(DEBUG) && defined(notdef)
206#define	STRAY_INITIALIZE \
207	xorl	%esi,%esi
208#define	STRAY_INTEGRATE \
209	orl	%eax,%esi
210#define	STRAY_TEST \
211	testl	%esi,%esi						;\
212	jz	_C_LABEL(Xstray)/**/irq_num
213#else /* !DEBUG */
214#define	STRAY_INITIALIZE
215#define	STRAY_INTEGRATE
216#define	STRAY_TEST
217#endif /* DEBUG */
218
219#ifdef DDB
220#define	MAKE_FRAME \
221	leal	-8(%esp),%ebp
222#else /* !DDB */
223#define	MAKE_FRAME
224#endif /* DDB */
225
226INTR(0, IO_ICU1, ACK1)
227INTR(1, IO_ICU1, ACK1)
228INTR(2, IO_ICU1, ACK1)
229INTR(3, IO_ICU1, ACK1)
230INTR(4, IO_ICU1, ACK1)
231INTR(5, IO_ICU1, ACK1)
232INTR(6, IO_ICU1, ACK1)
233INTR(7, IO_ICU1, ACK1)
234INTR(8, IO_ICU2, ACK2)
235INTR(9, IO_ICU2, ACK2)
236INTR(10, IO_ICU2, ACK2)
237INTR(11, IO_ICU2, ACK2)
238INTR(12, IO_ICU2, ACK2)
239INTR(13, IO_ICU2, ACK2)
240INTR(14, IO_ICU2, ACK2)
241INTR(15, IO_ICU2, ACK2)
242
243/*
244 * These tables are used by the ISA configuration code.
245 */
246/* interrupt service routine entry points */
247IDTVEC(intr)
248	.long   _C_LABEL(Xintr0), _C_LABEL(Xintr1), _C_LABEL(Xintr2)
249	.long	_C_LABEL(Xintr3), _C_LABEL(Xintr4), _C_LABEL(Xintr5)
250	.long	_C_LABEL(Xintr6), _C_LABEL(Xintr7), _C_LABEL(Xintr8)
251	.long	_C_LABEL(Xintr9), _C_LABEL(Xintr10), _C_LABEL(Xintr11)
252	.long	_C_LABEL(Xintr12), _C_LABEL(Xintr13)
253	.long	_C_LABEL(Xintr14), _C_LABEL(Xintr15)
254
255/*
256 * These tables are used by Xdoreti() and Xspllower().
257 */
258/* resume points for suspended interrupts */
259IDTVEC(resume)
260	.long	_C_LABEL(Xresume0), _C_LABEL(Xresume1)
261	.long	_C_LABEL(Xresume2), _C_LABEL(Xresume3)
262	.long	_C_LABEL(Xresume4), _C_LABEL(Xresume5)
263	.long	_C_LABEL(Xresume6), _C_LABEL(Xresume7)
264	.long	_C_LABEL(Xresume8), _C_LABEL(Xresume9)
265	.long	_C_LABEL(Xresume10), _C_LABEL(Xresume11)
266	.long	_C_LABEL(Xresume12), _C_LABEL(Xresume13)
267	.long	_C_LABEL(Xresume14), _C_LABEL(Xresume15)
268	/* for soft interrupts */
269	.long	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
270	.long	_C_LABEL(Xsofttty), _C_LABEL(Xsoftnet)
271	.long	_C_LABEL(Xsoftclock)
272/* fake interrupts to resume from splx() */
273IDTVEC(recurse)
274	.long	_C_LABEL(Xrecurse0), _C_LABEL(Xrecurse1)
275	.long	_C_LABEL(Xrecurse2), _C_LABEL(Xrecurse3)
276	.long	_C_LABEL(Xrecurse4), _C_LABEL(Xrecurse5)
277	.long	_C_LABEL(Xrecurse6), _C_LABEL(Xrecurse7)
278	.long	_C_LABEL(Xrecurse8), _C_LABEL(Xrecurse9)
279	.long	_C_LABEL(Xrecurse10), _C_LABEL(Xrecurse11)
280	.long	_C_LABEL(Xrecurse12), _C_LABEL(Xrecurse13)
281	.long	_C_LABEL(Xrecurse14), _C_LABEL(Xrecurse15)
282	/* for soft interrupts */
283	.long	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
284	.long	_C_LABEL(Xsofttty), _C_LABEL(Xsoftnet)
285	.long	_C_LABEL(Xsoftclock)
286
287/* Some bogus data, to keep vmstat happy, for now. */
288	.globl	_C_LABEL(intrnames), _C_LABEL(eintrnames)
289	.globl	_C_LABEL(intrcnt), _C_LABEL(eintrcnt)
290_C_LABEL(intrnames):
291	.long	0
292_C_LABEL(eintrnames):
293_C_LABEL(intrcnt):
294	.long	0
295_C_LABEL(eintrcnt):
296