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