FreeBSD/Linux Kernel Cross Reference
sys/i386/isa/vector.s
1 /*
2 * from: vector.s, 386BSD 0.1 unknown origin
3 * $FreeBSD: src/sys/i386/isa/vector.s,v 1.21.2.5 1999/09/05 08:13:41 peter Exp $
4 */
5
6 /*
7 * modified for PC98 by Kakefuda
8 */
9
10 #include "opt_auto_eoi.h"
11
12 #include <i386/isa/icu.h>
13 #ifdef PC98
14 #include <pc98/pc98/pc98.h>
15 #else
16 #include <i386/isa/isa.h>
17 #endif
18
19 #ifdef PC98
20 #define ICU_IMR_OFFSET 2 /* IO_ICU{1,2} + 2 */
21 #else
22 #define ICU_IMR_OFFSET 1 /* IO_ICU{1,2} + 1 */
23 #endif
24
25 #define ICU_EOI 0x20 /* XXX - define elsewhere */
26
27 #define IRQ_BIT(irq_num) (1 << ((irq_num) % 8))
28 #define IRQ_BYTE(irq_num) ((irq_num) / 8)
29
30 #ifdef AUTO_EOI_1
31 #define ENABLE_ICU1 /* use auto-EOI to reduce i/o */
32 #define OUTB_ICU1
33 #else
34 #define ENABLE_ICU1 \
35 movb $ICU_EOI,%al ; /* as soon as possible send EOI ... */ \
36 OUTB_ICU1 /* ... to clear in service bit */
37 #define OUTB_ICU1 \
38 outb %al,$IO_ICU1
39 #endif
40
41 #ifdef AUTO_EOI_2
42 /*
43 * The data sheet says no auto-EOI on slave, but it sometimes works.
44 */
45 #define ENABLE_ICU1_AND_2 ENABLE_ICU1
46 #else
47 #define ENABLE_ICU1_AND_2 \
48 movb $ICU_EOI,%al ; /* as above */ \
49 outb %al,$IO_ICU2 ; /* but do second icu first ... */ \
50 OUTB_ICU1 /* ... then first icu (if !AUTO_EOI_1) */
51 #endif
52
53 #ifdef FAST_INTR_HANDLER_USES_ES
54 #define ACTUALLY_PUSHED 1
55 #define MAYBE_MOVW_AX_ES movl %ax,%es
56 #define MAYBE_POPL_ES popl %es
57 #define MAYBE_PUSHL_ES pushl %es
58 #else
59 /*
60 * We can usually skip loading %es for fastintr handlers. %es should
61 * only be used for string instructions, and fastintr handlers shouldn't
62 * do anything slow enough to justify using a string instruction.
63 */
64 #define ACTUALLY_PUSHED 0
65 #define MAYBE_MOVW_AX_ES
66 #define MAYBE_POPL_ES
67 #define MAYBE_PUSHL_ES
68 #endif
69
70 /*
71 * Macros for interrupt interrupt entry, call to handler, and exit.
72 *
73 * XXX - the interrupt frame is set up to look like a trap frame. This is
74 * usually a waste of time. The only interrupt handlers that want a frame
75 * are the clock handler (it wants a clock frame), the npx handler (it's
76 * easier to do right all in assembler). The interrupt return routine
77 * needs a trap frame for rare AST's (it could easily convert the frame).
78 * The direct costs of setting up a trap frame are two pushl's (error
79 * code and trap number), an addl to get rid of these, and pushing and
80 * popping the call-saved regs %esi, %edi and %ebp twice, The indirect
81 * costs are making the driver interface nonuniform so unpending of
82 * interrupts is more complicated and slower (call_driver(unit) would
83 * be easier than ensuring an interrupt frame for all handlers. Finally,
84 * there are some struct copies in the npx handler and maybe in the clock
85 * handler that could be avoided by working more with pointers to frames
86 * instead of frames.
87 *
88 * XXX - should we do a cld on every system entry to avoid the requirement
89 * for scattered cld's?
90 *
91 * Coding notes for *.s:
92 *
93 * If possible, avoid operations that involve an operand size override.
94 * Word-sized operations might be smaller, but the operand size override
95 * makes them slower on on 486's and no faster on 386's unless perhaps
96 * the instruction pipeline is depleted. E.g.,
97 *
98 * Use movl to seg regs instead of the equivalent but more descriptive
99 * movw - gas generates an irelevant (slower) operand size override.
100 *
101 * Use movl to ordinary regs in preference to movw and especially
102 * in preference to movz[bw]l. Use unsigned (long) variables with the
103 * top bits clear instead of unsigned short variables to provide more
104 * opportunities for movl.
105 *
106 * If possible, use byte-sized operations. They are smaller and no slower.
107 *
108 * Use (%reg) instead of 0(%reg) - gas generates larger code for the latter.
109 *
110 * If the interrupt frame is made more flexible, INTR can push %eax first
111 * and decide the ipending case with less overhead, e.g., by avoiding
112 * loading segregs.
113 */
114
115 #define FAST_INTR(irq_num, vec_name, enable_icus) \
116 .text ; \
117 SUPERALIGN_TEXT ; \
118 IDTVEC(vec_name) ; \
119 pushl %eax ; /* save only call-used registers */ \
120 pushl %ecx ; \
121 pushl %edx ; \
122 pushl %ds ; \
123 MAYBE_PUSHL_ES ; \
124 movl $KDSEL,%eax ; \
125 movl %ax,%ds ; \
126 MAYBE_MOVW_AX_ES ; \
127 FAKE_MCOUNT((4+ACTUALLY_PUSHED)*4(%esp)) ; \
128 pushl _intr_unit + (irq_num) * 4 ; \
129 call *_intr_handler + (irq_num) * 4 ; /* do the work ASAP */ \
130 enable_icus ; /* (re)enable ASAP (helps edge trigger?) */ \
131 addl $4,%esp ; \
132 incl _cnt+V_INTR ; /* book-keeping can wait */ \
133 movl _intr_countp + (irq_num) * 4,%eax ; \
134 incl (%eax) ; \
135 movl _cpl,%eax ; /* are we unmasking pending HWIs or SWIs? */ \
136 notl %eax ; \
137 andl _ipending,%eax ; \
138 jne 2f ; /* yes, maybe handle them */ \
139 1: ; \
140 MEXITCOUNT ; \
141 MAYBE_POPL_ES ; \
142 popl %ds ; \
143 popl %edx ; \
144 popl %ecx ; \
145 popl %eax ; \
146 iret ; \
147 ; \
148 ALIGN_TEXT ; \
149 2: ; \
150 cmpb $3,_intr_nesting_level ; /* is there enough stack? */ \
151 jae 1b ; /* no, return */ \
152 movl _cpl,%eax ; \
153 /* XXX next line is probably unnecessary now. */ \
154 movl $HWI_MASK|SWI_MASK,_cpl ; /* limit nesting ... */ \
155 incb _intr_nesting_level ; /* ... really limit it ... */ \
156 sti ; /* ... to do this as early as possible */ \
157 MAYBE_POPL_ES ; /* discard most of thin frame ... */ \
158 popl %ecx ; /* ... original %ds ... */ \
159 popl %edx ; \
160 xchgl %eax,4(%esp) ; /* orig %eax; save cpl */ \
161 pushal ; /* build fat frame (grrr) ... */ \
162 pushl %ecx ; /* ... actually %ds ... */ \
163 pushl %es ; \
164 movl $KDSEL,%eax ; \
165 movl %ax,%es ; \
166 movl (2+8+0)*4(%esp),%ecx ; /* ... %ecx from thin frame ... */ \
167 movl %ecx,(2+6)*4(%esp) ; /* ... to fat frame ... */ \
168 movl (2+8+1)*4(%esp),%eax ; /* ... cpl from thin frame */ \
169 pushl %eax ; \
170 subl $4,%esp ; /* junk for unit number */ \
171 MEXITCOUNT ; \
172 jmp _doreti
173
174 #define INTR(irq_num, vec_name, icu, enable_icus, reg) \
175 .text ; \
176 SUPERALIGN_TEXT ; \
177 IDTVEC(vec_name) ; \
178 pushl $0 ; /* dummy error code */ \
179 pushl $0 ; /* dummy trap type */ \
180 pushal ; \
181 pushl %ds ; /* save our data and extra segments ... */ \
182 pushl %es ; \
183 movl $KDSEL,%eax ; /* ... and reload with kernel's own ... */ \
184 movl %ax,%ds ; /* ... early for obsolete reasons */ \
185 movl %ax,%es ; \
186 movb _imen + IRQ_BYTE(irq_num),%al ; \
187 orb $IRQ_BIT(irq_num),%al ; \
188 movb %al,_imen + IRQ_BYTE(irq_num) ; \
189 outb %al,$icu+ICU_IMR_OFFSET ; \
190 enable_icus ; \
191 movl _cpl,%eax ; \
192 testb $IRQ_BIT(irq_num),%reg ; \
193 jne 2f ; \
194 incb _intr_nesting_level ; \
195 __CONCAT(Xresume,irq_num): ; \
196 FAKE_MCOUNT(12*4(%esp)) ; /* XXX late to avoid double count */ \
197 incl _cnt+V_INTR ; /* tally interrupts */ \
198 movl _intr_countp + (irq_num) * 4,%eax ; \
199 incl (%eax) ; \
200 movl _cpl,%eax ; \
201 pushl %eax ; \
202 pushl _intr_unit + (irq_num) * 4 ; \
203 orl _intr_mask + (irq_num) * 4,%eax ; \
204 movl %eax,_cpl ; \
205 sti ; \
206 call *_intr_handler + (irq_num) * 4 ; \
207 cli ; /* must unmask _imen and icu atomically */ \
208 movb _imen + IRQ_BYTE(irq_num),%al ; \
209 andb $~IRQ_BIT(irq_num),%al ; \
210 movb %al,_imen + IRQ_BYTE(irq_num) ; \
211 outb %al,$icu+ICU_IMR_OFFSET ; \
212 sti ; /* XXX _doreti repeats the cli/sti */ \
213 MEXITCOUNT ; \
214 /* We could usually avoid the following jmp by inlining some of */ \
215 /* _doreti, but it's probably better to use less cache. */ \
216 jmp _doreti ; \
217 ; \
218 ALIGN_TEXT ; \
219 2: ; \
220 /* XXX skip mcounting here to avoid double count */ \
221 orb $IRQ_BIT(irq_num),_ipending + IRQ_BYTE(irq_num) ; \
222 popl %es ; \
223 popl %ds ; \
224 popal ; \
225 addl $4+4,%esp ; \
226 iret
227
228 MCOUNT_LABEL(bintr)
229 FAST_INTR(0,fastintr0, ENABLE_ICU1)
230 FAST_INTR(1,fastintr1, ENABLE_ICU1)
231 FAST_INTR(2,fastintr2, ENABLE_ICU1)
232 FAST_INTR(3,fastintr3, ENABLE_ICU1)
233 FAST_INTR(4,fastintr4, ENABLE_ICU1)
234 FAST_INTR(5,fastintr5, ENABLE_ICU1)
235 FAST_INTR(6,fastintr6, ENABLE_ICU1)
236 FAST_INTR(7,fastintr7, ENABLE_ICU1)
237 FAST_INTR(8,fastintr8, ENABLE_ICU1_AND_2)
238 FAST_INTR(9,fastintr9, ENABLE_ICU1_AND_2)
239 FAST_INTR(10,fastintr10, ENABLE_ICU1_AND_2)
240 FAST_INTR(11,fastintr11, ENABLE_ICU1_AND_2)
241 FAST_INTR(12,fastintr12, ENABLE_ICU1_AND_2)
242 FAST_INTR(13,fastintr13, ENABLE_ICU1_AND_2)
243 FAST_INTR(14,fastintr14, ENABLE_ICU1_AND_2)
244 FAST_INTR(15,fastintr15, ENABLE_ICU1_AND_2)
245 INTR(0,intr0, IO_ICU1, ENABLE_ICU1, al)
246 INTR(1,intr1, IO_ICU1, ENABLE_ICU1, al)
247 INTR(2,intr2, IO_ICU1, ENABLE_ICU1, al)
248 INTR(3,intr3, IO_ICU1, ENABLE_ICU1, al)
249 INTR(4,intr4, IO_ICU1, ENABLE_ICU1, al)
250 INTR(5,intr5, IO_ICU1, ENABLE_ICU1, al)
251 INTR(6,intr6, IO_ICU1, ENABLE_ICU1, al)
252 INTR(7,intr7, IO_ICU1, ENABLE_ICU1, al)
253 INTR(8,intr8, IO_ICU2, ENABLE_ICU1_AND_2, ah)
254 INTR(9,intr9, IO_ICU2, ENABLE_ICU1_AND_2, ah)
255 INTR(10,intr10, IO_ICU2, ENABLE_ICU1_AND_2, ah)
256 INTR(11,intr11, IO_ICU2, ENABLE_ICU1_AND_2, ah)
257 INTR(12,intr12, IO_ICU2, ENABLE_ICU1_AND_2, ah)
258 INTR(13,intr13, IO_ICU2, ENABLE_ICU1_AND_2, ah)
259 INTR(14,intr14, IO_ICU2, ENABLE_ICU1_AND_2, ah)
260 INTR(15,intr15, IO_ICU2, ENABLE_ICU1_AND_2, ah)
261 MCOUNT_LABEL(eintr)
262
263 .data
264 .globl _ihandlers
265 _ihandlers:
266 ihandlers: /* addresses of interrupt handlers */
267 /* actually resumption addresses for HWI's */
268 .long Xresume0, Xresume1, Xresume2, Xresume3
269 .long Xresume4, Xresume5, Xresume6, Xresume7
270 .long Xresume8, Xresume9, Xresume10, Xresume11
271 .long Xresume12, Xresume13, Xresume14, Xresume15
272 .long swi_tty, swi_net, dummycamisr, dummycamisr
273 .long _swi_vm, 0, 0, 0
274 .long 0, 0, 0, 0
275 .long 0, 0, _softclock, swi_ast
276 imasks: /* masks for interrupt handlers */
277 .space NHWI*4 /* padding; HWI masks are elsewhere */
278 .long SWI_TTY_MASK, SWI_NET_MASK, SWI_CAMNET_MASK, SWI_CAMBIO_MASK
279 .long SWI_VM_MASK, 0, 0, 0
280 .long 0, 0, 0, 0
281 .long 0, 0, SWI_CLOCK_MASK, SWI_AST_MASK
282 .globl _intr_nesting_level
283 _intr_nesting_level:
284 .byte 0
285 .space 3
286
287 /*
288 * Interrupt counters and names. The format of these and the label names
289 * must agree with what vmstat expects. The tables are indexed by device
290 * ids so that we don't have to move the names around as devices are
291 * attached.
292 */
293 #include "vector.h"
294 .globl _intrcnt, _eintrcnt
295 _intrcnt:
296 .space (NR_DEVICES + ICU_LEN) * 4
297 _eintrcnt:
298
299 .globl _intrnames, _eintrnames
300 _intrnames:
301 .ascii DEVICE_NAMES
302 .asciz "stray irq0"
303 .asciz "stray irq1"
304 .asciz "stray irq2"
305 .asciz "stray irq3"
306 .asciz "stray irq4"
307 .asciz "stray irq5"
308 .asciz "stray irq6"
309 .asciz "stray irq7"
310 .asciz "stray irq8"
311 .asciz "stray irq9"
312 .asciz "stray irq10"
313 .asciz "stray irq11"
314 .asciz "stray irq12"
315 .asciz "stray irq13"
316 .asciz "stray irq14"
317 .asciz "stray irq15"
318 _eintrnames:
319
320 .text
Cache object: dfe35507b9039e4f6fba51486ca75c0a
|