summaryrefslogtreecommitdiff
path: root/arch
diff options
context:
space:
mode:
authorRichard Braun <rbraun@sceen.net>2017-05-26 22:39:31 +0200
committerRichard Braun <rbraun@sceen.net>2017-05-26 22:40:01 +0200
commitcab8846284996df401c5bdaa0088b224e076b510 (patch)
tree884c1dc40cbcf47e763da9c1c470aec73165c0a3 /arch
parent10f2d5f59a37532fb48cf9d6896efa238c9c6eb5 (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.
Diffstat (limited to 'arch')
-rw-r--r--arch/x86/machine/acpimp.c1
-rw-r--r--arch/x86/machine/boot.c7
-rw-r--r--arch/x86/machine/cpu.c9
-rw-r--r--arch/x86/machine/ioapic.c182
-rw-r--r--arch/x86/machine/ioapic.h13
-rw-r--r--arch/x86/machine/lapic.c23
-rw-r--r--arch/x86/machine/lapic.h14
-rw-r--r--arch/x86/machine/param.h5
-rw-r--r--arch/x86/machine/pic.c146
-rw-r--r--arch/x86/machine/uart.c39
-rw-r--r--arch/x86/machine/uart.h10
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);