ia64/linux-2.6.18-xen.hg

annotate drivers/pci/guestdev.c @ 895:20be7f6d414a

pci/guestdev, iomul: use strlcpy()

use strlcpy() to make them robust.

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