intr_machdep.c revision 122572
1121982Sjhb/*- 2121982Sjhb * Copyright (c) 2003 John Baldwin <jhb@FreeBSD.org> 3121982Sjhb * All rights reserved. 4121982Sjhb * 5121982Sjhb * Redistribution and use in source and binary forms, with or without 6121982Sjhb * modification, are permitted provided that the following conditions 7121982Sjhb * are met: 8121982Sjhb * 1. Redistributions of source code must retain the above copyright 9121982Sjhb * notice, this list of conditions and the following disclaimer. 10121982Sjhb * 2. Redistributions in binary form must reproduce the above copyright 11121982Sjhb * notice, this list of conditions and the following disclaimer in the 12121982Sjhb * documentation and/or other materials provided with the distribution. 13121982Sjhb * 3. Neither the name of the author nor the names of any co-contributors 14121982Sjhb * may be used to endorse or promote products derived from this software 15121982Sjhb * without specific prior written permission. 16121982Sjhb * 17121982Sjhb * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18121982Sjhb * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19121982Sjhb * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20121982Sjhb * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21121982Sjhb * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22121982Sjhb * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23121982Sjhb * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24121982Sjhb * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25121982Sjhb * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26121982Sjhb * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27121982Sjhb * SUCH DAMAGE. 28121982Sjhb * 29121982Sjhb * $FreeBSD: head/sys/i386/i386/intr_machdep.c 122572 2003-11-12 18:13:57Z jhb $ 30121982Sjhb */ 31121982Sjhb 32121982Sjhb/* 33121982Sjhb * Machine dependent interrupt code for i386. For the i386, we have to 34121982Sjhb * deal with different PICs. Thus, we use the passed in vector to lookup 35121982Sjhb * an interrupt source associated with that vector. The interrupt source 36121982Sjhb * describes which PIC the source belongs to and includes methods to handle 37121982Sjhb * that source. 38121982Sjhb */ 39121982Sjhb 40121982Sjhb#include "opt_ddb.h" 41121982Sjhb 42121982Sjhb#include <sys/param.h> 43121982Sjhb#include <sys/bus.h> 44121982Sjhb#include <sys/interrupt.h> 45121982Sjhb#include <sys/lock.h> 46121982Sjhb#include <sys/ktr.h> 47121982Sjhb#include <sys/kernel.h> 48121982Sjhb#include <sys/mutex.h> 49121982Sjhb#include <sys/proc.h> 50121982Sjhb#include <sys/syslog.h> 51121982Sjhb#include <sys/systm.h> 52122572Sjhb#include <machine/clock.h> 53121982Sjhb#include <machine/intr_machdep.h> 54121982Sjhb#ifdef DDB 55121982Sjhb#include <ddb/ddb.h> 56121982Sjhb#endif 57121982Sjhb 58121982Sjhb#define MAX_STRAY_LOG 5 59121982Sjhb 60121982Sjhbtypedef void (*mask_fn)(int vector); 61121982Sjhb 62121982Sjhbstatic int intrcnt_index; 63121982Sjhbstatic struct intsrc *interrupt_sources[NUM_IO_INTS]; 64121982Sjhbstatic struct mtx intr_table_lock; 65121982Sjhb 66121982Sjhbstatic void intr_init(void *__dummy); 67121982Sjhbstatic void intrcnt_setname(const char *name, int index); 68121982Sjhbstatic void intrcnt_updatename(struct intsrc *is); 69121982Sjhbstatic void intrcnt_register(struct intsrc *is); 70121982Sjhb 71121982Sjhb/* 72121982Sjhb * Register a new interrupt source with the global interrupt system. 73121982Sjhb * The global interrupts need to be disabled when this function is 74121982Sjhb * called. 75121982Sjhb */ 76121982Sjhbint 77121982Sjhbintr_register_source(struct intsrc *isrc) 78121982Sjhb{ 79121982Sjhb int error, vector; 80121982Sjhb 81121982Sjhb vector = isrc->is_pic->pic_vector(isrc); 82121982Sjhb if (interrupt_sources[vector] != NULL) 83121982Sjhb return (EEXIST); 84121982Sjhb /* 85121982Sjhb * Ok, so this is kind of a nasty optimization that only works 86121982Sjhb * because sizeof(int) == sizeof(void *) on i386. If we passed 87121982Sjhb * in the actual vector to ithread_create and then used wrapper 88121982Sjhb * functions for disable_intsrc and enable_intsrc, then we'd 89121982Sjhb * have to go lookup in the table everytime we enabled/disabled 90121982Sjhb * the interrupt source. That involves looking at a lock, etc. 91121982Sjhb * and is just ugly. Instead, we cast the pointer to the intsrc 92121982Sjhb * to an int (yuck) and pass in the actual PIC methods meaning 93121982Sjhb * that when we enable/disable an interrupt we call the PIC 94121982Sjhb * methods directly. 95121982Sjhb */ 96121982Sjhb error = ithread_create(&isrc->is_ithread, (intptr_t)isrc, 0, 97121982Sjhb (mask_fn)isrc->is_pic->pic_disable_source, 98121982Sjhb (mask_fn)isrc->is_pic->pic_enable_source, "irq%d:", vector); 99121982Sjhb if (error) 100121982Sjhb return (error); 101121982Sjhb mtx_lock_spin(&intr_table_lock); 102121982Sjhb if (interrupt_sources[vector] != NULL) { 103121982Sjhb mtx_unlock_spin(&intr_table_lock); 104121982Sjhb ithread_destroy(isrc->is_ithread); 105121982Sjhb return (EEXIST); 106121982Sjhb } 107121982Sjhb intrcnt_register(isrc); 108121982Sjhb interrupt_sources[vector] = isrc; 109121982Sjhb mtx_unlock_spin(&intr_table_lock); 110121982Sjhb return (0); 111121982Sjhb} 112121982Sjhb 113121982Sjhbstruct intsrc * 114121982Sjhbintr_lookup_source(int vector) 115121982Sjhb{ 116121982Sjhb 117121982Sjhb return (interrupt_sources[vector]); 118121982Sjhb} 119121982Sjhb 120121982Sjhbint 121121982Sjhbintr_add_handler(const char *name, int vector, driver_intr_t handler, 122121982Sjhb void *arg, enum intr_type flags, void **cookiep) 123121982Sjhb{ 124121982Sjhb struct intsrc *isrc; 125121982Sjhb int error; 126121982Sjhb 127121982Sjhb isrc = intr_lookup_source(vector); 128121982Sjhb if (isrc == NULL) 129121982Sjhb return (EINVAL); 130121982Sjhb error = ithread_add_handler(isrc->is_ithread, name, handler, arg, 131121982Sjhb ithread_priority(flags), flags, cookiep); 132121982Sjhb if (error == 0) { 133121982Sjhb intrcnt_updatename(isrc); 134121982Sjhb isrc->is_pic->pic_enable_intr(isrc); 135121982Sjhb isrc->is_pic->pic_enable_source(isrc); 136121982Sjhb } 137121982Sjhb return (error); 138121982Sjhb} 139121982Sjhb 140121982Sjhbint 141121982Sjhbintr_remove_handler(void *cookie) 142121982Sjhb{ 143121982Sjhb int error; 144121982Sjhb 145121982Sjhb error = ithread_remove_handler(cookie); 146121982Sjhb#ifdef XXX 147121982Sjhb if (error == 0) 148121982Sjhb intrcnt_updatename(/* XXX */); 149121982Sjhb#endif 150121982Sjhb return (error); 151121982Sjhb} 152121982Sjhb 153121982Sjhbvoid 154121982Sjhbintr_execute_handlers(struct intsrc *isrc, struct intrframe *iframe) 155121982Sjhb{ 156122572Sjhb struct thread *td; 157121982Sjhb struct ithd *it; 158121982Sjhb struct intrhand *ih; 159121982Sjhb int error, vector; 160121982Sjhb 161122572Sjhb td = curthread; 162122572Sjhb td->td_intr_nesting_level++; 163122572Sjhb 164121982Sjhb /* 165121982Sjhb * We count software interrupts when we process them. The 166121982Sjhb * code here follows previous practice, but there's an 167121982Sjhb * argument for counting hardware interrupts when they're 168121982Sjhb * processed too. 169121982Sjhb */ 170121982Sjhb atomic_add_long(isrc->is_count, 1); 171121982Sjhb atomic_add_int(&cnt.v_intr, 1); 172121982Sjhb 173122572Sjhb it = isrc->is_ithread; 174122572Sjhb ih = TAILQ_FIRST(&it->it_handlers); 175122572Sjhb 176121982Sjhb /* 177122572Sjhb * XXX: We assume that IRQ 0 is only used for the ISA timer 178122572Sjhb * device (clk). 179121982Sjhb */ 180122572Sjhb vector = isrc->is_pic->pic_vector(isrc); 181122572Sjhb if (vector == 0) 182122572Sjhb clkintr_pending = 1; 183122572Sjhb 184121982Sjhb critical_enter(); 185122572Sjhb if (ih != NULL && ih->ih_flags & IH_FAST) { 186122572Sjhb /* 187122572Sjhb * Execute fast interrupt handlers directly. 188122572Sjhb * To support clock handlers, if a handler registers 189122572Sjhb * with a NULL argument, then we pass it a pointer to 190122572Sjhb * a trapframe as its argument. 191122572Sjhb */ 192121982Sjhb TAILQ_FOREACH(ih, &it->it_handlers, ih_next) { 193121982Sjhb MPASS(ih->ih_flags & IH_FAST); 194121982Sjhb CTR3(KTR_INTR, "%s: executing handler %p(%p)", 195121982Sjhb __func__, ih->ih_handler, 196121982Sjhb ih->ih_argument == NULL ? iframe : 197121982Sjhb ih->ih_argument); 198121982Sjhb if (ih->ih_argument == NULL) 199121982Sjhb ih->ih_handler(iframe); 200121982Sjhb else 201121982Sjhb ih->ih_handler(ih->ih_argument); 202121982Sjhb } 203122572Sjhb isrc->is_pic->pic_eoi_source(isrc); 204121982Sjhb error = 0; 205122572Sjhb } else { 206122572Sjhb /* 207122572Sjhb * For stray and threaded interrupts, we mask and EOI the 208122572Sjhb * source. 209122572Sjhb */ 210122572Sjhb isrc->is_pic->pic_disable_source(isrc); 211122572Sjhb isrc->is_pic->pic_eoi_source(isrc); 212122572Sjhb if (ih == NULL) 213122572Sjhb error = EINVAL; 214122572Sjhb else 215122572Sjhb error = ithread_schedule(it, !cold); 216122572Sjhb } 217121982Sjhb critical_exit(); 218121982Sjhb if (error == EINVAL) { 219121982Sjhb atomic_add_long(isrc->is_straycount, 1); 220121982Sjhb if (*isrc->is_straycount < MAX_STRAY_LOG) 221121982Sjhb log(LOG_ERR, "stray irq%d\n", vector); 222121982Sjhb else if (*isrc->is_straycount == MAX_STRAY_LOG) 223121982Sjhb log(LOG_CRIT, 224121982Sjhb "too many stray irq %d's: not logging anymore\n", 225121982Sjhb vector); 226121982Sjhb } 227122572Sjhb td->td_intr_nesting_level--; 228121982Sjhb} 229121982Sjhb 230121982Sjhbvoid 231121982Sjhbintr_resume(void) 232121982Sjhb{ 233121982Sjhb struct intsrc **isrc; 234121982Sjhb int i; 235121982Sjhb 236121982Sjhb mtx_lock_spin(&intr_table_lock); 237121982Sjhb for (i = 0, isrc = interrupt_sources; i < NUM_IO_INTS; i++, isrc++) 238121982Sjhb if (*isrc != NULL && (*isrc)->is_pic->pic_resume != NULL) 239121982Sjhb (*isrc)->is_pic->pic_resume(*isrc); 240121982Sjhb mtx_unlock_spin(&intr_table_lock); 241121982Sjhb} 242121982Sjhb 243121982Sjhbvoid 244121982Sjhbintr_suspend(void) 245121982Sjhb{ 246121982Sjhb struct intsrc **isrc; 247121982Sjhb int i; 248121982Sjhb 249121982Sjhb mtx_lock_spin(&intr_table_lock); 250121982Sjhb for (i = 0, isrc = interrupt_sources; i < NUM_IO_INTS; i++, isrc++) 251121982Sjhb if (*isrc != NULL && (*isrc)->is_pic->pic_suspend != NULL) 252121982Sjhb (*isrc)->is_pic->pic_suspend(*isrc); 253121982Sjhb mtx_unlock_spin(&intr_table_lock); 254121982Sjhb} 255121982Sjhb 256121982Sjhbstatic void 257121982Sjhbintrcnt_setname(const char *name, int index) 258121982Sjhb{ 259121982Sjhb 260121982Sjhb snprintf(intrnames + (MAXCOMLEN + 1) * index, MAXCOMLEN + 1, "%-*s", 261121982Sjhb MAXCOMLEN, name); 262121982Sjhb} 263121982Sjhb 264121982Sjhbstatic void 265121982Sjhbintrcnt_updatename(struct intsrc *is) 266121982Sjhb{ 267121982Sjhb 268121982Sjhb intrcnt_setname(is->is_ithread->it_td->td_proc->p_comm, is->is_index); 269121982Sjhb} 270121982Sjhb 271121982Sjhbstatic void 272121982Sjhbintrcnt_register(struct intsrc *is) 273121982Sjhb{ 274121982Sjhb char straystr[MAXCOMLEN + 1]; 275121982Sjhb 276121982Sjhb /* mtx_assert(&intr_table_lock, MA_OWNED); */ 277121982Sjhb KASSERT(is->is_ithread != NULL, ("%s: isrc with no ithread", __func__)); 278121982Sjhb is->is_index = intrcnt_index; 279121982Sjhb intrcnt_index += 2; 280121982Sjhb snprintf(straystr, MAXCOMLEN + 1, "stray irq%d", 281121982Sjhb is->is_pic->pic_vector(is)); 282121982Sjhb intrcnt_updatename(is); 283121982Sjhb is->is_count = &intrcnt[is->is_index]; 284121982Sjhb intrcnt_setname(straystr, is->is_index + 1); 285121982Sjhb is->is_straycount = &intrcnt[is->is_index + 1]; 286121982Sjhb} 287121982Sjhb 288121982Sjhbstatic void 289121982Sjhbintr_init(void *dummy __unused) 290121982Sjhb{ 291121982Sjhb 292121982Sjhb intrcnt_setname("???", 0); 293121982Sjhb intrcnt_index = 1; 294121982Sjhb mtx_init(&intr_table_lock, "intr table", NULL, MTX_SPIN); 295121982Sjhb} 296121982SjhbSYSINIT(intr_init, SI_SUB_INTR, SI_ORDER_FIRST, intr_init, NULL) 297121982Sjhb 298121982Sjhb#ifdef DDB 299121982Sjhb/* 300121982Sjhb * Dump data about interrupt handlers 301121982Sjhb */ 302121982SjhbDB_SHOW_COMMAND(irqs, db_show_irqs) 303121982Sjhb{ 304121982Sjhb struct intsrc **isrc; 305121982Sjhb int i, quit, verbose; 306121982Sjhb 307121982Sjhb quit = 0; 308121982Sjhb if (strcmp(modif, "v") == 0) 309121982Sjhb verbose = 1; 310121982Sjhb else 311121982Sjhb verbose = 0; 312121982Sjhb isrc = interrupt_sources; 313121982Sjhb db_setup_paging(db_simple_pager, &quit, DB_LINES_PER_PAGE); 314121982Sjhb for (i = 0; i < NUM_IO_INTS && !quit; i++, isrc++) 315121982Sjhb if (*isrc != NULL) 316121982Sjhb db_dump_ithread((*isrc)->is_ithread, verbose); 317121982Sjhb} 318121982Sjhb#endif 319