summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Braun <rbraun@sceen.net>2017-06-03 17:42:53 +0200
committerRichard Braun <rbraun@sceen.net>2017-06-03 17:42:53 +0200
commitbcac7e2c417bdbc0c2eace0a693d29442a0db12c (patch)
treeac4c82d3927d0328d2bd471db0c073c4d88bf06f
parent04002b5583c6da62b60b9c2470b0853178358658 (diff)
x86/{acpi,ioapic}: handle interrupt source overrides
-rw-r--r--arch/x86/machine/acpi.c67
-rw-r--r--arch/x86/machine/ioapic.c121
-rw-r--r--arch/x86/machine/ioapic.h12
-rw-r--r--arch/x86/machine/pic.c2
-rw-r--r--arch/x86/machine/pic.h10
-rw-r--r--arch/x86/machine/trap.h5
6 files changed, 203 insertions, 14 deletions
diff --git a/arch/x86/machine/acpi.c b/arch/x86/machine/acpi.c
index 1f7e5683..6d5e06e6 100644
--- a/arch/x86/machine/acpi.c
+++ b/arch/x86/machine/acpi.c
@@ -16,6 +16,7 @@
*/
#include <assert.h>
+#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
@@ -80,6 +81,7 @@ struct acpi_rsdt {
*/
#define ACPI_MADT_ENTRY_LAPIC 0
#define ACPI_MADT_ENTRY_IOAPIC 1
+#define ACPI_MADT_ENTRY_ISO 2
struct acpi_madt_entry_hdr {
uint8_t type;
@@ -103,11 +105,29 @@ struct acpi_madt_entry_ioapic {
uint32_t base;
} __packed;
+#define ACPI_MADT_ISO_POL_DEFAULT 0x00
+#define ACPI_MADT_ISO_POL_HIGH 0x01
+#define ACPI_MADT_ISO_POL_LOW 0x03
+#define ACPI_MADT_ISO_POL_MASK 0x03
+#define ACPI_MADT_ISO_TRIG_DEFAULT 0x00
+#define ACPI_MADT_ISO_TRIG_EDGE 0x04
+#define ACPI_MADT_ISO_TRIG_LEVEL 0x0c
+#define ACPI_MADT_ISO_TRIG_MASK 0x0c
+
+struct acpi_madt_entry_iso {
+ struct acpi_madt_entry_hdr header;
+ uint8_t bus;
+ uint8_t source;
+ uint32_t gsi;
+ int16_t flags;
+} __packed;
+
union acpi_madt_entry {
uint8_t type;
struct acpi_madt_entry_hdr header;
struct acpi_madt_entry_lapic lapic;
struct acpi_madt_entry_ioapic ioapic;
+ struct acpi_madt_entry_iso iso;
} __packed;
#define ACPI_MADT_PC_COMPAT 0x1
@@ -487,6 +507,45 @@ acpi_load_ioapic(const struct acpi_madt_entry_ioapic *ioapic)
}
static void __init
+acpi_load_iso(const struct acpi_madt_entry_iso *iso)
+{
+ bool active_high, edge_triggered;
+
+ if (iso->bus != 0) {
+ printf("acpi: error: invalid interrupt source override bus\n");
+ return;
+ }
+
+ switch (iso->flags & ACPI_MADT_ISO_POL_MASK) {
+ case ACPI_MADT_ISO_POL_DEFAULT:
+ case ACPI_MADT_ISO_POL_HIGH:
+ active_high = true;
+ break;
+ case ACPI_MADT_ISO_POL_LOW:
+ active_high = false;
+ break;
+ default:
+ printf("acpi: error: invalid polarity\n");
+ return;
+ }
+
+ switch (iso->flags & ACPI_MADT_ISO_TRIG_MASK) {
+ case ACPI_MADT_ISO_TRIG_DEFAULT:
+ case ACPI_MADT_ISO_TRIG_EDGE:
+ edge_triggered = true;
+ break;
+ case ACPI_MADT_ISO_TRIG_LEVEL:
+ edge_triggered = false;
+ break;
+ default:
+ printf("acpi: error: invalid trigger mode\n");
+ return;
+ }
+
+ ioapic_override(iso->source, iso->gsi, active_high, edge_triggered);
+}
+
+static void __init
acpi_load_madt(void)
{
const struct acpi_sdth *table;
@@ -501,10 +560,6 @@ acpi_load_madt(void)
lapic_setup(madt->lapic_addr);
is_bsp = 1;
- /*
- * TODO Handle interrupt overrides
- */
-
acpi_madt_foreach(madt, &iter) {
switch (iter.entry->type) {
case ACPI_MADT_ENTRY_LAPIC:
@@ -512,6 +567,10 @@ acpi_load_madt(void)
break;
case ACPI_MADT_ENTRY_IOAPIC:
acpi_load_ioapic(&iter.entry->ioapic);
+ break;
+ case ACPI_MADT_ENTRY_ISO:
+ acpi_load_iso(&iter.entry->iso);
+ break;
}
}
diff --git a/arch/x86/machine/ioapic.c b/arch/x86/machine/ioapic.c
index d48cc307..e49cb309 100644
--- a/arch/x86/machine/ioapic.c
+++ b/arch/x86/machine/ioapic.c
@@ -29,6 +29,7 @@
#include <machine/cpu.h>
#include <machine/ioapic.h>
#include <machine/lapic.h>
+#include <machine/pic.h>
#include <machine/trap.h>
#include <vm/vm_kmem.h>
@@ -40,6 +41,10 @@
#define IOAPIC_VERSION_MAXREDIR_MASK 0x00ff0000
#define IOAPIC_VERSION_MAXREDIR_SHIFT 16
+#define IOAPIC_ENTLOW_FIXED_DEST 0x00000
+#define IOAPIC_ENTLOW_PHYS_DELIVERY 0x00000
+#define IOAPIC_ENTLOW_ACTIVE_LOW 0x02000
+#define IOAPIC_ENTLOW_LEVEL 0x08000
#define IOAPIC_ENTLOW_INTRMASK 0x10000
#define IOAPIC_MAX_ENTRIES 24
@@ -55,6 +60,16 @@ struct ioapic_map {
uint32_t win;
};
+/*
+ * Interrupt source override descriptor.
+ */
+struct ioapic_iso {
+ uint8_t source;
+ uint32_t gsi;
+ bool active_high;
+ bool edge_triggered;
+};
+
struct ioapic {
struct spinlock lock;
unsigned int id;
@@ -67,6 +82,51 @@ struct ioapic {
static unsigned int ioapic_nr_devs;
+static struct ioapic_iso ioapic_isos[PIC_MAX_INTR + 1];
+static unsigned int ioapic_nr_isos;
+
+static void
+ioapic_iso_init(struct ioapic_iso *iso, uint8_t source, uint32_t gsi,
+ bool active_high, bool edge_triggered)
+{
+ iso->source = source;
+ iso->gsi = gsi;
+ iso->active_high = active_high;
+ iso->edge_triggered = edge_triggered;
+}
+
+static struct ioapic_iso * __init
+ioapic_alloc_iso(void)
+{
+ struct ioapic_iso *iso;
+
+ if (ioapic_nr_isos >= ARRAY_SIZE(ioapic_isos)) {
+ printf("ioapic: error: too many interrupt overrides\n");
+ return NULL;
+ }
+
+ iso = &ioapic_isos[ioapic_nr_isos];
+ ioapic_nr_isos++;
+ return iso;
+}
+
+static struct ioapic_iso *
+ioapic_lookup_iso(unsigned int intr)
+{
+ struct ioapic_iso *iso;
+ unsigned int i;
+
+ for (i = 0; i < ioapic_nr_isos; i++) {
+ iso = &ioapic_isos[i];
+
+ if (intr == iso->source) {
+ return iso;
+ }
+ }
+
+ return NULL;
+}
+
static uint32_t
ioapic_read(struct ioapic *ioapic, uint8_t reg)
{
@@ -161,18 +221,52 @@ ioapic_compute_id(const struct ioapic *ioapic, unsigned int intr)
}
static void
+ioapic_compute_entry(uint32_t *highp, uint32_t *lowp,
+ unsigned int apic_id, unsigned int intr,
+ bool active_high, bool edge_triggered)
+{
+ assert(apic_id < 16);
+ assert(intr < (TRAP_NR_VECTORS - TRAP_INTR_FIRST));
+
+ *highp = apic_id << 24;
+ *lowp = (!edge_triggered ? IOAPIC_ENTLOW_LEVEL : 0)
+ | (!active_high ? IOAPIC_ENTLOW_ACTIVE_LOW : 0)
+ | IOAPIC_ENTLOW_PHYS_DELIVERY
+ | IOAPIC_ENTLOW_FIXED_DEST
+ | (TRAP_INTR_FIRST + intr);
+}
+
+static void
ioapic_enable(void *priv, unsigned int intr, unsigned int cpu)
{
+ bool active_high, edge_triggered;
+ const struct ioapic_iso *iso;
+ uint32_t high, low, gsi;
struct ioapic *ioapic;
unsigned long flags;
unsigned int id;
+ iso = ioapic_lookup_iso(intr);
+
+ /* XXX These are defaults that should work with architectural devices */
+ if (iso == NULL) {
+ active_high = true;
+ edge_triggered = true;
+ gsi = intr;
+ } else {
+ active_high = iso->active_high;
+ edge_triggered = iso->edge_triggered;
+ gsi = iso->gsi;
+ }
+
ioapic = priv;
- id = ioapic_compute_id(ioapic, intr);
+ id = ioapic_compute_id(ioapic, gsi);
+ ioapic_compute_entry(&high, &low, cpu_apic_id(cpu), intr,
+ active_high, edge_triggered);
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);
+ ioapic_write_entry_high(ioapic, id, high);
+ ioapic_write_entry_low(ioapic, id, low);
spinlock_unlock_intr_restore(&ioapic->lock, flags);
}
@@ -207,6 +301,13 @@ static const struct intr_ops ioapic_ops = {
};
void __init
+ioapic_setup(void)
+{
+ ioapic_nr_devs = 0;
+ ioapic_nr_isos = 0;
+}
+
+void __init
ioapic_register(unsigned int apic_id, uintptr_t addr, unsigned int intr_base)
{
struct ioapic *ioapic;
@@ -216,3 +317,17 @@ ioapic_register(unsigned int apic_id, uintptr_t addr, unsigned int intr_base)
ioapic->first_intr, ioapic->last_intr);
}
+void __init
+ioapic_override(uint8_t source, uint32_t gsi,
+ bool active_high, bool edge_triggered)
+{
+ struct ioapic_iso *iso;
+
+ iso = ioapic_alloc_iso();
+
+ if (iso == NULL) {
+ return;
+ }
+
+ ioapic_iso_init(iso, source, gsi, active_high, edge_triggered);
+}
diff --git a/arch/x86/machine/ioapic.h b/arch/x86/machine/ioapic.h
index d508904e..b9a702d3 100644
--- a/arch/x86/machine/ioapic.h
+++ b/arch/x86/machine/ioapic.h
@@ -18,12 +18,24 @@
#ifndef _KERN_IOAPIC_H
#define _KERN_IOAPIC_H
+#include <stdbool.h>
#include <stdint.h>
/*
+ * Initialize the ioapic module.
+ */
+void ioapic_setup(void);
+
+/*
* Register an I/O APIC controller.
*/
void ioapic_register(unsigned int apic_id, uintptr_t addr,
unsigned int gsi_base);
+/*
+ * Report an interrupt source override.
+ */
+void ioapic_override(uint8_t source, uint32_t gsi,
+ bool active_high, bool edge_triggered);
+
#endif /* _KERN_IOAPIC_H */
diff --git a/arch/x86/machine/pic.c b/arch/x86/machine/pic.c
index ab30ac20..8ef57538 100644
--- a/arch/x86/machine/pic.c
+++ b/arch/x86/machine/pic.c
@@ -51,8 +51,6 @@
*/
#define PIC_SLAVE_INTR 2
#define PIC_SPURIOUS_INTR 7
-#define PIC_NR_INTRS 8
-#define PIC_MAX_INTR ((PIC_NR_INTRS * 2) - 1)
static unsigned int pic_nr_slave_intrs;
diff --git a/arch/x86/machine/pic.h b/arch/x86/machine/pic.h
index e49d336e..89833d00 100644
--- a/arch/x86/machine/pic.h
+++ b/arch/x86/machine/pic.h
@@ -18,7 +18,15 @@
#ifndef _X86_PIC_H
#define _X86_PIC_H
-#include <machine/trap.h>
+/*
+ * Interrupts per PIC.
+ */
+#define PIC_NR_INTRS 8
+
+/*
+ * Maximum global interrupt number.
+ */
+#define PIC_MAX_INTR ((PIC_NR_INTRS * 2) - 1)
/*
* Initialize the pic module.
diff --git a/arch/x86/machine/trap.h b/arch/x86/machine/trap.h
index 7bda9cf3..e4d5912b 100644
--- a/arch/x86/machine/trap.h
+++ b/arch/x86/machine/trap.h
@@ -61,10 +61,7 @@
#define TRAP_LAPIC_ERROR 254
#define TRAP_LAPIC_SPURIOUS 255
-/*
- * Vector identifying an unhandled trap.
- */
-#define TRAP_DEFAULT 256
+#define TRAP_NR_VECTORS 256
#ifndef __ASSEMBLER__