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