xref: /freebsd-src/lib/libdevinfo/devinfo.c (revision 7554746c43c50d99d15bb63ff43c90e561a9792e)
1a2e6df29SMike Smith /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
35e53a4f9SPedro F. Giffuni  *
4a2e6df29SMike Smith  * Copyright (c) 2000 Michael Smith
5a2e6df29SMike Smith  * Copyright (c) 2000 BSDi
6a2e6df29SMike Smith  * All rights reserved.
7a2e6df29SMike Smith  *
8a2e6df29SMike Smith  * Redistribution and use in source and binary forms, with or without
9a2e6df29SMike Smith  * modification, are permitted provided that the following conditions
10a2e6df29SMike Smith  * are met:
11a2e6df29SMike Smith  * 1. Redistributions of source code must retain the above copyright
12a2e6df29SMike Smith  *    notice, this list of conditions and the following disclaimer.
13a2e6df29SMike Smith  * 2. Redistributions in binary form must reproduce the above copyright
14a2e6df29SMike Smith  *    notice, this list of conditions and the following disclaimer in the
15a2e6df29SMike Smith  *    documentation and/or other materials provided with the distribution.
16a2e6df29SMike Smith  *
17a2e6df29SMike Smith  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18a2e6df29SMike Smith  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19a2e6df29SMike Smith  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20a2e6df29SMike Smith  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21a2e6df29SMike Smith  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22a2e6df29SMike Smith  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23a2e6df29SMike Smith  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24a2e6df29SMike Smith  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25a2e6df29SMike Smith  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26a2e6df29SMike Smith  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27a2e6df29SMike Smith  * SUCH DAMAGE.
28a2e6df29SMike Smith  */
29a2e6df29SMike Smith 
30e67f5b9fSMatthew Dillon #include <sys/cdefs.h>
31a2e6df29SMike Smith /*
322af3af27SStefan Farfeleder  * An interface to the FreeBSD kernel's bus/device information interface.
33a2e6df29SMike Smith  *
34a2e6df29SMike Smith  * This interface is implemented with the
35a2e6df29SMike Smith  *
36a2e6df29SMike Smith  * hw.bus
37a2e6df29SMike Smith  * hw.bus.devices
38a2e6df29SMike Smith  * hw.bus.rman
39a2e6df29SMike Smith  *
40a2e6df29SMike Smith  * sysctls.  The interface is not meant for general user application
41a2e6df29SMike Smith  * consumption.
42a2e6df29SMike Smith  *
43a2e6df29SMike Smith  * Device information is obtained by scanning a linear list of all devices
44a2e6df29SMike Smith  * maintained by the kernel.  The actual device structure pointers are
45a2e6df29SMike Smith  * handed out as opaque handles in order to allow reconstruction of the
46a2e6df29SMike Smith  * logical toplogy in user space.
47a2e6df29SMike Smith  *
48a2e6df29SMike Smith  * Resource information is obtained by scanning the kernel's resource
49a2e6df29SMike Smith  * managers and fetching their contents.  Ownership of resources is
50a2e6df29SMike Smith  * tracked using the device's structure pointer again as a handle.
51a2e6df29SMike Smith  *
52a2e6df29SMike Smith  * In order to ensure coherency of the library's picture of the kernel,
53a2e6df29SMike Smith  * a generation count is maintained by the kernel.  The initial generation
54a2e6df29SMike Smith  * count is obtained (along with the interface version) from the hw.bus
55a2e6df29SMike Smith  * sysctl, and must be passed in with every request.  If the generation
56a2e6df29SMike Smith  * number supplied by the library does not match the kernel's current
57a2e6df29SMike Smith  * generation number, the request is failed and the library must discard
58a2e6df29SMike Smith  * the data it has received and rescan.
59a2e6df29SMike Smith  *
60a2e6df29SMike Smith  * The information obtained from the kernel is exported to consumers of
61a2e6df29SMike Smith  * this library through a variety of interfaces.
62a2e6df29SMike Smith  */
63a2e6df29SMike Smith 
64a2e6df29SMike Smith #include <sys/param.h>
65a2e6df29SMike Smith #include <sys/types.h>
66a2e6df29SMike Smith #include <sys/sysctl.h>
67a2e6df29SMike Smith #include <err.h>
68a2e6df29SMike Smith #include <errno.h>
69a2e6df29SMike Smith #include <stdio.h>
70a2e6df29SMike Smith #include <stdlib.h>
71e495c560SJohn Baldwin #include <string.h>
72a2e6df29SMike Smith #include "devinfo.h"
73a2e6df29SMike Smith #include "devinfo_var.h"
74a2e6df29SMike Smith 
75a2e6df29SMike Smith static int	devinfo_init_devices(int generation);
76a2e6df29SMike Smith static int	devinfo_init_resources(int generation);
7732592d86SEric van Gyzen static void	devinfo_free_dev(struct devinfo_i_dev *dd);
78a2e6df29SMike Smith 
79a2e6df29SMike Smith TAILQ_HEAD(,devinfo_i_dev)	devinfo_dev;
80a2e6df29SMike Smith TAILQ_HEAD(,devinfo_i_rman)	devinfo_rman;
81a2e6df29SMike Smith TAILQ_HEAD(,devinfo_i_res)	devinfo_res;
82a2e6df29SMike Smith 
83a2e6df29SMike Smith static int	devinfo_initted = 0;
84a2e6df29SMike Smith static int	devinfo_generation = 0;
85a2e6df29SMike Smith 
86a2e6df29SMike Smith #if 0
8760cd5863SStefan Farfeleder # define debug(...)	do { \
8860cd5863SStefan Farfeleder 	fprintf(stderr, "%s:", __func__); \
8960cd5863SStefan Farfeleder 	fprintf(stderr, __VA_ARGS__); \
9060cd5863SStefan Farfeleder 	fprintf(stderr, "\n"); \
9160cd5863SStefan Farfeleder } while (0)
92a2e6df29SMike Smith #else
9360cd5863SStefan Farfeleder # define debug(...)
94a2e6df29SMike Smith #endif
95a2e6df29SMike Smith 
96a2e6df29SMike Smith /*
97a2e6df29SMike Smith  * Initialise our local database with the contents of the kernel's
98a2e6df29SMike Smith  * tables.
99a2e6df29SMike Smith  */
100a2e6df29SMike Smith int
101a2e6df29SMike Smith devinfo_init(void)
102a2e6df29SMike Smith {
103a2e6df29SMike Smith 	struct u_businfo	ubus;
104a2e6df29SMike Smith 	size_t		ub_size;
105a2e6df29SMike Smith 	int			error, retries;
106a2e6df29SMike Smith 
107a2e6df29SMike Smith 	if (!devinfo_initted) {
108a2e6df29SMike Smith 		TAILQ_INIT(&devinfo_dev);
109a2e6df29SMike Smith 		TAILQ_INIT(&devinfo_rman);
110a2e6df29SMike Smith 		TAILQ_INIT(&devinfo_res);
111a2e6df29SMike Smith 	}
112a2e6df29SMike Smith 
113a2e6df29SMike Smith 	/*
114a2e6df29SMike Smith 	 * Get the generation count and interface version, verify that we
115a2e6df29SMike Smith 	 * are compatible with the kernel.
116a2e6df29SMike Smith 	 */
117a2e6df29SMike Smith 	for (retries = 0; retries < 10; retries++) {
118a2e6df29SMike Smith 		debug("get interface version");
119a2e6df29SMike Smith 		ub_size = sizeof(ubus);
120a2e6df29SMike Smith 		if (sysctlbyname("hw.bus.info", &ubus,
121a2e6df29SMike Smith 		    &ub_size, NULL, 0) != 0) {
122a2e6df29SMike Smith 			warn("sysctlbyname(\"hw.bus.info\", ...) failed");
123a2e6df29SMike Smith 			return(EINVAL);
124a2e6df29SMike Smith 		}
125a2e6df29SMike Smith 		if ((ub_size != sizeof(ubus)) ||
126a2e6df29SMike Smith 		    (ubus.ub_version != BUS_USER_VERSION)) {
12751a9cfbfSWarner Losh 			warnx("kernel bus interface version mismatch: kernel %d expected %d",
12851a9cfbfSWarner Losh 			    ubus.ub_version, BUS_USER_VERSION);
129a2e6df29SMike Smith 			return(EINVAL);
130a2e6df29SMike Smith 		}
131a2e6df29SMike Smith 		debug("generation count is %d", ubus.ub_generation);
132a2e6df29SMike Smith 
133a2e6df29SMike Smith 		/*
134a2e6df29SMike Smith 		 * Don't rescan if the generation count hasn't changed.
135a2e6df29SMike Smith 		 */
136a2e6df29SMike Smith 		if (ubus.ub_generation == devinfo_generation)
137a2e6df29SMike Smith 			return(0);
138a2e6df29SMike Smith 
139a2e6df29SMike Smith 		/*
140a2e6df29SMike Smith 		 * Generation count changed, rescan
141a2e6df29SMike Smith 		 */
142a2e6df29SMike Smith 		devinfo_free();
143a2e6df29SMike Smith 		devinfo_initted = 0;
144a2e6df29SMike Smith 		devinfo_generation = 0;
145a2e6df29SMike Smith 
146a2e6df29SMike Smith 		if ((error = devinfo_init_devices(ubus.ub_generation)) != 0) {
147a2e6df29SMike Smith 			devinfo_free();
148a2e6df29SMike Smith 			if (error == EINVAL)
149a2e6df29SMike Smith 				continue;
150a2e6df29SMike Smith 			break;
151a2e6df29SMike Smith 		}
152a2e6df29SMike Smith 		if ((error = devinfo_init_resources(ubus.ub_generation)) != 0) {
153a2e6df29SMike Smith 			devinfo_free();
154a2e6df29SMike Smith 			if (error == EINVAL)
155a2e6df29SMike Smith 				continue;
156a2e6df29SMike Smith 			break;
157a2e6df29SMike Smith 		}
158a2e6df29SMike Smith 		devinfo_initted = 1;
159a2e6df29SMike Smith 		devinfo_generation = ubus.ub_generation;
160a2e6df29SMike Smith 		return(0);
161a2e6df29SMike Smith 	}
162a2e6df29SMike Smith 	debug("scan failed after %d retries", retries);
163a2e6df29SMike Smith 	errno = error;
164a2e6df29SMike Smith 	return(1);
165a2e6df29SMike Smith }
166a2e6df29SMike Smith 
167a2e6df29SMike Smith static int
168a2e6df29SMike Smith devinfo_init_devices(int generation)
169a2e6df29SMike Smith {
170a2e6df29SMike Smith 	struct u_device		udev;
171a2e6df29SMike Smith 	struct devinfo_i_dev	*dd;
172a2e6df29SMike Smith 	int			dev_idx;
173a2e6df29SMike Smith 	int			dev_ptr;
174a2e6df29SMike Smith 	int			name2oid[2];
175a2e6df29SMike Smith 	int			oid[CTL_MAXNAME + 12];
176a2e6df29SMike Smith 	size_t			oidlen, rlen;
177cf72c10fSWarner Losh 	char			*name, *walker, *ep;
178109a0e6bSPhilippe Charnier 	int			error;
179a2e6df29SMike Smith 
180a2e6df29SMike Smith 	/*
181a2e6df29SMike Smith 	 * Find the OID for the rman interface node.
182a2e6df29SMike Smith 	 * This is just the usual evil, undocumented sysctl juju.
183a2e6df29SMike Smith 	 */
184a2e6df29SMike Smith 	name2oid[0] = 0;
185a2e6df29SMike Smith 	name2oid[1] = 3;
186a2e6df29SMike Smith 	oidlen = sizeof(oid);
187a2e6df29SMike Smith 	name = "hw.bus.devices";
188a2e6df29SMike Smith 	error = sysctl(name2oid, 2, oid, &oidlen, name, strlen(name));
189a2e6df29SMike Smith 	if (error < 0) {
190a2e6df29SMike Smith 		warnx("can't find hw.bus.devices sysctl node");
191a2e6df29SMike Smith 		return(ENOENT);
192a2e6df29SMike Smith 	}
193a2e6df29SMike Smith 	oidlen /= sizeof(int);
194a2e6df29SMike Smith 	if (oidlen > CTL_MAXNAME) {
195a2e6df29SMike Smith 		warnx("hw.bus.devices oid is too large");
196a2e6df29SMike Smith 		return(EINVAL);
197a2e6df29SMike Smith 	}
198a2e6df29SMike Smith 	oid[oidlen++] = generation;
199a2e6df29SMike Smith 	dev_ptr = oidlen++;
200a2e6df29SMike Smith 
201a2e6df29SMike Smith 	/*
202a2e6df29SMike Smith 	 * Scan devices.
203a2e6df29SMike Smith 	 *
204a2e6df29SMike Smith 	 * Stop after a fairly insane number to avoid death in the case
205a2e6df29SMike Smith 	 * of kernel corruption.
206a2e6df29SMike Smith 	 */
20741ec95c4SWarner Losh 	for (dev_idx = 0; dev_idx < 10000; dev_idx++) {
208a2e6df29SMike Smith 
209a2e6df29SMike Smith 		/*
210a2e6df29SMike Smith 		 * Get the device information.
211a2e6df29SMike Smith 		 */
212a2e6df29SMike Smith 		oid[dev_ptr] = dev_idx;
213a2e6df29SMike Smith 		rlen = sizeof(udev);
214a2e6df29SMike Smith 		error = sysctl(oid, oidlen, &udev, &rlen, NULL, 0);
215a2e6df29SMike Smith 		if (error < 0) {
216a2e6df29SMike Smith 			if (errno == ENOENT)	/* end of list */
217a2e6df29SMike Smith 				break;
218a2e6df29SMike Smith 			if (errno != EINVAL)	/* gen count skip, restart */
219a2e6df29SMike Smith 				warn("sysctl hw.bus.devices.%d", dev_idx);
220a2e6df29SMike Smith 			return(errno);
221a2e6df29SMike Smith 		}
22292376fa7SWarner Losh 		if (rlen != sizeof(udev)) {
22392376fa7SWarner Losh 			warnx("sysctl returned wrong data %zd bytes instead of %zd",
22492376fa7SWarner Losh 			    rlen, sizeof(udev));
22592376fa7SWarner Losh 			return (EINVAL);
22692376fa7SWarner Losh 		}
22732592d86SEric van Gyzen 		if ((dd = calloc(1, sizeof(*dd))) == NULL)
228a2e6df29SMike Smith 			return(ENOMEM);
229a2e6df29SMike Smith 		dd->dd_dev.dd_handle = udev.dv_handle;
230a2e6df29SMike Smith 		dd->dd_dev.dd_parent = udev.dv_parent;
23143832002SWarner Losh 		dd->dd_dev.dd_devflags = udev.dv_devflags;
23243832002SWarner Losh 		dd->dd_dev.dd_flags = udev.dv_flags;
23343832002SWarner Losh 		dd->dd_dev.dd_state = udev.dv_state;
234cf72c10fSWarner Losh 
235cf72c10fSWarner Losh 		walker = udev.dv_fields;
236cf72c10fSWarner Losh 		ep = walker + sizeof(udev.dv_fields);
237cf72c10fSWarner Losh 		dd->dd_name = NULL;
238cf72c10fSWarner Losh 		dd->dd_desc = NULL;
239cf72c10fSWarner Losh 		dd->dd_drivername = NULL;
240cf72c10fSWarner Losh 		dd->dd_pnpinfo = NULL;
241cf72c10fSWarner Losh 		dd->dd_location = NULL;
242cf72c10fSWarner Losh #define UNPACK(x)							\
243cf72c10fSWarner Losh 		dd->dd_dev.x = dd->x = strdup(walker);			\
24432592d86SEric van Gyzen 		if (dd->x == NULL) {					\
24532592d86SEric van Gyzen 			devinfo_free_dev(dd);				\
246cf72c10fSWarner Losh 			return(ENOMEM);					\
24732592d86SEric van Gyzen 		}							\
24832592d86SEric van Gyzen 		if (walker + strnlen(walker, ep - walker) >= ep) {	\
24932592d86SEric van Gyzen 			devinfo_free_dev(dd);				\
250cf72c10fSWarner Losh 			return(EINVAL);					\
25132592d86SEric van Gyzen 		}							\
252cf72c10fSWarner Losh 		walker += strlen(walker) + 1;
253cf72c10fSWarner Losh 
254cf72c10fSWarner Losh 		UNPACK(dd_name);
255cf72c10fSWarner Losh 		UNPACK(dd_desc);
256cf72c10fSWarner Losh 		UNPACK(dd_drivername);
257cf72c10fSWarner Losh 		UNPACK(dd_pnpinfo);
258cf72c10fSWarner Losh 		UNPACK(dd_location);
259cf72c10fSWarner Losh #undef UNPACK
260a2e6df29SMike Smith 		TAILQ_INSERT_TAIL(&devinfo_dev, dd, dd_link);
261a2e6df29SMike Smith 	}
262a2e6df29SMike Smith 	debug("fetched %d devices", dev_idx);
263a2e6df29SMike Smith 	return(0);
264a2e6df29SMike Smith }
265a2e6df29SMike Smith 
266a2e6df29SMike Smith static int
267a2e6df29SMike Smith devinfo_init_resources(int generation)
268a2e6df29SMike Smith {
269a2e6df29SMike Smith 	struct u_rman		urman;
270a2e6df29SMike Smith 	struct devinfo_i_rman	*dm;
271a2e6df29SMike Smith 	struct u_resource	ures;
272a2e6df29SMike Smith 	struct devinfo_i_res	*dr;
273a2e6df29SMike Smith 	int			rman_idx, res_idx;
274a2e6df29SMike Smith 	int			rman_ptr, res_ptr;
275a2e6df29SMike Smith 	int			name2oid[2];
276a2e6df29SMike Smith 	int			oid[CTL_MAXNAME + 12];
277a2e6df29SMike Smith 	size_t			oidlen, rlen;
278109a0e6bSPhilippe Charnier 	char			*name;
279109a0e6bSPhilippe Charnier 	int			error;
280a2e6df29SMike Smith 
281a2e6df29SMike Smith 	/*
282a2e6df29SMike Smith 	 * Find the OID for the rman interface node.
283a2e6df29SMike Smith 	 * This is just the usual evil, undocumented sysctl juju.
284a2e6df29SMike Smith 	 */
285a2e6df29SMike Smith 	name2oid[0] = 0;
286a2e6df29SMike Smith 	name2oid[1] = 3;
287a2e6df29SMike Smith 	oidlen = sizeof(oid);
288a2e6df29SMike Smith 	name = "hw.bus.rman";
289a2e6df29SMike Smith 	error = sysctl(name2oid, 2, oid, &oidlen, name, strlen(name));
290a2e6df29SMike Smith 	if (error < 0) {
291a2e6df29SMike Smith 		warnx("can't find hw.bus.rman sysctl node");
292a2e6df29SMike Smith 		return(ENOENT);
293a2e6df29SMike Smith 	}
294a2e6df29SMike Smith 	oidlen /= sizeof(int);
295a2e6df29SMike Smith 	if (oidlen > CTL_MAXNAME) {
296a2e6df29SMike Smith 		warnx("hw.bus.rman oid is too large");
297a2e6df29SMike Smith 		return(EINVAL);
298a2e6df29SMike Smith 	}
299a2e6df29SMike Smith 	oid[oidlen++] = generation;
300a2e6df29SMike Smith 	rman_ptr = oidlen++;
301a2e6df29SMike Smith 	res_ptr = oidlen++;
302a2e6df29SMike Smith 
303a2e6df29SMike Smith 	/*
304a2e6df29SMike Smith 	 * Scan resource managers.
305a2e6df29SMike Smith 	 *
306a2e6df29SMike Smith 	 * Stop after a fairly insane number to avoid death in the case
307a2e6df29SMike Smith 	 * of kernel corruption.
308a2e6df29SMike Smith 	 */
309a2e6df29SMike Smith 	for (rman_idx = 0; rman_idx < 255; rman_idx++) {
310a2e6df29SMike Smith 
311a2e6df29SMike Smith 		/*
312a2e6df29SMike Smith 		 * Get the resource manager information.
313a2e6df29SMike Smith 		 */
314a2e6df29SMike Smith 		oid[rman_ptr] = rman_idx;
315a2e6df29SMike Smith 		oid[res_ptr] = -1;
316a2e6df29SMike Smith 		rlen = sizeof(urman);
317a2e6df29SMike Smith 		error = sysctl(oid, oidlen, &urman, &rlen, NULL, 0);
318a2e6df29SMike Smith 		if (error < 0) {
319a2e6df29SMike Smith 			if (errno == ENOENT)	/* end of list */
320a2e6df29SMike Smith 				break;
321a2e6df29SMike Smith 			if (errno != EINVAL)	/* gen count skip, restart */
322a2e6df29SMike Smith 				warn("sysctl hw.bus.rman.%d", rman_idx);
323a2e6df29SMike Smith 			return(errno);
324a2e6df29SMike Smith 		}
325a2e6df29SMike Smith 		if ((dm = malloc(sizeof(*dm))) == NULL)
326a2e6df29SMike Smith 			return(ENOMEM);
327a2e6df29SMike Smith 		dm->dm_rman.dm_handle = urman.rm_handle;
328a2e6df29SMike Smith 		dm->dm_rman.dm_start = urman.rm_start;
329a2e6df29SMike Smith 		dm->dm_rman.dm_size = urman.rm_size;
330a2e6df29SMike Smith 		snprintf(dm->dm_desc, DEVINFO_STRLEN, "%s", urman.rm_descr);
331a2e6df29SMike Smith 		dm->dm_rman.dm_desc = &dm->dm_desc[0];
332a2e6df29SMike Smith 		TAILQ_INSERT_TAIL(&devinfo_rman, dm, dm_link);
333a2e6df29SMike Smith 
334a2e6df29SMike Smith 		/*
335a2e6df29SMike Smith 		 * Scan resources on this resource manager.
336a2e6df29SMike Smith 		 *
337a2e6df29SMike Smith 		 * Stop after a fairly insane number to avoid death in the case
338a2e6df29SMike Smith 		 * of kernel corruption.
339a2e6df29SMike Smith 		 */
340a2e6df29SMike Smith 		for (res_idx = 0; res_idx < 1000; res_idx++) {
341a2e6df29SMike Smith 			/*
342a2e6df29SMike Smith 			 * Get the resource information.
343a2e6df29SMike Smith 			 */
344a2e6df29SMike Smith 			oid[res_ptr] = res_idx;
345a2e6df29SMike Smith 			rlen = sizeof(ures);
346a2e6df29SMike Smith 			error = sysctl(oid, oidlen, &ures, &rlen, NULL, 0);
347a2e6df29SMike Smith 			if (error < 0) {
348a2e6df29SMike Smith 				if (errno == ENOENT)	/* end of list */
349a2e6df29SMike Smith 					break;
350a2e6df29SMike Smith 				if (errno != EINVAL)	/* gen count skip */
351a2e6df29SMike Smith 					warn("sysctl hw.bus.rman.%d.%d",
352a2e6df29SMike Smith 					    rman_idx, res_idx);
353a2e6df29SMike Smith 				return(errno);
354a2e6df29SMike Smith 			}
355a2e6df29SMike Smith 			if ((dr = malloc(sizeof(*dr))) == NULL)
356a2e6df29SMike Smith 				return(ENOMEM);
357a2e6df29SMike Smith 			dr->dr_res.dr_handle = ures.r_handle;
358a2e6df29SMike Smith 			dr->dr_res.dr_rman = ures.r_parent;
359a2e6df29SMike Smith 			dr->dr_res.dr_device = ures.r_device;
360a2e6df29SMike Smith 			dr->dr_res.dr_start = ures.r_start;
361a2e6df29SMike Smith 			dr->dr_res.dr_size = ures.r_size;
362a2e6df29SMike Smith 			TAILQ_INSERT_TAIL(&devinfo_res, dr, dr_link);
363a2e6df29SMike Smith 		}
364a2e6df29SMike Smith 		debug("fetched %d resources", res_idx);
365a2e6df29SMike Smith 	}
366a2e6df29SMike Smith 	debug("scanned %d resource managers", rman_idx);
367a2e6df29SMike Smith 	return(0);
368a2e6df29SMike Smith }
369a2e6df29SMike Smith 
370a2e6df29SMike Smith /*
37132592d86SEric van Gyzen  * Free an individual dev.
37232592d86SEric van Gyzen  */
37332592d86SEric van Gyzen static void
37432592d86SEric van Gyzen devinfo_free_dev(struct devinfo_i_dev *dd)
37532592d86SEric van Gyzen {
37632592d86SEric van Gyzen 	free(dd->dd_name);
37732592d86SEric van Gyzen 	free(dd->dd_desc);
37832592d86SEric van Gyzen 	free(dd->dd_drivername);
37932592d86SEric van Gyzen 	free(dd->dd_pnpinfo);
38032592d86SEric van Gyzen 	free(dd->dd_location);
38132592d86SEric van Gyzen 	free(dd);
38232592d86SEric van Gyzen }
38332592d86SEric van Gyzen 
38432592d86SEric van Gyzen /*
385a2e6df29SMike Smith  * Free the list contents.
386a2e6df29SMike Smith  */
387a2e6df29SMike Smith void
388a2e6df29SMike Smith devinfo_free(void)
389a2e6df29SMike Smith {
390a2e6df29SMike Smith 	struct devinfo_i_dev	*dd;
391a2e6df29SMike Smith 	struct devinfo_i_rman	*dm;
392a2e6df29SMike Smith 	struct devinfo_i_res	*dr;
393a2e6df29SMike Smith 
394a2e6df29SMike Smith 	while ((dd = TAILQ_FIRST(&devinfo_dev)) != NULL) {
395a2e6df29SMike Smith 		TAILQ_REMOVE(&devinfo_dev, dd, dd_link);
39632592d86SEric van Gyzen 		devinfo_free_dev(dd);
397a2e6df29SMike Smith 	}
398a2e6df29SMike Smith 	while ((dm = TAILQ_FIRST(&devinfo_rman)) != NULL) {
399a2e6df29SMike Smith 		TAILQ_REMOVE(&devinfo_rman, dm, dm_link);
400a2e6df29SMike Smith 		free(dm);
401a2e6df29SMike Smith 	}
402a2e6df29SMike Smith 	while ((dr = TAILQ_FIRST(&devinfo_res)) != NULL) {
403a2e6df29SMike Smith 		TAILQ_REMOVE(&devinfo_res, dr, dr_link);
404a2e6df29SMike Smith 		free(dr);
405a2e6df29SMike Smith 	}
406a2e6df29SMike Smith 	devinfo_initted = 0;
407afc7f38cSJohn Baldwin 	devinfo_generation = 0;
408a2e6df29SMike Smith }
409a2e6df29SMike Smith 
410a2e6df29SMike Smith /*
411a2e6df29SMike Smith  * Find a device by its handle.
412a2e6df29SMike Smith  */
413a2e6df29SMike Smith struct devinfo_dev *
414a2e6df29SMike Smith devinfo_handle_to_device(devinfo_handle_t handle)
415a2e6df29SMike Smith {
416a2e6df29SMike Smith 	struct devinfo_i_dev	*dd;
417a2e6df29SMike Smith 
418a2e6df29SMike Smith 	/*
419a2e6df29SMike Smith 	 * Find the root device, whose parent is NULL
420a2e6df29SMike Smith 	 */
421a2e6df29SMike Smith 	if (handle == DEVINFO_ROOT_DEVICE) {
422a2e6df29SMike Smith 		TAILQ_FOREACH(dd, &devinfo_dev, dd_link)
423*7554746cSJohn Baldwin 		    if (dd->dd_dev.dd_parent == 0)
424a2e6df29SMike Smith 			    return(&dd->dd_dev);
425a2e6df29SMike Smith 		return(NULL);
426a2e6df29SMike Smith 	}
427a2e6df29SMike Smith 
428a2e6df29SMike Smith 	/*
429a2e6df29SMike Smith 	 * Scan for the device
430a2e6df29SMike Smith 	 */
431a2e6df29SMike Smith 	TAILQ_FOREACH(dd, &devinfo_dev, dd_link)
432a2e6df29SMike Smith 	    if (dd->dd_dev.dd_handle == handle)
433a2e6df29SMike Smith 		    return(&dd->dd_dev);
434a2e6df29SMike Smith 	return(NULL);
435a2e6df29SMike Smith }
436a2e6df29SMike Smith 
437a2e6df29SMike Smith /*
438a2e6df29SMike Smith  * Find a resource by its handle.
439a2e6df29SMike Smith  */
440a2e6df29SMike Smith struct devinfo_res *
441a2e6df29SMike Smith devinfo_handle_to_resource(devinfo_handle_t handle)
442a2e6df29SMike Smith {
443a2e6df29SMike Smith 	struct devinfo_i_res	*dr;
444a2e6df29SMike Smith 
445a2e6df29SMike Smith 	TAILQ_FOREACH(dr, &devinfo_res, dr_link)
446a2e6df29SMike Smith 	    if (dr->dr_res.dr_handle == handle)
447a2e6df29SMike Smith 		    return(&dr->dr_res);
448a2e6df29SMike Smith 	return(NULL);
449a2e6df29SMike Smith }
450a2e6df29SMike Smith 
451a2e6df29SMike Smith /*
452a2e6df29SMike Smith  * Find a resource manager by its handle.
453a2e6df29SMike Smith  */
454a2e6df29SMike Smith struct devinfo_rman *
455a2e6df29SMike Smith devinfo_handle_to_rman(devinfo_handle_t handle)
456a2e6df29SMike Smith {
457a2e6df29SMike Smith 	struct devinfo_i_rman	*dm;
458a2e6df29SMike Smith 
459a2e6df29SMike Smith 	TAILQ_FOREACH(dm, &devinfo_rman, dm_link)
460a2e6df29SMike Smith 	    if (dm->dm_rman.dm_handle == handle)
461a2e6df29SMike Smith 		    return(&dm->dm_rman);
462a2e6df29SMike Smith 	return(NULL);
463a2e6df29SMike Smith }
464a2e6df29SMike Smith 
465a2e6df29SMike Smith /*
466a2e6df29SMike Smith  * Iterate over the children of a device, calling (fn) on each.  If
467a2e6df29SMike Smith  * (fn) returns nonzero, abort the scan and return.
468a2e6df29SMike Smith  */
469a2e6df29SMike Smith int
470a2e6df29SMike Smith devinfo_foreach_device_child(struct devinfo_dev *parent,
471a2e6df29SMike Smith     int (* fn)(struct devinfo_dev *child, void *arg),
472a2e6df29SMike Smith     void *arg)
473a2e6df29SMike Smith {
474a2e6df29SMike Smith 	struct devinfo_i_dev	*dd;
475a2e6df29SMike Smith 	int				error;
476a2e6df29SMike Smith 
477a2e6df29SMike Smith 	TAILQ_FOREACH(dd, &devinfo_dev, dd_link)
478a2e6df29SMike Smith 	    if (dd->dd_dev.dd_parent == parent->dd_handle)
479a2e6df29SMike Smith 		    if ((error = fn(&dd->dd_dev, arg)) != 0)
480a2e6df29SMike Smith 			    return(error);
481a2e6df29SMike Smith 	return(0);
482a2e6df29SMike Smith }
483a2e6df29SMike Smith 
484a2e6df29SMike Smith /*
485a2e6df29SMike Smith  * Iterate over all the resources owned by a device, calling (fn) on each.
486a2e6df29SMike Smith  * If (fn) returns nonzero, abort the scan and return.
487a2e6df29SMike Smith  */
488a2e6df29SMike Smith int
489a2e6df29SMike Smith devinfo_foreach_device_resource(struct devinfo_dev *dev,
490a2e6df29SMike Smith     int (* fn)(struct devinfo_dev *dev, struct devinfo_res *res, void *arg),
491a2e6df29SMike Smith     void *arg)
492a2e6df29SMike Smith {
493a2e6df29SMike Smith 	struct devinfo_i_res	*dr;
494a2e6df29SMike Smith 	int				error;
495a2e6df29SMike Smith 
496a2e6df29SMike Smith 	TAILQ_FOREACH(dr, &devinfo_res, dr_link)
497a2e6df29SMike Smith 	    if (dr->dr_res.dr_device == dev->dd_handle)
498a2e6df29SMike Smith 		    if ((error = fn(dev, &dr->dr_res, arg)) != 0)
499a2e6df29SMike Smith 			    return(error);
500a2e6df29SMike Smith 	return(0);
501a2e6df29SMike Smith }
502a2e6df29SMike Smith 
503a2e6df29SMike Smith /*
504a2e6df29SMike Smith  * Iterate over all the resources owned by a resource manager, calling (fn)
505a2e6df29SMike Smith  * on each.  If (fn) returns nonzero, abort the scan and return.
506a2e6df29SMike Smith  */
507a2e6df29SMike Smith extern int
508a2e6df29SMike Smith devinfo_foreach_rman_resource(struct devinfo_rman *rman,
509a2e6df29SMike Smith     int (* fn)(struct devinfo_res *res, void *arg),
510a2e6df29SMike Smith     void *arg)
511a2e6df29SMike Smith {
512a2e6df29SMike Smith 	struct devinfo_i_res	*dr;
513a2e6df29SMike Smith 	int				error;
514a2e6df29SMike Smith 
515a2e6df29SMike Smith 	TAILQ_FOREACH(dr, &devinfo_res, dr_link)
516a2e6df29SMike Smith 	    if (dr->dr_res.dr_rman == rman->dm_handle)
517a2e6df29SMike Smith 		    if ((error = fn(&dr->dr_res, arg)) != 0)
518a2e6df29SMike Smith 			    return(error);
519a2e6df29SMike Smith 	return(0);
520a2e6df29SMike Smith }
521a2e6df29SMike Smith 
522a2e6df29SMike Smith /*
523a2e6df29SMike Smith  * Iterate over all the resource managers, calling (fn) on each.  If (fn)
524a2e6df29SMike Smith  * returns nonzero, abort the scan and return.
525a2e6df29SMike Smith  */
526a2e6df29SMike Smith extern int
527a2e6df29SMike Smith devinfo_foreach_rman(int (* fn)(struct devinfo_rman *rman, void *arg),
528a2e6df29SMike Smith     void *arg)
529a2e6df29SMike Smith {
530a2e6df29SMike Smith     struct devinfo_i_rman	*dm;
531a2e6df29SMike Smith     int				error;
532a2e6df29SMike Smith 
533a2e6df29SMike Smith     TAILQ_FOREACH(dm, &devinfo_rman, dm_link)
534a2e6df29SMike Smith 	if ((error = fn(&dm->dm_rman, arg)) != 0)
535a2e6df29SMike Smith 	    return(error);
536a2e6df29SMike Smith     return(0);
537a2e6df29SMike Smith }
538