From 1db3b7489bd952d6f8e74c7088f8cc7e46beca57 Mon Sep 17 00:00:00 2001 From: fvdl Date: Fri, 22 Nov 2002 15:05:23 +0000 Subject: [PATCH] New files for the changed interrupt code. i8259.c deals with some of the old ('legacy') i8259 code, intr.c now contains the bulk of the interrupt establish/disestablish code. --- sys/arch/i386/i386/i8259.c | 247 +++++++++++++ sys/arch/i386/i386/intr.c | 691 +++++++++++++++++++++++++++++++++++++ 2 files changed, 938 insertions(+) create mode 100644 sys/arch/i386/i386/i8259.c create mode 100644 sys/arch/i386/i386/intr.c diff --git a/sys/arch/i386/i386/i8259.c b/sys/arch/i386/i386/i8259.c new file mode 100644 index 000000000000..b5cf459b6873 --- /dev/null +++ b/sys/arch/i386/i386/i8259.c @@ -0,0 +1,247 @@ +/* $NetBSD: i8259.c,v 1.1 2002/11/22 15:05:23 fvdl Exp $ */ + +/* + * Copyright 2002 (c) Wasabi Systems, Inc. + * All rights reserved. + * + * Written by Frank van der Linden for Wasabi Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed for the NetBSD Project by + * Wasabi Systems, Inc. + * 4. The name of Wasabi Systems, Inc. may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * William Jolitz. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)isa.c 7.2 (Berkeley) 5/13/91 + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + + +#if NMCA > 0 +#include /* for MCA_system */ +#endif + +static void i8259_hwmask(struct pic *, int); +static void i8259_hwunmask(struct pic *, int); +static void i8259_setup(struct pic *, struct cpu_info *, int, int, int); +static void i8259_reinit_irqs(void); + +unsigned i8259_imen; + +/* + * Perhaps this should be made into a real device. + */ +struct pic i8259_pic = { + {0, {NULL}, NULL, NULL, NULL, 0, "pic0", NULL, 0}, + PIC_I8259, + __SIMPLELOCK_UNLOCKED, + i8259_hwmask, + i8259_hwunmask, + i8259_setup, + i8259_setup, + i8259_stubs, + i8259_stubs, +}; + +void +i8259_default_setup(void) +{ +#if NMCA > 0 + /* level-triggered interrupts on MCA PS/2s */ + if (MCA_system) + outb(IO_ICU1, 0x19); /* reset; program device, four bytes */ + else +#endif + outb(IO_ICU1, 0x11); /* reset; program device, four bytes */ + + outb(IO_ICU1+1, ICU_OFFSET); /* starting at this vector index */ + outb(IO_ICU1+1, 1 << IRQ_SLAVE); /* slave on line 2 */ +#ifdef AUTO_EOI_1 + outb(IO_ICU1+1, 2 | 1); /* auto EOI, 8086 mode */ +#else + outb(IO_ICU1+1, 1); /* 8086 mode */ +#endif + outb(IO_ICU1+1, 0xff); /* leave interrupts masked */ + outb(IO_ICU1, 0x68); /* special mask mode (if available) */ + outb(IO_ICU1, 0x0a); /* Read IRR by default. */ +#ifdef REORDER_IRQ + outb(IO_ICU1, 0xc0 | (3 - 1)); /* pri order 3-7, 0-2 (com2 first) */ +#endif + +#if NMCA > 0 + /* level-triggered interrupts on MCA PS/2s */ + if (MCA_system) + outb(IO_ICU2, 0x19); /* reset; program device, four bytes */ + else +#endif + outb(IO_ICU2, 0x11); /* reset; program device, four bytes */ + + outb(IO_ICU2+1, ICU_OFFSET+8); /* staring at this vector index */ + outb(IO_ICU2+1, IRQ_SLAVE); +#ifdef AUTO_EOI_2 + outb(IO_ICU2+1, 2 | 1); /* auto EOI, 8086 mode */ +#else + outb(IO_ICU2+1, 1); /* 8086 mode */ +#endif + outb(IO_ICU2+1, 0xff); /* leave interrupts masked */ + outb(IO_ICU2, 0x68); /* special mask mode (if available) */ + outb(IO_ICU2, 0x0a); /* Read IRR by default. */ +} + +static void +i8259_hwmask(struct pic *pic, int pin) +{ + unsigned port; + u_int8_t byte; + + i8259_imen |= (1 << pin); +#ifdef PIC_MASKDELAY + delay(10); +#endif + if (pin > 7) { + port = IO_ICU2 + 1; + byte = i8259_imen >> 8; + } else { + port = IO_ICU1 + 1; + byte = i8259_imen & 0xff; + } + outb(port, byte); +} + +static void +i8259_hwunmask(struct pic *pic, int pin) +{ + unsigned port; + u_int8_t byte; + + disable_intr(); /* XXX */ + i8259_imen &= ~(1 << pin); +#ifdef PIC_MASKDELAY + delay(10); +#endif + if (pin > 7) { + port = IO_ICU2 + 1; + byte = i8259_imen >> 8; + } else { + port = IO_ICU1 + 1; + byte = i8259_imen & 0xff; + } + outb(port, byte); + enable_intr(); +} + +static void +i8259_reinit_irqs(void) +{ + int irqs, irq; + struct cpu_info *ci = &cpu_info_primary; + + irqs = 0; + for (irq = 0; irq < NUM_LEGACY_IRQS; irq++) + if (ci->ci_isources[irq] != NULL) + irqs |= 1 << irq; + if (irqs >= 0x100) /* any IRQs >= 8 in use */ + irqs |= 1 << IRQ_SLAVE; + i8259_imen = ~irqs; + + outb(IO_ICU1 + 1, i8259_imen); + outb(IO_ICU2 + 1, i8259_imen >> 8); +} + +static void +i8259_setup(struct pic *pic, struct cpu_info *ci, int pin, int idtvec, int type) +{ + if (CPU_IS_PRIMARY(ci)) + i8259_reinit_irqs(); +} + +void +i8259_reinit(void) +{ + i8259_default_setup(); + i8259_reinit_irqs(); +} + +unsigned +i8259_setmask(unsigned mask) +{ + unsigned old = i8259_imen; + + i8259_imen = mask; + outb(IO_ICU1 + 1, i8259_imen); + outb(IO_ICU2 + 1, i8259_imen >> 8); + return old; +} diff --git a/sys/arch/i386/i386/intr.c b/sys/arch/i386/i386/intr.c new file mode 100644 index 000000000000..b68b80a66a83 --- /dev/null +++ b/sys/arch/i386/i386/intr.c @@ -0,0 +1,691 @@ +/* $NetBSD: intr.c,v 1.1 2002/11/22 15:05:24 fvdl Exp $ */ + +/* + * Copyright 2002 (c) Wasabi Systems, Inc. + * All rights reserved. + * + * Written by Frank van der Linden for Wasabi Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed for the NetBSD Project by + * Wasabi Systems, Inc. + * 4. The name of Wasabi Systems, Inc. may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "opt_multiprocessor.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "ioapic.h" +#include "lapic.h" + +#if NIOAPIC > 0 +#include +#include +#endif + +#include "mca.h" +#if NMCA > 0 +#include /* for MCA_system */ +#endif + +#if NLAPIC > 0 +#include +#endif + +struct pic softintr_pic = { + {0, {0}, NULL, NULL, NULL, 0, "softintr_fakepic", NULL, 0}, + PIC_SOFT, + __SIMPLELOCK_UNLOCKED, + NULL, + NULL, + NULL, + NULL, + NULL, +}; + +/* + * Fill in default interrupt table (in case of spurious interrupt + * during configuration of kernel), setup interrupt control unit + */ +void +intr_default_setup(void) +{ + int i; + + /* icu vectors */ + for (i = 0; i < NUM_LEGACY_IRQS; i++) { + idt_allocmap[ICU_OFFSET + i] = 1; + setgate(&idt[ICU_OFFSET + i].gd, + i8259_stubs[i].ist_entry, 0, SDT_SYS386IGT, + SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); + } + + /* + * Eventually might want to check if it's actually there. + */ + i8259_default_setup(); +} + +/* + * Handle a NMI, possibly a machine check. + * return true to panic system, false to ignore. + */ +int +i386_nmi(void) +{ + log(LOG_CRIT, "NMI port 61 %x, port 70 %x\n", inb(0x61), inb(0x70)); + return(0); +} + +/* + * Caught a stray interrupt, notify + */ +void +i386_strayintr(int irq) +{ + static u_long strays; + + /* + * Stray interrupts on irq 7 occur when an interrupt line is raised + * and then lowered before the CPU acknowledges it. This generally + * means either the device is screwed or something is cli'ing too + * long and it's timing out. + */ + if (++strays <= 5) + log(LOG_ERR, "stray interrupt %d%s\n", irq, + strays >= 5 ? "; stopped logging" : ""); +} + +/* + * Recalculate the interrupt masks from scratch. + */ +void +intr_calculatemasks(struct cpu_info *ci) +{ + int irq, level, unusedirqs, intrlevel[MAX_INTR_SOURCES]; + struct intrhand *q; + + /* First, figure out which levels each IRQ uses. */ + unusedirqs = 0xffffffff; + for (irq = 0; irq < MAX_INTR_SOURCES; irq++) { + int levels = 0; + + if (ci->ci_isources[irq] == NULL) { + intrlevel[irq] = 0; + continue; + } + for (q = ci->ci_isources[irq]->is_handlers; q; q = q->ih_next) + levels |= 1 << q->ih_level; + intrlevel[irq] = levels; + if (levels) + unusedirqs &= ~(1 << irq); + } + + /* Then figure out which IRQs use each level. */ + for (level = 0; level < NIPL; level++) { + int irqs = 0; + for (irq = 0; irq < MAX_INTR_SOURCES; irq++) + if (intrlevel[irq] & (1 << level)) + irqs |= 1 << irq; + ci->ci_imask[level] = irqs | unusedirqs; + } + + for (level = 0; level<(NIPL-1); level++) + ci->ci_imask[level+1] |= ci->ci_imask[level]; + + for (irq = 0; irq < MAX_INTR_SOURCES; irq++) { + int maxlevel = IPL_NONE; + int minlevel = IPL_HIGH; + + if (ci->ci_isources[irq] == NULL) + continue; + for (q = ci->ci_isources[irq]->is_handlers; q; + q = q->ih_next) { + if (q->ih_level < minlevel) + minlevel = q->ih_level; + if (q->ih_level > maxlevel) + maxlevel = q->ih_level; + } + ci->ci_isources[irq]->is_maxlevel = maxlevel; + ci->ci_isources[irq]->is_minlevel = minlevel; + } + + for (level = 0; level < NIPL; level++) + ci->ci_iunmask[level] = ~ci->ci_imask[level]; +} + + +/* + * XXX if defined(MULTIPROCESSOR) && .. ? + */ +#if NIOAPIC > 0 +int +intr_find_mpmapping(int bus, int pin, int *handle) +{ + struct mp_intr_map *mip; + + if (bus == -1 || mp_busses[bus].mb_intrs == NULL) + return ENOENT; + + for (mip = mp_busses[bus].mb_intrs; mip != NULL; mip=mip->next) { + if (mip->bus_pin == pin) { + *handle = mip->ioapic_ih; + return 0; + } + } + return ENOENT; +} +#endif + +int +intr_allocate_slot_cpu(struct cpu_info *ci, struct pic *pic, int pin, + int *index) +{ + int start, slot, i; + struct intrsource *isp; + + start = CPU_IS_PRIMARY(ci) ? NUM_LEGACY_IRQS : 0; + slot = -1; + + simple_lock(&ci->ci_slock); + for (i = start; i < MAX_INTR_SOURCES ; i++) { + isp = ci->ci_isources[i]; + if (isp != NULL && isp->is_pic == pic && isp->is_pin == pin) { + slot = i; + break; + } + if (isp == NULL && slot == -1) { + slot = i; + continue; + } + } + if (slot == -1) { + simple_unlock(&ci->ci_slock); + return EBUSY; + } + + isp = ci->ci_isources[slot]; + if (isp == NULL) { + MALLOC(isp, struct intrsource *, sizeof (struct intrsource), + M_DEVBUF, M_NOWAIT|M_ZERO); + if (isp == NULL) { + simple_unlock(&ci->ci_slock); + return ENOMEM; + } + snprintf(isp->is_evname, sizeof (isp->is_evname), + "pin %d", pin); + evcnt_attach_dynamic(&isp->is_evcnt, EVCNT_TYPE_INTR, NULL, + pic->pic_dev.dv_xname, isp->is_evname); + ci->ci_isources[slot] = isp; + } + simple_unlock(&ci->ci_slock); + + *index = slot; + return 0; +} + +/* + * A simple round-robin allocator to assign interrupts to CPUs. + */ +int +intr_allocate_slot(struct pic *pic, int legacy_irq, int pin, int level, + struct cpu_info **cip, int *index, int *idt_slot) +{ + CPU_INFO_ITERATOR cii; + struct cpu_info *ci; + struct intrsource *isp; + int slot, idtvec, error; + + /* + * If a legacy IRQ is wanted, try to use a fixed slot pointing + * at the primary CPU. In the case of IO APICs, multiple pins + * may map to one legacy IRQ, but they should not be shared + * in that case, so the first one gets the legacy slot, but + * a subsequent allocation with a different pin will get + * a different slot. + */ + if (legacy_irq != -1) { + ci = &cpu_info_primary; + slot = legacy_irq; + isp = ci->ci_isources[slot]; + if (isp == NULL) { + MALLOC(isp, struct intrsource *, + sizeof (struct intrsource), M_DEVBUF, + M_NOWAIT|M_ZERO); + if (isp == NULL) + return ENOMEM; + snprintf(isp->is_evname, sizeof (isp->is_evname), + "pin %d", pin); + evcnt_attach_dynamic(&isp->is_evcnt, EVCNT_TYPE_INTR, + NULL, pic->pic_dev.dv_xname, isp->is_evname); + simple_lock(&ci->ci_slock); + ci->ci_isources[slot] = isp; + simple_unlock(&ci->ci_slock); + } else { + if (isp->is_pin != pin) { + if (pic == &i8259_pic) + return EINVAL; + goto other; + } + } + + if (pic == &i8259_pic) + idtvec = ICU_OFFSET + legacy_irq; + else { +#ifdef IOAPIC_HWMASK + if (level > isp->is_maxlevel) { +#else + if (isp->is_minlevel == 0 || level < isp->is_minlevel) { +#endif + idtvec = idt_vec_alloc(APIC_LEVEL(level), + IDT_INTR_HIGH); + if (idtvec == 0) + return EBUSY; + } else + idtvec = isp->is_idtvec; + } + } else { +other: + /* + * Otherwise, look for a free slot elsewhere. Do the primary + * CPU first. + */ + ci = &cpu_info_primary; + error = intr_allocate_slot_cpu(ci, pic, pin, &slot); + if (error == 0) + goto found; + + /* + * ..now try the others. + */ + for (CPU_INFO_FOREACH(cii, ci)) { + if (CPU_IS_PRIMARY(ci)) + continue; + error = intr_allocate_slot_cpu(ci, pic, pin, &slot); + if (error == 0) + goto found; + } + return EBUSY; +found: + idtvec = idt_vec_alloc(APIC_LEVEL(level), IDT_INTR_HIGH); + if (idtvec == 0) { + simple_lock(&ci->ci_slock); + FREE(ci->ci_isources[slot], M_DEVBUF); + ci->ci_isources[slot] = NULL; + simple_unlock(&ci->ci_slock); + return EBUSY; + } + } + *idt_slot = idtvec; + *index = slot; + *cip = ci; + return 0; +} + +void * +intr_establish(int legacy_irq, struct pic *pic, int pin, int type, int level, + int (*handler)(void *), void *arg) +{ + struct intrhand **p, *q, *ih; + struct cpu_info *ci; + int slot, error, idt_vec; + struct intrsource *source; + struct intrstub *stubp; + +#ifdef DIAGNOSTIC + if (legacy_irq != -1 && (legacy_irq < 0 || legacy_irq > 15)) + panic("intr_establish: bad legacy IRQ value"); + + if (legacy_irq == -1 && pic == &i8259_pic) + panic("intr_establish: non-legacy IRQ on i8259"); +#endif + + error = intr_allocate_slot(pic, legacy_irq, pin, level, &ci, &slot, + &idt_vec); + if (error != 0) { + printf("failed to allocate interrupt slot for PIC %s pin %d\n", + pic->pic_dev.dv_xname, pin); + return NULL; + } + + /* no point in sleeping unless someone can free memory. */ + ih = malloc(sizeof *ih, M_DEVBUF, cold ? M_NOWAIT : M_WAITOK); + if (ih == NULL) { + printf("intr_establish: can't allocate malloc handler info\n"); + return NULL; + } + + source = ci->ci_isources[slot]; + + if (source->is_handlers != NULL && + source->is_pic->pic_type != pic->pic_type) { + free(ih, M_DEVBUF); + printf("intr_establish: can't share intr source between " + "different PIC types (legacy_irq %d pin %d slot %d)\n", + legacy_irq, pin, slot); + return NULL; + } + + simple_lock(&ci->ci_slock); + + source->is_pin = pin; + source->is_pic = pic; + + switch (source->is_type) { + case IST_NONE: + source->is_type = type; + break; + case IST_EDGE: + case IST_LEVEL: + if (source->is_type == type) + break; + case IST_PULSE: + if (type != IST_NONE) { + simple_unlock(&ci->ci_slock); + printf("intr_establish: pic %s pin %d: can't share " + "type %d with %d\n", pic->pic_name, pin, + source->is_type, type); + free(ih, M_DEVBUF); + return NULL; + } + break; + default: + simple_unlock(&ci->ci_slock); + panic("intr_establish: bad intr type %d for pic %s pin %d\n", + source->is_type, pic->pic_dev.dv_xname, pin); + } + + if (!cold) + pic->pic_hwmask(pic, pin); + + /* + * Figure out where to put the handler. + * This is O(N^2), but we want to preserve the order, and N is + * generally small. + */ + for (p = &ci->ci_isources[slot]->is_handlers; + (q = *p) != NULL && q->ih_level > level; + p = &q->ih_next) + ; + + ih->ih_fun = handler; + ih->ih_arg = arg; + ih->ih_next = *p; + ih->ih_level = level; + ih->ih_pin = pin; + ih->ih_cpu = ci; + ih->ih_slot = slot; + *p = ih; + + intr_calculatemasks(ci); + + simple_unlock(&ci->ci_slock); + + if (ci->ci_isources[slot]->is_resume == NULL || + source->is_idtvec != idt_vec) { + if (source->is_idtvec != 0 && source->is_idtvec != idt_vec) + idt_vec_free(source->is_idtvec); + source->is_idtvec = idt_vec; + stubp = type == IST_LEVEL ? + &pic->pic_level_stubs[slot] : &pic->pic_edge_stubs[slot]; + ci->ci_isources[slot]->is_resume = stubp->ist_resume; + ci->ci_isources[slot]->is_recurse = stubp->ist_recurse; + setgate(&idt[idt_vec].gd, stubp->ist_entry, 0, SDT_SYS386IGT, + SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); + } + + pic->pic_addroute(pic, ci, pin, idt_vec, type); + + if (!cold) + pic->pic_hwunmask(pic, pin); + +#ifdef INTRDEBUG + printf("allocated pic %s type %s pin %d level %d to cpu%u slot %d idt entry %d\n", + pic->pic_name, type == IST_EDGE ? "edge" : "level", pin, level, + (unsigned)ci->ci_cpuid, slot, idt_vec); +#endif + + return (ih); +} + +/* + * Deregister an interrupt handler. + */ +void +intr_disestablish(struct intrhand *ih) +{ + struct intrhand **p, *q; + struct cpu_info *ci; + struct pic *pic; + struct intrsource *source; + int idtvec; + + ci = ih->ih_cpu; + pic = ci->ci_isources[ih->ih_slot]->is_pic; + source = ci->ci_isources[ih->ih_slot]; + idtvec = source->is_idtvec; + + simple_lock(&ci->ci_slock); + pic->pic_hwmask(pic, ih->ih_pin); + i386_atomic_clearbits_l(&ci->ci_ipending, (1 << ih->ih_slot)); + + /* + * Remove the handler from the chain. + */ + for (p = &source->is_handlers; (q = *p) != NULL && q != ih; + p = &q->ih_next) + ; + if (q == NULL) { + simple_unlock(&ci->ci_slock); + panic("intr_disestablish: handler not registered"); + } + + *p = q->ih_next; + + intr_calculatemasks(ci); + pic->pic_delroute(pic, ci, ih->ih_pin, idtvec, source->is_type); + pic->pic_hwunmask(pic, ih->ih_pin); + +#ifdef INTRDEBUG + printf("cpu%lu: remove slot %d (pic %s pin %d vec %d)\n", + ci->ci_cpuid, ih->ih_slot, pic->pic_dev.dv_xname, ih->ih_pin, + idtvec); +#endif + + if (source->is_handlers == NULL) { + evcnt_detach(&source->is_evcnt); + FREE(source, M_DEVBUF); + ci->ci_isources[ih->ih_slot] = NULL; + if (pic != &i8259_pic) + idt_vec_free(idtvec); + } + + free(ih, M_DEVBUF); + + simple_unlock(&ci->ci_slock); +} + +#define CONCAT(x,y) __CONCAT(x,y) + +/* + * Fake interrupt handler structures for the benefit of symmetry with + * other interrupt sources, and the benefit of intr_calculatemasks() + */ +struct intrhand fake_softclock_intrhand; +struct intrhand fake_softnet_intrhand; +struct intrhand fake_softserial_intrhand; +struct intrhand fake_timer_intrhand; +struct intrhand fake_ipi_intrhand; + +#if NLAPIC > 0 +static char *i386_ipi_names[I386_NIPI] = I386_IPI_NAMES; +#endif + +/* + * Initialize all handlers that aren't dynamically allocated, and exist + * for each CPU. + */ +void +cpu_intr_init(struct cpu_info *ci) +{ + struct intrsource *isp; +#if NLAPIC > 0 + int i; +#endif + + MALLOC(isp, struct intrsource *, sizeof (struct intrsource), M_DEVBUF, + M_WAITOK|M_ZERO); + if (isp == NULL) + panic("can't allocate fixed interrupt source"); + isp->is_recurse = Xsoftclock; + isp->is_resume = Xsoftclock; + fake_softclock_intrhand.ih_level = IPL_SOFTCLOCK; + isp->is_handlers = &fake_softclock_intrhand; + isp->is_pic = &softintr_pic; + ci->ci_isources[SIR_CLOCK] = isp; + evcnt_attach_dynamic(&isp->is_evcnt, EVCNT_TYPE_INTR, NULL, + ci->ci_dev->dv_xname, "softclock"); + + MALLOC(isp, struct intrsource *, sizeof (struct intrsource), M_DEVBUF, + M_WAITOK|M_ZERO); + if (isp == NULL) + panic("can't allocate fixed interrupt source"); + isp->is_recurse = Xsoftnet; + isp->is_resume = Xsoftnet; + fake_softnet_intrhand.ih_level = IPL_SOFTNET; + isp->is_handlers = &fake_softnet_intrhand; + isp->is_pic = &softintr_pic; + ci->ci_isources[SIR_NET] = isp; + evcnt_attach_dynamic(&isp->is_evcnt, EVCNT_TYPE_INTR, NULL, + ci->ci_dev->dv_xname, "softnet"); + + MALLOC(isp, struct intrsource *, sizeof (struct intrsource), M_DEVBUF, + M_WAITOK|M_ZERO); + if (isp == NULL) + panic("can't allocate fixed interrupt source"); + isp->is_recurse = Xsoftserial; + isp->is_resume = Xsoftserial; + fake_softserial_intrhand.ih_level = IPL_SOFTSERIAL; + isp->is_handlers = &fake_softserial_intrhand; + isp->is_pic = &softintr_pic; + ci->ci_isources[SIR_SERIAL] = isp; + evcnt_attach_dynamic(&isp->is_evcnt, EVCNT_TYPE_INTR, NULL, + ci->ci_dev->dv_xname, "softserial"); + +#if NLAPIC > 0 + MALLOC(isp, struct intrsource *, sizeof (struct intrsource), M_DEVBUF, + M_WAITOK|M_ZERO); + if (isp == NULL) + panic("can't allocate fixed interrupt source"); + isp->is_recurse = Xrecurse_lapic_ltimer; + isp->is_resume = Xresume_lapic_ltimer; + fake_timer_intrhand.ih_level = IPL_CLOCK; + isp->is_handlers = &fake_timer_intrhand; + isp->is_pic = &local_pic; + ci->ci_isources[LIR_TIMER] = isp; + evcnt_attach_dynamic(&isp->is_evcnt, EVCNT_TYPE_INTR, NULL, + ci->ci_dev->dv_xname, "timer"); + + MALLOC(isp, struct intrsource *, sizeof (struct intrsource), M_DEVBUF, + M_WAITOK|M_ZERO); + if (isp == NULL) + panic("can't allocate fixed interrupt source"); + isp->is_recurse = Xrecurse_lapic_ipi; + isp->is_resume = Xresume_lapic_ipi; + fake_ipi_intrhand.ih_level = IPL_IPI; + isp->is_handlers = &fake_ipi_intrhand; + isp->is_pic = &local_pic; + ci->ci_isources[LIR_IPI] = isp; + + for (i = 0; i < I386_NIPI; i++) + evcnt_attach_dynamic(&ci->ci_ipi_events[i], EVCNT_TYPE_INTR, + NULL, ci->ci_dev->dv_xname, i386_ipi_names[i]); +#endif + + intr_calculatemasks(ci); + +} + +#ifdef MULTIPROCESSOR +void +i386_intlock(void) +{ + spinlockmgr(&kernel_lock, LK_EXCLUSIVE|LK_CANRECURSE, 0); +} + +void +i386_intunlock(void) +{ + spinlockmgr(&kernel_lock, LK_RELEASE, 0); +} +#endif + +#ifdef INTRDEBUG +void +intr_printconfig(void) +{ + int i; + struct intrhand *ih; + struct intrsource *isp; + struct cpu_info *ci; + CPU_INFO_ITERATOR cii; + + for (CPU_INFO_FOREACH(cii, ci)) { + printf("cpu%d: interrupt masks:\n", (unsigned)ci->ci_cpuid); + for (i = 0; i < NIPL; i++) + printf("IPL %d mask %lx unmask %lx\n", i, + (u_long)ci->ci_imask[i], (u_long)ci->ci_iunmask[i]); + simple_lock(&ci->ci_slock); + for (i = 0; i < MAX_INTR_SOURCES; i++) { + isp = ci->ci_isources[i]; + if (isp == NULL) + continue; + printf("cpu%u source %d is pin %d from pic %s maxlevel %d\n", + (unsigned)ci->ci_cpuid, i, isp->is_pin, + isp->is_pic->pic_name, isp->is_maxlevel); + for (ih = isp->is_handlers; ih != NULL; + ih = ih->ih_next) + printf("\thandler %p level %d\n", + ih->ih_fun, ih->ih_level); + + } + simple_unlock(&ci->ci_slock); + } +} +#endif