Commit b5401a96 authored by Alex Nixon's avatar Alex Nixon Committed by Konrad Rzeszutek Wilk
Browse files

xen/x86/PCI: Add support for the Xen PCI subsystem



The frontend stub lives in arch/x86/pci/xen.c, alongside other
sub-arch PCI init code (e.g. olpc.c).

It provides a mechanism for Xen PCI frontend to setup/destroy
legacy interrupts, MSI/MSI-X, and PCI configuration operations.

[ Impact: add core of Xen PCI support ]
[ v2: Removed the IOMMU code and only focusing on PCI.]
[ v3: removed usage of pci_scan_all_fns as that does not exist]
[ v4: introduced pci_xen value to fix compile warnings]
[ v5: squished fixes+features in one patch, changed Reviewed-by to Ccs]
[ v7: added Acked-by]
Signed-off-by: default avatarAlex Nixon <alex.nixon@citrix.com>
Signed-off-by: default avatarJeremy Fitzhardinge <jeremy.fitzhardinge@citrix.com>
Signed-off-by: default avatarIan Campbell <ian.campbell@citrix.com>
Signed-off-by: default avatarKonrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Signed-off-by: default avatarStefano Stabellini <stefano.stabellini@eu.citrix.com>
Acked-by: default avatarJesse Barnes <jbarnes@virtuousgeek.org>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Cc: Matthew Wilcox <willy@linux.intel.com>
Cc: Qing He <qing.he@intel.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: x86@kernel.org
parent 294ee6f8
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -1898,6 +1898,11 @@ config PCI_OLPC
	def_bool y
	depends on PCI && OLPC && (PCI_GOOLPC || PCI_GOANY)

config PCI_XEN
	def_bool y
	depends on PCI && XEN
	select SWIOTLB_XEN

config PCI_DOMAINS
	def_bool y
	depends on PCI
+53 −0
Original line number Diff line number Diff line
#ifndef _ASM_X86_XEN_PCI_H
#define _ASM_X86_XEN_PCI_H

#if defined(CONFIG_PCI_XEN)
extern int __init pci_xen_init(void);
#define pci_xen 1
#else
#define pci_xen 0
#define pci_xen_init (0)
#endif

#if defined(CONFIG_PCI_MSI)
#if defined(CONFIG_PCI_XEN)
/* The drivers/pci/xen-pcifront.c sets this structure to
 * its own functions.
 */
struct xen_pci_frontend_ops {
	int (*enable_msi)(struct pci_dev *dev, int **vectors);
	void (*disable_msi)(struct pci_dev *dev);
	int (*enable_msix)(struct pci_dev *dev, int **vectors, int nvec);
	void (*disable_msix)(struct pci_dev *dev);
};

extern struct xen_pci_frontend_ops *xen_pci_frontend;

static inline int xen_pci_frontend_enable_msi(struct pci_dev *dev,
					      int **vectors)
{
	if (xen_pci_frontend && xen_pci_frontend->enable_msi)
		return xen_pci_frontend->enable_msi(dev, vectors);
	return -ENODEV;
}
static inline void xen_pci_frontend_disable_msi(struct pci_dev *dev)
{
	if (xen_pci_frontend && xen_pci_frontend->disable_msi)
			xen_pci_frontend->disable_msi(dev);
}
static inline int xen_pci_frontend_enable_msix(struct pci_dev *dev,
					       int **vectors, int nvec)
{
	if (xen_pci_frontend && xen_pci_frontend->enable_msix)
		return xen_pci_frontend->enable_msix(dev, vectors, nvec);
	return -ENODEV;
}
static inline void xen_pci_frontend_disable_msix(struct pci_dev *dev)
{
	if (xen_pci_frontend && xen_pci_frontend->disable_msix)
			xen_pci_frontend->disable_msix(dev);
}
#endif /* CONFIG_PCI_XEN */
#endif /* CONFIG_PCI_MSI */

#endif	/* _ASM_X86_XEN_PCI_H */
+1 −0
Original line number Diff line number Diff line
@@ -4,6 +4,7 @@ obj-$(CONFIG_PCI_BIOS) += pcbios.o
obj-$(CONFIG_PCI_MMCONFIG)	+= mmconfig_$(BITS).o direct.o mmconfig-shared.o
obj-$(CONFIG_PCI_DIRECT)	+= direct.o
obj-$(CONFIG_PCI_OLPC)		+= olpc.o
obj-$(CONFIG_PCI_XEN)		+= xen.o

