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