--- /dev/null
+/*
+ * Copyright (c) 1996, by Steve Passe
+ * All rights reserved.
+ *
+ * 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. The name of the developer may NOT be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * $FreeBSD: src/sys/i386/i386/mpapic.c,v 1.37.2.7 2003/01/25 02:31:47 peter Exp $
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/machintr.h>
+#include <machine/globaldata.h>
+#include <machine/smp.h>
+#include <machine/cputypes.h>
+#include <machine/md_var.h>
+#include <machine/pmap.h>
+#include <machine_base/apic/mpapic.h>
+#include <machine_base/apic/ioapic_abi.h>
+#include <machine/segments.h>
+#include <sys/thread2.h>
+
+#include <machine/intr_machdep.h>
+
+#define IOAPIC_COUNT_MAX 16
+#define IOAPIC_ID_MASK (IOAPIC_COUNT_MAX - 1)
+
+/* XXX */
+extern pt_entry_t *SMPpt;
+
+struct ioapic_info {
+ int io_idx;
+ int io_apic_id;
+ void *io_addr;
+ int io_npin;
+ int io_gsi_base;
+
+ TAILQ_ENTRY(ioapic_info) io_link;
+};
+TAILQ_HEAD(ioapic_info_list, ioapic_info);
+
+struct ioapic_intsrc {
+ int int_gsi;
+ enum intr_trigger int_trig;
+ enum intr_polarity int_pola;
+};
+
+struct ioapic_conf {
+ struct ioapic_info_list ioc_list;
+ struct ioapic_intsrc ioc_intsrc[16]; /* XXX magic number */
+};
+
+static void ioapic_setup(const struct ioapic_info *);
+static int ioapic_alloc_apic_id(int);
+static void ioapic_set_apic_id(const struct ioapic_info *);
+static void ioapic_gsi_setup(int);
+static const struct ioapic_info *
+ ioapic_gsi_search(int);
+static void ioapic_pin_prog(void *, int, int,
+ enum intr_trigger, enum intr_polarity, uint32_t);
+
+static struct ioapic_conf ioapic_conf;
+
+static TAILQ_HEAD(, ioapic_enumerator) ioapic_enumerators =
+ TAILQ_HEAD_INITIALIZER(ioapic_enumerators);
+
+void
+ioapic_config(void)
+{
+ struct ioapic_info *info;
+ int start_apic_id = 0;
+ struct ioapic_enumerator *e;
+ int error, i;
+ u_long ef = 0;
+
+ TAILQ_INIT(&ioapic_conf.ioc_list);
+ /* XXX magic number */
+ for (i = 0; i < 16; ++i)
+ ioapic_conf.ioc_intsrc[i].int_gsi = -1;
+
+ TAILQ_FOREACH(e, &ioapic_enumerators, ioapic_link) {
+ error = e->ioapic_probe(e);
+ if (!error)
+ break;
+ }
+ if (e == NULL) {
+#ifdef notyet
+ panic("can't config I/O APIC\n");
+#else
+ kprintf("no I/O APIC\n");
+ return;
+#endif
+ }
+
+ crit_enter();
+
+ ef = read_eflags();
+ cpu_disable_intr();
+
+ /*
+ * Switch to I/O APIC MachIntrABI and reconfigure
+ * the default IDT entries.
+ */
+ MachIntrABI = MachIntrABI_IOAPIC;
+ MachIntrABI.setdefault();
+
+ e->ioapic_enumerate(e);
+
+ /*
+ * Setup index
+ */
+ i = 0;
+ TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link)
+ info->io_idx = i++;
+
+ if (i > IOAPIC_COUNT_MAX) /* XXX magic number */
+ panic("ioapic_config: more than 16 I/O APIC\n");
+
+ /*
+ * Setup APIC ID
+ */
+ TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link) {
+ int apic_id;
+
+ apic_id = ioapic_alloc_apic_id(start_apic_id);
+ if (apic_id == NAPICID) {
+ kprintf("IOAPIC: can't alloc APIC ID for "
+ "%dth I/O APIC\n", info->io_idx);
+ break;
+ }
+ info->io_apic_id = apic_id;
+
+ start_apic_id = apic_id + 1;
+ }
+ if (info != NULL) {
+ /*
+ * xAPIC allows I/O APIC's APIC ID to be same
+ * as the LAPIC's APIC ID
+ */
+ kprintf("IOAPIC: use xAPIC model to alloc APIC ID "
+ "for I/O APIC\n");
+
+ TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link)
+ info->io_apic_id = info->io_idx;
+ }
+
+ /*
+ * Warning about any GSI holes
+ */
+ TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link) {
+ const struct ioapic_info *prev_info;
+
+ prev_info = TAILQ_PREV(info, ioapic_info_list, io_link);
+ if (prev_info != NULL) {
+ if (info->io_gsi_base !=
+ prev_info->io_gsi_base + prev_info->io_npin) {
+ kprintf("IOAPIC: warning gsi hole "
+ "[%d, %d]\n",
+ prev_info->io_gsi_base +
+ prev_info->io_npin,
+ info->io_gsi_base - 1);
+ }
+ }
+ }
+
+ if (bootverbose) {
+ TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link) {
+ kprintf("IOAPIC: idx %d, apic id %d, "
+ "gsi base %d, npin %d\n",
+ info->io_idx,
+ info->io_apic_id,
+ info->io_gsi_base,
+ info->io_npin);
+ }
+ }
+
+ /*
+ * Setup all I/O APIC
+ */
+ TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link)
+ ioapic_setup(info);
+ ioapic_abi_fixup_irqmap();
+
+ write_eflags(ef);
+
+ MachIntrABI.cleanup();
+
+ crit_exit();
+}
+
+void
+ioapic_enumerator_register(struct ioapic_enumerator *ne)
+{
+ struct ioapic_enumerator *e;
+
+ TAILQ_FOREACH(e, &ioapic_enumerators, ioapic_link) {
+ if (e->ioapic_prio < ne->ioapic_prio) {
+ TAILQ_INSERT_BEFORE(e, ne, ioapic_link);
+ return;
+ }
+ }
+ TAILQ_INSERT_TAIL(&ioapic_enumerators, ne, ioapic_link);
+}
+
+void
+ioapic_add(void *addr, int gsi_base, int npin)
+{
+ struct ioapic_info *info, *ninfo;
+ int gsi_end;
+
+ gsi_end = gsi_base + npin - 1;
+ TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link) {
+ if ((gsi_base >= info->io_gsi_base &&
+ gsi_base < info->io_gsi_base + info->io_npin) ||
+ (gsi_end >= info->io_gsi_base &&
+ gsi_end < info->io_gsi_base + info->io_npin)) {
+ panic("ioapic_add: overlapped gsi, base %d npin %d, "
+ "hit base %d, npin %d\n", gsi_base, npin,
+ info->io_gsi_base, info->io_npin);
+ }
+ if (info->io_addr == addr)
+ panic("ioapic_add: duplicated addr %p\n", addr);
+ }
+
+ ninfo = kmalloc(sizeof(*ninfo), M_DEVBUF, M_WAITOK | M_ZERO);
+ ninfo->io_addr = addr;
+ ninfo->io_npin = npin;
+ ninfo->io_gsi_base = gsi_base;
+ ninfo->io_apic_id = -1;
+
+ /*
+ * Create IOAPIC list in ascending order of GSI base
+ */
+ TAILQ_FOREACH_REVERSE(info, &ioapic_conf.ioc_list,
+ ioapic_info_list, io_link) {
+ if (ninfo->io_gsi_base > info->io_gsi_base) {
+ TAILQ_INSERT_AFTER(&ioapic_conf.ioc_list,
+ info, ninfo, io_link);
+ break;
+ }
+ }
+ if (info == NULL)
+ TAILQ_INSERT_HEAD(&ioapic_conf.ioc_list, ninfo, io_link);
+}
+
+void
+ioapic_intsrc(int irq, int gsi, enum intr_trigger trig, enum intr_polarity pola)
+{
+ struct ioapic_intsrc *int_src;
+
+ KKASSERT(irq < 16);
+ int_src = &ioapic_conf.ioc_intsrc[irq];
+
+ if (gsi == 0) {
+ /* Don't allow mixed mode */
+ kprintf("IOAPIC: warning intsrc irq %d -> gsi 0\n", irq);
+ return;
+ }
+
+ if (int_src->int_gsi != -1) {
+ if (int_src->int_gsi != gsi) {
+ kprintf("IOAPIC: warning intsrc irq %d, gsi "
+ "%d -> %d\n", irq, int_src->int_gsi, gsi);
+ }
+ if (int_src->int_trig != trig) {
+ kprintf("IOAPIC: warning intsrc irq %d, trig "
+ "%s -> %s\n", irq,
+ intr_str_trigger(int_src->int_trig),
+ intr_str_trigger(trig));
+ }
+ if (int_src->int_pola != pola) {
+ kprintf("IOAPIC: warning intsrc irq %d, pola "
+ "%s -> %s\n", irq,
+ intr_str_polarity(int_src->int_pola),
+ intr_str_polarity(pola));
+ }
+ }
+ int_src->int_gsi = gsi;
+ int_src->int_trig = trig;
+ int_src->int_pola = pola;
+}
+
+static void
+ioapic_set_apic_id(const struct ioapic_info *info)
+{
+ uint32_t id;
+ int apic_id;
+
+ id = ioapic_read(info->io_addr, IOAPIC_ID);
+
+ id &= ~APIC_ID_MASK;
+ id |= (info->io_apic_id << 24);
+
+ ioapic_write(info->io_addr, IOAPIC_ID, id);
+
+ /*
+ * Re-read && test
+ */
+ id = ioapic_read(info->io_addr, IOAPIC_ID);
+ apic_id = (id & APIC_ID_MASK) >> 24;
+
+ /*
+ * I/O APIC ID is a 4bits field
+ */
+ if ((apic_id & IOAPIC_ID_MASK) !=
+ (info->io_apic_id & IOAPIC_ID_MASK)) {
+ panic("ioapic_set_apic_id: can't set apic id to %d, "
+ "currently set to %d\n", info->io_apic_id, apic_id);
+ }
+}
+
+static void
+ioapic_gsi_setup(int gsi)
+{
+ enum intr_trigger trig;
+ enum intr_polarity pola;
+ int irq;
+
+ if (gsi == 0) {
+ /* ExtINT */
+ imen_lock();
+ ioapic_extpin_setup(ioapic_gsi_ioaddr(gsi),
+ ioapic_gsi_pin(gsi), 0);
+ imen_unlock();
+ return;
+ }
+
+ trig = 0; /* silence older gcc's */
+ pola = 0; /* silence older gcc's */
+
+ for (irq = 0; irq < 16; ++irq) {
+ const struct ioapic_intsrc *int_src =
+ &ioapic_conf.ioc_intsrc[irq];
+
+ if (gsi == int_src->int_gsi) {
+ trig = int_src->int_trig;
+ pola = int_src->int_pola;
+ break;
+ }
+ }
+
+ if (irq == 16) {
+ if (gsi < 16) {
+ trig = INTR_TRIGGER_EDGE;
+ pola = INTR_POLARITY_HIGH;
+ } else {
+ trig = INTR_TRIGGER_LEVEL;
+ pola = INTR_POLARITY_LOW;
+ }
+ irq = gsi;
+ }
+
+ ioapic_abi_set_irqmap(irq, gsi, trig, pola);
+}
+
+void *
+ioapic_gsi_ioaddr(int gsi)
+{
+ const struct ioapic_info *info;
+
+ info = ioapic_gsi_search(gsi);
+ return info->io_addr;
+}
+
+int
+ioapic_gsi_pin(int gsi)
+{
+ const struct ioapic_info *info;
+
+ info = ioapic_gsi_search(gsi);
+ return gsi - info->io_gsi_base;
+}
+
+static const struct ioapic_info *
+ioapic_gsi_search(int gsi)
+{
+ const struct ioapic_info *info;
+
+ TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link) {
+ if (gsi >= info->io_gsi_base &&
+ gsi < info->io_gsi_base + info->io_npin)
+ return info;
+ }
+ panic("ioapic_gsi_search: no I/O APIC\n");
+}
+
+int
+ioapic_gsi(int idx, int pin)
+{
+ const struct ioapic_info *info;
+
+ TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link) {
+ if (info->io_idx == idx)
+ break;
+ }
+ if (info == NULL)
+ return -1;
+ if (pin >= info->io_npin)
+ return -1;
+ return info->io_gsi_base + pin;
+}
+
+void
+ioapic_extpin_setup(void *addr, int pin, int vec)
+{
+ ioapic_pin_prog(addr, pin, vec,
+ INTR_TRIGGER_CONFORM, INTR_POLARITY_CONFORM, IOART_DELEXINT);
+}
+
+int
+ioapic_extpin_gsi(void)
+{
+ return 0;
+}
+
+void
+ioapic_pin_setup(void *addr, int pin, int vec,
+ enum intr_trigger trig, enum intr_polarity pola)
+{
+ /*
+ * Always clear an I/O APIC pin before [re]programming it. This is
+ * particularly important if the pin is set up for a level interrupt
+ * as the IOART_REM_IRR bit might be set. When we reprogram the
+ * vector any EOI from pending ints on this pin could be lost and
+ * IRR might never get reset.
+ *
+ * To fix this problem, clear the vector and make sure it is
+ * programmed as an edge interrupt. This should theoretically
+ * clear IRR so we can later, safely program it as a level
+ * interrupt.
+ */
+ ioapic_pin_prog(addr, pin, vec, INTR_TRIGGER_EDGE, INTR_POLARITY_HIGH,
+ IOART_DELFIXED);
+ ioapic_pin_prog(addr, pin, vec, trig, pola, IOART_DELFIXED);
+}
+
+static void
+ioapic_pin_prog(void *addr, int pin, int vec,
+ enum intr_trigger trig, enum intr_polarity pola, uint32_t del_mode)
+{
+ uint32_t flags, target;
+ int select;
+
+ KKASSERT(del_mode == IOART_DELEXINT || del_mode == IOART_DELFIXED);
+
+ select = IOAPIC_REDTBL0 + (2 * pin);
+
+ flags = ioapic_read(addr, select) & IOART_RESV;
+ flags |= IOART_INTMSET | IOART_DESTPHY;
+#ifdef foo
+ flags |= del_mode;
+#else
+ /*
+ * We only support limited I/O APIC mixed mode,
+ * so even for ExtINT, we still use "fixed"
+ * delivery mode.
+ */
+ flags |= IOART_DELFIXED;
+#endif
+
+ if (del_mode == IOART_DELEXINT) {
+ KKASSERT(trig == INTR_TRIGGER_CONFORM &&
+ pola == INTR_POLARITY_CONFORM);
+ flags |= IOART_TRGREDG | IOART_INTAHI;
+ } else {
+ switch (trig) {
+ case INTR_TRIGGER_EDGE:
+ flags |= IOART_TRGREDG;
+ break;
+
+ case INTR_TRIGGER_LEVEL:
+ flags |= IOART_TRGRLVL;
+ break;
+
+ case INTR_TRIGGER_CONFORM:
+ panic("ioapic_pin_prog: trig conform is not "
+ "supported\n");
+ }
+ switch (pola) {
+ case INTR_POLARITY_HIGH:
+ flags |= IOART_INTAHI;
+ break;
+
+ case INTR_POLARITY_LOW:
+ flags |= IOART_INTALO;
+ break;
+
+ case INTR_POLARITY_CONFORM:
+ panic("ioapic_pin_prog: pola conform is not "
+ "supported\n");
+ }
+ }
+
+ target = ioapic_read(addr, select + 1) & IOART_HI_DEST_RESV;
+ target |= (CPU_TO_ID(0) << IOART_HI_DEST_SHIFT) &
+ IOART_HI_DEST_MASK;
+
+ ioapic_write(addr, select, flags | vec);
+ ioapic_write(addr, select + 1, target);
+}
+
+static void
+ioapic_setup(const struct ioapic_info *info)
+{
+ int i;
+
+ ioapic_set_apic_id(info);
+
+ for (i = 0; i < info->io_npin; ++i)
+ ioapic_gsi_setup(info->io_gsi_base + i);
+}
+
+static int
+ioapic_alloc_apic_id(int start)
+{
+ for (;;) {
+ const struct ioapic_info *info;
+ int apic_id, apic_id16;
+
+ apic_id = lapic_unused_apic_id(start);
+ if (apic_id == NAPICID) {
+ kprintf("IOAPIC: can't find unused APIC ID\n");
+ return apic_id;
+ }
+ apic_id16 = apic_id & IOAPIC_ID_MASK;
+
+ /*
+ * Check against other I/O APIC's APIC ID's lower 4bits.
+ *
+ * The new APIC ID will have to be different from others
+ * in the lower 4bits, no matter whether xAPIC is used
+ * or not.
+ */
+ TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link) {
+ if (info->io_apic_id == -1) {
+ info = NULL;
+ break;
+ }
+ if ((info->io_apic_id & IOAPIC_ID_MASK) == apic_id16)
+ break;
+ }
+ if (info == NULL)
+ return apic_id;
+
+ kprintf("IOAPIC: APIC ID %d has same lower 4bits as "
+ "%dth I/O APIC, keep searching...\n",
+ apic_id, info->io_idx);
+
+ start = apic_id + 1;
+ }
+ panic("ioapic_unused_apic_id: never reached\n");
+}
#include <machine/intr_machdep.h>
-#define IOAPIC_COUNT_MAX 16
-#define IOAPIC_ID_MASK (IOAPIC_COUNT_MAX - 1)
-
/* XXX */
extern pt_entry_t *SMPpt;
-struct ioapic_info {
- int io_idx;
- int io_apic_id;
- void *io_addr;
- int io_npin;
- int io_gsi_base;
-
- TAILQ_ENTRY(ioapic_info) io_link;
-};
-TAILQ_HEAD(ioapic_info_list, ioapic_info);
-
-struct ioapic_intsrc {
- int int_gsi;
- enum intr_trigger int_trig;
- enum intr_polarity int_pola;
-};
-
-struct ioapic_conf {
- struct ioapic_info_list ioc_list;
- struct ioapic_intsrc ioc_intsrc[16]; /* XXX magic number */
-};
-
static void lapic_timer_calibrate(void);
static void lapic_timer_set_divisor(int);
static void lapic_timer_fixup_handler(void *);
static void lapic_timer_intr_restart(struct cputimer_intr *);
static void lapic_timer_intr_pmfixup(struct cputimer_intr *);
-static int lapic_unused_apic_id(int);
-
-static void ioapic_setup(const struct ioapic_info *);
-static int ioapic_alloc_apic_id(int);
-static void ioapic_set_apic_id(const struct ioapic_info *);
-static void ioapic_gsi_setup(int);
-static const struct ioapic_info *
- ioapic_gsi_search(int);
-static void ioapic_pin_prog(void *, int, int,
- enum intr_trigger, enum intr_polarity, uint32_t);
-
static struct cputimer_intr lapic_cputimer_intr = {
.freq = 0,
.reload = lapic_timer_intr_reload,
};
#define APIC_TIMER_NDIVISORS (int)(NELEM(lapic_timer_divisors))
-static struct ioapic_conf ioapic_conf;
-
/*
* Enable LAPIC, configure interrupts.
*/
/* spin */ ;
}
-static int
+int
lapic_unused_apic_id(int start)
{
int i;
}
TAILQ_INSERT_TAIL(&lapic_enumerators, ne, lapic_link);
}
-
-static TAILQ_HEAD(, ioapic_enumerator) ioapic_enumerators =
- TAILQ_HEAD_INITIALIZER(ioapic_enumerators);
-
-void
-ioapic_config(void)
-{
- struct ioapic_info *info;
- int start_apic_id = 0;
- struct ioapic_enumerator *e;
- int error, i;
- u_long ef = 0;
-
- TAILQ_INIT(&ioapic_conf.ioc_list);
- /* XXX magic number */
- for (i = 0; i < 16; ++i)
- ioapic_conf.ioc_intsrc[i].int_gsi = -1;
-
- TAILQ_FOREACH(e, &ioapic_enumerators, ioapic_link) {
- error = e->ioapic_probe(e);
- if (!error)
- break;
- }
- if (e == NULL) {
-#ifdef notyet
- panic("can't config I/O APIC\n");
-#else
- kprintf("no I/O APIC\n");
- return;
-#endif
- }
-
- crit_enter();
-
- ef = read_eflags();
- cpu_disable_intr();
-
- /*
- * Switch to I/O APIC MachIntrABI and reconfigure
- * the default IDT entries.
- */
- MachIntrABI = MachIntrABI_IOAPIC;
- MachIntrABI.setdefault();
-
- e->ioapic_enumerate(e);
-
- /*
- * Setup index
- */
- i = 0;
- TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link)
- info->io_idx = i++;
-
- if (i > IOAPIC_COUNT_MAX) /* XXX magic number */
- panic("ioapic_config: more than 16 I/O APIC\n");
-
- /*
- * Setup APIC ID
- */
- TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link) {
- int apic_id;
-
- apic_id = ioapic_alloc_apic_id(start_apic_id);
- if (apic_id == NAPICID) {
- kprintf("IOAPIC: can't alloc APIC ID for "
- "%dth I/O APIC\n", info->io_idx);
- break;
- }
- info->io_apic_id = apic_id;
-
- start_apic_id = apic_id + 1;
- }
- if (info != NULL) {
- /*
- * xAPIC allows I/O APIC's APIC ID to be same
- * as the LAPIC's APIC ID
- */
- kprintf("IOAPIC: use xAPIC model to alloc APIC ID "
- "for I/O APIC\n");
-
- TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link)
- info->io_apic_id = info->io_idx;
- }
-
- /*
- * Warning about any GSI holes
- */
- TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link) {
- const struct ioapic_info *prev_info;
-
- prev_info = TAILQ_PREV(info, ioapic_info_list, io_link);
- if (prev_info != NULL) {
- if (info->io_gsi_base !=
- prev_info->io_gsi_base + prev_info->io_npin) {
- kprintf("IOAPIC: warning gsi hole "
- "[%d, %d]\n",
- prev_info->io_gsi_base +
- prev_info->io_npin,
- info->io_gsi_base - 1);
- }
- }
- }
-
- if (bootverbose) {
- TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link) {
- kprintf("IOAPIC: idx %d, apic id %d, "
- "gsi base %d, npin %d\n",
- info->io_idx,
- info->io_apic_id,
- info->io_gsi_base,
- info->io_npin);
- }
- }
-
- /*
- * Setup all I/O APIC
- */
- TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link)
- ioapic_setup(info);
- ioapic_abi_fixup_irqmap();
-
- write_eflags(ef);
-
- MachIntrABI.cleanup();
-
- crit_exit();
-}
-
-void
-ioapic_enumerator_register(struct ioapic_enumerator *ne)
-{
- struct ioapic_enumerator *e;
-
- TAILQ_FOREACH(e, &ioapic_enumerators, ioapic_link) {
- if (e->ioapic_prio < ne->ioapic_prio) {
- TAILQ_INSERT_BEFORE(e, ne, ioapic_link);
- return;
- }
- }
- TAILQ_INSERT_TAIL(&ioapic_enumerators, ne, ioapic_link);
-}
-
-void
-ioapic_add(void *addr, int gsi_base, int npin)
-{
- struct ioapic_info *info, *ninfo;
- int gsi_end;
-
- gsi_end = gsi_base + npin - 1;
- TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link) {
- if ((gsi_base >= info->io_gsi_base &&
- gsi_base < info->io_gsi_base + info->io_npin) ||
- (gsi_end >= info->io_gsi_base &&
- gsi_end < info->io_gsi_base + info->io_npin)) {
- panic("ioapic_add: overlapped gsi, base %d npin %d, "
- "hit base %d, npin %d\n", gsi_base, npin,
- info->io_gsi_base, info->io_npin);
- }
- if (info->io_addr == addr)
- panic("ioapic_add: duplicated addr %p\n", addr);
- }
-
- ninfo = kmalloc(sizeof(*ninfo), M_DEVBUF, M_WAITOK | M_ZERO);
- ninfo->io_addr = addr;
- ninfo->io_npin = npin;
- ninfo->io_gsi_base = gsi_base;
- ninfo->io_apic_id = -1;
-
- /*
- * Create IOAPIC list in ascending order of GSI base
- */
- TAILQ_FOREACH_REVERSE(info, &ioapic_conf.ioc_list,
- ioapic_info_list, io_link) {
- if (ninfo->io_gsi_base > info->io_gsi_base) {
- TAILQ_INSERT_AFTER(&ioapic_conf.ioc_list,
- info, ninfo, io_link);
- break;
- }
- }
- if (info == NULL)
- TAILQ_INSERT_HEAD(&ioapic_conf.ioc_list, ninfo, io_link);
-}
-
-void
-ioapic_intsrc(int irq, int gsi, enum intr_trigger trig, enum intr_polarity pola)
-{
- struct ioapic_intsrc *int_src;
-
- KKASSERT(irq < 16);
- int_src = &ioapic_conf.ioc_intsrc[irq];
-
- if (gsi == 0) {
- /* Don't allow mixed mode */
- kprintf("IOAPIC: warning intsrc irq %d -> gsi 0\n", irq);
- return;
- }
-
- if (int_src->int_gsi != -1) {
- if (int_src->int_gsi != gsi) {
- kprintf("IOAPIC: warning intsrc irq %d, gsi "
- "%d -> %d\n", irq, int_src->int_gsi, gsi);
- }
- if (int_src->int_trig != trig) {
- kprintf("IOAPIC: warning intsrc irq %d, trig "
- "%s -> %s\n", irq,
- intr_str_trigger(int_src->int_trig),
- intr_str_trigger(trig));
- }
- if (int_src->int_pola != pola) {
- kprintf("IOAPIC: warning intsrc irq %d, pola "
- "%s -> %s\n", irq,
- intr_str_polarity(int_src->int_pola),
- intr_str_polarity(pola));
- }
- }
- int_src->int_gsi = gsi;
- int_src->int_trig = trig;
- int_src->int_pola = pola;
-}
-
-static void
-ioapic_set_apic_id(const struct ioapic_info *info)
-{
- uint32_t id;
- int apic_id;
-
- id = ioapic_read(info->io_addr, IOAPIC_ID);
-
- id &= ~APIC_ID_MASK;
- id |= (info->io_apic_id << 24);
-
- ioapic_write(info->io_addr, IOAPIC_ID, id);
-
- /*
- * Re-read && test
- */
- id = ioapic_read(info->io_addr, IOAPIC_ID);
- apic_id = (id & APIC_ID_MASK) >> 24;
-
- /*
- * I/O APIC ID is a 4bits field
- */
- if ((apic_id & IOAPIC_ID_MASK) !=
- (info->io_apic_id & IOAPIC_ID_MASK)) {
- panic("ioapic_set_apic_id: can't set apic id to %d, "
- "currently set to %d\n", info->io_apic_id, apic_id);
- }
-}
-
-static void
-ioapic_gsi_setup(int gsi)
-{
- enum intr_trigger trig;
- enum intr_polarity pola;
- int irq;
-
- if (gsi == 0) {
- /* ExtINT */
- imen_lock();
- ioapic_extpin_setup(ioapic_gsi_ioaddr(gsi),
- ioapic_gsi_pin(gsi), 0);
- imen_unlock();
- return;
- }
-
- trig = 0; /* silence older gcc's */
- pola = 0; /* silence older gcc's */
-
- for (irq = 0; irq < 16; ++irq) {
- const struct ioapic_intsrc *int_src =
- &ioapic_conf.ioc_intsrc[irq];
-
- if (gsi == int_src->int_gsi) {
- trig = int_src->int_trig;
- pola = int_src->int_pola;
- break;
- }
- }
-
- if (irq == 16) {
- if (gsi < 16) {
- trig = INTR_TRIGGER_EDGE;
- pola = INTR_POLARITY_HIGH;
- } else {
- trig = INTR_TRIGGER_LEVEL;
- pola = INTR_POLARITY_LOW;
- }
- irq = gsi;
- }
-
- ioapic_abi_set_irqmap(irq, gsi, trig, pola);
-}
-
-void *
-ioapic_gsi_ioaddr(int gsi)
-{
- const struct ioapic_info *info;
-
- info = ioapic_gsi_search(gsi);
- return info->io_addr;
-}
-
-int
-ioapic_gsi_pin(int gsi)
-{
- const struct ioapic_info *info;
-
- info = ioapic_gsi_search(gsi);
- return gsi - info->io_gsi_base;
-}
-
-static const struct ioapic_info *
-ioapic_gsi_search(int gsi)
-{
- const struct ioapic_info *info;
-
- TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link) {
- if (gsi >= info->io_gsi_base &&
- gsi < info->io_gsi_base + info->io_npin)
- return info;
- }
- panic("ioapic_gsi_search: no I/O APIC\n");
-}
-
-int
-ioapic_gsi(int idx, int pin)
-{
- const struct ioapic_info *info;
-
- TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link) {
- if (info->io_idx == idx)
- break;
- }
- if (info == NULL)
- return -1;
- if (pin >= info->io_npin)
- return -1;
- return info->io_gsi_base + pin;
-}
-
-void
-ioapic_extpin_setup(void *addr, int pin, int vec)
-{
- ioapic_pin_prog(addr, pin, vec,
- INTR_TRIGGER_CONFORM, INTR_POLARITY_CONFORM, IOART_DELEXINT);
-}
-
-int
-ioapic_extpin_gsi(void)
-{
- return 0;
-}
-
-void
-ioapic_pin_setup(void *addr, int pin, int vec,
- enum intr_trigger trig, enum intr_polarity pola)
-{
- /*
- * Always clear an I/O APIC pin before [re]programming it. This is
- * particularly important if the pin is set up for a level interrupt
- * as the IOART_REM_IRR bit might be set. When we reprogram the
- * vector any EOI from pending ints on this pin could be lost and
- * IRR might never get reset.
- *
- * To fix this problem, clear the vector and make sure it is
- * programmed as an edge interrupt. This should theoretically
- * clear IRR so we can later, safely program it as a level
- * interrupt.
- */
- ioapic_pin_prog(addr, pin, vec, INTR_TRIGGER_EDGE, INTR_POLARITY_HIGH,
- IOART_DELFIXED);
- ioapic_pin_prog(addr, pin, vec, trig, pola, IOART_DELFIXED);
-}
-
-static void
-ioapic_pin_prog(void *addr, int pin, int vec,
- enum intr_trigger trig, enum intr_polarity pola, uint32_t del_mode)
-{
- uint32_t flags, target;
- int select;
-
- KKASSERT(del_mode == IOART_DELEXINT || del_mode == IOART_DELFIXED);
-
- select = IOAPIC_REDTBL0 + (2 * pin);
-
- flags = ioapic_read(addr, select) & IOART_RESV;
- flags |= IOART_INTMSET | IOART_DESTPHY;
-#ifdef foo
- flags |= del_mode;
-#else
- /*
- * We only support limited I/O APIC mixed mode,
- * so even for ExtINT, we still use "fixed"
- * delivery mode.
- */
- flags |= IOART_DELFIXED;
-#endif
-
- if (del_mode == IOART_DELEXINT) {
- KKASSERT(trig == INTR_TRIGGER_CONFORM &&
- pola == INTR_POLARITY_CONFORM);
- flags |= IOART_TRGREDG | IOART_INTAHI;
- } else {
- switch (trig) {
- case INTR_TRIGGER_EDGE:
- flags |= IOART_TRGREDG;
- break;
-
- case INTR_TRIGGER_LEVEL:
- flags |= IOART_TRGRLVL;
- break;
-
- case INTR_TRIGGER_CONFORM:
- panic("ioapic_pin_prog: trig conform is not "
- "supported\n");
- }
- switch (pola) {
- case INTR_POLARITY_HIGH:
- flags |= IOART_INTAHI;
- break;
-
- case INTR_POLARITY_LOW:
- flags |= IOART_INTALO;
- break;
-
- case INTR_POLARITY_CONFORM:
- panic("ioapic_pin_prog: pola conform is not "
- "supported\n");
- }
- }
-
- target = ioapic_read(addr, select + 1) & IOART_HI_DEST_RESV;
- target |= (CPU_TO_ID(0) << IOART_HI_DEST_SHIFT) &
- IOART_HI_DEST_MASK;
-
- ioapic_write(addr, select, flags | vec);
- ioapic_write(addr, select + 1, target);
-}
-
-static void
-ioapic_setup(const struct ioapic_info *info)
-{
- int i;
-
- ioapic_set_apic_id(info);
-
- for (i = 0; i < info->io_npin; ++i)
- ioapic_gsi_setup(info->io_gsi_base + i);
-}
-
-static int
-ioapic_alloc_apic_id(int start)
-{
- for (;;) {
- const struct ioapic_info *info;
- int apic_id, apic_id16;
-
- apic_id = lapic_unused_apic_id(start);
- if (apic_id == NAPICID) {
- kprintf("IOAPIC: can't find unused APIC ID\n");
- return apic_id;
- }
- apic_id16 = apic_id & IOAPIC_ID_MASK;
-
- /*
- * Check against other I/O APIC's APIC ID's lower 4bits.
- *
- * The new APIC ID will have to be different from others
- * in the lower 4bits, no matter whether xAPIC is used
- * or not.
- */
- TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link) {
- if (info->io_apic_id == -1) {
- info = NULL;
- break;
- }
- if ((info->io_apic_id & IOAPIC_ID_MASK) == apic_id16)
- break;
- }
- if (info == NULL)
- return apic_id;
-
- kprintf("IOAPIC: APIC ID %d has same lower 4bits as "
- "%dth I/O APIC, keep searching...\n",
- apic_id, info->io_idx);
-
- start = apic_id + 1;
- }
- panic("ioapic_unused_apic_id: never reached\n");
-}