[34] | 1 | /* |
---|
| 2 | * PCI Frontend Xenbus Setup - handles setup with backend (imports page/evtchn) |
---|
| 3 | * |
---|
| 4 | * Author: Ryan Wilson <hap9@epoch.ncsc.mil> |
---|
| 5 | */ |
---|
| 6 | #include <linux/module.h> |
---|
| 7 | #include <linux/init.h> |
---|
| 8 | #include <linux/mm.h> |
---|
| 9 | #include <xen/xenbus.h> |
---|
| 10 | #include <xen/gnttab.h> |
---|
| 11 | #include "pcifront.h" |
---|
| 12 | |
---|
| 13 | #define INVALID_GRANT_REF (0) |
---|
| 14 | #define INVALID_EVTCHN (-1) |
---|
| 15 | |
---|
| 16 | static struct pcifront_device *alloc_pdev(struct xenbus_device *xdev) |
---|
| 17 | { |
---|
| 18 | struct pcifront_device *pdev; |
---|
| 19 | |
---|
| 20 | pdev = kmalloc(sizeof(struct pcifront_device), GFP_KERNEL); |
---|
| 21 | if (pdev == NULL) |
---|
| 22 | goto out; |
---|
| 23 | |
---|
| 24 | pdev->sh_info = |
---|
| 25 | (struct xen_pci_sharedinfo *)__get_free_page(GFP_KERNEL); |
---|
| 26 | if (pdev->sh_info == NULL) { |
---|
| 27 | kfree(pdev); |
---|
| 28 | pdev = NULL; |
---|
| 29 | goto out; |
---|
| 30 | } |
---|
| 31 | pdev->sh_info->flags = 0; |
---|
| 32 | |
---|
| 33 | xdev->dev.driver_data = pdev; |
---|
| 34 | pdev->xdev = xdev; |
---|
| 35 | |
---|
| 36 | INIT_LIST_HEAD(&pdev->root_buses); |
---|
| 37 | |
---|
| 38 | spin_lock_init(&pdev->dev_lock); |
---|
| 39 | spin_lock_init(&pdev->sh_info_lock); |
---|
| 40 | |
---|
| 41 | pdev->evtchn = INVALID_EVTCHN; |
---|
| 42 | pdev->gnt_ref = INVALID_GRANT_REF; |
---|
| 43 | |
---|
| 44 | dev_dbg(&xdev->dev, "Allocated pdev @ 0x%p pdev->sh_info @ 0x%p\n", |
---|
| 45 | pdev, pdev->sh_info); |
---|
| 46 | out: |
---|
| 47 | return pdev; |
---|
| 48 | } |
---|
| 49 | |
---|
| 50 | static void free_pdev(struct pcifront_device *pdev) |
---|
| 51 | { |
---|
| 52 | dev_dbg(&pdev->xdev->dev, "freeing pdev @ 0x%p\n", pdev); |
---|
| 53 | |
---|
| 54 | pcifront_free_roots(pdev); |
---|
| 55 | |
---|
| 56 | if (pdev->evtchn != INVALID_EVTCHN) |
---|
| 57 | xenbus_free_evtchn(pdev->xdev, pdev->evtchn); |
---|
| 58 | |
---|
| 59 | if (pdev->gnt_ref != INVALID_GRANT_REF) |
---|
| 60 | gnttab_end_foreign_access(pdev->gnt_ref, 0, |
---|
| 61 | (unsigned long)pdev->sh_info); |
---|
| 62 | |
---|
| 63 | pdev->xdev->dev.driver_data = NULL; |
---|
| 64 | |
---|
| 65 | kfree(pdev); |
---|
| 66 | } |
---|
| 67 | |
---|
| 68 | static int pcifront_publish_info(struct pcifront_device *pdev) |
---|
| 69 | { |
---|
| 70 | int err = 0; |
---|
| 71 | struct xenbus_transaction trans; |
---|
| 72 | |
---|
| 73 | err = xenbus_grant_ring(pdev->xdev, virt_to_mfn(pdev->sh_info)); |
---|
| 74 | if (err < 0) |
---|
| 75 | goto out; |
---|
| 76 | |
---|
| 77 | pdev->gnt_ref = err; |
---|
| 78 | |
---|
| 79 | err = xenbus_alloc_evtchn(pdev->xdev, &pdev->evtchn); |
---|
| 80 | if (err) |
---|
| 81 | goto out; |
---|
| 82 | |
---|
| 83 | do_publish: |
---|
| 84 | err = xenbus_transaction_start(&trans); |
---|
| 85 | if (err) { |
---|
| 86 | xenbus_dev_fatal(pdev->xdev, err, |
---|
| 87 | "Error writing configuration for backend " |
---|
| 88 | "(start transaction)"); |
---|
| 89 | goto out; |
---|
| 90 | } |
---|
| 91 | |
---|
| 92 | err = xenbus_printf(trans, pdev->xdev->nodename, |
---|
| 93 | "pci-op-ref", "%u", pdev->gnt_ref); |
---|
| 94 | if (!err) |
---|
| 95 | err = xenbus_printf(trans, pdev->xdev->nodename, |
---|
| 96 | "event-channel", "%u", pdev->evtchn); |
---|
| 97 | if (!err) |
---|
| 98 | err = xenbus_printf(trans, pdev->xdev->nodename, |
---|
| 99 | "magic", XEN_PCI_MAGIC); |
---|
| 100 | |
---|
| 101 | if (err) { |
---|
| 102 | xenbus_transaction_end(trans, 1); |
---|
| 103 | xenbus_dev_fatal(pdev->xdev, err, |
---|
| 104 | "Error writing configuration for backend"); |
---|
| 105 | goto out; |
---|
| 106 | } else { |
---|
| 107 | err = xenbus_transaction_end(trans, 0); |
---|
| 108 | if (err == -EAGAIN) |
---|
| 109 | goto do_publish; |
---|
| 110 | else if (err) { |
---|
| 111 | xenbus_dev_fatal(pdev->xdev, err, |
---|
| 112 | "Error completing transaction " |
---|
| 113 | "for backend"); |
---|
| 114 | goto out; |
---|
| 115 | } |
---|
| 116 | } |
---|
| 117 | |
---|
| 118 | xenbus_switch_state(pdev->xdev, XenbusStateInitialised); |
---|
| 119 | |
---|
| 120 | dev_dbg(&pdev->xdev->dev, "publishing successful!\n"); |
---|
| 121 | |
---|
| 122 | out: |
---|
| 123 | return err; |
---|
| 124 | } |
---|
| 125 | |
---|
| 126 | static int pcifront_try_connect(struct pcifront_device *pdev) |
---|
| 127 | { |
---|
| 128 | int err = -EFAULT; |
---|
| 129 | int i, num_roots, len; |
---|
| 130 | char str[64]; |
---|
| 131 | unsigned int domain, bus; |
---|
| 132 | |
---|
| 133 | spin_lock(&pdev->dev_lock); |
---|
| 134 | |
---|
| 135 | /* Only connect once */ |
---|
| 136 | if (xenbus_read_driver_state(pdev->xdev->nodename) != |
---|
| 137 | XenbusStateInitialised) |
---|
| 138 | goto out; |
---|
| 139 | |
---|
| 140 | err = pcifront_connect(pdev); |
---|
| 141 | if (err) { |
---|
| 142 | xenbus_dev_fatal(pdev->xdev, err, |
---|
| 143 | "Error connecting PCI Frontend"); |
---|
| 144 | goto out; |
---|
| 145 | } |
---|
| 146 | |
---|
| 147 | err = xenbus_scanf(XBT_NIL, pdev->xdev->otherend, |
---|
| 148 | "root_num", "%d", &num_roots); |
---|
| 149 | if (err == -ENOENT) { |
---|
| 150 | xenbus_dev_error(pdev->xdev, err, |
---|
| 151 | "No PCI Roots found, trying 0000:00"); |
---|
| 152 | err = pcifront_scan_root(pdev, 0, 0); |
---|
| 153 | num_roots = 0; |
---|
| 154 | } else if (err != 1) { |
---|
| 155 | if (err == 0) |
---|
| 156 | err = -EINVAL; |
---|
| 157 | xenbus_dev_fatal(pdev->xdev, err, |
---|
| 158 | "Error reading number of PCI roots"); |
---|
| 159 | goto out; |
---|
| 160 | } |
---|
| 161 | |
---|
| 162 | for (i = 0; i < num_roots; i++) { |
---|
| 163 | len = snprintf(str, sizeof(str), "root-%d", i); |
---|
| 164 | if (unlikely(len >= (sizeof(str) - 1))) { |
---|
| 165 | err = -ENOMEM; |
---|
| 166 | goto out; |
---|
| 167 | } |
---|
| 168 | |
---|
| 169 | err = xenbus_scanf(XBT_NIL, pdev->xdev->otherend, str, |
---|
| 170 | "%x:%x", &domain, &bus); |
---|
| 171 | if (err != 2) { |
---|
| 172 | if (err >= 0) |
---|
| 173 | err = -EINVAL; |
---|
| 174 | xenbus_dev_fatal(pdev->xdev, err, |
---|
| 175 | "Error reading PCI root %d", i); |
---|
| 176 | goto out; |
---|
| 177 | } |
---|
| 178 | |
---|
| 179 | err = pcifront_scan_root(pdev, domain, bus); |
---|
| 180 | if (err) { |
---|
| 181 | xenbus_dev_fatal(pdev->xdev, err, |
---|
| 182 | "Error scanning PCI root %04x:%02x", |
---|
| 183 | domain, bus); |
---|
| 184 | goto out; |
---|
| 185 | } |
---|
| 186 | } |
---|
| 187 | |
---|
| 188 | err = xenbus_switch_state(pdev->xdev, XenbusStateConnected); |
---|
| 189 | if (err) |
---|
| 190 | goto out; |
---|
| 191 | |
---|
| 192 | out: |
---|
| 193 | spin_unlock(&pdev->dev_lock); |
---|
| 194 | return err; |
---|
| 195 | } |
---|
| 196 | |
---|
| 197 | static int pcifront_try_disconnect(struct pcifront_device *pdev) |
---|
| 198 | { |
---|
| 199 | int err = 0; |
---|
| 200 | enum xenbus_state prev_state; |
---|
| 201 | |
---|
| 202 | spin_lock(&pdev->dev_lock); |
---|
| 203 | |
---|
| 204 | prev_state = xenbus_read_driver_state(pdev->xdev->nodename); |
---|
| 205 | |
---|
| 206 | if (prev_state < XenbusStateClosing) |
---|
| 207 | err = xenbus_switch_state(pdev->xdev, XenbusStateClosing); |
---|
| 208 | |
---|
| 209 | if (!err && prev_state == XenbusStateConnected) |
---|
| 210 | pcifront_disconnect(pdev); |
---|
| 211 | |
---|
| 212 | spin_unlock(&pdev->dev_lock); |
---|
| 213 | |
---|
| 214 | return err; |
---|
| 215 | } |
---|
| 216 | |
---|
| 217 | static void pcifront_backend_changed(struct xenbus_device *xdev, |
---|
| 218 | enum xenbus_state be_state) |
---|
| 219 | { |
---|
| 220 | struct pcifront_device *pdev = xdev->dev.driver_data; |
---|
| 221 | |
---|
| 222 | switch (be_state) { |
---|
| 223 | case XenbusStateClosing: |
---|
| 224 | dev_warn(&xdev->dev, "backend going away!\n"); |
---|
| 225 | pcifront_try_disconnect(pdev); |
---|
| 226 | break; |
---|
| 227 | |
---|
| 228 | case XenbusStateUnknown: |
---|
| 229 | case XenbusStateClosed: |
---|
| 230 | dev_warn(&xdev->dev, "backend went away!\n"); |
---|
| 231 | pcifront_try_disconnect(pdev); |
---|
| 232 | |
---|
| 233 | device_unregister(&pdev->xdev->dev); |
---|
| 234 | break; |
---|
| 235 | |
---|
| 236 | case XenbusStateConnected: |
---|
| 237 | pcifront_try_connect(pdev); |
---|
| 238 | break; |
---|
| 239 | |
---|
| 240 | default: |
---|
| 241 | break; |
---|
| 242 | } |
---|
| 243 | } |
---|
| 244 | |
---|
| 245 | static int pcifront_xenbus_probe(struct xenbus_device *xdev, |
---|
| 246 | const struct xenbus_device_id *id) |
---|
| 247 | { |
---|
| 248 | int err = 0; |
---|
| 249 | struct pcifront_device *pdev = alloc_pdev(xdev); |
---|
| 250 | |
---|
| 251 | if (pdev == NULL) { |
---|
| 252 | err = -ENOMEM; |
---|
| 253 | xenbus_dev_fatal(xdev, err, |
---|
| 254 | "Error allocating pcifront_device struct"); |
---|
| 255 | goto out; |
---|
| 256 | } |
---|
| 257 | |
---|
| 258 | err = pcifront_publish_info(pdev); |
---|
| 259 | |
---|
| 260 | out: |
---|
| 261 | return err; |
---|
| 262 | } |
---|
| 263 | |
---|
| 264 | static int pcifront_xenbus_remove(struct xenbus_device *xdev) |
---|
| 265 | { |
---|
| 266 | if (xdev->dev.driver_data) |
---|
| 267 | free_pdev(xdev->dev.driver_data); |
---|
| 268 | |
---|
| 269 | return 0; |
---|
| 270 | } |
---|
| 271 | |
---|
| 272 | static struct xenbus_device_id xenpci_ids[] = { |
---|
| 273 | {"pci"}, |
---|
| 274 | {{0}}, |
---|
| 275 | }; |
---|
| 276 | |
---|
| 277 | static struct xenbus_driver xenbus_pcifront_driver = { |
---|
| 278 | .name = "pcifront", |
---|
| 279 | .owner = THIS_MODULE, |
---|
| 280 | .ids = xenpci_ids, |
---|
| 281 | .probe = pcifront_xenbus_probe, |
---|
| 282 | .remove = pcifront_xenbus_remove, |
---|
| 283 | .otherend_changed = pcifront_backend_changed, |
---|
| 284 | }; |
---|
| 285 | |
---|
| 286 | static int __init pcifront_init(void) |
---|
| 287 | { |
---|
| 288 | if (!is_running_on_xen()) |
---|
| 289 | return -ENODEV; |
---|
| 290 | |
---|
| 291 | return xenbus_register_frontend(&xenbus_pcifront_driver); |
---|
| 292 | } |
---|
| 293 | |
---|
| 294 | /* Initialize after the Xen PCI Frontend Stub is initialized */ |
---|
| 295 | subsys_initcall(pcifront_init); |
---|