ia64/linux-2.6.18-xen.hg

annotate drivers/pci/guestdev.c @ 857:a3ad7a5f2dcd

dom0 linux: support SBDF with "guestdev=" and remove "reassigndev="

When we don't need to reassign resources and use device path,
pciback.hide= boot parameter can be used. The parameter is also needed
for backward compatibility.

pciback.hide=(00:01.0)(00:02.0)

When we need to reassign resources or use device path, guestdev= boot
parameter can be used. reassign_resources boot parameter is needed to
reassign resources, too.

guestdev=00:01.0,00:02.0 reassign_resources
guestdev=PNP0A08:0-1.0,PNP0A08:0-2.0
guestdev=PNP0A08:0-1.0,PNP0A08:0-2.0 reassign_resources

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