diff options
author | Richard Braun <rbraun@sceen.net> | 2017-05-23 20:34:58 +0200 |
---|---|---|
committer | Richard Braun <rbraun@sceen.net> | 2017-05-23 20:38:11 +0200 |
commit | 9b1787cd1c821aa58a88795d68b65d4f5dc7dad0 (patch) | |
tree | f3a4f898dcc511fedc3a0103abf734970b67a0be | |
parent | d3742166baf0eca95875c61d5a72174b8e7bd0b2 (diff) |
x86/ioapic: new module
-rw-r--r-- | arch/x86/Makefrag.am | 2 | ||||
-rw-r--r-- | arch/x86/machine/acpimp.c | 27 | ||||
-rw-r--r-- | arch/x86/machine/ioapic.c | 265 | ||||
-rw-r--r-- | arch/x86/machine/ioapic.h | 41 |
4 files changed, 334 insertions, 1 deletions
diff --git a/arch/x86/Makefrag.am b/arch/x86/Makefrag.am index d5f1583a..6adc769f 100644 --- a/arch/x86/Makefrag.am +++ b/arch/x86/Makefrag.am @@ -41,6 +41,8 @@ x15_SOURCES += \ arch/x86/machine/cpu.h \ arch/x86/machine/elf.h \ arch/x86/machine/io.h \ + arch/x86/machine/ioapic.c \ + arch/x86/machine/ioapic.h \ arch/x86/machine/lapic.c \ arch/x86/machine/lapic.h \ arch/x86/machine/multiboot.h \ diff --git a/arch/x86/machine/acpimp.c b/arch/x86/machine/acpimp.c index 8d2433a9..4e98683a 100644 --- a/arch/x86/machine/acpimp.c +++ b/arch/x86/machine/acpimp.c @@ -29,6 +29,7 @@ #include <machine/biosmem.h> #include <machine/cpu.h> #include <machine/io.h> +#include <machine/ioapic.h> #include <machine/lapic.h> #include <machine/types.h> #include <vm/vm_kmem.h> @@ -76,7 +77,8 @@ struct acpimp_rsdt { /* * MADT entry type codes. */ -#define ACPIMP_MADT_ENTRY_LAPIC 0 +#define ACPIMP_MADT_ENTRY_LAPIC 0 +#define ACPIMP_MADT_ENTRY_IOAPIC 1 struct acpimp_madt_entry_hdr { uint8_t type; @@ -92,10 +94,19 @@ struct acpimp_madt_entry_lapic { uint32_t flags; } __packed; +struct acpimp_madt_entry_ioapic { + struct acpimp_madt_entry_hdr header; + uint8_t id; + uint8_t _reserved; + uint32_t addr; + uint32_t base; +} __packed; + union acpimp_madt_entry { uint8_t type; struct acpimp_madt_entry_hdr header; struct acpimp_madt_entry_lapic lapic; + struct acpimp_madt_entry_ioapic ioapic; } __packed; struct acpimp_madt { @@ -467,6 +478,12 @@ acpimp_load_lapic(const struct acpimp_madt_entry_lapic *lapic, int *is_bsp) } static void __init +acpimp_load_ioapic(const struct acpimp_madt_entry_ioapic *ioapic) +{ + ioapic_register(ioapic->id, ioapic->addr, ioapic->base); +} + +static void __init acpimp_load_madt(void) { const struct acpimp_sdth *table; @@ -478,13 +495,21 @@ acpimp_load_madt(void) assert(table != NULL); madt = structof(table, struct acpimp_madt, header); lapic_setup(madt->lapic_addr); + ioapic_setup(); is_bsp = 1; + /* + * TODO Handle PCAT_COMPAT flag + * TODO Handle interrupt overrides + */ + acpimp_madt_foreach(madt, &iter) { switch (iter.entry->type) { case ACPIMP_MADT_ENTRY_LAPIC: acpimp_load_lapic(&iter.entry->lapic, &is_bsp); break; + case ACPIMP_MADT_ENTRY_IOAPIC: + acpimp_load_ioapic(&iter.entry->ioapic); } } } diff --git a/arch/x86/machine/ioapic.c b/arch/x86/machine/ioapic.c new file mode 100644 index 00000000..48faf8d7 --- /dev/null +++ b/arch/x86/machine/ioapic.c @@ -0,0 +1,265 @@ +/* + * Copyright (c) 2017 Richard Braun. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> + +#include <kern/assert.h> +#include <kern/error.h> +#include <kern/init.h> +#include <kern/kmem.h> +#include <kern/panic.h> +#include <kern/spinlock.h> +#include <machine/cpu.h> +#include <machine/ioapic.h> +#include <machine/lapic.h> +#include <machine/trap.h> +#include <vm/vm_kmem.h> + +#define IOAPIC_REG_VERSION 0x01 +#define IOAPIC_REG_IOREDTBL 0x10 + +#define IOAPIC_VERSION_VERSION_MASK 0x000000ff +#define IOAPIC_VERSION_VERSION_SHIFT 0 +#define IOAPIC_VERSION_MAXREDIR_MASK 0x00ff0000 +#define IOAPIC_VERSION_MAXREDIR_SHIFT 16 + +#define IOAPIC_ENTLOW_INTRMASK 0x10000 + +#define IOAPIC_MAX_ENTRIES 24 + +struct ioapic_map { + uint8_t regsel; + uint8_t _reserved0; + uint8_t _reserved1; + uint8_t _reserved2; + uint32_t _reserved3; + uint32_t _reserved4; + uint32_t _reserved5; + uint32_t win; +}; + +struct ioapic { + unsigned int id; + unsigned int version; + volatile struct ioapic_map *map; + unsigned int first_intr; + unsigned int last_intr; +}; + +static struct ioapic *ioapic_devs; +static unsigned int ioapic_nr_devs; + +/* + * Global lock. + * + * Interrupts must be disabled when holding this lock. + */ +static struct spinlock ioapic_lock; + +static struct ioapic * +ioapic_get(unsigned int id) +{ + assert(id < ioapic_nr_devs); + return &ioapic_devs[id]; +} + +static uint32_t +ioapic_read(struct ioapic *ioapic, uint8_t reg) +{ + ioapic->map->regsel = reg; + return ioapic->map->win; +} + +static void +ioapic_write(struct ioapic *ioapic, uint8_t reg, uint32_t value) +{ + ioapic->map->regsel = reg; + ioapic->map->win = value; +} + +static void +ioapic_write_entry_low(struct ioapic *ioapic, unsigned int id, uint32_t value) +{ + assert(id < IOAPIC_MAX_ENTRIES); + ioapic_write(ioapic, IOAPIC_REG_IOREDTBL + (id * 2), value); +} + +static void +ioapic_write_entry_high(struct ioapic *ioapic, unsigned int id, uint32_t value) +{ + assert(id < IOAPIC_MAX_ENTRIES); + ioapic_write(ioapic, IOAPIC_REG_IOREDTBL + (id * 2) + 1, value); +} + +static bool +ioapic_has_intr(const struct ioapic *ioapic, unsigned int intr) +{ + return ((intr >= ioapic->first_intr) && (intr <= ioapic->last_intr)); +} + +static unsigned int +ioapic_compute_id(const struct ioapic *ioapic, unsigned int intr) +{ + assert(ioapic_has_intr(ioapic, intr)); + return intr - ioapic->first_intr; +} + +static void +ioapic_enable_intr(struct ioapic *ioapic, unsigned int intr, + unsigned int cpu, unsigned int vector) +{ + unsigned int id; + + id = ioapic_compute_id(ioapic, intr); + ioapic_write_entry_high(ioapic, id, cpu_apic_id(cpu) << 24); + ioapic_write_entry_low(ioapic, id, vector); +} + +static void +ioapic_disable_intr(struct ioapic *ioapic, unsigned int intr) +{ + unsigned int id; + + id = ioapic_compute_id(ioapic, intr); + ioapic_write_entry_low(ioapic, id, IOAPIC_ENTLOW_INTRMASK); +} + +static void +ioapic_intr(struct trap_frame *frame) +{ + lapic_eoi(); + printf("ioapic: cpu:%u vector:%lu\n", cpu_id(), frame->vector); +} + +void __init +ioapic_setup(void) +{ + ioapic_devs = NULL; + ioapic_nr_devs = 0; + spinlock_init(&ioapic_lock); +} + +static void __init +ioapic_init(struct ioapic *ioapic, unsigned int id, + uintptr_t addr, unsigned int intr_base) +{ + unsigned int i, nr_intrs; + uint32_t value; + + ioapic->id = id; + ioapic->first_intr = intr_base; + + ioapic->map = vm_kmem_map_pa(addr, sizeof(*ioapic->map), NULL, NULL); + + if (ioapic->map == NULL) { + panic("ioapic: unable to map register window in kernel map"); + } + + value = ioapic_read(ioapic, IOAPIC_REG_VERSION); + ioapic->version = (value & IOAPIC_VERSION_VERSION_MASK) + >> IOAPIC_VERSION_VERSION_SHIFT; + nr_intrs = ((value & IOAPIC_VERSION_MAXREDIR_MASK) + >> IOAPIC_VERSION_MAXREDIR_SHIFT) + 1; + ioapic->last_intr = ioapic->first_intr + nr_intrs - 1; + + if (ioapic->last_intr > (TRAP_INTR_LAST - TRAP_INTR_FIRST)) { + panic("ioapic: invalid interrupt range"); + } + + printf("ioapic%u: version:%#x intrs:%u-%u\n", ioapic->id, + ioapic->version, ioapic->first_intr, ioapic->last_intr); + + for (i = ioapic->first_intr; i < ioapic->last_intr; i++) { + trap_register(TRAP_INTR_FIRST + i, ioapic_intr); + } +} + +void __init +ioapic_register(unsigned int id, uintptr_t addr, unsigned int intr_base) +{ + struct ioapic *tmp; + + spinlock_lock(&ioapic_lock); + + tmp = kmem_alloc((ioapic_nr_devs + 1) * sizeof(*ioapic_devs)); + + if (tmp == NULL) { + panic("ioapic: unable to allocate memory for device"); + } + + memcpy(tmp, ioapic_devs, ioapic_nr_devs); + kmem_free(ioapic_devs, ioapic_nr_devs * sizeof(*ioapic_devs)); + ioapic_devs = tmp; + ioapic_nr_devs++; + + ioapic_init(ioapic_get(ioapic_nr_devs - 1), id, addr, intr_base); + + spinlock_unlock(&ioapic_lock); +} + +static struct ioapic * +ioapic_lookup(unsigned int intr) +{ + struct ioapic *ioapic; + unsigned int i; + + for (i = 0; i < ioapic_nr_devs; i++) { + ioapic = &ioapic_devs[i]; + + if ((intr >= ioapic->first_intr) && (intr <= ioapic->last_intr)) { + return ioapic; + } + } + + return NULL; +} + +int +ioapic_enable(unsigned int intr, unsigned int cpu, unsigned int vector) +{ + struct ioapic *ioapic; + unsigned long flags; + int error; + + spinlock_lock_intr_save(&ioapic_lock, &flags); + + ioapic = ioapic_lookup(intr); + + if (ioapic == NULL) { + error = ERROR_NODEV; + goto out; + } + + ioapic_enable_intr(ioapic, intr, cpu, vector); + error = 0; + +out: + spinlock_unlock_intr_restore(&ioapic_lock, flags); + return error; +} + +void +ioapic_disable(unsigned int intr) +{ + unsigned long flags; + + spinlock_lock_intr_save(&ioapic_lock, &flags); + ioapic_disable_intr(ioapic_lookup(intr), intr); + spinlock_unlock_intr_restore(&ioapic_lock, flags); +} diff --git a/arch/x86/machine/ioapic.h b/arch/x86/machine/ioapic.h new file mode 100644 index 00000000..daebdb19 --- /dev/null +++ b/arch/x86/machine/ioapic.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2017 Richard Braun. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef _KERN_IOAPIC_H +#define _KERN_IOAPIC_H + +#include <stdint.h> + +/* + * Initialize the ioapic module. + */ +void ioapic_setup(void); + +/* + * Register an I/O APIC controller. + */ +void ioapic_register(unsigned int id, uintptr_t addr, unsigned int gsi_base); + +/* + * Enable/disable an interrupt line. + * + * The given interrupt is routed to the given (cpu, vector) destination. + */ +int ioapic_enable(unsigned int intr, unsigned int cpu, unsigned int vector); +void ioapic_disable(unsigned int intr); + +#endif /* _KERN_IOAPIC_H */ |