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/devce 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(fmt, args...) \ 87 fprintf(stderr, "%s:" fmt "\n", __FUNCTION__ , ##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 */ 96 int 97 devinfo_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 162 static int 163 devinfo_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 243 static int 244 devinfo_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 */ 350 void 351 devinfo_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 */ 375 struct devinfo_dev * 376 devinfo_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 */ 402 struct devinfo_res * 403 devinfo_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 */ 416 struct devinfo_rman * 417 devinfo_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 */ 431 int 432 devinfo_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 */ 450 int 451 devinfo_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 */ 469 extern int 470 devinfo_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 */ 488 extern int 489 devinfo_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