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 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 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, *s; 286 287 walker = hints; 288 getint(&walker); 289 found = 0; 290 if (verbose_flag) 291 printf("Searching bus %s dev %s for pnpinfo %s\n", 292 bus, dev, pnpinfo); 293 while (walker < hints_end) { 294 len = getint(&walker); 295 ival = getint(&walker); 296 ptr = walker; 297 switch (ival) { 298 case MDT_VERSION: 299 getstr(&ptr, val1); 300 ival = getint(&ptr); 301 getstr(&ptr, val2); 302 if (dump_flag || verbose_flag) 303 printf("Version: if %s.%d kmod %s\n", val1, ival, val2); 304 break; 305 case MDT_MODULE: 306 getstr(&ptr, val1); 307 getstr(&ptr, val2); 308 if (lastmod) 309 free(lastmod); 310 lastmod = strdup(val2); 311 if (dump_flag || verbose_flag) 312 printf("Module %s in %s\n", val1, val2); 313 break; 314 case MDT_PNP_INFO: 315 if (!dump_flag && !unbound_flag && lastmod && strcmp(lastmod, "kernel") == 0) 316 break; 317 getstr(&ptr, val1); 318 getstr(&ptr, val2); 319 ents = getint(&ptr); 320 if (dump_flag || verbose_flag) 321 printf("PNP info for bus %s format %s %d entries (%s)\n", 322 val1, val2, ents, lastmod); 323 if (strcmp(val1, "usb") == 0) { 324 if (verbose_flag) 325 printf("Treating usb as uhub -- bug in source table still?\n"); 326 strcpy(val1, "uhub"); 327 } 328 if (bus && strcmp(val1, bus) != 0) { 329 if (verbose_flag) 330 printf("Skipped because table for bus %s, looking for %s\n", 331 val1, bus); 332 break; 333 } 334 for (i = 0; i < ents; i++) { 335 if (verbose_flag) 336 printf("---------- Entry %d ----------\n", i); 337 if (dump_flag) 338 printf(" "); 339 cp = val2; 340 notme = 0; 341 mask = -1; 342 bit = -1; 343 do { 344 switch (*cp) { 345 /* All integer fields */ 346 case 'I': 347 case 'J': 348 case 'G': 349 case 'L': 350 case 'M': 351 ival = getint(&ptr); 352 if (dump_flag) { 353 printf("%#x:", ival); 354 break; 355 } 356 if (bit >= 0 && ((1 << bit) & mask) == 0) 357 break; 358 if (cp[2] == '#') { 359 if (verbose_flag) { 360 printf("Ignoring %s (%c) table=%#x tomatch=%#x\n", 361 cp + 2, *cp, v, ival); 362 } 363 break; 364 } 365 v = pnpval_as_int(cp + 2, pnpinfo); 366 if (verbose_flag) 367 printf("Matching %s (%c) table=%#x tomatch=%#x\n", 368 cp + 2, *cp, v, ival); 369 switch (*cp) { 370 case 'J': 371 if (ival == -1) 372 break; 373 /*FALLTHROUGH*/ 374 case 'I': 375 if (v != ival) 376 notme++; 377 break; 378 case 'G': 379 if (v < ival) 380 notme++; 381 break; 382 case 'L': 383 if (v > ival) 384 notme++; 385 break; 386 case 'M': 387 mask = ival; 388 break; 389 } 390 break; 391 /* String fields */ 392 case 'D': 393 case 'Z': 394 getstr(&ptr, val1); 395 if (dump_flag) { 396 printf("'%s':", val1); 397 break; 398 } 399 if (*cp == 'D') 400 break; 401 if (bit >= 0 && ((1 << bit) & mask) == 0) 402 break; 403 if (cp[2] == '#') { 404 if (verbose_flag) { 405 printf("Ignoring %s (%c) table=%#x tomatch=%#x\n", 406 cp + 2, *cp, v, ival); 407 } 408 break; 409 } 410 s = pnpval_as_str(cp + 2, pnpinfo); 411 if (verbose_flag) 412 printf("Matching %s (%c) table=%s tomatch=%s\n", 413 cp + 2, *cp, s, val1); 414 if (strcmp(s, val1) != 0) 415 notme++; 416 break; 417 /* Key override fields, required to be last in the string */ 418 case 'T': 419 /* 420 * This is imperfect and only does one key and will be redone 421 * to be more general for multiple keys. Currently, nothing 422 * does that. 423 */ 424 if (dump_flag) /* No per-row data stored */ 425 break; 426 if (cp[strlen(cp) - 1] == ';') /* Skip required ; at end */ 427 cp[strlen(cp) - 1] = '\0'; /* in case it's not there */ 428 if ((s = strstr(pnpinfo, cp + 2)) == NULL) 429 notme++; 430 else if (s > pnpinfo && s[-1] != ' ') 431 notme++; 432 break; 433 default: 434 fprintf(stderr, "Unknown field type %c\n:", *cp); 435 break; 436 } 437 bit++; 438 cp = strchr(cp, ';'); 439 if (cp) 440 cp++; 441 } while (cp && *cp); 442 if (dump_flag) 443 printf("\n"); 444 else if (!notme) { 445 if (!unbound_flag) { 446 if (all_flag) 447 printf("%s: %s\n", *dev ? dev : "unattached", lastmod); 448 else 449 printf("%s\n", lastmod); 450 if (verbose_flag) 451 printf("Matches --- %s ---\n", lastmod); 452 } 453 found++; 454 } 455 } 456 break; 457 default: 458 if (dump_flag) 459 printf("Unknown Type %d len %d\n", ival, len); 460 break; 461 } 462 walker = (void *)(len - sizeof(int) + (intptr_t)walker); 463 } 464 if (unbound_flag && found == 0 && *pnpinfo) { 465 if (verbose_flag) 466 printf("------------------------- "); 467 printf("%s on %s pnpinfo %s", *dev ? dev : "unattached", bus, pnpinfo); 468 if (verbose_flag) 469 printf(" -------------------------"); 470 printf("\n"); 471 } 472 free(lastmod); 473 } 474 475 static int 476 find_unmatched(struct devinfo_dev *dev, void *arg) 477 { 478 struct devinfo_dev *parent; 479 char *bus, *p; 480 481 do { 482 if (!all_flag && dev->dd_name[0] != '\0') 483 break; 484 if (!(dev->dd_flags & DF_ENABLED)) 485 break; 486 if (!all_flag && dev->dd_flags & DF_ATTACHED_ONCE) 487 break; 488 parent = devinfo_handle_to_device(dev->dd_parent); 489 bus = strdup(parent->dd_name); 490 p = bus + strlen(bus) - 1; 491 while (p >= bus && isdigit(*p)) 492 p--; 493 *++p = '\0'; 494 if (verbose_flag) 495 printf("Searching %s %s bus at %s for pnpinfo %s\n", 496 dev->dd_name, bus, dev->dd_location, dev->dd_pnpinfo); 497 search_hints(bus, dev->dd_name, dev->dd_pnpinfo); 498 free(bus); 499 } while (0); 500 501 return (devinfo_foreach_device_child(dev, find_unmatched, arg)); 502 } 503 504 struct exact_info 505 { 506 const char *bus; 507 const char *loc; 508 struct devinfo_dev *dev; 509 }; 510 511 /* 512 * Look for the exact location specified by the nomatch event. The 513 * loc and pnpinfo run together to get the string we're looking for, 514 * so we have to synthesize the same thing that subr_bus.c is 515 * generating in devnomatch/devaddq to do the string comparison. 516 */ 517 static int 518 find_exact_dev(struct devinfo_dev *dev, void *arg) 519 { 520 struct devinfo_dev *parent; 521 char *loc; 522 struct exact_info *info; 523 524 info = arg; 525 do { 526 if (info->dev != NULL) 527 break; 528 if (!(dev->dd_flags & DF_ENABLED)) 529 break; 530 parent = devinfo_handle_to_device(dev->dd_parent); 531 if (strcmp(info->bus, parent->dd_name) != 0) 532 break; 533 asprintf(&loc, "%s %s", parent->dd_pnpinfo, 534 parent->dd_location); 535 if (strcmp(loc, info->loc) == 0) 536 info->dev = dev; 537 free(loc); 538 } while (0); 539 540 return (devinfo_foreach_device_child(dev, find_exact_dev, arg)); 541 } 542 543 static void 544 find_nomatch(char *nomatch) 545 { 546 char *bus, *pnpinfo, *tmp, *busnameunit; 547 struct exact_info info; 548 549 /* 550 * Find our bus name. It will include the unit number. We have to search 551 * backwards to avoid false positive for any PNP string that has ' on ' 552 * in them, which would come earlier in the string. Like if there were 553 * an 'Old Bard' ethernet card made by 'Stratford on Avon Hardware' or 554 * something silly like that. 555 */ 556 tmp = nomatch + strlen(nomatch) - 4; 557 while (tmp > nomatch && strncmp(tmp, " on ", 4) != 0) 558 tmp--; 559 if (tmp == nomatch) 560 errx(1, "No bus found in nomatch string: '%s'", nomatch); 561 bus = tmp + 4; 562 *tmp = '\0'; 563 busnameunit = strdup(bus); 564 if (busnameunit == NULL) 565 errx(1, "Can't allocate memory for strings"); 566 tmp = bus + strlen(bus) - 1; 567 while (tmp > bus && isdigit(*tmp)) 568 tmp--; 569 *++tmp = '\0'; 570 571 /* 572 * Note: the NOMATCH events place both the bus location as well as the 573 * pnp info after the 'at' and we don't know where one stops and the 574 * other begins, so we pass the whole thing to our search routine. 575 */ 576 if (*nomatch == '?') 577 nomatch++; 578 if (strncmp(nomatch, " at ", 4) != 0) 579 errx(1, "Malformed NOMATCH string: '%s'", nomatch); 580 pnpinfo = nomatch + 4; 581 582 /* 583 * See if we can find the devinfo_dev for this device. If we 584 * can, and it's been attached before, we should filter it out 585 * so that a kldunload foo doesn't cause an immediate reload. 586 */ 587 info.loc = pnpinfo; 588 info.bus = busnameunit; 589 info.dev = NULL; 590 devinfo_foreach_device_child(root, find_exact_dev, (void *)&info); 591 if (info.dev != NULL && info.dev->dd_flags & DF_ATTACHED_ONCE) 592 exit(0); 593 search_hints(bus, "", pnpinfo); 594 595 exit(0); 596 } 597 598 static void 599 usage(void) 600 { 601 602 errx(1, "devmatch [-adv] [-p nomatch] [-h linker-hints]"); 603 } 604 605 int 606 main(int argc, char **argv) 607 { 608 int ch; 609 610 while ((ch = getopt_long(argc, argv, "adh:p:quv", 611 longopts, NULL)) != -1) { 612 switch (ch) { 613 case 'a': 614 all_flag = true; 615 break; 616 case 'd': 617 dump_flag = true; 618 break; 619 case 'h': 620 linker_hints = optarg; 621 break; 622 case 'p': 623 nomatch_str = optarg; 624 break; 625 case 'q': 626 quiet_flag = true; 627 break; 628 case 'u': 629 unbound_flag = true; 630 break; 631 case 'v': 632 verbose_flag = true; 633 break; 634 default: 635 usage(); 636 } 637 } 638 argc -= optind; 639 argv += optind; 640 641 if (argc >= 1) 642 usage(); 643 644 read_linker_hints(); 645 if (dump_flag) { 646 search_hints(NULL, NULL, NULL); 647 exit(0); 648 } 649 650 if (devinfo_init()) 651 err(1, "devinfo_init"); 652 if ((root = devinfo_handle_to_device(DEVINFO_ROOT_DEVICE)) == NULL) 653 errx(1, "can't find root device"); 654 if (nomatch_str != NULL) 655 find_nomatch(nomatch_str); 656 else 657 devinfo_foreach_device_child(root, find_unmatched, (void *)0); 658 devinfo_free(); 659 } 660