xref: /dflybsd-src/sys/platform/pc64/apic/ioapic.c (revision 0eb2eccd5a86ef7dd7492d2651de55c3589f23d7)
1 /*
2  * Copyright (c) 1996, by Steve Passe
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. The name of the developer may NOT be used to endorse or promote products
11  *    derived from this software without specific prior written permission.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  *
25  * $FreeBSD: src/sys/i386/i386/mpapic.c,v 1.37.2.7 2003/01/25 02:31:47 peter Exp $
26  */
27 
28 #include <sys/param.h>
29 #include <sys/systm.h>
30 #include <sys/kernel.h>
31 #include <sys/bus.h>
32 #include <sys/machintr.h>
33 #include <machine/globaldata.h>
34 #include <machine/smp.h>
35 #include <machine/md_var.h>
36 #include <machine/pmap.h>
37 #include <machine_base/apic/lapic.h>
38 #include <machine_base/apic/ioapic.h>
39 #include <machine_base/apic/ioapic_abi.h>
40 #include <machine/segments.h>
41 #include <sys/thread2.h>
42 
43 #include <machine/intr_machdep.h>
44 
45 #include "apicvar.h"
46 
47 #define IOAPIC_COUNT_MAX	16
48 #define IOAPIC_ID_MASK		(IOAPIC_COUNT_MAX - 1)
49 
50 struct ioapic_info {
51 	int		io_idx;
52 	int		io_apic_id;
53 	void		*io_addr;
54 	int		io_npin;
55 	int		io_gsi_base;
56 
57 	TAILQ_ENTRY(ioapic_info) io_link;
58 };
59 TAILQ_HEAD(ioapic_info_list, ioapic_info);
60 
61 struct ioapic_intsrc {
62 	int		int_gsi;
63 	enum intr_trigger int_trig;
64 	enum intr_polarity int_pola;
65 };
66 
67 struct ioapic_conf {
68 	struct ioapic_info_list ioc_list;
69 	struct ioapic_intsrc ioc_intsrc[16];	/* XXX magic number */
70 };
71 
72 static void	ioapic_setup(const struct ioapic_info *);
73 static int	ioapic_alloc_apic_id(int);
74 static void	ioapic_set_apic_id(const struct ioapic_info *);
75 static void	ioapic_gsi_setup(int);
76 static const struct ioapic_info *
77 		ioapic_gsi_search(int);
78 static void	ioapic_pin_prog(void *, int, int,
79 		    enum intr_trigger, enum intr_polarity, uint32_t);
80 
81 static struct ioapic_conf	ioapic_conf;
82 
83 static TAILQ_HEAD(, ioapic_enumerator) ioapic_enumerators =
84 	TAILQ_HEAD_INITIALIZER(ioapic_enumerators);
85 
86 void
87 ioapic_config(void)
88 {
89 	struct ioapic_enumerator *e;
90 	struct ioapic_info *info;
91 	int start_apic_id = 0;
92 	int error, i;
93 	register_t ef = 0;
94 
95 	TAILQ_INIT(&ioapic_conf.ioc_list);
96 	/* XXX magic number */
97 	for (i = 0; i < 16; ++i)
98 		ioapic_conf.ioc_intsrc[i].int_gsi = -1;
99 
100 	TAILQ_FOREACH(e, &ioapic_enumerators, ioapic_link) {
101 		error = e->ioapic_probe(e);
102 		if (!error)
103 			break;
104 	}
105 	if (e == NULL) {
106 #ifdef notyet
107 		panic("can't config I/O APIC\n");
108 #else
109 		kprintf("no I/O APIC\n");
110 		return;
111 #endif
112 	}
113 
114 	crit_enter();
115 
116 	ef = read_rflags();
117 	cpu_disable_intr();
118 
119 	/*
120 	 * Switch to I/O APIC MachIntrABI and reconfigure
121 	 * the default IDT entries.
122 	 */
123 	MachIntrABI = MachIntrABI_IOAPIC;
124 	MachIntrABI.setdefault();
125 
126 	e->ioapic_enumerate(e);
127 
128 	/*
129 	 * Setup index
130 	 */
131 	i = 0;
132 	TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link)
133 		info->io_idx = i++;
134 
135 	if (i > IOAPIC_COUNT_MAX) /* XXX magic number */
136 		panic("ioapic_config: more than 16 I/O APIC\n");
137 
138 	/*
139 	 * Setup APIC ID
140 	 */
141 	TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link) {
142 		int apic_id;
143 
144 		apic_id = ioapic_alloc_apic_id(start_apic_id);
145 		if (apic_id == NAPICID) {
146 			kprintf("IOAPIC: can't alloc APIC ID for "
147 				"%dth I/O APIC\n", info->io_idx);
148 			break;
149 		}
150 		info->io_apic_id = apic_id;
151 
152 		start_apic_id = apic_id + 1;
153 	}
154 	if (info != NULL) {
155 		/*
156 		 * xAPIC allows I/O APIC's APIC ID to be same
157 		 * as the LAPIC's APIC ID
158 		 */
159 		kprintf("IOAPIC: use xAPIC model to alloc APIC ID "
160 			"for I/O APIC\n");
161 
162 		TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link)
163 			info->io_apic_id = info->io_idx;
164 	}
165 
166 	/*
167 	 * Warning about any GSI holes
168 	 */
169 	TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link) {
170 		const struct ioapic_info *prev_info;
171 
172 		prev_info = TAILQ_PREV(info, ioapic_info_list, io_link);
173 		if (prev_info != NULL) {
174 			if (info->io_gsi_base !=
175 			prev_info->io_gsi_base + prev_info->io_npin) {
176 				kprintf("IOAPIC: warning gsi hole "
177 					"[%d, %d]\n",
178 					prev_info->io_gsi_base +
179 					prev_info->io_npin,
180 					info->io_gsi_base - 1);
181 			}
182 		}
183 	}
184 
185 	if (bootverbose) {
186 		TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link) {
187 			kprintf("IOAPIC: idx %d, apic id %d, "
188 				"gsi base %d, npin %d\n",
189 				info->io_idx,
190 				info->io_apic_id,
191 				info->io_gsi_base,
192 				info->io_npin);
193 		}
194 	}
195 
196 	/*
197 	 * Setup all I/O APIC
198 	 */
199 	TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link)
200 		ioapic_setup(info);
201 	ioapic_abi_fixup_irqmap();
202 
203 	write_rflags(ef);
204 
205 	MachIntrABI.cleanup();
206 
207 	crit_exit();
208 }
209 
210 void
211 ioapic_enumerator_register(struct ioapic_enumerator *ne)
212 {
213 	struct ioapic_enumerator *e;
214 
215 	TAILQ_FOREACH(e, &ioapic_enumerators, ioapic_link) {
216 		if (e->ioapic_prio < ne->ioapic_prio) {
217 			TAILQ_INSERT_BEFORE(e, ne, ioapic_link);
218 			return;
219 		}
220 	}
221 	TAILQ_INSERT_TAIL(&ioapic_enumerators, ne, ioapic_link);
222 }
223 
224 void
225 ioapic_add(void *addr, int gsi_base, int npin)
226 {
227 	struct ioapic_info *info, *ninfo;
228 	int gsi_end;
229 
230 	gsi_end = gsi_base + npin - 1;
231 	TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link) {
232 		if ((gsi_base >= info->io_gsi_base &&
233 		     gsi_base < info->io_gsi_base + info->io_npin) ||
234 		    (gsi_end >= info->io_gsi_base &&
235 		     gsi_end < info->io_gsi_base + info->io_npin)) {
236 			panic("ioapic_add: overlapped gsi, base %d npin %d, "
237 			      "hit base %d, npin %d\n", gsi_base, npin,
238 			      info->io_gsi_base, info->io_npin);
239 		}
240 		if (info->io_addr == addr)
241 			panic("ioapic_add: duplicated addr %p\n", addr);
242 	}
243 
244 	ninfo = kmalloc(sizeof(*ninfo), M_DEVBUF, M_WAITOK | M_ZERO);
245 	ninfo->io_addr = addr;
246 	ninfo->io_npin = npin;
247 	ninfo->io_gsi_base = gsi_base;
248 	ninfo->io_apic_id = -1;
249 
250 	/*
251 	 * Create IOAPIC list in ascending order of GSI base
252 	 */
253 	TAILQ_FOREACH_REVERSE(info, &ioapic_conf.ioc_list,
254 	    ioapic_info_list, io_link) {
255 		if (ninfo->io_gsi_base > info->io_gsi_base) {
256 			TAILQ_INSERT_AFTER(&ioapic_conf.ioc_list,
257 			    info, ninfo, io_link);
258 			break;
259 		}
260 	}
261 	if (info == NULL)
262 		TAILQ_INSERT_HEAD(&ioapic_conf.ioc_list, ninfo, io_link);
263 }
264 
265 void
266 ioapic_intsrc(int irq, int gsi, enum intr_trigger trig, enum intr_polarity pola)
267 {
268 	struct ioapic_intsrc *int_src;
269 
270 	KKASSERT(irq < 16);
271 	int_src = &ioapic_conf.ioc_intsrc[irq];
272 
273 	if (gsi == 0) {
274 		/* Don't allow mixed mode */
275 		kprintf("IOAPIC: warning intsrc irq %d -> gsi 0\n", irq);
276 		return;
277 	}
278 
279 	if (int_src->int_gsi != -1) {
280 		if (int_src->int_gsi != gsi) {
281 			kprintf("IOAPIC: warning intsrc irq %d, gsi "
282 				"%d -> %d\n", irq, int_src->int_gsi, gsi);
283 		}
284 		if (int_src->int_trig != trig) {
285 			kprintf("IOAPIC: warning intsrc irq %d, trig "
286 				"%s -> %s\n", irq,
287 				intr_str_trigger(int_src->int_trig),
288 				intr_str_trigger(trig));
289 		}
290 		if (int_src->int_pola != pola) {
291 			kprintf("IOAPIC: warning intsrc irq %d, pola "
292 				"%s -> %s\n", irq,
293 				intr_str_polarity(int_src->int_pola),
294 				intr_str_polarity(pola));
295 		}
296 	}
297 	int_src->int_gsi = gsi;
298 	int_src->int_trig = trig;
299 	int_src->int_pola = pola;
300 }
301 
302 static void
303 ioapic_set_apic_id(const struct ioapic_info *info)
304 {
305 	uint32_t id;
306 	int apic_id;
307 
308 	id = ioapic_read(info->io_addr, IOAPIC_ID);
309 
310 	id &= ~APIC_ID_MASK;
311 	id |= (info->io_apic_id << 24);
312 
313 	ioapic_write(info->io_addr, IOAPIC_ID, id);
314 
315 	/*
316 	 * Re-read && test
317 	 */
318 	id = ioapic_read(info->io_addr, IOAPIC_ID);
319 	apic_id = (id & APIC_ID_MASK) >> 24;
320 
321 	/*
322 	 * I/O APIC ID is a 4bits field
323 	 */
324 	if ((apic_id & IOAPIC_ID_MASK) !=
325 	    (info->io_apic_id & IOAPIC_ID_MASK)) {
326 		panic("ioapic_set_apic_id: can't set apic id to %d, "
327 		      "currently set to %d\n", info->io_apic_id, apic_id);
328 	}
329 }
330 
331 static void
332 ioapic_gsi_setup(int gsi)
333 {
334 	enum intr_trigger trig;
335 	enum intr_polarity pola;
336 	int irq;
337 
338 	if (gsi == 0) {
339 		/* ExtINT */
340 		imen_lock();
341 		ioapic_extpin_setup(ioapic_gsi_ioaddr(gsi),
342 		    ioapic_gsi_pin(gsi), 0);
343 		imen_unlock();
344 		return;
345 	}
346 
347 	for (irq = 0; irq < 16; ++irq) {
348 		const struct ioapic_intsrc *int_src =
349 		    &ioapic_conf.ioc_intsrc[irq];
350 
351 		if (gsi == int_src->int_gsi) {
352 			trig = int_src->int_trig;
353 			pola = int_src->int_pola;
354 			break;
355 		}
356 	}
357 
358 	if (irq == 16) {
359 		if (gsi < 16) {
360 			trig = INTR_TRIGGER_EDGE;
361 			pola = INTR_POLARITY_HIGH;
362 		} else {
363 			trig = INTR_TRIGGER_LEVEL;
364 			pola = INTR_POLARITY_LOW;
365 		}
366 		irq = gsi;
367 	}
368 
369 	ioapic_abi_set_irqmap(irq, gsi, trig, pola);
370 }
371 
372 void *
373 ioapic_gsi_ioaddr(int gsi)
374 {
375 	const struct ioapic_info *info;
376 
377 	info = ioapic_gsi_search(gsi);
378 	return info->io_addr;
379 }
380 
381 int
382 ioapic_gsi_pin(int gsi)
383 {
384 	const struct ioapic_info *info;
385 
386 	info = ioapic_gsi_search(gsi);
387 	return gsi - info->io_gsi_base;
388 }
389 
390 static const struct ioapic_info *
391 ioapic_gsi_search(int gsi)
392 {
393 	const struct ioapic_info *info;
394 
395 	TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link) {
396 		if (gsi >= info->io_gsi_base &&
397 		    gsi < info->io_gsi_base + info->io_npin)
398 			return info;
399 	}
400 	panic("ioapic_gsi_search: no I/O APIC\n");
401 }
402 
403 int
404 ioapic_gsi(int idx, int pin)
405 {
406 	const struct ioapic_info *info;
407 
408 	TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link) {
409 		if (info->io_idx == idx)
410 			break;
411 	}
412 	if (info == NULL)
413 		return -1;
414 	if (pin >= info->io_npin)
415 		return -1;
416 	return info->io_gsi_base + pin;
417 }
418 
419 void
420 ioapic_extpin_setup(void *addr, int pin, int vec)
421 {
422 	ioapic_pin_prog(addr, pin, vec,
423 	    INTR_TRIGGER_CONFORM, INTR_POLARITY_CONFORM, IOART_DELEXINT);
424 }
425 
426 int
427 ioapic_extpin_gsi(void)
428 {
429 	return 0;
430 }
431 
432 void
433 ioapic_pin_setup(void *addr, int pin, int vec,
434     enum intr_trigger trig, enum intr_polarity pola)
435 {
436 	/*
437 	 * Always clear an I/O APIC pin before [re]programming it.  This is
438 	 * particularly important if the pin is set up for a level interrupt
439 	 * as the IOART_REM_IRR bit might be set.   When we reprogram the
440 	 * vector any EOI from pending ints on this pin could be lost and
441 	 * IRR might never get reset.
442 	 *
443 	 * To fix this problem, clear the vector and make sure it is
444 	 * programmed as an edge interrupt.  This should theoretically
445 	 * clear IRR so we can later, safely program it as a level
446 	 * interrupt.
447 	 */
448 	ioapic_pin_prog(addr, pin, vec, INTR_TRIGGER_EDGE, INTR_POLARITY_HIGH,
449 	    IOART_DELFIXED);
450 	ioapic_pin_prog(addr, pin, vec, trig, pola, IOART_DELFIXED);
451 }
452 
453 static void
454 ioapic_pin_prog(void *addr, int pin, int vec,
455     enum intr_trigger trig, enum intr_polarity pola, uint32_t del_mode)
456 {
457 	uint32_t flags, target;
458 	int select;
459 
460 	KKASSERT(del_mode == IOART_DELEXINT || del_mode == IOART_DELFIXED);
461 
462 	select = IOAPIC_REDTBL0 + (2 * pin);
463 
464 	flags = ioapic_read(addr, select) & IOART_RESV;
465 	flags |= IOART_INTMSET | IOART_DESTPHY;
466 #ifdef foo
467 	flags |= del_mode;
468 #else
469 	/*
470 	 * We only support limited I/O APIC mixed mode,
471 	 * so even for ExtINT, we still use "fixed"
472 	 * delivery mode.
473 	 */
474 	flags |= IOART_DELFIXED;
475 #endif
476 
477 	if (del_mode == IOART_DELEXINT) {
478 		KKASSERT(trig == INTR_TRIGGER_CONFORM &&
479 			 pola == INTR_POLARITY_CONFORM);
480 		flags |= IOART_TRGREDG | IOART_INTAHI;
481 	} else {
482 		switch (trig) {
483 		case INTR_TRIGGER_EDGE:
484 			flags |= IOART_TRGREDG;
485 			break;
486 
487 		case INTR_TRIGGER_LEVEL:
488 			flags |= IOART_TRGRLVL;
489 			break;
490 
491 		case INTR_TRIGGER_CONFORM:
492 			panic("ioapic_pin_prog: trig conform is not "
493 			      "supported\n");
494 		}
495 		switch (pola) {
496 		case INTR_POLARITY_HIGH:
497 			flags |= IOART_INTAHI;
498 			break;
499 
500 		case INTR_POLARITY_LOW:
501 			flags |= IOART_INTALO;
502 			break;
503 
504 		case INTR_POLARITY_CONFORM:
505 			panic("ioapic_pin_prog: pola conform is not "
506 			      "supported\n");
507 		}
508 	}
509 
510 	target = ioapic_read(addr, select + 1) & IOART_HI_DEST_RESV;
511 	target |= (CPU_TO_ID(0) << IOART_HI_DEST_SHIFT) &
512 		  IOART_HI_DEST_MASK;
513 
514 	ioapic_write(addr, select, flags | vec);
515 	ioapic_write(addr, select + 1, target);
516 }
517 
518 static void
519 ioapic_setup(const struct ioapic_info *info)
520 {
521 	int i;
522 
523 	ioapic_set_apic_id(info);
524 
525 	for (i = 0; i < info->io_npin; ++i)
526 		ioapic_gsi_setup(info->io_gsi_base + i);
527 }
528 
529 static int
530 ioapic_alloc_apic_id(int start)
531 {
532 	for (;;) {
533 		const struct ioapic_info *info;
534 		int apic_id, apic_id16;
535 
536 		apic_id = lapic_unused_apic_id(start);
537 		if (apic_id == NAPICID) {
538 			kprintf("IOAPIC: can't find unused APIC ID\n");
539 			return apic_id;
540 		}
541 		apic_id16 = apic_id & IOAPIC_ID_MASK;
542 
543 		/*
544 		 * Check against other I/O APIC's APIC ID's lower 4bits.
545 		 *
546 		 * The new APIC ID will have to be different from others
547 		 * in the lower 4bits, no matter whether xAPIC is used
548 		 * or not.
549 		 */
550 		TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link) {
551 			if (info->io_apic_id == -1) {
552 				info = NULL;
553 				break;
554 			}
555 			if ((info->io_apic_id & IOAPIC_ID_MASK) == apic_id16)
556 				break;
557 		}
558 		if (info == NULL)
559 			return apic_id;
560 
561 		kprintf("IOAPIC: APIC ID %d has same lower 4bits as "
562 			"%dth I/O APIC, keep searching...\n",
563 			apic_id, info->io_idx);
564 
565 		start = apic_id + 1;
566 	}
567 	panic("ioapic_unused_apic_id: never reached\n");
568 }
569