From b0bf29e8e158fa64c5920fd93ebd3c2cdec9fd57 Mon Sep 17 00:00:00 2001
From: Roger Pau Monne <roger.pau@citrix.com>
Date: Mon, 3 Mar 2014 11:47:52 +0100
Subject: [PATCH 15/31] xen: implement support for mapping IO APIC interrupts
 on Xen

Allow a priveleged Xen guest (Dom0) to parse the MADT ACPI interrupt
overwrites and registers them with the interrupt subsystem.

Also add a Xen specific hook to bus_config_intr that registers
interrupts on demand for all the vectors less than FIRST_MSI_INT.

Approved by: xxx
Sponsored by: Citrix Systems R&D

x86/xen/pvcpu_enum.c:
 - Add helper functions from x86/acpica/madt.c in order to parse
   interrupt overwrites from the MADT.
 - Walk the MADT and register any interrupt overwrite with the
   interrupt subsystem.

x86/xen/xen_nexus.c:
 - Add a custom bus_config_intr method for Xen that intercepts calls
   to configure unset interrupts and registers them on the fly (if the
   vector is < FIRST_MSI_INT).
---
 sys/x86/xen/pvcpu_enum.c |  183 ++++++++++++++++++++++++++++++++++++++++++++++
 sys/x86/xen/xen_nexus.c  |   24 ++++++
 2 files changed, 207 insertions(+), 0 deletions(-)

diff --git a/sys/x86/xen/pvcpu_enum.c b/sys/x86/xen/pvcpu_enum.c
index 30e6803..0929fde 100644
--- a/sys/x86/xen/pvcpu_enum.c
+++ b/sys/x86/xen/pvcpu_enum.c
@@ -44,15 +44,26 @@ __FBSDID("$FreeBSD$");
 #include <machine/smp.h>
 
 #include <xen/xen-os.h>
+#include <xen/xen_intr.h>
 #include <xen/hypervisor.h>
 
 #include <xen/interface/vcpu.h>
 
+#include <contrib/dev/acpica/include/acpi.h>
+#include <contrib/dev/acpica/include/actables.h>
+
+#include <dev/acpica/acpivar.h>
+
 static int xenpv_probe(void);
 static int xenpv_probe_cpus(void);
 static int xenpv_setup_local(void);
 static int xenpv_setup_io(void);
 
