debuggers.hg

view tools/libxl/libxl_pci.c @ 21965:20540b8fe4b6

xl: detect pci-insert-failed dm status on pci-passthrough

NOTE: This functionality depends on a corresponding qemu-dm patch to work as
expected. Should be safe to use with an un-patched qemu-dm as before.

libxl_wait_for_device_model can only wait for one status value, re-work the
API so that a callback function can chose between several different possible
status values for qemu-dm and fix up all callers appropriately.

In the case of PCI device insert we succeed if qemu-dm reports
"pci-device-inserted" and error out instead of hanging forever if it fails
since qemu-dm now reports a status of "pci-insert-failed".

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:43:46 2010 +0100 (2010-08-04)
parents 9c720d64160b
children 1644b4efef8a
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 static int hex_convert(const char *str, unsigned int *val, unsigned int mask)
56 {
57 unsigned long ret;
58 char *end;
60 ret = strtoul(str, &end, 16);
61 if ( end == str || *end != '\0' )
62 return -1;
63 if ( ret & ~mask )
64 return -1;
65 *val = (unsigned int)ret & mask;
66 return 0;
67 }
69 #define STATE_DOMAIN 0
70 #define STATE_BUS 1
71 #define STATE_DEV 2
72 #define STATE_FUNC 3
73 #define STATE_VSLOT 4
74 #define STATE_OPTIONS_K 6
75 #define STATE_OPTIONS_V 7
76 #define STATE_TERMINAL 8
77 int libxl_device_pci_parse_bdf(libxl_ctx *ctx, libxl_device_pci *pcidev, const char *str)
78 {
79 unsigned state = STATE_DOMAIN;
80 unsigned dom, bus, dev, func, vslot = 0;
81 char *buf2, *tok, *ptr, *end, *optkey = NULL;
83 if ( NULL == (buf2 = ptr = strdup(str)) )
84 return ERROR_NOMEM;
86 for(tok = ptr, end = ptr + strlen(ptr) + 1; ptr < end; ptr++) {
87 switch(state) {
88 case STATE_DOMAIN:
89 if ( *ptr == ':' ) {
90 state = STATE_BUS;
91 *ptr = '\0';
92 if ( hex_convert(tok, &dom, 0xffff) )
93 goto parse_error;
94 tok = ptr + 1;
95 }
96 break;
97 case STATE_BUS:
98 if ( *ptr == ':' ) {
99 state = STATE_DEV;
100 *ptr = '\0';
101 if ( hex_convert(tok, &bus, 0xff) )
102 goto parse_error;
103 tok = ptr + 1;
104 }else if ( *ptr == '.' ) {
105 state = STATE_FUNC;
106 *ptr = '\0';
107 if ( dom & ~0xff )
108 goto parse_error;
109 bus = dom;
110 dom = 0;
111 if ( hex_convert(tok, &dev, 0xff) )
112 goto parse_error;
113 tok = ptr + 1;
114 }
115 break;
116 case STATE_DEV:
117 if ( *ptr == '.' ) {
118 state = STATE_FUNC;
119 *ptr = '\0';
120 if ( hex_convert(tok, &dev, 0xff) )
121 goto parse_error;
122 tok = ptr + 1;
123 }
124 break;
125 case STATE_FUNC:
126 if ( *ptr == '\0' || *ptr == '@' || *ptr == ',' ) {
127 switch( *ptr ) {
128 case '\0':
129 state = STATE_TERMINAL;
130 break;
131 case '@':
132 state = STATE_VSLOT;
133 break;
134 case ',':
135 state = STATE_OPTIONS_K;
136 break;
137 }
138 *ptr = '\0';
139 if ( hex_convert(tok, &func, 0x7) )
140 goto parse_error;
141 tok = ptr + 1;
142 }
143 break;
144 case STATE_VSLOT:
145 if ( *ptr == '\0' || *ptr == ',' ) {
146 state = ( *ptr == ',' ) ? STATE_OPTIONS_K : STATE_TERMINAL;
147 *ptr = '\0';
148 if ( hex_convert(tok, &vslot, 0xff) )
149 goto parse_error;
150 tok = ptr + 1;
151 }
152 break;
153 case STATE_OPTIONS_K:
154 if ( *ptr == '=' ) {
155 state = STATE_OPTIONS_V;
156 *ptr = '\0';
157 optkey = tok;
158 tok = ptr + 1;
159 }
160 break;
161 case STATE_OPTIONS_V:
162 if ( *ptr == ',' || *ptr == '\0' ) {
163 state = (*ptr == ',') ? STATE_OPTIONS_K : STATE_TERMINAL;
164 *ptr = '\0';
165 if ( !strcmp(optkey, "msitranslate") ) {
166 pcidev->msitranslate = atoi(tok);
167 }else if ( !strcmp(optkey, "power_mgmt") ) {
168 pcidev->power_mgmt = atoi(tok);
169 }else{
170 XL_LOG(ctx, XL_LOG_WARNING,
171 "Unknown PCI BDF option: %s", optkey);
172 }
173 tok = ptr + 1;
174 }
175 default:
176 break;
177 }
178 }
180 free(buf2);
182 if ( tok != ptr || state != STATE_TERMINAL )
183 goto parse_error;
185 pcidev_init(pcidev, dom, bus, dev, func, vslot << 3);
187 return 0;
189 parse_error:
190 printf("parse error: %s\n", str);
191 return ERROR_INVAL;
192 }
194 static int libxl_create_pci_backend(libxl_ctx *ctx, uint32_t domid, libxl_device_pci *pcidev, int num)
195 {
196 flexarray_t *front;
197 flexarray_t *back;
198 unsigned int boffset = 0;
199 unsigned int foffset = 0;
200 libxl_device device;
201 int i;
203 front = flexarray_make(16, 1);
204 if (!front)
205 return ERROR_NOMEM;
206 back = flexarray_make(16, 1);
207 if (!back)
208 return ERROR_NOMEM;
210 XL_LOG(ctx, XL_LOG_DEBUG, "Creating pci backend");
212 /* add pci device */
213 device.backend_devid = 0;
214 device.backend_domid = 0;
215 device.backend_kind = DEVICE_PCI;
216 device.devid = 0;
217 device.domid = domid;
218 device.kind = DEVICE_PCI;
220 flexarray_set(back, boffset++, "frontend-id");
221 flexarray_set(back, boffset++, libxl_sprintf(ctx, "%d", domid));
222 flexarray_set(back, boffset++, "online");
223 flexarray_set(back, boffset++, "1");
224 flexarray_set(back, boffset++, "state");
225 flexarray_set(back, boffset++, libxl_sprintf(ctx, "%d", 1));
226 flexarray_set(back, boffset++, "domain");
227 flexarray_set(back, boffset++, libxl_domid_to_name(ctx, domid));
228 for (i = 0; i < num; i++) {
229 flexarray_set(back, boffset++, libxl_sprintf(ctx, "key-%d", i));
230 flexarray_set(back, boffset++, libxl_sprintf(ctx, PCI_BDF, pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func));
231 flexarray_set(back, boffset++, libxl_sprintf(ctx, "dev-%d", i));
232 flexarray_set(back, boffset++, libxl_sprintf(ctx, PCI_BDF, pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func));
233 if (pcidev->vdevfn) {
234 flexarray_set(back, boffset++, libxl_sprintf(ctx, "vdevfn-%d", i));
235 flexarray_set(back, boffset++, libxl_sprintf(ctx, "%x", pcidev->vdevfn));
236 }
237 flexarray_set(back, boffset++, libxl_sprintf(ctx, "opts-%d", i));
238 flexarray_set(back, boffset++, libxl_sprintf(ctx, "msitranslate=%d,power_mgmt=%d", pcidev->msitranslate, pcidev->power_mgmt));
239 flexarray_set(back, boffset++, libxl_sprintf(ctx, "state-%d", i));
240 flexarray_set(back, boffset++, libxl_sprintf(ctx, "%d", 1));
241 }
242 flexarray_set(back, boffset++, "num_devs");
243 flexarray_set(back, boffset++, libxl_sprintf(ctx, "%d", num));
245 flexarray_set(front, foffset++, "backend-id");
246 flexarray_set(front, foffset++, libxl_sprintf(ctx, "%d", 0));
247 flexarray_set(front, foffset++, "state");
248 flexarray_set(front, foffset++, libxl_sprintf(ctx, "%d", 1));
250 libxl_device_generic_add(ctx, &device,
251 libxl_xs_kvs_of_flexarray(ctx, back, boffset),
252 libxl_xs_kvs_of_flexarray(ctx, front, foffset));
254 flexarray_free(back);
255 flexarray_free(front);
256 return 0;
257 }
259 static int libxl_device_pci_add_xenstore(libxl_ctx *ctx, uint32_t domid, libxl_device_pci *pcidev)
260 {
261 flexarray_t *back;
262 char *num_devs, *be_path;
263 int num = 0;
264 unsigned int boffset = 0;
265 xs_transaction_t t;
267 be_path = libxl_sprintf(ctx, "%s/backend/pci/%d/0", libxl_xs_get_dompath(ctx, 0), domid);
268 num_devs = libxl_xs_read(ctx, XBT_NULL, libxl_sprintf(ctx, "%s/num_devs", be_path));
269 if (!num_devs)
270 return libxl_create_pci_backend(ctx, domid, pcidev, 1);
272 if (!is_hvm(ctx, domid)) {
273 if (libxl_wait_for_backend(ctx, be_path, "4") < 0)
274 return ERROR_FAIL;
275 }
277 back = flexarray_make(16, 1);
278 if (!back)
279 return ERROR_NOMEM;
281 XL_LOG(ctx, XL_LOG_DEBUG, "Adding new pci device to xenstore");
282 num = atoi(num_devs);
283 flexarray_set(back, boffset++, libxl_sprintf(ctx, "key-%d", num));
284 flexarray_set(back, boffset++, libxl_sprintf(ctx, PCI_BDF, pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func));
285 flexarray_set(back, boffset++, libxl_sprintf(ctx, "dev-%d", num));
286 flexarray_set(back, boffset++, libxl_sprintf(ctx, PCI_BDF, pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func));
287 if (pcidev->vdevfn) {
288 flexarray_set(back, boffset++, libxl_sprintf(ctx, "vdevfn-%d", num));
289 flexarray_set(back, boffset++, libxl_sprintf(ctx, "%x", pcidev->vdevfn));
290 }
291 flexarray_set(back, boffset++, libxl_sprintf(ctx, "opts-%d", num));
292 flexarray_set(back, boffset++, libxl_sprintf(ctx, "msitranslate=%d,power_mgmt=%d", pcidev->msitranslate, pcidev->power_mgmt));
293 flexarray_set(back, boffset++, libxl_sprintf(ctx, "state-%d", num));
294 flexarray_set(back, boffset++, libxl_sprintf(ctx, "%d", 1));
295 flexarray_set(back, boffset++, "num_devs");
296 flexarray_set(back, boffset++, libxl_sprintf(ctx, "%d", num + 1));
297 flexarray_set(back, boffset++, "state");
298 flexarray_set(back, boffset++, libxl_sprintf(ctx, "%d", 7));
300 retry_transaction:
301 t = xs_transaction_start(ctx->xsh);
302 libxl_xs_writev(ctx, t, be_path,
303 libxl_xs_kvs_of_flexarray(ctx, back, boffset));
304 if (!xs_transaction_end(ctx->xsh, t, 0))
305 if (errno == EAGAIN)
306 goto retry_transaction;
308 flexarray_free(back);
309 return 0;
310 }
312 static int libxl_device_pci_remove_xenstore(libxl_ctx *ctx, uint32_t domid, libxl_device_pci *pcidev)
313 {
314 char *be_path, *num_devs_path, *num_devs, *xsdev, *tmp, *tmppath;
315 int num, i, j;
316 xs_transaction_t t;
317 unsigned int domain = 0, bus = 0, dev = 0, func = 0;
319 be_path = libxl_sprintf(ctx, "%s/backend/pci/%d/0", libxl_xs_get_dompath(ctx, 0), domid);
320 num_devs_path = libxl_sprintf(ctx, "%s/num_devs", be_path);
321 num_devs = libxl_xs_read(ctx, XBT_NULL, num_devs_path);
322 if (!num_devs)
323 return ERROR_INVAL;
324 num = atoi(num_devs);
326 if (!is_hvm(ctx, domid)) {
327 if (libxl_wait_for_backend(ctx, be_path, "4") < 0) {
328 XL_LOG(ctx, XL_LOG_DEBUG, "pci backend at %s is not ready", be_path);
329 return ERROR_FAIL;
330 }
331 }
333 for (i = 0; i < num; i++) {
334 xsdev = libxl_xs_read(ctx, XBT_NULL, libxl_sprintf(ctx, "%s/dev-%d", be_path, i));
335 sscanf(xsdev, PCI_BDF, &domain, &bus, &dev, &func);
336 if (domain == pcidev->domain && bus == pcidev->bus &&
337 pcidev->dev == dev && pcidev->func == func) {
338 break;
339 }
340 }
341 if (i == num) {
342 XL_LOG(ctx, XL_LOG_ERROR, "Couldn't find the device on xenstore");
343 return ERROR_INVAL;
344 }
346 retry_transaction:
347 t = xs_transaction_start(ctx->xsh);
348 xs_write(ctx->xsh, t, libxl_sprintf(ctx, "%s/state-%d", be_path, i), "5", strlen("5"));
349 xs_write(ctx->xsh, t, libxl_sprintf(ctx, "%s/state", be_path), "7", strlen("7"));
350 if (!xs_transaction_end(ctx->xsh, t, 0))
351 if (errno == EAGAIN)
352 goto retry_transaction;
354 if (!is_hvm(ctx, domid)) {
355 if (libxl_wait_for_backend(ctx, be_path, "4") < 0) {
356 XL_LOG(ctx, XL_LOG_DEBUG, "pci backend at %s is not ready", be_path);
357 return ERROR_FAIL;
358 }
359 }
361 retry_transaction2:
362 t = xs_transaction_start(ctx->xsh);
363 xs_rm(ctx->xsh, t, libxl_sprintf(ctx, "%s/state-%d", be_path, i));
364 xs_rm(ctx->xsh, t, libxl_sprintf(ctx, "%s/key-%d", be_path, i));
365 xs_rm(ctx->xsh, t, libxl_sprintf(ctx, "%s/dev-%d", be_path, i));
366 xs_rm(ctx->xsh, t, libxl_sprintf(ctx, "%s/vdev-%d", be_path, i));
367 xs_rm(ctx->xsh, t, libxl_sprintf(ctx, "%s/opts-%d", be_path, i));
368 xs_rm(ctx->xsh, t, libxl_sprintf(ctx, "%s/vdevfn-%d", be_path, i));
369 libxl_xs_write(ctx, t, num_devs_path, "%d", num - 1);
370 for (j = i + 1; j < num; j++) {
371 tmppath = libxl_sprintf(ctx, "%s/state-%d", be_path, j);
372 tmp = libxl_xs_read(ctx, t, tmppath);
373 xs_write(ctx->xsh, t, libxl_sprintf(ctx, "%s/state-%d", be_path, j - 1), tmp, strlen(tmp));
374 xs_rm(ctx->xsh, t, tmppath);
375 tmppath = libxl_sprintf(ctx, "%s/dev-%d", be_path, j);
376 tmp = libxl_xs_read(ctx, t, tmppath);
377 xs_write(ctx->xsh, t, libxl_sprintf(ctx, "%s/dev-%d", be_path, j - 1), tmp, strlen(tmp));
378 xs_rm(ctx->xsh, t, tmppath);
379 tmppath = libxl_sprintf(ctx, "%s/key-%d", be_path, j);
380 tmp = libxl_xs_read(ctx, t, tmppath);
381 xs_write(ctx->xsh, t, libxl_sprintf(ctx, "%s/key-%d", be_path, j - 1), tmp, strlen(tmp));
382 xs_rm(ctx->xsh, t, tmppath);
383 tmppath = libxl_sprintf(ctx, "%s/vdev-%d", be_path, j);
384 tmp = libxl_xs_read(ctx, t, tmppath);
385 if (tmp) {
386 xs_write(ctx->xsh, t, libxl_sprintf(ctx, "%s/vdev-%d", be_path, j - 1), tmp, strlen(tmp));
387 xs_rm(ctx->xsh, t, tmppath);
388 }
389 tmppath = libxl_sprintf(ctx, "%s/opts-%d", be_path, j);
390 tmp = libxl_xs_read(ctx, t, tmppath);
391 if (tmp) {
392 xs_write(ctx->xsh, t, libxl_sprintf(ctx, "%s/opts-%d", be_path, j - 1), tmp, strlen(tmp));
393 xs_rm(ctx->xsh, t, tmppath);
394 }
395 tmppath = libxl_sprintf(ctx, "%s/vdevfn-%d", be_path, j);
396 tmp = libxl_xs_read(ctx, t, tmppath);
397 if (tmp) {
398 xs_write(ctx->xsh, t, libxl_sprintf(ctx, "%s/vdevfn-%d", be_path, j - 1), tmp, strlen(tmp));
399 xs_rm(ctx->xsh, t, tmppath);
400 }
401 }
402 if (!xs_transaction_end(ctx->xsh, t, 0))
403 if (errno == EAGAIN)
404 goto retry_transaction2;
406 if (num == 1) {
407 char *fe_path = libxl_xs_read(ctx, XBT_NULL, libxl_sprintf(ctx, "%s/frontend", be_path));
408 libxl_device_destroy(ctx, be_path, 1);
409 xs_rm(ctx->xsh, XBT_NULL, be_path);
410 xs_rm(ctx->xsh, XBT_NULL, fe_path);
411 return 0;
412 }
414 return 0;
415 }
417 static int get_all_assigned_devices(libxl_ctx *ctx, libxl_device_pci **list, int *num)
418 {
419 libxl_device_pci *pcidevs = NULL;
420 char **domlist;
421 unsigned int nd = 0, i;
423 *list = NULL;
424 *num = 0;
426 domlist = libxl_xs_directory(ctx, XBT_NULL, "/local/domain", &nd);
427 for(i = 0; i < nd; i++) {
428 char *path, *num_devs;
430 path = libxl_sprintf(ctx, "/local/domain/0/backend/pci/%s/0/num_devs", domlist[i]);
431 num_devs = libxl_xs_read(ctx, XBT_NULL, path);
432 if ( num_devs ) {
433 int ndev = atoi(num_devs), j;
434 char *devpath, *bdf;
436 pcidevs = calloc(sizeof(*pcidevs), ndev);
437 for(j = (pcidevs) ? 0 : ndev; j < ndev; j++) {
438 devpath = libxl_sprintf(ctx, "/local/domain/0/backend/pci/%s/0/dev-%u",
439 domlist[i], j);
440 bdf = libxl_xs_read(ctx, XBT_NULL, devpath);
441 if ( bdf ) {
442 unsigned dom, bus, dev, func;
443 if ( sscanf(bdf, PCI_BDF, &dom, &bus, &dev, &func) != 4 )
444 continue;
446 pcidev_init(pcidevs + *num, dom, bus, dev, func, 0);
447 (*num)++;
448 }
449 }
450 }
451 }
453 if ( 0 == *num ) {
454 free(pcidevs);
455 pcidevs = NULL;
456 }else{
457 *list = pcidevs;
458 }
460 return 0;
461 }
463 static int is_assigned(libxl_device_pci *assigned, int num_assigned,
464 int dom, int bus, int dev, int func)
465 {
466 int i;
468 for(i = 0; i < num_assigned; i++) {
469 if ( assigned[i].domain != dom )
470 continue;
471 if ( assigned[i].bus != bus )
472 continue;
473 if ( assigned[i].dev != dev )
474 continue;
475 if ( assigned[i].func != func )
476 continue;
477 return 1;
478 }
480 return 0;
481 }
483 int libxl_device_pci_list_assignable(libxl_ctx *ctx, libxl_device_pci **list, int *num)
484 {
485 libxl_device_pci *pcidevs = NULL, *new, *assigned;
486 struct dirent *de;
487 DIR *dir;
488 int rc, num_assigned;
490 *num = 0;
491 *list = NULL;
493 rc = get_all_assigned_devices(ctx, &assigned, &num_assigned);
494 if ( rc )
495 return rc;
497 dir = opendir(SYSFS_PCIBACK_DRIVER);
498 if ( NULL == dir ) {
499 if ( errno == ENOENT ) {
500 XL_LOG(ctx, XL_LOG_ERROR, "Looks like pciback driver not loaded");
501 }else{
502 XL_LOG_ERRNO(ctx, XL_LOG_ERROR, "Couldn't open %s", SYSFS_PCIBACK_DRIVER);
503 }
504 free(assigned);
505 return ERROR_FAIL;
506 }
508 while( (de = readdir(dir)) ) {
509 unsigned dom, bus, dev, func;
510 if ( sscanf(de->d_name, PCI_BDF, &dom, &bus, &dev, &func) != 4 )
511 continue;
513 if ( is_assigned(assigned, num_assigned, dom, bus, dev, func) )
514 continue;
516 new = realloc(pcidevs, ((*num) + 1) * sizeof(*new));
517 if ( NULL == new )
518 continue;
520 pcidevs = new;
521 new = pcidevs + *num;
523 memset(new, 0, sizeof(*new));
524 pcidev_init(new, dom, bus, dev, func, 0);
525 (*num)++;
526 }
528 closedir(dir);
529 free(assigned);
530 *list = pcidevs;
531 return 0;
532 }
534 static int pci_ins_check(libxl_ctx *ctx, uint32_t domid, const char *state, void *priv)
535 {
536 char *orig_state = priv;
538 if ( !strcmp(state, "pci-insert-failed") )
539 return -1;
540 if ( !strcmp(state, "pci-inserted") )
541 return 0;
542 if ( !strcmp(state, orig_state) )
543 return 1;
545 return 1;
546 }
548 static int do_pci_add(libxl_ctx *ctx, uint32_t domid, libxl_device_pci *pcidev)
549 {
550 char *path;
551 char *state, *vdevfn;
552 int rc, hvm;
554 hvm = is_hvm(ctx, domid);
555 if (hvm) {
556 if (libxl_wait_for_device_model(ctx, domid, "running", NULL, NULL) < 0) {
557 return ERROR_FAIL;
558 }
559 path = libxl_sprintf(ctx, "/local/domain/0/device-model/%d/state", domid);
560 state = libxl_xs_read(ctx, XBT_NULL, path);
561 path = libxl_sprintf(ctx, "/local/domain/0/device-model/%d/parameter", domid);
562 if (pcidev->vdevfn)
563 libxl_xs_write(ctx, XBT_NULL, path, PCI_BDF_VDEVFN, pcidev->domain,
564 pcidev->bus, pcidev->dev, pcidev->func, pcidev->vdevfn);
565 else
566 libxl_xs_write(ctx, XBT_NULL, path, PCI_BDF, pcidev->domain,
567 pcidev->bus, pcidev->dev, pcidev->func);
568 path = libxl_sprintf(ctx, "/local/domain/0/device-model/%d/command", domid);
569 xs_write(ctx->xsh, XBT_NULL, path, "pci-ins", strlen("pci-ins"));
570 rc = libxl_wait_for_device_model(ctx, domid, NULL, pci_ins_check, state);
571 path = libxl_sprintf(ctx, "/local/domain/0/device-model/%d/parameter", domid);
572 vdevfn = libxl_xs_read(ctx, XBT_NULL, path);
573 path = libxl_sprintf(ctx, "/local/domain/0/device-model/%d/state", domid);
574 if ( rc < 0 )
575 XL_LOG(ctx, XL_LOG_ERROR, "qemu refused to add device: %s", vdevfn);
576 else if ( sscanf(vdevfn, "0x%x", &pcidev->vdevfn) != 1 )
577 rc = -1;
578 xs_write(ctx->xsh, XBT_NULL, path, state, strlen(state));
579 if ( rc )
580 return ERROR_FAIL;
581 } else {
582 char *sysfs_path = libxl_sprintf(ctx, SYSFS_PCI_DEV"/"PCI_BDF"/resource", pcidev->domain,
583 pcidev->bus, pcidev->dev, pcidev->func);
584 FILE *f = fopen(sysfs_path, "r");
585 unsigned long long start = 0, end = 0, flags = 0, size = 0;
586 int irq = 0;
587 int i;
589 if (f == NULL) {
590 XL_LOG_ERRNO(ctx, XL_LOG_ERROR, "Couldn't open %s", sysfs_path);
591 return ERROR_FAIL;
592 }
593 for (i = 0; i < PROC_PCI_NUM_RESOURCES; i++) {
594 if (fscanf(f, "0x%llx 0x%llx 0x%llx\n", &start, &end, &flags) != 3)
595 continue;
596 size = end - start + 1;
597 if (start) {
598 if (flags & PCI_BAR_IO) {
599 rc = xc_domain_ioport_permission(ctx->xch, domid, start, size, 1);
600 if (rc < 0) {
601 XL_LOG_ERRNOVAL(ctx, XL_LOG_ERROR, rc, "Error: xc_domain_ioport_permission error 0x%llx/0x%llx", start, size);
602 fclose(f);
603 return ERROR_FAIL;
604 }
605 } else {
606 rc = xc_domain_iomem_permission(ctx->xch, domid, start>>XC_PAGE_SHIFT,
607 (size+(XC_PAGE_SIZE-1))>>XC_PAGE_SHIFT, 1);
608 if (rc < 0) {
609 XL_LOG_ERRNOVAL(ctx, XL_LOG_ERROR, rc, "Error: xc_domain_iomem_permission error 0x%llx/0x%llx", start, size);
610 fclose(f);
611 return ERROR_FAIL;
612 }
613 }
614 }
615 }
616 fclose(f);
617 sysfs_path = libxl_sprintf(ctx, SYSFS_PCI_DEV"/"PCI_BDF"/irq", pcidev->domain,
618 pcidev->bus, pcidev->dev, pcidev->func);
619 f = fopen(sysfs_path, "r");
620 if (f == NULL) {
621 XL_LOG_ERRNO(ctx, XL_LOG_ERROR, "Couldn't open %s", sysfs_path);
622 goto out;
623 }
624 if ((fscanf(f, "%u", &irq) == 1) && irq) {
625 rc = xc_physdev_map_pirq(ctx->xch, domid, irq, &irq);
626 if (rc < 0) {
627 XL_LOG_ERRNOVAL(ctx, XL_LOG_ERROR, rc, "Error: xc_physdev_map_pirq irq=%d", irq);
628 fclose(f);
629 return ERROR_FAIL;
630 }
631 rc = xc_domain_irq_permission(ctx->xch, domid, irq, 1);
632 if (rc < 0) {
633 XL_LOG_ERRNOVAL(ctx, XL_LOG_ERROR, rc, "Error: xc_domain_irq_permission irq=%d", irq);
634 fclose(f);
635 return ERROR_FAIL;
636 }
637 }
638 fclose(f);
639 }
640 out:
641 if (!libxl_is_stubdom(ctx, domid, NULL)) {
642 rc = xc_assign_device(ctx->xch, domid, pcidev->value);
643 if (rc < 0 && (hvm || errno != ENOSYS)) {
644 XL_LOG_ERRNOVAL(ctx, XL_LOG_ERROR, rc, "xc_assign_device failed");
645 return ERROR_FAIL;
646 }
647 }
649 libxl_device_pci_add_xenstore(ctx, domid, pcidev);
650 return 0;
651 }
653 int libxl_device_pci_add(libxl_ctx *ctx, uint32_t domid, libxl_device_pci *pcidev)
654 {
655 libxl_device_pci *assigned;
656 int num_assigned, rc;
657 int stubdomid = 0;
659 rc = get_all_assigned_devices(ctx, &assigned, &num_assigned);
660 if ( rc ) {
661 XL_LOG(ctx, XL_LOG_ERROR, "cannot determine if device is assigned, refusing to continue");
662 return ERROR_FAIL;
663 }
664 if ( is_assigned(assigned, num_assigned, pcidev->domain,
665 pcidev->bus, pcidev->dev, pcidev->func) ) {
666 XL_LOG(ctx, XL_LOG_ERROR, "PCI device already attached to a domain");
667 free(assigned);
668 return ERROR_FAIL;
669 }
670 free(assigned);
672 libxl_device_pci_reset(ctx, pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func);
674 stubdomid = libxl_get_stubdom_id(ctx, domid);
675 if (stubdomid != 0) {
676 libxl_device_pci pcidev_s = *pcidev;
677 rc = do_pci_add(ctx, stubdomid, &pcidev_s);
678 if ( rc )
679 return rc;
680 }
682 return do_pci_add(ctx, domid, pcidev);
683 }
685 int libxl_device_pci_remove(libxl_ctx *ctx, uint32_t domid, libxl_device_pci *pcidev)
686 {
687 libxl_device_pci *assigned;
688 char *path;
689 char *state;
690 int hvm, rc, num;
691 int stubdomid = 0;
693 if ( !libxl_device_pci_list_assigned(ctx, &assigned, domid, &num) ) {
694 if ( !is_assigned(assigned, num, pcidev->domain,
695 pcidev->bus, pcidev->dev, pcidev->func) ) {
696 XL_LOG(ctx, XL_LOG_ERROR, "PCI device not attached to this domain");
697 return ERROR_INVAL;
698 }
699 }
701 libxl_device_pci_remove_xenstore(ctx, domid, pcidev);
703 hvm = is_hvm(ctx, domid);
704 if (hvm) {
705 if (libxl_wait_for_device_model(ctx, domid, "running", NULL, NULL) < 0) {
706 return ERROR_FAIL;
707 }
708 path = libxl_sprintf(ctx, "/local/domain/0/device-model/%d/state", domid);
709 state = libxl_xs_read(ctx, XBT_NULL, path);
710 path = libxl_sprintf(ctx, "/local/domain/0/device-model/%d/parameter", domid);
711 libxl_xs_write(ctx, XBT_NULL, path, PCI_BDF, pcidev->domain,
712 pcidev->bus, pcidev->dev, pcidev->func);
713 path = libxl_sprintf(ctx, "/local/domain/0/device-model/%d/command", domid);
714 xs_write(ctx->xsh, XBT_NULL, path, "pci-rem", strlen("pci-rem"));
715 if (libxl_wait_for_device_model(ctx, domid, "pci-removed", NULL, NULL) < 0) {
716 XL_LOG(ctx, XL_LOG_ERROR, "Device Model didn't respond in time");
717 return ERROR_FAIL;
718 }
719 path = libxl_sprintf(ctx, "/local/domain/0/device-model/%d/state", domid);
720 xs_write(ctx->xsh, XBT_NULL, path, state, strlen(state));
721 } else {
722 char *sysfs_path = libxl_sprintf(ctx, SYSFS_PCI_DEV"/"PCI_BDF"/resource", pcidev->domain,
723 pcidev->bus, pcidev->dev, pcidev->func);
724 FILE *f = fopen(sysfs_path, "r");
725 unsigned int start = 0, end = 0, flags = 0, size = 0;
726 int irq = 0;
727 int i;
729 if (f == NULL) {
730 XL_LOG_ERRNO(ctx, XL_LOG_ERROR, "Couldn't open %s", sysfs_path);
731 goto skip1;
732 }
733 for (i = 0; i < PROC_PCI_NUM_RESOURCES; i++) {
734 if (fscanf(f, "0x%x 0x%x 0x%x\n", &start, &end, &flags) != 3)
735 continue;
736 size = end - start + 1;
737 if (start) {
738 if (flags & PCI_BAR_IO) {
739 rc = xc_domain_ioport_permission(ctx->xch, domid, start, size, 0);
740 if (rc < 0)
741 XL_LOG_ERRNOVAL(ctx, XL_LOG_ERROR, rc, "xc_domain_ioport_permission error 0x%x/0x%x", start, size);
742 } else {
743 rc = xc_domain_iomem_permission(ctx->xch, domid, start>>XC_PAGE_SHIFT,
744 (size+(XC_PAGE_SIZE-1))>>XC_PAGE_SHIFT, 0);
745 if (rc < 0)
746 XL_LOG_ERRNOVAL(ctx, XL_LOG_ERROR, rc, "xc_domain_iomem_permission error 0x%x/0x%x", start, size);
747 }
748 }
749 }
750 fclose(f);
751 skip1:
752 sysfs_path = libxl_sprintf(ctx, SYSFS_PCI_DEV"/"PCI_BDF"/irq", pcidev->domain,
753 pcidev->bus, pcidev->dev, pcidev->func);
754 f = fopen(sysfs_path, "r");
755 if (f == NULL) {
756 XL_LOG_ERRNO(ctx, XL_LOG_ERROR, "Couldn't open %s", sysfs_path);
757 goto out;
758 }
759 if ((fscanf(f, "%u", &irq) == 1) && irq) {
760 rc = xc_physdev_unmap_pirq(ctx->xch, domid, irq);
761 if (rc < 0) {
762 XL_LOG_ERRNOVAL(ctx, XL_LOG_ERROR, rc, "xc_physdev_map_pirq irq=%d", irq);
763 }
764 rc = xc_domain_irq_permission(ctx->xch, domid, irq, 0);
765 if (rc < 0) {
766 XL_LOG_ERRNOVAL(ctx, XL_LOG_ERROR, rc, "xc_domain_irq_permission irq=%d", irq);
767 }
768 }
769 fclose(f);
770 }
771 out:
772 libxl_device_pci_reset(ctx, pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func);
774 if (!libxl_is_stubdom(ctx, domid, NULL)) {
775 rc = xc_deassign_device(ctx->xch, domid, pcidev->value);
776 if (rc < 0 && (hvm || errno != ENOSYS))
777 XL_LOG_ERRNOVAL(ctx, XL_LOG_ERROR, rc, "xc_deassign_device failed");
778 }
780 stubdomid = libxl_get_stubdom_id(ctx, domid);
781 if (stubdomid != 0) {
782 libxl_device_pci pcidev_s = *pcidev;
783 libxl_device_pci_remove(ctx, stubdomid, &pcidev_s);
784 }
786 return 0;
787 }
789 int libxl_device_pci_list_assigned(libxl_ctx *ctx, libxl_device_pci **list, uint32_t domid, int *num)
790 {
791 char *be_path, *num_devs, *xsdev, *xsvdevfn, *xsopts;
792 int n, i;
793 unsigned int domain = 0, bus = 0, dev = 0, func = 0, vdevfn = 0;
794 libxl_device_pci *pcidevs;
796 be_path = libxl_sprintf(ctx, "%s/backend/pci/%d/0", libxl_xs_get_dompath(ctx, 0), domid);
797 num_devs = libxl_xs_read(ctx, XBT_NULL, libxl_sprintf(ctx, "%s/num_devs", be_path));
798 if (!num_devs) {
799 *num = 0;
800 *list = NULL;
801 return 0;
802 }
803 n = atoi(num_devs);
804 pcidevs = calloc(n, sizeof(libxl_device_pci));
805 *num = n;
807 for (i = 0; i < n; i++) {
808 xsdev = libxl_xs_read(ctx, XBT_NULL, libxl_sprintf(ctx, "%s/dev-%d", be_path, i));
809 sscanf(xsdev, PCI_BDF, &domain, &bus, &dev, &func);
810 xsvdevfn = libxl_xs_read(ctx, XBT_NULL, libxl_sprintf(ctx, "%s/vdevfn-%d", be_path, i));
811 if (xsvdevfn)
812 vdevfn = strtol(xsvdevfn, (char **) NULL, 16);
813 pcidev_init(pcidevs + i, domain, bus, dev, func, vdevfn);
814 xsopts = libxl_xs_read(ctx, XBT_NULL, libxl_sprintf(ctx, "%s/opts-%d", be_path, i));
815 if (xsopts) {
816 char *saveptr;
817 char *p = strtok_r(xsopts, ",=", &saveptr);
818 do {
819 while (*p == ' ')
820 p++;
821 if (!strcmp(p, "msitranslate")) {
822 p = strtok_r(NULL, ",=", &saveptr);
823 pcidevs[i].msitranslate = atoi(p);
824 } else if (!strcmp(p, "power_mgmt")) {
825 p = strtok_r(NULL, ",=", &saveptr);
826 pcidevs[i].power_mgmt = atoi(p);
827 }
828 } while ((p = strtok_r(NULL, ",=", &saveptr)) != NULL);
829 }
830 }
831 if ( *num )
832 *list = pcidevs;
833 return 0;
834 }
836 int libxl_device_pci_shutdown(libxl_ctx *ctx, uint32_t domid)
837 {
838 libxl_device_pci *pcidevs;
839 int num, i, rc;
841 rc = libxl_device_pci_list_assigned(ctx, &pcidevs, domid, &num);
842 if ( rc )
843 return rc;
844 for (i = 0; i < num; i++) {
845 if (libxl_device_pci_remove(ctx, domid, pcidevs + i) < 0)
846 return ERROR_FAIL;
847 }
848 free(pcidevs);
849 return 0;
850 }
852 int libxl_device_pci_reset(libxl_ctx *ctx, unsigned int domain, unsigned int bus,
853 unsigned int dev, unsigned int func)
854 {
855 char *reset;
856 int fd, rc;
858 reset = libxl_sprintf(ctx, "%s/pciback/do_flr", SYSFS_PCI_DEV);
859 fd = open(reset, O_WRONLY);
860 if (fd > 0) {
861 char *buf = libxl_sprintf(ctx, PCI_BDF, domain, bus, dev, func);
862 rc = write(fd, buf, strlen(buf));
863 if (rc < 0)
864 XL_LOG(ctx, XL_LOG_ERROR, "write to %s returned %d", reset, rc);
865 close(fd);
866 return rc < 0 ? rc : 0;
867 }
868 if (errno != ENOENT)
869 XL_LOG_ERRNO(ctx, XL_LOG_ERROR, "Failed to access pciback path %s", reset);
870 reset = libxl_sprintf(ctx, "%s/"PCI_BDF"/reset", SYSFS_PCI_DEV, domain, bus, dev, func);
871 fd = open(reset, O_WRONLY);
872 if (fd > 0) {
873 rc = write(fd, "1", 1);
874 if (rc < 0)
875 XL_LOG_ERRNO(ctx, XL_LOG_ERROR, "write to %s returned %d", reset, rc);
876 close(fd);
877 return rc < 0 ? rc : 0;
878 }
879 if (errno == ENOENT) {
880 XL_LOG(ctx, XL_LOG_ERROR, "The kernel doesn't support PCI device reset from sysfs");
881 } else {
882 XL_LOG_ERRNO(ctx, XL_LOG_ERROR, "Failed to access reset path %s", reset);
883 }
884 return -1;
885 }