diff options
-rw-r--r-- | arch/x86/Kconfig | 5 | ||||
-rw-r--r-- | arch/x86/include/asm/xen/pci.h | 53 | ||||
-rw-r--r-- | arch/x86/pci/Makefile | 1 | ||||
-rw-r--r-- | arch/x86/pci/xen.c | 147 | ||||
-rw-r--r-- | arch/x86/xen/enlighten.c | 3 | ||||
-rw-r--r-- | drivers/xen/events.c | 32 | ||||
-rw-r--r-- | include/xen/events.h | 3 |
7 files changed, 242 insertions, 2 deletions
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 8cc510874e1b..74ea59d34076 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig | |||
@@ -1898,6 +1898,11 @@ config PCI_OLPC | |||
1898 | def_bool y | 1898 | def_bool y |
1899 | depends on PCI && OLPC && (PCI_GOOLPC || PCI_GOANY) | 1899 | depends on PCI && OLPC && (PCI_GOOLPC || PCI_GOANY) |
1900 | 1900 | ||
1901 | config PCI_XEN | ||
1902 | def_bool y | ||
1903 | depends on PCI && XEN | ||
1904 | select SWIOTLB_XEN | ||
1905 | |||
1901 | config PCI_DOMAINS | 1906 | config PCI_DOMAINS |
1902 | def_bool y | 1907 | def_bool y |
1903 | depends on PCI | 1908 | depends on PCI |
diff --git a/arch/x86/include/asm/xen/pci.h b/arch/x86/include/asm/xen/pci.h new file mode 100644 index 000000000000..449c82f71677 --- /dev/null +++ b/arch/x86/include/asm/xen/pci.h | |||
@@ -0,0 +1,53 @@ | |||
1 | #ifndef _ASM_X86_XEN_PCI_H | ||
2 | #define _ASM_X86_XEN_PCI_H | ||
3 | |||
4 | #if defined(CONFIG_PCI_XEN) | ||
5 | extern int __init pci_xen_init(void); | ||
6 | #define pci_xen 1 | ||
7 | #else | ||
8 | #define pci_xen 0 | ||
9 | #define pci_xen_init (0) | ||
10 | #endif | ||
11 | |||
12 | #if defined(CONFIG_PCI_MSI) | ||
13 | #if defined(CONFIG_PCI_XEN) | ||
14 | /* The drivers/pci/xen-pcifront.c sets this structure to | ||
15 | * its own functions. | ||
16 | */ | ||
17 | struct xen_pci_frontend_ops { | ||
18 | int (*enable_msi)(struct pci_dev *dev, int **vectors); | ||
19 | void (*disable_msi)(struct pci_dev *dev); | ||
20 | int (*enable_msix)(struct pci_dev *dev, int **vectors, int nvec); | ||
21 | void (*disable_msix)(struct pci_dev *dev); | ||
22 | }; | ||
23 | |||
24 | extern struct xen_pci_frontend_ops *xen_pci_frontend; | ||
25 | |||
26 | static inline int xen_pci_frontend_enable_msi(struct pci_dev *dev, | ||
27 | int **vectors) | ||
28 | { | ||
29 | if (xen_pci_frontend && xen_pci_frontend->enable_msi) | ||
30 | return xen_pci_frontend->enable_msi(dev, vectors); | ||
31 | return -ENODEV; | ||
32 | } | ||
33 | static inline void xen_pci_frontend_disable_msi(struct pci_dev *dev) | ||
34 | { | ||
35 | if (xen_pci_frontend && xen_pci_frontend->disable_msi) | ||
36 | xen_pci_frontend->disable_msi(dev); | ||
37 | } | ||
38 | static inline int xen_pci_frontend_enable_msix(struct pci_dev *dev, | ||
39 | int **vectors, int nvec) | ||
40 | { | ||
41 | if (xen_pci_frontend && xen_pci_frontend->enable_msix) | ||
42 | return xen_pci_frontend->enable_msix(dev, vectors, nvec); | ||
43 | return -ENODEV; | ||
44 | } | ||
45 | static inline void xen_pci_frontend_disable_msix(struct pci_dev *dev) | ||
46 | { | ||
47 | if (xen_pci_frontend && xen_pci_frontend->disable_msix) | ||
48 | xen_pci_frontend->disable_msix(dev); | ||
49 | } | ||
50 | #endif /* CONFIG_PCI_XEN */ | ||
51 | #endif /* CONFIG_PCI_MSI */ | ||
52 | |||
53 | #endif /* _ASM_X86_XEN_PCI_H */ | ||
diff --git a/arch/x86/pci/Makefile b/arch/x86/pci/Makefile index a0207a7fdf39..effd96e33f16 100644 --- a/arch/x86/pci/Makefile +++ b/arch/x86/pci/Makefile | |||
@@ -4,6 +4,7 @@ obj-$(CONFIG_PCI_BIOS) += pcbios.o | |||
4 | obj-$(CONFIG_PCI_MMCONFIG) += mmconfig_$(BITS).o direct.o mmconfig-shared.o | 4 | obj-$(CONFIG_PCI_MMCONFIG) += mmconfig_$(BITS).o direct.o mmconfig-shared.o |
5 | obj-$(CONFIG_PCI_DIRECT) += direct.o | 5 | obj-$(CONFIG_PCI_DIRECT) += direct.o |
6 | obj-$(CONFIG_PCI_OLPC) += olpc.o | 6 | obj-$(CONFIG_PCI_OLPC) += olpc.o |
7 | obj-$(CONFIG_PCI_XEN) += xen.o | ||
7 | 8 | ||
8 | obj-y += fixup.o | 9 | obj-y += fixup.o |
9 | obj-$(CONFIG_ACPI) += acpi.o | 10 | obj-$(CONFIG_ACPI) += acpi.o |
diff --git a/arch/x86/pci/xen.c b/arch/x86/pci/xen.c new file mode 100644 index 000000000000..b19c873d8d0c --- /dev/null +++ b/arch/x86/pci/xen.c | |||
@@ -0,0 +1,147 @@ | |||
1 | /* | ||
2 | * Xen PCI Frontend Stub - puts some "dummy" functions in to the Linux | ||
3 | * x86 PCI core to support the Xen PCI Frontend | ||
4 | * | ||
5 | * Author: Ryan Wilson <hap9@epoch.ncsc.mil> | ||
6 | */ | ||
7 | #include <linux/module.h> | ||
8 | #include <linux/init.h> | ||
9 | #include <linux/pci.h> | ||
10 | #include <linux/acpi.h> | ||
11 | |||
12 | #include <linux/io.h> | ||
13 | #include <asm/pci_x86.h> | ||
14 | |||
15 | #include <asm/xen/hypervisor.h> | ||
16 | |||
17 | #include <xen/events.h> | ||
18 | #include <asm/xen/pci.h> | ||
19 | |||
20 | #if defined(CONFIG_PCI_MSI) | ||
21 | #include <linux/msi.h> | ||
22 | |||
23 | struct xen_pci_frontend_ops *xen_pci_frontend; | ||
24 | EXPORT_SYMBOL_GPL(xen_pci_frontend); | ||
25 | |||
26 | /* | ||
27 | * For MSI interrupts we have to use drivers/xen/event.s functions to | ||
28 | * allocate an irq_desc and setup the right */ | ||
29 | |||
30 | |||
31 | static int xen_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) | ||
32 | { | ||
33 | int irq, ret, i; | ||
34 | struct msi_desc *msidesc; | ||
35 | int *v; | ||
36 | |||
37 | v = kzalloc(sizeof(int) * max(1, nvec), GFP_KERNEL); | ||
38 | if (!v) | ||
39 | return -ENOMEM; | ||
40 | |||
41 | if (!xen_initial_domain()) { | ||
42 | if (type == PCI_CAP_ID_MSIX) | ||
43 | ret = xen_pci_frontend_enable_msix(dev, &v, nvec); | ||
44 | else | ||
45 | ret = xen_pci_frontend_enable_msi(dev, &v); | ||
46 | if (ret) | ||
47 | goto error; | ||
48 | } | ||
49 | i = 0; | ||
50 | list_for_each_entry(msidesc, &dev->msi_list, list) { | ||
51 | irq = xen_allocate_pirq(v[i], 0, /* not sharable */ | ||
52 | (type == PCI_CAP_ID_MSIX) ? | ||
53 | "pcifront-msi-x" : "pcifront-msi"); | ||
54 | if (irq < 0) | ||
55 | return -1; | ||
56 | |||
57 | ret = set_irq_msi(irq, msidesc); | ||
58 | if (ret) | ||
59 | goto error_while; | ||
60 | i++; | ||
61 | } | ||
62 | kfree(v); | ||
63 | return 0; | ||
64 | |||
65 | error_while: | ||
66 | unbind_from_irqhandler(irq, NULL); | ||
67 | error: | ||
68 | if (ret == -ENODEV) | ||
69 | dev_err(&dev->dev, "Xen PCI frontend has not registered" \ | ||
70 | " MSI/MSI-X support!\n"); | ||
71 | |||
72 | kfree(v); | ||
73 | return ret; | ||
74 | } | ||
75 | |||
76 | static void xen_teardown_msi_irqs(struct pci_dev *dev) | ||
77 | { | ||
78 | /* Only do this when were are in non-privileged mode.*/ | ||
79 | if (!xen_initial_domain()) { | ||
80 | struct msi_desc *msidesc; | ||
81 | |||
82 | msidesc = list_entry(dev->msi_list.next, struct msi_desc, list); | ||
83 | if (msidesc->msi_attrib.is_msix) | ||
84 | xen_pci_frontend_disable_msix(dev); | ||
85 | else | ||
86 | xen_pci_frontend_disable_msi(dev); | ||
87 | } | ||
88 | |||
89 | } | ||
90 | |||
91 | static void xen_teardown_msi_irq(unsigned int irq) | ||
92 | { | ||
93 | xen_destroy_irq(irq); | ||
94 | } | ||
95 | #endif | ||
96 | |||
97 | static int xen_pcifront_enable_irq(struct pci_dev *dev) | ||
98 | { | ||
99 | int rc; | ||
100 | int share = 1; | ||
101 | |||
102 | dev_info(&dev->dev, "Xen PCI enabling IRQ: %d\n", dev->irq); | ||
103 | |||
104 | if (dev->irq < 0) | ||
105 | return -EINVAL; | ||
106 | |||
107 | if (dev->irq < NR_IRQS_LEGACY) | ||
108 | share = 0; | ||
109 | |||
110 | rc = xen_allocate_pirq(dev->irq, share, "pcifront"); | ||
111 | if (rc < 0) { | ||
112 | dev_warn(&dev->dev, "Xen PCI IRQ: %d, failed to register:%d\n", | ||
113 | dev->irq, rc); | ||
114 | return rc; | ||
115 | } | ||
116 | return 0; | ||
117 | } | ||
118 | |||
119 | int __init pci_xen_init(void) | ||
120 | { | ||
121 | if (!xen_pv_domain() || xen_initial_domain()) | ||
122 | return -ENODEV; | ||
123 | |||
124 | printk(KERN_INFO "PCI: setting up Xen PCI frontend stub\n"); | ||
125 | |||
126 | pcibios_set_cache_line_size(); | ||
127 | |||
128 | pcibios_enable_irq = xen_pcifront_enable_irq; | ||
129 | pcibios_disable_irq = NULL; | ||
130 | |||
131 | #ifdef CONFIG_ACPI | ||
132 | /* Keep ACPI out of the picture */ | ||
133 | acpi_noirq = 1; | ||
134 | #endif | ||
135 | |||
136 | #ifdef CONFIG_ISAPNP | ||
137 | /* Stop isapnp from probing */ | ||
138 | isapnp_disable = 1; | ||
139 | #endif | ||
140 | |||
141 | #ifdef CONFIG_PCI_MSI | ||
142 | x86_msi.setup_msi_irqs = xen_setup_msi_irqs; | ||
143 | x86_msi.teardown_msi_irq = xen_teardown_msi_irq; | ||
144 | x86_msi.teardown_msi_irqs = xen_teardown_msi_irqs; | ||
145 | #endif | ||
146 | return 0; | ||
147 | } | ||
diff --git a/arch/x86/xen/enlighten.c b/arch/x86/xen/enlighten.c index 7d46c8441418..1ccfa1bf0f89 100644 --- a/arch/x86/xen/enlighten.c +++ b/arch/x86/xen/enlighten.c | |||
@@ -45,6 +45,7 @@ | |||
45 | #include <asm/paravirt.h> | 45 | #include <asm/paravirt.h> |
46 | #include <asm/apic.h> | 46 | #include <asm/apic.h> |
47 | #include <asm/page.h> | 47 | #include <asm/page.h> |
48 | #include <asm/xen/pci.h> | ||
48 | #include <asm/xen/hypercall.h> | 49 | #include <asm/xen/hypercall.h> |
49 | #include <asm/xen/hypervisor.h> | 50 | #include <asm/xen/hypervisor.h> |
50 | #include <asm/fixmap.h> | 51 | #include <asm/fixmap.h> |
@@ -1220,6 +1221,8 @@ asmlinkage void __init xen_start_kernel(void) | |||
1220 | add_preferred_console("xenboot", 0, NULL); | 1221 | add_preferred_console("xenboot", 0, NULL); |
1221 | add_preferred_console("tty", 0, NULL); | 1222 | add_preferred_console("tty", 0, NULL); |
1222 | add_preferred_console("hvc", 0, NULL); | 1223 | add_preferred_console("hvc", 0, NULL); |
1224 | if (pci_xen) | ||
1225 | x86_init.pci.arch_init = pci_xen_init; | ||
1223 | } else { | 1226 | } else { |
1224 | /* Make sure ACS will be enabled */ | 1227 | /* Make sure ACS will be enabled */ |
1225 | pci_request_acs(); | 1228 | pci_request_acs(); |
diff --git a/drivers/xen/events.c b/drivers/xen/events.c index cd504092299b..7016a734257c 100644 --- a/drivers/xen/events.c +++ b/drivers/xen/events.c | |||
@@ -582,7 +582,9 @@ int xen_allocate_pirq(unsigned gsi, int shareable, char *name) | |||
582 | goto out; /* XXX need refcount? */ | 582 | goto out; /* XXX need refcount? */ |
583 | } | 583 | } |
584 | 584 | ||
585 | if (identity_mapped_irq(gsi)) { | 585 | /* If we are a PV guest, we don't have GSIs (no ACPI passed). Therefore |
586 | * we are using the !xen_initial_domain() to drop in the function.*/ | ||
587 | if (identity_mapped_irq(gsi) || !xen_initial_domain()) { | ||
586 | irq = gsi; | 588 | irq = gsi; |
587 | irq_to_desc_alloc_node(irq, 0); | 589 | irq_to_desc_alloc_node(irq, 0); |
588 | dynamic_irq_init(irq); | 590 | dynamic_irq_init(irq); |
@@ -593,7 +595,13 @@ int xen_allocate_pirq(unsigned gsi, int shareable, char *name) | |||
593 | handle_level_irq, name); | 595 | handle_level_irq, name); |
594 | 596 | ||
595 | irq_op.irq = irq; | 597 | irq_op.irq = irq; |
596 | if (HYPERVISOR_physdev_op(PHYSDEVOP_alloc_irq_vector, &irq_op)) { | 598 | irq_op.vector = 0; |
599 | |||
600 | /* Only the privileged domain can do this. For non-priv, the pcifront | ||
601 | * driver provides a PCI bus that does the call to do exactly | ||
602 | * this in the priv domain. */ | ||
603 | if (xen_initial_domain() && | ||
604 | HYPERVISOR_physdev_op(PHYSDEVOP_alloc_irq_vector, &irq_op)) { | ||
597 | dynamic_irq_cleanup(irq); | 605 | dynamic_irq_cleanup(irq); |
598 | irq = -ENOSPC; | 606 | irq = -ENOSPC; |
599 | goto out; | 607 | goto out; |
@@ -608,6 +616,26 @@ out: | |||
608 | return irq; | 616 | return irq; |
609 | } | 617 | } |
610 | 618 | ||
619 | int xen_destroy_irq(int irq) | ||
620 | { | ||
621 | struct irq_desc *desc; | ||
622 | int rc = -ENOENT; | ||
623 | |||
624 | spin_lock(&irq_mapping_update_lock); | ||
625 | |||
626 | desc = irq_to_desc(irq); | ||
627 | if (!desc) | ||
628 | goto out; | ||
629 | |||
630 | irq_info[irq] = mk_unbound_info(); | ||
631 | |||
632 | dynamic_irq_cleanup(irq); | ||
633 | |||
634 | out: | ||
635 | spin_unlock(&irq_mapping_update_lock); | ||
636 | return rc; | ||
637 | } | ||
638 | |||
611 | int xen_vector_from_irq(unsigned irq) | 639 | int xen_vector_from_irq(unsigned irq) |
612 | { | 640 | { |
613 | return vector_from_irq(irq); | 641 | return vector_from_irq(irq); |
diff --git a/include/xen/events.h b/include/xen/events.h index d7a4ca7d17b5..c1717ca5ac13 100644 --- a/include/xen/events.h +++ b/include/xen/events.h | |||
@@ -72,6 +72,9 @@ void xen_hvm_evtchn_do_upcall(void); | |||
72 | * usual. */ | 72 | * usual. */ |
73 | int xen_allocate_pirq(unsigned gsi, int shareable, char *name); | 73 | int xen_allocate_pirq(unsigned gsi, int shareable, char *name); |
74 | 74 | ||
75 | /* De-allocates the above mentioned physical interrupt. */ | ||
76 | int xen_destroy_irq(int irq); | ||
77 | |||
75 | /* Return vector allocated to pirq */ | 78 | /* Return vector allocated to pirq */ |
76 | int xen_vector_from_irq(unsigned pirq); | 79 | int xen_vector_from_irq(unsigned pirq); |
77 | 80 | ||