From 3e8e985f67fe86379387b32b7d08095c7a81b505 Mon Sep 17 00:00:00 2001 From: Sepherosa Ziehau Date: Wed, 4 May 2011 16:19:17 +0800 Subject: [PATCH] x86_64: Split mpapic.c into lapic.c and ioapic.c --- sys/platform/pc64/apic/ioapic.c | 567 +++++++++++++++++++ sys/platform/pc64/apic/{mpapic.c => lapic.c} | 527 +---------------- sys/platform/pc64/apic/mpapic.h | 1 + sys/platform/pc64/conf/files | 3 +- 4 files changed, 571 insertions(+), 527 deletions(-) create mode 100644 sys/platform/pc64/apic/ioapic.c rename sys/platform/pc64/apic/{mpapic.c => lapic.c} (60%) diff --git a/sys/platform/pc64/apic/ioapic.c b/sys/platform/pc64/apic/ioapic.c new file mode 100644 index 0000000000..4336aecefc --- /dev/null +++ b/sys/platform/pc64/apic/ioapic.c @@ -0,0 +1,567 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "apicvar.h" + +#define IOAPIC_COUNT_MAX 16 +#define IOAPIC_ID_MASK (IOAPIC_COUNT_MAX - 1) + +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_enumerator *e; + struct ioapic_info *info; + int start_apic_id = 0; + int error, i; + register_t 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_rflags(); + 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_rflags(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; + } + + 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"); +} diff --git a/sys/platform/pc64/apic/mpapic.c b/sys/platform/pc64/apic/lapic.c similarity index 60% rename from sys/platform/pc64/apic/mpapic.c rename to sys/platform/pc64/apic/lapic.c index b4b0237872..b56d40e678 100644 --- a/sys/platform/pc64/apic/mpapic.c +++ b/sys/platform/pc64/apic/lapic.c @@ -43,31 +43,6 @@ #include "apicvar.h" -#define IOAPIC_COUNT_MAX 16 -#define IOAPIC_ID_MASK (IOAPIC_COUNT_MAX - 1) - -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 */ -}; - volatile lapic_t *lapic; static void lapic_timer_calibrate(void); @@ -87,17 +62,6 @@ static void lapic_timer_intr_enable(struct cputimer_intr *); 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, @@ -120,8 +84,6 @@ static const uint32_t lapic_timer_divisors[] = { }; #define APIC_TIMER_NDIVISORS (int)(NELEM(lapic_timer_divisors)) -static struct ioapic_conf ioapic_conf; - void lapic_eoi(void) { @@ -722,7 +684,7 @@ u_sleep(int count) /* spin */ ; } -static int +int lapic_unused_apic_id(int start) { int i; @@ -778,490 +740,3 @@ lapic_enumerator_register(struct lapic_enumerator *ne) } 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_enumerator *e; - struct ioapic_info *info; - int start_apic_id = 0; - int error, i; - register_t 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_rflags(); - 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_rflags(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; - } - - 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"); -} diff --git a/sys/platform/pc64/apic/mpapic.h b/sys/platform/pc64/apic/mpapic.h index 6d6ad6c835..ff0f952713 100644 --- a/sys/platform/pc64/apic/mpapic.h +++ b/sys/platform/pc64/apic/mpapic.h @@ -76,5 +76,6 @@ all_but_self_ipi(int vector) #endif void lapic_map(vm_offset_t /* XXX should be vm_paddr_t */); +int lapic_unused_apic_id(int); #endif /* _MACHINE_MPAPIC_H */ diff --git a/sys/platform/pc64/conf/files b/sys/platform/pc64/conf/files index db67cce947..b4e64f235c 100644 --- a/sys/platform/pc64/conf/files +++ b/sys/platform/pc64/conf/files @@ -167,8 +167,9 @@ platform/pc64/x86_64/identcpu.c standard platform/pc64/x86_64/amd64_mem.c standard platform/pc64/x86_64/cpufreq_machdep.c standard +platform/pc64/apic/lapic.c optional smp +platform/pc64/apic/ioapic.c optional smp platform/pc64/apic/ioapic_abi.c optional smp -platform/pc64/apic/mpapic.c optional smp platform/pc64/apic/ioapic_ipl.s optional smp platform/pc64/apic/apic_vector.s optional smp -- 2.41.0