nmi.c revision 37050
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 * $Id: intr_machdep.c,v 1.11 1998/05/31 10:53:52 bde Exp $ 38 */ 39 40#include "opt_auto_eoi.h" 41 42#include <sys/param.h> 43#include <sys/systm.h> 44#include <sys/syslog.h> 45#include <machine/ipl.h> 46#include <machine/md_var.h> 47#include <machine/segments.h> 48#if defined(APIC_IO) 49#include <machine/smp.h> 50#include <machine/smptests.h> /** FAST_HI */ 51#endif /* APIC_IO */ 52#include <i386/isa/isa_device.h> 53#ifdef PC98 54#include <pc98/pc98/pc98.h> 55#include <pc98/pc98/pc98_machdep.h> 56#include <pc98/pc98/epsonio.h> 57#else 58#include <i386/isa/isa.h> 59#endif 60#include <i386/isa/icu.h> 61#include "vector.h" 62 63#include <i386/isa/intr_machdep.h> 64#include <sys/interrupt.h> 65#ifdef APIC_IO 66#include <machine/clock.h> 67#endif 68 69/* XXX should be in suitable include files */ 70#ifdef PC98 71#define ICU_IMR_OFFSET 2 /* IO_ICU{1,2} + 2 */ 72#define ICU_SLAVEID 7 73#else 74#define ICU_IMR_OFFSET 1 /* IO_ICU{1,2} + 1 */ 75#define ICU_SLAVEID 2 76#endif 77 78#ifdef APIC_IO 79/* 80 * This is to accommodate "mixed-mode" programming for 81 * motherboards that don't connect the 8254 to the IO APIC. 82 */ 83#define AUTO_EOI_1 84#endif 85 86u_long *intr_countp[ICU_LEN]; 87inthand2_t *intr_handler[ICU_LEN]; 88u_int intr_mask[ICU_LEN]; 89static u_int* intr_mptr[ICU_LEN]; 90void *intr_unit[ICU_LEN]; 91 92static inthand_t *fastintr[ICU_LEN] = { 93 &IDTVEC(fastintr0), &IDTVEC(fastintr1), 94 &IDTVEC(fastintr2), &IDTVEC(fastintr3), 95 &IDTVEC(fastintr4), &IDTVEC(fastintr5), 96 &IDTVEC(fastintr6), &IDTVEC(fastintr7), 97 &IDTVEC(fastintr8), &IDTVEC(fastintr9), 98 &IDTVEC(fastintr10), &IDTVEC(fastintr11), 99 &IDTVEC(fastintr12), &IDTVEC(fastintr13), 100 &IDTVEC(fastintr14), &IDTVEC(fastintr15) 101#if defined(APIC_IO) 102 , &IDTVEC(fastintr16), &IDTVEC(fastintr17), 103 &IDTVEC(fastintr18), &IDTVEC(fastintr19), 104 &IDTVEC(fastintr20), &IDTVEC(fastintr21), 105 &IDTVEC(fastintr22), &IDTVEC(fastintr23) 106#endif /* APIC_IO */ 107}; 108 109static inthand_t *slowintr[ICU_LEN] = { 110 &IDTVEC(intr0), &IDTVEC(intr1), &IDTVEC(intr2), &IDTVEC(intr3), 111 &IDTVEC(intr4), &IDTVEC(intr5), &IDTVEC(intr6), &IDTVEC(intr7), 112 &IDTVEC(intr8), &IDTVEC(intr9), &IDTVEC(intr10), &IDTVEC(intr11), 113 &IDTVEC(intr12), &IDTVEC(intr13), &IDTVEC(intr14), &IDTVEC(intr15) 114#if defined(APIC_IO) 115 , &IDTVEC(intr16), &IDTVEC(intr17), &IDTVEC(intr18), &IDTVEC(intr19), 116 &IDTVEC(intr20), &IDTVEC(intr21), &IDTVEC(intr22), &IDTVEC(intr23) 117#endif /* APIC_IO */ 118}; 119 120static ointhand2_t isa_strayintr; 121 122#ifdef PC98 123#define NMI_PARITY 0x04 124#define NMI_EPARITY 0x02 125#else 126#define NMI_PARITY (1 << 7) 127#define NMI_IOCHAN (1 << 6) 128#define ENMI_WATCHDOG (1 << 7) 129#define ENMI_BUSTIMER (1 << 6) 130#define ENMI_IOSTATUS (1 << 5) 131#endif 132 133/* 134 * Handle a NMI, possibly a machine check. 135 * return true to panic system, false to ignore. 136 */ 137int 138isa_nmi(cd) 139 int cd; 140{ 141#ifdef PC98 142 int port = inb(0x33); 143 if (epson_machine_id == 0x20) 144 epson_outb(0xc16, epson_inb(0xc16) | 0x1); 145 if (port & NMI_PARITY) { 146 panic("BASE RAM parity error, likely hardware failure."); 147 } else if (port & NMI_EPARITY) { 148 panic("EXTENDED RAM parity error, likely hardware failure."); 149 } else { 150 printf("\nNMI Resume ??\n"); 151 return(0); 152 } 153#else /* IBM-PC */ 154 int isa_port = inb(0x61); 155 int eisa_port = inb(0x461); 156 157 if (isa_port & NMI_PARITY) 158 panic("RAM parity error, likely hardware failure."); 159 160 if (isa_port & NMI_IOCHAN) 161 panic("I/O channel check, likely hardware failure."); 162 163 /* 164 * On a real EISA machine, this will never happen. However it can 165 * happen on ISA machines which implement XT style floating point 166 * error handling (very rare). Save them from a meaningless panic. 167 */ 168 if (eisa_port == 0xff) 169 return(0); 170 171 if (eisa_port & ENMI_WATCHDOG) 172 panic("EISA watchdog timer expired, likely hardware failure."); 173 174 if (eisa_port & ENMI_BUSTIMER) 175 panic("EISA bus timeout, likely hardware failure."); 176 177 if (eisa_port & ENMI_IOSTATUS) 178 panic("EISA I/O port status error."); 179 180 printf("\nNMI ISA %x, EISA %x\n", isa_port, eisa_port); 181 return(0); 182#endif 183} 184 185/* 186 * Fill in default interrupt table (in case of spuruious interrupt 187 * during configuration of kernel, setup interrupt control unit 188 */ 189void 190isa_defaultirq() 191{ 192 int i; 193 194 /* icu vectors */ 195 for (i = 0; i < ICU_LEN; i++) 196 icu_unset(i, (inthand2_t *)NULL); 197 198 /* initialize 8259's */ 199 outb(IO_ICU1, 0x11); /* reset; program device, four bytes */ 200 201 outb(IO_ICU1+ICU_IMR_OFFSET, NRSVIDT); /* starting at this vector index */ 202 outb(IO_ICU1+ICU_IMR_OFFSET, IRQ_SLAVE); /* slave on line 7 */ 203#ifdef PC98 204#ifdef AUTO_EOI_1 205 outb(IO_ICU1+ICU_IMR_OFFSET, 0x1f); /* (master) auto EOI, 8086 mode */ 206#else 207 outb(IO_ICU1+ICU_IMR_OFFSET, 0x1d); /* (master) 8086 mode */ 208#endif 209#else /* IBM-PC */ 210#ifdef AUTO_EOI_1 211 outb(IO_ICU1+ICU_IMR_OFFSET, 2 | 1); /* auto EOI, 8086 mode */ 212#else 213 outb(IO_ICU1+ICU_IMR_OFFSET, 1); /* 8086 mode */ 214#endif 215#endif /* PC98 */ 216 outb(IO_ICU1+ICU_IMR_OFFSET, 0xff); /* leave interrupts masked */ 217 outb(IO_ICU1, 0x0a); /* default to IRR on read */ 218#ifndef PC98 219 outb(IO_ICU1, 0xc0 | (3 - 1)); /* pri order 3-7, 0-2 (com2 first) */ 220#endif /* !PC98 */ 221 222 outb(IO_ICU2, 0x11); /* reset; program device, four bytes */ 223 outb(IO_ICU2+ICU_IMR_OFFSET, NRSVIDT+8); /* staring at this vector index */ 224 outb(IO_ICU2+ICU_IMR_OFFSET, ICU_SLAVEID); /* my slave id is 7 */ 225#ifdef PC98 226 outb(IO_ICU2+ICU_IMR_OFFSET,9); /* 8086 mode */ 227#else /* IBM-PC */ 228#ifdef AUTO_EOI_2 229 outb(IO_ICU2+ICU_IMR_OFFSET, 2 | 1); /* auto EOI, 8086 mode */ 230#else 231 outb(IO_ICU2+ICU_IMR_OFFSET,1); /* 8086 mode */ 232#endif 233#endif /* PC98 */ 234 outb(IO_ICU2+ICU_IMR_OFFSET, 0xff); /* leave interrupts masked */ 235 outb(IO_ICU2, 0x0a); /* default to IRR on read */ 236} 237 238/* 239 * Caught a stray interrupt, notify 240 */ 241static void 242isa_strayintr(d) 243 int d; 244{ 245 246 /* DON'T BOTHER FOR NOW! */ 247 /* for some reason, we get bursts of intr #7, even if not enabled! */ 248 /* 249 * Well the reason you got bursts of intr #7 is because someone 250 * raised an interrupt line and dropped it before the 8259 could 251 * prioritize it. This is documented in the intel data book. This 252 * means you have BAD hardware! I have changed this so that only 253 * the first 5 get logged, then it quits logging them, and puts 254 * out a special message. rgrimes 3/25/1993 255 */ 256 /* 257 * XXX TODO print a different message for #7 if it is for a 258 * glitch. Glitches can be distinguished from real #7's by 259 * testing that the in-service bit is _not_ set. The test 260 * must be done before sending an EOI so it can't be done if 261 * we are using AUTO_EOI_1. 262 */ 263 if (intrcnt[NR_DEVICES + d] <= 5) 264 log(LOG_ERR, "stray irq %d\n", d); 265 if (intrcnt[NR_DEVICES + d] == 5) 266 log(LOG_CRIT, 267 "too many stray irq %d's; not logging any more\n", d); 268} 269 270/* 271 * Return a bitmap of the current interrupt requests. This is 8259-specific 272 * and is only suitable for use at probe time. 273 */ 274intrmask_t 275isa_irq_pending() 276{ 277 u_char irr1; 278 u_char irr2; 279 280 irr1 = inb(IO_ICU1); 281 irr2 = inb(IO_ICU2); 282 return ((irr2 << 8) | irr1); 283} 284 285int 286update_intr_masks(void) 287{ 288 int intr, n=0; 289 u_int mask,*maskptr; 290 291 for (intr=0; intr < ICU_LEN; intr ++) { 292#if defined(APIC_IO) 293 /* no 8259 SLAVE to ignore */ 294#else 295 if (intr==ICU_SLAVEID) continue; /* ignore 8259 SLAVE output */ 296#endif /* APIC_IO */ 297 maskptr = intr_mptr[intr]; 298 if (!maskptr) continue; 299 *maskptr |= 1 << intr; 300 mask = *maskptr; 301 if (mask != intr_mask[intr]) { 302#if 0 303 printf ("intr_mask[%2d] old=%08x new=%08x ptr=%p.\n", 304 intr, intr_mask[intr], mask, maskptr); 305#endif 306 intr_mask[intr]=mask; 307 n++; 308 } 309 310 } 311 return (n); 312} 313 314/* 315 * The find_device_id function is only required because of the way the 316 * device names are currently stored for reporting in systat or vmstat. 317 * In fact, those programs should be modified to use the sysctl interface 318 * to obtain a list of driver names by traversing intreclist_head[irq]. 319 */ 320static int 321find_device_id(int irq) 322{ 323 char buf[16]; 324 char *cp; 325 int free_id, id; 326 327 sprintf(buf, "pci irq%d", irq); 328 cp = intrnames; 329 /* default to 0, which corresponds to clk0 */ 330 free_id = 0; 331 332 for (id = 0; id < NR_DEVICES; id++) { 333 if (strcmp(cp, buf) == 0) 334 return (id); 335 if (free_id == 0 && strcmp(cp, "pci irqnn") == 0) 336 free_id = id; 337 while (*cp++ != '\0'); 338 } 339#if 0 340 if (free_id == 0) { 341 /* 342 * All pci irq counters are in use, perhaps because config 343 * is old so there aren't any. Abuse the clk0 counter. 344 */ 345 printf("\tcounting shared irq%d as clk0 irq\n", irq); 346 } 347#endif 348 return (free_id); 349} 350 351void 352update_intrname(int intr, int device_id) 353{ 354 char *cp; 355 int id; 356 357 if (device_id == -1) 358 device_id = find_device_id(intr); 359 360 if ((u_int)device_id >= NR_DEVICES) 361 return; 362 363 intr_countp[intr] = &intrcnt[device_id]; 364 365 for (cp = intrnames, id = 0; id <= device_id; id++) 366 while (*cp++ != '\0') 367 ; 368 if (cp > eintrnames) 369 return; 370 if (intr < 10) { 371 cp[-3] = intr + '0'; 372 cp[-2] = ' '; 373 } else if (intr < 20) { 374 cp[-3] = '1'; 375 cp[-2] = intr - 10 + '0'; 376 } else { 377 cp[-3] = '2'; 378 cp[-2] = intr - 20 + '0'; 379 } 380} 381 382 383int 384icu_setup(int intr, inthand2_t *handler, void *arg, u_int *maskptr, int flags) 385{ 386#ifdef FAST_HI 387 int select; /* the select register is 8 bits */ 388 int vector; 389 u_int32_t value; /* the window register is 32 bits */ 390#endif /* FAST_HI */ 391 u_long ef; 392 u_int mask = (maskptr ? *maskptr : 0); 393 394#if defined(APIC_IO) 395 if ((u_int)intr >= ICU_LEN) /* no 8259 SLAVE to ignore */ 396#else 397 if ((u_int)intr >= ICU_LEN || intr == ICU_SLAVEID) 398#endif /* APIC_IO */ 399 if (intr_handler[intr] != isa_strayintr) 400 return (EBUSY); 401 402 ef = read_eflags(); 403 disable_intr(); 404 intr_handler[intr] = handler; 405 intr_mptr[intr] = maskptr; 406 intr_mask[intr] = mask | (1 << intr); 407 intr_unit[intr] = arg; 408#ifdef FAST_HI 409 if (flags & INTR_FAST) { 410 vector = TPR_FAST_INTS + intr; 411 setidt(vector, fastintr[intr], 412 SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); 413 } 414 else { 415 vector = TPR_SLOW_INTS + intr; 416#ifdef APIC_INTR_REORDER 417#ifdef APIC_INTR_HIGHPRI_CLOCK 418 /* XXX: Hack (kludge?) for more accurate clock. */ 419 if (intr == apic_8254_intr || intr == 8) { 420 vector = TPR_FAST_INTS + intr; 421 } 422#endif 423#endif 424 setidt(vector, slowintr[intr], 425 SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); 426 } 427#ifdef APIC_INTR_REORDER 428 set_lapic_isrloc(intr, vector); 429#endif 430 /* 431 * XXX MULTIPLE_IOAPICSXXX 432 * Reprogram the vector in the IO APIC. 433 */ 434 select = (intr * 2) + IOAPIC_REDTBL0; 435 value = io_apic_read(0, select) & ~IOART_INTVEC; 436 io_apic_write(0, select, value | vector); 437#else 438 setidt(ICU_OFFSET + intr, 439 flags & INTR_FAST ? fastintr[intr] : slowintr[intr], 440 SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); 441#endif /* FAST_HI */ 442 INTREN(1 << intr); 443 MPINTR_UNLOCK(); 444 write_eflags(ef); 445 return (0); 446} 447 448void 449register_imask(dvp, mask) 450 struct isa_device *dvp; 451 u_int mask; 452{ 453 if (dvp->id_alive && dvp->id_irq) { 454 int intr; 455 456 intr = ffs(dvp->id_irq) - 1; 457 intr_mask[intr] = mask | (1 <<intr); 458 } 459 (void) update_intr_masks(); 460} 461 462int 463icu_unset(intr, handler) 464 int intr; 465 inthand2_t *handler; 466{ 467 u_long ef; 468 469 if ((u_int)intr >= ICU_LEN || handler != intr_handler[intr]) 470 return (EINVAL); 471 472 INTRDIS(1 << intr); 473 ef = read_eflags(); 474 disable_intr(); 475 intr_countp[intr] = &intrcnt[NR_DEVICES + intr]; 476 intr_handler[intr] = isa_strayintr; 477 intr_mptr[intr] = NULL; 478 intr_mask[intr] = HWI_MASK | SWI_MASK; 479 intr_unit[intr] = intr; 480#ifdef FAST_HI_XXX 481 /* XXX how do I re-create dvp here? */ 482 setidt(flags & INTR_FAST ? TPR_FAST_INTS + intr : TPR_SLOW_INTS + intr, 483 slowintr[intr], SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); 484#else /* FAST_HI */ 485#ifdef APIC_INTR_REORDER 486 set_lapic_isrloc(intr, ICU_OFFSET + intr); 487#endif 488 setidt(ICU_OFFSET + intr, slowintr[intr], SDT_SYS386IGT, SEL_KPL, 489 GSEL(GCODE_SEL, SEL_KPL)); 490#endif /* FAST_HI */ 491 MPINTR_UNLOCK(); 492 write_eflags(ef); 493 return (0); 494} 495