Commit | Line | Data |
---|---|---|
30edc14b KRW |
1 | /* |
2 | * PCI Backend Xenbus Setup - handles setup with frontend and xend | |
3 | * | |
4 | * Author: Ryan Wilson <hap9@epoch.ncsc.mil> | |
5 | */ | |
6 | #include <linux/module.h> | |
7 | #include <linux/init.h> | |
8 | #include <linux/list.h> | |
9 | #include <linux/vmalloc.h> | |
10 | #include <linux/workqueue.h> | |
11 | #include <xen/xenbus.h> | |
12 | #include <xen/events.h> | |
6221a9b2 | 13 | #include <asm/xen/pci.h> |
30edc14b KRW |
14 | #include <linux/workqueue.h> |
15 | #include "pciback.h" | |
16 | ||
a92336a1 | 17 | #define DRV_NAME "xen-pciback" |
30edc14b | 18 | #define INVALID_EVTCHN_IRQ (-1) |
a92336a1 | 19 | struct workqueue_struct *xen_pcibk_wq; |
30edc14b | 20 | |
a92336a1 | 21 | static struct xen_pcibk_device *alloc_pdev(struct xenbus_device *xdev) |
30edc14b | 22 | { |
a92336a1 | 23 | struct xen_pcibk_device *pdev; |
30edc14b | 24 | |
a92336a1 | 25 | pdev = kzalloc(sizeof(struct xen_pcibk_device), GFP_KERNEL); |
30edc14b KRW |
26 | if (pdev == NULL) |
27 | goto out; | |
28 | dev_dbg(&xdev->dev, "allocated pdev @ 0x%p\n", pdev); | |
29 | ||
30 | pdev->xdev = xdev; | |
31 | dev_set_drvdata(&xdev->dev, pdev); | |
32 | ||
33 | spin_lock_init(&pdev->dev_lock); | |
34 | ||
35 | pdev->sh_info = NULL; | |
36 | pdev->evtchn_irq = INVALID_EVTCHN_IRQ; | |
37 | pdev->be_watching = 0; | |
38 | ||
a92336a1 | 39 | INIT_WORK(&pdev->op_work, xen_pcibk_do_op); |
30edc14b | 40 | |
a92336a1 | 41 | if (xen_pcibk_init_devices(pdev)) { |
30edc14b KRW |
42 | kfree(pdev); |
43 | pdev = NULL; | |
44 | } | |
45 | out: | |
46 | return pdev; | |
47 | } | |
48 | ||
a92336a1 | 49 | static void xen_pcibk_disconnect(struct xen_pcibk_device *pdev) |
30edc14b KRW |
50 | { |
51 | spin_lock(&pdev->dev_lock); | |
52 | ||
53 | /* Ensure the guest can't trigger our handler before removing devices */ | |
54 | if (pdev->evtchn_irq != INVALID_EVTCHN_IRQ) { | |
55 | unbind_from_irqhandler(pdev->evtchn_irq, pdev); | |
56 | pdev->evtchn_irq = INVALID_EVTCHN_IRQ; | |
57 | } | |
494ef20d | 58 | spin_unlock(&pdev->dev_lock); |
30edc14b KRW |
59 | |
60 | /* If the driver domain started an op, make sure we complete it | |
61 | * before releasing the shared memory */ | |
494ef20d KRW |
62 | |
63 | /* Note, the workqueue does not use spinlocks at all.*/ | |
a92336a1 | 64 | flush_workqueue(xen_pcibk_wq); |
30edc14b | 65 | |
494ef20d | 66 | spin_lock(&pdev->dev_lock); |
30edc14b KRW |
67 | if (pdev->sh_info != NULL) { |
68 | xenbus_unmap_ring_vfree(pdev->xdev, pdev->sh_info); | |
69 | pdev->sh_info = NULL; | |
70 | } | |
30edc14b | 71 | spin_unlock(&pdev->dev_lock); |
494ef20d | 72 | |
30edc14b KRW |
73 | } |
74 | ||
a92336a1 | 75 | static void free_pdev(struct xen_pcibk_device *pdev) |
30edc14b | 76 | { |
494ef20d | 77 | if (pdev->be_watching) { |
30edc14b | 78 | unregister_xenbus_watch(&pdev->be_watch); |
494ef20d KRW |
79 | pdev->be_watching = 0; |
80 | } | |
30edc14b | 81 | |
a92336a1 | 82 | xen_pcibk_disconnect(pdev); |
30edc14b | 83 | |
a92336a1 | 84 | xen_pcibk_release_devices(pdev); |
30edc14b KRW |
85 | |
86 | dev_set_drvdata(&pdev->xdev->dev, NULL); | |
87 | pdev->xdev = NULL; | |
88 | ||
89 | kfree(pdev); | |
90 | } | |
91 | ||
a92336a1 | 92 | static int xen_pcibk_do_attach(struct xen_pcibk_device *pdev, int gnt_ref, |
30edc14b KRW |
93 | int remote_evtchn) |
94 | { | |
95 | int err = 0; | |
96 | void *vaddr; | |
97 | ||
98 | dev_dbg(&pdev->xdev->dev, | |
99 | "Attaching to frontend resources - gnt_ref=%d evtchn=%d\n", | |
100 | gnt_ref, remote_evtchn); | |
101 | ||
102 | err = xenbus_map_ring_valloc(pdev->xdev, gnt_ref, &vaddr); | |
103 | if (err < 0) { | |
104 | xenbus_dev_fatal(pdev->xdev, err, | |
105 | "Error mapping other domain page in ours."); | |
106 | goto out; | |
107 | } | |
494ef20d KRW |
108 | |
109 | spin_lock(&pdev->dev_lock); | |
30edc14b | 110 | pdev->sh_info = vaddr; |
494ef20d | 111 | spin_unlock(&pdev->dev_lock); |
30edc14b KRW |
112 | |
113 | err = bind_interdomain_evtchn_to_irqhandler( | |
a92336a1 KRW |
114 | pdev->xdev->otherend_id, remote_evtchn, xen_pcibk_handle_event, |
115 | 0, DRV_NAME, pdev); | |
30edc14b KRW |
116 | if (err < 0) { |
117 | xenbus_dev_fatal(pdev->xdev, err, | |
118 | "Error binding event channel to IRQ"); | |
119 | goto out; | |
120 | } | |
494ef20d KRW |
121 | |
122 | spin_lock(&pdev->dev_lock); | |
30edc14b | 123 | pdev->evtchn_irq = err; |
494ef20d | 124 | spin_unlock(&pdev->dev_lock); |
30edc14b KRW |
125 | err = 0; |
126 | ||
127 | dev_dbg(&pdev->xdev->dev, "Attached!\n"); | |
128 | out: | |
129 | return err; | |
130 | } | |
131 | ||
a92336a1 | 132 | static int xen_pcibk_attach(struct xen_pcibk_device *pdev) |
30edc14b KRW |
133 | { |
134 | int err = 0; | |
135 | int gnt_ref, remote_evtchn; | |
136 | char *magic = NULL; | |
137 | ||
30edc14b KRW |
138 | |
139 | /* Make sure we only do this setup once */ | |
140 | if (xenbus_read_driver_state(pdev->xdev->nodename) != | |
141 | XenbusStateInitialised) | |
142 | goto out; | |
143 | ||
144 | /* Wait for frontend to state that it has published the configuration */ | |
145 | if (xenbus_read_driver_state(pdev->xdev->otherend) != | |
146 | XenbusStateInitialised) | |
147 | goto out; | |
148 | ||
149 | dev_dbg(&pdev->xdev->dev, "Reading frontend config\n"); | |
150 | ||
151 | err = xenbus_gather(XBT_NIL, pdev->xdev->otherend, | |
152 | "pci-op-ref", "%u", &gnt_ref, | |
153 | "event-channel", "%u", &remote_evtchn, | |
154 | "magic", NULL, &magic, NULL); | |
155 | if (err) { | |
156 | /* If configuration didn't get read correctly, wait longer */ | |
157 | xenbus_dev_fatal(pdev->xdev, err, | |
158 | "Error reading configuration from frontend"); | |
159 | goto out; | |
160 | } | |
161 | ||
162 | if (magic == NULL || strcmp(magic, XEN_PCI_MAGIC) != 0) { | |
163 | xenbus_dev_fatal(pdev->xdev, -EFAULT, | |
164 | "version mismatch (%s/%s) with pcifront - " | |
a92336a1 | 165 | "halting xen_pcibk", |
30edc14b KRW |
166 | magic, XEN_PCI_MAGIC); |
167 | goto out; | |
168 | } | |
169 | ||
a92336a1 | 170 | err = xen_pcibk_do_attach(pdev, gnt_ref, remote_evtchn); |
30edc14b KRW |
171 | if (err) |
172 | goto out; | |
173 | ||
174 | dev_dbg(&pdev->xdev->dev, "Connecting...\n"); | |
175 | ||
176 | err = xenbus_switch_state(pdev->xdev, XenbusStateConnected); | |
177 | if (err) | |
178 | xenbus_dev_fatal(pdev->xdev, err, | |
179 | "Error switching to connected state!"); | |
180 | ||
181 | dev_dbg(&pdev->xdev->dev, "Connected? %d\n", err); | |
182 | out: | |
30edc14b KRW |
183 | |
184 | kfree(magic); | |
185 | ||
186 | return err; | |
187 | } | |
188 | ||
a92336a1 | 189 | static int xen_pcibk_publish_pci_dev(struct xen_pcibk_device *pdev, |
30edc14b KRW |
190 | unsigned int domain, unsigned int bus, |
191 | unsigned int devfn, unsigned int devid) | |
192 | { | |
193 | int err; | |
194 | int len; | |
195 | char str[64]; | |
196 | ||
197 | len = snprintf(str, sizeof(str), "vdev-%d", devid); | |
198 | if (unlikely(len >= (sizeof(str) - 1))) { | |
199 | err = -ENOMEM; | |
200 | goto out; | |
201 | } | |
202 | ||
203 | err = xenbus_printf(XBT_NIL, pdev->xdev->nodename, str, | |
204 | "%04x:%02x:%02x.%02x", domain, bus, | |
205 | PCI_SLOT(devfn), PCI_FUNC(devfn)); | |
206 | ||
207 | out: | |
208 | return err; | |
209 | } | |
210 | ||
a92336a1 | 211 | static int xen_pcibk_export_device(struct xen_pcibk_device *pdev, |
30edc14b KRW |
212 | int domain, int bus, int slot, int func, |
213 | int devid) | |
214 | { | |
215 | struct pci_dev *dev; | |
216 | int err = 0; | |
217 | ||
218 | dev_dbg(&pdev->xdev->dev, "exporting dom %x bus %x slot %x func %x\n", | |
219 | domain, bus, slot, func); | |
220 | ||
221 | dev = pcistub_get_pci_dev_by_slot(pdev, domain, bus, slot, func); | |
222 | if (!dev) { | |
223 | err = -EINVAL; | |
224 | xenbus_dev_fatal(pdev->xdev, err, | |
225 | "Couldn't locate PCI device " | |
226 | "(%04x:%02x:%02x.%01x)! " | |
227 | "perhaps already in-use?", | |
228 | domain, bus, slot, func); | |
229 | goto out; | |
230 | } | |
231 | ||
a92336a1 KRW |
232 | err = xen_pcibk_add_pci_dev(pdev, dev, devid, |
233 | xen_pcibk_publish_pci_dev); | |
30edc14b KRW |
234 | if (err) |
235 | goto out; | |
236 | ||
6221a9b2 KRW |
237 | dev_dbg(&dev->dev, "registering for %d\n", pdev->xdev->otherend_id); |
238 | if (xen_register_device_domain_owner(dev, | |
239 | pdev->xdev->otherend_id) != 0) { | |
240 | dev_err(&dev->dev, "device has been assigned to another " \ | |
241 | "domain! Over-writting the ownership, but beware.\n"); | |
242 | xen_unregister_device_domain_owner(dev); | |
243 | xen_register_device_domain_owner(dev, pdev->xdev->otherend_id); | |
244 | } | |
245 | ||
30edc14b KRW |
246 | /* TODO: It'd be nice to export a bridge and have all of its children |
247 | * get exported with it. This may be best done in xend (which will | |
248 | * have to calculate resource usage anyway) but we probably want to | |
249 | * put something in here to ensure that if a bridge gets given to a | |
250 | * driver domain, that all devices under that bridge are not given | |
251 | * to other driver domains (as he who controls the bridge can disable | |
252 | * it and stop the other devices from working). | |
253 | */ | |
254 | out: | |
255 | return err; | |
256 | } | |
257 | ||
a92336a1 | 258 | static int xen_pcibk_remove_device(struct xen_pcibk_device *pdev, |
30edc14b KRW |
259 | int domain, int bus, int slot, int func) |
260 | { | |
261 | int err = 0; | |
262 | struct pci_dev *dev; | |
263 | ||
264 | dev_dbg(&pdev->xdev->dev, "removing dom %x bus %x slot %x func %x\n", | |
265 | domain, bus, slot, func); | |
266 | ||
a92336a1 | 267 | dev = xen_pcibk_get_pci_dev(pdev, domain, bus, PCI_DEVFN(slot, func)); |
30edc14b KRW |
268 | if (!dev) { |
269 | err = -EINVAL; | |
270 | dev_dbg(&pdev->xdev->dev, "Couldn't locate PCI device " | |
271 | "(%04x:%02x:%02x.%01x)! not owned by this domain\n", | |
272 | domain, bus, slot, func); | |
273 | goto out; | |
274 | } | |
275 | ||
6221a9b2 KRW |
276 | dev_dbg(&dev->dev, "unregistering for %d\n", pdev->xdev->otherend_id); |
277 | xen_unregister_device_domain_owner(dev); | |
278 | ||
a92336a1 | 279 | xen_pcibk_release_pci_dev(pdev, dev); |
30edc14b KRW |
280 | |
281 | out: | |
282 | return err; | |
283 | } | |
284 | ||
a92336a1 | 285 | static int xen_pcibk_publish_pci_root(struct xen_pcibk_device *pdev, |
30edc14b KRW |
286 | unsigned int domain, unsigned int bus) |
287 | { | |
288 | unsigned int d, b; | |
289 | int i, root_num, len, err; | |
290 | char str[64]; | |
291 | ||
292 | dev_dbg(&pdev->xdev->dev, "Publishing pci roots\n"); | |
293 | ||
294 | err = xenbus_scanf(XBT_NIL, pdev->xdev->nodename, | |
295 | "root_num", "%d", &root_num); | |
296 | if (err == 0 || err == -ENOENT) | |
297 | root_num = 0; | |
298 | else if (err < 0) | |
299 | goto out; | |
300 | ||
301 | /* Verify that we haven't already published this pci root */ | |
302 | for (i = 0; i < root_num; i++) { | |
303 | len = snprintf(str, sizeof(str), "root-%d", i); | |
304 | if (unlikely(len >= (sizeof(str) - 1))) { | |
305 | err = -ENOMEM; | |
306 | goto out; | |
307 | } | |
308 | ||
309 | err = xenbus_scanf(XBT_NIL, pdev->xdev->nodename, | |
310 | str, "%x:%x", &d, &b); | |
311 | if (err < 0) | |
312 | goto out; | |
313 | if (err != 2) { | |
314 | err = -EINVAL; | |
315 | goto out; | |
316 | } | |
317 | ||
318 | if (d == domain && b == bus) { | |
319 | err = 0; | |
320 | goto out; | |
321 | } | |
322 | } | |
323 | ||
324 | len = snprintf(str, sizeof(str), "root-%d", root_num); | |
325 | if (unlikely(len >= (sizeof(str) - 1))) { | |
326 | err = -ENOMEM; | |
327 | goto out; | |
328 | } | |
329 | ||
330 | dev_dbg(&pdev->xdev->dev, "writing root %d at %04x:%02x\n", | |
331 | root_num, domain, bus); | |
332 | ||
333 | err = xenbus_printf(XBT_NIL, pdev->xdev->nodename, str, | |
334 | "%04x:%02x", domain, bus); | |
335 | if (err) | |
336 | goto out; | |
337 | ||
338 | err = xenbus_printf(XBT_NIL, pdev->xdev->nodename, | |
339 | "root_num", "%d", (root_num + 1)); | |
340 | ||
341 | out: | |
342 | return err; | |
343 | } | |
344 | ||
a92336a1 | 345 | static int xen_pcibk_reconfigure(struct xen_pcibk_device *pdev) |
30edc14b KRW |
346 | { |
347 | int err = 0; | |
348 | int num_devs; | |
349 | int domain, bus, slot, func; | |
350 | int substate; | |
351 | int i, len; | |
352 | char state_str[64]; | |
353 | char dev_str[64]; | |
354 | ||
30edc14b KRW |
355 | |
356 | dev_dbg(&pdev->xdev->dev, "Reconfiguring device ...\n"); | |
357 | ||
358 | /* Make sure we only reconfigure once */ | |
359 | if (xenbus_read_driver_state(pdev->xdev->nodename) != | |
360 | XenbusStateReconfiguring) | |
361 | goto out; | |
362 | ||
363 | err = xenbus_scanf(XBT_NIL, pdev->xdev->nodename, "num_devs", "%d", | |
364 | &num_devs); | |
365 | if (err != 1) { | |
366 | if (err >= 0) | |
367 | err = -EINVAL; | |
368 | xenbus_dev_fatal(pdev->xdev, err, | |
369 | "Error reading number of devices"); | |
370 | goto out; | |
371 | } | |
372 | ||
373 | for (i = 0; i < num_devs; i++) { | |
374 | len = snprintf(state_str, sizeof(state_str), "state-%d", i); | |
375 | if (unlikely(len >= (sizeof(state_str) - 1))) { | |
376 | err = -ENOMEM; | |
377 | xenbus_dev_fatal(pdev->xdev, err, | |
378 | "String overflow while reading " | |
379 | "configuration"); | |
380 | goto out; | |
381 | } | |
382 | err = xenbus_scanf(XBT_NIL, pdev->xdev->nodename, state_str, | |
383 | "%d", &substate); | |
384 | if (err != 1) | |
385 | substate = XenbusStateUnknown; | |
386 | ||
387 | switch (substate) { | |
388 | case XenbusStateInitialising: | |
389 | dev_dbg(&pdev->xdev->dev, "Attaching dev-%d ...\n", i); | |
390 | ||
391 | len = snprintf(dev_str, sizeof(dev_str), "dev-%d", i); | |
392 | if (unlikely(len >= (sizeof(dev_str) - 1))) { | |
393 | err = -ENOMEM; | |
394 | xenbus_dev_fatal(pdev->xdev, err, | |
395 | "String overflow while " | |
396 | "reading configuration"); | |
397 | goto out; | |
398 | } | |
399 | err = xenbus_scanf(XBT_NIL, pdev->xdev->nodename, | |
400 | dev_str, "%x:%x:%x.%x", | |
401 | &domain, &bus, &slot, &func); | |
402 | if (err < 0) { | |
403 | xenbus_dev_fatal(pdev->xdev, err, | |
404 | "Error reading device " | |
405 | "configuration"); | |
406 | goto out; | |
407 | } | |
408 | if (err != 4) { | |
409 | err = -EINVAL; | |
410 | xenbus_dev_fatal(pdev->xdev, err, | |
411 | "Error parsing pci device " | |
412 | "configuration"); | |
413 | goto out; | |
414 | } | |
415 | ||
a92336a1 | 416 | err = xen_pcibk_export_device(pdev, domain, bus, slot, |
30edc14b KRW |
417 | func, i); |
418 | if (err) | |
419 | goto out; | |
420 | ||
421 | /* Publish pci roots. */ | |
a92336a1 KRW |
422 | err = xen_pcibk_publish_pci_roots(pdev, |
423 | xen_pcibk_publish_pci_root); | |
30edc14b KRW |
424 | if (err) { |
425 | xenbus_dev_fatal(pdev->xdev, err, | |
426 | "Error while publish PCI root" | |
427 | "buses for frontend"); | |
428 | goto out; | |
429 | } | |
430 | ||
431 | err = xenbus_printf(XBT_NIL, pdev->xdev->nodename, | |
432 | state_str, "%d", | |
433 | XenbusStateInitialised); | |
434 | if (err) { | |
435 | xenbus_dev_fatal(pdev->xdev, err, | |
436 | "Error switching substate of " | |
437 | "dev-%d\n", i); | |
438 | goto out; | |
439 | } | |
440 | break; | |
441 | ||
442 | case XenbusStateClosing: | |
443 | dev_dbg(&pdev->xdev->dev, "Detaching dev-%d ...\n", i); | |
444 | ||
445 | len = snprintf(dev_str, sizeof(dev_str), "vdev-%d", i); | |
446 | if (unlikely(len >= (sizeof(dev_str) - 1))) { | |
447 | err = -ENOMEM; | |
448 | xenbus_dev_fatal(pdev->xdev, err, | |
449 | "String overflow while " | |
450 | "reading configuration"); | |
451 | goto out; | |
452 | } | |
453 | err = xenbus_scanf(XBT_NIL, pdev->xdev->nodename, | |
454 | dev_str, "%x:%x:%x.%x", | |
455 | &domain, &bus, &slot, &func); | |
456 | if (err < 0) { | |
457 | xenbus_dev_fatal(pdev->xdev, err, | |
458 | "Error reading device " | |
459 | "configuration"); | |
460 | goto out; | |
461 | } | |
462 | if (err != 4) { | |
463 | err = -EINVAL; | |
464 | xenbus_dev_fatal(pdev->xdev, err, | |
465 | "Error parsing pci device " | |
466 | "configuration"); | |
467 | goto out; | |
468 | } | |
469 | ||
a92336a1 | 470 | err = xen_pcibk_remove_device(pdev, domain, bus, slot, |
30edc14b KRW |
471 | func); |
472 | if (err) | |
473 | goto out; | |
474 | ||
475 | /* TODO: If at some point we implement support for pci | |
476 | * root hot-remove on pcifront side, we'll need to | |
477 | * remove unnecessary xenstore nodes of pci roots here. | |
478 | */ | |
479 | ||
480 | break; | |
481 | ||
482 | default: | |
483 | break; | |
484 | } | |
485 | } | |
486 | ||
487 | err = xenbus_switch_state(pdev->xdev, XenbusStateReconfigured); | |
488 | if (err) { | |
489 | xenbus_dev_fatal(pdev->xdev, err, | |
490 | "Error switching to reconfigured state!"); | |
491 | goto out; | |
492 | } | |
493 | ||
494 | out: | |
30edc14b KRW |
495 | return 0; |
496 | } | |
497 | ||
a92336a1 | 498 | static void xen_pcibk_frontend_changed(struct xenbus_device *xdev, |
30edc14b KRW |
499 | enum xenbus_state fe_state) |
500 | { | |
a92336a1 | 501 | struct xen_pcibk_device *pdev = dev_get_drvdata(&xdev->dev); |
30edc14b KRW |
502 | |
503 | dev_dbg(&xdev->dev, "fe state changed %d\n", fe_state); | |
504 | ||
505 | switch (fe_state) { | |
506 | case XenbusStateInitialised: | |
a92336a1 | 507 | xen_pcibk_attach(pdev); |
30edc14b KRW |
508 | break; |
509 | ||
510 | case XenbusStateReconfiguring: | |
a92336a1 | 511 | xen_pcibk_reconfigure(pdev); |
30edc14b KRW |
512 | break; |
513 | ||
514 | case XenbusStateConnected: | |
515 | /* pcifront switched its state from reconfiguring to connected. | |
516 | * Then switch to connected state. | |
517 | */ | |
518 | xenbus_switch_state(xdev, XenbusStateConnected); | |
519 | break; | |
520 | ||
521 | case XenbusStateClosing: | |
a92336a1 | 522 | xen_pcibk_disconnect(pdev); |
30edc14b KRW |
523 | xenbus_switch_state(xdev, XenbusStateClosing); |
524 | break; | |
525 | ||
526 | case XenbusStateClosed: | |
a92336a1 | 527 | xen_pcibk_disconnect(pdev); |
30edc14b KRW |
528 | xenbus_switch_state(xdev, XenbusStateClosed); |
529 | if (xenbus_dev_is_online(xdev)) | |
530 | break; | |
531 | /* fall through if not online */ | |
532 | case XenbusStateUnknown: | |
533 | dev_dbg(&xdev->dev, "frontend is gone! unregister device\n"); | |
534 | device_unregister(&xdev->dev); | |
535 | break; | |
536 | ||
537 | default: | |
538 | break; | |
539 | } | |
540 | } | |
541 | ||
a92336a1 | 542 | static int xen_pcibk_setup_backend(struct xen_pcibk_device *pdev) |
30edc14b KRW |
543 | { |
544 | /* Get configuration from xend (if available now) */ | |
545 | int domain, bus, slot, func; | |
546 | int err = 0; | |
547 | int i, num_devs; | |
548 | char dev_str[64]; | |
549 | char state_str[64]; | |
550 | ||
30edc14b KRW |
551 | /* It's possible we could get the call to setup twice, so make sure |
552 | * we're not already connected. | |
553 | */ | |
554 | if (xenbus_read_driver_state(pdev->xdev->nodename) != | |
555 | XenbusStateInitWait) | |
556 | goto out; | |
557 | ||
558 | dev_dbg(&pdev->xdev->dev, "getting be setup\n"); | |
559 | ||
560 | err = xenbus_scanf(XBT_NIL, pdev->xdev->nodename, "num_devs", "%d", | |
561 | &num_devs); | |
562 | if (err != 1) { | |
563 | if (err >= 0) | |
564 | err = -EINVAL; | |
565 | xenbus_dev_fatal(pdev->xdev, err, | |
566 | "Error reading number of devices"); | |
567 | goto out; | |
568 | } | |
569 | ||
570 | for (i = 0; i < num_devs; i++) { | |
571 | int l = snprintf(dev_str, sizeof(dev_str), "dev-%d", i); | |
572 | if (unlikely(l >= (sizeof(dev_str) - 1))) { | |
573 | err = -ENOMEM; | |
574 | xenbus_dev_fatal(pdev->xdev, err, | |
575 | "String overflow while reading " | |
576 | "configuration"); | |
577 | goto out; | |
578 | } | |
579 | ||
580 | err = xenbus_scanf(XBT_NIL, pdev->xdev->nodename, dev_str, | |
581 | "%x:%x:%x.%x", &domain, &bus, &slot, &func); | |
582 | if (err < 0) { | |
583 | xenbus_dev_fatal(pdev->xdev, err, | |
584 | "Error reading device configuration"); | |
585 | goto out; | |
586 | } | |
587 | if (err != 4) { | |
588 | err = -EINVAL; | |
589 | xenbus_dev_fatal(pdev->xdev, err, | |
590 | "Error parsing pci device " | |
591 | "configuration"); | |
592 | goto out; | |
593 | } | |
594 | ||
a92336a1 | 595 | err = xen_pcibk_export_device(pdev, domain, bus, slot, func, i); |
30edc14b KRW |
596 | if (err) |
597 | goto out; | |
598 | ||
599 | /* Switch substate of this device. */ | |
600 | l = snprintf(state_str, sizeof(state_str), "state-%d", i); | |
601 | if (unlikely(l >= (sizeof(state_str) - 1))) { | |
602 | err = -ENOMEM; | |
603 | xenbus_dev_fatal(pdev->xdev, err, | |
604 | "String overflow while reading " | |
605 | "configuration"); | |
606 | goto out; | |
607 | } | |
608 | err = xenbus_printf(XBT_NIL, pdev->xdev->nodename, state_str, | |
609 | "%d", XenbusStateInitialised); | |
610 | if (err) { | |
611 | xenbus_dev_fatal(pdev->xdev, err, "Error switching " | |
612 | "substate of dev-%d\n", i); | |
613 | goto out; | |
614 | } | |
615 | } | |
616 | ||
a92336a1 | 617 | err = xen_pcibk_publish_pci_roots(pdev, xen_pcibk_publish_pci_root); |
30edc14b KRW |
618 | if (err) { |
619 | xenbus_dev_fatal(pdev->xdev, err, | |
620 | "Error while publish PCI root buses " | |
621 | "for frontend"); | |
622 | goto out; | |
623 | } | |
624 | ||
625 | err = xenbus_switch_state(pdev->xdev, XenbusStateInitialised); | |
626 | if (err) | |
627 | xenbus_dev_fatal(pdev->xdev, err, | |
628 | "Error switching to initialised state!"); | |
629 | ||
630 | out: | |
30edc14b KRW |
631 | if (!err) |
632 | /* see if pcifront is already configured (if not, we'll wait) */ | |
a92336a1 | 633 | xen_pcibk_attach(pdev); |
30edc14b KRW |
634 | |
635 | return err; | |
636 | } | |
637 | ||
a92336a1 | 638 | static void xen_pcibk_be_watch(struct xenbus_watch *watch, |
30edc14b KRW |
639 | const char **vec, unsigned int len) |
640 | { | |
a92336a1 KRW |
641 | struct xen_pcibk_device *pdev = |
642 | container_of(watch, struct xen_pcibk_device, be_watch); | |
30edc14b KRW |
643 | |
644 | switch (xenbus_read_driver_state(pdev->xdev->nodename)) { | |
645 | case XenbusStateInitWait: | |
a92336a1 | 646 | xen_pcibk_setup_backend(pdev); |
30edc14b KRW |
647 | break; |
648 | ||
649 | default: | |
650 | break; | |
651 | } | |
652 | } | |
653 | ||
a92336a1 | 654 | static int xen_pcibk_xenbus_probe(struct xenbus_device *dev, |
30edc14b KRW |
655 | const struct xenbus_device_id *id) |
656 | { | |
657 | int err = 0; | |
a92336a1 | 658 | struct xen_pcibk_device *pdev = alloc_pdev(dev); |
30edc14b KRW |
659 | |
660 | if (pdev == NULL) { | |
661 | err = -ENOMEM; | |
662 | xenbus_dev_fatal(dev, err, | |
a92336a1 | 663 | "Error allocating xen_pcibk_device struct"); |
30edc14b KRW |
664 | goto out; |
665 | } | |
666 | ||
667 | /* wait for xend to configure us */ | |
668 | err = xenbus_switch_state(dev, XenbusStateInitWait); | |
669 | if (err) | |
670 | goto out; | |
671 | ||
672 | /* watch the backend node for backend configuration information */ | |
673 | err = xenbus_watch_path(dev, dev->nodename, &pdev->be_watch, | |
a92336a1 | 674 | xen_pcibk_be_watch); |
30edc14b KRW |
675 | if (err) |
676 | goto out; | |
494ef20d | 677 | |
30edc14b KRW |
678 | pdev->be_watching = 1; |
679 | ||
680 | /* We need to force a call to our callback here in case | |
681 | * xend already configured us! | |
682 | */ | |
a92336a1 | 683 | xen_pcibk_be_watch(&pdev->be_watch, NULL, 0); |
30edc14b KRW |
684 | |
685 | out: | |
686 | return err; | |
687 | } | |
688 | ||
a92336a1 | 689 | static int xen_pcibk_xenbus_remove(struct xenbus_device *dev) |
30edc14b | 690 | { |
a92336a1 | 691 | struct xen_pcibk_device *pdev = dev_get_drvdata(&dev->dev); |
30edc14b KRW |
692 | |
693 | if (pdev != NULL) | |
694 | free_pdev(pdev); | |
695 | ||
696 | return 0; | |
697 | } | |
698 | ||
699 | static const struct xenbus_device_id xenpci_ids[] = { | |
700 | {"pci"}, | |
701 | {""}, | |
702 | }; | |
703 | ||
a92336a1 KRW |
704 | static struct xenbus_driver xenbus_xen_pcibk_driver = { |
705 | .name = DRV_NAME, | |
8bfd4e02 KRW |
706 | .owner = THIS_MODULE, |
707 | .ids = xenpci_ids, | |
a92336a1 KRW |
708 | .probe = xen_pcibk_xenbus_probe, |
709 | .remove = xen_pcibk_xenbus_remove, | |
710 | .otherend_changed = xen_pcibk_frontend_changed, | |
30edc14b KRW |
711 | }; |
712 | ||
a92336a1 | 713 | int __init xen_pcibk_xenbus_register(void) |
30edc14b | 714 | { |
a92336a1 KRW |
715 | xen_pcibk_wq = create_workqueue("xen_pciback_workqueue"); |
716 | if (!xen_pcibk_wq) { | |
8bfd4e02 | 717 | printk(KERN_ERR "%s: create" |
a92336a1 | 718 | "xen_pciback_workqueue failed\n", __func__); |
30edc14b KRW |
719 | return -EFAULT; |
720 | } | |
a92336a1 | 721 | return xenbus_register_backend(&xenbus_xen_pcibk_driver); |
30edc14b KRW |
722 | } |
723 | ||
a92336a1 | 724 | void __exit xen_pcibk_xenbus_unregister(void) |
30edc14b | 725 | { |
a92336a1 KRW |
726 | destroy_workqueue(xen_pcibk_wq); |
727 | xenbus_unregister_driver(&xenbus_xen_pcibk_driver); | |
30edc14b | 728 | } |