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