ia64/linux-2.6.18-xen.hg

annotate drivers/pci/guestdev.c @ 883:b998614e2e2a

pci/guestdev: enhance guestdev to accept +iomul.

enhance guestdev to accept +iomul and use it.

Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>
author Keir Fraser <keir.fraser@citrix.com>
date Thu May 28 09:59:18 2009 +0100 (2009-05-28)
parents a3ad7a5f2dcd
children 20be7f6d414a
rev   line source
keir@769 1 /*
keir@857 2 * Copyright (c) 2008, 2009 NEC Corporation.
keir@883 3 * Copyright (c) 2009 Isaku Yamahata
keir@883 4 * VA Linux Systems Japan K.K.
keir@769 5 *
keir@769 6 * This program is free software; you can redistribute it and/or modify it
keir@769 7 * under the terms and conditions of the GNU General Public License,
keir@769 8 * version 2, as published by the Free Software Foundation.
keir@769 9 *
keir@769 10 * This program is distributed in the hope it will be useful, but WITHOUT
keir@769 11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
keir@769 12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
keir@769 13 * more details.
keir@769 14 *
keir@769 15 * You should have received a copy of the GNU General Public License along with
keir@769 16 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
keir@769 17 * Place - Suite 330, Boston, MA 02111-1307 USA.
keir@769 18 */
keir@769 19
keir@769 20 #include <linux/kernel.h>
keir@769 21 #include <linux/list.h>
keir@769 22 #include <linux/mm.h>
keir@769 23 #include <linux/pci.h>
keir@769 24 #include <linux/module.h>
keir@769 25 #include <linux/string.h>
keir@769 26 #include <linux/acpi.h>
keir@769 27 #include <asm-x86_64/setup.h>
keir@769 28
keir@769 29 #define HID_LEN 8
keir@769 30 #define UID_LEN 8
keir@769 31 #define DEV_LEN 2
keir@769 32 #define FUNC_LEN 1
keir@769 33 #define DEV_NUM_MAX 31
keir@769 34 #define FUNC_NUM_MAX 7
keir@769 35 #define INVALID_SEG (-1)
keir@769 36 #define INVALID_BBN (-1)
keir@857 37 #define GUESTDEV_STR_MAX 128
keir@857 38
keir@857 39 #define GUESTDEV_FLAG_TYPE_MASK 0x3
keir@857 40 #define GUESTDEV_FLAG_DEVICEPATH 0x1
keir@857 41 #define GUESTDEV_FLAG_SBDF 0x2
keir@769 42
keir@883 43 #define GUESTDEV_OPT_IOMUL 0x1
keir@883 44
keir@769 45 struct guestdev {
keir@857 46 int flags;
keir@883 47 int options;
keir@769 48 struct list_head root_list;
keir@857 49 union {
keir@857 50 struct devicepath {
keir@857 51 char hid[HID_LEN + 1];
keir@857 52 char uid[UID_LEN + 1];
keir@857 53 int seg;
keir@857 54 int bbn;
keir@857 55 struct devicepath_node *child;
keir@857 56 } devicepath;
keir@857 57 struct sbdf {
keir@857 58 int seg;
keir@857 59 int bus;
keir@857 60 int dev;
keir@857 61 int func;
keir@857 62 } sbdf;
keir@857 63 } u;
keir@769 64 };
keir@769 65
keir@857 66 struct devicepath_node {
keir@769 67 int dev;
keir@769 68 int func;
keir@857 69 struct devicepath_node *child;
keir@769 70 };
keir@769 71
keir@769 72 struct pcidev_sbdf {
keir@769 73 int seg;
keir@769 74 int bus;
keir@769 75 struct pcidev_sbdf_node *child;
keir@769 76 };
keir@769 77
keir@769 78 struct pcidev_sbdf_node {
keir@769 79 int dev;
keir@769 80 int func;
keir@769 81 struct pcidev_sbdf_node *child;
keir@769 82 };
keir@769 83
keir@769 84 static int reassign_resources = 0;
keir@769 85
keir@769 86 static char guestdev_param[COMMAND_LINE_SIZE];
keir@769 87 LIST_HEAD(guestdev_list);
keir@769 88
keir@769 89 /* Get hid and uid */
keir@857 90 static int __init pci_get_hid_uid(char *str, char *hid, char *uid)
keir@769 91 {
keir@769 92 char *sp, *ep;
keir@769 93 int len;
keir@769 94
keir@769 95 sp = str;
keir@769 96 ep = strchr(sp, ':');
keir@769 97 if (!ep) {
keir@769 98 ep = strchr(sp, '-');
keir@769 99 if (!ep)
keir@769 100 goto format_err_end;
keir@769 101 }
keir@769 102 /* hid length */
keir@769 103 len = ep - sp;
keir@769 104 if (len <= 0 || HID_LEN < len)
keir@769 105 goto format_err_end;
keir@769 106
keir@769 107 strncpy(hid, sp, len);
keir@769 108 hid[len] = '\0';
keir@769 109
keir@769 110 if (*ep == '-') { /* no uid */
keir@769 111 uid[0] = '\0';
keir@769 112 return TRUE;
keir@769 113 }
keir@769 114
keir@769 115 sp = ep + 1;
keir@769 116 ep = strchr(sp, '-');
keir@769 117 if (!ep)
keir@769 118 ep = strchr(sp, '\0');
keir@769 119
keir@769 120 /* uid length */
keir@769 121 len = ep - sp;
keir@769 122 if (len <= 0 || UID_LEN < len)
keir@769 123 goto format_err_end;
keir@769 124
keir@769 125 strncpy(uid, sp, len);
keir@769 126 uid[len] = '\0';
keir@769 127 return TRUE;
keir@769 128
keir@769 129 format_err_end:
keir@769 130 return FALSE;
keir@769 131 }
keir@769 132
keir@769 133 /* Get device and function */
keir@857 134 static int __init pci_get_dev_func(char *str, int *dev, int *func)
keir@769 135 {
keir@769 136 if (sscanf(str, "%02x.%01x", dev, func) != 2)
keir@769 137 goto format_err_end;
keir@769 138
keir@769 139 if (*dev < 0 || DEV_NUM_MAX < *dev)
keir@769 140 goto format_err_end;
keir@769 141
keir@769 142 if (*func < 0 || FUNC_NUM_MAX < *func)
keir@769 143 goto format_err_end;
keir@769 144
keir@769 145 return TRUE;
keir@769 146
keir@769 147 format_err_end:
keir@769 148 return FALSE;
keir@769 149 }
keir@769 150
keir@769 151 /* Check extended guestdev parameter format error */
keir@857 152 static int __init pci_check_extended_guestdev_format(char *str)
keir@769 153 {
keir@769 154 int flg;
keir@769 155 char *p;
keir@769 156
keir@769 157 /* Check extended format */
keir@769 158 if (strpbrk(str, "(|)") == NULL)
keir@769 159 return TRUE;
keir@769 160
keir@769 161 flg = 0;
keir@769 162 p = str;
keir@769 163 while (*p) {
keir@769 164 switch (*p) {
keir@769 165 case '(':
keir@769 166 /* Check nesting error */
keir@769 167 if (flg != 0)
keir@769 168 goto format_err_end;
keir@769 169 flg = 1;
keir@769 170 /* Check position of '(' is head or
keir@769 171 previos charactor of '(' is not '-'. */
keir@769 172 if (p == str || *(p - 1) != '-')
keir@769 173 goto format_err_end;
keir@769 174 break;
keir@769 175 case ')':
keir@769 176 /* Check nesting error */
keir@769 177 if (flg != 1)
keir@769 178 goto format_err_end;
keir@769 179 flg = 0;
keir@769 180 /* Check next charactor of ')' is not '\0' */
keir@769 181 if (*(p + 1) != '\0')
keir@769 182 goto format_err_end;
keir@769 183 break;
keir@769 184 case '|':
keir@769 185 /* Check position of '|' is outside of '(' and ')' */
keir@769 186 if (flg != 1)
keir@769 187 goto format_err_end;
keir@769 188 break;
keir@769 189 default:
keir@769 190 break;
keir@769 191 }
keir@769 192 p++;
keir@769 193 }
keir@769 194 /* Check number of '(' and ')' are not equal */
keir@769 195 if (flg != 0)
keir@769 196 goto format_err_end;
keir@769 197 return TRUE;
keir@769 198
keir@769 199 format_err_end:
keir@769 200 printk(KERN_ERR
keir@769 201 "PCI: The format of the guestdev parameter is illegal. [%s]\n",
keir@769 202 str);
keir@769 203 return FALSE;
keir@769 204 }
keir@769 205
keir@769 206 /* Make guestdev strings */
keir@857 207 static void pci_make_guestdev_str(struct guestdev *gdev,
keir@769 208 char *gdev_str, int buf_size)
keir@769 209 {
keir@857 210 struct devicepath_node *node;
keir@857 211 int count;
keir@769 212
keir@857 213 switch (gdev->flags & GUESTDEV_FLAG_TYPE_MASK) {
keir@857 214 case GUESTDEV_FLAG_DEVICEPATH:
keir@857 215 memset(gdev_str, 0, buf_size);
keir@769 216
keir@857 217 if (strlen(gdev->u.devicepath.uid))
keir@857 218 count = snprintf(gdev_str, buf_size, "%s:%s",
keir@857 219 gdev->u.devicepath.hid,
keir@857 220 gdev->u.devicepath.uid);
keir@857 221 else
keir@857 222 count = snprintf(gdev_str, buf_size, "%s",
keir@857 223 gdev->u.devicepath.hid);
keir@857 224 if (count < 0)
keir@769 225 return;
keir@857 226
keir@857 227 node = gdev->u.devicepath.child;
keir@857 228 while (node) {
keir@857 229 gdev_str += count;
keir@857 230 buf_size -= count;
keir@857 231 if (buf_size <= 0)
keir@857 232 return;
keir@857 233 count = snprintf(gdev_str, buf_size, "-%02x.%01x",
keir@857 234 node->dev, node->func);
keir@857 235 if (count < 0)
keir@857 236 return;
keir@857 237 node = node->child;
keir@857 238 }
keir@857 239 break;
keir@857 240 case GUESTDEV_FLAG_SBDF:
keir@857 241 snprintf(gdev_str, buf_size, "%04x:%02x:%02x.%01x",
keir@857 242 gdev->u.sbdf.seg, gdev->u.sbdf.bus,
keir@857 243 gdev->u.sbdf.dev, gdev->u.sbdf.func);
keir@857 244 break;
keir@857 245 default:
keir@857 246 BUG();
keir@769 247 }
keir@769 248 }
keir@769 249
keir@769 250 /* Free guestdev and nodes */
keir@857 251 static void __init pci_free_guestdev(struct guestdev *gdev)
keir@769 252 {
keir@857 253 struct devicepath_node *node, *next;
keir@769 254
keir@769 255 if (!gdev)
keir@769 256 return;
keir@857 257 if (gdev->flags & GUESTDEV_FLAG_DEVICEPATH) {
keir@857 258 node = gdev->u.devicepath.child;
keir@857 259 while (node) {
keir@857 260 next = node->child;
keir@857 261 kfree(node);
keir@857 262 node = next;
keir@857 263 }
keir@769 264 }
keir@769 265 list_del(&gdev->root_list);
keir@769 266 kfree(gdev);
keir@769 267 }
keir@769 268
keir@769 269 /* Copy guestdev and nodes */
keir@857 270 struct guestdev __init *pci_copy_guestdev(struct guestdev *gdev_src)
keir@769 271 {
keir@769 272 struct guestdev *gdev;
keir@857 273 struct devicepath_node *node, *node_src, *node_upper;
keir@857 274
keir@857 275 BUG_ON(!(gdev_src->flags & GUESTDEV_FLAG_DEVICEPATH));
keir@769 276
keir@769 277 gdev = kmalloc(sizeof(*gdev), GFP_KERNEL);
keir@769 278 if (!gdev)
keir@769 279 goto allocate_err_end;
keir@769 280
keir@769 281 memset(gdev, 0, sizeof(*gdev));
keir@769 282 INIT_LIST_HEAD(&gdev->root_list);
keir@857 283 gdev->flags = gdev_src->flags;
keir@883 284 gdev->options = gdev_src->options;
keir@857 285 strcpy(gdev->u.devicepath.hid, gdev_src->u.devicepath.hid);
keir@857 286 strcpy(gdev->u.devicepath.uid, gdev_src->u.devicepath.uid);
keir@857 287 gdev->u.devicepath.seg = gdev_src->u.devicepath.seg;
keir@857 288 gdev->u.devicepath.bbn = gdev_src->u.devicepath.bbn;
keir@769 289
keir@769 290 node_upper = NULL;
keir@769 291
keir@857 292 node_src = gdev_src->u.devicepath.child;
keir@769 293 while (node_src) {
keir@769 294 node = kmalloc(sizeof(*node), GFP_KERNEL);
keir@769 295 if (!node)
keir@769 296 goto allocate_err_end;
keir@769 297 memset(node, 0, sizeof(*node));
keir@769 298 node->dev = node_src->dev;
keir@769 299 node->func = node_src->func;
keir@769 300 if (!node_upper)
keir@857 301 gdev->u.devicepath.child = node;
keir@769 302 else
keir@769 303 node_upper->child = node;
keir@769 304 node_upper = node;
keir@769 305 node_src = node_src->child;
keir@769 306 }
keir@769 307
keir@769 308 return gdev;
keir@769 309
keir@769 310 allocate_err_end:
keir@769 311 if (gdev)
keir@769 312 pci_free_guestdev(gdev);
keir@769 313 printk(KERN_ERR "PCI: Failed to allocate memory.\n");
keir@769 314 return NULL;
keir@769 315 }
keir@769 316
keir@769 317 /* Make guestdev from path strings */
keir@883 318 static int __init pci_make_devicepath_guestdev(char *path_str, int options)
keir@769 319 {
keir@769 320 char hid[HID_LEN + 1], uid[UID_LEN + 1];
keir@769 321 char *sp, *ep;
keir@769 322 struct guestdev *gdev, *gdev_org;
keir@857 323 struct devicepath_node *node, *node_tmp;
keir@769 324 int dev, func, ret_val;
keir@769 325
keir@769 326 ret_val = 0;
keir@769 327 gdev = gdev_org = NULL;
keir@769 328 sp = path_str;
keir@769 329 /* Look for end of hid:uid'-' */
keir@769 330 ep = strchr(sp, '-');
keir@769 331 /* Only hid, uid. (No dev, func) */
keir@769 332 if (!ep)
keir@769 333 goto format_err_end;
keir@769 334
keir@769 335 memset(hid, 0 ,sizeof(hid));
keir@769 336 memset(uid, 0, sizeof(uid));
keir@769 337 if (!pci_get_hid_uid(sp, hid, uid))
keir@769 338 goto format_err_end;
keir@769 339
keir@769 340 gdev_org = kmalloc(sizeof(*gdev_org), GFP_KERNEL);
keir@769 341 if (!gdev_org)
keir@769 342 goto allocate_err_end;
keir@769 343 memset(gdev_org, 0, sizeof(*gdev_org));
keir@769 344 INIT_LIST_HEAD(&gdev_org->root_list);
keir@857 345 gdev_org->flags = GUESTDEV_FLAG_DEVICEPATH;
keir@883 346 gdev_org->options = options;
keir@857 347 strcpy(gdev_org->u.devicepath.hid, hid);
keir@857 348 strcpy(gdev_org->u.devicepath.uid, uid);
keir@857 349 gdev_org->u.devicepath.seg = INVALID_SEG;
keir@857 350 gdev_org->u.devicepath.bbn = INVALID_BBN;
keir@769 351
keir@769 352 gdev = gdev_org;
keir@769 353
keir@769 354 sp = ep + 1;
keir@769 355 ep = sp;
keir@769 356 do {
keir@769 357 if (*sp == '(') {
keir@769 358 sp++;
keir@769 359 if (strchr(sp, '|')) {
keir@769 360 gdev = pci_copy_guestdev(gdev_org);
keir@769 361 if (!gdev) {
keir@769 362 ret_val = -ENOMEM;
keir@857 363 goto end;
keir@769 364 }
keir@769 365 }
keir@769 366 continue;
keir@769 367 }
keir@857 368 if (gdev && pci_get_dev_func(sp, &dev, &func)) {
keir@769 369 node = kmalloc(sizeof(*node), GFP_KERNEL);
keir@769 370 if (!node)
keir@769 371 goto allocate_err_end;
keir@769 372 memset(node, 0, sizeof(*node));
keir@769 373 node->dev = dev;
keir@769 374 node->func = func;
keir@769 375 /* add node to end of guestdev */
keir@857 376 if (gdev->u.devicepath.child) {
keir@857 377 node_tmp = gdev->u.devicepath.child;
keir@769 378 while (node_tmp->child) {
keir@769 379 node_tmp = node_tmp->child;
keir@769 380 }
keir@769 381 node_tmp->child = node;
keir@769 382 } else
keir@857 383 gdev->u.devicepath.child = node;
keir@857 384 } else if (gdev) {
keir@857 385 printk(KERN_ERR
keir@857 386 "PCI: Can't obtain dev# and #func# from %s.\n",
keir@857 387 sp);
keir@857 388 ret_val = -EINVAL;
keir@857 389 if (gdev == gdev_org)
keir@857 390 goto end;
keir@857 391 pci_free_guestdev(gdev);
keir@857 392 gdev = NULL;
keir@857 393 }
keir@769 394
keir@769 395 ep = strpbrk(sp, "-|)");
keir@769 396 if (!ep)
keir@769 397 ep = strchr(sp, '\0');
keir@857 398 /* Is *ep '|' OR ')' OR '\0' ? */
keir@769 399 if (*ep != '-') {
keir@857 400 if (gdev)
keir@857 401 list_add_tail(&gdev->root_list, &guestdev_list);
keir@769 402 if (*ep == '|') {
keir@769 403 /* Between '|' and '|' ? */
keir@769 404 if (strchr(ep + 1, '|')) {
keir@769 405 gdev = pci_copy_guestdev(gdev_org);
keir@769 406 if (!gdev) {
keir@769 407 ret_val = -ENOMEM;
keir@857 408 goto end;
keir@769 409 }
keir@857 410 } else {
keir@769 411 gdev = gdev_org;
keir@857 412 gdev_org = NULL;
keir@857 413 }
keir@857 414 } else {
keir@857 415 gdev_org = NULL;
keir@857 416 gdev = NULL;
keir@769 417 }
keir@769 418 }
keir@769 419 if (*ep == ')')
keir@769 420 ep++;
keir@769 421 sp = ep + 1;
keir@769 422 } while (*ep != '\0');
keir@769 423
keir@857 424 goto end;
keir@769 425
keir@769 426 format_err_end:
keir@769 427 printk(KERN_ERR
keir@769 428 "PCI: The format of the guestdev parameter is illegal. [%s]\n",
keir@769 429 path_str);
keir@769 430 ret_val = -EINVAL;
keir@857 431 goto end;
keir@769 432
keir@769 433 allocate_err_end:
keir@769 434 printk(KERN_ERR "PCI: Failed to allocate memory.\n");
keir@769 435 ret_val = -ENOMEM;
keir@857 436 goto end;
keir@769 437
keir@857 438 end:
keir@769 439 if (gdev_org && (gdev_org != gdev))
keir@769 440 pci_free_guestdev(gdev_org);
keir@769 441 if (gdev)
keir@769 442 pci_free_guestdev(gdev);
keir@769 443 return ret_val;
keir@769 444 }
keir@769 445
keir@883 446 static int __init pci_make_sbdf_guestdev(char* str, int options)
keir@857 447 {
keir@857 448 struct guestdev *gdev;
keir@857 449 int seg, bus, dev, func;
keir@857 450
keir@857 451 if (sscanf(str, "%x:%x:%x.%x", &seg, &bus, &dev, &func) != 4) {
keir@857 452 seg = 0;
keir@857 453 if (sscanf(str, "%x:%x.%x", &bus, &dev, &func) != 3)
keir@857 454 return -EINVAL;
keir@857 455 }
keir@857 456 gdev = kmalloc(sizeof(*gdev), GFP_KERNEL);
keir@857 457 if (!gdev) {
keir@857 458 printk(KERN_ERR "PCI: Failed to allocate memory.\n");
keir@857 459 return -ENOMEM;
keir@857 460 }
keir@857 461 INIT_LIST_HEAD(&gdev->root_list);
keir@857 462 gdev->flags = GUESTDEV_FLAG_SBDF;
keir@883 463 gdev->options = options;
keir@857 464 gdev->u.sbdf.seg = seg;
keir@857 465 gdev->u.sbdf.bus = bus;
keir@857 466 gdev->u.sbdf.dev = dev;
keir@857 467 gdev->u.sbdf.func = func;
keir@857 468 list_add_tail(&gdev->root_list, &guestdev_list);
keir@857 469 return 0;
keir@857 470 }
keir@857 471
keir@883 472 static int __init pci_parse_options(const char *str)
keir@883 473 {
keir@883 474 int options = 0;
keir@883 475 char *ep;
keir@883 476
keir@883 477 while (str) {
keir@883 478 str++;
keir@883 479 ep = strchr(str, '+');
keir@883 480 if (ep)
keir@883 481 ep = '\0'; /* Chop */
keir@883 482
keir@883 483 if (!strcmp(str, "iomul"))
keir@883 484 options |= GUESTDEV_OPT_IOMUL;
keir@883 485
keir@883 486 str = ep;
keir@883 487 }
keir@883 488 return options;
keir@883 489 }
keir@883 490
keir@769 491 /* Parse guestdev parameter */
keir@769 492 static int __init pci_parse_guestdev(void)
keir@769 493 {
keir@857 494 int len;
keir@883 495 char *sp, *ep, *op;
keir@883 496 int options;
keir@769 497 struct list_head *head;
keir@769 498 struct guestdev *gdev;
keir@857 499 char path_str[GUESTDEV_STR_MAX];
keir@857 500 int ret_val = 0;
keir@769 501
keir@769 502 len = strlen(guestdev_param);
keir@769 503 if (len == 0)
keir@857 504 return 0;
keir@769 505
keir@769 506 sp = guestdev_param;
keir@769 507
keir@769 508 do {
keir@769 509 ep = strchr(sp, ',');
keir@769 510 /* Chop */
keir@769 511 if (ep)
keir@769 512 *ep = '\0';
keir@883 513 options = 0;
keir@883 514 op = strchr(sp, '+');
keir@883 515 if (op && (!ep || op < ep)) {
keir@883 516 options = pci_parse_options(op);
keir@883 517 *op = '\0'; /* Chop */
keir@883 518 }
keir@883 519 ret_val = pci_make_sbdf_guestdev(sp, options);
keir@857 520 if (ret_val == -EINVAL) {
keir@857 521 if (pci_check_extended_guestdev_format(sp)) {
keir@883 522 ret_val = pci_make_devicepath_guestdev(
keir@883 523 sp, options);
keir@857 524 if (ret_val && ret_val != -EINVAL)
keir@857 525 break;
keir@857 526 }
keir@857 527 } else if (ret_val)
keir@857 528 break;
keir@883 529
keir@883 530 if (ep)
keir@883 531 ep++;
keir@883 532 sp = ep;
keir@769 533 } while (ep);
keir@769 534
keir@769 535 list_for_each(head, &guestdev_list) {
keir@769 536 gdev = list_entry(head, struct guestdev, root_list);
keir@857 537 pci_make_guestdev_str(gdev, path_str, GUESTDEV_STR_MAX);
keir@769 538 printk(KERN_DEBUG
keir@769 539 "PCI: %s has been reserved for guest domain.\n",
keir@769 540 path_str);
keir@769 541 }
keir@857 542 return 0;
keir@769 543 }
keir@769 544
keir@769 545 arch_initcall(pci_parse_guestdev);
keir@769 546
keir@769 547 /* Get command line */
keir@769 548 static int __init pci_guestdev_setup(char *str)
keir@769 549 {
keir@769 550 if (strlen(str) >= COMMAND_LINE_SIZE)
keir@769 551 return 0;
keir@769 552 strcpy(guestdev_param, str);
keir@769 553 return 1;
keir@769 554 }
keir@769 555
keir@769 556 __setup("guestdev=", pci_guestdev_setup);
keir@769 557
keir@769 558 /* Free sbdf and nodes */
keir@769 559 static void pci_free_sbdf(struct pcidev_sbdf *sbdf)
keir@769 560 {
keir@769 561 struct pcidev_sbdf_node *node, *next;
keir@769 562
keir@769 563 node = sbdf->child;
keir@769 564 while (node) {
keir@769 565 next = node->child;
keir@769 566 kfree(node);
keir@769 567 node = next;
keir@769 568 }
keir@769 569 /* Skip kfree(sbdf) */
keir@769 570 }
keir@769 571
keir@857 572 /* Does PCI device belong to sub tree specified by guestdev with device path? */
keir@883 573 typedef int (*pci_node_match_t)(const struct devicepath_node *gdev_node,
keir@883 574 const struct pcidev_sbdf_node *sbdf_node,
keir@883 575 int options);
keir@883 576
keir@883 577 static int pci_node_match(const struct devicepath_node *gdev_node,
keir@883 578 const struct pcidev_sbdf_node *sbdf_node,
keir@883 579 int options_unused)
keir@883 580 {
keir@883 581 return (gdev_node->dev == sbdf_node->dev &&
keir@883 582 gdev_node->func == sbdf_node->func);
keir@883 583 }
keir@883 584
keir@857 585 static int pci_is_in_devicepath_sub_tree(struct guestdev *gdev,
keir@883 586 struct pcidev_sbdf *sbdf,
keir@883 587 pci_node_match_t match)
keir@769 588 {
keir@769 589 int seg, bbn;
keir@857 590 struct devicepath_node *gdev_node;
keir@769 591 struct pcidev_sbdf_node *sbdf_node;
keir@769 592
keir@769 593 if (!gdev || !sbdf)
keir@769 594 return FALSE;
keir@769 595
keir@857 596 BUG_ON(!(gdev->flags & GUESTDEV_FLAG_DEVICEPATH));
keir@857 597
keir@769 598 /* Compare seg and bbn */
keir@857 599 if (gdev->u.devicepath.seg == INVALID_SEG ||
keir@857 600 gdev->u.devicepath.bbn == INVALID_BBN) {
keir@857 601 if (acpi_pci_get_root_seg_bbn(gdev->u.devicepath.hid,
keir@857 602 gdev->u.devicepath.uid, &seg, &bbn)) {
keir@857 603 gdev->u.devicepath.seg = seg;
keir@857 604 gdev->u.devicepath.bbn = bbn;
keir@769 605 } else
keir@769 606 return FALSE;
keir@769 607 }
keir@769 608
keir@857 609 if (gdev->u.devicepath.seg != sbdf->seg ||
keir@857 610 gdev->u.devicepath.bbn != sbdf->bus)
keir@769 611 return FALSE;
keir@769 612
keir@857 613 gdev_node = gdev->u.devicepath.child;
keir@769 614 sbdf_node = sbdf->child;
keir@769 615
keir@769 616 /* Compare dev and func */
keir@769 617 while (gdev_node) {
keir@769 618 if (!sbdf_node)
keir@769 619 return FALSE;
keir@883 620 if (!match(gdev_node, sbdf_node, gdev->options))
keir@769 621 return FALSE;
keir@769 622 gdev_node = gdev_node->child;
keir@769 623 sbdf_node = sbdf_node->child;
keir@769 624 }
keir@769 625 return TRUE;
keir@769 626 }
keir@769 627
keir@769 628 /* Get sbdf from device */
keir@769 629 static int pci_get_sbdf_from_pcidev(
keir@769 630 struct pci_dev *dev, struct pcidev_sbdf *sbdf)
keir@769 631 {
keir@769 632 struct pcidev_sbdf_node *node;
keir@769 633
keir@769 634 if (!dev)
keir@769 635 return FALSE;
keir@769 636
keir@769 637 for(;;) {
keir@769 638 node = kmalloc(sizeof(*node), GFP_KERNEL);
keir@769 639 if (!node) {
keir@769 640 printk(KERN_ERR "PCI: Failed to allocate memory.\n");
keir@769 641 goto err_end;
keir@769 642 }
keir@769 643 memset(node, 0, sizeof(*node));
keir@769 644 node->dev = PCI_SLOT(dev->devfn);
keir@769 645 node->func = PCI_FUNC(dev->devfn);
keir@769 646
keir@769 647 if (!sbdf->child)
keir@769 648 sbdf->child = node;
keir@769 649 else {
keir@769 650 node->child = sbdf->child;
keir@769 651 sbdf->child = node;
keir@769 652 }
keir@769 653 if (!dev->bus)
keir@769 654 goto err_end;
keir@769 655 if (!dev->bus->self)
keir@769 656 break;
keir@769 657 dev = dev->bus->self;
keir@769 658 }
keir@769 659 if (sscanf(dev->dev.bus_id, "%04x:%02x", &sbdf->seg, &sbdf->bus) != 2)
keir@769 660 goto err_end;
keir@769 661 return TRUE;
keir@769 662
keir@769 663 err_end:
keir@769 664 pci_free_sbdf(sbdf);
keir@769 665 return FALSE;
keir@769 666 }
keir@769 667
keir@857 668 /* Does PCI device belong to sub tree specified by guestdev with sbdf? */
keir@883 669 typedef int (*pci_sbdf_match_t)(const struct guestdev *gdev,
keir@883 670 const struct pci_dev *dev);
keir@883 671
keir@883 672 static int pci_sbdf_match(const struct guestdev *gdev,
keir@883 673 const struct pci_dev *dev)
keir@857 674 {
keir@857 675 int seg, bus;
keir@883 676
keir@883 677 if (sscanf(dev->dev.bus_id, "%04x:%02x", &seg, &bus) != 2)
keir@883 678 return FALSE;
keir@883 679
keir@883 680 return gdev->u.sbdf.seg == seg &&
keir@883 681 gdev->u.sbdf.bus == bus &&
keir@883 682 gdev->u.sbdf.dev == PCI_SLOT(dev->devfn) &&
keir@883 683 gdev->u.sbdf.func == PCI_FUNC(dev->devfn);
keir@883 684 }
keir@883 685
keir@883 686 static int pci_is_in_sbdf_sub_tree(struct guestdev *gdev, struct pci_dev *dev,
keir@883 687 pci_sbdf_match_t match)
keir@883 688 {
keir@857 689 BUG_ON(!(gdev->flags & GUESTDEV_FLAG_SBDF));
keir@857 690 for (;;) {
keir@883 691 if (match(gdev, dev))
keir@857 692 return TRUE;
keir@857 693 if (!dev->bus || !dev->bus->self)
keir@857 694 break;
keir@857 695 dev = dev->bus->self;
keir@857 696 }
keir@857 697 return FALSE;
keir@857 698 }
keir@857 699
keir@857 700 /* Does PCI device belong to sub tree specified by guestdev parameter? */
keir@883 701 static int __pci_is_guestdev(struct pci_dev *dev, pci_node_match_t node_match,
keir@883 702 pci_sbdf_match_t sbdf_match)
keir@769 703 {
keir@769 704 struct guestdev *gdev;
keir@857 705 struct pcidev_sbdf pcidev_sbdf, *sbdf = NULL;
keir@769 706 struct list_head *head;
keir@857 707 int result = FALSE;
keir@769 708
keir@769 709 if (!dev)
keir@769 710 return FALSE;
keir@769 711
keir@769 712 list_for_each(head, &guestdev_list) {
keir@769 713 gdev = list_entry(head, struct guestdev, root_list);
keir@857 714 switch (gdev->flags & GUESTDEV_FLAG_TYPE_MASK) {
keir@857 715 case GUESTDEV_FLAG_DEVICEPATH:
keir@857 716 if (sbdf == NULL) {
keir@857 717 sbdf = &pcidev_sbdf;
keir@857 718 memset(sbdf, 0 ,sizeof(*sbdf));
keir@857 719 if (!pci_get_sbdf_from_pcidev(dev, sbdf))
keir@857 720 goto out;
keir@857 721 }
keir@883 722 if (pci_is_in_devicepath_sub_tree(gdev, sbdf,
keir@883 723 node_match)) {
keir@857 724 result = TRUE;
keir@857 725 goto out;
keir@857 726 }
keir@769 727 break;
keir@857 728 case GUESTDEV_FLAG_SBDF:
keir@883 729 if (pci_is_in_sbdf_sub_tree(gdev, dev, sbdf_match)) {
keir@857 730 result = TRUE;
keir@857 731 goto out;
keir@857 732 }
keir@857 733 break;
keir@857 734 default:
keir@857 735 BUG();
keir@769 736 }
keir@769 737 }
keir@857 738 out:
keir@857 739 if (sbdf)
keir@857 740 pci_free_sbdf(sbdf);
keir@769 741 return result;
keir@769 742 }
keir@883 743
keir@883 744 int pci_is_guestdev(struct pci_dev *dev)
keir@883 745 {
keir@883 746 return __pci_is_guestdev(dev, pci_node_match, pci_sbdf_match);
keir@883 747 }
keir@769 748 EXPORT_SYMBOL(pci_is_guestdev);
keir@769 749
keir@769 750 static int __init pci_set_reassign_resources(char *str)
keir@769 751 {
keir@769 752 reassign_resources = 1;
keir@769 753
keir@769 754 return 1;
keir@769 755 }
keir@769 756
keir@769 757 __setup("reassign_resources", pci_set_reassign_resources);
keir@769 758
keir@857 759 int pci_is_reassigndev(struct pci_dev *dev)
keir@769 760 {
keir@769 761 if (reassign_resources)
keir@769 762 return pci_is_guestdev(dev);
keir@769 763 return FALSE;
keir@769 764 }
keir@857 765 EXPORT_SYMBOL(pci_is_reassigndev);
keir@769 766
keir@883 767 #ifdef CONFIG_PCI_IOMULTI
keir@883 768 static int pci_iomul_node_match(const struct devicepath_node *gdev_node,
keir@883 769 const struct pcidev_sbdf_node *sbdf_node,
keir@883 770 int options)
keir@883 771 {
keir@883 772 return (options & GUESTDEV_OPT_IOMUL) &&
keir@883 773 ((gdev_node->child != NULL &&
keir@883 774 sbdf_node->child != NULL &&
keir@883 775 gdev_node->dev == sbdf_node->dev &&
keir@883 776 gdev_node->func == sbdf_node->func) ||
keir@883 777 (gdev_node->child == NULL &&
keir@883 778 sbdf_node->child == NULL &&
keir@883 779 gdev_node->dev == sbdf_node->dev));
keir@883 780 }
keir@883 781
keir@883 782 static int pci_iomul_sbdf_match(const struct guestdev *gdev,
keir@883 783 const struct pci_dev *dev)
keir@883 784 {
keir@883 785 int seg, bus;
keir@883 786
keir@883 787 if (sscanf(dev->dev.bus_id, "%04x:%02x", &seg, &bus) != 2)
keir@883 788 return FALSE;
keir@883 789
keir@883 790 return (gdev->options & GUESTDEV_OPT_IOMUL) &&
keir@883 791 gdev->u.sbdf.seg == seg &&
keir@883 792 gdev->u.sbdf.bus == bus &&
keir@883 793 gdev->u.sbdf.dev == PCI_SLOT(dev->devfn);
keir@883 794 }
keir@883 795
keir@883 796 int pci_is_iomuldev(struct pci_dev *dev)
keir@883 797 {
keir@883 798 return __pci_is_guestdev(dev,
keir@883 799 pci_iomul_node_match, pci_iomul_sbdf_match);
keir@883 800 }
keir@883 801 EXPORT_SYMBOL_GPL(pci_is_iomuldev);
keir@883 802 #endif /* CONFIG_PCI_IOMULTI */
keir@883 803
keir@857 804 /* Check whether the devicepath exists under the pci root bus */
keir@857 805 static int __init pci_check_devicepath_exists(
keir@769 806 struct guestdev *gdev, struct pci_bus *bus)
keir@769 807 {
keir@857 808 struct devicepath_node *node;
keir@769 809 struct pci_dev *dev;
keir@769 810
keir@857 811 BUG_ON(!(gdev->flags & GUESTDEV_FLAG_DEVICEPATH));
keir@857 812
keir@857 813 node = gdev->u.devicepath.child;
keir@769 814 while (node) {
keir@769 815 if (!bus)
keir@769 816 return FALSE;
keir@769 817 dev = pci_get_slot(bus, PCI_DEVFN(node->dev, node->func));
keir@857 818 if (!dev)
keir@769 819 return FALSE;
keir@769 820 bus = dev->subordinate;
keir@769 821 node = node->child;
keir@769 822 pci_dev_put(dev);
keir@769 823 }
keir@769 824 return TRUE;
keir@769 825 }
keir@769 826
keir@769 827 /* Check whether the guestdev exists in the PCI device tree */
keir@769 828 static int __init pci_check_guestdev_exists(void)
keir@769 829 {
keir@769 830 struct list_head *head;
keir@769 831 struct guestdev *gdev;
keir@769 832 int seg, bbn;
keir@769 833 struct pci_bus *bus;
keir@857 834 struct pci_dev *dev;
keir@857 835 char path_str[GUESTDEV_STR_MAX];
keir@769 836
keir@769 837 list_for_each(head, &guestdev_list) {
keir@769 838 gdev = list_entry(head, struct guestdev, root_list);
keir@857 839 switch (gdev->flags & GUESTDEV_FLAG_TYPE_MASK) {
keir@857 840 case GUESTDEV_FLAG_DEVICEPATH:
keir@857 841 if (gdev->u.devicepath.seg == INVALID_SEG ||
keir@857 842 gdev->u.devicepath.bbn == INVALID_BBN) {
keir@857 843 if (acpi_pci_get_root_seg_bbn(
keir@857 844 gdev->u.devicepath.hid,
keir@857 845 gdev->u.devicepath.uid, &seg, &bbn)) {
keir@857 846 gdev->u.devicepath.seg = seg;
keir@857 847 gdev->u.devicepath.bbn = bbn;
keir@857 848 } else {
keir@857 849 pci_make_guestdev_str(gdev,
keir@857 850 path_str, GUESTDEV_STR_MAX);
keir@857 851 printk(KERN_INFO
keir@857 852 "PCI: Device does not exist. %s\n",
keir@857 853 path_str);
keir@857 854 continue;
keir@857 855 }
keir@857 856 }
keir@857 857
keir@857 858 bus = pci_find_bus(gdev->u.devicepath.seg,
keir@857 859 gdev->u.devicepath.bbn);
keir@857 860 if (!bus ||
keir@857 861 !pci_check_devicepath_exists(gdev, bus)) {
keir@857 862 pci_make_guestdev_str(gdev, path_str,
keir@857 863 GUESTDEV_STR_MAX);
keir@769 864 printk(KERN_INFO
keir@769 865 "PCI: Device does not exist. %s\n",
keir@769 866 path_str);
keir@769 867 }
keir@857 868 break;
keir@857 869 case GUESTDEV_FLAG_SBDF:
keir@857 870 bus = pci_find_bus(gdev->u.sbdf.seg, gdev->u.sbdf.bus);
keir@857 871 if (bus) {
keir@857 872 dev = pci_get_slot(bus,
keir@857 873 PCI_DEVFN(gdev->u.sbdf.dev,
keir@857 874 gdev->u.sbdf.func));
keir@857 875 if (dev) {
keir@857 876 pci_dev_put(dev);
keir@857 877 continue;
keir@857 878 }
keir@857 879 }
keir@857 880 pci_make_guestdev_str(gdev, path_str, GUESTDEV_STR_MAX);
keir@857 881 printk(KERN_INFO "PCI: Device does not exist. %s\n",
keir@857 882 path_str);
keir@857 883 break;
keir@857 884 default:
keir@857 885 BUG();
keir@769 886 }
keir@769 887 }
keir@769 888 return 0;
keir@769 889 }
keir@769 890
keir@769 891 fs_initcall(pci_check_guestdev_exists);
keir@769 892