diff options
author | Roger Pau Monné <royger@FreeBSD.org> | 2014-08-04 09:01:21 +0000 |
---|---|---|
committer | Roger Pau Monné <royger@FreeBSD.org> | 2014-08-04 09:01:21 +0000 |
commit | c0c19cce9e3085ee911ac4f441e0e13d16e20d1f (patch) | |
tree | 3f7c5d18e348871d099d579827972888b03a4789 | |
parent | f48223fad26b7a075fbc83fe099d9cac93bab5b5 (diff) | |
download | src-c0c19cce9e3085ee911ac4f441e0e13d16e20d1f.tar.gz src-c0c19cce9e3085ee911ac4f441e0e13d16e20d1f.zip |
xen: implement support for mapping IO APIC interrupts on Xen
Allow a privileged Xen guest (Dom0) to parse the MADT ACPI interrupt
overrides and register them with the interrupt subsystem.
Also add a Xen specific implementation for bus_config_intr that
registers interrupts on demand for all the vectors less than
FIRST_MSI_INT.
Sponsored by: Citrix Systems R&D
x86/xen/pvcpu_enum.c:
- Use helper functions from x86/acpica/madt.c in order to parse
interrupt overrides from the MADT.
- Walk the MADT and register any interrupt override 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).
Notes
Notes:
svn path=/head/; revision=269513
-rw-r--r-- | sys/x86/xen/pvcpu_enum.c | 93 | ||||
-rw-r--r-- | sys/x86/xen/xen_nexus.c | 24 |
2 files changed, 117 insertions, 0 deletions
diff --git a/sys/x86/xen/pvcpu_enum.c b/sys/x86/xen/pvcpu_enum.c index 30e6803e973d..42944642cd00 100644 --- a/sys/x86/xen/pvcpu_enum.c +++ b/sys/x86/xen/pvcpu_enum.c @@ -44,15 +44,25 @@ __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 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 +71,55 @@ static struct apic_enumerator xenpv_enumerator = { xenpv_setup_io }; +/*--------------------- Helper functions to parse MADT -----------------------*/ + +/* + * 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; + + if (acpi_quirks & ACPI_Q_MADT_IRQ0 && intr->SourceIrq == 0 && + intr->GlobalIrq == 2) { + if (bootverbose) + printf("MADT: Skipping timer override\n"); + return; + } + + madt_parse_interrupt_values(intr, &trig, &pol); + + /* 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) +{ + + if (entry->Type == ACPI_MADT_TYPE_INTERRUPT_OVERRIDE) + madt_parse_interrupt_override( + (ACPI_MADT_INTERRUPT_OVERRIDE *)entry); +} + +/*---------------------------- Xen PV enumerator -----------------------------*/ + /* * This enumerator will only be registered on PVH */ @@ -105,6 +164,40 @@ 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 overrides. */ + 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 8809f214beed..f9cf57814e8f 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 preregistered 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 } }; |