1 /*-
2 * Copyright (c) 2003 John Baldwin <jhb@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the author nor the names of any co-contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 * $FreeBSD$
30 */
31
32 /*
33 * Machine dependent interrupt code for amd64. For amd64, we have to
34 * deal with different PICs. Thus, we use the passed in vector to lookup
35 * an interrupt source associated with that vector. The interrupt source
36 * describes which PIC the source belongs to and includes methods to handle
37 * that source.
38 */
39
40 #include "opt_atpic.h"
41 #include "opt_ddb.h"
42
43 #include <sys/param.h>
44 #include <sys/bus.h>
45 #include <sys/interrupt.h>
46 #include <sys/ktr.h>
47 #include <sys/kernel.h>
48 #include <sys/lock.h>
49 #include <sys/mutex.h>
50 #include <sys/proc.h>
51 #include <sys/syslog.h>
52 #include <sys/systm.h>
53 #include <sys/sx.h>
54 #include <machine/clock.h>
55 #include <machine/intr_machdep.h>
56 #ifdef DDB
57 #include <ddb/ddb.h>
58 #endif
59
60 #ifndef DEV_ATPIC
61 #include <machine/segments.h>
62 #include <machine/frame.h>
63 #include <dev/ic/i8259.h>
64 #include <amd64/isa/icu.h>
65 #include <amd64/isa/isa.h>
66 #endif
67
68 #define MAX_STRAY_LOG 5
69
70 typedef void (*mask_fn)(void *);
71
72 static int intrcnt_index;
73 static struct intsrc *interrupt_sources[NUM_IO_INTS];
74 static struct sx intr_table_lock;
75 static struct mtx intrcnt_lock;
76 static STAILQ_HEAD(, pic) pics;
77
78 #ifdef SMP
79 static int assign_cpu;
80
81 static void intr_assign_next_cpu(struct intsrc *isrc);
82 #endif
83
84 static void intr_init(void *__dummy);
85 static int intr_pic_registered(struct pic *pic);
86 static void intrcnt_setname(const char *name, int index);
87 static void intrcnt_updatename(struct intsrc *is);
88 static void intrcnt_register(struct intsrc *is);
89
90 static int
91 intr_pic_registered(struct pic *pic)
92 {
93 struct pic *p;
94
95 STAILQ_FOREACH(p, &pics, pics) {
96 if (p == pic)
97 return (1);
98 }
99 return (0);
100 }
101
102 /*
103 * Register a new interrupt controller (PIC). This is to support suspend
104 * and resume where we suspend/resume controllers rather than individual
105 * sources. This also allows controllers with no active sources (such as
106 * 8259As in a system using the APICs) to participate in suspend and resume.
107 */
108 int
109 intr_register_pic(struct pic *pic)
110 {
111 int error;
112
113 sx_xlock(&intr_table_lock);
114 if (intr_pic_registered(pic))
115 error = EBUSY;
116 else {
117 STAILQ_INSERT_TAIL(&pics, pic, pics);
118 error = 0;
119 }
120 sx_xunlock(&intr_table_lock);
121 return (error);
122 }
123
124 /*
125 * Register a new interrupt source with the global interrupt system.
126 * The global interrupts need to be disabled when this function is
127 * called.
128 */
129 int
130 intr_register_source(struct intsrc *isrc)
131 {
132 int error, vector;
133
134 KASSERT(intr_pic_registered(isrc->is_pic), ("unregistered PIC"));
135 vector = isrc->is_pic->pic_vector(isrc);
136 if (interrupt_sources[vector] != NULL)
137 return (EEXIST);
138 error = intr_event_create(&isrc->is_event, isrc, 0,
139 (mask_fn)isrc->is_pic->pic_enable_source, "irq%d:", vector);
140 if (error)
141 return (error);
142 sx_xlock(&intr_table_lock);
143 if (interrupt_sources[vector] != NULL) {
144 sx_xunlock(&intr_table_lock);
145 intr_event_destroy(isrc->is_event);
146 return (EEXIST);
147 }
148 intrcnt_register(isrc);
149 interrupt_sources[vector] = isrc;
150 isrc->is_enabled = 0;
151 sx_xunlock(&intr_table_lock);
152 return (0);
153 }
154
155 struct intsrc *
156 intr_lookup_source(int vector)
157 {
158
159 return (interrupt_sources[vector]);
160 }
161
162 int
163 intr_add_handler(const char *name, int vector, driver_intr_t handler,
164 void *arg, enum intr_type flags, void **cookiep)
165 {
166 struct intsrc *isrc;
167 int error;
168
169 isrc = intr_lookup_source(vector);
170 if (isrc == NULL)
171 return (EINVAL);
172 error = intr_event_add_handler(isrc->is_event, name, handler, arg,
173 intr_priority(flags), flags, cookiep);
174 if (error == 0) {
175 sx_xlock(&intr_table_lock);
176 intrcnt_updatename(isrc);
177 if (!isrc->is_enabled) {
178 isrc->is_enabled = 1;
179 #ifdef SMP
180 if (assign_cpu)
181 intr_assign_next_cpu(isrc);
182 #endif
183 isrc->is_pic->pic_enable_intr(isrc);
184 }
185 isrc->is_pic->pic_enable_source(isrc);
186 sx_xunlock(&intr_table_lock);
187 }
188 return (error);
189 }
190
191 int
192 intr_remove_handler(void *cookie)
193 {
194 int error;
195
196 error = intr_event_remove_handler(cookie);
197 #ifdef XXX
198 if (error == 0)
199 intrcnt_updatename(/* XXX */);
200 #endif
201 return (error);
202 }
203
204 int
205 intr_config_intr(int vector, enum intr_trigger trig, enum intr_polarity pol)
206 {
207 struct intsrc *isrc;
208
209 isrc = intr_lookup_source(vector);
210 if (isrc == NULL)
211 return (EINVAL);
212 return (isrc->is_pic->pic_config_intr(isrc, trig, pol));
213 }
214
215 void
216 intr_execute_handlers(struct intsrc *isrc, struct intrframe *iframe)
217 {
218 struct thread *td;
219 struct intr_event *ie;
220 struct intr_handler *ih;
221 int error, vector, thread;
222
223 td = curthread;
224
225 /*
226 * We count software interrupts when we process them. The
227 * code here follows previous practice, but there's an
228 * argument for counting hardware interrupts when they're
229 * processed too.
230 */
231 (*isrc->is_count)++;
232 PCPU_LAZY_INC(cnt.v_intr);
233
234 ie = isrc->is_event;
235
236 /*
237 * XXX: We assume that IRQ 0 is only used for the ISA timer
238 * device (clk).
239 */
240 vector = isrc->is_pic->pic_vector(isrc);
241 if (vector == 0)
242 clkintr_pending = 1;
243
244 /*
245 * For stray interrupts, mask and EOI the source, bump the
246 * stray count, and log the condition.
247 */
248 if (ie == NULL || TAILQ_EMPTY(&ie->ie_handlers)) {
249 isrc->is_pic->pic_disable_source(isrc, PIC_EOI);
250 (*isrc->is_straycount)++;
251 if (*isrc->is_straycount < MAX_STRAY_LOG)
252 log(LOG_ERR, "stray irq%d\n", vector);
253 else if (*isrc->is_straycount == MAX_STRAY_LOG)
254 log(LOG_CRIT,
255 "too many stray irq %d's: not logging anymore\n",
256 vector);
257 return;
258 }
259
260 /*
261 * Execute fast interrupt handlers directly.
262 * To support clock handlers, if a handler registers
263 * with a NULL argument, then we pass it a pointer to
264 * an intrframe as its argument.
265 */
266 td->td_intr_nesting_level++;
267 thread = 0;
268 critical_enter();
269 TAILQ_FOREACH(ih, &ie->ie_handlers, ih_next) {
270 if (!(ih->ih_flags & IH_FAST)) {
271 thread = 1;
272 continue;
273 }
274 CTR4(KTR_INTR, "%s: exec %p(%p) for %s", __func__,
275 ih->ih_handler, ih->ih_argument == NULL ? iframe :
276 ih->ih_argument, ih->ih_name);
277 if (ih->ih_argument == NULL)
278 ih->ih_handler(iframe);
279 else
280 ih->ih_handler(ih->ih_argument);
281 }
282
283 /*
284 * If there are any threaded handlers that need to run,
285 * mask the source as well as sending it an EOI. Otherwise,
286 * just send it an EOI but leave it unmasked.
287 */
288 if (thread)
289 isrc->is_pic->pic_disable_source(isrc, PIC_EOI);
290 else
291 isrc->is_pic->pic_eoi_source(isrc);
292
293 /* Schedule the ithread if needed. */
294 if (thread) {
295 error = intr_event_schedule_thread(ie);
296 KASSERT(error == 0, ("bad stray interrupt"));
297 }
298 critical_exit();
299 td->td_intr_nesting_level--;
300 }
301
302 void
303 intr_resume(void)
304 {
305 struct pic *pic;
306
307 #ifndef DEV_ATPIC
308 atpic_reset();
309 #endif
310 sx_xlock(&intr_table_lock);
311 STAILQ_FOREACH(pic, &pics, pics) {
312 if (pic->pic_resume != NULL)
313 pic->pic_resume(pic);
314 }
315 sx_xunlock(&intr_table_lock);
316 }
317
318 void
319 intr_suspend(void)
320 {
321 struct pic *pic;
322
323 sx_xlock(&intr_table_lock);
324 STAILQ_FOREACH(pic, &pics, pics) {
325 if (pic->pic_suspend != NULL)
326 pic->pic_suspend(pic);
327 }
328 sx_xunlock(&intr_table_lock);
329 }
330
331 static void
332 intrcnt_setname(const char *name, int index)
333 {
334
335 snprintf(intrnames + (MAXCOMLEN + 1) * index, MAXCOMLEN + 1, "%-*s",
336 MAXCOMLEN, name);
337 }
338
339 static void
340 intrcnt_updatename(struct intsrc *is)
341 {
342
343 intrcnt_setname(is->is_event->ie_fullname, is->is_index);
344 }
345
346 static void
347 intrcnt_register(struct intsrc *is)
348 {
349 char straystr[MAXCOMLEN + 1];
350
351 KASSERT(is->is_event != NULL, ("%s: isrc with no event", __func__));
352 mtx_lock_spin(&intrcnt_lock);
353 is->is_index = intrcnt_index;
354 intrcnt_index += 2;
355 snprintf(straystr, MAXCOMLEN + 1, "stray irq%d",
356 is->is_pic->pic_vector(is));
357 intrcnt_updatename(is);
358 is->is_count = &intrcnt[is->is_index];
359 intrcnt_setname(straystr, is->is_index + 1);
360 is->is_straycount = &intrcnt[is->is_index + 1];
361 mtx_unlock_spin(&intrcnt_lock);
362 }
363
364 void
365 intrcnt_add(const char *name, u_long **countp)
366 {
367
368 mtx_lock_spin(&intrcnt_lock);
369 *countp = &intrcnt[intrcnt_index];
370 intrcnt_setname(name, intrcnt_index);
371 intrcnt_index++;
372 mtx_unlock_spin(&intrcnt_lock);
373 }
374
375 static void
376 intr_init(void *dummy __unused)
377 {
378
379 intrcnt_setname("???", 0);
380 intrcnt_index = 1;
381 STAILQ_INIT(&pics);
382 sx_init(&intr_table_lock, "intr sources");
383 mtx_init(&intrcnt_lock, "intrcnt", NULL, MTX_SPIN);
384 }
385 SYSINIT(intr_init, SI_SUB_INTR, SI_ORDER_FIRST, intr_init, NULL)
386
387 #ifndef DEV_ATPIC
388 /* Initialize the two 8259A's to a known-good shutdown state. */
389 void
390 atpic_reset(void)
391 {
392
393 outb(IO_ICU1, ICW1_RESET | ICW1_IC4);
394 outb(IO_ICU1 + ICU_IMR_OFFSET, IDT_IO_INTS);
395 outb(IO_ICU1 + ICU_IMR_OFFSET, 1 << 2);
396 outb(IO_ICU1 + ICU_IMR_OFFSET, ICW4_8086);
397 outb(IO_ICU1 + ICU_IMR_OFFSET, 0xff);
398 outb(IO_ICU1, OCW3_SEL | OCW3_RR);
399
400 outb(IO_ICU2, ICW1_RESET | ICW1_IC4);
401 outb(IO_ICU2 + ICU_IMR_OFFSET, IDT_IO_INTS + 8);
402 outb(IO_ICU2 + ICU_IMR_OFFSET, 2);
403 outb(IO_ICU2 + ICU_IMR_OFFSET, ICW4_8086);
404 outb(IO_ICU2 + ICU_IMR_OFFSET, 0xff);
405 outb(IO_ICU2, OCW3_SEL | OCW3_RR);
406 }
407 #endif
408
409 #ifdef DDB
410 /*
411 * Dump data about interrupt handlers
412 */
413 DB_SHOW_COMMAND(irqs, db_show_irqs)
414 {
415 struct intsrc **isrc;
416 int i, quit, verbose;
417
418 quit = 0;
419 if (strcmp(modif, "v") == 0)
420 verbose = 1;
421 else
422 verbose = 0;
423 isrc = interrupt_sources;
424 db_setup_paging(db_simple_pager, &quit, db_lines_per_page);
425 for (i = 0; i < NUM_IO_INTS && !quit; i++, isrc++)
426 if (*isrc != NULL)
427 db_dump_intr_event((*isrc)->is_event, verbose);
428 }
429 #endif
430
431 #ifdef SMP
432 /*
433 * Support for balancing interrupt sources across CPUs. For now we just
434 * allocate CPUs round-robin.
435 */
436
437 static u_int cpu_apic_ids[MAXCPU];
438 static int current_cpu, num_cpus;
439
440 static void
441 intr_assign_next_cpu(struct intsrc *isrc)
442 {
443 struct pic *pic;
444 u_int apic_id;
445
446 /*
447 * Assign this source to a local APIC in a round-robin fashion.
448 */
449 pic = isrc->is_pic;
450 apic_id = cpu_apic_ids[current_cpu];
451 current_cpu++;
452 if (current_cpu >= num_cpus)
453 current_cpu = 0;
454 pic->pic_assign_cpu(isrc, apic_id);
455 }
456
457 /*
458 * Add a local APIC ID to our list of valid local APIC IDs that can
459 * be destinations of interrupts.
460 */
461 void
462 intr_add_cpu(u_int apic_id)
463 {
464
465 if (bootverbose)
466 printf("INTR: Adding local APIC %d as a target\n", apic_id);
467 if (num_cpus >= MAXCPU)
468 panic("WARNING: Local APIC IDs exhausted!");
469 cpu_apic_ids[num_cpus] = apic_id;
470 num_cpus++;
471 }
472
473 /*
474 * Distribute all the interrupt sources among the available CPUs once the
475 * AP's have been launched.
476 */
477 static void
478 intr_shuffle_irqs(void *arg __unused)
479 {
480 struct intsrc *isrc;
481 int i;
482
483 /* Don't bother on UP. */
484 if (num_cpus <= 1)
485 return;
486
487 /* Round-robin assign a CPU to each enabled source. */
488 sx_xlock(&intr_table_lock);
489 assign_cpu = 1;
490 for (i = 0; i < NUM_IO_INTS; i++) {
491 isrc = interrupt_sources[i];
492 if (isrc != NULL && isrc->is_enabled)
493 intr_assign_next_cpu(isrc);
494 }
495 sx_xunlock(&intr_table_lock);
496 }
497 SYSINIT(intr_shuffle_irqs, SI_SUB_SMP, SI_ORDER_SECOND, intr_shuffle_irqs, NULL)
498 #endif
Cache object: 91e491a8c129b7748de5c24c97d0d2b3
|