devinfo.c revision 142951
11573Srgrimes/*-
251933Speter * Copyright (c) 2000 Michael Smith
31573Srgrimes * Copyright (c) 2000 BSDi
41573Srgrimes * All rights reserved.
5119071Sobrien *
676812Sru * Redistribution and use in source and binary forms, with or without
7163149Skmacy * modification, are permitted provided that the following conditions
8163149Skmacy * are met:
9163148Skmacy * 1. Redistributions of source code must retain the above copyright
10163148Skmacy *    notice, this list of conditions and the following disclaimer.
11163148Skmacy * 2. Redistributions in binary form must reproduce the above copyright
12211934Snwhitehorn *    notice, this list of conditions and the following disclaimer in the
13211934Snwhitehorn *    documentation and/or other materials provided with the distribution.
14211934Snwhitehorn *
15211934Snwhitehorn * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16211934Snwhitehorn * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17211934Snwhitehorn * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18201381Sed * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19201381Sed * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20211934Snwhitehorn * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21195838Sbz * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22211725Simp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23214904Sgonzo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24211934Snwhitehorn * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25157911Speter * SUCH DAMAGE.
2655955Srgrimes */
271573Srgrimes
28181880Sjhb#include <sys/cdefs.h>
29181880Sjhb__FBSDID("$FreeBSD: head/lib/libdevinfo/devinfo.c 142951 2005-03-01 20:32:05Z stefanf $");
30181880Sjhb
311573Srgrimes/*
32181876Sjhb * An interface to the FreeBSD kernel's bus/device information interface.
33204494Srwatson *
341573Srgrimes * This interface is implemented with the
3543089Sbde *
361573Srgrimes * hw.bus
371573Srgrimes * hw.bus.devices
381573Srgrimes * 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
75static int	devinfo_init_devices(int generation);
76static int	devinfo_init_resources(int generation);
77
78TAILQ_HEAD(,devinfo_i_dev)	devinfo_dev;
79TAILQ_HEAD(,devinfo_i_rman)	devinfo_rman;
80TAILQ_HEAD(,devinfo_i_res)	devinfo_res;
81
82static int	devinfo_initted = 0;
83static int	devinfo_generation = 0;
84
85#if 0
86# define debug(fmt, args...)	\
87	fprintf(stderr, "%s:" fmt "\n", __func__ , ##args)
88#else
89# define debug(fmt, args...)
90#endif
91
92/*
93 * Initialise our local database with the contents of the kernel's
94 * tables.
95 */
96int
97devinfo_init(void)
98{
99	struct u_businfo	ubus;
100	size_t		ub_size;
101	int			error, retries;
102
103	if (!devinfo_initted) {
104		TAILQ_INIT(&devinfo_dev);
105		TAILQ_INIT(&devinfo_rman);
106		TAILQ_INIT(&devinfo_res);
107	}
108
109	/*
110	 * Get the generation count and interface version, verify that we
111	 * are compatible with the kernel.
112	 */
113	for (retries = 0; retries < 10; retries++) {
114		debug("get interface version");
115		ub_size = sizeof(ubus);
116		if (sysctlbyname("hw.bus.info", &ubus,
117		    &ub_size, NULL, 0) != 0) {
118			warn("sysctlbyname(\"hw.bus.info\", ...) failed");
119			return(EINVAL);
120		}
121		if ((ub_size != sizeof(ubus)) ||
122		    (ubus.ub_version != BUS_USER_VERSION)) {
123			warn("kernel bus interface version mismatch");
124			return(EINVAL);
125		}
126		debug("generation count is %d", ubus.ub_generation);
127
128		/*
129		 * Don't rescan if the generation count hasn't changed.
130		 */
131		if (ubus.ub_generation == devinfo_generation)
132			return(0);
133
134		/*
135		 * Generation count changed, rescan
136		 */
137		devinfo_free();
138		devinfo_initted = 0;
139		devinfo_generation = 0;
140
141		if ((error = devinfo_init_devices(ubus.ub_generation)) != 0) {
142			devinfo_free();
143			if (error == EINVAL)
144				continue;
145			break;
146		}
147		if ((error = devinfo_init_resources(ubus.ub_generation)) != 0) {
148			devinfo_free();
149			if (error == EINVAL)
150				continue;
151			break;
152		}
153		devinfo_initted = 1;
154		devinfo_generation = ubus.ub_generation;
155		return(0);
156	}
157	debug("scan failed after %d retries", retries);
158	errno = error;
159	return(1);
160}
161
162static int
163devinfo_init_devices(int generation)
164{
165	struct u_device		udev;
166	struct devinfo_i_dev	*dd;
167	int				dev_idx;
168	int				dev_ptr;
169	int				name2oid[2];
170	int				oid[CTL_MAXNAME + 12];
171	size_t			oidlen, rlen;
172	char			*name, *np, *fmt;
173	int				error, hexmode;
174
175	/*
176	 * Find the OID for the rman interface node.
177	 * This is just the usual evil, undocumented sysctl juju.
178	 */
179	name2oid[0] = 0;
180	name2oid[1] = 3;
181	oidlen = sizeof(oid);
182	name = "hw.bus.devices";
183	error = sysctl(name2oid, 2, oid, &oidlen, name, strlen(name));
184	if (error < 0) {
185		warnx("can't find hw.bus.devices sysctl node");
186		return(ENOENT);
187	}
188	oidlen /= sizeof(int);
189	if (oidlen > CTL_MAXNAME) {
190		warnx("hw.bus.devices oid is too large");
191		return(EINVAL);
192	}
193	oid[oidlen++] = generation;
194	dev_ptr = oidlen++;
195
196	/*
197	 * Scan devices.
198	 *
199	 * Stop after a fairly insane number to avoid death in the case
200	 * of kernel corruption.
201	 */
202	for (dev_idx = 0; dev_idx < 1000; dev_idx++) {
203
204		/*
205		 * Get the device information.
206		 */
207		oid[dev_ptr] = dev_idx;
208		rlen = sizeof(udev);
209		error = sysctl(oid, oidlen, &udev, &rlen, NULL, 0);
210		if (error < 0) {
211			if (errno == ENOENT)	/* end of list */
212				break;
213			if (errno != EINVAL)	/* gen count skip, restart */
214				warn("sysctl hw.bus.devices.%d", dev_idx);
215			return(errno);
216		}
217		if ((dd = malloc(sizeof(*dd))) == NULL)
218			return(ENOMEM);
219		dd->dd_dev.dd_handle = udev.dv_handle;
220		dd->dd_dev.dd_parent = udev.dv_parent;
221		snprintf(dd->dd_name, sizeof(dd->dd_name), "%s", udev.dv_name);
222		dd->dd_dev.dd_name = &dd->dd_name[0];
223		snprintf(dd->dd_desc, sizeof(dd->dd_desc), "%s", udev.dv_desc);
224		dd->dd_dev.dd_desc = &dd->dd_desc[0];
225		snprintf(dd->dd_drivername, sizeof(dd->dd_drivername), "%s",
226		    udev.dv_drivername);
227		dd->dd_dev.dd_drivername = &dd->dd_drivername[0];
228		snprintf(dd->dd_pnpinfo, sizeof(dd->dd_pnpinfo), "%s",
229		    udev.dv_pnpinfo);
230		dd->dd_dev.dd_pnpinfo = &dd->dd_pnpinfo[0];
231		snprintf(dd->dd_location, sizeof(dd->dd_location), "%s",
232		    udev.dv_location);
233		dd->dd_dev.dd_location = &dd->dd_location[0];
234		dd->dd_dev.dd_devflags = udev.dv_devflags;
235		dd->dd_dev.dd_flags = udev.dv_flags;
236		dd->dd_dev.dd_state = udev.dv_state;
237		TAILQ_INSERT_TAIL(&devinfo_dev, dd, dd_link);
238	}
239	debug("fetched %d devices", dev_idx);
240	return(0);
241}
242
243static int
244devinfo_init_resources(int generation)
245{
246	struct u_rman		urman;
247	struct devinfo_i_rman	*dm;
248	struct u_resource		ures;
249	struct devinfo_i_res	*dr;
250	int				rman_idx, res_idx;
251	int				rman_ptr, res_ptr;
252	int				name2oid[2];
253	int				oid[CTL_MAXNAME + 12];
254	size_t			oidlen, rlen;
255	char			*name, *np, *fmt;
256	int				error, hexmode;
257
258	/*
259	 * Find the OID for the rman interface node.
260	 * This is just the usual evil, undocumented sysctl juju.
261	 */
262	name2oid[0] = 0;
263	name2oid[1] = 3;
264	oidlen = sizeof(oid);
265	name = "hw.bus.rman";
266	error = sysctl(name2oid, 2, oid, &oidlen, name, strlen(name));
267	if (error < 0) {
268		warnx("can't find hw.bus.rman sysctl node");
269		return(ENOENT);
270	}
271	oidlen /= sizeof(int);
272	if (oidlen > CTL_MAXNAME) {
273		warnx("hw.bus.rman oid is too large");
274		return(EINVAL);
275	}
276	oid[oidlen++] = generation;
277	rman_ptr = oidlen++;
278	res_ptr = oidlen++;
279
280	/*
281	 * Scan resource managers.
282	 *
283	 * Stop after a fairly insane number to avoid death in the case
284	 * of kernel corruption.
285	 */
286	for (rman_idx = 0; rman_idx < 255; rman_idx++) {
287
288		/*
289		 * Get the resource manager information.
290		 */
291		oid[rman_ptr] = rman_idx;
292		oid[res_ptr] = -1;
293		rlen = sizeof(urman);
294		error = sysctl(oid, oidlen, &urman, &rlen, NULL, 0);
295		if (error < 0) {
296			if (errno == ENOENT)	/* end of list */
297				break;
298			if (errno != EINVAL)	/* gen count skip, restart */
299				warn("sysctl hw.bus.rman.%d", rman_idx);
300			return(errno);
301		}
302		if ((dm = malloc(sizeof(*dm))) == NULL)
303			return(ENOMEM);
304		dm->dm_rman.dm_handle = urman.rm_handle;
305		dm->dm_rman.dm_start = urman.rm_start;
306		dm->dm_rman.dm_size = urman.rm_size;
307		snprintf(dm->dm_desc, DEVINFO_STRLEN, "%s", urman.rm_descr);
308		dm->dm_rman.dm_desc = &dm->dm_desc[0];
309		TAILQ_INSERT_TAIL(&devinfo_rman, dm, dm_link);
310
311		/*
312		 * Scan resources on this resource manager.
313		 *
314		 * Stop after a fairly insane number to avoid death in the case
315		 * of kernel corruption.
316		 */
317		for (res_idx = 0; res_idx < 1000; res_idx++) {
318			/*
319			 * Get the resource information.
320			 */
321			oid[res_ptr] = res_idx;
322			rlen = sizeof(ures);
323			error = sysctl(oid, oidlen, &ures, &rlen, NULL, 0);
324			if (error < 0) {
325				if (errno == ENOENT)	/* end of list */
326					break;
327				if (errno != EINVAL)	/* gen count skip */
328					warn("sysctl hw.bus.rman.%d.%d",
329					    rman_idx, res_idx);
330				return(errno);
331			}
332			if ((dr = malloc(sizeof(*dr))) == NULL)
333				return(ENOMEM);
334			dr->dr_res.dr_handle = ures.r_handle;
335			dr->dr_res.dr_rman = ures.r_parent;
336			dr->dr_res.dr_device = ures.r_device;
337			dr->dr_res.dr_start = ures.r_start;
338			dr->dr_res.dr_size = ures.r_size;
339			TAILQ_INSERT_TAIL(&devinfo_res, dr, dr_link);
340		}
341		debug("fetched %d resources", res_idx);
342	}
343	debug("scanned %d resource managers", rman_idx);
344	return(0);
345}
346
347/*
348 * Free the list contents.
349 */
350void
351devinfo_free(void)
352{
353	struct devinfo_i_dev	*dd;
354	struct devinfo_i_rman	*dm;
355	struct devinfo_i_res	*dr;
356
357	while ((dd = TAILQ_FIRST(&devinfo_dev)) != NULL) {
358		TAILQ_REMOVE(&devinfo_dev, dd, dd_link);
359		free(dd);
360	}
361	while ((dm = TAILQ_FIRST(&devinfo_rman)) != NULL) {
362		TAILQ_REMOVE(&devinfo_rman, dm, dm_link);
363		free(dm);
364	}
365	while ((dr = TAILQ_FIRST(&devinfo_res)) != NULL) {
366		TAILQ_REMOVE(&devinfo_res, dr, dr_link);
367		free(dr);
368	}
369	devinfo_initted = 0;
370}
371
372/*
373 * Find a device by its handle.
374 */
375struct devinfo_dev *
376devinfo_handle_to_device(devinfo_handle_t handle)
377{
378	struct devinfo_i_dev	*dd;
379
380	/*
381	 * Find the root device, whose parent is NULL
382	 */
383	if (handle == DEVINFO_ROOT_DEVICE) {
384		TAILQ_FOREACH(dd, &devinfo_dev, dd_link)
385		    if (dd->dd_dev.dd_parent == DEVINFO_ROOT_DEVICE)
386			    return(&dd->dd_dev);
387		return(NULL);
388	}
389
390	/*
391	 * Scan for the device
392	 */
393	TAILQ_FOREACH(dd, &devinfo_dev, dd_link)
394	    if (dd->dd_dev.dd_handle == handle)
395		    return(&dd->dd_dev);
396	return(NULL);
397}
398
399/*
400 * Find a resource by its handle.
401 */
402struct devinfo_res *
403devinfo_handle_to_resource(devinfo_handle_t handle)
404{
405	struct devinfo_i_res	*dr;
406
407	TAILQ_FOREACH(dr, &devinfo_res, dr_link)
408	    if (dr->dr_res.dr_handle == handle)
409		    return(&dr->dr_res);
410	return(NULL);
411}
412
413/*
414 * Find a resource manager by its handle.
415 */
416struct devinfo_rman *
417devinfo_handle_to_rman(devinfo_handle_t handle)
418{
419	struct devinfo_i_rman	*dm;
420
421	TAILQ_FOREACH(dm, &devinfo_rman, dm_link)
422	    if (dm->dm_rman.dm_handle == handle)
423		    return(&dm->dm_rman);
424	return(NULL);
425}
426
427/*
428 * Iterate over the children of a device, calling (fn) on each.  If
429 * (fn) returns nonzero, abort the scan and return.
430 */
431int
432devinfo_foreach_device_child(struct devinfo_dev *parent,
433    int (* fn)(struct devinfo_dev *child, void *arg),
434    void *arg)
435{
436	struct devinfo_i_dev	*dd;
437	int				error;
438
439	TAILQ_FOREACH(dd, &devinfo_dev, dd_link)
440	    if (dd->dd_dev.dd_parent == parent->dd_handle)
441		    if ((error = fn(&dd->dd_dev, arg)) != 0)
442			    return(error);
443	return(0);
444}
445
446/*
447 * Iterate over all the resources owned by a device, calling (fn) on each.
448 * If (fn) returns nonzero, abort the scan and return.
449 */
450int
451devinfo_foreach_device_resource(struct devinfo_dev *dev,
452    int (* fn)(struct devinfo_dev *dev, struct devinfo_res *res, void *arg),
453    void *arg)
454{
455	struct devinfo_i_res	*dr;
456	int				error;
457
458	TAILQ_FOREACH(dr, &devinfo_res, dr_link)
459	    if (dr->dr_res.dr_device == dev->dd_handle)
460		    if ((error = fn(dev, &dr->dr_res, arg)) != 0)
461			    return(error);
462	return(0);
463}
464
465/*
466 * Iterate over all the resources owned by a resource manager, calling (fn)
467 * on each.  If (fn) returns nonzero, abort the scan and return.
468 */
469extern int
470devinfo_foreach_rman_resource(struct devinfo_rman *rman,
471    int (* fn)(struct devinfo_res *res, void *arg),
472    void *arg)
473{
474	struct devinfo_i_res	*dr;
475	int				error;
476
477	TAILQ_FOREACH(dr, &devinfo_res, dr_link)
478	    if (dr->dr_res.dr_rman == rman->dm_handle)
479		    if ((error = fn(&dr->dr_res, arg)) != 0)
480			    return(error);
481	return(0);
482}
483
484/*
485 * Iterate over all the resource managers, calling (fn) on each.  If (fn)
486 * returns nonzero, abort the scan and return.
487 */
488extern int
489devinfo_foreach_rman(int (* fn)(struct devinfo_rman *rman, void *arg),
490    void *arg)
491{
492    struct devinfo_i_rman	*dm;
493    int				error;
494
495    TAILQ_FOREACH(dm, &devinfo_rman, dm_link)
496	if ((error = fn(&dm->dm_rman, arg)) != 0)
497	    return(error);
498    return(0);
499}
500