xref: /openbsd-src/sys/arch/amd64/amd64/ioapic.c (revision f7f466bcc6fc8631f34cea67ad9c163a47844a09)
1 /*	$OpenBSD: ioapic.c,v 1.32 2023/04/29 10:18:06 mlarkin Exp $	*/
2 /* 	$NetBSD: ioapic.c,v 1.6 2003/05/15 13:30:31 fvdl 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 
75 #include <uvm/uvm_extern.h>
76 #include <machine/i82093reg.h>
77 #include <machine/i82093var.h>
78 
79 #include <machine/mpbiosvar.h>
80 
81 /*
82  * XXX locking
83  */
84 
85 int     ioapic_match(struct device *, void *, void *);
86 void    ioapic_attach(struct device *, struct device *, void *);
87 int	ioapic_activate(struct device *, int);
88 
89 extern int x86_mem_add_mapping(bus_addr_t, bus_size_t,
90     int, bus_space_handle_t *); /* XXX XXX */
91 
92 void ioapic_hwmask(struct pic *, int);
93 void ioapic_hwunmask(struct pic *, int);
94 void ioapic_addroute(struct pic *, struct cpu_info *, int, int, int);
95 void ioapic_delroute(struct pic *, struct cpu_info *, int, int, int);
96 void apic_set_redir(struct ioapic_softc *, int, int, struct cpu_info *);
97 
98 int ioapic_bsp_id = 0;
99 int ioapic_cold = 1;
100 
101 struct ioapic_softc *ioapics;	 /* head of linked list */
102 int nioapics = 0;	   	 /* number attached */
103 static int ioapic_vecbase;
104 
105 void ioapic_set_id(struct ioapic_softc *);
106 
107 static __inline u_long
ioapic_lock(struct ioapic_softc * sc)108 ioapic_lock(struct ioapic_softc *sc)
109 {
110 	u_long flags;
111 
112 	flags = intr_disable();
113 #ifdef MULTIPROCESSOR
114 	mtx_enter(&sc->sc_pic.pic_mutex);
115 #endif
116 	return flags;
117 }
118 
119 static __inline void
ioapic_unlock(struct ioapic_softc * sc,u_long flags)120 ioapic_unlock(struct ioapic_softc *sc, u_long flags)
121 {
122 #ifdef MULTIPROCESSOR
123 	mtx_leave(&sc->sc_pic.pic_mutex);
124 #endif
125 	intr_restore(flags);
126 }
127 
128 /*
129  * Register read/write routines.
130  */
131 static __inline u_int32_t
ioapic_read_ul(struct ioapic_softc * sc,int regid)132 ioapic_read_ul(struct ioapic_softc *sc,int regid)
133 {
134 	u_int32_t val;
135 
136 	*(sc->sc_reg) = regid;
137 	val = *sc->sc_data;
138 
139 	return (val);
140 }
141 
142 static __inline void
ioapic_write_ul(struct ioapic_softc * sc,int regid,u_int32_t val)143 ioapic_write_ul(struct ioapic_softc *sc,int regid, u_int32_t val)
144 {
145 	*(sc->sc_reg) = regid;
146 	*(sc->sc_data) = val;
147 }
148 
149 static __inline u_int32_t
ioapic_read(struct ioapic_softc * sc,int regid)150 ioapic_read(struct ioapic_softc *sc, int regid)
151 {
152 	u_int32_t val;
153 	u_long flags;
154 
155 	flags = ioapic_lock(sc);
156 	val = ioapic_read_ul(sc, regid);
157 	ioapic_unlock(sc, flags);
158 	return val;
159 }
160 
161 static __inline void
ioapic_write(struct ioapic_softc * sc,int regid,int val)162 ioapic_write(struct ioapic_softc *sc,int regid, int val)
163 {
164 	u_long flags;
165 
166 	flags = ioapic_lock(sc);
167 	ioapic_write_ul(sc, regid, val);
168 	ioapic_unlock(sc, flags);
169 }
170 
171 struct ioapic_softc *
ioapic_find(int apicid)172 ioapic_find(int apicid)
173 {
174 	struct ioapic_softc *sc;
175 
176 	if (apicid == MPS_ALL_APICS) {	/* XXX mpbios-specific */
177 		/*
178 		 * XXX kludge for all-ioapics interrupt support
179 		 * on single ioapic systems
180 		 */
181 		if (nioapics <= 1)
182 			return (ioapics);
183 		panic("unsupported: all-ioapics interrupt with >1 ioapic");
184 	}
185 
186 	for (sc = ioapics; sc != NULL; sc = sc->sc_next)
187 		if (sc->sc_apicid == apicid)
188 			return (sc);
189 
190 	return (NULL);
191 }
192 
193 /*
194  * For the case the I/O APICs were configured using ACPI, there must
195  * be an option to match global ACPI interrupts with APICs.
196  */
197 struct ioapic_softc *
ioapic_find_bybase(int vec)198 ioapic_find_bybase(int vec)
199 {
200 	struct ioapic_softc *sc;
201 
202 	for (sc = ioapics; sc != NULL; sc = sc->sc_next) {
203 		if (vec >= sc->sc_apic_vecbase &&
204 		    vec < (sc->sc_apic_vecbase + sc->sc_apic_sz))
205 			return sc;
206 	}
207 
208 	return NULL;
209 }
210 
211 static __inline void
ioapic_add(struct ioapic_softc * sc)212 ioapic_add(struct ioapic_softc *sc)
213 {
214 	sc->sc_next = ioapics;
215 	ioapics = sc;
216 	nioapics++;
217 }
218 
219 void
ioapic_print_redir(struct ioapic_softc * sc,char * why,int pin)220 ioapic_print_redir(struct ioapic_softc *sc, char *why, int pin)
221 {
222 	u_int32_t redirlo = ioapic_read(sc, IOAPIC_REDLO(pin));
223 	u_int32_t redirhi = ioapic_read(sc, IOAPIC_REDHI(pin));
224 
225 	apic_format_redir(sc->sc_pic.pic_name, why, pin, redirhi, redirlo);
226 }
227 
228 const struct cfattach ioapic_ca = {
229 	sizeof(struct ioapic_softc), ioapic_match, ioapic_attach, NULL,
230 	ioapic_activate
231 };
232 
233 struct cfdriver ioapic_cd = {
234 	NULL, "ioapic", DV_DULL
235 };
236 
237 int
ioapic_match(struct device * parent,void * v,void * aux)238 ioapic_match(struct device *parent, void *v, void *aux)
239 {
240 	struct apic_attach_args *aaa = (struct apic_attach_args *)aux;
241 	struct cfdata *match = v;
242 
243 	if (strcmp(aaa->aaa_name, match->cf_driver->cd_name) == 0)
244 		return (1);
245 	return (0);
246 }
247 
248 /* Reprogram the APIC ID, and check that it actually got set. */
249 void
ioapic_set_id(struct ioapic_softc * sc)250 ioapic_set_id(struct ioapic_softc *sc)
251 {
252 	u_int8_t apic_id;
253 
254 	ioapic_write(sc, IOAPIC_ID,
255 	    (ioapic_read(sc, IOAPIC_ID) & ~IOAPIC_ID_MASK) |
256 	    (sc->sc_apicid << IOAPIC_ID_SHIFT));
257 
258 	apic_id = (ioapic_read(sc, IOAPIC_ID) & IOAPIC_ID_MASK) >>
259 	    IOAPIC_ID_SHIFT;
260 
261 	if (apic_id != sc->sc_apicid)
262 		printf(", can't remap");
263 	else
264 		printf(", remapped");
265 }
266 
267 /*
268  * can't use bus_space_xxx as we don't have a bus handle ...
269  */
270 void
ioapic_attach(struct device * parent,struct device * self,void * aux)271 ioapic_attach(struct device *parent, struct device *self, void *aux)
272 {
273 	struct ioapic_softc *sc = (struct ioapic_softc *)self;
274 	struct apic_attach_args  *aaa = (struct apic_attach_args *)aux;
275 	int apic_id;
276 	bus_space_handle_t bh;
277 	u_int32_t ver_sz;
278 	int i;
279 
280 	sc->sc_flags = aaa->flags;
281 	sc->sc_apicid = aaa->apic_id;
282 
283 	printf(": apid %d", aaa->apic_id);
284 
285 	if (ioapic_find(aaa->apic_id) != NULL) {
286 		printf(", duplicate apic id (ignored)\n");
287 		return;
288 	}
289 
290 	ioapic_add(sc);
291 
292 	printf(" pa 0x%lx", aaa->apic_address);
293 
294 	if (x86_mem_add_mapping(aaa->apic_address, PAGE_SIZE, 0, &bh) != 0) {
295 		printf(", map failed\n");
296 		return;
297 	}
298 	sc->sc_reg = (volatile u_int32_t *)(bh + IOAPIC_REG);
299 	sc->sc_data = (volatile u_int32_t *)(bh + IOAPIC_DATA);
300 
301 	sc->sc_pic.pic_type = PIC_IOAPIC;
302 #ifdef MULTIPROCESSOR
303 	mtx_init(&sc->sc_pic.pic_mutex, IPL_NONE);
304 #endif
305 	sc->sc_pic.pic_hwmask = ioapic_hwmask;
306 	sc->sc_pic.pic_hwunmask = ioapic_hwunmask;
307 	sc->sc_pic.pic_addroute = ioapic_addroute;
308 	sc->sc_pic.pic_delroute = ioapic_delroute;
309 	sc->sc_pic.pic_edge_stubs = ioapic_edge_stubs;
310 	sc->sc_pic.pic_level_stubs = ioapic_level_stubs;
311 
312 	ver_sz = ioapic_read(sc, IOAPIC_VER);
313 	sc->sc_apic_vers = (ver_sz & IOAPIC_VER_MASK) >> IOAPIC_VER_SHIFT;
314 	sc->sc_apic_sz = (ver_sz & IOAPIC_MAX_MASK) >> IOAPIC_MAX_SHIFT;
315 	sc->sc_apic_sz++;
316 
317 	if (aaa->apic_vecbase != -1)
318 		sc->sc_apic_vecbase = aaa->apic_vecbase;
319 	else {
320 		/*
321 		 * XXX this assumes ordering of ioapics in the table.
322 		 * Only needed for broken BIOS workaround (see mpbios.c)
323 		 */
324 		sc->sc_apic_vecbase = ioapic_vecbase;
325 		ioapic_vecbase += sc->sc_apic_sz;
326 	}
327 
328 	if (mp_verbose) {
329 		printf(", %s mode",
330 		    aaa->flags & IOAPIC_PICMODE ? "PIC" : "virtual wire");
331 	}
332 
333 	printf(", version %x, %d pins", sc->sc_apic_vers, sc->sc_apic_sz);
334 
335 	apic_id = (ioapic_read(sc, IOAPIC_ID) & IOAPIC_ID_MASK) >>
336 	    IOAPIC_ID_SHIFT;
337 
338 	sc->sc_pins = mallocarray(sc->sc_apic_sz, sizeof(struct ioapic_pin),
339 	    M_DEVBUF, M_WAITOK);
340 
341 	for (i = 0; i < sc->sc_apic_sz; i++) {
342 		sc->sc_pins[i].ip_next = NULL;
343 		sc->sc_pins[i].ip_map = NULL;
344 		sc->sc_pins[i].ip_vector = 0;
345 		sc->sc_pins[i].ip_type = IST_NONE;
346 	}
347 
348 	/*
349 	 * In case the APIC is not initialized to the correct ID
350 	 * do it now.
351 	 * Maybe we should record the original ID for interrupt
352 	 * mapping later ...
353 	 */
354 	if (apic_id != sc->sc_apicid) {
355 		if (mp_verbose)
356 			printf("\n%s: misconfigured as apic %d",
357 			    sc->sc_pic.pic_name, apic_id);
358 		ioapic_set_id(sc);
359 	}
360 
361 	printf("\n");
362 }
363 
364 int
ioapic_activate(struct device * self,int act)365 ioapic_activate(struct device *self, int act)
366 {
367 	struct ioapic_softc *sc = (struct ioapic_softc *)self;
368 
369 	switch (act) {
370 	case DVACT_RESUME:
371 		/* On resume, reset the APIC id, like we do on boot */
372 		ioapic_write(sc, IOAPIC_ID,
373 		    (ioapic_read(sc, IOAPIC_ID) & ~IOAPIC_ID_MASK) |
374 		    (sc->sc_apicid << IOAPIC_ID_SHIFT));
375 	}
376 
377 	return (0);
378 }
379 
380 void
apic_set_redir(struct ioapic_softc * sc,int pin,int idt_vec,struct cpu_info * ci)381 apic_set_redir(struct ioapic_softc *sc, int pin, int idt_vec,
382     struct cpu_info *ci)
383 {
384 	u_int32_t redlo;
385 	u_int32_t redhi = 0;
386 	int delmode;
387 
388 	struct ioapic_pin *pp;
389 	struct mp_intr_map *map;
390 
391 	pp = &sc->sc_pins[pin];
392 	map = pp->ip_map;
393 	redlo = (map == NULL) ? IOAPIC_REDLO_MASK : map->redir;
394 	delmode = (redlo & IOAPIC_REDLO_DEL_MASK) >> IOAPIC_REDLO_DEL_SHIFT;
395 
396 	/* XXX magic numbers */
397 	if ((delmode != 0) && (delmode != 1))
398 		;
399 	else if (pp->ip_type == IST_NONE) {
400 		redlo |= IOAPIC_REDLO_MASK;
401 	} else {
402 		redlo |= (idt_vec & 0xff);
403 		redlo &= ~IOAPIC_REDLO_DEL_MASK;
404 		redlo |= (IOAPIC_REDLO_DEL_FIXED << IOAPIC_REDLO_DEL_SHIFT);
405 		redlo &= ~IOAPIC_REDLO_DSTMOD;
406 
407 		/*
408 		 * Destination: BSP CPU
409 		 *
410 		 * XXX will want to distribute interrupts across cpu's
411 		 * eventually.  most likely, we'll want to vector each
412 		 * interrupt to a specific CPU and load-balance across
413 		 * cpu's.  but there's no point in doing that until after
414 		 * most interrupts run without the kernel lock.
415 		 */
416 		redhi |= (ci->ci_apicid << IOAPIC_REDHI_DEST_SHIFT);
417 
418 		/* XXX derive this bit from BIOS info */
419 		if (pp->ip_type == IST_LEVEL)
420 			redlo |= IOAPIC_REDLO_LEVEL;
421 		else
422 			redlo &= ~IOAPIC_REDLO_LEVEL;
423 		if (map != NULL && ((map->flags & 3) == MPS_INTPO_DEF)) {
424 			if (pp->ip_type == IST_LEVEL)
425 				redlo |= IOAPIC_REDLO_ACTLO;
426 			else
427 				redlo &= ~IOAPIC_REDLO_ACTLO;
428 		}
429 	}
430 	/* Do atomic write */
431 	ioapic_write(sc, IOAPIC_REDLO(pin), IOAPIC_REDLO_MASK);
432 	ioapic_write(sc, IOAPIC_REDHI(pin), redhi);
433 	ioapic_write(sc, IOAPIC_REDLO(pin), redlo);
434 	if (mp_verbose)
435 		ioapic_print_redir(sc, "int", pin);
436 }
437 
438 /*
439  * Throw the switch and enable interrupts..
440  */
441 
442 void
ioapic_enable(void)443 ioapic_enable(void)
444 {
445 	int p;
446 	struct ioapic_softc *sc;
447 	struct ioapic_pin *ip;
448 
449 	ioapic_cold = 0;
450 
451 	if (ioapics == NULL)
452 		return;
453 
454 	if (ioapics->sc_flags & IOAPIC_PICMODE) {
455 		printf("%s: writing to IMCR to disable pics\n",
456 		    ioapics->sc_pic.pic_name);
457 		outb(IMCR_ADDR, IMCR_REGISTER);
458 		outb(IMCR_DATA, IMCR_APIC);
459 	}
460 
461 	for (sc = ioapics; sc != NULL; sc = sc->sc_next) {
462 		if (mp_verbose)
463 			printf("%s: enabling\n", sc->sc_pic.pic_name);
464 
465 		for (p = 0; p < sc->sc_apic_sz; p++) {
466 			ip = &sc->sc_pins[p];
467 			if (ip->ip_type != IST_NONE)
468 				apic_set_redir(sc, p, ip->ip_vector,
469 				    ip->ip_cpu);
470 		}
471 	}
472 }
473 
474 void
ioapic_hwmask(struct pic * pic,int pin)475 ioapic_hwmask(struct pic *pic, int pin)
476 {
477 	u_int32_t redlo;
478 	struct ioapic_softc *sc = (struct ioapic_softc *)pic;
479 	u_long flags;
480 
481 	if (ioapic_cold)
482 		return;
483 	flags = ioapic_lock(sc);
484 	redlo = ioapic_read_ul(sc, IOAPIC_REDLO(pin));
485 	redlo |= IOAPIC_REDLO_MASK;
486 	redlo &= ~IOAPIC_REDLO_RIRR;
487 	ioapic_write_ul(sc, IOAPIC_REDLO(pin), redlo);
488 	ioapic_unlock(sc, flags);
489 }
490 
491 void
ioapic_hwunmask(struct pic * pic,int pin)492 ioapic_hwunmask(struct pic *pic, int pin)
493 {
494 	u_int32_t redlo;
495 	struct ioapic_softc *sc = (struct ioapic_softc *)pic;
496 	u_long flags;
497 
498 	if (ioapic_cold)
499 		return;
500 	flags = ioapic_lock(sc);
501 	redlo = ioapic_read_ul(sc, IOAPIC_REDLO(pin));
502 	redlo &= ~IOAPIC_REDLO_MASK;
503 	redlo &= ~IOAPIC_REDLO_RIRR;
504 	ioapic_write_ul(sc, IOAPIC_REDLO(pin), redlo);
505 	ioapic_unlock(sc, flags);
506 }
507 
508 void
ioapic_addroute(struct pic * pic,struct cpu_info * ci,int pin,int idtvec,int type)509 ioapic_addroute(struct pic *pic, struct cpu_info *ci, int pin,
510     int idtvec, int type)
511 {
512 	struct ioapic_softc *sc = (struct ioapic_softc *)pic;
513 	struct ioapic_pin *pp;
514 
515 	pp = &sc->sc_pins[pin];
516 	pp->ip_type = type;
517 	pp->ip_vector = idtvec;
518 	pp->ip_cpu = ci;
519 	if (ioapic_cold)
520 		return;
521 	apic_set_redir(sc, pin, idtvec, ci);
522 }
523 
524 void
ioapic_delroute(struct pic * pic,struct cpu_info * ci,int pin,int idtvec,int type)525 ioapic_delroute(struct pic *pic, struct cpu_info *ci, int pin,
526     int idtvec, int type)
527 {
528 	struct ioapic_softc *sc = (struct ioapic_softc *)pic;
529 	struct ioapic_pin *pp;
530 
531 	if (ioapic_cold) {
532 		pp = &sc->sc_pins[pin];
533 		pp->ip_type = IST_NONE;
534 		return;
535 	}
536 	ioapic_hwmask(pic, pin);
537 }
538 
539 #ifdef DDB
540 void ioapic_dump(void);
541 
542 void
ioapic_dump(void)543 ioapic_dump(void)
544 {
545 	struct ioapic_softc *sc;
546 	struct ioapic_pin *ip;
547 	int p;
548 
549 	for (sc = ioapics; sc != NULL; sc = sc->sc_next) {
550 		for (p = 0; p < sc->sc_apic_sz; p++) {
551 			ip = &sc->sc_pins[p];
552 			if (ip->ip_type != IST_NONE)
553 				ioapic_print_redir(sc, "dump", p);
554 		}
555 	}
556 }
557 #endif
558