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