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