diff options
Diffstat (limited to 'arch/x86/pci/xen.c')
| -rw-r--r-- | arch/x86/pci/xen.c | 371 | 
1 files changed, 161 insertions, 210 deletions
diff --git a/arch/x86/pci/xen.c b/arch/x86/pci/xen.c index f567965c062..1017c7bee38 100644 --- a/arch/x86/pci/xen.c +++ b/arch/x86/pci/xen.c @@ -1,8 +1,13 @@  /* - * Xen PCI Frontend Stub - puts some "dummy" functions in to the Linux - *			   x86 PCI core to support the Xen PCI Frontend + * Xen PCI - handle PCI (INTx) and MSI infrastructure calls for PV, HVM and + * initial domain support. We also handle the DSDT _PRT callbacks for GSI's + * used in HVM and initial domain mode (PV does not parse ACPI, so it has no + * concept of GSIs). Under PV we hook under the pnbbios API for IRQs and + * 0xcf8 PCI configuration read/write.   *   *   Author: Ryan Wilson <hap9@epoch.ncsc.mil> + *           Konrad Rzeszutek Wilk <konrad.wilk@oracle.com> + *           Stefano Stabellini <stefano.stabellini@eu.citrix.com>   */  #include <linux/module.h>  #include <linux/init.h> @@ -19,22 +24,53 @@  #include <xen/events.h>  #include <asm/xen/pci.h> +static int xen_pcifront_enable_irq(struct pci_dev *dev) +{ +	int rc; +	int share = 1; +	int pirq; +	u8 gsi; + +	rc = pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &gsi); +	if (rc < 0) { +		dev_warn(&dev->dev, "Xen PCI: failed to read interrupt line: %d\n", +			 rc); +		return rc; +	} +	/* In PV DomU the Xen PCI backend puts the PIRQ in the interrupt line.*/ +	pirq = gsi; + +	if (gsi < NR_IRQS_LEGACY) +		share = 0; + +	rc = xen_bind_pirq_gsi_to_irq(gsi, pirq, share, "pcifront"); +	if (rc < 0) { +		dev_warn(&dev->dev, "Xen PCI: failed to bind GSI%d (PIRQ%d) to IRQ: %d\n", +			 gsi, pirq, rc); +		return rc; +	} + +	dev->irq = rc; +	dev_info(&dev->dev, "Xen PCI mapped GSI%d to IRQ%d\n", gsi, dev->irq); +	return 0; +} +  #ifdef CONFIG_ACPI -static int acpi_register_gsi_xen_hvm(struct device *dev, u32 gsi, -				 int trigger, int polarity) +static int xen_register_pirq(u32 gsi, int gsi_override, int triggering, +			     bool set_pirq)  { -	int rc, irq; +	int rc, pirq = -1, irq = -1;  	struct physdev_map_pirq map_irq;  	int shareable = 0;  	char *name; -	if (!xen_hvm_domain()) -		return -1; +	if (set_pirq) +		pirq = gsi;  	map_irq.domid = DOMID_SELF;  	map_irq.type = MAP_PIRQ_TYPE_GSI;  	map_irq.index = gsi; -	map_irq.pirq = -1; +	map_irq.pirq = pirq;  	rc = HYPERVISOR_physdev_op(PHYSDEVOP_map_pirq, &map_irq);  	if (rc) { @@ -42,7 +78,7 @@ static int acpi_register_gsi_xen_hvm(struct device *dev, u32 gsi,  		return -1;  	} -	if (trigger == ACPI_EDGE_SENSITIVE) { +	if (triggering == ACPI_EDGE_SENSITIVE) {  		shareable = 0;  		name = "ioapic-edge";  	} else { @@ -50,12 +86,63 @@ static int acpi_register_gsi_xen_hvm(struct device *dev, u32 gsi,  		name = "ioapic-level";  	} +	if (gsi_override >= 0) +		gsi = gsi_override; +  	irq = xen_bind_pirq_gsi_to_irq(gsi, map_irq.pirq, shareable, name); +	if (irq < 0) +		goto out; + +	printk(KERN_DEBUG "xen: --> pirq=%d -> irq=%d (gsi=%d)\n", map_irq.pirq, irq, gsi); +out: +	return irq; +} + +static int acpi_register_gsi_xen_hvm(struct device *dev, u32 gsi, +				     int trigger, int polarity) +{ +	if (!xen_hvm_domain()) +		return -1; -	printk(KERN_DEBUG "xen: --> irq=%d, pirq=%d\n", irq, map_irq.pirq); +	return xen_register_pirq(gsi, -1 /* no GSI override */, trigger, +				 false /* no mapping of GSI to PIRQ */); +} + +#ifdef CONFIG_XEN_DOM0 +static int xen_register_gsi(u32 gsi, int gsi_override, int triggering, int polarity) +{ +	int rc, irq; +	struct physdev_setup_gsi setup_gsi; + +	if (!xen_pv_domain()) +		return -1; + +	printk(KERN_DEBUG "xen: registering gsi %u triggering %d polarity %d\n", +			gsi, triggering, polarity); + +	irq = xen_register_pirq(gsi, gsi_override, triggering, true); + +	setup_gsi.gsi = gsi; +	setup_gsi.triggering = (triggering == ACPI_EDGE_SENSITIVE ? 0 : 1); +	setup_gsi.polarity = (polarity == ACPI_ACTIVE_HIGH ? 0 : 1); + +	rc = HYPERVISOR_physdev_op(PHYSDEVOP_setup_gsi, &setup_gsi); +	if (rc == -EEXIST) +		printk(KERN_INFO "Already setup the GSI :%d\n", gsi); +	else if (rc) { +		printk(KERN_ERR "Failed to setup GSI :%d, err_code:%d\n", +				gsi, rc); +	}  	return irq;  } + +static int acpi_register_gsi_xen(struct device *dev, u32 gsi, +				 int trigger, int polarity) +{ +	return xen_register_gsi(gsi, -1 /* no GSI override */, trigger, polarity); +} +#endif  #endif  #if defined(CONFIG_PCI_MSI) @@ -65,6 +152,43 @@ static int acpi_register_gsi_xen_hvm(struct device *dev, u32 gsi,  struct xen_pci_frontend_ops *xen_pci_frontend;  EXPORT_SYMBOL_GPL(xen_pci_frontend); +static int xen_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) +{ +	int irq, ret, i; +	struct msi_desc *msidesc; +	int *v; + +	v = kzalloc(sizeof(int) * max(1, nvec), GFP_KERNEL); +	if (!v) +		return -ENOMEM; + +	if (type == PCI_CAP_ID_MSIX) +		ret = xen_pci_frontend_enable_msix(dev, v, nvec); +	else +		ret = xen_pci_frontend_enable_msi(dev, v); +	if (ret) +		goto error; +	i = 0; +	list_for_each_entry(msidesc, &dev->msi_list, list) { +		irq = xen_bind_pirq_msi_to_irq(dev, msidesc, v[i], 0, +					       (type == PCI_CAP_ID_MSIX) ? +					       "pcifront-msi-x" : +					       "pcifront-msi", +						DOMID_SELF); +		if (irq < 0) +			goto free; +		i++; +	} +	kfree(v); +	return 0; + +error: +	dev_err(&dev->dev, "Xen PCI frontend has not registered MSI/MSI-X support!\n"); +free: +	kfree(v); +	return ret; +} +  #define XEN_PIRQ_MSI_DATA  (MSI_DATA_TRIGGER_EDGE | \  		MSI_DATA_LEVEL_ASSERT | (3 << 8) | MSI_DATA_VECTOR(0)) @@ -123,67 +247,6 @@ error:  	return -ENODEV;  } -/* - * For MSI interrupts we have to use drivers/xen/event.s functions to - * allocate an irq_desc and setup the right */ - - -static int xen_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) -{ -	int irq, ret, i; -	struct msi_desc *msidesc; -	int *v; - -	v = kzalloc(sizeof(int) * max(1, nvec), GFP_KERNEL); -	if (!v) -		return -ENOMEM; - -	if (type == PCI_CAP_ID_MSIX) -		ret = xen_pci_frontend_enable_msix(dev, v, nvec); -	else -		ret = xen_pci_frontend_enable_msi(dev, v); -	if (ret) -		goto error; -	i = 0; -	list_for_each_entry(msidesc, &dev->msi_list, list) { -		irq = xen_bind_pirq_msi_to_irq(dev, msidesc, v[i], 0, -					       (type == PCI_CAP_ID_MSIX) ? -					       "pcifront-msi-x" : -					       "pcifront-msi", -						DOMID_SELF); -		if (irq < 0) -			goto free; -		i++; -	} -	kfree(v); -	return 0; - -error: -	dev_err(&dev->dev, "Xen PCI frontend has not registered MSI/MSI-X support!\n"); -free: -	kfree(v); -	return ret; -} - -static void xen_teardown_msi_irqs(struct pci_dev *dev) -{ -	struct msi_desc *msidesc; - -	msidesc = list_entry(dev->msi_list.next, struct msi_desc, list); -	if (msidesc->msi_attrib.is_msix) -		xen_pci_frontend_disable_msix(dev); -	else -		xen_pci_frontend_disable_msi(dev); - -	/* Free the IRQ's and the msidesc using the generic code. */ -	default_teardown_msi_irqs(dev); -} - -static void xen_teardown_msi_irq(unsigned int irq) -{ -	xen_destroy_irq(irq); -} -  #ifdef CONFIG_XEN_DOM0  static int xen_initdom_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)  { @@ -242,45 +305,28 @@ out:  	return ret;  }  #endif -#endif -static int xen_pcifront_enable_irq(struct pci_dev *dev) +static void xen_teardown_msi_irqs(struct pci_dev *dev)  { -	int rc; -	int share = 1; -	int pirq; -	u8 gsi; - -	rc = pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &gsi); -	if (rc < 0) { -		dev_warn(&dev->dev, "Xen PCI: failed to read interrupt line: %d\n", -			 rc); -		return rc; -	} - -	rc = xen_allocate_pirq_gsi(gsi); -	if (rc < 0) { -		dev_warn(&dev->dev, "Xen PCI: failed to allocate a PIRQ for GSI%d: %d\n", -			 gsi, rc); -		return rc; -	} -	pirq = rc; +	struct msi_desc *msidesc; -	if (gsi < NR_IRQS_LEGACY) -		share = 0; +	msidesc = list_entry(dev->msi_list.next, struct msi_desc, list); +	if (msidesc->msi_attrib.is_msix) +		xen_pci_frontend_disable_msix(dev); +	else +		xen_pci_frontend_disable_msi(dev); -	rc = xen_bind_pirq_gsi_to_irq(gsi, pirq, share, "pcifront"); -	if (rc < 0) { -		dev_warn(&dev->dev, "Xen PCI: failed to bind GSI%d (PIRQ%d) to IRQ: %d\n", -			 gsi, pirq, rc); -		return rc; -	} +	/* Free the IRQ's and the msidesc using the generic code. */ +	default_teardown_msi_irqs(dev); +} -	dev->irq = rc; -	dev_info(&dev->dev, "Xen PCI mapped GSI%d to IRQ%d\n", gsi, dev->irq); -	return 0; +static void xen_teardown_msi_irq(unsigned int irq) +{ +	xen_destroy_irq(irq);  } +#endif +  int __init pci_xen_init(void)  {  	if (!xen_pv_domain() || xen_initial_domain()) @@ -327,79 +373,6 @@ int __init pci_xen_hvm_init(void)  }  #ifdef CONFIG_XEN_DOM0 -static int xen_register_pirq(u32 gsi, int gsi_override, int triggering) -{ -	int rc, pirq, irq = -1; -	struct physdev_map_pirq map_irq; -	int shareable = 0; -	char *name; - -	if (!xen_pv_domain()) -		return -1; - -	if (triggering == ACPI_EDGE_SENSITIVE) { -		shareable = 0; -		name = "ioapic-edge"; -	} else { -		shareable = 1; -		name = "ioapic-level"; -	} -	pirq = xen_allocate_pirq_gsi(gsi); -	if (pirq < 0) -		goto out; - -	if (gsi_override >= 0) -		irq = xen_bind_pirq_gsi_to_irq(gsi_override, pirq, shareable, name); -	else -		irq = xen_bind_pirq_gsi_to_irq(gsi, pirq, shareable, name); -	if (irq < 0) -		goto out; - -	printk(KERN_DEBUG "xen: --> pirq=%d -> irq=%d (gsi=%d)\n", pirq, irq, gsi); - -	map_irq.domid = DOMID_SELF; -	map_irq.type = MAP_PIRQ_TYPE_GSI; -	map_irq.index = gsi; -	map_irq.pirq = pirq; - -	rc = HYPERVISOR_physdev_op(PHYSDEVOP_map_pirq, &map_irq); -	if (rc) { -		printk(KERN_WARNING "xen map irq failed %d\n", rc); -		return -1; -	} - -out: -	return irq; -} - -static int xen_register_gsi(u32 gsi, int gsi_override, int triggering, int polarity) -{ -	int rc, irq; -	struct physdev_setup_gsi setup_gsi; - -	if (!xen_pv_domain()) -		return -1; - -	printk(KERN_DEBUG "xen: registering gsi %u triggering %d polarity %d\n", -			gsi, triggering, polarity); - -	irq = xen_register_pirq(gsi, gsi_override, triggering); - -	setup_gsi.gsi = gsi; -	setup_gsi.triggering = (triggering == ACPI_EDGE_SENSITIVE ? 0 : 1); -	setup_gsi.polarity = (polarity == ACPI_ACTIVE_HIGH ? 0 : 1); - -	rc = HYPERVISOR_physdev_op(PHYSDEVOP_setup_gsi, &setup_gsi); -	if (rc == -EEXIST) -		printk(KERN_INFO "Already setup the GSI :%d\n", gsi); -	else if (rc) { -		printk(KERN_ERR "Failed to setup GSI :%d, err_code:%d\n", -				gsi, rc); -	} - -	return irq; -} -  static __init void xen_setup_acpi_sci(void)  {  	int rc; @@ -419,7 +392,7 @@ static __init void xen_setup_acpi_sci(void)  	}  	trigger = trigger ? ACPI_LEVEL_SENSITIVE : ACPI_EDGE_SENSITIVE;  	polarity = polarity ? ACPI_ACTIVE_LOW : ACPI_ACTIVE_HIGH; -	 +  	printk(KERN_INFO "xen: sci override: global_irq=%d trigger=%d "  			"polarity=%d\n", gsi, trigger, polarity); @@ -434,10 +407,9 @@ static __init void xen_setup_acpi_sci(void)  	 * the ACPI interpreter and keels over since IRQ 9 has not been  	 * setup as we had setup IRQ 20 for it).  	 */ -	/* Check whether the GSI != IRQ */  	if (acpi_gsi_to_irq(gsi, &irq) == 0) { -		if (irq >= 0 && irq != gsi) -			/* Bugger, we MUST have that IRQ. */ +		/* Use the provided value if it's valid. */ +		if (irq >= 0)  			gsi_override = irq;  	} @@ -447,41 +419,16 @@ static __init void xen_setup_acpi_sci(void)  	return;  } -static int acpi_register_gsi_xen(struct device *dev, u32 gsi, -				 int trigger, int polarity) +int __init pci_xen_initial_domain(void)  { -	return xen_register_gsi(gsi, -1 /* no GSI override */, trigger, polarity); -} +	int irq; -static int __init pci_xen_initial_domain(void) -{  #ifdef CONFIG_PCI_MSI  	x86_msi.setup_msi_irqs = xen_initdom_setup_msi_irqs;  	x86_msi.teardown_msi_irq = xen_teardown_msi_irq;  #endif  	xen_setup_acpi_sci();  	__acpi_register_gsi = acpi_register_gsi_xen; - -	return 0; -} - -void __init xen_setup_pirqs(void) -{ -	int pirq, irq; - -	pci_xen_initial_domain(); - -	if (0 == nr_ioapics) { -		for (irq = 0; irq < NR_IRQS_LEGACY; irq++) { -			pirq = xen_allocate_pirq_gsi(irq); -			if (WARN(pirq < 0, -				 "Could not allocate PIRQ for legacy interrupt\n")) -				break; -			irq = xen_bind_pirq_gsi_to_irq(irq, pirq, 0, "xt-pic"); -		} -		return; -	} -  	/* Pre-allocate legacy irqs */  	for (irq = 0; irq < NR_IRQS_LEGACY; irq++) {  		int trigger, polarity; @@ -490,12 +437,16 @@ void __init xen_setup_pirqs(void)  			continue;  		xen_register_pirq(irq, -1 /* no GSI override */, -			trigger ? ACPI_LEVEL_SENSITIVE : ACPI_EDGE_SENSITIVE); +			trigger ? ACPI_LEVEL_SENSITIVE : ACPI_EDGE_SENSITIVE, +			true /* Map GSI to PIRQ */);  	} +	if (0 == nr_ioapics) { +		for (irq = 0; irq < NR_IRQS_LEGACY; irq++) +			xen_bind_pirq_gsi_to_irq(irq, irq, 0, "xt-pic"); +	} +	return 0;  } -#endif -#ifdef CONFIG_XEN_DOM0  struct xen_device_domain_owner {  	domid_t domain;  	struct pci_dev *dev;  |