diff options
Diffstat (limited to 'arch/x86/pci/xen.c')
-rw-r--r-- | arch/x86/pci/xen.c | 414 |
1 files changed, 414 insertions, 0 deletions
diff --git a/arch/x86/pci/xen.c b/arch/x86/pci/xen.c new file mode 100644 index 000000000000..117f5b8daf75 --- /dev/null +++ b/arch/x86/pci/xen.c | |||
@@ -0,0 +1,414 @@ | |||
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/io_apic.h> | ||
14 | #include <asm/pci_x86.h> | ||
15 | |||
16 | #include <asm/xen/hypervisor.h> | ||
17 | |||
18 | #include <xen/features.h> | ||
19 | #include <xen/events.h> | ||
20 | #include <asm/xen/pci.h> | ||
21 | |||
22 | #ifdef CONFIG_ACPI | ||
23 | static int xen_hvm_register_pirq(u32 gsi, int triggering) | ||
24 | { | ||
25 | int rc, irq; | ||
26 | struct physdev_map_pirq map_irq; | ||
27 | int shareable = 0; | ||
28 | char *name; | ||
29 | |||
30 | if (!xen_hvm_domain()) | ||
31 | return -1; | ||
32 | |||
33 | map_irq.domid = DOMID_SELF; | ||
34 | map_irq.type = MAP_PIRQ_TYPE_GSI; | ||
35 | map_irq.index = gsi; | ||
36 | map_irq.pirq = -1; | ||
37 | |||
38 | rc = HYPERVISOR_physdev_op(PHYSDEVOP_map_pirq, &map_irq); | ||
39 | if (rc) { | ||
40 | printk(KERN_WARNING "xen map irq failed %d\n", rc); | ||
41 | return -1; | ||
42 | } | ||
43 | |||
44 | if (triggering == ACPI_EDGE_SENSITIVE) { | ||
45 | shareable = 0; | ||
46 | name = "ioapic-edge"; | ||
47 | } else { | ||
48 | shareable = 1; | ||
49 | name = "ioapic-level"; | ||
50 | } | ||
51 | |||
52 | irq = xen_map_pirq_gsi(map_irq.pirq, gsi, shareable, name); | ||
53 | |||
54 | printk(KERN_DEBUG "xen: --> irq=%d, pirq=%d\n", irq, map_irq.pirq); | ||
55 | |||
56 | return irq; | ||
57 | } | ||
58 | |||
59 | static int acpi_register_gsi_xen_hvm(struct device *dev, u32 gsi, | ||
60 | int trigger, int polarity) | ||
61 | { | ||
62 | return xen_hvm_register_pirq(gsi, trigger); | ||
63 | } | ||
64 | #endif | ||
65 | |||
66 | #if defined(CONFIG_PCI_MSI) | ||
67 | #include <linux/msi.h> | ||
68 | #include <asm/msidef.h> | ||
69 | |||
70 | struct xen_pci_frontend_ops *xen_pci_frontend; | ||
71 | EXPORT_SYMBOL_GPL(xen_pci_frontend); | ||
72 | |||
73 | static void xen_msi_compose_msg(struct pci_dev *pdev, unsigned int pirq, | ||
74 | struct msi_msg *msg) | ||
75 | { | ||
76 | /* We set vector == 0 to tell the hypervisor we don't care about it, | ||
77 | * but we want a pirq setup instead. | ||
78 | * We use the dest_id field to pass the pirq that we want. */ | ||
79 | msg->address_hi = MSI_ADDR_BASE_HI | MSI_ADDR_EXT_DEST_ID(pirq); | ||
80 | msg->address_lo = | ||
81 | MSI_ADDR_BASE_LO | | ||
82 | MSI_ADDR_DEST_MODE_PHYSICAL | | ||
83 | MSI_ADDR_REDIRECTION_CPU | | ||
84 | MSI_ADDR_DEST_ID(pirq); | ||
85 | |||
86 | msg->data = | ||
87 | MSI_DATA_TRIGGER_EDGE | | ||
88 | MSI_DATA_LEVEL_ASSERT | | ||
89 | /* delivery mode reserved */ | ||
90 | (3 << 8) | | ||
91 | MSI_DATA_VECTOR(0); | ||
92 | } | ||
93 | |||
94 | static int xen_hvm_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) | ||
95 | { | ||
96 | int irq, pirq, ret = 0; | ||
97 | struct msi_desc *msidesc; | ||
98 | struct msi_msg msg; | ||
99 | |||
100 | list_for_each_entry(msidesc, &dev->msi_list, list) { | ||
101 | xen_allocate_pirq_msi((type == PCI_CAP_ID_MSIX) ? | ||
102 | "msi-x" : "msi", &irq, &pirq); | ||
103 | if (irq < 0 || pirq < 0) | ||
104 | goto error; | ||
105 | printk(KERN_DEBUG "xen: msi --> irq=%d, pirq=%d\n", irq, pirq); | ||
106 | xen_msi_compose_msg(dev, pirq, &msg); | ||
107 | ret = set_irq_msi(irq, msidesc); | ||
108 | if (ret < 0) | ||
109 | goto error_while; | ||
110 | write_msi_msg(irq, &msg); | ||
111 | } | ||
112 | return 0; | ||
113 | |||
114 | error_while: | ||
115 | unbind_from_irqhandler(irq, NULL); | ||
116 | error: | ||
117 | if (ret == -ENODEV) | ||
118 | dev_err(&dev->dev, "Xen PCI frontend has not registered" \ | ||
119 | " MSI/MSI-X support!\n"); | ||
120 | |||
121 | return ret; | ||
122 | } | ||
123 | |||
124 | /* | ||
125 | * For MSI interrupts we have to use drivers/xen/event.s functions to | ||
126 | * allocate an irq_desc and setup the right */ | ||
127 | |||
128 | |||
129 | static int xen_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) | ||
130 | { | ||
131 | int irq, ret, i; | ||
132 | struct msi_desc *msidesc; | ||
133 | int *v; | ||
134 | |||
135 | v = kzalloc(sizeof(int) * max(1, nvec), GFP_KERNEL); | ||
136 | if (!v) | ||
137 | return -ENOMEM; | ||
138 | |||
139 | if (type == PCI_CAP_ID_MSIX) | ||
140 | ret = xen_pci_frontend_enable_msix(dev, &v, nvec); | ||
141 | else | ||
142 | ret = xen_pci_frontend_enable_msi(dev, &v); | ||
143 | if (ret) | ||
144 | goto error; | ||
145 | i = 0; | ||
146 | list_for_each_entry(msidesc, &dev->msi_list, list) { | ||
147 | irq = xen_allocate_pirq(v[i], 0, /* not sharable */ | ||
148 | (type == PCI_CAP_ID_MSIX) ? | ||
149 | "pcifront-msi-x" : "pcifront-msi"); | ||
150 | if (irq < 0) | ||
151 | return -1; | ||
152 | |||
153 | ret = set_irq_msi(irq, msidesc); | ||
154 | if (ret) | ||
155 | goto error_while; | ||
156 | i++; | ||
157 | } | ||
158 | kfree(v); | ||
159 | return 0; | ||
160 | |||
161 | error_while: | ||
162 | unbind_from_irqhandler(irq, NULL); | ||
163 | error: | ||
164 | if (ret == -ENODEV) | ||
165 | dev_err(&dev->dev, "Xen PCI frontend has not registered" \ | ||
166 | " MSI/MSI-X support!\n"); | ||
167 | |||
168 | kfree(v); | ||
169 | return ret; | ||
170 | } | ||
171 | |||
172 | static void xen_teardown_msi_irqs(struct pci_dev *dev) | ||
173 | { | ||
174 | struct msi_desc *msidesc; | ||
175 | |||
176 | msidesc = list_entry(dev->msi_list.next, struct msi_desc, list); | ||
177 | if (msidesc->msi_attrib.is_msix) | ||
178 | xen_pci_frontend_disable_msix(dev); | ||
179 | else | ||
180 | xen_pci_frontend_disable_msi(dev); | ||
181 | } | ||
182 | |||
183 | static void xen_teardown_msi_irq(unsigned int irq) | ||
184 | { | ||
185 | xen_destroy_irq(irq); | ||
186 | } | ||
187 | |||
188 | static int xen_initdom_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) | ||
189 | { | ||
190 | int irq, ret; | ||
191 | struct msi_desc *msidesc; | ||
192 | |||
193 | list_for_each_entry(msidesc, &dev->msi_list, list) { | ||
194 | irq = xen_create_msi_irq(dev, msidesc, type); | ||
195 | if (irq < 0) | ||
196 | return -1; | ||
197 | |||
198 | ret = set_irq_msi(irq, msidesc); | ||
199 | if (ret) | ||
200 | goto error; | ||
201 | } | ||
202 | return 0; | ||
203 | |||
204 | error: | ||
205 | xen_destroy_irq(irq); | ||
206 | return ret; | ||
207 | } | ||
208 | #endif | ||
209 | |||
210 | static int xen_pcifront_enable_irq(struct pci_dev *dev) | ||
211 | { | ||
212 | int rc; | ||
213 | int share = 1; | ||
214 | |||
215 | dev_info(&dev->dev, "Xen PCI enabling IRQ: %d\n", dev->irq); | ||
216 | |||
217 | if (dev->irq < 0) | ||
218 | return -EINVAL; | ||
219 | |||
220 | if (dev->irq < NR_IRQS_LEGACY) | ||
221 | share = 0; | ||
222 | |||
223 | rc = xen_allocate_pirq(dev->irq, share, "pcifront"); | ||
224 | if (rc < 0) { | ||
225 | dev_warn(&dev->dev, "Xen PCI IRQ: %d, failed to register:%d\n", | ||
226 | dev->irq, rc); | ||
227 | return rc; | ||
228 | } | ||
229 | return 0; | ||
230 | } | ||
231 | |||
232 | int __init pci_xen_init(void) | ||
233 | { | ||
234 | if (!xen_pv_domain() || xen_initial_domain()) | ||
235 | return -ENODEV; | ||
236 | |||
237 | printk(KERN_INFO "PCI: setting up Xen PCI frontend stub\n"); | ||
238 | |||
239 | pcibios_set_cache_line_size(); | ||
240 | |||
241 | pcibios_enable_irq = xen_pcifront_enable_irq; | ||
242 | pcibios_disable_irq = NULL; | ||
243 | |||
244 | #ifdef CONFIG_ACPI | ||
245 | /* Keep ACPI out of the picture */ | ||
246 | acpi_noirq = 1; | ||
247 | #endif | ||
248 | |||
249 | #ifdef CONFIG_PCI_MSI | ||
250 | x86_msi.setup_msi_irqs = xen_setup_msi_irqs; | ||
251 | x86_msi.teardown_msi_irq = xen_teardown_msi_irq; | ||
252 | x86_msi.teardown_msi_irqs = xen_teardown_msi_irqs; | ||
253 | #endif | ||
254 | return 0; | ||
255 | } | ||
256 | |||
257 | int __init pci_xen_hvm_init(void) | ||
258 | { | ||
259 | if (!xen_feature(XENFEAT_hvm_pirqs)) | ||
260 | return 0; | ||
261 | |||
262 | #ifdef CONFIG_ACPI | ||
263 | /* | ||
264 | * We don't want to change the actual ACPI delivery model, | ||
265 | * just how GSIs get registered. | ||
266 | */ | ||
267 | __acpi_register_gsi = acpi_register_gsi_xen_hvm; | ||
268 | #endif | ||
269 | |||
270 | #ifdef CONFIG_PCI_MSI | ||
271 | x86_msi.setup_msi_irqs = xen_hvm_setup_msi_irqs; | ||
272 | x86_msi.teardown_msi_irq = xen_teardown_msi_irq; | ||
273 | #endif | ||
274 | return 0; | ||
275 | } | ||
276 | |||
277 | #ifdef CONFIG_XEN_DOM0 | ||
278 | static int xen_register_pirq(u32 gsi, int triggering) | ||
279 | { | ||
280 | int rc, irq; | ||
281 | struct physdev_map_pirq map_irq; | ||
282 | int shareable = 0; | ||
283 | char *name; | ||
284 | |||
285 | if (!xen_pv_domain()) | ||
286 | return -1; | ||
287 | |||
288 | if (triggering == ACPI_EDGE_SENSITIVE) { | ||
289 | shareable = 0; | ||
290 | name = "ioapic-edge"; | ||
291 | } else { | ||
292 | shareable = 1; | ||
293 | name = "ioapic-level"; | ||
294 | } | ||
295 | |||
296 | irq = xen_allocate_pirq(gsi, shareable, name); | ||
297 | |||
298 | printk(KERN_DEBUG "xen: --> irq=%d\n", irq); | ||
299 | |||
300 | if (irq < 0) | ||
301 | goto out; | ||
302 | |||
303 | map_irq.domid = DOMID_SELF; | ||
304 | map_irq.type = MAP_PIRQ_TYPE_GSI; | ||
305 | map_irq.index = gsi; | ||
306 | map_irq.pirq = irq; | ||
307 | |||
308 | rc = HYPERVISOR_physdev_op(PHYSDEVOP_map_pirq, &map_irq); | ||
309 | if (rc) { | ||
310 | printk(KERN_WARNING "xen map irq failed %d\n", rc); | ||
311 | return -1; | ||
312 | } | ||
313 | |||
314 | out: | ||
315 | return irq; | ||
316 | } | ||
317 | |||
318 | static int xen_register_gsi(u32 gsi, int triggering, int polarity) | ||
319 | { | ||
320 | int rc, irq; | ||
321 | struct physdev_setup_gsi setup_gsi; | ||
322 | |||
323 | if (!xen_pv_domain()) | ||
324 | return -1; | ||
325 | |||
326 | printk(KERN_DEBUG "xen: registering gsi %u triggering %d polarity %d\n", | ||
327 | gsi, triggering, polarity); | ||
328 | |||
329 | irq = xen_register_pirq(gsi, triggering); | ||
330 | |||
331 | setup_gsi.gsi = gsi; | ||
332 | setup_gsi.triggering = (triggering == ACPI_EDGE_SENSITIVE ? 0 : 1); | ||
333 | setup_gsi.polarity = (polarity == ACPI_ACTIVE_HIGH ? 0 : 1); | ||
334 | |||
335 | rc = HYPERVISOR_physdev_op(PHYSDEVOP_setup_gsi, &setup_gsi); | ||
336 | if (rc == -EEXIST) | ||
337 | printk(KERN_INFO "Already setup the GSI :%d\n", gsi); | ||
338 | else if (rc) { | ||
339 | printk(KERN_ERR "Failed to setup GSI :%d, err_code:%d\n", | ||
340 | gsi, rc); | ||
341 | } | ||
342 | |||
343 | return irq; | ||
344 | } | ||
345 | |||
346 | static __init void xen_setup_acpi_sci(void) | ||
347 | { | ||
348 | int rc; | ||
349 | int trigger, polarity; | ||
350 | int gsi = acpi_sci_override_gsi; | ||
351 | |||
352 | if (!gsi) | ||
353 | return; | ||
354 | |||
355 | rc = acpi_get_override_irq(gsi, &trigger, &polarity); | ||
356 | if (rc) { | ||
357 | printk(KERN_WARNING "xen: acpi_get_override_irq failed for acpi" | ||
358 | " sci, rc=%d\n", rc); | ||
359 | return; | ||
360 | } | ||
361 | trigger = trigger ? ACPI_LEVEL_SENSITIVE : ACPI_EDGE_SENSITIVE; | ||
362 | polarity = polarity ? ACPI_ACTIVE_LOW : ACPI_ACTIVE_HIGH; | ||
363 | |||
364 | printk(KERN_INFO "xen: sci override: global_irq=%d trigger=%d " | ||
365 | "polarity=%d\n", gsi, trigger, polarity); | ||
366 | |||
367 | gsi = xen_register_gsi(gsi, trigger, polarity); | ||
368 | printk(KERN_INFO "xen: acpi sci %d\n", gsi); | ||
369 | |||
370 | return; | ||
371 | } | ||
372 | |||
373 | static int acpi_register_gsi_xen(struct device *dev, u32 gsi, | ||
374 | int trigger, int polarity) | ||
375 | { | ||
376 | return xen_register_gsi(gsi, trigger, polarity); | ||
377 | } | ||
378 | |||
379 | static int __init pci_xen_initial_domain(void) | ||
380 | { | ||
381 | #ifdef CONFIG_PCI_MSI | ||
382 | x86_msi.setup_msi_irqs = xen_initdom_setup_msi_irqs; | ||
383 | x86_msi.teardown_msi_irq = xen_teardown_msi_irq; | ||
384 | #endif | ||
385 | xen_setup_acpi_sci(); | ||
386 | __acpi_register_gsi = acpi_register_gsi_xen; | ||
387 | |||
388 | return 0; | ||
389 | } | ||
390 | |||
391 | void __init xen_setup_pirqs(void) | ||
392 | { | ||
393 | int irq; | ||
394 | |||
395 | pci_xen_initial_domain(); | ||
396 | |||
397 | if (0 == nr_ioapics) { | ||
398 | for (irq = 0; irq < NR_IRQS_LEGACY; irq++) | ||
399 | xen_allocate_pirq(irq, 0, "xt-pic"); | ||
400 | return; | ||
401 | } | ||
402 | |||
403 | /* Pre-allocate legacy irqs */ | ||
404 | for (irq = 0; irq < NR_IRQS_LEGACY; irq++) { | ||
405 | int trigger, polarity; | ||
406 | |||
407 | if (acpi_get_override_irq(irq, &trigger, &polarity) == -1) | ||
408 | continue; | ||
409 | |||
410 | xen_register_pirq(irq, | ||
411 | trigger ? ACPI_LEVEL_SENSITIVE : ACPI_EDGE_SENSITIVE); | ||
412 | } | ||
413 | } | ||
414 | #endif | ||