debuggers.hg

annotate tools/libxl/libxl_pci.c @ 22855:1d1eec7e1fb4

xl: Perform minimal validation of virtual disk file while parsing config file

This patch performs some very basic validation on the virtual disk
file passed through the config file. This validation ensures that we
don't go too far with the initialization like spawn qemu and more
while there could be some potentially fundamental issues.

[ Patch fixed up to work with PHYSTYPE_EMPTY 22808:6ec61438713a -iwj ]

Signed-off-by: Kamala Narasimhan <kamala.narasimhan@citrix.com>
Acked-by: Ian Jackson <ian.jackson@eu.citrix.com>
Signed-off-by: Ian Jackson <ian.jackson@eu.citrix.com>
Committed-by: Ian Jackson <ian.jackson@eu.citrix.com>
author Kamala Narasimhan <kamala.narasimhan@gmail.com>
date Tue Jan 25 18:09:49 2011 +0000 (2011-01-25)
parents 5b8034ce8b8c
children 5429204f3c06
rev   line source
gianni@21926 1 /*
gianni@21926 2 * Copyright (C) 2009 Citrix Ltd.
gianni@21926 3 * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
gianni@21926 4 * Author Stefano Stabellini <stefano.stabellini@eu.citrix.com>
gianni@21926 5 *
gianni@21926 6 * This program is free software; you can redistribute it and/or modify
gianni@21926 7 * it under the terms of the GNU Lesser General Public License as published
gianni@21926 8 * by the Free Software Foundation; version 2.1 only. with the special
gianni@21926 9 * exception on linking described in file LICENSE.
gianni@21926 10 *
gianni@21926 11 * This program is distributed in the hope that it will be useful,
gianni@21926 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
gianni@21926 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
gianni@21926 14 * GNU Lesser General Public License for more details.
gianni@21926 15 */
gianni@21926 16
gianni@21926 17 #include "libxl_osdeps.h"
gianni@21926 18
gianni@21926 19 #include <stdio.h>
gianni@21926 20 #include <string.h>
gianni@21926 21 #include <stdlib.h>
gianni@21926 22 #include <sys/types.h>
gianni@21926 23 #include <fcntl.h>
gianni@21926 24 #include <sys/select.h>
gianni@21926 25 #include <sys/mman.h>
gianni@21926 26 #include <sys/wait.h>
dgdegra@22119 27 #include <sys/stat.h>
gianni@21926 28 #include <signal.h>
gianni@21926 29 #include <unistd.h> /* for write, unlink and close */
gianni@21926 30 #include <stdint.h>
gianni@21926 31 #include <inttypes.h>
gianni@21938 32 #include <dirent.h>
gianni@21926 33 #include <assert.h>
gianni@21926 34
gianni@21926 35 #include "libxl.h"
gianni@21926 36 #include "libxl_utils.h"
gianni@21926 37 #include "libxl_internal.h"
gianni@21926 38 #include "flexarray.h"
gianni@21926 39
gianni@21962 40 #define PCI_BDF "%04x:%02x:%02x.%01x"
gianni@21962 41 #define PCI_BDF_SHORT "%02x:%02x.%01x"
gianni@21962 42 #define PCI_BDF_VDEVFN "%04x:%02x:%02x.%01x@%02x"
gianni@21962 43
gianni@22212 44 static unsigned int pcidev_value(libxl_device_pci *pcidev)
gianni@22212 45 {
gianni@22212 46 union {
gianni@22212 47 unsigned int value;
gianni@22212 48 struct {
gianni@22212 49 unsigned int reserved1:2;
gianni@22212 50 unsigned int reg:6;
gianni@22212 51 unsigned int func:3;
gianni@22212 52 unsigned int dev:5;
gianni@22212 53 unsigned int bus:8;
gianni@22212 54 unsigned int reserved2:7;
gianni@22212 55 unsigned int enable:1;
gianni@22212 56 }fields;
gianni@22212 57 }u;
gianni@22212 58
gianni@22212 59 u.value = 0;
gianni@22212 60 u.fields.reg = pcidev->reg;
gianni@22212 61 u.fields.func = pcidev->func;
gianni@22212 62 u.fields.dev = pcidev->dev;
gianni@22212 63 u.fields.bus = pcidev->bus;
gianni@22212 64 u.fields.enable = pcidev->enable;
gianni@22212 65
gianni@22212 66 return u.value;
gianni@22212 67 }
gianni@22212 68
gianni@21962 69 static int pcidev_init(libxl_device_pci *pcidev, unsigned int domain,
gianni@21962 70 unsigned int bus, unsigned int dev,
gianni@21962 71 unsigned int func, unsigned int vdevfn)
gianni@21962 72 {
gianni@21962 73 pcidev->domain = domain;
gianni@21962 74 pcidev->bus = bus;
gianni@21962 75 pcidev->dev = dev;
gianni@21962 76 pcidev->func = func;
gianni@21962 77 pcidev->vdevfn = vdevfn;
gianni@21962 78 return 0;
gianni@21962 79 }
gianni@21962 80
gianni@21964 81 static int hex_convert(const char *str, unsigned int *val, unsigned int mask)
gianni@21962 82 {
gianni@21964 83 unsigned long ret;
gianni@21964 84 char *end;
gianni@21964 85
gianni@21964 86 ret = strtoul(str, &end, 16);
gianni@21964 87 if ( end == str || *end != '\0' )
gianni@21964 88 return -1;
gianni@21964 89 if ( ret & ~mask )
gianni@21964 90 return -1;
gianni@21964 91 *val = (unsigned int)ret & mask;
gianni@21964 92 return 0;
gianni@21964 93 }
gianni@21962 94
gianni@21964 95 #define STATE_DOMAIN 0
gianni@21964 96 #define STATE_BUS 1
gianni@21964 97 #define STATE_DEV 2
gianni@21964 98 #define STATE_FUNC 3
gianni@21964 99 #define STATE_VSLOT 4
gianni@21964 100 #define STATE_OPTIONS_K 6
gianni@21964 101 #define STATE_OPTIONS_V 7
gianni@21964 102 #define STATE_TERMINAL 8
gianni@21964 103 int libxl_device_pci_parse_bdf(libxl_ctx *ctx, libxl_device_pci *pcidev, const char *str)
gianni@21964 104 {
gianni@21964 105 unsigned state = STATE_DOMAIN;
gianni@21964 106 unsigned dom, bus, dev, func, vslot = 0;
gianni@21964 107 char *buf2, *tok, *ptr, *end, *optkey = NULL;
gianni@21964 108
gianni@21964 109 if ( NULL == (buf2 = ptr = strdup(str)) )
gianni@21962 110 return ERROR_NOMEM;
gianni@21962 111
gianni@21964 112 for(tok = ptr, end = ptr + strlen(ptr) + 1; ptr < end; ptr++) {
gianni@21964 113 switch(state) {
gianni@21964 114 case STATE_DOMAIN:
gianni@21964 115 if ( *ptr == ':' ) {
gianni@21964 116 state = STATE_BUS;
gianni@21964 117 *ptr = '\0';
gianni@21964 118 if ( hex_convert(tok, &dom, 0xffff) )
gianni@21964 119 goto parse_error;
gianni@21964 120 tok = ptr + 1;
gianni@21964 121 }
gianni@21964 122 break;
gianni@21964 123 case STATE_BUS:
gianni@21964 124 if ( *ptr == ':' ) {
gianni@21964 125 state = STATE_DEV;
gianni@21964 126 *ptr = '\0';
gianni@21964 127 if ( hex_convert(tok, &bus, 0xff) )
gianni@21964 128 goto parse_error;
gianni@21964 129 tok = ptr + 1;
gianni@21964 130 }else if ( *ptr == '.' ) {
gianni@21964 131 state = STATE_FUNC;
gianni@21964 132 *ptr = '\0';
gianni@21964 133 if ( dom & ~0xff )
gianni@21964 134 goto parse_error;
gianni@21964 135 bus = dom;
gianni@21964 136 dom = 0;
gianni@21964 137 if ( hex_convert(tok, &dev, 0xff) )
gianni@21964 138 goto parse_error;
gianni@21964 139 tok = ptr + 1;
gianni@21964 140 }
gianni@21964 141 break;
gianni@21964 142 case STATE_DEV:
gianni@21964 143 if ( *ptr == '.' ) {
gianni@21964 144 state = STATE_FUNC;
gianni@21964 145 *ptr = '\0';
gianni@21964 146 if ( hex_convert(tok, &dev, 0xff) )
gianni@21964 147 goto parse_error;
gianni@21964 148 tok = ptr + 1;
gianni@21964 149 }
gianni@21964 150 break;
gianni@21964 151 case STATE_FUNC:
gianni@21964 152 if ( *ptr == '\0' || *ptr == '@' || *ptr == ',' ) {
gianni@21964 153 switch( *ptr ) {
gianni@21964 154 case '\0':
gianni@21964 155 state = STATE_TERMINAL;
gianni@21964 156 break;
gianni@21964 157 case '@':
gianni@21964 158 state = STATE_VSLOT;
gianni@21964 159 break;
gianni@21964 160 case ',':
gianni@21964 161 state = STATE_OPTIONS_K;
gianni@21964 162 break;
gianni@21964 163 }
gianni@21964 164 *ptr = '\0';
sstabellini@21997 165 if ( !strcmp(tok, "*") ) {
sstabellini@21997 166 pcidev->vfunc_mask = LIBXL_PCI_FUNC_ALL;
sstabellini@21997 167 }else{
sstabellini@21997 168 if ( hex_convert(tok, &func, 0x7) )
sstabellini@21997 169 goto parse_error;
sstabellini@21997 170 pcidev->vfunc_mask = (1 << 0);
sstabellini@21997 171 }
gianni@21964 172 tok = ptr + 1;
gianni@21964 173 }
gianni@21964 174 break;
gianni@21964 175 case STATE_VSLOT:
gianni@21964 176 if ( *ptr == '\0' || *ptr == ',' ) {
gianni@21964 177 state = ( *ptr == ',' ) ? STATE_OPTIONS_K : STATE_TERMINAL;
gianni@21964 178 *ptr = '\0';
gianni@21964 179 if ( hex_convert(tok, &vslot, 0xff) )
gianni@21964 180 goto parse_error;
gianni@21964 181 tok = ptr + 1;
gianni@21964 182 }
gianni@21964 183 break;
gianni@21964 184 case STATE_OPTIONS_K:
gianni@21964 185 if ( *ptr == '=' ) {
gianni@21964 186 state = STATE_OPTIONS_V;
gianni@21964 187 *ptr = '\0';
gianni@21964 188 optkey = tok;
gianni@21964 189 tok = ptr + 1;
gianni@21964 190 }
gianni@21964 191 break;
gianni@21964 192 case STATE_OPTIONS_V:
gianni@21964 193 if ( *ptr == ',' || *ptr == '\0' ) {
gianni@21964 194 state = (*ptr == ',') ? STATE_OPTIONS_K : STATE_TERMINAL;
gianni@21964 195 *ptr = '\0';
gianni@21964 196 if ( !strcmp(optkey, "msitranslate") ) {
gianni@21964 197 pcidev->msitranslate = atoi(tok);
gianni@21964 198 }else if ( !strcmp(optkey, "power_mgmt") ) {
gianni@21964 199 pcidev->power_mgmt = atoi(tok);
gianni@21964 200 }else{
ian@22165 201 LIBXL__LOG(ctx, LIBXL__LOG_WARNING,
gianni@21964 202 "Unknown PCI BDF option: %s", optkey);
gianni@21964 203 }
gianni@21964 204 tok = ptr + 1;
gianni@21964 205 }
gianni@21964 206 default:
gianni@21964 207 break;
gianni@21962 208 }
gianni@21962 209 }
gianni@21962 210
gianni@21964 211 free(buf2);
gianni@21964 212
gianni@21964 213 if ( tok != ptr || state != STATE_TERMINAL )
gianni@21964 214 goto parse_error;
gianni@21962 215
gianni@21964 216 pcidev_init(pcidev, dom, bus, dev, func, vslot << 3);
gianni@21964 217
gianni@21964 218 return 0;
gianni@21964 219
gianni@21964 220 parse_error:
gianni@21964 221 return ERROR_INVAL;
gianni@21962 222 }
gianni@21962 223
ian@22766 224 static void libxl_create_pci_backend_device(libxl__gc *gc, flexarray_t *back, int num, libxl_device_pci *pcidev)
ian@22766 225 {
ian@22766 226 flexarray_append(back, libxl__sprintf(gc, "key-%d", num));
ian@22766 227 flexarray_append(back, libxl__sprintf(gc, PCI_BDF, pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func));
ian@22766 228 flexarray_append(back, libxl__sprintf(gc, "dev-%d", num));
ian@22766 229 flexarray_append(back, libxl__sprintf(gc, PCI_BDF, pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func));
ian@22766 230 if (pcidev->vdevfn)
ian@22766 231 flexarray_vappend(back, libxl__sprintf(gc, "vdevfn-%d", num), libxl__sprintf(gc, "%x", pcidev->vdevfn), NULL);
ian@22766 232 flexarray_append(back, libxl__sprintf(gc, "opts-%d", num));
ian@22766 233 flexarray_append(back, libxl__sprintf(gc, "msitranslate=%d,power_mgmt=%d", pcidev->msitranslate, pcidev->power_mgmt));
ian@22766 234 flexarray_vappend(back, libxl__sprintf(gc, "state-%d", num), libxl__sprintf(gc, "%d", 1), NULL);
ian@22766 235 }
ian@22766 236
ian@22167 237 static int libxl_create_pci_backend(libxl__gc *gc, uint32_t domid, libxl_device_pci *pcidev, int num)
gianni@21926 238 {
ian@22167 239 libxl_ctx *ctx = libxl__gc_owner(gc);
ian@22764 240 flexarray_t *front = NULL;
ian@22764 241 flexarray_t *back = NULL;
ian@22167 242 libxl__device device;
ian@22764 243 int ret = ERROR_NOMEM, i;
gianni@21926 244
gianni@21926 245 front = flexarray_make(16, 1);
gianni@21926 246 if (!front)
ian@22764 247 goto out;
gianni@21926 248 back = flexarray_make(16, 1);
gianni@21926 249 if (!back)
ian@22764 250 goto out;
ian@22764 251
ian@22764 252 ret = 0;
gianni@21926 253
ian@22165 254 LIBXL__LOG(ctx, LIBXL__LOG_DEBUG, "Creating pci backend");
gianni@21926 255
gianni@21926 256 /* add pci device */
gianni@21926 257 device.backend_devid = 0;
gianni@21926 258 device.backend_domid = 0;
gianni@21926 259 device.backend_kind = DEVICE_PCI;
gianni@21926 260 device.devid = 0;
gianni@21926 261 device.domid = domid;
gianni@21926 262 device.kind = DEVICE_PCI;
gianni@21926 263
gianni@22726 264 flexarray_vappend(back, "frontend-id", libxl__sprintf(gc, "%d", domid),
ian@22766 265 "online", "1", "state", libxl__sprintf(gc, "%d", 1),
ian@22766 266 "domain", libxl__domid_to_name(gc, domid), NULL);
ian@22766 267
ian@22766 268 for (i = 0; i < num; i++, pcidev++)
ian@22766 269 libxl_create_pci_backend_device(gc, back, i, pcidev);
ian@22766 270
ian@22763 271 flexarray_vappend(back, "num_devs", libxl__sprintf(gc, "%d", num));
ian@22763 272
ian@22763 273 flexarray_vappend(front,
ian@22763 274 "backend-id", libxl__sprintf(gc, "%d", 0),
ian@22763 275 "state", libxl__sprintf(gc, "%d", 1), NULL);
gianni@21926 276
ian@22164 277 libxl__device_generic_add(ctx, &device,
gianni@22726 278 libxl__xs_kvs_of_flexarray(gc, back, back->count),
gianni@22726 279 libxl__xs_kvs_of_flexarray(gc, front, front->count));
gianni@21926 280
ian@22764 281 out:
ian@22764 282 if (back)
ian@22764 283 flexarray_free(back);
ian@22764 284 if (front)
ian@22764 285 flexarray_free(front);
gianni@21926 286 return 0;
gianni@21926 287 }
gianni@21926 288
ian@22167 289 static int libxl_device_pci_add_xenstore(libxl__gc *gc, uint32_t domid, libxl_device_pci *pcidev)
gianni@21926 290 {
ian@22167 291 libxl_ctx *ctx = libxl__gc_owner(gc);
gianni@21926 292 flexarray_t *back;
gianni@21926 293 char *num_devs, *be_path;
gianni@21926 294 int num = 0;
gianni@21926 295 xs_transaction_t t;
gianni@21926 296
ian@22164 297 be_path = libxl__sprintf(gc, "%s/backend/pci/%d/0", libxl__xs_get_dompath(gc, 0), domid);
ian@22164 298 num_devs = libxl__xs_read(gc, XBT_NULL, libxl__sprintf(gc, "%s/num_devs", be_path));
gianni@21926 299 if (!num_devs)
gianni@22023 300 return libxl_create_pci_backend(gc, domid, pcidev, 1);
gianni@21926 301
ian@22165 302 if (!libxl__domain_is_hvm(ctx, domid)) {
ian@22164 303 if (libxl__wait_for_backend(ctx, be_path, "4") < 0)
gianni@21926 304 return ERROR_FAIL;
gianni@21926 305 }
gianni@21926 306
gianni@21926 307 back = flexarray_make(16, 1);
gianni@21926 308 if (!back)
gianni@21926 309 return ERROR_NOMEM;
gianni@21926 310
ian@22165 311 LIBXL__LOG(ctx, LIBXL__LOG_DEBUG, "Adding new pci device to xenstore");
gianni@21926 312 num = atoi(num_devs);
ian@22766 313 libxl_create_pci_backend_device(gc, back, num, pcidev);
gianni@22726 314 flexarray_vappend(back, "num_devs", libxl__sprintf(gc, "%d", num + 1), NULL);
gianni@22726 315 flexarray_vappend(back, "state", libxl__sprintf(gc, "%d", 7), NULL);
gianni@21926 316
gianni@21926 317 retry_transaction:
gianni@21926 318 t = xs_transaction_start(ctx->xsh);
ian@22164 319 libxl__xs_writev(gc, t, be_path,
gianni@22726 320 libxl__xs_kvs_of_flexarray(gc, back, back->count));
gianni@21926 321 if (!xs_transaction_end(ctx->xsh, t, 0))
gianni@21926 322 if (errno == EAGAIN)
gianni@21926 323 goto retry_transaction;
gianni@21926 324
gianni@21926 325 flexarray_free(back);
gianni@21926 326 return 0;
gianni@21926 327 }
gianni@21926 328
ian@22167 329 static int libxl_device_pci_remove_xenstore(libxl__gc *gc, uint32_t domid, libxl_device_pci *pcidev)
gianni@21926 330 {
ian@22167 331 libxl_ctx *ctx = libxl__gc_owner(gc);
gianni@21926 332 char *be_path, *num_devs_path, *num_devs, *xsdev, *tmp, *tmppath;
gianni@21926 333 int num, i, j;
gianni@21926 334 xs_transaction_t t;
gianni@21926 335 unsigned int domain = 0, bus = 0, dev = 0, func = 0;
gianni@21926 336
ian@22164 337 be_path = libxl__sprintf(gc, "%s/backend/pci/%d/0", libxl__xs_get_dompath(gc, 0), domid);
ian@22164 338 num_devs_path = libxl__sprintf(gc, "%s/num_devs", be_path);
ian@22164 339 num_devs = libxl__xs_read(gc, XBT_NULL, num_devs_path);
gianni@21926 340 if (!num_devs)
gianni@21926 341 return ERROR_INVAL;
gianni@21926 342 num = atoi(num_devs);
gianni@21926 343
ian@22165 344 if (!libxl__domain_is_hvm(ctx, domid)) {
ian@22164 345 if (libxl__wait_for_backend(ctx, be_path, "4") < 0) {
ian@22165 346 LIBXL__LOG(ctx, LIBXL__LOG_DEBUG, "pci backend at %s is not ready", be_path);
gianni@21926 347 return ERROR_FAIL;
gianni@21926 348 }
gianni@21926 349 }
gianni@21926 350
gianni@21926 351 for (i = 0; i < num; i++) {
ian@22164 352 xsdev = libxl__xs_read(gc, XBT_NULL, libxl__sprintf(gc, "%s/dev-%d", be_path, i));
gianni@21926 353 sscanf(xsdev, PCI_BDF, &domain, &bus, &dev, &func);
gianni@21926 354 if (domain == pcidev->domain && bus == pcidev->bus &&
gianni@21926 355 pcidev->dev == dev && pcidev->func == func) {
gianni@21926 356 break;
gianni@21926 357 }
gianni@21926 358 }
gianni@21926 359 if (i == num) {
ian@22165 360 LIBXL__LOG(ctx, LIBXL__LOG_ERROR, "Couldn't find the device on xenstore");
gianni@21926 361 return ERROR_INVAL;
gianni@21926 362 }
gianni@21926 363
gianni@21926 364 retry_transaction:
gianni@21926 365 t = xs_transaction_start(ctx->xsh);
ian@22164 366 xs_write(ctx->xsh, t, libxl__sprintf(gc, "%s/state-%d", be_path, i), "5", strlen("5"));
ian@22164 367 xs_write(ctx->xsh, t, libxl__sprintf(gc, "%s/state", be_path), "7", strlen("7"));
gianni@21926 368 if (!xs_transaction_end(ctx->xsh, t, 0))
gianni@21926 369 if (errno == EAGAIN)
gianni@21926 370 goto retry_transaction;
gianni@21926 371
ian@22165 372 if (!libxl__domain_is_hvm(ctx, domid)) {
ian@22164 373 if (libxl__wait_for_backend(ctx, be_path, "4") < 0) {
ian@22165 374 LIBXL__LOG(ctx, LIBXL__LOG_DEBUG, "pci backend at %s is not ready", be_path);
gianni@21926 375 return ERROR_FAIL;
gianni@21926 376 }
gianni@21926 377 }
gianni@21926 378
gianni@21926 379 retry_transaction2:
gianni@21926 380 t = xs_transaction_start(ctx->xsh);
ian@22164 381 xs_rm(ctx->xsh, t, libxl__sprintf(gc, "%s/state-%d", be_path, i));
ian@22164 382 xs_rm(ctx->xsh, t, libxl__sprintf(gc, "%s/key-%d", be_path, i));
ian@22164 383 xs_rm(ctx->xsh, t, libxl__sprintf(gc, "%s/dev-%d", be_path, i));
ian@22164 384 xs_rm(ctx->xsh, t, libxl__sprintf(gc, "%s/vdev-%d", be_path, i));
ian@22164 385 xs_rm(ctx->xsh, t, libxl__sprintf(gc, "%s/opts-%d", be_path, i));
ian@22164 386 xs_rm(ctx->xsh, t, libxl__sprintf(gc, "%s/vdevfn-%d", be_path, i));
ian@22164 387 libxl__xs_write(gc, t, num_devs_path, "%d", num - 1);
gianni@21926 388 for (j = i + 1; j < num; j++) {
ian@22164 389 tmppath = libxl__sprintf(gc, "%s/state-%d", be_path, j);
ian@22164 390 tmp = libxl__xs_read(gc, t, tmppath);
ian@22164 391 xs_write(ctx->xsh, t, libxl__sprintf(gc, "%s/state-%d", be_path, j - 1), tmp, strlen(tmp));
gianni@21926 392 xs_rm(ctx->xsh, t, tmppath);
ian@22164 393 tmppath = libxl__sprintf(gc, "%s/dev-%d", be_path, j);
ian@22164 394 tmp = libxl__xs_read(gc, t, tmppath);
ian@22164 395 xs_write(ctx->xsh, t, libxl__sprintf(gc, "%s/dev-%d", be_path, j - 1), tmp, strlen(tmp));
gianni@21926 396 xs_rm(ctx->xsh, t, tmppath);
ian@22164 397 tmppath = libxl__sprintf(gc, "%s/key-%d", be_path, j);
ian@22164 398 tmp = libxl__xs_read(gc, t, tmppath);
ian@22164 399 xs_write(ctx->xsh, t, libxl__sprintf(gc, "%s/key-%d", be_path, j - 1), tmp, strlen(tmp));
gianni@21926 400 xs_rm(ctx->xsh, t, tmppath);
ian@22164 401 tmppath = libxl__sprintf(gc, "%s/vdev-%d", be_path, j);
ian@22164 402 tmp = libxl__xs_read(gc, t, tmppath);
gianni@21926 403 if (tmp) {
ian@22164 404 xs_write(ctx->xsh, t, libxl__sprintf(gc, "%s/vdev-%d", be_path, j - 1), tmp, strlen(tmp));
gianni@21926 405 xs_rm(ctx->xsh, t, tmppath);
gianni@21926 406 }
ian@22164 407 tmppath = libxl__sprintf(gc, "%s/opts-%d", be_path, j);
ian@22164 408 tmp = libxl__xs_read(gc, t, tmppath);
gianni@21926 409 if (tmp) {
ian@22164 410 xs_write(ctx->xsh, t, libxl__sprintf(gc, "%s/opts-%d", be_path, j - 1), tmp, strlen(tmp));
gianni@21926 411 xs_rm(ctx->xsh, t, tmppath);
gianni@21926 412 }
ian@22164 413 tmppath = libxl__sprintf(gc, "%s/vdevfn-%d", be_path, j);
ian@22164 414 tmp = libxl__xs_read(gc, t, tmppath);
gianni@21926 415 if (tmp) {
ian@22164 416 xs_write(ctx->xsh, t, libxl__sprintf(gc, "%s/vdevfn-%d", be_path, j - 1), tmp, strlen(tmp));
gianni@21926 417 xs_rm(ctx->xsh, t, tmppath);
gianni@21926 418 }
gianni@21926 419 }
gianni@21926 420 if (!xs_transaction_end(ctx->xsh, t, 0))
gianni@21926 421 if (errno == EAGAIN)
gianni@21926 422 goto retry_transaction2;
gianni@21926 423
gianni@21926 424 if (num == 1) {
ian@22164 425 char *fe_path = libxl__xs_read(gc, XBT_NULL, libxl__sprintf(gc, "%s/frontend", be_path));
ian@22164 426 libxl__device_destroy(ctx, be_path, 1);
gianni@21926 427 xs_rm(ctx->xsh, XBT_NULL, be_path);
gianni@21926 428 xs_rm(ctx->xsh, XBT_NULL, fe_path);
gianni@21926 429 return 0;
gianni@21926 430 }
gianni@21926 431
gianni@21926 432 return 0;
gianni@21926 433 }
gianni@21926 434
ian@22167 435 static int get_all_assigned_devices(libxl__gc *gc, libxl_device_pci **list, int *num)
gianni@21938 436 {
gianni@21938 437 libxl_device_pci *pcidevs = NULL;
gianni@21938 438 char **domlist;
gianni@21938 439 unsigned int nd = 0, i;
gianni@21938 440
gianni@21938 441 *list = NULL;
gianni@21938 442 *num = 0;
gianni@21938 443
ian@22164 444 domlist = libxl__xs_directory(gc, XBT_NULL, "/local/domain", &nd);
gianni@21938 445 for(i = 0; i < nd; i++) {
gianni@21938 446 char *path, *num_devs;
gianni@21938 447
ian@22164 448 path = libxl__sprintf(gc, "/local/domain/0/backend/pci/%s/0/num_devs", domlist[i]);
ian@22164 449 num_devs = libxl__xs_read(gc, XBT_NULL, path);
gianni@21938 450 if ( num_devs ) {
gianni@21938 451 int ndev = atoi(num_devs), j;
gianni@21938 452 char *devpath, *bdf;
gianni@21938 453
ian@22164 454 pcidevs = libxl__calloc(gc, sizeof(*pcidevs), ndev);
gianni@21938 455 for(j = (pcidevs) ? 0 : ndev; j < ndev; j++) {
ian@22164 456 devpath = libxl__sprintf(gc, "/local/domain/0/backend/pci/%s/0/dev-%u",
gianni@21938 457 domlist[i], j);
ian@22164 458 bdf = libxl__xs_read(gc, XBT_NULL, devpath);
gianni@21938 459 if ( bdf ) {
gianni@21938 460 unsigned dom, bus, dev, func;
gianni@21938 461 if ( sscanf(bdf, PCI_BDF, &dom, &bus, &dev, &func) != 4 )
gianni@21938 462 continue;
gianni@21938 463
gianni@21962 464 pcidev_init(pcidevs + *num, dom, bus, dev, func, 0);
gianni@21938 465 (*num)++;
gianni@21938 466 }
gianni@21938 467 }
gianni@21938 468 }
gianni@21938 469 }
gianni@21938 470
gianni@21938 471 if ( 0 == *num ) {
gianni@21938 472 free(pcidevs);
gianni@21938 473 pcidevs = NULL;
gianni@21938 474 }else{
gianni@21938 475 *list = pcidevs;
gianni@21938 476 }
gianni@21938 477
gianni@21938 478 return 0;
gianni@21938 479 }
gianni@21938 480
gianni@21938 481 static int is_assigned(libxl_device_pci *assigned, int num_assigned,
gianni@21938 482 int dom, int bus, int dev, int func)
gianni@21938 483 {
gianni@21938 484 int i;
gianni@21938 485
gianni@21938 486 for(i = 0; i < num_assigned; i++) {
gianni@21938 487 if ( assigned[i].domain != dom )
gianni@21938 488 continue;
gianni@21938 489 if ( assigned[i].bus != bus )
gianni@21938 490 continue;
gianni@21938 491 if ( assigned[i].dev != dev )
gianni@21938 492 continue;
gianni@21938 493 if ( assigned[i].func != func )
gianni@21938 494 continue;
gianni@21938 495 return 1;
gianni@21938 496 }
gianni@21938 497
gianni@21938 498 return 0;
gianni@21938 499 }
gianni@21938 500
gianni@21961 501 int libxl_device_pci_list_assignable(libxl_ctx *ctx, libxl_device_pci **list, int *num)
gianni@21961 502 {
ian@22167 503 libxl__gc gc = LIBXL_INIT_GC(ctx);
gianni@21961 504 libxl_device_pci *pcidevs = NULL, *new, *assigned;
gianni@21961 505 struct dirent *de;
gianni@21961 506 DIR *dir;
gianni@21961 507 int rc, num_assigned;
gianni@21961 508
gianni@21961 509 *num = 0;
gianni@21961 510 *list = NULL;
gianni@21961 511
gianni@22023 512 rc = get_all_assigned_devices(&gc, &assigned, &num_assigned);
juergen@22549 513 if ( rc ) {
juergen@22549 514 libxl__free_all(&gc);
gianni@21961 515 return rc;
juergen@22549 516 }
gianni@21961 517
gianni@21961 518 dir = opendir(SYSFS_PCIBACK_DRIVER);
gianni@21961 519 if ( NULL == dir ) {
gianni@21961 520 if ( errno == ENOENT ) {
ian@22165 521 LIBXL__LOG(ctx, LIBXL__LOG_ERROR, "Looks like pciback driver not loaded");
gianni@21961 522 }else{
ian@22165 523 LIBXL__LOG_ERRNO(ctx, LIBXL__LOG_ERROR, "Couldn't open %s", SYSFS_PCIBACK_DRIVER);
gianni@21961 524 }
ian@22164 525 libxl__free_all(&gc);
gianni@21961 526 return ERROR_FAIL;
gianni@21961 527 }
gianni@21961 528
gianni@21961 529 while( (de = readdir(dir)) ) {
gianni@21961 530 unsigned dom, bus, dev, func;
gianni@21961 531 if ( sscanf(de->d_name, PCI_BDF, &dom, &bus, &dev, &func) != 4 )
gianni@21961 532 continue;
gianni@21961 533
gianni@21961 534 if ( is_assigned(assigned, num_assigned, dom, bus, dev, func) )
gianni@21961 535 continue;
gianni@21961 536
gianni@21961 537 new = realloc(pcidevs, ((*num) + 1) * sizeof(*new));
gianni@21961 538 if ( NULL == new )
gianni@21961 539 continue;
gianni@21961 540
gianni@21961 541 pcidevs = new;
gianni@21961 542 new = pcidevs + *num;
gianni@21961 543
gianni@21961 544 memset(new, 0, sizeof(*new));
gianni@21962 545 pcidev_init(new, dom, bus, dev, func, 0);
gianni@21961 546 (*num)++;
gianni@21961 547 }
gianni@21961 548
gianni@21961 549 closedir(dir);
gianni@21961 550 *list = pcidevs;
ian@22164 551 libxl__free_all(&gc);
gianni@21961 552 return 0;
gianni@21961 553 }
gianni@21961 554
sstabellini@21997 555 /*
sstabellini@21997 556 * This function checks that all functions of a device are bound to pciback
sstabellini@21997 557 * driver. It also initialises a bit-mask of which function numbers are present
sstabellini@21997 558 * on that device.
sstabellini@21997 559 */
ian@22167 560 static int pci_multifunction_check(libxl__gc *gc, libxl_device_pci *pcidev, unsigned int *func_mask)
sstabellini@21997 561 {
ian@22167 562 libxl_ctx *ctx = libxl__gc_owner(gc);
sstabellini@21997 563 struct dirent *de;
sstabellini@21997 564 DIR *dir;
sstabellini@21997 565
sstabellini@21997 566 *func_mask = 0;
sstabellini@21997 567
sstabellini@21997 568 dir = opendir(SYSFS_PCI_DEV);
sstabellini@21997 569 if ( NULL == dir ) {
ian@22165 570 LIBXL__LOG_ERRNO(ctx, LIBXL__LOG_ERROR, "Couldn't open %s", SYSFS_PCI_DEV);
sstabellini@21997 571 return -1;
sstabellini@21997 572 }
sstabellini@21997 573
sstabellini@21997 574 while( (de = readdir(dir)) ) {
sstabellini@21997 575 unsigned dom, bus, dev, func;
sstabellini@21997 576 struct stat st;
sstabellini@21997 577 char *path;
sstabellini@21997 578
sstabellini@21997 579 if ( sscanf(de->d_name, PCI_BDF, &dom, &bus, &dev, &func) != 4 )
sstabellini@21997 580 continue;
sstabellini@21997 581 if ( pcidev->domain != dom )
sstabellini@21997 582 continue;
sstabellini@21997 583 if ( pcidev->bus != bus )
sstabellini@21997 584 continue;
sstabellini@21997 585 if ( pcidev->dev != dev )
sstabellini@21997 586 continue;
sstabellini@21997 587
ian@22164 588 path = libxl__sprintf(gc, "%s/" PCI_BDF, SYSFS_PCIBACK_DRIVER, dom, bus, dev, func);
sstabellini@21997 589 if ( lstat(path, &st) ) {
sstabellini@21997 590 if ( errno == ENOENT )
ian@22165 591 LIBXL__LOG(ctx, LIBXL__LOG_ERROR, PCI_BDF " is not assigned to pciback driver",
sstabellini@21997 592 dom, bus, dev, func);
sstabellini@21997 593 else
ian@22165 594 LIBXL__LOG_ERRNO(ctx, LIBXL__LOG_ERROR, "Couldn't lstat %s", path);
sstabellini@21997 595 closedir(dir);
sstabellini@21997 596 return -1;
sstabellini@21997 597 }
sstabellini@21997 598 (*func_mask) |= (1 << func);
sstabellini@21997 599 }
sstabellini@21997 600
sstabellini@21997 601 closedir(dir);
sstabellini@21997 602 return 0;
sstabellini@21997 603 }
sstabellini@21997 604
gianni@21965 605 static int pci_ins_check(libxl_ctx *ctx, uint32_t domid, const char *state, void *priv)
gianni@21965 606 {
gianni@21965 607 char *orig_state = priv;
gianni@21965 608
gianni@21965 609 if ( !strcmp(state, "pci-insert-failed") )
gianni@21965 610 return -1;
gianni@21965 611 if ( !strcmp(state, "pci-inserted") )
gianni@21965 612 return 0;
gianni@21965 613 if ( !strcmp(state, orig_state) )
gianni@21965 614 return 1;
gianni@21965 615
gianni@21965 616 return 1;
gianni@21965 617 }
gianni@21965 618
ian@22167 619 static int do_pci_add(libxl__gc *gc, uint32_t domid, libxl_device_pci *pcidev)
gianni@21926 620 {
ian@22167 621 libxl_ctx *ctx = libxl__gc_owner(gc);
gianni@21926 622 char *path;
gianni@21926 623 char *state, *vdevfn;
gianni@21926 624 int rc, hvm;
gianni@21926 625
ian@22165 626 hvm = libxl__domain_is_hvm(ctx, domid);
gianni@21926 627 if (hvm) {
ian@22164 628 if (libxl__wait_for_device_model(ctx, domid, "running", NULL, NULL) < 0) {
gianni@21926 629 return ERROR_FAIL;
gianni@21926 630 }
ian@22164 631 path = libxl__sprintf(gc, "/local/domain/0/device-model/%d/state", domid);
ian@22164 632 state = libxl__xs_read(gc, XBT_NULL, path);
ian@22164 633 path = libxl__sprintf(gc, "/local/domain/0/device-model/%d/parameter", domid);
gianni@21926 634 if (pcidev->vdevfn)
ian@22164 635 libxl__xs_write(gc, XBT_NULL, path, PCI_BDF_VDEVFN, pcidev->domain,
gianni@21926 636 pcidev->bus, pcidev->dev, pcidev->func, pcidev->vdevfn);
gianni@21926 637 else
ian@22164 638 libxl__xs_write(gc, XBT_NULL, path, PCI_BDF, pcidev->domain,
gianni@21926 639 pcidev->bus, pcidev->dev, pcidev->func);
ian@22164 640 path = libxl__sprintf(gc, "/local/domain/0/device-model/%d/command", domid);
gianni@21926 641 xs_write(ctx->xsh, XBT_NULL, path, "pci-ins", strlen("pci-ins"));
ian@22164 642 rc = libxl__wait_for_device_model(ctx, domid, NULL, pci_ins_check, state);
ian@22164 643 path = libxl__sprintf(gc, "/local/domain/0/device-model/%d/parameter", domid);
ian@22164 644 vdevfn = libxl__xs_read(gc, XBT_NULL, path);
ian@22164 645 path = libxl__sprintf(gc, "/local/domain/0/device-model/%d/state", domid);
gianni@21965 646 if ( rc < 0 )
ian@22165 647 LIBXL__LOG(ctx, LIBXL__LOG_ERROR, "qemu refused to add device: %s", vdevfn);
gianni@21965 648 else if ( sscanf(vdevfn, "0x%x", &pcidev->vdevfn) != 1 )
gianni@21965 649 rc = -1;
gianni@21926 650 xs_write(ctx->xsh, XBT_NULL, path, state, strlen(state));
gianni@21965 651 if ( rc )
gianni@21965 652 return ERROR_FAIL;
gianni@21926 653 } else {
ian@22164 654 char *sysfs_path = libxl__sprintf(gc, SYSFS_PCI_DEV"/"PCI_BDF"/resource", pcidev->domain,
gianni@21926 655 pcidev->bus, pcidev->dev, pcidev->func);
gianni@21926 656 FILE *f = fopen(sysfs_path, "r");
gianni@21926 657 unsigned long long start = 0, end = 0, flags = 0, size = 0;
gianni@21926 658 int irq = 0;
gianni@21926 659 int i;
gianni@21926 660
gianni@21926 661 if (f == NULL) {
ian@22165 662 LIBXL__LOG_ERRNO(ctx, LIBXL__LOG_ERROR, "Couldn't open %s", sysfs_path);
gianni@21926 663 return ERROR_FAIL;
gianni@21926 664 }
gianni@21926 665 for (i = 0; i < PROC_PCI_NUM_RESOURCES; i++) {
gianni@21926 666 if (fscanf(f, "0x%llx 0x%llx 0x%llx\n", &start, &end, &flags) != 3)
gianni@21926 667 continue;
gianni@21926 668 size = end - start + 1;
gianni@21926 669 if (start) {
gianni@21926 670 if (flags & PCI_BAR_IO) {
gianni@21926 671 rc = xc_domain_ioport_permission(ctx->xch, domid, start, size, 1);
gianni@21926 672 if (rc < 0) {
ian@22165 673 LIBXL__LOG_ERRNOVAL(ctx, LIBXL__LOG_ERROR, rc, "Error: xc_domain_ioport_permission error 0x%llx/0x%llx", start, size);
gianni@21926 674 fclose(f);
gianni@21926 675 return ERROR_FAIL;
gianni@21926 676 }
gianni@21926 677 } else {
gianni@21926 678 rc = xc_domain_iomem_permission(ctx->xch, domid, start>>XC_PAGE_SHIFT,
gianni@21926 679 (size+(XC_PAGE_SIZE-1))>>XC_PAGE_SHIFT, 1);
gianni@21926 680 if (rc < 0) {
ian@22165 681 LIBXL__LOG_ERRNOVAL(ctx, LIBXL__LOG_ERROR, rc, "Error: xc_domain_iomem_permission error 0x%llx/0x%llx", start, size);
gianni@21926 682 fclose(f);
gianni@21926 683 return ERROR_FAIL;
gianni@21926 684 }
gianni@21926 685 }
gianni@21926 686 }
gianni@21926 687 }
gianni@21926 688 fclose(f);
ian@22164 689 sysfs_path = libxl__sprintf(gc, SYSFS_PCI_DEV"/"PCI_BDF"/irq", pcidev->domain,
gianni@21926 690 pcidev->bus, pcidev->dev, pcidev->func);
gianni@21926 691 f = fopen(sysfs_path, "r");
gianni@21926 692 if (f == NULL) {
ian@22165 693 LIBXL__LOG_ERRNO(ctx, LIBXL__LOG_ERROR, "Couldn't open %s", sysfs_path);
gianni@21926 694 goto out;
gianni@21926 695 }
gianni@21926 696 if ((fscanf(f, "%u", &irq) == 1) && irq) {
gianni@21926 697 rc = xc_physdev_map_pirq(ctx->xch, domid, irq, &irq);
gianni@21926 698 if (rc < 0) {
ian@22165 699 LIBXL__LOG_ERRNOVAL(ctx, LIBXL__LOG_ERROR, rc, "Error: xc_physdev_map_pirq irq=%d", irq);
gianni@21926 700 fclose(f);
gianni@21926 701 return ERROR_FAIL;
gianni@21926 702 }
gianni@21926 703 rc = xc_domain_irq_permission(ctx->xch, domid, irq, 1);
gianni@21926 704 if (rc < 0) {
ian@22165 705 LIBXL__LOG_ERRNOVAL(ctx, LIBXL__LOG_ERROR, rc, "Error: xc_domain_irq_permission irq=%d", irq);
gianni@21926 706 fclose(f);
gianni@21926 707 return ERROR_FAIL;
gianni@21926 708 }
gianni@21926 709 }
gianni@21926 710 fclose(f);
gianni@21926 711 }
gianni@21926 712 out:
gianni@21926 713 if (!libxl_is_stubdom(ctx, domid, NULL)) {
gianni@22212 714 rc = xc_assign_device(ctx->xch, domid, pcidev_value(pcidev));
mihirn@21951 715 if (rc < 0 && (hvm || errno != ENOSYS)) {
ian@22165 716 LIBXL__LOG_ERRNOVAL(ctx, LIBXL__LOG_ERROR, rc, "xc_assign_device failed");
gianni@21926 717 return ERROR_FAIL;
gianni@21926 718 }
gianni@21926 719 }
gianni@21926 720
gianni@22023 721 libxl_device_pci_add_xenstore(gc, domid, pcidev);
gianni@21926 722 return 0;
gianni@21926 723 }
gianni@21926 724
ian@22167 725 static int libxl_device_pci_reset(libxl__gc *gc, unsigned int domain, unsigned int bus,
gianni@22023 726 unsigned int dev, unsigned int func)
gianni@22023 727 {
ian@22167 728 libxl_ctx *ctx = libxl__gc_owner(gc);
gianni@22023 729 char *reset;
gianni@22023 730 int fd, rc;
gianni@22023 731
ian@22164 732 reset = libxl__sprintf(gc, "%s/pciback/do_flr", SYSFS_PCI_DEV);
gianni@22023 733 fd = open(reset, O_WRONLY);
gianni@22023 734 if (fd > 0) {
ian@22164 735 char *buf = libxl__sprintf(gc, PCI_BDF, domain, bus, dev, func);
gianni@22023 736 rc = write(fd, buf, strlen(buf));
gianni@22023 737 if (rc < 0)
ian@22165 738 LIBXL__LOG(ctx, LIBXL__LOG_ERROR, "write to %s returned %d", reset, rc);
gianni@22023 739 close(fd);
gianni@22023 740 return rc < 0 ? rc : 0;
gianni@22023 741 }
gianni@22023 742 if (errno != ENOENT)
ian@22165 743 LIBXL__LOG_ERRNO(ctx, LIBXL__LOG_ERROR, "Failed to access pciback path %s", reset);
ian@22164 744 reset = libxl__sprintf(gc, "%s/"PCI_BDF"/reset", SYSFS_PCI_DEV, domain, bus, dev, func);
gianni@22023 745 fd = open(reset, O_WRONLY);
gianni@22023 746 if (fd > 0) {
gianni@22023 747 rc = write(fd, "1", 1);
gianni@22023 748 if (rc < 0)
ian@22165 749 LIBXL__LOG_ERRNO(ctx, LIBXL__LOG_ERROR, "write to %s returned %d", reset, rc);
gianni@22023 750 close(fd);
gianni@22023 751 return rc < 0 ? rc : 0;
gianni@22023 752 }
gianni@22023 753 if (errno == ENOENT) {
ian@22765 754 LIBXL__LOG(ctx, LIBXL__LOG_ERROR, "The kernel doesn't support reset from sysfs for PCI device "PCI_BDF, domain, bus, dev, func);
gianni@22023 755 } else {
ian@22165 756 LIBXL__LOG_ERRNO(ctx, LIBXL__LOG_ERROR, "Failed to access reset path %s", reset);
gianni@22023 757 }
gianni@22023 758 return -1;
gianni@22023 759 }
gianni@22023 760
gianni@21938 761 int libxl_device_pci_add(libxl_ctx *ctx, uint32_t domid, libxl_device_pci *pcidev)
gianni@21938 762 {
ian@22167 763 libxl__gc gc = LIBXL_INIT_GC(ctx);
sstabellini@21997 764 unsigned int orig_vdev, pfunc_mask;
gianni@21938 765 libxl_device_pci *assigned;
gianni@22023 766 int num_assigned, i, rc;
gianni@21938 767 int stubdomid = 0;
gianni@21938 768
gianni@22023 769 rc = get_all_assigned_devices(&gc, &assigned, &num_assigned);
gianni@21938 770 if ( rc ) {
ian@22165 771 LIBXL__LOG(ctx, LIBXL__LOG_ERROR, "cannot determine if device is assigned, refusing to continue");
gianni@22023 772 goto out;
gianni@21938 773 }
gianni@21938 774 if ( is_assigned(assigned, num_assigned, pcidev->domain,
gianni@21938 775 pcidev->bus, pcidev->dev, pcidev->func) ) {
ian@22165 776 LIBXL__LOG(ctx, LIBXL__LOG_ERROR, "PCI device already attached to a domain");
gianni@22023 777 rc = ERROR_FAIL;
gianni@22023 778 goto out;
gianni@21938 779 }
gianni@21938 780
gianni@22023 781 libxl_device_pci_reset(&gc, pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func);
gianni@21938 782
gianni@21938 783 stubdomid = libxl_get_stubdom_id(ctx, domid);
gianni@21938 784 if (stubdomid != 0) {
gianni@21938 785 libxl_device_pci pcidev_s = *pcidev;
gianni@22023 786 rc = do_pci_add(&gc, stubdomid, &pcidev_s);
gianni@21938 787 if ( rc )
gianni@22023 788 goto out;
gianni@21938 789 }
gianni@21938 790
sstabellini@21997 791 orig_vdev = pcidev->vdevfn & ~7U;
sstabellini@21997 792
sstabellini@21997 793 if ( pcidev->vfunc_mask == LIBXL_PCI_FUNC_ALL ) {
sstabellini@21997 794 if ( !(pcidev->vdevfn >> 3) ) {
ian@22165 795 LIBXL__LOG(ctx, LIBXL__LOG_ERROR, "Must specify a v-slot for multi-function devices");
gianni@22023 796 rc = ERROR_INVAL;
gianni@22023 797 goto out;
sstabellini@21997 798 }
gianni@22023 799 if ( pci_multifunction_check(&gc, pcidev, &pfunc_mask) ) {
gianni@22023 800 rc = ERROR_FAIL;
gianni@22023 801 goto out;
sstabellini@21997 802 }
sstabellini@21997 803 pcidev->vfunc_mask &= pfunc_mask;
sstabellini@21997 804 /* so now vfunc_mask == pfunc_mask */
sstabellini@21997 805 }else{
sstabellini@21997 806 pfunc_mask = (1 << pcidev->func);
sstabellini@21997 807 }
sstabellini@21997 808
gianni@22023 809 for(rc = 0, i = 7; i >= 0; --i) {
sstabellini@21997 810 if ( (1 << i) & pfunc_mask ) {
sstabellini@21997 811 if ( pcidev->vfunc_mask == pfunc_mask ) {
sstabellini@21997 812 pcidev->func = i;
sstabellini@21997 813 pcidev->vdevfn = orig_vdev | i;
sstabellini@21997 814 }else{
sstabellini@21997 815 /* if not passing through multiple devices in a block make
sstabellini@21997 816 * sure that virtual function number 0 is always used otherwise
sstabellini@21997 817 * guest won't see the device
sstabellini@21997 818 */
sstabellini@21997 819 pcidev->vdevfn = orig_vdev;
sstabellini@21997 820 }
gianni@22023 821 if ( do_pci_add(&gc, domid, pcidev) )
sstabellini@21997 822 rc = ERROR_FAIL;
sstabellini@21997 823 }
sstabellini@21997 824 }
sstabellini@21997 825
gianni@22023 826 out:
ian@22164 827 libxl__free_all(&gc);
sstabellini@21997 828 return rc;
gianni@21938 829 }
gianni@21938 830
gianni@22275 831 static int do_pci_remove(libxl__gc *gc, uint32_t domid,
gianni@22275 832 libxl_device_pci *pcidev, int force)
gianni@21926 833 {
ian@22167 834 libxl_ctx *ctx = libxl__gc_owner(gc);
gianni@21963 835 libxl_device_pci *assigned;
gianni@21926 836 char *path;
gianni@21926 837 char *state;
gianni@21963 838 int hvm, rc, num;
gianni@21926 839 int stubdomid = 0;
gianni@21926 840
gianni@21963 841 if ( !libxl_device_pci_list_assigned(ctx, &assigned, domid, &num) ) {
gianni@21963 842 if ( !is_assigned(assigned, num, pcidev->domain,
gianni@21963 843 pcidev->bus, pcidev->dev, pcidev->func) ) {
ian@22165 844 LIBXL__LOG(ctx, LIBXL__LOG_ERROR, "PCI device not attached to this domain");
gianni@21963 845 return ERROR_INVAL;
gianni@21963 846 }
gianni@21963 847 }
gianni@21963 848
ian@22165 849 hvm = libxl__domain_is_hvm(ctx, domid);
gianni@21926 850 if (hvm) {
ian@22164 851 if (libxl__wait_for_device_model(ctx, domid, "running", NULL, NULL) < 0) {
gianni@21926 852 return ERROR_FAIL;
gianni@21926 853 }
ian@22164 854 path = libxl__sprintf(gc, "/local/domain/0/device-model/%d/state", domid);
ian@22164 855 state = libxl__xs_read(gc, XBT_NULL, path);
ian@22164 856 path = libxl__sprintf(gc, "/local/domain/0/device-model/%d/parameter", domid);
ian@22164 857 libxl__xs_write(gc, XBT_NULL, path, PCI_BDF, pcidev->domain,
gianni@21926 858 pcidev->bus, pcidev->dev, pcidev->func);
ian@22164 859 path = libxl__sprintf(gc, "/local/domain/0/device-model/%d/command", domid);
sstabellini@21997 860
sstabellini@21997 861 /* Remove all functions at once atomically by only signalling
sstabellini@21997 862 * device-model for function 0 */
sstabellini@21997 863 if ( (pcidev->vdevfn & 0x7) == 0 ) {
sstabellini@21997 864 xs_write(ctx->xsh, XBT_NULL, path, "pci-rem", strlen("pci-rem"));
ian@22164 865 if (libxl__wait_for_device_model(ctx, domid, "pci-removed", NULL, NULL) < 0) {
ian@22165 866 LIBXL__LOG(ctx, LIBXL__LOG_ERROR, "Device Model didn't respond in time");
gianni@22275 867 /* This depends on guest operating system acknowledging the
gianni@22275 868 * SCI, if it doesn't respond in time then we may wish to
gianni@22275 869 * force the removal.
gianni@22275 870 */
gianni@22275 871 if ( !force )
gianni@22275 872 return ERROR_FAIL;
sstabellini@21997 873 }
gianni@21926 874 }
ian@22164 875 path = libxl__sprintf(gc, "/local/domain/0/device-model/%d/state", domid);
gianni@21926 876 xs_write(ctx->xsh, XBT_NULL, path, state, strlen(state));
gianni@21926 877 } else {
ian@22164 878 char *sysfs_path = libxl__sprintf(gc, SYSFS_PCI_DEV"/"PCI_BDF"/resource", pcidev->domain,
gianni@21926 879 pcidev->bus, pcidev->dev, pcidev->func);
gianni@21926 880 FILE *f = fopen(sysfs_path, "r");
gianni@21926 881 unsigned int start = 0, end = 0, flags = 0, size = 0;
gianni@21926 882 int irq = 0;
gianni@21926 883 int i;
gianni@21926 884
gianni@21926 885 if (f == NULL) {
ian@22165 886 LIBXL__LOG_ERRNO(ctx, LIBXL__LOG_ERROR, "Couldn't open %s", sysfs_path);
gianni@21926 887 goto skip1;
gianni@21926 888 }
gianni@21926 889 for (i = 0; i < PROC_PCI_NUM_RESOURCES; i++) {
gianni@21926 890 if (fscanf(f, "0x%x 0x%x 0x%x\n", &start, &end, &flags) != 3)
gianni@21926 891 continue;
gianni@21926 892 size = end - start + 1;
gianni@21926 893 if (start) {
gianni@21926 894 if (flags & PCI_BAR_IO) {
gianni@21926 895 rc = xc_domain_ioport_permission(ctx->xch, domid, start, size, 0);
gianni@21926 896 if (rc < 0)
ian@22165 897 LIBXL__LOG_ERRNOVAL(ctx, LIBXL__LOG_ERROR, rc, "xc_domain_ioport_permission error 0x%x/0x%x", start, size);
gianni@21926 898 } else {
gianni@21926 899 rc = xc_domain_iomem_permission(ctx->xch, domid, start>>XC_PAGE_SHIFT,
gianni@21926 900 (size+(XC_PAGE_SIZE-1))>>XC_PAGE_SHIFT, 0);
gianni@21926 901 if (rc < 0)
ian@22165 902 LIBXL__LOG_ERRNOVAL(ctx, LIBXL__LOG_ERROR, rc, "xc_domain_iomem_permission error 0x%x/0x%x", start, size);
gianni@21926 903 }
gianni@21926 904 }
gianni@21926 905 }
gianni@21926 906 fclose(f);
gianni@21926 907 skip1:
ian@22164 908 sysfs_path = libxl__sprintf(gc, SYSFS_PCI_DEV"/"PCI_BDF"/irq", pcidev->domain,
gianni@21926 909 pcidev->bus, pcidev->dev, pcidev->func);
gianni@21926 910 f = fopen(sysfs_path, "r");
gianni@21926 911 if (f == NULL) {
ian@22165 912 LIBXL__LOG_ERRNO(ctx, LIBXL__LOG_ERROR, "Couldn't open %s", sysfs_path);
gianni@21926 913 goto out;
gianni@21926 914 }
gianni@21926 915 if ((fscanf(f, "%u", &irq) == 1) && irq) {
gianni@21926 916 rc = xc_physdev_unmap_pirq(ctx->xch, domid, irq);
gianni@21926 917 if (rc < 0) {
gianni@22275 918 LIBXL__LOG_ERRNOVAL(ctx, LIBXL__LOG_ERROR, rc, "xc_physdev_unmap_pirq irq=%d", irq);
gianni@21926 919 }
gianni@21926 920 rc = xc_domain_irq_permission(ctx->xch, domid, irq, 0);
gianni@21926 921 if (rc < 0) {
ian@22165 922 LIBXL__LOG_ERRNOVAL(ctx, LIBXL__LOG_ERROR, rc, "xc_domain_irq_permission irq=%d", irq);
gianni@21926 923 }
gianni@21926 924 }
gianni@21926 925 fclose(f);
gianni@21926 926 }
gianni@21926 927 out:
sstabellini@21997 928 /* don't do multiple resets while some functions are still passed through */
sstabellini@21997 929 if ( (pcidev->vdevfn & 0x7) == 0 ) {
gianni@22023 930 libxl_device_pci_reset(gc, pcidev->domain, pcidev->bus, pcidev->dev, pcidev->func);
sstabellini@21997 931 }
gianni@21926 932
gianni@21926 933 if (!libxl_is_stubdom(ctx, domid, NULL)) {
gianni@22212 934 rc = xc_deassign_device(ctx->xch, domid, pcidev_value(pcidev));
mihirn@21951 935 if (rc < 0 && (hvm || errno != ENOSYS))
ian@22165 936 LIBXL__LOG_ERRNOVAL(ctx, LIBXL__LOG_ERROR, rc, "xc_deassign_device failed");
gianni@21926 937 }
gianni@21926 938
gianni@21926 939 stubdomid = libxl_get_stubdom_id(ctx, domid);
gianni@21926 940 if (stubdomid != 0) {
gianni@21926 941 libxl_device_pci pcidev_s = *pcidev;
gianni@22275 942 libxl_device_pci_remove(ctx, stubdomid, &pcidev_s, force);
gianni@21926 943 }
gianni@21926 944
gianni@22275 945 libxl_device_pci_remove_xenstore(gc, domid, pcidev);
gianni@22275 946
gianni@21926 947 return 0;
gianni@21926 948 }
gianni@21926 949
gianni@22275 950 int libxl_device_pci_remove(libxl_ctx *ctx, uint32_t domid,
gianni@22275 951 libxl_device_pci *pcidev, int force)
sstabellini@21997 952 {
ian@22167 953 libxl__gc gc = LIBXL_INIT_GC(ctx);
sstabellini@21997 954 unsigned int orig_vdev, pfunc_mask;
sstabellini@21997 955 int i, rc;
sstabellini@21997 956
sstabellini@21997 957 orig_vdev = pcidev->vdevfn & ~7U;
sstabellini@21997 958
sstabellini@21997 959 if ( pcidev->vfunc_mask == LIBXL_PCI_FUNC_ALL ) {
gianni@22023 960 if ( pci_multifunction_check(&gc, pcidev, &pfunc_mask) ) {
gianni@22023 961 rc = ERROR_FAIL;
gianni@22023 962 goto out;
sstabellini@21997 963 }
sstabellini@21997 964 pcidev->vfunc_mask &= pfunc_mask;
sstabellini@21997 965 }else{
sstabellini@21997 966 pfunc_mask = (1 << pcidev->func);
sstabellini@21997 967 }
sstabellini@21997 968
gianni@22023 969 for(rc = 0, i = 7; i >= 0; --i) {
sstabellini@21997 970 if ( (1 << i) & pfunc_mask ) {
sstabellini@21997 971 if ( pcidev->vfunc_mask == pfunc_mask ) {
sstabellini@21997 972 pcidev->func = i;
sstabellini@21997 973 pcidev->vdevfn = orig_vdev | i;
sstabellini@21997 974 }else{
sstabellini@21997 975 pcidev->vdevfn = orig_vdev;
sstabellini@21997 976 }
gianni@22275 977 if ( do_pci_remove(&gc, domid, pcidev, force) )
sstabellini@21997 978 rc = ERROR_FAIL;
sstabellini@21997 979 }
sstabellini@21997 980 }
sstabellini@21997 981
gianni@22023 982 out:
ian@22164 983 libxl__free_all(&gc);
sstabellini@21997 984 return rc;
sstabellini@21997 985 }
sstabellini@21997 986
gianni@21938 987 int libxl_device_pci_list_assigned(libxl_ctx *ctx, libxl_device_pci **list, uint32_t domid, int *num)
gianni@21926 988 {
ian@22167 989 libxl__gc gc = LIBXL_INIT_GC(ctx);
gianni@21926 990 char *be_path, *num_devs, *xsdev, *xsvdevfn, *xsopts;
gianni@21926 991 int n, i;
gianni@21926 992 unsigned int domain = 0, bus = 0, dev = 0, func = 0, vdevfn = 0;
gianni@21926 993 libxl_device_pci *pcidevs;
gianni@21926 994
ian@22164 995 be_path = libxl__sprintf(&gc, "%s/backend/pci/%d/0", libxl__xs_get_dompath(&gc, 0), domid);
ian@22164 996 num_devs = libxl__xs_read(&gc, XBT_NULL, libxl__sprintf(&gc, "%s/num_devs", be_path));
gianni@21926 997 if (!num_devs) {
gianni@21926 998 *num = 0;
gianni@21938 999 *list = NULL;
ian@22164 1000 libxl__free_all(&gc);
gianni@21961 1001 return 0;
gianni@21926 1002 }
gianni@21926 1003 n = atoi(num_devs);
gianni@21926 1004 pcidevs = calloc(n, sizeof(libxl_device_pci));
gianni@21926 1005 *num = n;
gianni@21926 1006
gianni@21926 1007 for (i = 0; i < n; i++) {
ian@22164 1008 xsdev = libxl__xs_read(&gc, XBT_NULL, libxl__sprintf(&gc, "%s/dev-%d", be_path, i));
gianni@21926 1009 sscanf(xsdev, PCI_BDF, &domain, &bus, &dev, &func);
ian@22164 1010 xsvdevfn = libxl__xs_read(&gc, XBT_NULL, libxl__sprintf(&gc, "%s/vdevfn-%d", be_path, i));
gianni@21926 1011 if (xsvdevfn)
gianni@21926 1012 vdevfn = strtol(xsvdevfn, (char **) NULL, 16);
gianni@21962 1013 pcidev_init(pcidevs + i, domain, bus, dev, func, vdevfn);
ian@22164 1014 xsopts = libxl__xs_read(&gc, XBT_NULL, libxl__sprintf(&gc, "%s/opts-%d", be_path, i));
gianni@21926 1015 if (xsopts) {
gianni@21926 1016 char *saveptr;
gianni@21926 1017 char *p = strtok_r(xsopts, ",=", &saveptr);
gianni@21926 1018 do {
gianni@21926 1019 while (*p == ' ')
gianni@21926 1020 p++;
gianni@21926 1021 if (!strcmp(p, "msitranslate")) {
gianni@21926 1022 p = strtok_r(NULL, ",=", &saveptr);
gianni@21926 1023 pcidevs[i].msitranslate = atoi(p);
gianni@21926 1024 } else if (!strcmp(p, "power_mgmt")) {
gianni@21926 1025 p = strtok_r(NULL, ",=", &saveptr);
gianni@21926 1026 pcidevs[i].power_mgmt = atoi(p);
gianni@21926 1027 }
gianni@21926 1028 } while ((p = strtok_r(NULL, ",=", &saveptr)) != NULL);
gianni@21926 1029 }
gianni@21926 1030 }
gianni@21938 1031 if ( *num )
gianni@21938 1032 *list = pcidevs;
ian@22164 1033 libxl__free_all(&gc);
gianni@21938 1034 return 0;
gianni@21926 1035 }
gianni@21926 1036
ian@21930 1037 int libxl_device_pci_shutdown(libxl_ctx *ctx, uint32_t domid)
gianni@21926 1038 {
gianni@21926 1039 libxl_device_pci *pcidevs;
gianni@21938 1040 int num, i, rc;
gianni@21926 1041
gianni@21938 1042 rc = libxl_device_pci_list_assigned(ctx, &pcidevs, domid, &num);
gianni@21938 1043 if ( rc )
gianni@21938 1044 return rc;
gianni@21926 1045 for (i = 0; i < num; i++) {
gianni@22275 1046 /* Force remove on shutdown since, on HVM, qemu will not always
gianni@22275 1047 * respond to SCI interrupt because the guest kernel has shut down the
gianni@22275 1048 * devices by the time we even get here!
gianni@22275 1049 */
gianni@22275 1050 if (libxl_device_pci_remove(ctx, domid, pcidevs + i, 1) < 0)
gianni@21926 1051 return ERROR_FAIL;
gianni@21926 1052 }
gianni@21926 1053 free(pcidevs);
gianni@21926 1054 return 0;
gianni@21926 1055 }