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