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 28 #include <sys/cdefs.h> 29 __FBSDID("$FreeBSD$"); 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 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 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 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 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 * 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 * 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 * 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 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 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 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 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