Changeset View
Changeset View
Standalone View
Standalone View
sys/x86/x86/io_apic.c
Show All 21 Lines | |||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | * 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 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||||
* SUCH DAMAGE. | * SUCH DAMAGE. | ||||
*/ | */ | ||||
#include <sys/cdefs.h> | #include <sys/cdefs.h> | ||||
__FBSDID("$FreeBSD$"); | __FBSDID("$FreeBSD$"); | ||||
#include "opt_acpi.h" | |||||
#include "opt_isa.h" | #include "opt_isa.h" | ||||
#include <sys/param.h> | #include <sys/param.h> | ||||
#include <sys/systm.h> | #include <sys/systm.h> | ||||
#include <sys/bus.h> | #include <sys/bus.h> | ||||
#include <sys/kernel.h> | #include <sys/kernel.h> | ||||
#include <sys/lock.h> | #include <sys/lock.h> | ||||
#include <sys/malloc.h> | #include <sys/malloc.h> | ||||
#include <sys/module.h> | #include <sys/module.h> | ||||
#include <sys/mutex.h> | #include <sys/mutex.h> | ||||
#include <sys/sysctl.h> | #include <sys/sysctl.h> | ||||
#include <dev/pci/pcireg.h> | #include <dev/pci/pcireg.h> | ||||
#include <dev/pci/pcivar.h> | #include <dev/pci/pcivar.h> | ||||
#include <vm/vm.h> | #include <vm/vm.h> | ||||
#include <vm/pmap.h> | #include <vm/pmap.h> | ||||
#include <x86/apicreg.h> | #include <x86/apicreg.h> | ||||
#include <machine/frame.h> | #include <machine/frame.h> | ||||
#include <machine/intr_machdep.h> | #include <machine/intr_machdep.h> | ||||
#include <x86/apicvar.h> | #include <x86/apicvar.h> | ||||
#include <machine/resource.h> | #include <machine/resource.h> | ||||
#include <machine/segments.h> | #include <machine/segments.h> | ||||
#include <x86/iommu/iommu_intrmap.h> | |||||
#define IOAPIC_ISA_INTS 16 | #define IOAPIC_ISA_INTS 16 | ||||
#define IOAPIC_MEM_REGION 32 | #define IOAPIC_MEM_REGION 32 | ||||
#define IOAPIC_REDTBL_LO(i) (IOAPIC_REDTBL + (i) * 2) | #define IOAPIC_REDTBL_LO(i) (IOAPIC_REDTBL + (i) * 2) | ||||
#define IOAPIC_REDTBL_HI(i) (IOAPIC_REDTBL_LO(i) + 1) | #define IOAPIC_REDTBL_HI(i) (IOAPIC_REDTBL_LO(i) + 1) | ||||
#define IRQ_EXTINT (NUM_IO_INTS + 1) | |||||
#define IRQ_NMI (NUM_IO_INTS + 2) | |||||
#define IRQ_SMI (NUM_IO_INTS + 3) | |||||
#define IRQ_DISABLED (NUM_IO_INTS + 4) | |||||
static MALLOC_DEFINE(M_IOAPIC, "io_apic", "I/O APIC structures"); | static MALLOC_DEFINE(M_IOAPIC, "io_apic", "I/O APIC structures"); | ||||
/* | /* | ||||
* I/O APIC interrupt source driver. Each pin is assigned an IRQ cookie | * I/O APIC interrupt source driver. Each pin is assigned an IRQ cookie | ||||
* as laid out in the ACPI System Interrupt number model where each I/O | * as laid out in the ACPI System Interrupt number model where each I/O | ||||
* APIC has a contiguous chunk of the System Interrupt address space. | * APIC has a contiguous chunk of the System Interrupt address space. | ||||
* We assume that IRQs 1 - 15 behave like ISA IRQs and that all other | * We assume that IRQs 1 - 15 behave like ISA IRQs and that all other | ||||
* IRQs behave as PCI IRQs by default. We also assume that the pin for | * IRQs behave as PCI IRQs by default. We also assume that the pin for | ||||
* IRQ 0 is actually an ExtINT pin. The apic enumerators override the | * IRQ 0 is actually an ExtINT pin. The apic enumerators override the | ||||
* configuration of individual pins as indicated by their tables. | * configuration of individual pins as indicated by their tables. | ||||
* | * | ||||
* Documentation for the I/O APIC: "82093AA I/O Advanced Programmable | * Documentation for the I/O APIC: "82093AA I/O Advanced Programmable | ||||
* Interrupt Controller (IOAPIC)", May 1996, Intel Corp. | * Interrupt Controller (IOAPIC)", May 1996, Intel Corp. | ||||
* ftp://download.intel.com/design/chipsets/datashts/29056601.pdf | * ftp://download.intel.com/design/chipsets/datashts/29056601.pdf | ||||
*/ | */ | ||||
struct ioapic_intsrc { | struct ioapic_intsrc { | ||||
struct intsrc io_intsrc; | struct intsrc io_intsrc; | ||||
u_int io_irq; | u_int io_irq; | ||||
u_int io_intpin:8; | u_int io_intpin:8; | ||||
u_int io_vector:8; | u_int io_vector:8; | ||||
u_int io_cpu:8; | u_int io_cpu; | ||||
u_int io_activehi:1; | u_int io_activehi:1; | ||||
u_int io_edgetrigger:1; | u_int io_edgetrigger:1; | ||||
u_int io_masked:1; | u_int io_masked:1; | ||||
int io_bus:4; | int io_bus:4; | ||||
uint32_t io_lowreg; | uint32_t io_lowreg; | ||||
u_int io_remap_cookie; | |||||
}; | }; | ||||
struct ioapic { | struct ioapic { | ||||
struct pic io_pic; | struct pic io_pic; | ||||
u_int io_id:8; /* logical ID */ | u_int io_id:8; /* logical ID */ | ||||
u_int io_apic_id:4; | u_int io_apic_id:4; | ||||
u_int io_intbase:8; /* System Interrupt base */ | u_int io_intbase:8; /* System Interrupt base */ | ||||
u_int io_numintr:8; | u_int io_numintr:8; | ||||
Show All 15 Lines | |||||
static void ioapic_disable_intr(struct intsrc *isrc); | static void ioapic_disable_intr(struct intsrc *isrc); | ||||
static int ioapic_vector(struct intsrc *isrc); | static int ioapic_vector(struct intsrc *isrc); | ||||
static int ioapic_source_pending(struct intsrc *isrc); | static int ioapic_source_pending(struct intsrc *isrc); | ||||
static int ioapic_config_intr(struct intsrc *isrc, enum intr_trigger trig, | static int ioapic_config_intr(struct intsrc *isrc, enum intr_trigger trig, | ||||
enum intr_polarity pol); | enum intr_polarity pol); | ||||
static void ioapic_resume(struct pic *pic, bool suspend_cancelled); | static void ioapic_resume(struct pic *pic, bool suspend_cancelled); | ||||
static int ioapic_assign_cpu(struct intsrc *isrc, u_int apic_id); | static int ioapic_assign_cpu(struct intsrc *isrc, u_int apic_id); | ||||
static void ioapic_program_intpin(struct ioapic_intsrc *intpin); | static void ioapic_program_intpin(struct ioapic_intsrc *intpin); | ||||
static void ioapic_reprogram_intpin(struct intsrc *isrc); | |||||
static STAILQ_HEAD(,ioapic) ioapic_list = STAILQ_HEAD_INITIALIZER(ioapic_list); | static STAILQ_HEAD(,ioapic) ioapic_list = STAILQ_HEAD_INITIALIZER(ioapic_list); | ||||
struct pic ioapic_template = { ioapic_enable_source, ioapic_disable_source, | struct pic ioapic_template = { | ||||
ioapic_eoi_source, ioapic_enable_intr, | .pic_enable_source = ioapic_enable_source, | ||||
ioapic_disable_intr, ioapic_vector, | .pic_disable_source = ioapic_disable_source, | ||||
ioapic_source_pending, NULL, ioapic_resume, | .pic_eoi_source = ioapic_eoi_source, | ||||
ioapic_config_intr, ioapic_assign_cpu }; | .pic_enable_intr = ioapic_enable_intr, | ||||
.pic_disable_intr = ioapic_disable_intr, | |||||
.pic_vector = ioapic_vector, | |||||
.pic_source_pending = ioapic_source_pending, | |||||
.pic_suspend = NULL, | |||||
.pic_resume = ioapic_resume, | |||||
.pic_config_intr = ioapic_config_intr, | |||||
.pic_assign_cpu = ioapic_assign_cpu, | |||||
.pic_reprogram_pin = ioapic_reprogram_intpin, | |||||
}; | |||||
static int next_ioapic_base; | static int next_ioapic_base; | ||||
static u_int next_id; | static u_int next_id; | ||||
static int enable_extint; | static int enable_extint; | ||||
SYSCTL_INT(_hw_apic, OID_AUTO, enable_extint, CTLFLAG_RDTUN, &enable_extint, 0, | SYSCTL_INT(_hw_apic, OID_AUTO, enable_extint, CTLFLAG_RDTUN, &enable_extint, 0, | ||||
"Enable the ExtINT pin in the first I/O APIC"); | "Enable the ExtINT pin in the first I/O APIC"); | ||||
▲ Show 20 Lines • Show All 152 Lines • ▼ Show 20 Lines | |||||
* Completely program an intpin based on the data in its interrupt source | * Completely program an intpin based on the data in its interrupt source | ||||
* structure. | * structure. | ||||
*/ | */ | ||||
static void | static void | ||||
ioapic_program_intpin(struct ioapic_intsrc *intpin) | ioapic_program_intpin(struct ioapic_intsrc *intpin) | ||||
{ | { | ||||
struct ioapic *io = (struct ioapic *)intpin->io_intsrc.is_pic; | struct ioapic *io = (struct ioapic *)intpin->io_intsrc.is_pic; | ||||
uint32_t low, high, value; | uint32_t low, high, value; | ||||
#ifdef ACPI_DMAR | |||||
int error; | |||||
#endif | |||||
/* | /* | ||||
* If a pin is completely invalid or if it is valid but hasn't | * If a pin is completely invalid or if it is valid but hasn't | ||||
* been enabled yet, just ensure that the pin is masked. | * been enabled yet, just ensure that the pin is masked. | ||||
*/ | */ | ||||
mtx_assert(&icu_lock, MA_OWNED); | mtx_assert(&icu_lock, MA_OWNED); | ||||
if (intpin->io_irq == IRQ_DISABLED || (intpin->io_irq < NUM_IO_INTS && | if (intpin->io_irq == IRQ_DISABLED || (intpin->io_irq < NUM_IO_INTS && | ||||
intpin->io_vector == 0)) { | intpin->io_vector == 0)) { | ||||
low = ioapic_read(io->io_addr, | low = ioapic_read(io->io_addr, | ||||
IOAPIC_REDTBL_LO(intpin->io_intpin)); | IOAPIC_REDTBL_LO(intpin->io_intpin)); | ||||
if ((low & IOART_INTMASK) == IOART_INTMCLR) | if ((low & IOART_INTMASK) == IOART_INTMCLR) | ||||
ioapic_write(io->io_addr, | ioapic_write(io->io_addr, | ||||
IOAPIC_REDTBL_LO(intpin->io_intpin), | IOAPIC_REDTBL_LO(intpin->io_intpin), | ||||
low | IOART_INTMSET); | low | IOART_INTMSET); | ||||
#ifdef ACPI_DMAR | |||||
mtx_unlock_spin(&icu_lock); | |||||
iommu_unmap_ioapic_intr(io->io_apic_id, | |||||
&intpin->io_remap_cookie); | |||||
mtx_lock_spin(&icu_lock); | |||||
#endif | |||||
return; | return; | ||||
} | } | ||||
#ifdef ACPI_DMAR | |||||
mtx_unlock_spin(&icu_lock); | |||||
error = iommu_map_ioapic_intr(io->io_apic_id, | |||||
intpin->io_cpu, intpin->io_vector, intpin->io_edgetrigger, | |||||
intpin->io_activehi, intpin->io_irq, &intpin->io_remap_cookie, | |||||
&high, &low); | |||||
mtx_lock_spin(&icu_lock); | |||||
if (error == 0) { | |||||
ioapic_write(io->io_addr, IOAPIC_REDTBL_HI(intpin->io_intpin), | |||||
high); | |||||
intpin->io_lowreg = low; | |||||
ioapic_write(io->io_addr, IOAPIC_REDTBL_LO(intpin->io_intpin), | |||||
low); | |||||
return; | |||||
} else if (error != EOPNOTSUPP) { | |||||
return; | |||||
} | |||||
#endif | |||||
/* Set the destination. */ | /* Set the destination. */ | ||||
low = IOART_DESTPHY; | low = IOART_DESTPHY; | ||||
high = intpin->io_cpu << APIC_ID_SHIFT; | high = intpin->io_cpu << APIC_ID_SHIFT; | ||||
/* Program the rest of the low word. */ | /* Program the rest of the low word. */ | ||||
if (intpin->io_edgetrigger) | if (intpin->io_edgetrigger) | ||||
low |= IOART_TRGREDG; | low |= IOART_TRGREDG; | ||||
else | else | ||||
Show All 30 Lines | #endif | ||||
value = ioapic_read(io->io_addr, IOAPIC_REDTBL_HI(intpin->io_intpin)); | value = ioapic_read(io->io_addr, IOAPIC_REDTBL_HI(intpin->io_intpin)); | ||||
value &= ~IOART_DEST; | value &= ~IOART_DEST; | ||||
value |= high; | value |= high; | ||||
ioapic_write(io->io_addr, IOAPIC_REDTBL_HI(intpin->io_intpin), value); | ioapic_write(io->io_addr, IOAPIC_REDTBL_HI(intpin->io_intpin), value); | ||||
intpin->io_lowreg = low; | intpin->io_lowreg = low; | ||||
ioapic_write(io->io_addr, IOAPIC_REDTBL_LO(intpin->io_intpin), low); | ioapic_write(io->io_addr, IOAPIC_REDTBL_LO(intpin->io_intpin), low); | ||||
} | } | ||||
static void | |||||
ioapic_reprogram_intpin(struct intsrc *isrc) | |||||
{ | |||||
mtx_lock_spin(&icu_lock); | |||||
ioapic_program_intpin((struct ioapic_intsrc *)isrc); | |||||
mtx_unlock_spin(&icu_lock); | |||||
} | |||||
static int | static int | ||||
ioapic_assign_cpu(struct intsrc *isrc, u_int apic_id) | ioapic_assign_cpu(struct intsrc *isrc, u_int apic_id) | ||||
{ | { | ||||
struct ioapic_intsrc *intpin = (struct ioapic_intsrc *)isrc; | struct ioapic_intsrc *intpin = (struct ioapic_intsrc *)isrc; | ||||
struct ioapic *io = (struct ioapic *)isrc->is_pic; | struct ioapic *io = (struct ioapic *)isrc->is_pic; | ||||
u_int old_vector, new_vector; | u_int old_vector, new_vector; | ||||
u_int old_id; | u_int old_id; | ||||
▲ Show 20 Lines • Show All 269 Lines • ▼ Show 20 Lines | for (i = 0, intpin = io->io_pins; i < numintr; i++, intpin++) { | ||||
/* | /* | ||||
* Route interrupts to the BSP by default. Interrupts may | * Route interrupts to the BSP by default. Interrupts may | ||||
* be routed to other CPUs later after they are enabled. | * be routed to other CPUs later after they are enabled. | ||||
*/ | */ | ||||
intpin->io_cpu = PCPU_GET(apic_id); | intpin->io_cpu = PCPU_GET(apic_id); | ||||
value = ioapic_read(apic, IOAPIC_REDTBL_LO(i)); | value = ioapic_read(apic, IOAPIC_REDTBL_LO(i)); | ||||
ioapic_write(apic, IOAPIC_REDTBL_LO(i), value | IOART_INTMSET); | ioapic_write(apic, IOAPIC_REDTBL_LO(i), value | IOART_INTMSET); | ||||
#ifdef ACPI_DMAR | |||||
/* dummy, but sets cookie */ | |||||
mtx_unlock_spin(&icu_lock); | |||||
iommu_map_ioapic_intr(io->io_apic_id, | |||||
intpin->io_cpu, intpin->io_vector, intpin->io_edgetrigger, | |||||
intpin->io_activehi, intpin->io_irq, | |||||
&intpin->io_remap_cookie, NULL, NULL); | |||||
mtx_lock_spin(&icu_lock); | |||||
#endif | |||||
} | } | ||||
mtx_unlock_spin(&icu_lock); | mtx_unlock_spin(&icu_lock); | ||||
return (io); | return (io); | ||||
} | } | ||||
int | int | ||||
ioapic_get_vector(void *cookie, u_int pin) | ioapic_get_vector(void *cookie, u_int pin) | ||||
▲ Show 20 Lines • Show All 422 Lines • Show Last 20 Lines |