+static int madt_found_sci_override;
+static ACPI_TABLE_MADT *madt;
+static vm_paddr_t madt_physaddr;
+static vm_offset_t madt_length;
+
 static struct apic_enumerator xenpv_enumerator = {
 	"Xen PV",
 	xenpv_probe,
@@ -61,6 +72,145 @@ static struct apic_enumerator xenpv_enumerator = {
 	xenpv_setup_io
 };
 
+/*--------------------- Helper functions to parse MADT -----------------------*/
+
+/*
+ * Determine properties of an interrupt source.  Note that for ACPI these
+ * functions are only used for ISA interrupts, so we assume ISA bus values
+ * (Active Hi, Edge Triggered) for conforming values except for the ACPI
+ * SCI for which we use Active Lo, Level Triggered.
+ */
+static enum intr_polarity
+interrupt_polarity(UINT16 IntiFlags, UINT8 Source)
+{
+
+	switch (IntiFlags & ACPI_MADT_POLARITY_MASK) {
+	case ACPI_MADT_POLARITY_CONFORMS:
+		if (Source == AcpiGbl_FADT.SciInterrupt)
+			return (INTR_POLARITY_LOW);
+		else
+			return (INTR_POLARITY_HIGH);
+	case ACPI_MADT_POLARITY_ACTIVE_HIGH:
+		return (INTR_POLARITY_HIGH);
+	case ACPI_MADT_POLARITY_ACTIVE_LOW:
+		return (INTR_POLARITY_LOW);
+	default:
+		panic("Bogus Interrupt Polarity");
+	}
+}
+
+static enum intr_trigger
+interrupt_trigger(UINT16 IntiFlags, UINT8 Source)
+{
+
+	switch (IntiFlags & ACPI_MADT_TRIGGER_MASK) {
+	case ACPI_MADT_TRIGGER_CONFORMS:
+		if (Source == AcpiGbl_FADT.SciInterrupt)
+			return (INTR_TRIGGER_LEVEL);
+		else
+			return (INTR_TRIGGER_EDGE);
+	case ACPI_MADT_TRIGGER_EDGE:
+		return (INTR_TRIGGER_EDGE);
+	case ACPI_MADT_TRIGGER_LEVEL:
+		return (INTR_TRIGGER_LEVEL);
+	default:
+		panic("Bogus Interrupt Trigger Mode");
+	}
+}
+
+/*
+ * Parse an interrupt source override for an ISA interrupt.
+ */
+static void
+madt_parse_interrupt_override(ACPI_MADT_INTERRUPT_OVERRIDE *intr)
+{
+	enum intr_trigger trig;
+	enum intr_polarity pol;
+	char buf[64];
+
+	if (acpi_quirks & ACPI_Q_MADT_IRQ0 && intr->SourceIrq == 0 &&
+	    intr->GlobalIrq == 2) {
+		if (bootverbose)
+			printf("MADT: Skipping timer override\n");
+		return;
+	}
+	if (bootverbose)
+		printf("MADT: Interrupt override: source %u, irq %u\n",
+		    intr->SourceIrq, intr->GlobalIrq);
+	KASSERT(intr->Bus == 0, ("bus for interrupt overrides must be zero"));
+
+	/*
+	 * Lookup the appropriate trigger and polarity modes for this
+	 * entry.
+	 */
+	trig = interrupt_trigger(intr->IntiFlags, intr->SourceIrq);
+	pol = interrupt_polarity(intr->IntiFlags, intr->SourceIrq);
+
+	/*
+	 * If the SCI is identity mapped but has edge trigger and
+	 * active-hi polarity or the force_sci_lo tunable is set,
+	 * force it to use level/lo.
+	 */
+	if (intr->SourceIrq == AcpiGbl_FADT.SciInterrupt) {
+		madt_found_sci_override = 1;
+		if (getenv_string("hw.acpi.sci.trigger", buf, sizeof(buf))) {
+			if (tolower(buf[0]) == 'e')
+				trig = INTR_TRIGGER_EDGE;
+			else if (tolower(buf[0]) == 'l')
+				trig = INTR_TRIGGER_LEVEL;
+			else
+				panic(
+				"Invalid trigger %s: must be 'edge' or 'level'",
+				    buf);
+			printf("MADT: Forcing SCI to %s trigger\n",
+			    trig == INTR_TRIGGER_EDGE ? "edge" : "level");
+		}
+		if (getenv_string("hw.acpi.sci.polarity", buf, sizeof(buf))) {
+			if (tolower(buf[0]) == 'h')
+				pol = INTR_POLARITY_HIGH;
+			else if (tolower(buf[0]) == 'l')
+				pol = INTR_POLARITY_LOW;
+			else
+				panic(
+				"Invalid polarity %s: must be 'high' or 'low'",
+				    buf);
+			printf("MADT: Forcing SCI to active %s polarity\n",
+			    pol == INTR_POLARITY_HIGH ? "high" : "low");
+		}
+	}
+
+	/* Register the IRQ with the polarity and trigger mode found. */
+	xen_register_pirq(intr->GlobalIrq, trig, pol);
+}
+
+/*
+ * Call the handler routine for each entry in the MADT table.
+ */
+static void
+madt_walk_table(acpi_subtable_handler *handler, void *arg)
+{
+
+	acpi_walk_subtables(madt + 1, (char *)madt + madt->Header.Length,
+	    handler, arg);
+}
+
+/*
+ * Parse interrupt entries.
+ */
+static void
+madt_parse_ints(ACPI_SUBTABLE_HEADER *entry, void *arg __unused)
+{
+
+	switch (entry->Type) {
+	case ACPI_MADT_TYPE_INTERRUPT_OVERRIDE:
+		madt_parse_interrupt_override(
+			(ACPI_MADT_INTERRUPT_OVERRIDE *)entry);
+		break;
+	}
+}
+
+/*---------------------------- Xen PV enumerator -----------------------------*/
+
 /*
  * This enumerator will only be registered on PVH
  */
@@ -105,6 +255,39 @@ xenpv_setup_local(void)
 static int
 xenpv_setup_io(void)
 {
+
+	if (xen_initial_domain()) {
+		int i;
+
+		/* Map MADT */
+		madt_physaddr = acpi_find_table(ACPI_SIG_MADT);
+		madt = acpi_map_table(madt_physaddr, ACPI_SIG_MADT);
+		madt_length = madt->Header.Length;
+
+		/* Try to initialize ACPI so that we can access the FADT. */
+		i = acpi_Startup();
+		if (ACPI_FAILURE(i)) {
+			printf("MADT: ACPI Startup failed with %s\n",
+			    AcpiFormatException(i));
+			printf("Try disabling either ACPI or apic support.\n");
+			panic("Using MADT but ACPI doesn't work");
+		}
+
+		/* Run through the table to see if there are any overwrites. */
+		madt_walk_table(madt_parse_ints, NULL);
+
+		/*
+		 * If there was not an explicit override entry for the SCI,
+		 * force it to use level trigger and active-low polarity.
+		 */
+		if (!madt_found_sci_override) {
+			printf("MADT: Forcing active-low polarity and level trigger for SCI\n");
+			xen_register_pirq(AcpiGbl_FADT.SciInterrupt,
+			    INTR_TRIGGER_LEVEL, INTR_POLARITY_LOW);
+		}
+
+		acpi_SetDefaultIntrModel(ACPI_INTR_APIC);
+	}
 	return (0);
 }
 
diff --git a/sys/x86/xen/xen_nexus.c b/sys/x86/xen/xen_nexus.c
index 8809f21..bf98ca2 100644
--- a/sys/x86/xen/xen_nexus.c
+++ b/sys/x86/xen/xen_nexus.c
@@ -36,8 +36,10 @@ __FBSDID("$FreeBSD$");
 #include <sys/smp.h>
 
 #include <machine/nexusvar.h>
+#include <machine/intr_machdep.h>
 
 #include <xen/xen-os.h>
+#include <xen/xen_intr.h>
 
 /*
  * Xen nexus(4) driver.
@@ -63,11 +65,33 @@ nexus_xen_attach(device_t dev)
 	return (0);
 }
 
+static int
+nexus_xen_config_intr(device_t dev, int irq, enum intr_trigger trig,
+    enum intr_polarity pol)
+{
+	int ret;
+
+	/*
+	 * ISA and PCI intline IRQs are not pre-registered on Xen, so
+	 * intercept calls to configure those and register them on the fly.
+	 */
+	if ((irq < FIRST_MSI_INT) && (intr_lookup_source(irq) == NULL)) {
+		ret = xen_register_pirq(irq, trig, pol);
+		if (ret != 0)
+			return (ret);
+		nexus_add_irq(irq);
+	}
+	return (intr_config_intr(irq, trig, pol));
+}
+
 static device_method_t nexus_xen_methods[] = {
 	/* Device interface */
 	DEVMETHOD(device_probe,		nexus_xen_probe),
 	DEVMETHOD(device_attach,	nexus_xen_attach),
 
+	/* INTR */
+	DEVMETHOD(bus_config_intr,	nexus_xen_config_intr),
+
 	{ 0, 0 }
 };
 
-- 
1.7.7.5 (Apple Git-26)

