| 1 | /*- |
| 2 | * Copyright (c) 1991 The Regents of the University of California. |
| 3 | * All rights reserved. |
| 4 | * |
| 5 | * This code is derived from software contributed to Berkeley by |
| 6 | * William Jolitz. |
| 7 | * |
| 8 | * Redistribution and use in source and binary forms, with or without |
| 9 | * modification, are permitted provided that the following conditions |
| 10 | * are met: |
| 11 | * 1. Redistributions of source code must retain the above copyright |
| 12 | * notice, this list of conditions and the following disclaimer. |
| 13 | * 2. Redistributions in binary form must reproduce the above copyright |
| 14 | * notice, this list of conditions and the following disclaimer in the |
| 15 | * documentation and/or other materials provided with the distribution. |
| 16 | * 3. All advertising materials mentioning features or use of this software |
| 17 | * must display the following acknowledgement: |
| 18 | * This product includes software developed by the University of |
| 19 | * California, Berkeley and its contributors. |
| 20 | * 4. Neither the name of the University nor the names of its contributors |
| 21 | * may be used to endorse or promote products derived from this software |
| 22 | * without specific prior written permission. |
| 23 | * |
| 24 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
| 25 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| 27 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
| 28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| 29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| 30 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| 31 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| 32 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| 33 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| 34 | * SUCH DAMAGE. |
| 35 | * |
| 36 | * from: @(#)isa.c 7.2 (Berkeley) 5/13/91 |
| 37 | * $FreeBSD: src/sys/i386/isa/intr_machdep.c,v 1.29.2.5 2001/10/14 06:54:27 luigi Exp $ |
| 38 | * $DragonFly: src/sys/i386/isa/Attic/intr_machdep.c,v 1.31 2005/06/16 21:12:47 dillon Exp $ |
| 39 | */ |
| 40 | /* |
| 41 | * This file contains an aggregated module marked: |
| 42 | * Copyright (c) 1997, Stefan Esser <se@freebsd.org> |
| 43 | * All rights reserved. |
| 44 | * See the notice for details. |
| 45 | */ |
| 46 | |
| 47 | #include "use_isa.h" |
| 48 | #include "opt_auto_eoi.h" |
| 49 | |
| 50 | #include <sys/param.h> |
| 51 | #ifndef SMP |
| 52 | #include <machine/lock.h> |
| 53 | #endif |
| 54 | #include <sys/systm.h> |
| 55 | #include <sys/syslog.h> |
| 56 | #include <sys/malloc.h> |
| 57 | #include <sys/errno.h> |
| 58 | #include <sys/interrupt.h> |
| 59 | #include <machine/ipl.h> |
| 60 | #include <machine/md_var.h> |
| 61 | #include <machine/segments.h> |
| 62 | #include <sys/bus.h> |
| 63 | #include <machine/globaldata.h> |
| 64 | #include <sys/proc.h> |
| 65 | #include <sys/thread2.h> |
| 66 | |
| 67 | #include <machine/smptests.h> /** FAST_HI */ |
| 68 | #include <machine/smp.h> |
| 69 | #include <bus/isa/i386/isa.h> |
| 70 | #include <i386/isa/icu.h> |
| 71 | |
| 72 | #if NISA > 0 |
| 73 | #include <bus/isa/isavar.h> |
| 74 | #endif |
| 75 | #include <i386/isa/intr_machdep.h> |
| 76 | #include <bus/isa/isavar.h> |
| 77 | #include <sys/interrupt.h> |
| 78 | #ifdef APIC_IO |
| 79 | #include <machine/clock.h> |
| 80 | #endif |
| 81 | #include <machine/cpu.h> |
| 82 | |
| 83 | /* XXX should be in suitable include files */ |
| 84 | #define ICU_IMR_OFFSET 1 /* IO_ICU{1,2} + 1 */ |
| 85 | #define ICU_SLAVEID 2 |
| 86 | |
| 87 | #ifdef APIC_IO |
| 88 | /* |
| 89 | * This is to accommodate "mixed-mode" programming for |
| 90 | * motherboards that don't connect the 8254 to the IO APIC. |
| 91 | */ |
| 92 | #define AUTO_EOI_1 1 |
| 93 | #endif |
| 94 | |
| 95 | #define NR_INTRNAMES (1 + ICU_LEN + 2 * ICU_LEN) |
| 96 | |
| 97 | static inthand2_t isa_strayintr; |
| 98 | #if defined(FAST_HI) && defined(APIC_IO) |
| 99 | static inthand2_t isa_wrongintr; |
| 100 | #endif |
| 101 | static void init_i8259(void); |
| 102 | |
| 103 | void *intr_unit[ICU_LEN*2]; |
| 104 | u_long *intr_countp[ICU_LEN*2]; |
| 105 | inthand2_t *intr_handler[ICU_LEN*2] = { |
| 106 | isa_strayintr, isa_strayintr, isa_strayintr, isa_strayintr, |
| 107 | isa_strayintr, isa_strayintr, isa_strayintr, isa_strayintr, |
| 108 | isa_strayintr, isa_strayintr, isa_strayintr, isa_strayintr, |
| 109 | isa_strayintr, isa_strayintr, isa_strayintr, isa_strayintr, |
| 110 | isa_strayintr, isa_strayintr, isa_strayintr, isa_strayintr, |
| 111 | isa_strayintr, isa_strayintr, isa_strayintr, isa_strayintr, |
| 112 | isa_strayintr, isa_strayintr, isa_strayintr, isa_strayintr, |
| 113 | isa_strayintr, isa_strayintr, isa_strayintr, isa_strayintr, |
| 114 | }; |
| 115 | |
| 116 | static struct md_intr_info { |
| 117 | int irq; |
| 118 | int mihandler_installed; |
| 119 | } intr_info[ICU_LEN*2]; |
| 120 | |
| 121 | static inthand_t *fastintr[ICU_LEN] = { |
| 122 | &IDTVEC(fastintr0), &IDTVEC(fastintr1), |
| 123 | &IDTVEC(fastintr2), &IDTVEC(fastintr3), |
| 124 | &IDTVEC(fastintr4), &IDTVEC(fastintr5), |
| 125 | &IDTVEC(fastintr6), &IDTVEC(fastintr7), |
| 126 | &IDTVEC(fastintr8), &IDTVEC(fastintr9), |
| 127 | &IDTVEC(fastintr10), &IDTVEC(fastintr11), |
| 128 | &IDTVEC(fastintr12), &IDTVEC(fastintr13), |
| 129 | &IDTVEC(fastintr14), &IDTVEC(fastintr15), |
| 130 | #if defined(APIC_IO) |
| 131 | &IDTVEC(fastintr16), &IDTVEC(fastintr17), |
| 132 | &IDTVEC(fastintr18), &IDTVEC(fastintr19), |
| 133 | &IDTVEC(fastintr20), &IDTVEC(fastintr21), |
| 134 | &IDTVEC(fastintr22), &IDTVEC(fastintr23), |
| 135 | #endif /* APIC_IO */ |
| 136 | }; |
| 137 | |
| 138 | unpendhand_t *fastunpend[ICU_LEN] = { |
| 139 | IDTVEC(fastunpend0), IDTVEC(fastunpend1), |
| 140 | IDTVEC(fastunpend2), IDTVEC(fastunpend3), |
| 141 | IDTVEC(fastunpend4), IDTVEC(fastunpend5), |
| 142 | IDTVEC(fastunpend6), IDTVEC(fastunpend7), |
| 143 | IDTVEC(fastunpend8), IDTVEC(fastunpend9), |
| 144 | IDTVEC(fastunpend10), IDTVEC(fastunpend11), |
| 145 | IDTVEC(fastunpend12), IDTVEC(fastunpend13), |
| 146 | IDTVEC(fastunpend14), IDTVEC(fastunpend15), |
| 147 | #if defined(APIC_IO) |
| 148 | IDTVEC(fastunpend16), IDTVEC(fastunpend17), |
| 149 | IDTVEC(fastunpend18), IDTVEC(fastunpend19), |
| 150 | IDTVEC(fastunpend20), IDTVEC(fastunpend21), |
| 151 | IDTVEC(fastunpend22), IDTVEC(fastunpend23), |
| 152 | #endif |
| 153 | }; |
| 154 | |
| 155 | static inthand_t *slowintr[ICU_LEN] = { |
| 156 | &IDTVEC(intr0), &IDTVEC(intr1), &IDTVEC(intr2), &IDTVEC(intr3), |
| 157 | &IDTVEC(intr4), &IDTVEC(intr5), &IDTVEC(intr6), &IDTVEC(intr7), |
| 158 | &IDTVEC(intr8), &IDTVEC(intr9), &IDTVEC(intr10), &IDTVEC(intr11), |
| 159 | &IDTVEC(intr12), &IDTVEC(intr13), &IDTVEC(intr14), &IDTVEC(intr15), |
| 160 | #if defined(APIC_IO) |
| 161 | &IDTVEC(intr16), &IDTVEC(intr17), &IDTVEC(intr18), &IDTVEC(intr19), |
| 162 | &IDTVEC(intr20), &IDTVEC(intr21), &IDTVEC(intr22), &IDTVEC(intr23), |
| 163 | #endif /* APIC_IO */ |
| 164 | }; |
| 165 | |
| 166 | #define NMI_PARITY (1 << 7) |
| 167 | #define NMI_IOCHAN (1 << 6) |
| 168 | #define ENMI_WATCHDOG (1 << 7) |
| 169 | #define ENMI_BUSTIMER (1 << 6) |
| 170 | #define ENMI_IOSTATUS (1 << 5) |
| 171 | |
| 172 | /* |
| 173 | * Handle a NMI, possibly a machine check. |
| 174 | * return true to panic system, false to ignore. |
| 175 | */ |
| 176 | int |
| 177 | isa_nmi(cd) |
| 178 | int cd; |
| 179 | { |
| 180 | int retval = 0; |
| 181 | int isa_port = inb(0x61); |
| 182 | int eisa_port = inb(0x461); |
| 183 | |
| 184 | log(LOG_CRIT, "NMI ISA %x, EISA %x\n", isa_port, eisa_port); |
| 185 | |
| 186 | if (isa_port & NMI_PARITY) { |
| 187 | log(LOG_CRIT, "RAM parity error, likely hardware failure."); |
| 188 | retval = 1; |
| 189 | } |
| 190 | |
| 191 | if (isa_port & NMI_IOCHAN) { |
| 192 | log(LOG_CRIT, "I/O channel check, likely hardware failure."); |
| 193 | retval = 1; |
| 194 | } |
| 195 | |
| 196 | /* |
| 197 | * On a real EISA machine, this will never happen. However it can |
| 198 | * happen on ISA machines which implement XT style floating point |
| 199 | * error handling (very rare). Save them from a meaningless panic. |
| 200 | */ |
| 201 | if (eisa_port == 0xff) |
| 202 | return(retval); |
| 203 | |
| 204 | if (eisa_port & ENMI_WATCHDOG) { |
| 205 | log(LOG_CRIT, "EISA watchdog timer expired, likely hardware failure."); |
| 206 | retval = 1; |
| 207 | } |
| 208 | |
| 209 | if (eisa_port & ENMI_BUSTIMER) { |
| 210 | log(LOG_CRIT, "EISA bus timeout, likely hardware failure."); |
| 211 | retval = 1; |
| 212 | } |
| 213 | |
| 214 | if (eisa_port & ENMI_IOSTATUS) { |
| 215 | log(LOG_CRIT, "EISA I/O port status error."); |
| 216 | retval = 1; |
| 217 | } |
| 218 | return(retval); |
| 219 | } |
| 220 | |
| 221 | /* |
| 222 | * ICU reinitialize when ICU configuration has lost. |
| 223 | */ |
| 224 | void |
| 225 | icu_reinit() |
| 226 | { |
| 227 | int i; |
| 228 | |
| 229 | init_i8259(); |
| 230 | for(i=0;i<ICU_LEN;i++) |
| 231 | if(intr_handler[i] != isa_strayintr) |
| 232 | INTREN(1<<i); |
| 233 | } |
| 234 | |
| 235 | |
| 236 | /* |
| 237 | * Fill in default interrupt table (in case of spurious interrupt |
| 238 | * during configuration of kernel, setup interrupt control unit |
| 239 | */ |
| 240 | void |
| 241 | isa_defaultirq() |
| 242 | { |
| 243 | int i; |
| 244 | |
| 245 | /* icu vectors */ |
| 246 | for (i = 0; i < ICU_LEN; i++) |
| 247 | icu_unset(i, isa_strayintr); |
| 248 | init_i8259(); |
| 249 | } |
| 250 | |
| 251 | static void |
| 252 | init_i8259(void) |
| 253 | { |
| 254 | |
| 255 | /* initialize 8259's */ |
| 256 | outb(IO_ICU1, 0x11); /* reset; program device, four bytes */ |
| 257 | outb(IO_ICU1+ICU_IMR_OFFSET, NRSVIDT); /* starting at this vector index */ |
| 258 | outb(IO_ICU1+ICU_IMR_OFFSET, IRQ_SLAVE); /* slave on line 7 */ |
| 259 | #ifdef AUTO_EOI_1 |
| 260 | outb(IO_ICU1+ICU_IMR_OFFSET, 2 | 1); /* auto EOI, 8086 mode */ |
| 261 | #else |
| 262 | outb(IO_ICU1+ICU_IMR_OFFSET, 1); /* 8086 mode */ |
| 263 | #endif |
| 264 | outb(IO_ICU1+ICU_IMR_OFFSET, 0xff); /* leave interrupts masked */ |
| 265 | outb(IO_ICU1, 0x0a); /* default to IRR on read */ |
| 266 | outb(IO_ICU1, 0xc0 | (3 - 1)); /* pri order 3-7, 0-2 (com2 first) */ |
| 267 | outb(IO_ICU2, 0x11); /* reset; program device, four bytes */ |
| 268 | outb(IO_ICU2+ICU_IMR_OFFSET, NRSVIDT+8); /* staring at this vector index */ |
| 269 | outb(IO_ICU2+ICU_IMR_OFFSET, ICU_SLAVEID); /* my slave id is 7 */ |
| 270 | #ifdef AUTO_EOI_2 |
| 271 | outb(IO_ICU2+ICU_IMR_OFFSET, 2 | 1); /* auto EOI, 8086 mode */ |
| 272 | #else |
| 273 | outb(IO_ICU2+ICU_IMR_OFFSET,1); /* 8086 mode */ |
| 274 | #endif |
| 275 | outb(IO_ICU2+ICU_IMR_OFFSET, 0xff); /* leave interrupts masked */ |
| 276 | outb(IO_ICU2, 0x0a); /* default to IRR on read */ |
| 277 | } |
| 278 | |
| 279 | /* |
| 280 | * Caught a stray interrupt, notify |
| 281 | */ |
| 282 | static void |
| 283 | isa_strayintr(void *vcookiep) |
| 284 | { |
| 285 | int intr = (void **)vcookiep - &intr_unit[0]; |
| 286 | |
| 287 | /* DON'T BOTHER FOR NOW! */ |
| 288 | /* for some reason, we get bursts of intr #7, even if not enabled! */ |
| 289 | /* |
| 290 | * Well the reason you got bursts of intr #7 is because someone |
| 291 | * raised an interrupt line and dropped it before the 8259 could |
| 292 | * prioritize it. This is documented in the intel data book. This |
| 293 | * means you have BAD hardware! I have changed this so that only |
| 294 | * the first 5 get logged, then it quits logging them, and puts |
| 295 | * out a special message. rgrimes 3/25/1993 |
| 296 | */ |
| 297 | /* |
| 298 | * XXX TODO print a different message for #7 if it is for a |
| 299 | * glitch. Glitches can be distinguished from real #7's by |
| 300 | * testing that the in-service bit is _not_ set. The test |
| 301 | * must be done before sending an EOI so it can't be done if |
| 302 | * we are using AUTO_EOI_1. |
| 303 | */ |
| 304 | if (intrcnt[1 + intr] <= 5) |
| 305 | log(LOG_ERR, "stray irq %d\n", intr); |
| 306 | if (intrcnt[1 + intr] == 5) |
| 307 | log(LOG_CRIT, |
| 308 | "too many stray irq %d's; not logging any more\n", intr); |
| 309 | } |
| 310 | |
| 311 | #if defined(FAST_HI) && defined(APIC_IO) |
| 312 | |
| 313 | /* |
| 314 | * This occurs if we mis-programmed the APIC and its vector is still |
| 315 | * pointing to the slow vector even when we thought we reprogrammed it |
| 316 | * to the high vector. This can occur when interrupts are improperly |
| 317 | * routed by the APIC. The unit data is opaque so we have to try to |
| 318 | * find it in the unit array. |
| 319 | */ |
| 320 | static void |
| 321 | isa_wrongintr(void *vcookiep) |
| 322 | { |
| 323 | int intr; |
| 324 | |
| 325 | for (intr = 0; intr < ICU_LEN*2; ++intr) { |
| 326 | if (intr_unit[intr] == vcookiep) |
| 327 | break; |
| 328 | } |
| 329 | if (intr == ICU_LEN*2) { |
| 330 | log(LOG_ERR, "stray unknown irq (APIC misprogrammed)\n"); |
| 331 | } else if (intrcnt[1 + intr] <= 5) { |
| 332 | log(LOG_ERR, "stray irq ~%d (APIC misprogrammed)\n", intr); |
| 333 | } else if (intrcnt[1 + intr] == 6) { |
| 334 | log(LOG_CRIT, |
| 335 | "too many stray irq ~%d's; not logging any more\n", intr); |
| 336 | } |
| 337 | } |
| 338 | |
| 339 | #endif |
| 340 | |
| 341 | #if NISA > 0 |
| 342 | /* |
| 343 | * Return a bitmap of the current interrupt requests. This is 8259-specific |
| 344 | * and is only suitable for use at probe time. |
| 345 | */ |
| 346 | intrmask_t |
| 347 | isa_irq_pending(void) |
| 348 | { |
| 349 | u_char irr1; |
| 350 | u_char irr2; |
| 351 | |
| 352 | irr1 = inb(IO_ICU1); |
| 353 | irr2 = inb(IO_ICU2); |
| 354 | return ((irr2 << 8) | irr1); |
| 355 | } |
| 356 | #endif |
| 357 | |
| 358 | static void |
| 359 | update_intrname(int intr, char *name) |
| 360 | { |
| 361 | char buf[32]; |
| 362 | char *cp; |
| 363 | int name_index, off, strayintr; |
| 364 | |
| 365 | /* |
| 366 | * Initialise strings for bitbucket and stray interrupt counters. |
| 367 | * These have statically allocated indices 0 and 1 through ICU_LEN. |
| 368 | */ |
| 369 | if (intrnames[0] == '\0') { |
| 370 | off = sprintf(intrnames, "???") + 1; |
| 371 | for (strayintr = 0; strayintr < ICU_LEN; strayintr++) |
| 372 | off += sprintf(intrnames + off, "stray irq%d", |
| 373 | strayintr) + 1; |
| 374 | } |
| 375 | |
| 376 | if (name == NULL) |
| 377 | name = "???"; |
| 378 | if (snprintf(buf, sizeof(buf), "%s irq%d", name, intr) >= sizeof(buf)) |
| 379 | goto use_bitbucket; |
| 380 | |
| 381 | /* |
| 382 | * Search for `buf' in `intrnames'. In the usual case when it is |
| 383 | * not found, append it to the end if there is enough space (the \0 |
| 384 | * terminator for the previous string, if any, becomes a separator). |
| 385 | */ |
| 386 | for (cp = intrnames, name_index = 0; |
| 387 | cp != eintrnames && name_index < NR_INTRNAMES; |
| 388 | cp += strlen(cp) + 1, name_index++) { |
| 389 | if (*cp == '\0') { |
| 390 | if (strlen(buf) >= eintrnames - cp) |
| 391 | break; |
| 392 | strcpy(cp, buf); |
| 393 | goto found; |
| 394 | } |
| 395 | if (strcmp(cp, buf) == 0) |
| 396 | goto found; |
| 397 | } |
| 398 | |
| 399 | use_bitbucket: |
| 400 | printf("update_intrname: counting %s irq%d as %s\n", name, intr, |
| 401 | intrnames); |
| 402 | name_index = 0; |
| 403 | found: |
| 404 | intr_countp[intr] = &intrcnt[name_index]; |
| 405 | } |
| 406 | |
| 407 | /* |
| 408 | * NOTE! intr_handler[] is only used for FAST interrupts, the *vector.s |
| 409 | * code ignores it for normal interrupts. |
| 410 | */ |
| 411 | int |
| 412 | icu_setup(int intr, inthand2_t *handler, void *arg, int flags) |
| 413 | { |
| 414 | #if defined(FAST_HI) && defined(APIC_IO) |
| 415 | int select; /* the select register is 8 bits */ |
| 416 | int vector; |
| 417 | u_int32_t value; /* the window register is 32 bits */ |
| 418 | #endif /* FAST_HI */ |
| 419 | u_long ef; |
| 420 | |
| 421 | #if defined(APIC_IO) |
| 422 | if ((u_int)intr >= ICU_LEN) /* no 8259 SLAVE to ignore */ |
| 423 | #else |
| 424 | if ((u_int)intr >= ICU_LEN || intr == ICU_SLAVEID) |
| 425 | #endif /* APIC_IO */ |
| 426 | return (EINVAL); |
| 427 | if (intr_handler[intr] != isa_strayintr) |
| 428 | return (EBUSY); |
| 429 | |
| 430 | ef = read_eflags(); |
| 431 | cpu_disable_intr(); /* YYY */ |
| 432 | intr_handler[intr] = handler; |
| 433 | intr_unit[intr] = arg; |
| 434 | #if 0 |
| 435 | /* YYY fast ints supported and mp protected but ... */ |
| 436 | flags &= ~INTR_FAST; |
| 437 | #endif |
| 438 | #if defined(FAST_HI) && defined(APIC_IO) |
| 439 | if (flags & INTR_FAST) { |
| 440 | /* |
| 441 | * Install a spurious interrupt in the low space in case |
| 442 | * the IO apic is not properly reprogrammed. |
| 443 | */ |
| 444 | vector = TPR_SLOW_INTS + intr; |
| 445 | setidt(vector, isa_wrongintr, |
| 446 | SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); |
| 447 | vector = TPR_FAST_INTS + intr; |
| 448 | setidt(vector, fastintr[intr], |
| 449 | SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); |
| 450 | } else { |
| 451 | vector = TPR_SLOW_INTS + intr; |
| 452 | #ifdef APIC_INTR_REORDER |
| 453 | #ifdef APIC_INTR_HIGHPRI_CLOCK |
| 454 | /* XXX: Hack (kludge?) for more accurate clock. */ |
| 455 | if (intr == apic_8254_intr || intr == 8) { |
| 456 | vector = TPR_FAST_INTS + intr; |
| 457 | } |
| 458 | #endif |
| 459 | #endif |
| 460 | setidt(vector, slowintr[intr], |
| 461 | SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); |
| 462 | } |
| 463 | #ifdef APIC_INTR_REORDER |
| 464 | set_lapic_isrloc(intr, vector); |
| 465 | #endif |
| 466 | /* |
| 467 | * Reprogram the vector in the IO APIC. |
| 468 | * |
| 469 | * XXX EOI/mask a pending (stray) interrupt on the old vector? |
| 470 | */ |
| 471 | if (int_to_apicintpin[intr].ioapic >= 0) { |
| 472 | select = int_to_apicintpin[intr].redirindex; |
| 473 | value = io_apic_read(int_to_apicintpin[intr].ioapic, |
| 474 | select) & ~IOART_INTVEC; |
| 475 | io_apic_write(int_to_apicintpin[intr].ioapic, |
| 476 | select, value | vector); |
| 477 | } |
| 478 | #else |
| 479 | setidt(ICU_OFFSET + intr, |
| 480 | flags & INTR_FAST ? fastintr[intr] : slowintr[intr], |
| 481 | SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); |
| 482 | #endif /* FAST_HI && APIC_IO */ |
| 483 | INTREN(1 << intr); |
| 484 | write_eflags(ef); |
| 485 | return (0); |
| 486 | } |
| 487 | |
| 488 | int |
| 489 | icu_unset(intr, handler) |
| 490 | int intr; |
| 491 | inthand2_t *handler; |
| 492 | { |
| 493 | u_long ef; |
| 494 | |
| 495 | if ((u_int)intr >= ICU_LEN || handler != intr_handler[intr]) { |
| 496 | printf("icu_unset: invalid handler %d %p/%p\n", intr, handler, |
| 497 | (((u_int)intr >= ICU_LEN) ? (void *)-1 : intr_handler[intr])); |
| 498 | return (EINVAL); |
| 499 | } |
| 500 | |
| 501 | INTRDIS(1 << intr); |
| 502 | ef = read_eflags(); |
| 503 | cpu_disable_intr(); /* YYY */ |
| 504 | intr_countp[intr] = &intrcnt[1 + intr]; |
| 505 | intr_handler[intr] = isa_strayintr; |
| 506 | intr_unit[intr] = &intr_unit[intr]; |
| 507 | #ifdef FAST_HI_XXX |
| 508 | /* XXX how do I re-create dvp here? */ |
| 509 | setidt(flags & INTR_FAST ? TPR_FAST_INTS + intr : TPR_SLOW_INTS + intr, |
| 510 | slowintr[intr], SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); |
| 511 | #else /* FAST_HI */ |
| 512 | #ifdef APIC_INTR_REORDER |
| 513 | set_lapic_isrloc(intr, ICU_OFFSET + intr); |
| 514 | #endif |
| 515 | setidt(ICU_OFFSET + intr, slowintr[intr], SDT_SYS386IGT, SEL_KPL, |
| 516 | GSEL(GCODE_SEL, SEL_KPL)); |
| 517 | #endif /* FAST_HI */ |
| 518 | write_eflags(ef); |
| 519 | return (0); |
| 520 | } |
| 521 | |
| 522 | |
| 523 | /* The following notice applies beyond this point in the file */ |
| 524 | |
| 525 | /* |
| 526 | * Copyright (c) 1997, Stefan Esser <se@freebsd.org> |
| 527 | * All rights reserved. |
| 528 | * |
| 529 | * Redistribution and use in source and binary forms, with or without |
| 530 | * modification, are permitted provided that the following conditions |
| 531 | * are met: |
| 532 | * 1. Redistributions of source code must retain the above copyright |
| 533 | * notice unmodified, this list of conditions, and the following |
| 534 | * disclaimer. |
| 535 | * 2. Redistributions in binary form must reproduce the above copyright |
| 536 | * notice, this list of conditions and the following disclaimer in the |
| 537 | * documentation and/or other materials provided with the distribution. |
| 538 | * |
| 539 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
| 540 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| 541 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
| 542 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
| 543 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
| 544 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 545 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 546 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 547 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| 548 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 549 | * |
| 550 | * $FreeBSD: src/sys/i386/isa/intr_machdep.c,v 1.29.2.5 2001/10/14 06:54:27 luigi Exp $ |
| 551 | * |
| 552 | */ |
| 553 | |
| 554 | typedef struct intrec { |
| 555 | inthand2_t *handler; |
| 556 | void *argument; |
| 557 | struct intrec *next; |
| 558 | char *name; |
| 559 | int intr; |
| 560 | int flags; |
| 561 | lwkt_serialize_t serializer; |
| 562 | volatile int in_handler; |
| 563 | } intrec; |
| 564 | |
| 565 | static intrec *intreclist_head[ICU_LEN]; |
| 566 | |
| 567 | /* |
| 568 | * The interrupt multiplexer calls each of the handlers in turn. A handler |
| 569 | * is called only if we can successfully obtain the interlock, meaning |
| 570 | * (1) we aren't recursed and (2) the handler has not been disabled via |
| 571 | * inthand_disabled(). |
| 572 | * |
| 573 | * XXX the IPL is currently raised as necessary for the handler. However, |
| 574 | * IPLs are not MP safe so the IPL code will be removed when the device |
| 575 | * drivers, BIO, and VM no longer depend on it. |
| 576 | */ |
| 577 | static void |
| 578 | intr_mux(void *arg) |
| 579 | { |
| 580 | intrec **pp; |
| 581 | intrec *p; |
| 582 | |
| 583 | for (pp = arg; (p = *pp) != NULL; pp = &p->next) { |
| 584 | if (p->serializer) { |
| 585 | /* |
| 586 | * New handler dispatch method. Only the serializer |
| 587 | * is used to interlock access. Note that this |
| 588 | * API includes a handler disablement feature. |
| 589 | */ |
| 590 | lwkt_serialize_handler_call(p->serializer, |
| 591 | p->handler, p->argument); |
| 592 | } else { |
| 593 | /* |
| 594 | * Old handlers may expect multiple interrupt |
| 595 | * sources to be masked. We must use a critical |
| 596 | * section. |
| 597 | */ |
| 598 | crit_enter(); |
| 599 | p->handler(p->argument); |
| 600 | crit_exit(); |
| 601 | } |
| 602 | } |
| 603 | } |
| 604 | |
| 605 | /* |
| 606 | * Add an interrupt handler to the linked list hung off of intreclist_head[irq] |
| 607 | * and install a shared interrupt multiplex handler. Install an interrupt |
| 608 | * thread for each interrupt (though FAST interrupts will not use it). |
| 609 | * The preemption procedure checks the CPL. lwkt_preempt() will check |
| 610 | * relative thread priorities for us as long as we properly pass through |
| 611 | * critpri. |
| 612 | * |
| 613 | * The interrupt thread has already been put on the run queue, so if we cannot |
| 614 | * preempt we should force a reschedule. |
| 615 | * |
| 616 | * This preemption check routine is currently empty, but will be used in the |
| 617 | * future to pre-check interrupts for preemptability to avoid the |
| 618 | * inefficiencies of having to instantly block. We used to do a CPL check |
| 619 | * here (otherwise the interrupt thread could preempt even when it wasn't |
| 620 | * supposed to), but with CPLs gone we no longer have to do this. |
| 621 | */ |
| 622 | static void |
| 623 | cpu_intr_preempt(struct thread *td, int critpri) |
| 624 | { |
| 625 | lwkt_preempt(td, critpri); |
| 626 | } |
| 627 | |
| 628 | static int |
| 629 | add_intrdesc(intrec *idesc) |
| 630 | { |
| 631 | int irq = idesc->intr; |
| 632 | intrec *head; |
| 633 | intrec **headp; |
| 634 | |
| 635 | /* |
| 636 | * There are two ways to enter intr_mux(). (1) via the scheduled |
| 637 | * interrupt thread or (2) directly. The thread mechanism is the |
| 638 | * normal mechanism used by SLOW interrupts, while the direct method |
| 639 | * is used by FAST interrupts. |
| 640 | * |
| 641 | * We need to create an interrupt thread if none exists. |
| 642 | */ |
| 643 | if (intr_info[irq].mihandler_installed == 0) { |
| 644 | struct thread *td; |
| 645 | |
| 646 | intr_info[irq].mihandler_installed = 1; |
| 647 | intr_info[irq].irq = irq; |
| 648 | td = register_int(irq, intr_mux, &intreclist_head[irq], idesc->name); |
| 649 | td->td_info.intdata = &intr_info[irq]; |
| 650 | td->td_preemptable = cpu_intr_preempt; |
| 651 | printf("installed MI handler for int %d\n", irq); |
| 652 | } |
| 653 | |
| 654 | headp = &intreclist_head[irq]; |
| 655 | head = *headp; |
| 656 | |
| 657 | /* |
| 658 | * Check exclusion |
| 659 | */ |
| 660 | if (head) { |
| 661 | if ((idesc->flags & INTR_EXCL) || (head->flags & INTR_EXCL)) { |
| 662 | printf("\tdevice combination doesn't support " |
| 663 | "shared irq%d\n", irq); |
| 664 | return (-1); |
| 665 | } |
| 666 | if ((idesc->flags & INTR_FAST) || (head->flags & INTR_FAST)) { |
| 667 | printf("\tdevice combination doesn't support " |
| 668 | "multiple FAST interrupts on IRQ%d\n", irq); |
| 669 | } |
| 670 | } |
| 671 | |
| 672 | /* |
| 673 | * Always install intr_mux as the hard handler so it can deal with |
| 674 | * individual enablement on handlers. |
| 675 | */ |
| 676 | if (head == NULL) { |
| 677 | if (icu_setup(irq, idesc->handler, idesc->argument, idesc->flags) != 0) |
| 678 | return (-1); |
| 679 | update_intrname(irq, idesc->name); |
| 680 | } else if (head->next == NULL) { |
| 681 | icu_unset(irq, head->handler); |
| 682 | if (icu_setup(irq, intr_mux, &intreclist_head[irq], 0) != 0) |
| 683 | return (-1); |
| 684 | if (bootverbose && head->next == NULL) |
| 685 | printf("\tusing shared irq%d.\n", irq); |
| 686 | update_intrname(irq, "mux"); |
| 687 | } |
| 688 | |
| 689 | /* |
| 690 | * Append to the end of the chain. |
| 691 | */ |
| 692 | while (*headp != NULL) |
| 693 | headp = &(*headp)->next; |
| 694 | *headp = idesc; |
| 695 | |
| 696 | return (0); |
| 697 | } |
| 698 | |
| 699 | /* |
| 700 | * Create and activate an interrupt handler descriptor data structure. |
| 701 | * |
| 702 | * The dev_instance pointer is required for resource management, and will |
| 703 | * only be passed through to resource_claim(). |
| 704 | * |
| 705 | * There will be functions that derive a driver and unit name from a |
| 706 | * dev_instance variable, and those functions will be used to maintain the |
| 707 | * interrupt counter label array referenced by systat and vmstat to report |
| 708 | * device interrupt rates (->update_intrlabels). |
| 709 | * |
| 710 | * Add the interrupt handler descriptor data structure created by an |
| 711 | * earlier call of create_intr() to the linked list for its irq. |
| 712 | * |
| 713 | * WARNING: This is an internal function and not to be used by device |
| 714 | * drivers. It is subject to change without notice. |
| 715 | */ |
| 716 | |
| 717 | intrec * |
| 718 | inthand_add(const char *name, int irq, inthand2_t handler, void *arg, |
| 719 | int flags, lwkt_serialize_t serializer) |
| 720 | { |
| 721 | intrec *idesc; |
| 722 | int errcode = -1; |
| 723 | |
| 724 | if ((unsigned)irq >= ICU_LEN) { |
| 725 | printf("create_intr: requested irq%d too high, limit is %d\n", |
| 726 | irq, ICU_LEN -1); |
| 727 | return (NULL); |
| 728 | } |
| 729 | |
| 730 | idesc = malloc(sizeof *idesc, M_DEVBUF, M_WAITOK | M_ZERO); |
| 731 | if (idesc == NULL) |
| 732 | return NULL; |
| 733 | |
| 734 | if (name == NULL) |
| 735 | name = "???"; |
| 736 | idesc->name = malloc(strlen(name) + 1, M_DEVBUF, M_WAITOK); |
| 737 | if (idesc->name == NULL) { |
| 738 | free(idesc, M_DEVBUF); |
| 739 | return NULL; |
| 740 | } |
| 741 | strcpy(idesc->name, name); |
| 742 | |
| 743 | idesc->handler = handler; |
| 744 | idesc->argument = arg; |
| 745 | idesc->intr = irq; |
| 746 | idesc->flags = flags; |
| 747 | idesc->serializer = serializer; |
| 748 | |
| 749 | crit_enter(); |
| 750 | errcode = add_intrdesc(idesc); |
| 751 | crit_exit(); |
| 752 | |
| 753 | if (errcode != 0) { |
| 754 | if (bootverbose) |
| 755 | printf("\tintr_connect(irq%d) failed, result=%d\n", |
| 756 | irq, errcode); |
| 757 | free(idesc->name, M_DEVBUF); |
| 758 | free(idesc, M_DEVBUF); |
| 759 | idesc = NULL; |
| 760 | } |
| 761 | |
| 762 | return (idesc); |
| 763 | } |
| 764 | |
| 765 | /* |
| 766 | * Deactivate and remove the interrupt handler descriptor data connected |
| 767 | * created by an earlier call of intr_connect() from the linked list. |
| 768 | * |
| 769 | * Return the memory held by the interrupt handler descriptor data structure |
| 770 | * to the system. Make sure, the handler is not actively used anymore, before. |
| 771 | */ |
| 772 | int |
| 773 | inthand_remove(intrec *idesc) |
| 774 | { |
| 775 | intrec **hook, *head; |
| 776 | int irq; |
| 777 | |
| 778 | if (idesc == NULL) |
| 779 | return (-1); |
| 780 | |
| 781 | irq = idesc->intr; |
| 782 | crit_enter(); |
| 783 | |
| 784 | /* |
| 785 | * Find and remove the interrupt descriptor. |
| 786 | */ |
| 787 | hook = &intreclist_head[irq]; |
| 788 | while (*hook != idesc) { |
| 789 | if (*hook == NULL) { |
| 790 | crit_exit(); |
| 791 | return(-1); |
| 792 | } |
| 793 | hook = &(*hook)->next; |
| 794 | } |
| 795 | *hook = idesc->next; |
| 796 | |
| 797 | /* |
| 798 | * If the list is now empty, revert the hard vector to the spurious |
| 799 | * interrupt. |
| 800 | */ |
| 801 | head = intreclist_head[irq]; |
| 802 | if (head == NULL) { |
| 803 | /* |
| 804 | * No more interrupts on this irq |
| 805 | */ |
| 806 | icu_unset(irq, idesc->handler); |
| 807 | update_intrname(irq, NULL); |
| 808 | } else if (head->next) { |
| 809 | /* |
| 810 | * This irq is still shared (has at least two handlers) |
| 811 | * (the name should already be set to "mux"). |
| 812 | */ |
| 813 | } else { |
| 814 | /* |
| 815 | * This irq is no longer shared |
| 816 | */ |
| 817 | icu_unset(irq, intr_mux); |
| 818 | icu_setup(irq, head->handler, head->argument, head->flags); |
| 819 | update_intrname(irq, head->name); |
| 820 | } |
| 821 | crit_exit(); |
| 822 | free(idesc, M_DEVBUF); |
| 823 | |
| 824 | return (0); |
| 825 | } |
| 826 | |
| 827 | /* |
| 828 | * ithread_done() |
| 829 | * |
| 830 | * This function is called by an interrupt thread when it has completed |
| 831 | * processing a loop. We re-enable interrupts and interlock with |
| 832 | * ipending. |
| 833 | * |
| 834 | * See kern/kern_intr.c for more information. |
| 835 | */ |
| 836 | void |
| 837 | ithread_done(int irq) |
| 838 | { |
| 839 | struct mdglobaldata *gd = mdcpu; |
| 840 | int mask = 1 << irq; |
| 841 | thread_t td; |
| 842 | |
| 843 | td = gd->mi.gd_curthread; |
| 844 | |
| 845 | KKASSERT(td->td_pri >= TDPRI_CRIT); |
| 846 | lwkt_deschedule_self(td); |
| 847 | INTREN(mask); |
| 848 | if (gd->gd_ipending & mask) { |
| 849 | atomic_clear_int_nonlocked(&gd->gd_ipending, mask); |
| 850 | INTRDIS(mask); |
| 851 | lwkt_schedule_self(td); |
| 852 | } else { |
| 853 | lwkt_switch(); |
| 854 | } |
| 855 | } |
| 856 | |
| 857 | #ifdef SMP |
| 858 | /* |
| 859 | * forward_fast_remote() |
| 860 | * |
| 861 | * This function is called from the receiving end of an IPIQ when a |
| 862 | * remote cpu wishes to forward a fast interrupt to us. All we have to |
| 863 | * do is set the interrupt pending and let the IPI's doreti deal with it. |
| 864 | */ |
| 865 | void |
| 866 | forward_fastint_remote(void *arg) |
| 867 | { |
| 868 | int irq = (int)arg; |
| 869 | struct mdglobaldata *gd = mdcpu; |
| 870 | |
| 871 | atomic_set_int_nonlocked(&gd->gd_fpending, 1 << irq); |
| 872 | atomic_set_int_nonlocked(&gd->mi.gd_reqflags, RQF_INTPEND); |
| 873 | } |
| 874 | |
| 875 | #endif |