1 /*-
2 * Copyright (c) 2000 Michael Smith
3 * Copyright (c) 2000 BSDi
4 * All rights reserved.
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 * $FreeBSD: src/lib/libdevinfo/devinfo.c,v 1.9 2006/07/17 09:33:24 stefanf Exp $
28 * $DragonFly: src/lib/libdevinfo/devinfo.c,v 1.1 2008/09/30 12:20:29 hasso Exp $
29 */
30
31 /*
32 * An interface to the FreeBSD kernel's bus/device information interface.
33 *
34 * This interface is implemented with the
35 *
36 * hw.bus
37 * hw.bus.devices
38 * hw.bus.rman
39 *
40 * sysctls. The interface is not meant for general user application
41 * consumption.
42 *
43 * Device information is obtained by scanning a linear list of all devices
44 * maintained by the kernel. The actual device structure pointers are
45 * handed out as opaque handles in order to allow reconstruction of the
46 * logical toplogy in user space.
47 *
48 * Resource information is obtained by scanning the kernel's resource
49 * managers and fetching their contents. Ownership of resources is
50 * tracked using the device's structure pointer again as a handle.
51 *
52 * In order to ensure coherency of the library's picture of the kernel,
53 * a generation count is maintained by the kernel. The initial generation
54 * count is obtained (along with the interface version) from the hw.bus
55 * sysctl, and must be passed in with every request. If the generation
56 * number supplied by the library does not match the kernel's current
57 * generation number, the request is failed and the library must discard
58 * the data it has received and rescan.
59 *
60 * The information obtained from the kernel is exported to consumers of
61 * this library through a variety of interfaces.
62 */
63
64 #include <sys/param.h>
65 #include <sys/types.h>
66 #include <sys/sysctl.h>
67 #include <err.h>
68 #include <errno.h>
69 #include <stdio.h>
70 #include <stdlib.h>
71 #include <string.h>
72 #include "devinfo.h"
73 #include "devinfo_var.h"
74
75 static int devinfo_init_devices(int generation);
76 static int devinfo_init_resources(int generation);
77
78 TAILQ_HEAD(,devinfo_i_dev) devinfo_dev;
79 TAILQ_HEAD(,devinfo_i_rman) devinfo_rman;
80 TAILQ_HEAD(,devinfo_i_res) devinfo_res;
81
82 static int devinfo_initted = 0;
83 static int devinfo_generation = 0;
84
85 #if 0
86 # define debug(...) do { \
87 fprintf(stderr, "%s:", __func__); \
88 fprintf(stderr, __VA_ARGS__); \
89 fprintf(stderr, "\n"); \
90 } while (0)
91 #else
92 # define debug(...)
93 #endif
94
95 /*
96 * Initialise our local database with the contents of the kernel's
97 * tables.
98 */
99 int
devinfo_init(void)100 devinfo_init(void)
101 {
102 struct u_businfo ubus;
103 size_t ub_size;
104 int error, retries;
105
106 if (!devinfo_initted) {
107 TAILQ_INIT(&devinfo_dev);
108 TAILQ_INIT(&devinfo_rman);
109 TAILQ_INIT(&devinfo_res);
110 }
111
112 /*
113 * Get the generation count and interface version, verify that we
114 * are compatible with the kernel.
115 */
116 for (retries = 0; retries < 10; retries++) {
117 debug("get interface version");
118 ub_size = sizeof(ubus);
119 if (sysctlbyname("hw.bus.info", &ubus,
120 &ub_size, NULL, 0) != 0) {
121 warn("sysctlbyname(\"hw.bus.info\", ...) failed");
122 return(EINVAL);
123 }
124 if ((ub_size != sizeof(ubus)) ||
125 (ubus.ub_version != BUS_USER_VERSION)) {
126 warn("kernel bus interface version mismatch");
127 return(EINVAL);
128 }
129 debug("generation count is %d", ubus.ub_generation);
130
131 /*
132 * Don't rescan if the generation count hasn't changed.
133 */
134 if (ubus.ub_generation == devinfo_generation)
135 return(0);
136
137 /*
138 * Generation count changed, rescan
139 */
140 devinfo_free();
141 devinfo_initted = 0;
142 devinfo_generation = 0;
143
144 if ((error = devinfo_init_devices(ubus.ub_generation)) != 0) {
145 devinfo_free();
146 if (error == EINVAL)
147 continue;
148 break;
149 }
150 if ((error = devinfo_init_resources(ubus.ub_generation)) != 0) {
151 devinfo_free();
152 if (error == EINVAL)
153 continue;
154 break;
155 }
156 devinfo_initted = 1;
157 devinfo_generation = ubus.ub_generation;
158 return(0);
159 }
160 debug("scan failed after %d retries", retries);
161 errno = error;
162 return(1);
163 }
164
165 static int
devinfo_init_devices(int generation)166 devinfo_init_devices(int generation)
167 {
168 struct u_device udev;
169 struct devinfo_i_dev *dd;
170 int dev_idx;
171 int dev_ptr;
172 int name2oid[2];
173 int oid[CTL_MAXNAME + 12];
174 size_t oidlen, rlen;
175 char *name;
176 int error;
177
178 /*
179 * Find the OID for the rman interface node.
180 * This is just the usual evil, undocumented sysctl juju.
181 */
182 name2oid[0] = 0;
183 name2oid[1] = 3;
184 oidlen = sizeof(oid);
185 name = "hw.bus.devices";
186 error = sysctl(name2oid, 2, oid, &oidlen, name, strlen(name));
187 if (error < 0) {
188 warnx("can't find hw.bus.devices sysctl node");
189 return(ENOENT);
190 }
191 oidlen /= sizeof(int);
192 if (oidlen > CTL_MAXNAME) {
193 warnx("hw.bus.devices oid is too large");
194 return(EINVAL);
195 }
196 oid[oidlen++] = generation;
197 dev_ptr = oidlen++;
198
199 /*
200 * Scan devices.
201 *
202 * Stop after a fairly insane number to avoid death in the case
203 * of kernel corruption.
204 */
205 for (dev_idx = 0; dev_idx < 1000; dev_idx++) {
206
207 /*
208 * Get the device information.
209 */
210 oid[dev_ptr] = dev_idx;
211 rlen = sizeof(udev);
212 error = sysctl(oid, oidlen, &udev, &rlen, NULL, 0);
213 if (error < 0) {
214 if (errno == ENOENT) /* end of list */
215 break;
216 if (errno != EINVAL) /* gen count skip, restart */
217 warn("sysctl hw.bus.devices.%d", dev_idx);
218 return(errno);
219 }
220 if ((dd = malloc(sizeof(*dd))) == NULL)
221 return(ENOMEM);
222 dd->dd_dev.dd_handle = udev.dv_handle;
223 dd->dd_dev.dd_parent = udev.dv_parent;
224 snprintf(dd->dd_name, sizeof(dd->dd_name), "%s", udev.dv_name);
225 dd->dd_dev.dd_name = &dd->dd_name[0];
226 snprintf(dd->dd_desc, sizeof(dd->dd_desc), "%s", udev.dv_desc);
227 dd->dd_dev.dd_desc = &dd->dd_desc[0];
228 snprintf(dd->dd_drivername, sizeof(dd->dd_drivername), "%s",
229 udev.dv_drivername);
230 dd->dd_dev.dd_drivername = &dd->dd_drivername[0];
231 snprintf(dd->dd_pnpinfo, sizeof(dd->dd_pnpinfo), "%s",
232 udev.dv_pnpinfo);
233 dd->dd_dev.dd_pnpinfo = &dd->dd_pnpinfo[0];
234 snprintf(dd->dd_location, sizeof(dd->dd_location), "%s",
235 udev.dv_location);
236 dd->dd_dev.dd_location = &dd->dd_location[0];
237 dd->dd_dev.dd_devflags = udev.dv_devflags;
238 dd->dd_dev.dd_flags = udev.dv_flags;
239 dd->dd_dev.dd_state = udev.dv_state;
240 TAILQ_INSERT_TAIL(&devinfo_dev, dd, dd_link);
241 }
242 debug("fetched %d devices", dev_idx);
243 return(0);
244 }
245
246 static int
devinfo_init_resources(int generation)247 devinfo_init_resources(int generation)
248 {
249 struct u_rman urman;
250 struct devinfo_i_rman *dm;
251 struct u_resource ures;
252 struct devinfo_i_res *dr;
253 int rman_idx, res_idx;
254 int rman_ptr, res_ptr;
255 int name2oid[2];
256 int oid[CTL_MAXNAME + 12];
257 size_t oidlen, rlen;
258 char *name;
259 int error;
260
261 /*
262 * Find the OID for the rman interface node.
263 * This is just the usual evil, undocumented sysctl juju.
264 */
265 name2oid[0] = 0;
266 name2oid[1] = 3;
267 oidlen = sizeof(oid);
268 name = "hw.bus.rman";
269 error = sysctl(name2oid, 2, oid, &oidlen, name, strlen(name));
270 if (error < 0) {
271 warnx("can't find hw.bus.rman sysctl node");
272 return(ENOENT);
273 }
274 oidlen /= sizeof(int);
275 if (oidlen > CTL_MAXNAME) {
276 warnx("hw.bus.rman oid is too large");
277 return(EINVAL);
278 }
279 oid[oidlen++] = generation;
280 rman_ptr = oidlen++;
281 res_ptr = oidlen++;
282
283 /*
284 * Scan resource managers.
285 *
286 * Stop after a fairly insane number to avoid death in the case
287 * of kernel corruption.
288 */
289 for (rman_idx = 0; rman_idx < 255; rman_idx++) {
290
291 /*
292 * Get the resource manager information.
293 */
294 oid[rman_ptr] = rman_idx;
295 oid[res_ptr] = -1;
296 rlen = sizeof(urman);
297 error = sysctl(oid, oidlen, &urman, &rlen, NULL, 0);
298 if (error < 0) {
299 if (errno == ENOENT) /* end of list */
300 break;
301 if (errno != EINVAL) /* gen count skip, restart */
302 warn("sysctl hw.bus.rman.%d", rman_idx);
303 return(errno);
304 }
305 if ((dm = malloc(sizeof(*dm))) == NULL)
306 return(ENOMEM);
307 dm->dm_rman.dm_handle = urman.rm_handle;
308 dm->dm_rman.dm_start = urman.rm_start;
309 dm->dm_rman.dm_size = urman.rm_size;
310 snprintf(dm->dm_desc, DEVINFO_STRLEN, "%s", urman.rm_descr);
311 dm->dm_rman.dm_desc = &dm->dm_desc[0];
312 TAILQ_INSERT_TAIL(&devinfo_rman, dm, dm_link);
313
314 /*
315 * Scan resources on this resource manager.
316 *
317 * Stop after a fairly insane number to avoid death in the case
318 * of kernel corruption.
319 */
320 for (res_idx = 0; res_idx < 1000; res_idx++) {
321 /*
322 * Get the resource information.
323 */
324 oid[res_ptr] = res_idx;
325 rlen = sizeof(ures);
326 error = sysctl(oid, oidlen, &ures, &rlen, NULL, 0);
327 if (error < 0) {
328 if (errno == ENOENT) /* end of list */
329 break;
330 if (errno != EINVAL) /* gen count skip */
331 warn("sysctl hw.bus.rman.%d.%d",
332 rman_idx, res_idx);
333 return(errno);
334 }
335 if ((dr = malloc(sizeof(*dr))) == NULL)
336 return(ENOMEM);
337 dr->dr_res.dr_handle = ures.r_handle;
338 dr->dr_res.dr_rman = ures.r_parent;
339 dr->dr_res.dr_device = ures.r_device;
340 dr->dr_res.dr_start = ures.r_start;
341 dr->dr_res.dr_size = ures.r_size;
342 TAILQ_INSERT_TAIL(&devinfo_res, dr, dr_link);
343 }
344 debug("fetched %d resources", res_idx);
345 }
346 debug("scanned %d resource managers", rman_idx);
347 return(0);
348 }
349
350 /*
351 * Free the list contents.
352 */
353 void
devinfo_free(void)354 devinfo_free(void)
355 {
356 struct devinfo_i_dev *dd;
357 struct devinfo_i_rman *dm;
358 struct devinfo_i_res *dr;
359
360 while ((dd = TAILQ_FIRST(&devinfo_dev)) != NULL) {
361 TAILQ_REMOVE(&devinfo_dev, dd, dd_link);
362 free(dd);
363 }
364 while ((dm = TAILQ_FIRST(&devinfo_rman)) != NULL) {
365 TAILQ_REMOVE(&devinfo_rman, dm, dm_link);
366 free(dm);
367 }
368 while ((dr = TAILQ_FIRST(&devinfo_res)) != NULL) {
369 TAILQ_REMOVE(&devinfo_res, dr, dr_link);
370 free(dr);
371 }
372 devinfo_initted = 0;
373 devinfo_generation = 0;
374 }
375
376 /*
377 * Find a device by its handle.
378 */
379 struct devinfo_dev *
devinfo_handle_to_device(devinfo_handle_t handle)380 devinfo_handle_to_device(devinfo_handle_t handle)
381 {
382 struct devinfo_i_dev *dd;
383
384 /*
385 * Find the root device, whose parent is NULL
386 */
387 if (handle == DEVINFO_ROOT_DEVICE) {
388 TAILQ_FOREACH(dd, &devinfo_dev, dd_link)
389 if (dd->dd_dev.dd_parent == DEVINFO_ROOT_DEVICE)
390 return(&dd->dd_dev);
391 return(NULL);
392 }
393
394 /*
395 * Scan for the device
396 */
397 TAILQ_FOREACH(dd, &devinfo_dev, dd_link)
398 if (dd->dd_dev.dd_handle == handle)
399 return(&dd->dd_dev);
400 return(NULL);
401 }
402
403 /*
404 * Find a resource by its handle.
405 */
406 struct devinfo_res *
devinfo_handle_to_resource(devinfo_handle_t handle)407 devinfo_handle_to_resource(devinfo_handle_t handle)
408 {
409 struct devinfo_i_res *dr;
410
411 TAILQ_FOREACH(dr, &devinfo_res, dr_link)
412 if (dr->dr_res.dr_handle == handle)
413 return(&dr->dr_res);
414 return(NULL);
415 }
416
417 /*
418 * Find a resource manager by its handle.
419 */
420 struct devinfo_rman *
devinfo_handle_to_rman(devinfo_handle_t handle)421 devinfo_handle_to_rman(devinfo_handle_t handle)
422 {
423 struct devinfo_i_rman *dm;
424
425 TAILQ_FOREACH(dm, &devinfo_rman, dm_link)
426 if (dm->dm_rman.dm_handle == handle)
427 return(&dm->dm_rman);
428 return(NULL);
429 }
430
431 /*
432 * Iterate over the children of a device, calling (fn) on each. If
433 * (fn) returns nonzero, abort the scan and return.
434 */
435 int
devinfo_foreach_device_child(struct devinfo_dev * parent,int (* fn)(struct devinfo_dev * child,void * arg),void * arg)436 devinfo_foreach_device_child(struct devinfo_dev *parent,
437 int (* fn)(struct devinfo_dev *child, void *arg),
438 void *arg)
439 {
440 struct devinfo_i_dev *dd;
441 int error;
442
443 TAILQ_FOREACH(dd, &devinfo_dev, dd_link)
444 if (dd->dd_dev.dd_parent == parent->dd_handle)
445 if ((error = fn(&dd->dd_dev, arg)) != 0)
446 return(error);
447 return(0);
448 }
449
450 /*
451 * Iterate over all the resources owned by a device, calling (fn) on each.
452 * If (fn) returns nonzero, abort the scan and return.
453 */
454 int
devinfo_foreach_device_resource(struct devinfo_dev * dev,int (* fn)(struct devinfo_dev * dev,struct devinfo_res * res,void * arg),void * arg)455 devinfo_foreach_device_resource(struct devinfo_dev *dev,
456 int (* fn)(struct devinfo_dev *dev, struct devinfo_res *res, void *arg),
457 void *arg)
458 {
459 struct devinfo_i_res *dr;
460 int error;
461
462 TAILQ_FOREACH(dr, &devinfo_res, dr_link)
463 if (dr->dr_res.dr_device == dev->dd_handle)
464 if ((error = fn(dev, &dr->dr_res, arg)) != 0)
465 return(error);
466 return(0);
467 }
468
469 /*
470 * Iterate over all the resources owned by a resource manager, calling (fn)
471 * on each. If (fn) returns nonzero, abort the scan and return.
472 */
473 extern int
devinfo_foreach_rman_resource(struct devinfo_rman * rman,int (* fn)(struct devinfo_res * res,void * arg),void * arg)474 devinfo_foreach_rman_resource(struct devinfo_rman *rman,
475 int (* fn)(struct devinfo_res *res, void *arg),
476 void *arg)
477 {
478 struct devinfo_i_res *dr;
479 int error;
480
481 TAILQ_FOREACH(dr, &devinfo_res, dr_link)
482 if (dr->dr_res.dr_rman == rman->dm_handle)
483 if ((error = fn(&dr->dr_res, arg)) != 0)
484 return(error);
485 return(0);
486 }
487
488 /*
489 * Iterate over all the resource managers, calling (fn) on each. If (fn)
490 * returns nonzero, abort the scan and return.
491 */
492 extern int
devinfo_foreach_rman(int (* fn)(struct devinfo_rman * rman,void * arg),void * arg)493 devinfo_foreach_rman(int (* fn)(struct devinfo_rman *rman, void *arg),
494 void *arg)
495 {
496 struct devinfo_i_rman *dm;
497 int error;
498
499 TAILQ_FOREACH(dm, &devinfo_rman, dm_link)
500 if ((error = fn(&dm->dm_rman, arg)) != 0)
501 return(error);
502 return(0);
503 }
504