obj-y				+= fixup.o
obj-$(CONFIG_ACPI)		+= acpi.o

arch/x86/pci/xen.c

0 → 100644
+147 −0
Original line number Diff line number Diff line
/*
 * Xen PCI Frontend Stub - puts some "dummy" functions in to the Linux
 *			   x86 PCI core to support the Xen PCI Frontend
 *
 *   Author: Ryan Wilson <hap9@epoch.ncsc.mil>
 */
#include <linux/module.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/acpi.h>

#include <linux/io.h>
#include <asm/pci_x86.h>

#include <asm/xen/hypervisor.h>

#include <xen/events.h>
#include <asm/xen/pci.h>

#if defined(CONFIG_PCI_MSI)
#include <linux/msi.h>

struct xen_pci_frontend_ops *xen_pci_frontend;
EXPORT_SYMBOL_GPL(xen_pci_frontend);

/*
 * 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 (!xen_initial_domain()) {
		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_allocate_pirq(v[i], 0, /* not sharable */
			(type == PCI_CAP_ID_MSIX) ?
			"pcifront-msi-x" : "pcifront-msi");
		if (irq < 0)
			return -1;

		ret = set_irq_msi(irq, msidesc);
		if (ret)
			goto error_while;
		i++;
	}
	kfree(v);
	return 0;

error_while:
	unbind_from_irqhandler(irq, NULL);
error:
	if (ret == -ENODEV)
		dev_err(&dev->dev, "Xen PCI frontend has not registered" \
			" MSI/MSI-X support!\n");

	kfree(v);
	return ret;
}

static void xen_teardown_msi_irqs(struct pci_dev *dev)
{
	/* Only do this when were are in non-privileged mode.*/
	if (!xen_initial_domain()) {
		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);
	}

}

static void xen_teardown_msi_irq(unsigned int irq)
{
	xen_destroy_irq(irq);
}
#endif

static int xen_pcifront_enable_irq(struct pci_dev *dev)
{
	int rc;
	int share = 1;

	dev_info(&dev->dev, "Xen PCI enabling IRQ: %d\n", dev->irq);

	if (dev->irq < 0)
		return -EINVAL;

	if (dev->irq < NR_IRQS_LEGACY)
		share = 0;

	rc = xen_allocate_pirq(dev->irq, share, "pcifront");
	if (rc < 0) {
		dev_warn(&dev->dev, "Xen PCI IRQ: %d, failed to register:%d\n",
			 dev->irq, rc);
		return rc;
	}
	return 0;
}

int __init pci_xen_init(void)
{
	if (!xen_pv_domain() || xen_initial_domain())
		return -ENODEV;

	printk(KERN_INFO "PCI: setting up Xen PCI frontend stub\n");

	pcibios_set_cache_line_size();

	pcibios_enable_irq = xen_pcifront_enable_irq;
	pcibios_disable_irq = NULL;

#ifdef CONFIG_ACPI
	/* Keep ACPI out of the picture */
	acpi_noirq = 1;
#endif

#ifdef CONFIG_ISAPNP
	/* Stop isapnp from probing */
	isapnp_disable = 1;
#endif

#ifdef CONFIG_PCI_MSI
	x86_msi.setup_msi_irqs = xen_setup_msi_irqs;
	x86_msi.teardown_msi_irq = xen_teardown_msi_irq;
	x86_msi.teardown_msi_irqs = xen_teardown_msi_irqs;
#endif
	return 0;
}
+3 −0
Original line number Diff line number Diff line
@@ -45,6 +45,7 @@
#include <asm/paravirt.h>
#include <asm/apic.h>
#include <asm/page.h>
#include <asm/xen/pci.h>
#include <asm/xen/hypercall.h>
#include <asm/xen/hypervisor.h>
#include <asm/fixmap.h>
@@ -1220,6 +1221,8 @@ asmlinkage void __init xen_start_kernel(void)
		add_preferred_console("xenboot", 0, NULL);
		add_preferred_console("tty", 0, NULL);
		add_preferred_console("hvc", 0, NULL);
		if (pci_xen)
			x86_init.pci.arch_init = pci_xen_init;
	} else {
		/* Make sure ACS will be enabled */
		pci_request_acs();
Loading