1 /*- 2 * Copyright (c) 2017 Netflix, Inc. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 * SUCH DAMAGE. 24 */ 25 26 #include <sys/param.h> 27 #include <ctype.h> 28 #include <devinfo.h> 29 #include <err.h> 30 #include <errno.h> 31 #include <fcntl.h> 32 #include <getopt.h> 33 #include <stdio.h> 34 #include <stdlib.h> 35 #include <string.h> 36 #include <sysexits.h> 37 #include <unistd.h> 38 #include <sys/linker.h> 39 #include <sys/module.h> 40 #include <sys/stat.h> 41 #include <sys/sysctl.h> 42 43 /* options descriptor */ 44 static struct option longopts[] = { 45 { "all", no_argument, NULL, 'a' }, 46 { "dump", no_argument, NULL, 'd' }, 47 { "hints", required_argument, NULL, 'h' }, 48 { "nomatch", required_argument, NULL, 'p' }, 49 { "quiet", no_argument, NULL, 'q' }, 50 { "unbound", no_argument, NULL, 'u' }, 51 { "verbose", no_argument, NULL, 'v' }, 52 { NULL, 0, NULL, 0 } 53 }; 54 55 #define DEVMATCH_MAX_HITS 256 56 57 static int all_flag; 58 static int dump_flag; 59 static char *linker_hints; 60 static char *nomatch_str; 61 static int quiet_flag; 62 static int unbound_flag; 63 static int verbose_flag; 64 65 static void *hints; 66 static void *hints_end; 67 static struct devinfo_dev *root; 68 69 static void * 70 read_hints(const char *fn, size_t *len) 71 { 72 void *h; 73 int fd; 74 struct stat sb; 75 76 fd = open(fn, O_RDONLY); 77 if (fd < 0) { 78 if (errno == ENOENT) 79 return NULL; 80 err(1, "Can't open %s for reading", fn); 81 } 82 if (fstat(fd, &sb) != 0) 83 err(1, "Can't fstat %s\n", fn); 84 h = malloc(sb.st_size); 85 if (h == NULL) 86 err(1, "not enough space to read hints file of %ju bytes", (uintmax_t)sb.st_size); 87 if (read(fd, h, sb.st_size) != sb.st_size) 88 err(1, "Can't read in %ju bytes from %s", (uintmax_t)sb.st_size, fn); 89 close(fd); 90 *len = sb.st_size; 91 return h; 92 } 93 94 static void 95 read_linker_hints(void) 96 { 97 char fn[MAXPATHLEN]; 98 char *modpath, *p, *q; 99 size_t buflen, len; 100 101 if (linker_hints == NULL) { 102 if (sysctlbyname("kern.module_path", NULL, &buflen, NULL, 0) < 0) 103 errx(1, "Can't find kernel module path."); 104 modpath = malloc(buflen); 105 if (modpath == NULL) 106 err(1, "Can't get memory for modpath."); 107 if (sysctlbyname("kern.module_path", modpath, &buflen, NULL, 0) < 0) 108 errx(1, "Can't find kernel module path."); 109 p = modpath; 110 while ((q = strsep(&p, ";")) != NULL) { 111 snprintf(fn, sizeof(fn), "%s/linker.hints", q); 112 hints = read_hints(fn, &len); 113 if (hints == NULL) 114 continue; 115 break; 116 } 117 if (q == NULL) { 118 if (quiet_flag) 119 exit(EX_UNAVAILABLE); 120 else 121 errx(EX_UNAVAILABLE, "Can't read linker hints file."); 122 } 123 } else { 124 hints = read_hints(linker_hints, &len); 125 if (hints == NULL) 126 err(1, "Can't open %s for reading", fn); 127 } 128 129 if (*(int *)(intptr_t)hints != LINKER_HINTS_VERSION) { 130 warnx("Linker hints version %d doesn't match expected %d.", 131 *(int *)(intptr_t)hints, LINKER_HINTS_VERSION); 132 free(hints); 133 hints = NULL; 134 } 135 if (hints != NULL) 136 hints_end = (void *)((intptr_t)hints + (intptr_t)len); 137 } 138 139 static int 140 getint(void **ptr) 141 { 142 int *p = *ptr; 143 int rv; 144 145 p = (int *)roundup2((intptr_t)p, sizeof(int)); 146 rv = *p++; 147 *ptr = p; 148 return rv; 149 } 150 151 static void 152 getstr(void **ptr, char *val) 153 { 154 int *p = *ptr; 155 char *c = (char *)p; 156 int len = *(uint8_t *)c; 157 158 memcpy(val, c + 1, len); 159 val[len] = 0; 160 c += len + 1; 161 *ptr = (void *)c; 162 } 163 164 static int 165 pnpval_as_int(const char *val, const char *pnpinfo) 166 { 167 int rv; 168 char key[256]; 169 char *cp; 170 171 if (pnpinfo == NULL) 172 return -1; 173 174 cp = strchr(val, ';'); 175 key[0] = ' '; 176 if (cp == NULL) 177 strlcpy(key + 1, val, sizeof(key) - 1); 178 else { 179 memcpy(key + 1, val, cp - val); 180 key[cp - val + 1] = '\0'; 181 } 182 strlcat(key, "=", sizeof(key)); 183 if (strncmp(key + 1, pnpinfo, strlen(key + 1)) == 0) 184 rv = strtol(pnpinfo + strlen(key + 1), NULL, 0); 185 else { 186 cp = strstr(pnpinfo, key); 187 if (cp == NULL) 188 rv = -1; 189 else 190 rv = strtol(cp + strlen(key), NULL, 0); 191 } 192 return rv; 193 } 194 195 static void 196 quoted_strcpy(char *dst, const char *src) 197 { 198 char q = ' '; 199 200 if (*src == '\'' || *src == '"') 201 q = *src++; 202 while (*src && *src != q) 203 *dst++ = *src++; // XXX backtick quoting 204 *dst++ = '\0'; 205 // XXX overflow 206 } 207 208 static char * 209 pnpval_as_str(const char *val, const char *pnpinfo) 210 { 211 static char retval[256]; 212 char key[256]; 213 char *cp; 214 215 if (pnpinfo == NULL) { 216 *retval = '\0'; 217 return retval; 218 } 219 220 cp = strchr(val, ';'); 221 key[0] = ' '; 222 if (cp == NULL) 223 strlcpy(key + 1, val, sizeof(key) - 1); 224 else { 225 memcpy(key + 1, val, cp - val); 226 key[cp - val + 1] = '\0'; 227 } 228 strlcat(key, "=", sizeof(key)); 229 if (strncmp(key + 1, pnpinfo, strlen(key + 1)) == 0) 230 quoted_strcpy(retval, pnpinfo + strlen(key + 1)); 231 else { 232 cp = strstr(pnpinfo, key); 233 if (cp == NULL) 234 strcpy(retval, "MISSING"); 235 else 236 quoted_strcpy(retval, cp + strlen(key)); 237 } 238 return retval; 239 } 240 241 static void 242 search_hints(const char *bus, const char *dev, const char *pnpinfo) 243 { 244 char val1[256], val2[256]; 245 int ival, len, ents, i, notme, mask, bit, v, found; 246 void *ptr, *walker; 247 char *lastmod = NULL, *cp, *s; 248 249 walker = hints; 250 getint(&walker); 251 found = 0; 252 if (verbose_flag) 253 printf("Searching bus %s dev %s for pnpinfo %s\n", 254 bus, dev, pnpinfo); 255 while (walker < hints_end) { 256 len = getint(&walker); 257 ival = getint(&walker); 258 ptr = walker; 259 switch (ival) { 260 case MDT_VERSION: 261 getstr(&ptr, val1); 262 ival = getint(&ptr); 263 getstr(&ptr, val2); 264 if (dump_flag || verbose_flag) 265 printf("Version: if %s.%d kmod %s\n", val1, ival, val2); 266 break; 267 case MDT_MODULE: 268 getstr(&ptr, val1); 269 getstr(&ptr, val2); 270 if (lastmod) 271 free(lastmod); 272 lastmod = strdup(val2); 273 if (dump_flag || verbose_flag) 274 printf("Module %s in %s\n", val1, val2); 275 break; 276 case MDT_PNP_INFO: 277 if (!dump_flag && !unbound_flag && lastmod && strcmp(lastmod, "kernel") == 0) 278 break; 279 getstr(&ptr, val1); 280 getstr(&ptr, val2); 281 ents = getint(&ptr); 282 if (dump_flag || verbose_flag) 283 printf("PNP info for bus %s format %s %d entries (%s)\n", 284 val1, val2, ents, lastmod); 285 if (strcmp(val1, "usb") == 0) { 286 if (verbose_flag) 287 printf("Treating usb as uhub -- bug in source table still?\n"); 288 strcpy(val1, "uhub"); 289 } 290 if (bus && strcmp(val1, bus) != 0) { 291 if (verbose_flag) 292 printf("Skipped because table for bus %s, looking for %s\n", 293 val1, bus); 294 break; 295 } 296 for (i = 0; i < ents; i++) { 297 if (verbose_flag) 298 printf("---------- Entry %d ----------\n", i); 299 if (dump_flag) 300 printf(" "); 301 cp = val2; 302 notme = 0; 303 mask = -1; 304 bit = -1; 305 do { 306 switch (*cp) { 307 /* All integer fields */ 308 case 'I': 309 case 'J': 310 case 'G': 311 case 'L': 312 case 'M': 313 ival = getint(&ptr); 314 if (dump_flag) { 315 printf("%#x:", ival); 316 break; 317 } 318 if (bit >= 0 && ((1 << bit) & mask) == 0) 319 break; 320 if (cp[2] == '#') { 321 if (verbose_flag) { 322 printf("Ignoring %s (%c) table=%#x tomatch=%#x\n", 323 cp + 2, *cp, v, ival); 324 } 325 break; 326 } 327 v = pnpval_as_int(cp + 2, pnpinfo); 328 if (verbose_flag) 329 printf("Matching %s (%c) table=%#x tomatch=%#x\n", 330 cp + 2, *cp, v, ival); 331 switch (*cp) { 332 case 'J': 333 if (ival == -1) 334 break; 335 /*FALLTHROUGH*/ 336 case 'I': 337 if (v != ival) 338 notme++; 339 break; 340 case 'G': 341 if (v < ival) 342 notme++; 343 break; 344 case 'L': 345 if (v > ival) 346 notme++; 347 break; 348 case 'M': 349 mask = ival; 350 break; 351 } 352 break; 353 /* String fields */ 354 case 'D': 355 case 'Z': 356 getstr(&ptr, val1); 357 if (dump_flag) { 358 printf("'%s':", val1); 359 break; 360 } 361 if (*cp == 'D') 362 break; 363 if (bit >= 0 && ((1 << bit) & mask) == 0) 364 break; 365 if (cp[2] == '#') { 366 if (verbose_flag) { 367 printf("Ignoring %s (%c) table=%#x tomatch=%#x\n", 368 cp + 2, *cp, v, ival); 369 } 370 break; 371 } 372 s = pnpval_as_str(cp + 2, pnpinfo); 373 if (verbose_flag) 374 printf("Matching %s (%c) table=%s tomatch=%s\n", 375 cp + 2, *cp, s, val1); 376 if (strcmp(s, val1) != 0) 377 notme++; 378 break; 379 /* Key override fields, required to be last in the string */ 380 case 'T': 381 /* 382 * This is imperfect and only does one key and will be redone 383 * to be more general for multiple keys. Currently, nothing 384 * does that. 385 */ 386 if (dump_flag) /* No per-row data stored */ 387 break; 388 if (cp[strlen(cp) - 1] == ';') /* Skip required ; at end */ 389 cp[strlen(cp) - 1] = '\0'; /* in case it's not there */ 390 if ((s = strstr(pnpinfo, cp + 2)) == NULL) 391 notme++; 392 else if (s > pnpinfo && s[-1] != ' ') 393 notme++; 394 break; 395 default: 396 fprintf(stderr, "Unknown field type %c\n:", *cp); 397 break; 398 } 399 bit++; 400 cp = strchr(cp, ';'); 401 if (cp) 402 cp++; 403 } while (cp && *cp); 404 if (dump_flag) 405 printf("\n"); 406 else if (!notme) { 407 if (!unbound_flag) { 408 if (all_flag) 409 printf("%s: %s", *dev ? dev : "unattached", lastmod); 410 else 411 printf("%s\n", lastmod); 412 if (verbose_flag) 413 printf("Matches --- %s ---\n", lastmod); 414 } 415 found++; 416 } 417 } 418 break; 419 default: 420 if (dump_flag) 421 printf("Unknown Type %d len %d\n", ival, len); 422 break; 423 } 424 walker = (void *)(len - sizeof(int) + (intptr_t)walker); 425 } 426 if (unbound_flag && found == 0 && *pnpinfo) { 427 if (verbose_flag) 428 printf("------------------------- "); 429 printf("%s on %s pnpinfo %s", *dev ? dev : "unattached", bus, pnpinfo); 430 if (verbose_flag) 431 printf(" -------------------------"); 432 printf("\n"); 433 } 434 free(lastmod); 435 } 436 437 static int 438 find_unmatched(struct devinfo_dev *dev, void *arg) 439 { 440 struct devinfo_dev *parent; 441 char *bus, *p; 442 443 do { 444 if (!all_flag && dev->dd_name[0] != '\0') 445 break; 446 if (!(dev->dd_flags & DF_ENABLED)) 447 break; 448 if (dev->dd_flags & DF_ATTACHED_ONCE) 449 break; 450 parent = devinfo_handle_to_device(dev->dd_parent); 451 bus = strdup(parent->dd_name); 452 p = bus + strlen(bus) - 1; 453 while (p >= bus && isdigit(*p)) 454 p--; 455 *++p = '\0'; 456 if (verbose_flag) 457 printf("Searching %s %s bus at %s for pnpinfo %s\n", 458 dev->dd_name, bus, dev->dd_location, dev->dd_pnpinfo); 459 search_hints(bus, dev->dd_name, dev->dd_pnpinfo); 460 free(bus); 461 } while (0); 462 463 return (devinfo_foreach_device_child(dev, find_unmatched, arg)); 464 } 465 466 struct exact_info 467 { 468 const char *bus; 469 const char *loc; 470 struct devinfo_dev *dev; 471 }; 472 473 /* 474 * Look for the exact location specified by the nomatch event. The 475 * loc and pnpinfo run together to get the string we're looking for, 476 * so we have to synthesize the same thing that subr_bus.c is 477 * generating in devnomatch/devaddq to do the string comparison. 478 */ 479 static int 480 find_exact_dev(struct devinfo_dev *dev, void *arg) 481 { 482 struct devinfo_dev *parent; 483 char *loc; 484 struct exact_info *info; 485 486 info = arg; 487 do { 488 if (info->dev != NULL) 489 break; 490 if (!(dev->dd_flags & DF_ENABLED)) 491 break; 492 parent = devinfo_handle_to_device(dev->dd_parent); 493 if (strcmp(info->bus, parent->dd_name) != 0) 494 break; 495 asprintf(&loc, "%s %s", parent->dd_pnpinfo, 496 parent->dd_location); 497 if (strcmp(loc, info->loc) == 0) 498 info->dev = dev; 499 free(loc); 500 } while (0); 501 502 return (devinfo_foreach_device_child(dev, find_exact_dev, arg)); 503 } 504 505 static void 506 find_nomatch(char *nomatch) 507 { 508 char *bus, *pnpinfo, *tmp, *busnameunit; 509 struct exact_info info; 510 511 /* 512 * Find our bus name. It will include the unit number. We have to search 513 * backwards to avoid false positive for any PNP string that has ' on ' 514 * in them, which would come earlier in the string. Like if there were 515 * an 'Old Bard' ethernet card made by 'Stratford on Avon Hardware' or 516 * something silly like that. 517 */ 518 tmp = nomatch + strlen(nomatch) - 4; 519 while (tmp > nomatch && strncmp(tmp, " on ", 4) != 0) 520 tmp--; 521 if (tmp == nomatch) 522 errx(1, "No bus found in nomatch string: '%s'", nomatch); 523 bus = tmp + 4; 524 *tmp = '\0'; 525 busnameunit = strdup(bus); 526 if (busnameunit == NULL) 527 errx(1, "Can't allocate memory for strings"); 528 tmp = bus + strlen(bus) - 1; 529 while (tmp > bus && isdigit(*tmp)) 530 tmp--; 531 *++tmp = '\0'; 532 533 /* 534 * Note: the NOMATCH events place both the bus location as well as the 535 * pnp info after the 'at' and we don't know where one stops and the 536 * other begins, so we pass the whole thing to our search routine. 537 */ 538 if (*nomatch == '?') 539 nomatch++; 540 if (strncmp(nomatch, " at ", 4) != 0) 541 errx(1, "Malformed NOMATCH string: '%s'", nomatch); 542 pnpinfo = nomatch + 4; 543 544 /* 545 * See if we can find the devinfo_dev for this device. If we 546 * can, and it's been attached before, we should filter it out 547 * so that a kldunload foo doesn't cause an immediate reload. 548 */ 549 info.loc = pnpinfo; 550 info.bus = busnameunit; 551 info.dev = NULL; 552 devinfo_foreach_device_child(root, find_exact_dev, (void *)&info); 553 if (info.dev != NULL && info.dev->dd_flags & DF_ATTACHED_ONCE) 554 exit(0); 555 search_hints(bus, "", pnpinfo); 556 557 exit(0); 558 } 559 560 static void 561 usage(void) 562 { 563 564 errx(1, "devmatch [-adv] [-p nomatch] [-h linker-hints]"); 565 } 566 567 int 568 main(int argc, char **argv) 569 { 570 int ch; 571 572 while ((ch = getopt_long(argc, argv, "adh:p:quv", 573 longopts, NULL)) != -1) { 574 switch (ch) { 575 case 'a': 576 all_flag++; 577 break; 578 case 'd': 579 dump_flag++; 580 break; 581 case 'h': 582 linker_hints = optarg; 583 break; 584 case 'p': 585 nomatch_str = optarg; 586 break; 587 case 'q': 588 quiet_flag++; 589 break; 590 case 'u': 591 unbound_flag++; 592 break; 593 case 'v': 594 verbose_flag++; 595 break; 596 default: 597 usage(); 598 } 599 } 600 argc -= optind; 601 argv += optind; 602 603 if (argc >= 1) 604 usage(); 605 606 read_linker_hints(); 607 if (dump_flag) { 608 search_hints(NULL, NULL, NULL); 609 exit(0); 610 } 611 612 if (devinfo_init()) 613 err(1, "devinfo_init"); 614 if ((root = devinfo_handle_to_device(DEVINFO_ROOT_DEVICE)) == NULL) 615 errx(1, "can't find root device"); 616 if (nomatch_str != NULL) 617 find_nomatch(nomatch_str); 618 else 619 devinfo_foreach_device_child(root, find_unmatched, (void *)0); 620 devinfo_free(); 621 } 622