xref: /openbsd-src/sys/arch/i386/i386/ioapic.c (revision f4e7063748a2ac72b2bab4389c0a7efc72d82189)
1 /*	$OpenBSD: ioapic.c,v 1.44 2023/01/30 10:49:05 jsg Exp $	*/
2 /* 	$NetBSD: ioapic.c,v 1.7 2003/07/14 22:32:40 lukem Exp $	*/
3 
4 /*-
5  * Copyright (c) 2000 The NetBSD Foundation, Inc.
6  * All rights reserved.
7  *
8  * This code is derived from software contributed to The NetBSD Foundation
9  * by RedBack Networks Inc.
10  *
11  * Author: Bill Sommerfeld
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  * 1. Redistributions of source code must retain the above copyright
17  *    notice, this list of conditions and the following disclaimer.
18  * 2. Redistributions in binary form must reproduce the above copyright
19  *    notice, this list of conditions and the following disclaimer in the
20  *    documentation and/or other materials provided with the distribution.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
23  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
24  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
25  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
26  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  */
34 
35 
36 /*
37  * Copyright (c) 1999 Stefan Grefen
38  *
39  * Redistribution and use in source and binary forms, with or without
40  * modification, are permitted provided that the following conditions
41  * are met:
42  * 1. Redistributions of source code must retain the above copyright
43  *    notice, this list of conditions and the following disclaimer.
44  * 2. Redistributions in binary form must reproduce the above copyright
45  *    notice, this list of conditions and the following disclaimer in the
46  *    documentation and/or other materials provided with the distribution.
47  * 3. All advertising materials mentioning features or use of this software
48  *    must display the following acknowledgement:
49  *      This product includes software developed by the NetBSD
50  *      Foundation, Inc. and its contributors.
51  * 4. Neither the name of The NetBSD Foundation nor the names of its
52  *    contributors may be used to endorse or promote products derived
53  *    from this software without specific prior written permission.
54  *
55  * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
56  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
57  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
58  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR AND CONTRIBUTORS BE LIABLE
59  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
60  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
61  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
62  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
63  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
64  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
65  * SUCH DAMAGE.
66  */
67 
68 #include <sys/param.h>
69 #include <sys/systm.h>
70 #include <sys/device.h>
71 #include <sys/malloc.h>
72 
73 #include <machine/bus.h>
74 #include <machine/psl.h>
75 
76 #include <machine/i82093reg.h>
77 #include <machine/i82093var.h>
78 
79 #include <machine/i82489reg.h>
80 #include <machine/i82489var.h>
81 
82 #include <machine/mpbiosvar.h>
83 
84 /*
85  * XXX locking
86  */
87 
88 int     ioapic_match(struct device *, void *, void *);
89 void    ioapic_attach(struct device *, struct device *, void *);
90 int	ioapic_activate(struct device *, int);
91 
92 /* XXX */
93 extern int bus_mem_add_mapping(bus_addr_t, bus_size_t, int,
94     bus_space_handle_t *);
95 
96 void ioapic_hwmask(struct pic *, int);
97 void ioapic_hwunmask(struct pic *, int);
98 void	apic_set_redir(struct ioapic_softc *, int);
99 void	apic_vectorset(struct ioapic_softc *, int, int, int);
100 
101 void	apic_stray(int);
102 
103 int ioapic_bsp_id = 0;
104 int ioapic_cold = 1;
105 
106 struct ioapic_softc *ioapics;	 /* head of linked list */
107 int nioapics = 0;	   	 /* number attached */
108 static int ioapic_vecbase;
109 
110 void ioapic_set_id(struct ioapic_softc *);
111 
112 static __inline u_long
ioapic_lock(struct ioapic_softc * sc)113 ioapic_lock(struct ioapic_softc *sc)
114 {
115 	u_long flags;
116 
117 	flags = intr_disable();
118 #ifdef MULTIPROCESSOR
119 	mtx_enter(&sc->sc_pic.pic_mutex);
120 #endif
121 	return flags;
122 }
123 
124 static __inline void
ioapic_unlock(struct ioapic_softc * sc,u_long flags)125 ioapic_unlock(struct ioapic_softc *sc, u_long flags)
126 {
127 #ifdef MULTIPROCESSOR
128 	mtx_leave(&sc->sc_pic.pic_mutex);
129 #endif
130 	intr_restore(flags);
131 }
132 
133 /*
134  * Register read/write routines.
135  */
136 static __inline u_int32_t
ioapic_read_ul(struct ioapic_softc * sc,int regid)137 ioapic_read_ul(struct ioapic_softc *sc,int regid)
138 {
139 	u_int32_t val;
140 
141 	*(sc->sc_reg) = regid;
142 	val = *sc->sc_data;
143 
144 	return (val);
145 }
146 
147 static __inline void
ioapic_write_ul(struct ioapic_softc * sc,int regid,u_int32_t val)148 ioapic_write_ul(struct ioapic_softc *sc,int regid, u_int32_t val)
149 {
150 	*(sc->sc_reg) = regid;
151 	*(sc->sc_data) = val;
152 }
153 
154 static __inline u_int32_t
ioapic_read(struct ioapic_softc * sc,int regid)155 ioapic_read(struct ioapic_softc *sc, int regid)
156 {
157 	u_int32_t val;
158 	u_long flags;
159 
160 	flags = ioapic_lock(sc);
161 	val = ioapic_read_ul(sc, regid);
162 	ioapic_unlock(sc, flags);
163 	return val;
164 }
165 
166 static __inline void
ioapic_write(struct ioapic_softc * sc,int regid,int val)167 ioapic_write(struct ioapic_softc *sc,int regid, int val)
168 {
169 	u_long flags;
170 
171 	flags = ioapic_lock(sc);
172 	ioapic_write_ul(sc, regid, val);
173 	ioapic_unlock(sc, flags);
174 }
175 
176 struct ioapic_softc *
ioapic_find(int apicid)177 ioapic_find(int apicid)
178 {
179 	struct ioapic_softc *sc;
180 
181 	if (apicid == MPS_ALL_APICS) {	/* XXX mpbios-specific */
182 		/*
183 		 * XXX kludge for all-ioapics interrupt support
184 		 * on single ioapic systems
185 		 */
186 		if (nioapics <= 1)
187 			return (ioapics);
188 		panic("unsupported: all-ioapics interrupt with >1 ioapic");
189 	}
190 
191 	for (sc = ioapics; sc != NULL; sc = sc->sc_next)
192 		if (sc->sc_apicid == apicid)
193 			return (sc);
194 
195 	return (NULL);
196 }
197 
198 /*
199  * For the case the I/O APICs were configured using ACPI, there must
200  * be an option to match global ACPI interrupts with APICs.
201  */
202 struct ioapic_softc *
ioapic_find_bybase(int vec)203 ioapic_find_bybase(int vec)
204 {
205 	struct ioapic_softc *sc;
206 
207 	for (sc = ioapics; sc != NULL; sc = sc->sc_next) {
208 		if (vec >= sc->sc_apic_vecbase &&
209 		    vec < (sc->sc_apic_vecbase + sc->sc_apic_sz))
210 			return sc;
211 	}
212 
213 	return NULL;
214 }
215 
216 static __inline void
ioapic_add(struct ioapic_softc * sc)217 ioapic_add(struct ioapic_softc *sc)
218 {
219 	sc->sc_next = ioapics;
220 	ioapics = sc;
221 	nioapics++;
222 }
223 
224 void
ioapic_print_redir(struct ioapic_softc * sc,char * why,int pin)225 ioapic_print_redir(struct ioapic_softc *sc, char *why, int pin)
226 {
227 	u_int32_t redirlo = ioapic_read(sc, IOAPIC_REDLO(pin));
228 	u_int32_t redirhi = ioapic_read(sc, IOAPIC_REDHI(pin));
229 
230 	apic_format_redir(sc->sc_pic.pic_name, why, pin, redirhi, redirlo);
231 }
232 
233 const struct cfattach ioapic_ca = {
234 	sizeof(struct ioapic_softc), ioapic_match, ioapic_attach, NULL,
235 	ioapic_activate
236 };
237 
238 struct cfdriver ioapic_cd = {
239 	NULL, "ioapic", DV_DULL
240 };
241 
242 int
ioapic_match(struct device * parent,void * v,void * aux)243 ioapic_match(struct device *parent, void *v, void *aux)
244 {
245 	struct apic_attach_args *aaa = (struct apic_attach_args *)aux;
246 	struct cfdata *match = v;
247 
248 	if (strcmp(aaa->aaa_name, match->cf_driver->cd_name) == 0)
249 		return (1);
250 	return (0);
251 }
252 
253 /* Reprogram the APIC ID, and check that it actually got set. */
254 void
ioapic_set_id(struct ioapic_softc * sc)255 ioapic_set_id(struct ioapic_softc *sc)
256 {
257 	u_int8_t apic_id;
258 
259 	ioapic_write(sc, IOAPIC_ID,
260 	    (ioapic_read(sc, IOAPIC_ID) & ~IOAPIC_ID_MASK) |
261 	    (sc->sc_apicid << IOAPIC_ID_SHIFT));
262 
263 	apic_id = (ioapic_read(sc, IOAPIC_ID) & IOAPIC_ID_MASK) >>
264 	    IOAPIC_ID_SHIFT;
265 
266 	if (apic_id != sc->sc_apicid)
267 		printf(", can't remap");
268 	else
269 		printf(", remapped");
270 }
271 
272 /*
273  * can't use bus_space_xxx as we don't have a bus handle ...
274  */
275 void
ioapic_attach(struct device * parent,struct device * self,void * aux)276 ioapic_attach(struct device *parent, struct device *self, void *aux)
277 {
278 	struct ioapic_softc *sc = (struct ioapic_softc *)self;
279 	struct apic_attach_args  *aaa = (struct apic_attach_args *)aux;
280 	int apic_id;
281 	bus_space_handle_t bh;
282 	u_int32_t ver_sz;
283 	int i;
284 
285 	sc->sc_flags = aaa->flags;
286 	sc->sc_apicid = aaa->apic_id;
287 
288 	printf(": apid %d", aaa->apic_id);
289 
290 	if (ioapic_find(aaa->apic_id) != NULL) {
291 		printf(", duplicate apic id (ignored)\n");
292 		return;
293 	}
294 
295 	ioapic_add(sc);
296 
297 	printf(" pa 0x%x", aaa->apic_address);
298 
299 	if (bus_mem_add_mapping(aaa->apic_address, PAGE_SIZE, 0, &bh) != 0) {
300 		printf(", map failed\n");
301 		return;
302 	}
303 	sc->sc_reg = (volatile u_int32_t *)(bh + IOAPIC_REG);
304 	sc->sc_data = (volatile u_int32_t *)(bh + IOAPIC_DATA);
305 
306 #ifdef MULTIPROCESSOR
307 	mtx_init(&sc->sc_pic.pic_mutex, IPL_NONE);
308 #endif
309 
310 	ver_sz = ioapic_read(sc, IOAPIC_VER);
311 	sc->sc_apic_vers = (ver_sz & IOAPIC_VER_MASK) >> IOAPIC_VER_SHIFT;
312 	sc->sc_apic_sz = (ver_sz & IOAPIC_MAX_MASK) >> IOAPIC_MAX_SHIFT;
313 	sc->sc_apic_sz++;
314 
315 	if (aaa->apic_vecbase != -1)
316 		sc->sc_apic_vecbase = aaa->apic_vecbase;
317 	else {
318 		/*
319 		 * XXX this assumes ordering of ioapics in the table.
320 		 * Only needed for broken BIOS workaround (see mpbios.c)
321 		 */
322 		sc->sc_apic_vecbase = ioapic_vecbase;
323 		ioapic_vecbase += sc->sc_apic_sz;
324 	}
325 
326 	if (mp_verbose) {
327 		printf(", %s mode",
328 		    aaa->flags & IOAPIC_PICMODE ? "PIC" : "virtual wire");
329 	}
330 
331 	printf(", version %x, %d pins", sc->sc_apic_vers, sc->sc_apic_sz);
332 
333 	apic_id = (ioapic_read(sc, IOAPIC_ID) & IOAPIC_ID_MASK) >>
334 	    IOAPIC_ID_SHIFT;
335 
336 	sc->sc_pins = mallocarray(sc->sc_apic_sz, sizeof(struct ioapic_pin),
337 	    M_DEVBUF, M_WAITOK);
338 
339 	for (i = 0; i < sc->sc_apic_sz; i++) {
340 		sc->sc_pins[i].ip_handler = NULL;
341 		sc->sc_pins[i].ip_next = NULL;
342 		sc->sc_pins[i].ip_map = NULL;
343 		sc->sc_pins[i].ip_vector = 0;
344 		sc->sc_pins[i].ip_type = 0;
345 		sc->sc_pins[i].ip_minlevel = 0xff; /* XXX magic*/
346 		sc->sc_pins[i].ip_maxlevel = 0; /* XXX magic */
347 	}
348 
349 	/*
350 	 * In case the APIC is not initialized to the correct ID
351 	 * do it now.
352 	 */
353 	if (apic_id != sc->sc_apicid) {
354 		if (mp_verbose)
355 			printf("\n%s: misconfigured as apic %d",
356 			    sc->sc_pic.pic_name, apic_id);
357 		ioapic_set_id(sc);
358 	}
359 
360 	printf("\n");
361 
362 #if 0
363 	/* output of this was boring. */
364 	if (mp_verbose)
365 		for (i = 0; i < sc->sc_apic_sz; i++)
366 			ioapic_print_redir(sc, "boot", i);
367 #endif
368 }
369 
370 int
ioapic_activate(struct device * self,int act)371 ioapic_activate(struct device *self, int act)
372 {
373 	struct ioapic_softc *sc = (struct ioapic_softc *)self;
374 
375 	switch (act) {
376 	case DVACT_RESUME:
377 		/* On resume, reset the APIC id, like we do on boot */
378 		ioapic_write(sc, IOAPIC_ID,
379 		    (ioapic_read(sc, IOAPIC_ID) & ~IOAPIC_ID_MASK) |
380 		    (sc->sc_apicid << IOAPIC_ID_SHIFT));
381 	}
382 
383 	return (0);
384 }
385 
386 /*
387  * Interrupt mapping.
388  *
389  * Multiple handlers may exist for each pin, so there's an
390  * intrhand chain for each pin.
391  *
392  * Ideally, each pin maps to a single vector at the priority of the
393  * highest level interrupt for that pin.
394  *
395  * XXX in the event that there are more than 16 interrupt sources at a
396  * single level, some doubling-up may be needed.  This is not yet
397  * implemented.
398  *
399  * XXX we are wasting some space here because we only use a limited
400  * range of the vectors here.  (0x30..0xef)
401  */
402 
403 struct intrhand *apic_intrhand[256];
404 int	apic_maxlevel[256];
405 
406 
407 /* XXX should check vs. softc max int number */
408 #define	LEGAL_IRQ(x)	((x) >= 0 && (x) < APIC_ICU_LEN && (x) != 2)
409 
410 void
apic_set_redir(struct ioapic_softc * sc,int pin)411 apic_set_redir(struct ioapic_softc *sc, int pin)
412 {
413 	u_int32_t redlo;
414 	u_int32_t redhi = 0;
415 	int delmode;
416 
417 	struct ioapic_pin *pp;
418 	struct mp_intr_map *map;
419 
420 	pp = &sc->sc_pins[pin];
421 	map = pp->ip_map;
422 	redlo = (map == NULL) ? IOAPIC_REDLO_MASK : map->redir;
423 	delmode = (redlo & IOAPIC_REDLO_DEL_MASK) >> IOAPIC_REDLO_DEL_SHIFT;
424 
425 	/* XXX magic numbers */
426 	if ((delmode != 0) && (delmode != 1))
427 		;
428 	else if (pp->ip_handler == NULL) {
429 		redlo |= IOAPIC_REDLO_MASK;
430 	} else {
431 		redlo |= (pp->ip_vector & 0xff);
432 		redlo &= ~IOAPIC_REDLO_DEL_MASK;
433 		redlo |= (IOAPIC_REDLO_DEL_FIXED << IOAPIC_REDLO_DEL_SHIFT);
434 		redlo &= ~IOAPIC_REDLO_DSTMOD;
435 
436 		/*
437 		 * Destination: BSP CPU
438 		 *
439 		 * XXX will want to distribute interrupts across cpu's
440 		 * eventually.  most likely, we'll want to vector each
441 		 * interrupt to a specific CPU and load-balance across
442 		 * cpu's.  but there's no point in doing that until after
443 		 * most interrupts run without the kernel lock.
444 		 */
445 		redhi |= (ioapic_bsp_id << IOAPIC_REDHI_DEST_SHIFT);
446 
447 		/* XXX derive this bit from BIOS info */
448 		if (pp->ip_type == IST_LEVEL)
449 			redlo |= IOAPIC_REDLO_LEVEL;
450 		else
451 			redlo &= ~IOAPIC_REDLO_LEVEL;
452 		if (map != NULL && ((map->flags & 3) == MPS_INTPO_DEF)) {
453 			if (pp->ip_type == IST_LEVEL)
454 				redlo |= IOAPIC_REDLO_ACTLO;
455 			else
456 				redlo &= ~IOAPIC_REDLO_ACTLO;
457 		}
458 	}
459 	/* Do atomic write */
460 	ioapic_write(sc, IOAPIC_REDLO(pin), IOAPIC_REDLO_MASK);
461 	ioapic_write(sc, IOAPIC_REDHI(pin), redhi);
462 	ioapic_write(sc, IOAPIC_REDLO(pin), redlo);
463 	if (mp_verbose)
464 		ioapic_print_redir(sc, "int", pin);
465 }
466 
467 /*
468  * XXX To be really correct an NISA > 0 condition should check for these.
469  * However, the i386 port pretty much assumes isa is there anyway.
470  * For example, pci_intr_establish calls isa_intr_establish unconditionally.
471  */
472 extern int fakeintr(void *); 	/* XXX headerify */
473 extern char *isa_intr_typename(int); 	/* XXX headerify */
474 
475 /*
476  * apic_vectorset: allocate a vector for the given pin, based on
477  * the levels of the interrupts on that pin.
478  *
479  * XXX if the level of the pin changes while the pin is
480  * masked, need to do something special to prevent pending
481  * interrupts from being lost.
482  * (the answer may be to hang the interrupt chain off of both vectors
483  * until any interrupts from the old source have been handled.  the trouble
484  * is that we don't have a global view of what interrupts are pending.
485  *
486  * Deferring for now since MP systems are more likely servers rather
487  * than laptops or desktops, and thus will have relatively static
488  * interrupt configuration.
489  */
490 
491 void
apic_vectorset(struct ioapic_softc * sc,int pin,int minlevel,int maxlevel)492 apic_vectorset(struct ioapic_softc *sc, int pin, int minlevel, int maxlevel)
493 {
494 	struct ioapic_pin *pp = &sc->sc_pins[pin];
495 	int nvector, ovector = pp->ip_vector;
496 
497 	if (maxlevel == 0) {
498 		/* no vector needed. */
499 		pp->ip_minlevel = 0xff; /* XXX magic */
500 		pp->ip_maxlevel = 0; /* XXX magic */
501 		pp->ip_vector = 0;
502 	} else if (minlevel != pp->ip_minlevel) {
503 #ifdef MPVERBOSE
504 		if (minlevel != maxlevel)
505 			printf("%s: pin %d shares different IPL interrupts "
506 			    "(%x..%x)\n", sc->sc_pic.pic_name, pin,
507 			    minlevel, maxlevel);
508 #endif
509 
510 		/*
511 		 * Allocate interrupt vector at the *lowest* priority level
512 		 * of any of the handlers invoked by this pin.
513 		 *
514 		 * The interrupt handler will raise ipl higher than this
515 		 * as appropriate.
516 		 */
517 		nvector = idt_vec_alloc(minlevel, minlevel+15);
518 		if (nvector == 0) {
519 			/*
520 			 * XXX XXX we should be able to deal here..
521 			 * need to double-up an existing vector
522 			 * and install a slightly different handler.
523 			 */
524 			panic("%s: can't alloc vector for pin %d at level %x",
525 			    sc->sc_pic.pic_name, pin, maxlevel);
526 		}
527 
528 		idt_vec_set(nvector, apichandler[nvector & 0xf]);
529 		pp->ip_minlevel = minlevel;
530 		pp->ip_vector = nvector;
531 	}
532 
533 	pp->ip_maxlevel = maxlevel;
534 	apic_maxlevel[pp->ip_vector] = maxlevel;
535 	apic_intrhand[pp->ip_vector] = pp->ip_handler;
536 
537 	if (ovector && ovector != pp->ip_vector) {
538 		/*
539 		 * XXX should defer this until we're sure the old vector
540 		 * doesn't have a pending interrupt on any processor.
541 		 * do this by setting a counter equal to the number of CPU's,
542 		 * and firing off a low-priority broadcast IPI to all cpu's.
543 		 * each cpu then decrements the counter; when it
544 		 * goes to zero, free the vector..
545 		 * i.e., defer until all processors have run with a CPL
546 		 * less than the level of the interrupt..
547 		 *
548 		 * this is only an issue for dynamic interrupt configuration
549 		 * (e.g., cardbus or pcmcia).
550 		 */
551 		apic_intrhand[ovector] = NULL;
552 		idt_vec_free(ovector);
553 	}
554 
555 	apic_set_redir(sc, pin);
556 }
557 
558 /*
559  * Throw the switch and enable interrupts..
560  */
561 
562 void
ioapic_enable(void)563 ioapic_enable(void)
564 {
565 	int p, maxlevel, minlevel;
566 	struct ioapic_softc *sc;
567 	struct intrhand *q;
568 	extern void intr_calculatemasks(void); /* XXX */
569 
570 	intr_calculatemasks();	/* for softints, AST's */
571 
572 	ioapic_cold = 0;
573 
574 	if (ioapics == NULL)
575 		return;
576 
577 #if 1 /* XXX Will probably get removed */
578 	lapic_set_softvectors();
579 	lapic_set_lvt();
580 #endif
581 
582 	if (ioapics->sc_flags & IOAPIC_PICMODE) {
583 		printf("%s: writing to IMCR to disable pics\n",
584 		    ioapics->sc_pic.pic_name);
585 		outb(IMCR_ADDR, IMCR_REGISTER);
586 		outb(IMCR_DATA, IMCR_APIC);
587 	}
588 
589 	for (sc = ioapics; sc != NULL; sc = sc->sc_next) {
590 		if (mp_verbose)
591 			printf("%s: enabling\n", sc->sc_pic.pic_name);
592 
593 		for (p = 0; p < sc->sc_apic_sz; p++) {
594 			maxlevel = 0;	 /* magic */
595 			minlevel = 0xff; /* magic */
596 
597 			for (q = sc->sc_pins[p].ip_handler; q != NULL;
598 			     q = q->ih_next) {
599 				if (q->ih_level > maxlevel)
600 					maxlevel = q->ih_level;
601 				if (q->ih_level < minlevel)
602 					minlevel = q->ih_level;
603 			}
604 			apic_vectorset(sc, p, minlevel, maxlevel);
605 		}
606 	}
607 }
608 
609 void
ioapic_hwmask(struct pic * pic,int pin)610 ioapic_hwmask(struct pic *pic, int pin)
611 {
612 	u_int32_t redlo;
613 	struct ioapic_softc *sc = (struct ioapic_softc *)pic;
614 	u_long flags;
615 
616 	if (ioapic_cold)
617 		return;
618 	flags = ioapic_lock(sc);
619 	redlo = ioapic_read_ul(sc, IOAPIC_REDLO(pin));
620 	redlo |= IOAPIC_REDLO_MASK;
621 	ioapic_write_ul(sc, IOAPIC_REDLO(pin), redlo);
622 	ioapic_unlock(sc, flags);
623 }
624 
625 void
ioapic_hwunmask(struct pic * pic,int pin)626 ioapic_hwunmask(struct pic *pic, int pin)
627 {
628 	u_int32_t redlo;
629 	struct ioapic_softc *sc = (struct ioapic_softc *)pic;
630 	u_long flags;
631 
632 	if (ioapic_cold)
633 		return;
634 	flags = ioapic_lock(sc);
635 	redlo = ioapic_read_ul(sc, IOAPIC_REDLO(pin));
636 	redlo &= ~IOAPIC_REDLO_MASK;
637 	ioapic_write_ul(sc, IOAPIC_REDLO(pin), redlo);
638 	ioapic_unlock(sc, flags);
639 }
640 
641 /*
642  * Interrupt handler management with the apic is radically different from the
643  * good old 8259.
644  *
645  * The APIC adds an additional level of indirection between interrupt
646  * signals and interrupt vectors in the IDT.
647  * It also encodes a priority into the high-order 4 bits of the IDT vector
648  * number.
649  *
650  *
651  * interrupt establishment:
652  *	-> locate interrupt pin.
653  *	-> locate or allocate vector for pin.
654  *	-> locate or allocate handler chain for vector.
655  *	-> chain interrupt into handler chain.
656  * 	#ifdef notyet
657  *	-> if level of handler chain increases, reallocate vector, move chain.
658  *	#endif
659  */
660 
661 void *
apic_intr_establish(int irq,int type,int level,int (* ih_fun)(void *),void * ih_arg,const char * ih_what)662 apic_intr_establish(int irq, int type, int level, int (*ih_fun)(void *),
663     void *ih_arg, const char *ih_what)
664 {
665 	unsigned int ioapic = APIC_IRQ_APIC(irq);
666 	unsigned int intr = APIC_IRQ_PIN(irq);
667 	struct ioapic_softc *sc = ioapic_find(ioapic);
668 	struct ioapic_pin *pin;
669 	struct intrhand **p, *q, *ih;
670 	extern int cold;
671 	int minlevel, maxlevel;
672 	extern void intr_calculatemasks(void); /* XXX */
673 	int flags;
674 
675 	flags = level & IPL_MPSAFE;
676 	level &= ~IPL_MPSAFE;
677 
678 	KASSERT(level <= IPL_TTY || flags & IPL_MPSAFE);
679 
680 	if (sc == NULL)
681 		panic("apic_intr_establish: unknown ioapic %d", ioapic);
682 
683 	if ((irq & APIC_INT_VIA_APIC) == 0)
684 		panic("apic_intr_establish of non-apic interrupt 0x%x", irq);
685 
686 	if (intr >= sc->sc_apic_sz || type == IST_NONE)
687 		panic("apic_intr_establish: bogus intr or type");
688 
689 	/* no point in sleeping unless someone can free memory. */
690 	ih = malloc(sizeof *ih, M_DEVBUF, cold ? M_NOWAIT : M_WAITOK);
691 	if (ih == NULL)
692 		panic("apic_intr_establish: can't malloc handler info");
693 
694 	pin = &sc->sc_pins[intr];
695 	switch (pin->ip_type) {
696 	case IST_NONE:
697 		pin->ip_type = type;
698 		break;
699 	case IST_EDGE:
700 		intr_shared_edge = 1;
701 		/* FALLTHROUGH */
702 	case IST_LEVEL:
703 		if (type == pin->ip_type)
704 			break;
705 	case IST_PULSE:
706 		if (type != IST_NONE) {
707 			/*printf("%s: intr_establish: can't share %s with %s, irq %d\n",
708 			    ih_what, isa_intr_typename(pin->ip_type),
709 			    isa_intr_typename(type), intr);*/
710 			free(ih, M_DEVBUF, sizeof(*ih));
711 			return (NULL);
712 		}
713 		break;
714 	}
715 
716 	if (!cold)
717 		ioapic_hwmask(&sc->sc_pic, intr);
718 
719 	/*
720 	 * Figure out where to put the handler.
721 	 * This is O(N^2) to establish N interrupts, but we want to
722 	 * preserve the order, and N is generally small.
723 	 */
724 	maxlevel = level;
725 	minlevel = level;
726 	for (p = &pin->ip_handler; (q = *p) != NULL; p = &q->ih_next) {
727 		if (q->ih_level > maxlevel)
728 			maxlevel = q->ih_level;
729 		if (q->ih_level < minlevel)
730 			minlevel = q->ih_level;
731 	}
732 
733 	ih->ih_fun = ih_fun;
734 	ih->ih_arg = ih_arg;
735 	ih->ih_next = NULL;
736 	ih->ih_level = level;
737 	ih->ih_flags = flags;
738 	ih->ih_irq = irq;
739 	evcount_attach(&ih->ih_count, ih_what, &pin->ip_vector);
740 
741 	*p = ih;
742 
743 	intr_calculatemasks();
744 
745 	/*
746 	 * Fix up the vector for this pin.
747 	 * (if cold, defer this until most interrupts have been established,
748 	 * to avoid too much thrashing of the idt..)
749 	 */
750 
751 	if (!ioapic_cold)
752 		apic_vectorset(sc, intr, minlevel, maxlevel);
753 
754 	if (!cold)
755 		ioapic_hwunmask(&sc->sc_pic, intr);
756 
757 	return (ih);
758 }
759 
760 /*
761  * apic disestablish:
762  *	locate handler chain.
763  * 	dechain intrhand from handler chain
764  *	if chain empty {
765  *		reprogram apic for "safe" vector.
766  *		free vector (point at stray handler).
767  *	}
768  *	#ifdef notyet
769  *	else {
770  *		recompute level for current chain.
771  *		if changed, reallocate vector, move chain.
772  *	}
773  *	#endif
774  */
775 
776 void
apic_intr_disestablish(void * arg)777 apic_intr_disestablish(void *arg)
778 {
779 	struct intrhand *ih = arg;
780 	int irq = ih->ih_irq;
781 	unsigned int ioapic = APIC_IRQ_APIC(irq);
782 	unsigned int intr = APIC_IRQ_PIN(irq);
783 	struct ioapic_softc *sc = ioapic_find(ioapic);
784 	struct ioapic_pin *pin;
785 	struct intrhand **p, *q;
786 	int minlevel, maxlevel;
787 	extern void intr_calculatemasks(void); /* XXX */
788 
789 	if (sc == NULL)
790 		panic("apic_intr_disestablish: unknown ioapic %d", ioapic);
791 	pin = &sc->sc_pins[intr];
792 
793 	if (intr >= sc->sc_apic_sz)
794 		panic("apic_intr_disestablish: bogus irq");
795 
796 	/*
797 	 * Remove the handler from the chain.
798 	 * This is O(n^2), too.
799 	 */
800 	maxlevel = 0;
801 	minlevel = 0xff;
802 	for (p = &pin->ip_handler; (q = *p) != NULL && q != ih;
803 	     p = &q->ih_next) {
804 		if (q->ih_level > maxlevel)
805 			maxlevel = q->ih_level;
806 		if (q->ih_level < minlevel)
807 			minlevel = q->ih_level;
808 	}
809 
810 	if (q)
811 		*p = q->ih_next;
812 	else
813 		panic("intr_disestablish: handler not registered");
814 	for (q = *p; q != NULL; q = q->ih_next) {
815 		if (q->ih_level > maxlevel)
816 			maxlevel = q->ih_level;
817 		if (q->ih_level < minlevel)
818 			minlevel = q->ih_level;
819 	}
820 
821 	intr_calculatemasks();
822 	if (!ioapic_cold)
823 		apic_vectorset(sc, intr, minlevel, maxlevel);
824 
825 	evcount_detach(&ih->ih_count);
826 	free(ih, M_DEVBUF, sizeof(*ih));
827 }
828 
829 void
apic_stray(int irqnum)830 apic_stray(int irqnum)
831 {
832 	unsigned int apicid;
833 	struct ioapic_softc *sc;
834 
835 	apicid = APIC_IRQ_APIC(irqnum);
836 	sc = ioapic_find(apicid);
837 	if (sc == NULL)
838 		return;
839 	printf("%s: stray interrupt %d\n", sc->sc_pic.pic_name, irqnum);
840 }
841 
842 #ifdef DDB
843 void ioapic_dump(void);
844 
845 void
ioapic_dump(void)846 ioapic_dump(void)
847 {
848 	struct ioapic_softc *sc;
849 	struct ioapic_pin *ip;
850 	int p;
851 
852 	for (sc = ioapics; sc != NULL; sc = sc->sc_next) {
853 		for (p = 0; p < sc->sc_apic_sz; p++) {
854 			ip = &sc->sc_pins[p];
855 			if (ip->ip_type != IST_NONE)
856 				ioapic_print_redir(sc, "dump", p);
857 		}
858 	}
859 }
860 #endif
861