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