diff options
author | Richard Braun <rbraun@sceen.net> | 2017-05-26 22:39:31 +0200 |
---|---|---|
committer | Richard Braun <rbraun@sceen.net> | 2017-05-26 22:40:01 +0200 |
commit | cab8846284996df401c5bdaa0088b224e076b510 (patch) | |
tree | 884c1dc40cbcf47e763da9c1c470aec73165c0a3 | |
parent | 10f2d5f59a37532fb48cf9d6896efa238c9c6eb5 (diff) |
x86: use the new intr module
In particular, the pic and ioapic modules register their respective
devices as interrupt controllers. Selection between the legacy XT-PIC
and the modern APIC system is made on the availability of ACPI,
disregarding the multiprocessor specification entirely. The uart
driver is also updated to register devices interrupt handlers.
-rw-r--r-- | arch/x86/machine/acpimp.c | 1 | ||||
-rw-r--r-- | arch/x86/machine/boot.c | 7 | ||||
-rw-r--r-- | arch/x86/machine/cpu.c | 9 | ||||
-rw-r--r-- | arch/x86/machine/ioapic.c | 182 | ||||
-rw-r--r-- | arch/x86/machine/ioapic.h | 13 | ||||
-rw-r--r-- | arch/x86/machine/lapic.c | 23 | ||||
-rw-r--r-- | arch/x86/machine/lapic.h | 14 | ||||
-rw-r--r-- | arch/x86/machine/param.h | 5 | ||||
-rw-r--r-- | arch/x86/machine/pic.c | 146 | ||||
-rw-r--r-- | arch/x86/machine/uart.c | 39 | ||||
-rw-r--r-- | arch/x86/machine/uart.h | 10 |
11 files changed, 293 insertions, 156 deletions
diff --git a/arch/x86/machine/acpimp.c b/arch/x86/machine/acpimp.c index 4e98683..bba2b8c 100644 --- a/arch/x86/machine/acpimp.c +++ b/arch/x86/machine/acpimp.c @@ -495,7 +495,6 @@ acpimp_load_madt(void) assert(table != NULL); madt = structof(table, struct acpimp_madt, header); lapic_setup(madt->lapic_addr); - ioapic_setup(); is_bsp = 1; /* diff --git a/arch/x86/machine/boot.c b/arch/x86/machine/boot.c index 1830600..0df9674 100644 --- a/arch/x86/machine/boot.c +++ b/arch/x86/machine/boot.c @@ -51,6 +51,7 @@ #include <kern/arg.h> #include <kern/console.h> #include <kern/init.h> +#include <kern/intr.h> #include <kern/kmem.h> #include <kern/kernel.h> #include <kern/macros.h> @@ -468,7 +469,7 @@ boot_main(void) thread_bootstrap(); console_setup(); cga_setup(); - uart_setup(); + uart_bootstrap(); printf_setup(); boot_show_version(); arg_info(); @@ -481,8 +482,10 @@ boot_main(void) vm_setup(); boot_save_data(); biosmem_free_usable(); - pic_setup(); + intr_setup(); cpu_mp_probe(); + pic_setup(); + uart_setup(); kernel_main(); /* Never reached */ diff --git a/arch/x86/machine/cpu.c b/arch/x86/machine/cpu.c index d62b68f..fbc1f95 100644 --- a/arch/x86/machine/cpu.c +++ b/arch/x86/machine/cpu.c @@ -650,9 +650,14 @@ cpu_mp_probe(void) error = acpimp_setup(); - /* TODO Support UP with legacy PIC */ if (error) { - panic("cpu: ACPI required to initialize local APIC"); + /* + * For the sake of simplicity, it has been decided to ignore legacy + * specifications such as the multiprocessor specification, and use + * ACPI only. If ACPI is unavailable, consider the APIC system to + * be missing and fall back to using the legaxy XT-PIC. + */ + lapic_setup_unused(); } printf("cpu: %u processor(s) configured\n", cpu_count()); diff --git a/arch/x86/machine/ioapic.c b/arch/x86/machine/ioapic.c index 48faf8d..4517c3f 100644 --- a/arch/x86/machine/ioapic.c +++ b/arch/x86/machine/ioapic.c @@ -22,6 +22,7 @@ #include <kern/assert.h> #include <kern/error.h> #include <kern/init.h> +#include <kern/intr.h> #include <kern/kmem.h> #include <kern/panic.h> #include <kern/spinlock.h> @@ -55,6 +56,7 @@ struct ioapic_map { }; struct ioapic { + struct spinlock lock; unsigned int id; unsigned int version; volatile struct ioapic_map *map; @@ -62,23 +64,6 @@ struct ioapic { 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) { @@ -107,61 +92,26 @@ ioapic_write_entry_high(struct ioapic *ioapic, unsigned int id, uint32_t value) 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); + intr_handle(frame->vector - TRAP_INTR_FIRST); } -static void __init -ioapic_init(struct ioapic *ioapic, unsigned int id, - uintptr_t addr, unsigned int intr_base) +static struct ioapic * __init +ioapic_create(unsigned int id, uintptr_t addr, unsigned int intr_base) { + struct ioapic *ioapic; unsigned int i, nr_intrs; uint32_t value; + ioapic = kmem_alloc(sizeof(*ioapic)); + + if (ioapic == NULL) { + panic("ioapic: unable to allocate memory for controller"); + } + + spinlock_init(&ioapic->lock); ioapic->id = id; ioapic->first_intr = intr_base; @@ -182,84 +132,82 @@ ioapic_init(struct ioapic *ioapic, unsigned int id, 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"); - } + printf("ioapic%u: version:%#x intrs:%u-%u\n", ioapic->id, + ioapic->version, ioapic->first_intr, ioapic->last_intr); - memcpy(tmp, ioapic_devs, ioapic_nr_devs); - kmem_free(ioapic_devs, ioapic_nr_devs * sizeof(*ioapic_devs)); - ioapic_devs = tmp; - ioapic_nr_devs++; + return ioapic; +} - ioapic_init(ioapic_get(ioapic_nr_devs - 1), id, addr, intr_base); +static bool +ioapic_has_intr(const struct ioapic *ioapic, unsigned int intr) +{ + return ((intr >= ioapic->first_intr) && (intr <= ioapic->last_intr)); +} - spinlock_unlock(&ioapic_lock); +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 struct ioapic * -ioapic_lookup(unsigned int intr) +static void +ioapic_enable(void *priv, unsigned int intr, unsigned int cpu) { struct ioapic *ioapic; - unsigned int i; - - for (i = 0; i < ioapic_nr_devs; i++) { - ioapic = &ioapic_devs[i]; + unsigned long flags; + unsigned int id; - if ((intr >= ioapic->first_intr) && (intr <= ioapic->last_intr)) { - return ioapic; - } - } + ioapic = priv; + id = ioapic_compute_id(ioapic, intr); - return NULL; + spinlock_lock_intr_save(&ioapic->lock, &flags); + ioapic_write_entry_high(ioapic, id, cpu_apic_id(cpu) << 24); + ioapic_write_entry_low(ioapic, id, TRAP_INTR_FIRST + intr); + spinlock_unlock_intr_restore(&ioapic->lock, flags); } -int -ioapic_enable(unsigned int intr, unsigned int cpu, unsigned int vector) +static void +ioapic_disable(void *priv, unsigned int intr) { struct ioapic *ioapic; unsigned long flags; - int error; - - spinlock_lock_intr_save(&ioapic_lock, &flags); + unsigned int id; - ioapic = ioapic_lookup(intr); + ioapic = priv; + id = ioapic_compute_id(ioapic, intr); - if (ioapic == NULL) { - error = ERROR_NODEV; - goto out; - } + spinlock_lock_intr_save(&ioapic->lock, &flags); + ioapic_write_entry_low(ioapic, id, IOAPIC_ENTLOW_INTRMASK); + spinlock_unlock_intr_restore(&ioapic->lock, flags); +} - ioapic_enable_intr(ioapic, intr, cpu, vector); - error = 0; +static void +ioapic_eoi(void *priv, unsigned int intr) +{ + (void)priv; + (void)intr; -out: - spinlock_unlock_intr_restore(&ioapic_lock, flags); - return error; + lapic_eoi(); } -void -ioapic_disable(unsigned int intr) +static const struct intr_ops ioapic_ops = { + .enable = ioapic_enable, + .disable = ioapic_disable, + .eoi = ioapic_eoi, +}; + +void __init +ioapic_register(unsigned int id, uintptr_t addr, unsigned int intr_base) { - unsigned long flags; + struct ioapic *ioapic; - spinlock_lock_intr_save(&ioapic_lock, &flags); - ioapic_disable_intr(ioapic_lookup(intr), intr); - spinlock_unlock_intr_restore(&ioapic_lock, flags); + ioapic = ioapic_create(id, addr, intr_base); + intr_register_ctl(&ioapic_ops, ioapic, + ioapic->first_intr, ioapic->last_intr); } + diff --git a/arch/x86/machine/ioapic.h b/arch/x86/machine/ioapic.h index daebdb1..4d22085 100644 --- a/arch/x86/machine/ioapic.h +++ b/arch/x86/machine/ioapic.h @@ -21,21 +21,8 @@ #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 */ diff --git a/arch/x86/machine/lapic.c b/arch/x86/machine/lapic.c index a978da1..444e661 100644 --- a/arch/x86/machine/lapic.c +++ b/arch/x86/machine/lapic.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011-2014 Richard Braun. + * Copyright (c) 2011-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 @@ -15,10 +15,12 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stdbool.h> #include <stddef.h> #include <stdint.h> #include <stdio.h> +#include <kern/assert.h> #include <kern/init.h> #include <kern/macros.h> #include <kern/panic.h> @@ -182,6 +184,9 @@ static volatile struct lapic_map *lapic_map __read_mostly; */ static uint32_t lapic_bus_freq __read_mostly; +static bool lapic_initialized __initdata; +static bool lapic_is_unused __initdata; + static uint32_t lapic_read(const volatile struct lapic_register *r) { @@ -237,6 +242,20 @@ lapic_setup_registers(void) lapic_write(&lapic_map->timer_icr, lapic_bus_freq / HZ); } +bool __init +lapic_unused(void) +{ + assert(lapic_initialized); + return lapic_is_unused; +} + +void __init +lapic_setup_unused(void) +{ + lapic_initialized = true; + lapic_is_unused = true; +} + void __init lapic_setup(uint32_t map_addr) { @@ -256,6 +275,8 @@ lapic_setup(uint32_t map_addr) lapic_setup_registers(); lapic_setup_timer(); + + lapic_initialized = true; } void __init diff --git a/arch/x86/machine/lapic.h b/arch/x86/machine/lapic.h index 802d125..ff9c893 100644 --- a/arch/x86/machine/lapic.h +++ b/arch/x86/machine/lapic.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2012 Richard Braun. + * Copyright (c) 2011-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 @@ -18,6 +18,7 @@ #ifndef _X86_LAPIC_H #define _X86_LAPIC_H +#include <stdbool.h> #include <stdint.h> #include <machine/trap.h> @@ -28,6 +29,17 @@ void lapic_eoi(void); /* + * Report whether the local APIC is actually used or not. + */ +bool lapic_unused(void); + +/* + * Initialize the lapic module for the sole purpose of reporting that + * it's actually not used. + */ +void lapic_setup_unused(void); + +/* * Set up the lapic module. */ void lapic_setup(uint32_t map_addr); diff --git a/arch/x86/machine/param.h b/arch/x86/machine/param.h index e5e0f0a..6ee11cc 100644 --- a/arch/x86/machine/param.h +++ b/arch/x86/machine/param.h @@ -83,6 +83,11 @@ #define STACK_SIZE PAGE_SIZE /* + * Maximum number of available interrupts. + */ +#define INTR_TABLE_SIZE 256 + +/* * Virtual memory properties. */ diff --git a/arch/x86/machine/pic.c b/arch/x86/machine/pic.c index 412135c..fed8c97 100644 --- a/arch/x86/machine/pic.c +++ b/arch/x86/machine/pic.c @@ -19,9 +19,11 @@ #include <kern/assert.h> #include <kern/init.h> +#include <kern/intr.h> #include <kern/panic.h> -#include <machine/io.h> #include <machine/cpu.h> +#include <machine/io.h> +#include <machine/lapic.h> #include <machine/pic.h> #include <machine/trap.h> @@ -48,29 +50,43 @@ #define PIC_SLAVE_INTR 2 #define PIC_SPURIOUS_INTR 7 #define PIC_NR_INTRS 8 +#define PIC_MAX_INTR ((PIC_NR_INTRS * 2) - 1) -void __init -pic_setup(void) +static unsigned int pic_nr_slave_intrs; + +static uint8_t pic_master_mask; +static uint8_t pic_slave_mask; + +static bool +pic_is_slave_intr(unsigned int intr) { - /* ICW 1 - State that ICW 4 will be sent */ - io_write_byte(PIC_MASTER_CMD, PIC_ICW1_INIT | PIC_ICW1_IC4); - io_write_byte(PIC_SLAVE_CMD, PIC_ICW1_INIT | PIC_ICW1_IC4); + assert(intr <= PIC_MAX_INTR); + return (intr >= PIC_NR_INTRS); +} - /* ICW 2 */ - io_write_byte(PIC_MASTER_IMR, TRAP_INTR_FIRST); - io_write_byte(PIC_SLAVE_IMR, TRAP_INTR_FIRST + PIC_NR_INTRS); +static void +pic_inc_slave_intrs(void) +{ + if (pic_nr_slave_intrs == 0) { + pic_master_mask |= 1 << PIC_SLAVE_INTR; + io_write_byte(PIC_MASTER_IMR, pic_master_mask); + } - /* ICW 3 - Set up cascading */ - io_write_byte(PIC_MASTER_IMR, 1 << PIC_SLAVE_INTR); - io_write_byte(PIC_SLAVE_IMR, PIC_SLAVE_INTR); + pic_nr_slave_intrs++; + assert(pic_nr_slave_intrs != 0); +} - /* ICW 4 - Set 8086 mode */ - io_write_byte(PIC_MASTER_IMR, PIC_ICW4_8086); - io_write_byte(PIC_SLAVE_IMR, PIC_ICW4_8086); +static void +pic_dec_slave_intrs(void) +{ + assert(pic_nr_slave_intrs != 0); - /* OCW 1 - Mask all interrupts */ - io_write_byte(PIC_MASTER_IMR, 0xff); - io_write_byte(PIC_SLAVE_IMR, 0xff); + pic_nr_slave_intrs--; + + if (pic_nr_slave_intrs == 0) { + pic_master_mask &= ~(1 << PIC_SLAVE_INTR); + io_write_byte(PIC_MASTER_IMR, pic_master_mask); + } } static void @@ -90,6 +106,100 @@ pic_read_isr(uint16_t port) return io_read_byte(port); } +static void +pic_ops_enable(void *priv, unsigned int intr, unsigned int cpu) +{ + (void)priv; + (void)cpu; + + if (pic_is_slave_intr(intr)) { + pic_slave_mask &= ~(1 << (intr - PIC_NR_INTRS)); + io_write_byte(PIC_SLAVE_IMR, pic_slave_mask); + pic_inc_slave_intrs(); + } else { + pic_master_mask &= ~(1 << intr); + io_write_byte(PIC_MASTER_IMR, pic_master_mask); + } +} + +static void +pic_ops_disable(void *priv, unsigned int intr) +{ + (void)priv; + + if (pic_is_slave_intr(intr)) { + pic_dec_slave_intrs(); + pic_slave_mask |= 1 << (intr - PIC_NR_INTRS); + io_write_byte(PIC_SLAVE_IMR, pic_slave_mask); + } else { + pic_master_mask |= 1 << intr; + io_write_byte(PIC_MASTER_IMR, pic_master_mask); + } +} + +static void +pic_ops_eoi(void *priv, unsigned int intr) +{ + (void)priv; + pic_eoi(intr); +} + +static const struct intr_ops pic_ops = { + .enable = pic_ops_enable, + .disable = pic_ops_disable, + .eoi = pic_ops_eoi, +}; + +static void +pic_intr(struct trap_frame *frame) +{ + intr_handle(frame->vector - TRAP_INTR_FIRST); +} + +static void __init +pic_register(void) +{ + unsigned int intr; + + intr_register_ctl(&pic_ops, NULL, 0, PIC_MAX_INTR); + + for (intr = 0; intr <= PIC_MAX_INTR; intr++) { + trap_register(TRAP_INTR_FIRST + intr, pic_intr); + } +} + +void __init +pic_setup(void) +{ + pic_nr_slave_intrs = 0; + pic_master_mask = 0xff; + pic_slave_mask = 0xff; + + /* ICW 1 - State that ICW 4 will be sent */ + io_write_byte(PIC_MASTER_CMD, PIC_ICW1_INIT | PIC_ICW1_IC4); + io_write_byte(PIC_SLAVE_CMD, PIC_ICW1_INIT | PIC_ICW1_IC4); + + /* ICW 2 */ + io_write_byte(PIC_MASTER_IMR, TRAP_INTR_FIRST); + io_write_byte(PIC_SLAVE_IMR, TRAP_INTR_FIRST + PIC_NR_INTRS); + + /* ICW 3 - Set up cascading */ + io_write_byte(PIC_MASTER_IMR, 1 << PIC_SLAVE_INTR); + io_write_byte(PIC_SLAVE_IMR, PIC_SLAVE_INTR); + + /* ICW 4 - Set 8086 mode */ + io_write_byte(PIC_MASTER_IMR, PIC_ICW4_8086); + io_write_byte(PIC_SLAVE_IMR, PIC_ICW4_8086); + + /* OCW 1 - Mask all interrupts */ + io_write_byte(PIC_MASTER_IMR, pic_master_mask); + io_write_byte(PIC_SLAVE_IMR, pic_slave_mask); + + if (lapic_unused()) { + pic_register(); + } +} + void pic_spurious_intr(struct trap_frame *frame) { diff --git a/arch/x86/machine/uart.c b/arch/x86/machine/uart.c index d70cc41..86ef1e3 100644 --- a/arch/x86/machine/uart.c +++ b/arch/x86/machine/uart.c @@ -26,6 +26,7 @@ #include <kern/assert.h> #include <kern/console.h> #include <kern/init.h> +#include <kern/intr.h> #include <kern/macros.h> #include <machine/biosmem.h> #include <machine/io.h> @@ -183,7 +184,7 @@ uart_init(struct uart *uart, uint16_t port, uint16_t intr) } void __init -uart_setup(void) +uart_bootstrap(void) { const uint16_t *ptr; size_t i; @@ -199,6 +200,42 @@ uart_setup(void) } } +static int +uart_intr(void *arg) +{ + struct uart *uart; + uint8_t byte; + + uart = arg; + byte = uart_read(uart, UART_REG_DAT); + printf("uart: intr:%u byte:%hhu (%c)\n", uart->intr, byte, byte); + return 0; +} + +void __init +uart_setup(void) +{ + struct uart *uart; + int error; + size_t i; + + for (i = 0; i < ARRAY_SIZE(uart_devs); i++) { + uart = uart_get_dev(i); + + if (uart->port == 0) { + continue; + } + + error = intr_register(uart->intr, uart_intr, uart); + + if (error) { + printf("uart%zu: unable to register interrupt %u\n", i, uart->intr); + } + + uart_write(uart, UART_REG_IER, 1); + } +} + void __init uart_info(void) { diff --git a/arch/x86/machine/uart.h b/arch/x86/machine/uart.h index 9cb7cbf..60b195c 100644 --- a/arch/x86/machine/uart.h +++ b/arch/x86/machine/uart.h @@ -22,7 +22,17 @@ #define _X86_UART_H /* + * Early initialization of the uart module. + * + * Devices may only be used to report diagnostics until initialization + * is completed. + */ +void uart_bootstrap(void); + +/* * Initialize the uart module. + * + * On return, devices may be used for both input and output, using interrupts. */ void uart_setup(void); |