debuggers.hg

view tools/libxl/libxl_pci.c @ 21963:b92dfdc284dd

xl: prevent attempts to remove non-attached pci pass-through devices

Signed-off-by: Gianni Tedesco <gianni.tedesco@citrix.com>
Signed-off-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com>
author Gianni Tedesco <gianni.tedesco@citrix.com>
date Wed Aug 04 14:24:43 2010 +0100 (2010-08-04)
parents 3eec4c068649
children 9c720d64160b
line source
1 /*
2 * Copyright (C) 2009 Citrix Ltd.
3 * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
4 * Author Stefano Stabellini <stefano.stabellini@eu.citrix.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published
8 * by the Free Software Foundation; version 2.1 only. with the special
9 * exception on linking described in file LICENSE.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
15 */
17 #include "libxl_osdeps.h"
19 #include <stdio.h>
20 #include <string.h>
21 #include <stdlib.h>
22 #include <sys/types.h>
23 #include <fcntl.h>
24 #include <sys/select.h>
25 #include <sys/mman.h>
26 #include <sys/wait.h>
27 #include <signal.h>
28 #include <unistd.h> /* for write, unlink and close */
29 #include <stdint.h>
30 #include <inttypes.h>
31 #include <dirent.h>
32 #include <assert.h>
34 #include "libxl.h"
35 #include "libxl_utils.h"
36 #include "libxl_internal.h"
37 #include "flexarray.h"
39 #define PCI_BDF "%04x:%02x:%02x.%01x"
40 #define PCI_BDF_SHORT "%02x:%02x.%01x"
41 #define PCI_BDF_VDEVFN "%04x:%02x:%02x.%01x@%02x"
43 static int pcidev_init(libxl_device_pci *pcidev, unsigned int domain,
44 unsigned int bus, unsigned int dev,
45 unsigned int func, unsigned int vdevfn)
46 {
47 pcidev->domain = domain;
48 pcidev->bus = bus;
49 pcidev->dev = dev;
50 pcidev->func = func;
51 pcidev->vdevfn = vdevfn;
52 return 0;
53 }
55 int libxl_device_pci_parse_bdf(libxl_device_pci *pcidev, const char *str)
56 {
57 unsigned dom, bus, dev, func;
58 char *p, *buf2;
59 int rc;
61 if ( NULL == (buf2 = strdup(str)) )
62 return ERROR_NOMEM;
64 p = strtok(buf2, ",");
66 if ( sscanf(str, PCI_BDF, &dom, &bus, &dev, &func) != 4 ) {
67 dom = 0;
68 if ( sscanf(str, PCI_BDF_SHORT, &bus, &dev, &func) != 3 ) {
69 rc = ERROR_FAIL;
70 goto out;
71 }
72 }
74 rc = pcidev_init(pcidev, dom, bus, dev, func, 0);
76 while ((p = strtok(NULL, ",=")) != NULL) {
77 while (*p == ' ')
78 p++;
79 if (!strcmp(p, "msitranslate")) {
80 p = strtok(NULL, ",=");
81 pcidev->msitranslate = atoi(p);
82 } else if (!strcmp(p, "power_mgmt")) {
83 p = strtok(NULL, ",=");
84 pcidev->power_mgmt = atoi(p);
85 }
86 }
87 out:
88 free(buf2);
89 return rc;
90 }
92 static int libxl_create_pci_backend(libxl_ctx *ctx, uint32_t domid, libxl_device_pci *pcidev, int num)
93 {
94 flexarray_t *front;
95 flexarray_t *back;
96 unsigned int boffset = 0;
97 unsigned int foffset = 0;
98 libxl_device device;
99 int i;
101 front = flexarray_make(16, 1);
102 if (!front)
103 return ERROR_NOMEM;
104 back = flexarray_make(16, 1);
105 if (!back)
106 return ERROR_NOMEM;
108 XL_LOG(ctx, XL_LOG_DEBUG, "Creating pci backend");
110 /* add pci device */
111 device.backend_devid = 0;
112 device.backend_domid = 0;
113 device.backend_kind = DEVICE_PCI;
114 device.devid = 0;
115 device.domid = domid;
116 device.kind = DEVICE_PCI;
118 flexarray_set(back, boffset++, "frontend-id");
119 flexarray_set(back, boffset++, libxl_sprintf(ctx, "%d", domid));
120 flexarray_set(back, boffset++, "online");
121 flexarray_set(back, boffset++, "1");
122 flexarray_set(back, boffset++, "state");
123 flexarray_set(back, boffset++, libxl_sprintf(ctx, "%d", 1));
124 flexarray_set(back, boffset++, "domain");
125 flexarray_set(back, boffset++, libxl_domid_to_name(ctx, domid));
126 for (i = 0; i < num; i++) {
127 flexarray_set(back, boffset++, libxl_sprintf(ctx, "key-%d", i));
128 flexarray_set(back, boffset++, libxl_sprintf(ctx, PCI_BDF, pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func));
129 flexarray_set(back, boffset++, libxl_sprintf(ctx, "dev-%d", i));
130 flexarray_set(back, boffset++, libxl_sprintf(ctx, PCI_BDF, pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func));
131 if (pcidev->vdevfn) {
132 flexarray_set(back, boffset++, libxl_sprintf(ctx, "vdevfn-%d", i));
133 flexarray_set(back, boffset++, libxl_sprintf(ctx, "%x", pcidev->vdevfn));
134 }
135 flexarray_set(back, boffset++, libxl_sprintf(ctx, "opts-%d", i));
136 flexarray_set(back, boffset++, libxl_sprintf(ctx, "msitranslate=%d,power_mgmt=%d", pcidev->msitranslate, pcidev->power_mgmt));
137 flexarray_set(back, boffset++, libxl_sprintf(ctx, "state-%d", i));
138 flexarray_set(back, boffset++, libxl_sprintf(ctx, "%d", 1));
139 }
140 flexarray_set(back, boffset++, "num_devs");
141 flexarray_set(back, boffset++, libxl_sprintf(ctx, "%d", num));
143 flexarray_set(front, foffset++, "backend-id");
144 flexarray_set(front, foffset++, libxl_sprintf(ctx, "%d", 0));
145 flexarray_set(front, foffset++, "state");
146 flexarray_set(front, foffset++, libxl_sprintf(ctx, "%d", 1));
148 libxl_device_generic_add(ctx, &device,
149 libxl_xs_kvs_of_flexarray(ctx, back, boffset),
150 libxl_xs_kvs_of_flexarray(ctx, front, foffset));
152 flexarray_free(back);
153 flexarray_free(front);
154 return 0;
155 }
157 static int libxl_device_pci_add_xenstore(libxl_ctx *ctx, uint32_t domid, libxl_device_pci *pcidev)
158 {
159 flexarray_t *back;
160 char *num_devs, *be_path;
161 int num = 0;
162 unsigned int boffset = 0;
163 xs_transaction_t t;
165 be_path = libxl_sprintf(ctx, "%s/backend/pci/%d/0", libxl_xs_get_dompath(ctx, 0), domid);
166 num_devs = libxl_xs_read(ctx, XBT_NULL, libxl_sprintf(ctx, "%s/num_devs", be_path));
167 if (!num_devs)
168 return libxl_create_pci_backend(ctx, domid, pcidev, 1);
170 if (!is_hvm(ctx, domid)) {
171 if (libxl_wait_for_backend(ctx, be_path, "4") < 0)
172 return ERROR_FAIL;
173 }
175 back = flexarray_make(16, 1);
176 if (!back)
177 return ERROR_NOMEM;
179 XL_LOG(ctx, XL_LOG_DEBUG, "Adding new pci device to xenstore");
180 num = atoi(num_devs);
181 flexarray_set(back, boffset++, libxl_sprintf(ctx, "key-%d", num));
182 flexarray_set(back, boffset++, libxl_sprintf(ctx, PCI_BDF, pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func));
183 flexarray_set(back, boffset++, libxl_sprintf(ctx, "dev-%d", num));
184 flexarray_set(back, boffset++, libxl_sprintf(ctx, PCI_BDF, pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func));
185 if (pcidev->vdevfn) {
186 flexarray_set(back, boffset++, libxl_sprintf(ctx, "vdevfn-%d", num));
187 flexarray_set(back, boffset++, libxl_sprintf(ctx, "%x", pcidev->vdevfn));
188 }
189 flexarray_set(back, boffset++, libxl_sprintf(ctx, "opts-%d", num));
190 flexarray_set(back, boffset++, libxl_sprintf(ctx, "msitranslate=%d,power_mgmt=%d", pcidev->msitranslate, pcidev->power_mgmt));
191 flexarray_set(back, boffset++, libxl_sprintf(ctx, "state-%d", num));
192 flexarray_set(back, boffset++, libxl_sprintf(ctx, "%d", 1));
193 flexarray_set(back, boffset++, "num_devs");
194 flexarray_set(back, boffset++, libxl_sprintf(ctx, "%d", num + 1));
195 flexarray_set(back, boffset++, "state");
196 flexarray_set(back, boffset++, libxl_sprintf(ctx, "%d", 7));
198 retry_transaction:
199 t = xs_transaction_start(ctx->xsh);
200 libxl_xs_writev(ctx, t, be_path,
201 libxl_xs_kvs_of_flexarray(ctx, back, boffset));
202 if (!xs_transaction_end(ctx->xsh, t, 0))
203 if (errno == EAGAIN)
204 goto retry_transaction;
206 flexarray_free(back);
207 return 0;
208 }
210 static int libxl_device_pci_remove_xenstore(libxl_ctx *ctx, uint32_t domid, libxl_device_pci *pcidev)
211 {
212 char *be_path, *num_devs_path, *num_devs, *xsdev, *tmp, *tmppath;
213 int num, i, j;
214 xs_transaction_t t;
215 unsigned int domain = 0, bus = 0, dev = 0, func = 0;
217 be_path = libxl_sprintf(ctx, "%s/backend/pci/%d/0", libxl_xs_get_dompath(ctx, 0), domid);
218 num_devs_path = libxl_sprintf(ctx, "%s/num_devs", be_path);
219 num_devs = libxl_xs_read(ctx, XBT_NULL, num_devs_path);
220 if (!num_devs)
221 return ERROR_INVAL;
222 num = atoi(num_devs);
224 if (!is_hvm(ctx, domid)) {
225 if (libxl_wait_for_backend(ctx, be_path, "4") < 0) {
226 XL_LOG(ctx, XL_LOG_DEBUG, "pci backend at %s is not ready", be_path);
227 return ERROR_FAIL;
228 }
229 }
231 for (i = 0; i < num; i++) {
232 xsdev = libxl_xs_read(ctx, XBT_NULL, libxl_sprintf(ctx, "%s/dev-%d", be_path, i));
233 sscanf(xsdev, PCI_BDF, &domain, &bus, &dev, &func);
234 if (domain == pcidev->domain && bus == pcidev->bus &&
235 pcidev->dev == dev && pcidev->func == func) {
236 break;
237 }
238 }
239 if (i == num) {
240 XL_LOG(ctx, XL_LOG_ERROR, "Couldn't find the device on xenstore");
241 return ERROR_INVAL;
242 }
244 retry_transaction:
245 t = xs_transaction_start(ctx->xsh);
246 xs_write(ctx->xsh, t, libxl_sprintf(ctx, "%s/state-%d", be_path, i), "5", strlen("5"));
247 xs_write(ctx->xsh, t, libxl_sprintf(ctx, "%s/state", be_path), "7", strlen("7"));
248 if (!xs_transaction_end(ctx->xsh, t, 0))
249 if (errno == EAGAIN)
250 goto retry_transaction;
252 if (!is_hvm(ctx, domid)) {
253 if (libxl_wait_for_backend(ctx, be_path, "4") < 0) {
254 XL_LOG(ctx, XL_LOG_DEBUG, "pci backend at %s is not ready", be_path);
255 return ERROR_FAIL;
256 }
257 }
259 retry_transaction2:
260 t = xs_transaction_start(ctx->xsh);
261 xs_rm(ctx->xsh, t, libxl_sprintf(ctx, "%s/state-%d", be_path, i));
262 xs_rm(ctx->xsh, t, libxl_sprintf(ctx, "%s/key-%d", be_path, i));
263 xs_rm(ctx->xsh, t, libxl_sprintf(ctx, "%s/dev-%d", be_path, i));
264 xs_rm(ctx->xsh, t, libxl_sprintf(ctx, "%s/vdev-%d", be_path, i));
265 xs_rm(ctx->xsh, t, libxl_sprintf(ctx, "%s/opts-%d", be_path, i));
266 xs_rm(ctx->xsh, t, libxl_sprintf(ctx, "%s/vdevfn-%d", be_path, i));
267 libxl_xs_write(ctx, t, num_devs_path, "%d", num - 1);
268 for (j = i + 1; j < num; j++) {
269 tmppath = libxl_sprintf(ctx, "%s/state-%d", be_path, j);
270 tmp = libxl_xs_read(ctx, t, tmppath);
271 xs_write(ctx->xsh, t, libxl_sprintf(ctx, "%s/state-%d", be_path, j - 1), tmp, strlen(tmp));
272 xs_rm(ctx->xsh, t, tmppath);
273 tmppath = libxl_sprintf(ctx, "%s/dev-%d", be_path, j);
274 tmp = libxl_xs_read(ctx, t, tmppath);
275 xs_write(ctx->xsh, t, libxl_sprintf(ctx, "%s/dev-%d", be_path, j - 1), tmp, strlen(tmp));
276 xs_rm(ctx->xsh, t, tmppath);
277 tmppath = libxl_sprintf(ctx, "%s/key-%d", be_path, j);
278 tmp = libxl_xs_read(ctx, t, tmppath);
279 xs_write(ctx->xsh, t, libxl_sprintf(ctx, "%s/key-%d", be_path, j - 1), tmp, strlen(tmp));
280 xs_rm(ctx->xsh, t, tmppath);
281 tmppath = libxl_sprintf(ctx, "%s/vdev-%d", be_path, j);
282 tmp = libxl_xs_read(ctx, t, tmppath);
283 if (tmp) {
284 xs_write(ctx->xsh, t, libxl_sprintf(ctx, "%s/vdev-%d", be_path, j - 1), tmp, strlen(tmp));
285 xs_rm(ctx->xsh, t, tmppath);
286 }
287 tmppath = libxl_sprintf(ctx, "%s/opts-%d", be_path, j);
288 tmp = libxl_xs_read(ctx, t, tmppath);
289 if (tmp) {
290 xs_write(ctx->xsh, t, libxl_sprintf(ctx, "%s/opts-%d", be_path, j - 1), tmp, strlen(tmp));
291 xs_rm(ctx->xsh, t, tmppath);
292 }
293 tmppath = libxl_sprintf(ctx, "%s/vdevfn-%d", be_path, j);
294 tmp = libxl_xs_read(ctx, t, tmppath);
295 if (tmp) {
296 xs_write(ctx->xsh, t, libxl_sprintf(ctx, "%s/vdevfn-%d", be_path, j - 1), tmp, strlen(tmp));
297 xs_rm(ctx->xsh, t, tmppath);
298 }
299 }
300 if (!xs_transaction_end(ctx->xsh, t, 0))
301 if (errno == EAGAIN)
302 goto retry_transaction2;
304 if (num == 1) {
305 char *fe_path = libxl_xs_read(ctx, XBT_NULL, libxl_sprintf(ctx, "%s/frontend", be_path));
306 libxl_device_destroy(ctx, be_path, 1);
307 xs_rm(ctx->xsh, XBT_NULL, be_path);
308 xs_rm(ctx->xsh, XBT_NULL, fe_path);
309 return 0;
310 }
312 return 0;
313 }
315 static int get_all_assigned_devices(libxl_ctx *ctx, libxl_device_pci **list, int *num)
316 {
317 libxl_device_pci *pcidevs = NULL;
318 char **domlist;
319 unsigned int nd = 0, i;
321 *list = NULL;
322 *num = 0;
324 domlist = libxl_xs_directory(ctx, XBT_NULL, "/local/domain", &nd);
325 for(i = 0; i < nd; i++) {
326 char *path, *num_devs;
328 path = libxl_sprintf(ctx, "/local/domain/0/backend/pci/%s/0/num_devs", domlist[i]);
329 num_devs = libxl_xs_read(ctx, XBT_NULL, path);
330 if ( num_devs ) {
331 int ndev = atoi(num_devs), j;
332 char *devpath, *bdf;
334 pcidevs = calloc(sizeof(*pcidevs), ndev);
335 for(j = (pcidevs) ? 0 : ndev; j < ndev; j++) {
336 devpath = libxl_sprintf(ctx, "/local/domain/0/backend/pci/%s/0/dev-%u",
337 domlist[i], j);
338 bdf = libxl_xs_read(ctx, XBT_NULL, devpath);
339 if ( bdf ) {
340 unsigned dom, bus, dev, func;
341 if ( sscanf(bdf, PCI_BDF, &dom, &bus, &dev, &func) != 4 )
342 continue;
344 pcidev_init(pcidevs + *num, dom, bus, dev, func, 0);
345 (*num)++;
346 }
347 }
348 }
349 }
351 if ( 0 == *num ) {
352 free(pcidevs);
353 pcidevs = NULL;
354 }else{
355 *list = pcidevs;
356 }
358 return 0;
359 }
361 static int is_assigned(libxl_device_pci *assigned, int num_assigned,
362 int dom, int bus, int dev, int func)
363 {
364 int i;
366 for(i = 0; i < num_assigned; i++) {
367 if ( assigned[i].domain != dom )
368 continue;
369 if ( assigned[i].bus != bus )
370 continue;
371 if ( assigned[i].dev != dev )
372 continue;
373 if ( assigned[i].func != func )
374 continue;
375 return 1;
376 }
378 return 0;
379 }
381 int libxl_device_pci_list_assignable(libxl_ctx *ctx, libxl_device_pci **list, int *num)
382 {
383 libxl_device_pci *pcidevs = NULL, *new, *assigned;
384 struct dirent *de;
385 DIR *dir;
386 int rc, num_assigned;
388 *num = 0;
389 *list = NULL;
391 rc = get_all_assigned_devices(ctx, &assigned, &num_assigned);
392 if ( rc )
393 return rc;
395 dir = opendir(SYSFS_PCIBACK_DRIVER);
396 if ( NULL == dir ) {
397 if ( errno == ENOENT ) {
398 XL_LOG(ctx, XL_LOG_ERROR, "Looks like pciback driver not loaded");
399 }else{
400 XL_LOG_ERRNO(ctx, XL_LOG_ERROR, "Couldn't open %s", SYSFS_PCIBACK_DRIVER);
401 }
402 free(assigned);
403 return ERROR_FAIL;
404 }
406 while( (de = readdir(dir)) ) {
407 unsigned dom, bus, dev, func;
408 if ( sscanf(de->d_name, PCI_BDF, &dom, &bus, &dev, &func) != 4 )
409 continue;
411 if ( is_assigned(assigned, num_assigned, dom, bus, dev, func) )
412 continue;
414 new = realloc(pcidevs, ((*num) + 1) * sizeof(*new));
415 if ( NULL == new )
416 continue;
418 pcidevs = new;
419 new = pcidevs + *num;
421 memset(new, 0, sizeof(*new));
422 pcidev_init(new, dom, bus, dev, func, 0);
423 (*num)++;
424 }
426 closedir(dir);
427 free(assigned);
428 *list = pcidevs;
429 return 0;
430 }
432 static int do_pci_add(libxl_ctx *ctx, uint32_t domid, libxl_device_pci *pcidev)
433 {
434 char *path;
435 char *state, *vdevfn;
436 int rc, hvm;
438 hvm = is_hvm(ctx, domid);
439 if (hvm) {
440 if (libxl_wait_for_device_model(ctx, domid, "running", NULL, NULL) < 0) {
441 return ERROR_FAIL;
442 }
443 path = libxl_sprintf(ctx, "/local/domain/0/device-model/%d/state", domid);
444 state = libxl_xs_read(ctx, XBT_NULL, path);
445 path = libxl_sprintf(ctx, "/local/domain/0/device-model/%d/parameter", domid);
446 if (pcidev->vdevfn)
447 libxl_xs_write(ctx, XBT_NULL, path, PCI_BDF_VDEVFN, pcidev->domain,
448 pcidev->bus, pcidev->dev, pcidev->func, pcidev->vdevfn);
449 else
450 libxl_xs_write(ctx, XBT_NULL, path, PCI_BDF, pcidev->domain,
451 pcidev->bus, pcidev->dev, pcidev->func);
452 path = libxl_sprintf(ctx, "/local/domain/0/device-model/%d/command", domid);
453 xs_write(ctx->xsh, XBT_NULL, path, "pci-ins", strlen("pci-ins"));
454 if (libxl_wait_for_device_model(ctx, domid, "pci-inserted", NULL, NULL) < 0)
455 XL_LOG(ctx, XL_LOG_ERROR, "Device Model didn't respond in time");
456 path = libxl_sprintf(ctx, "/local/domain/0/device-model/%d/parameter", domid);
457 vdevfn = libxl_xs_read(ctx, XBT_NULL, path);
458 sscanf(vdevfn + 2, "%x", &pcidev->vdevfn);
459 path = libxl_sprintf(ctx, "/local/domain/0/device-model/%d/state", domid);
460 xs_write(ctx->xsh, XBT_NULL, path, state, strlen(state));
461 } else {
462 char *sysfs_path = libxl_sprintf(ctx, SYSFS_PCI_DEV"/"PCI_BDF"/resource", pcidev->domain,
463 pcidev->bus, pcidev->dev, pcidev->func);
464 FILE *f = fopen(sysfs_path, "r");
465 unsigned long long start = 0, end = 0, flags = 0, size = 0;
466 int irq = 0;
467 int i;
469 if (f == NULL) {
470 XL_LOG_ERRNO(ctx, XL_LOG_ERROR, "Couldn't open %s", sysfs_path);
471 return ERROR_FAIL;
472 }
473 for (i = 0; i < PROC_PCI_NUM_RESOURCES; i++) {
474 if (fscanf(f, "0x%llx 0x%llx 0x%llx\n", &start, &end, &flags) != 3)
475 continue;
476 size = end - start + 1;
477 if (start) {
478 if (flags & PCI_BAR_IO) {
479 rc = xc_domain_ioport_permission(ctx->xch, domid, start, size, 1);
480 if (rc < 0) {
481 XL_LOG_ERRNOVAL(ctx, XL_LOG_ERROR, rc, "Error: xc_domain_ioport_permission error 0x%llx/0x%llx", start, size);
482 fclose(f);
483 return ERROR_FAIL;
484 }
485 } else {
486 rc = xc_domain_iomem_permission(ctx->xch, domid, start>>XC_PAGE_SHIFT,
487 (size+(XC_PAGE_SIZE-1))>>XC_PAGE_SHIFT, 1);
488 if (rc < 0) {
489 XL_LOG_ERRNOVAL(ctx, XL_LOG_ERROR, rc, "Error: xc_domain_iomem_permission error 0x%llx/0x%llx", start, size);
490 fclose(f);
491 return ERROR_FAIL;
492 }
493 }
494 }
495 }
496 fclose(f);
497 sysfs_path = libxl_sprintf(ctx, SYSFS_PCI_DEV"/"PCI_BDF"/irq", pcidev->domain,
498 pcidev->bus, pcidev->dev, pcidev->func);
499 f = fopen(sysfs_path, "r");
500 if (f == NULL) {
501 XL_LOG_ERRNO(ctx, XL_LOG_ERROR, "Couldn't open %s", sysfs_path);
502 goto out;
503 }
504 if ((fscanf(f, "%u", &irq) == 1) && irq) {
505 rc = xc_physdev_map_pirq(ctx->xch, domid, irq, &irq);
506 if (rc < 0) {
507 XL_LOG_ERRNOVAL(ctx, XL_LOG_ERROR, rc, "Error: xc_physdev_map_pirq irq=%d", irq);
508 fclose(f);
509 return ERROR_FAIL;
510 }
511 rc = xc_domain_irq_permission(ctx->xch, domid, irq, 1);
512 if (rc < 0) {
513 XL_LOG_ERRNOVAL(ctx, XL_LOG_ERROR, rc, "Error: xc_domain_irq_permission irq=%d", irq);
514 fclose(f);
515 return ERROR_FAIL;
516 }
517 }
518 fclose(f);
519 }
520 out:
521 if (!libxl_is_stubdom(ctx, domid, NULL)) {
522 rc = xc_assign_device(ctx->xch, domid, pcidev->value);
523 if (rc < 0 && (hvm || errno != ENOSYS)) {
524 XL_LOG_ERRNOVAL(ctx, XL_LOG_ERROR, rc, "xc_assign_device failed");
525 return ERROR_FAIL;
526 }
527 }
529 libxl_device_pci_add_xenstore(ctx, domid, pcidev);
530 return 0;
531 }
533 int libxl_device_pci_add(libxl_ctx *ctx, uint32_t domid, libxl_device_pci *pcidev)
534 {
535 libxl_device_pci *assigned;
536 int num_assigned, rc;
537 int stubdomid = 0;
539 rc = get_all_assigned_devices(ctx, &assigned, &num_assigned);
540 if ( rc ) {
541 XL_LOG(ctx, XL_LOG_ERROR, "cannot determine if device is assigned, refusing to continue");
542 return ERROR_FAIL;
543 }
544 if ( is_assigned(assigned, num_assigned, pcidev->domain,
545 pcidev->bus, pcidev->dev, pcidev->func) ) {
546 XL_LOG(ctx, XL_LOG_ERROR, "PCI device already attached to a domain");
547 free(assigned);
548 return ERROR_FAIL;
549 }
550 free(assigned);
552 libxl_device_pci_reset(ctx, pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func);
554 stubdomid = libxl_get_stubdom_id(ctx, domid);
555 if (stubdomid != 0) {
556 libxl_device_pci pcidev_s = *pcidev;
557 rc = do_pci_add(ctx, stubdomid, &pcidev_s);
558 if ( rc )
559 return rc;
560 }
562 return do_pci_add(ctx, domid, pcidev);
563 }
565 int libxl_device_pci_remove(libxl_ctx *ctx, uint32_t domid, libxl_device_pci *pcidev)
566 {
567 libxl_device_pci *assigned;
568 char *path;
569 char *state;
570 int hvm, rc, num;
571 int stubdomid = 0;
573 if ( !libxl_device_pci_list_assigned(ctx, &assigned, domid, &num) ) {
574 if ( !is_assigned(assigned, num, pcidev->domain,
575 pcidev->bus, pcidev->dev, pcidev->func) ) {
576 XL_LOG(ctx, XL_LOG_ERROR, "PCI device not attached to this domain");
577 return ERROR_INVAL;
578 }
579 }
581 libxl_device_pci_remove_xenstore(ctx, domid, pcidev);
583 hvm = is_hvm(ctx, domid);
584 if (hvm) {
585 if (libxl_wait_for_device_model(ctx, domid, "running", NULL, NULL) < 0) {
586 return ERROR_FAIL;
587 }
588 path = libxl_sprintf(ctx, "/local/domain/0/device-model/%d/state", domid);
589 state = libxl_xs_read(ctx, XBT_NULL, path);
590 path = libxl_sprintf(ctx, "/local/domain/0/device-model/%d/parameter", domid);
591 libxl_xs_write(ctx, XBT_NULL, path, PCI_BDF, pcidev->domain,
592 pcidev->bus, pcidev->dev, pcidev->func);
593 path = libxl_sprintf(ctx, "/local/domain/0/device-model/%d/command", domid);
594 xs_write(ctx->xsh, XBT_NULL, path, "pci-rem", strlen("pci-rem"));
595 if (libxl_wait_for_device_model(ctx, domid, "pci-removed", NULL, NULL) < 0) {
596 XL_LOG(ctx, XL_LOG_ERROR, "Device Model didn't respond in time");
597 return ERROR_FAIL;
598 }
599 path = libxl_sprintf(ctx, "/local/domain/0/device-model/%d/state", domid);
600 xs_write(ctx->xsh, XBT_NULL, path, state, strlen(state));
601 } else {
602 char *sysfs_path = libxl_sprintf(ctx, SYSFS_PCI_DEV"/"PCI_BDF"/resource", pcidev->domain,
603 pcidev->bus, pcidev->dev, pcidev->func);
604 FILE *f = fopen(sysfs_path, "r");
605 unsigned int start = 0, end = 0, flags = 0, size = 0;
606 int irq = 0;
607 int i;
609 if (f == NULL) {
610 XL_LOG_ERRNO(ctx, XL_LOG_ERROR, "Couldn't open %s", sysfs_path);
611 goto skip1;
612 }
613 for (i = 0; i < PROC_PCI_NUM_RESOURCES; i++) {
614 if (fscanf(f, "0x%x 0x%x 0x%x\n", &start, &end, &flags) != 3)
615 continue;
616 size = end - start + 1;
617 if (start) {
618 if (flags & PCI_BAR_IO) {
619 rc = xc_domain_ioport_permission(ctx->xch, domid, start, size, 0);
620 if (rc < 0)
621 XL_LOG_ERRNOVAL(ctx, XL_LOG_ERROR, rc, "xc_domain_ioport_permission error 0x%x/0x%x", start, size);
622 } else {
623 rc = xc_domain_iomem_permission(ctx->xch, domid, start>>XC_PAGE_SHIFT,
624 (size+(XC_PAGE_SIZE-1))>>XC_PAGE_SHIFT, 0);
625 if (rc < 0)
626 XL_LOG_ERRNOVAL(ctx, XL_LOG_ERROR, rc, "xc_domain_iomem_permission error 0x%x/0x%x", start, size);
627 }
628 }
629 }
630 fclose(f);
631 skip1:
632 sysfs_path = libxl_sprintf(ctx, SYSFS_PCI_DEV"/"PCI_BDF"/irq", pcidev->domain,
633 pcidev->bus, pcidev->dev, pcidev->func);
634 f = fopen(sysfs_path, "r");
635 if (f == NULL) {
636 XL_LOG_ERRNO(ctx, XL_LOG_ERROR, "Couldn't open %s", sysfs_path);
637 goto out;
638 }
639 if ((fscanf(f, "%u", &irq) == 1) && irq) {
640 rc = xc_physdev_unmap_pirq(ctx->xch, domid, irq);
641 if (rc < 0) {
642 XL_LOG_ERRNOVAL(ctx, XL_LOG_ERROR, rc, "xc_physdev_map_pirq irq=%d", irq);
643 }
644 rc = xc_domain_irq_permission(ctx->xch, domid, irq, 0);
645 if (rc < 0) {
646 XL_LOG_ERRNOVAL(ctx, XL_LOG_ERROR, rc, "xc_domain_irq_permission irq=%d", irq);
647 }
648 }
649 fclose(f);
650 }
651 out:
652 libxl_device_pci_reset(ctx, pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func);
654 if (!libxl_is_stubdom(ctx, domid, NULL)) {
655 rc = xc_deassign_device(ctx->xch, domid, pcidev->value);
656 if (rc < 0 && (hvm || errno != ENOSYS))
657 XL_LOG_ERRNOVAL(ctx, XL_LOG_ERROR, rc, "xc_deassign_device failed");
658 }
660 stubdomid = libxl_get_stubdom_id(ctx, domid);
661 if (stubdomid != 0) {
662 libxl_device_pci pcidev_s = *pcidev;
663 libxl_device_pci_remove(ctx, stubdomid, &pcidev_s);
664 }
666 return 0;
667 }
669 int libxl_device_pci_list_assigned(libxl_ctx *ctx, libxl_device_pci **list, uint32_t domid, int *num)
670 {
671 char *be_path, *num_devs, *xsdev, *xsvdevfn, *xsopts;
672 int n, i;
673 unsigned int domain = 0, bus = 0, dev = 0, func = 0, vdevfn = 0;
674 libxl_device_pci *pcidevs;
676 be_path = libxl_sprintf(ctx, "%s/backend/pci/%d/0", libxl_xs_get_dompath(ctx, 0), domid);
677 num_devs = libxl_xs_read(ctx, XBT_NULL, libxl_sprintf(ctx, "%s/num_devs", be_path));
678 if (!num_devs) {
679 *num = 0;
680 *list = NULL;
681 return 0;
682 }
683 n = atoi(num_devs);
684 pcidevs = calloc(n, sizeof(libxl_device_pci));
685 *num = n;
687 for (i = 0; i < n; i++) {
688 xsdev = libxl_xs_read(ctx, XBT_NULL, libxl_sprintf(ctx, "%s/dev-%d", be_path, i));
689 sscanf(xsdev, PCI_BDF, &domain, &bus, &dev, &func);
690 xsvdevfn = libxl_xs_read(ctx, XBT_NULL, libxl_sprintf(ctx, "%s/vdevfn-%d", be_path, i));
691 if (xsvdevfn)
692 vdevfn = strtol(xsvdevfn, (char **) NULL, 16);
693 pcidev_init(pcidevs + i, domain, bus, dev, func, vdevfn);
694 xsopts = libxl_xs_read(ctx, XBT_NULL, libxl_sprintf(ctx, "%s/opts-%d", be_path, i));
695 if (xsopts) {
696 char *saveptr;
697 char *p = strtok_r(xsopts, ",=", &saveptr);
698 do {
699 while (*p == ' ')
700 p++;
701 if (!strcmp(p, "msitranslate")) {
702 p = strtok_r(NULL, ",=", &saveptr);
703 pcidevs[i].msitranslate = atoi(p);
704 } else if (!strcmp(p, "power_mgmt")) {
705 p = strtok_r(NULL, ",=", &saveptr);
706 pcidevs[i].power_mgmt = atoi(p);
707 }
708 } while ((p = strtok_r(NULL, ",=", &saveptr)) != NULL);
709 }
710 }
711 if ( *num )
712 *list = pcidevs;
713 return 0;
714 }
716 int libxl_device_pci_shutdown(libxl_ctx *ctx, uint32_t domid)
717 {
718 libxl_device_pci *pcidevs;
719 int num, i, rc;
721 rc = libxl_device_pci_list_assigned(ctx, &pcidevs, domid, &num);
722 if ( rc )
723 return rc;
724 for (i = 0; i < num; i++) {
725 if (libxl_device_pci_remove(ctx, domid, pcidevs + i) < 0)
726 return ERROR_FAIL;
727 }
728 free(pcidevs);
729 return 0;
730 }
732 int libxl_device_pci_reset(libxl_ctx *ctx, unsigned int domain, unsigned int bus,
733 unsigned int dev, unsigned int func)
734 {
735 char *reset;
736 int fd, rc;
738 reset = libxl_sprintf(ctx, "%s/pciback/do_flr", SYSFS_PCI_DEV);
739 fd = open(reset, O_WRONLY);
740 if (fd > 0) {
741 char *buf = libxl_sprintf(ctx, PCI_BDF, domain, bus, dev, func);
742 rc = write(fd, buf, strlen(buf));
743 if (rc < 0)
744 XL_LOG(ctx, XL_LOG_ERROR, "write to %s returned %d", reset, rc);
745 close(fd);
746 return rc < 0 ? rc : 0;
747 }
748 if (errno != ENOENT)
749 XL_LOG_ERRNO(ctx, XL_LOG_ERROR, "Failed to access pciback path %s", reset);
750 reset = libxl_sprintf(ctx, "%s/"PCI_BDF"/reset", SYSFS_PCI_DEV, domain, bus, dev, func);
751 fd = open(reset, O_WRONLY);
752 if (fd > 0) {
753 rc = write(fd, "1", 1);
754 if (rc < 0)
755 XL_LOG_ERRNO(ctx, XL_LOG_ERROR, "write to %s returned %d", reset, rc);
756 close(fd);
757 return rc < 0 ? rc : 0;
758 }
759 if (errno == ENOENT) {
760 XL_LOG(ctx, XL_LOG_ERROR, "The kernel doesn't support PCI device reset from sysfs");
761 } else {
762 XL_LOG_ERRNO(ctx, XL_LOG_ERROR, "Failed to access reset path %s", reset);
763 }
764 return -1;
765 }