1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright (c) 2009, Intel Corporation. 23 * All rights reserved. 24 */ 25 26 /* 27 * Platform specific device enumerator for ACPI specific devices. 28 * "x86 system devices" refers to the suite of hardware components which are 29 * common to the x86 platform and play important roles in the system 30 * architecture but can't be enumerated/discovered through industry-standard 31 * bus specifications. Examples of these x86 system devices include: 32 * * Logical processor/CPU 33 * * Memory device 34 * * Non-PCI discoverable IOMMU or DMA Remapping Engine 35 * * Non-PCI discoverable IOxAPIC 36 * * Non-PCI discoverable HPET (High Precision Event Timer) 37 * * ACPI defined devices, including power button, sleep button, battery etc. 38 * 39 * X86 system devices may be discovered through BIOS/Firmware interfaces, such 40 * as SMBIOS tables, MPS tables and ACPI tables since their discovery isn't 41 * covered by any industry-standard bus specifications. 42 * 43 * In order to aid Solaris in flexibly managing x86 system devices, 44 * x86 system devices are placed into a specific firmware device 45 * subtree whose device path is '/devices/fw'. 46 * 47 * This driver populates the firmware device subtree with ACPI-discoverable 48 * system devices if possible. To achieve that, the ACPI object 49 * namespace is abstracted as ACPI virtual buses which host system devices. 50 * Another nexus driver for the ACPI virtual bus will manage all devices 51 * connected to it. 52 * 53 * For more detailed information, please refer to PSARC/2009/104. 54 */ 55 56 #include <sys/types.h> 57 #include <sys/bitmap.h> 58 #include <sys/cmn_err.h> 59 #include <sys/ddi_subrdefs.h> 60 #include <sys/errno.h> 61 #include <sys/modctl.h> 62 #include <sys/mutex.h> 63 #include <sys/obpdefs.h> 64 #include <sys/sunddi.h> 65 #include <sys/sunndi.h> 66 #include <sys/acpi/acpi.h> 67 #include <sys/acpica.h> 68 #include <sys/acpidev.h> 69 #include <sys/acpidev_impl.h> 70 71 /* Patchable through /etc/system */ 72 int acpidev_options = 0; 73 int acpidev_debug = 0; 74 75 acpidev_class_list_t *acpidev_class_list_root = NULL; 76 77 /* ACPI device autoconfig global status */ 78 typedef enum acpidev_status { 79 ACPIDEV_STATUS_FAILED = -2, /* ACPI device autoconfig failed */ 80 ACPIDEV_STATUS_DISABLED = -1, /* ACPI device autoconfig disabled */ 81 ACPIDEV_STATUS_UNKNOWN = 0, /* initial status */ 82 ACPIDEV_STATUS_INITIALIZED, /* ACPI device autoconfig initialized */ 83 ACPIDEV_STATUS_FIRST_PASS, /* first probing finished */ 84 ACPIDEV_STATUS_READY /* second probing finished */ 85 } acpidev_status_t; 86 87 static acpidev_status_t acpidev_status = ACPIDEV_STATUS_UNKNOWN; 88 static kmutex_t acpidev_drv_lock; 89 static krwlock_t acpidev_class_lock; 90 static dev_info_t *acpidev_root_dip = NULL; 91 static ulong_t acpidev_object_type_mask[BT_BITOUL(ACPI_TYPE_NS_NODE_MAX + 1)]; 92 93 /* Boot time ACPI device enumerator. */ 94 static void acpidev_boot_probe(int type); 95 96 /* DDI module auto configuration interface */ 97 extern struct mod_ops mod_miscops; 98 99 static struct modlmisc modlmisc = { 100 &mod_miscops, 101 "ACPI device enumerator" 102 }; 103 104 static struct modlinkage modlinkage = { 105 MODREV_1, 106 (void *)&modlmisc, 107 NULL 108 }; 109 110 int 111 _init(void) 112 { 113 int err; 114 115 if ((err = mod_install(&modlinkage)) == 0) { 116 bzero(acpidev_object_type_mask, 117 sizeof (acpidev_object_type_mask)); 118 mutex_init(&acpidev_drv_lock, NULL, MUTEX_DRIVER, NULL); 119 rw_init(&acpidev_class_lock, NULL, RW_DEFAULT, NULL); 120 impl_bus_add_probe(acpidev_boot_probe); 121 } else { 122 cmn_err(CE_WARN, "!acpidev: failed to install driver."); 123 } 124 125 return (err); 126 } 127 128 int 129 _fini(void) 130 { 131 /* No support for module unload. */ 132 return (EBUSY); 133 } 134 135 int 136 _info(struct modinfo *modinfop) 137 { 138 return (mod_info(&modlinkage, modinfop)); 139 } 140 141 /* Check blacklists and load platform specific driver modules. */ 142 static ACPI_STATUS 143 acpidev_load_plat_modules(void) 144 { 145 return (AE_OK); 146 } 147 148 /* Unload platform specific driver modules. */ 149 static void 150 acpidev_unload_plat_modules(void) 151 { 152 } 153 154 /* Unregister all device class drivers from the device driver lists. */ 155 static void 156 acpidev_class_list_fini(void) 157 { 158 acpidev_unload_plat_modules(); 159 160 if ((acpidev_options & ACPIDEV_OUSER_NO_MEM) == 0) { 161 (void) acpidev_unregister_class(&acpidev_class_list_device, 162 &acpidev_class_memory); 163 } 164 165 if (acpidev_options & ACPIDEV_OUSER_NO_CPU) { 166 (void) acpidev_unregister_class(&acpidev_class_list_device, 167 &acpidev_class_cpu); 168 (void) acpidev_unregister_class(&acpidev_class_list_scope, 169 &acpidev_class_cpu); 170 (void) acpidev_unregister_class(&acpidev_class_list_root, 171 &acpidev_class_cpu); 172 } 173 174 if ((acpidev_options & ACPIDEV_OUSER_NO_CONTAINER) == 0) { 175 (void) acpidev_unregister_class(&acpidev_class_list_device, 176 &acpidev_class_container); 177 } 178 179 (void) acpidev_unregister_class(&acpidev_class_list_device, 180 &acpidev_class_device); 181 (void) acpidev_unregister_class(&acpidev_class_list_root, 182 &acpidev_class_device); 183 184 (void) acpidev_unregister_class(&acpidev_class_list_root, 185 &acpidev_class_scope); 186 } 187 188 /* Register all device class drivers onto the driver lists. */ 189 static ACPI_STATUS 190 acpidev_class_list_init(uint64_t *fp) 191 { 192 ACPI_STATUS rc = AE_OK; 193 194 /* Set bit in mask for supported object types. */ 195 BT_SET(acpidev_object_type_mask, ACPI_TYPE_LOCAL_SCOPE); 196 BT_SET(acpidev_object_type_mask, ACPI_TYPE_DEVICE); 197 198 /* 199 * Register the ACPI scope class driver onto the class driver lists. 200 * Currently only ACPI scope objects under ACPI root node, such as _PR, 201 * _SB, _TZ etc, need to be handled, so only register the scope class 202 * driver onto the root list. 203 */ 204 if (ACPI_FAILURE(acpidev_register_class(&acpidev_class_list_root, 205 &acpidev_class_scope, B_FALSE))) { 206 goto error_out; 207 } 208 209 /* 210 * Register the ACPI device class driver onto the class driver lists. 211 * The ACPI device class driver should be registered at the tail to 212 * handle all device objects which haven't been handled by other 213 * HID/CID specific device class drivers. 214 */ 215 if (ACPI_FAILURE(acpidev_register_class(&acpidev_class_list_root, 216 &acpidev_class_device, B_TRUE))) { 217 goto error_root_device; 218 } 219 if (ACPI_FAILURE(acpidev_register_class(&acpidev_class_list_device, 220 &acpidev_class_device, B_TRUE))) { 221 goto error_device_device; 222 } 223 224 /* Check and register support for ACPI container device. */ 225 if ((acpidev_options & ACPIDEV_OUSER_NO_CONTAINER) == 0) { 226 if (ACPI_FAILURE(acpidev_register_class( 227 &acpidev_class_list_device, &acpidev_class_container, 228 B_FALSE))) { 229 goto error_device_container; 230 } 231 *fp |= ACPI_DEVCFG_CONTAINER; 232 } 233 234 /* Check and register support for ACPI CPU device. */ 235 if ((acpidev_options & ACPIDEV_OUSER_NO_CPU) == 0) { 236 /* Handle ACPI CPU Device */ 237 if (ACPI_FAILURE(acpidev_register_class( 238 &acpidev_class_list_device, &acpidev_class_cpu, B_FALSE))) { 239 goto error_device_cpu; 240 } 241 /* Handle ACPI Processor under _PR */ 242 if (ACPI_FAILURE(acpidev_register_class( 243 &acpidev_class_list_scope, &acpidev_class_cpu, B_FALSE))) { 244 goto error_scope_cpu; 245 } 246 /* House-keeping for CPU scan */ 247 if (ACPI_FAILURE(acpidev_register_class( 248 &acpidev_class_list_root, &acpidev_class_cpu, B_FALSE))) { 249 goto error_root_cpu; 250 } 251 BT_SET(acpidev_object_type_mask, ACPI_TYPE_PROCESSOR); 252 *fp |= ACPI_DEVCFG_CPU; 253 } 254 255 /* Check support for ACPI memory device. */ 256 if ((acpidev_options & ACPIDEV_OUSER_NO_MEM) == 0) { 257 /* 258 * Register the ACPI memory class driver onto the 259 * acpidev_class_list_device list because ACPI module 260 * class driver uses that list. 261 */ 262 if (ACPI_FAILURE(acpidev_register_class( 263 &acpidev_class_list_device, &acpidev_class_memory, 264 B_FALSE))) { 265 goto error_device_memory; 266 } 267 *fp |= ACPI_DEVCFG_MEMORY; 268 } 269 270 /* Check blacklist and load platform specific modules. */ 271 rc = acpidev_load_plat_modules(); 272 if (ACPI_FAILURE(rc)) { 273 ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to check blacklist " 274 "or load pratform modules."); 275 goto error_plat; 276 } 277 278 return (AE_OK); 279 280 error_plat: 281 if ((acpidev_options & ACPIDEV_OUSER_NO_MEM) == 0) { 282 (void) acpidev_unregister_class(&acpidev_class_list_device, 283 &acpidev_class_memory); 284 } 285 error_device_memory: 286 if (acpidev_options & ACPIDEV_OUSER_NO_CPU) { 287 (void) acpidev_unregister_class(&acpidev_class_list_root, 288 &acpidev_class_cpu); 289 } 290 error_root_cpu: 291 if (acpidev_options & ACPIDEV_OUSER_NO_CPU) { 292 (void) acpidev_unregister_class(&acpidev_class_list_scope, 293 &acpidev_class_cpu); 294 } 295 error_scope_cpu: 296 if (acpidev_options & ACPIDEV_OUSER_NO_CPU) { 297 (void) acpidev_unregister_class(&acpidev_class_list_device, 298 &acpidev_class_cpu); 299 } 300 error_device_cpu: 301 if ((acpidev_options & ACPIDEV_OUSER_NO_CONTAINER) == 0) { 302 (void) acpidev_unregister_class(&acpidev_class_list_device, 303 &acpidev_class_container); 304 } 305 error_device_container: 306 (void) acpidev_unregister_class(&acpidev_class_list_device, 307 &acpidev_class_device); 308 error_device_device: 309 (void) acpidev_unregister_class(&acpidev_class_list_root, 310 &acpidev_class_device); 311 error_root_device: 312 (void) acpidev_unregister_class(&acpidev_class_list_root, 313 &acpidev_class_scope); 314 error_out: 315 ACPIDEV_DEBUG(CE_WARN, 316 "acpidev: failed to register built-in class drivers."); 317 *fp = 0; 318 319 return (AE_ERROR); 320 } 321 322 /* 323 * Called in single threaded context during boot, no protection for 324 * reentrance. 325 */ 326 static ACPI_STATUS 327 acpidev_create_root_node(void) 328 { 329 int circ, rv = AE_OK; 330 dev_info_t *dip = NULL; 331 acpidev_data_handle_t objhdl; 332 char *compatibles[] = { 333 ACPIDEV_HID_ROOTNEX, 334 ACPIDEV_TYPE_ROOTNEX, 335 ACPIDEV_HID_VIRTNEX, 336 ACPIDEV_TYPE_VIRTNEX, 337 }; 338 339 ndi_devi_enter(ddi_root_node(), &circ); 340 ASSERT(acpidev_root_dip == NULL); 341 342 /* Query whether device node already exists. */ 343 dip = ddi_find_devinfo(ACPIDEV_NODE_NAME_ROOT, -1, 0); 344 if (dip != NULL && ddi_get_parent(dip) == ddi_root_node()) { 345 ndi_devi_exit(ddi_root_node(), circ); 346 cmn_err(CE_WARN, "!acpidev: node /devices/%s already exists, " 347 "disable driver.", ACPIDEV_NODE_NAME_ROOT); 348 return (AE_ALREADY_EXISTS); 349 } 350 351 /* Create the device node if it doesn't exist. */ 352 rv = ndi_devi_alloc(ddi_root_node(), ACPIDEV_NODE_NAME_ROOT, 353 (pnode_t)DEVI_SID_NODEID, &dip); 354 if (rv != NDI_SUCCESS) { 355 ndi_devi_exit(ddi_root_node(), circ); 356 ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to create device node " 357 "for ACPI root with errcode %d.", rv); 358 return (AE_ERROR); 359 } 360 361 /* Build cross reference between dip and ACPI object. */ 362 if (ACPI_FAILURE(acpica_tag_devinfo(dip, ACPI_ROOT_OBJECT))) { 363 (void) ddi_remove_child(dip, 0); 364 ndi_devi_exit(ddi_root_node(), circ); 365 ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to tag object %s.", 366 ACPIDEV_OBJECT_NAME_SB); 367 return (AE_ERROR); 368 } 369 370 /* Set device properties. */ 371 rv = ndi_prop_update_string_array(DDI_DEV_T_NONE, dip, 372 OBP_COMPATIBLE, ACPIDEV_ARRAY_PARAM(compatibles)); 373 if (rv == NDI_SUCCESS) { 374 rv = ndi_prop_update_string(DDI_DEV_T_NONE, dip, 375 OBP_DEVICETYPE, ACPIDEV_TYPE_ROOTNEX); 376 } 377 if (rv != DDI_SUCCESS) { 378 ACPIDEV_DEBUG(CE_WARN, 379 "acpidev: failed to set device property for /devices/%s.", 380 ACPIDEV_NODE_NAME_ROOT); 381 goto error_out; 382 } 383 384 /* Manually create an object handle for the root node */ 385 objhdl = acpidev_data_create_handle(ACPI_ROOT_OBJECT); 386 if (objhdl == NULL) { 387 ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to create object " 388 "handle for the root node."); 389 goto error_out; 390 } 391 objhdl->aod_level = 0; 392 objhdl->aod_hdl = ACPI_ROOT_OBJECT; 393 objhdl->aod_dip = dip; 394 objhdl->aod_class = &acpidev_class_scope; 395 objhdl->aod_status = acpidev_query_device_status(ACPI_ROOT_OBJECT); 396 objhdl->aod_iflag = ACPIDEV_ODF_STATUS_VALID | 397 ACPIDEV_ODF_DEVINFO_CREATED | ACPIDEV_ODF_DEVINFO_TAGGED; 398 399 /* Bind device driver. */ 400 (void) ndi_devi_bind_driver(dip, 0); 401 402 acpidev_root_dip = dip; 403 ndi_devi_exit(ddi_root_node(), circ); 404 405 return (AE_OK); 406 407 error_out: 408 (void) acpica_untag_devinfo(dip, ACPI_ROOT_OBJECT); 409 (void) ddi_remove_child(dip, 0); 410 ndi_devi_exit(ddi_root_node(), circ); 411 return (AE_ERROR); 412 } 413 414 static void 415 acpidev_initialize(void) 416 { 417 int rc; 418 char *str = NULL; 419 uint64_t features = 0; 420 421 /* Check whether it has already been initialized. */ 422 if (acpidev_status == ACPIDEV_STATUS_DISABLED) { 423 cmn_err(CE_CONT, "?acpidev: ACPI device autoconfig " 424 "disabled by user.\n"); 425 return; 426 } else if (acpidev_status != ACPIDEV_STATUS_UNKNOWN) { 427 ACPIDEV_DEBUG(CE_NOTE, 428 "acpidev: initialization called more than once."); 429 return; 430 } 431 432 /* Check whether ACPI device autoconfig has been disabled by user. */ 433 rc = ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_root_node(), 434 DDI_PROP_DONTPASS, "acpidev-autoconfig", &str); 435 if (rc == DDI_SUCCESS) { 436 if (strcasecmp(str, "off") == 0 || strcasecmp(str, "no") == 0) { 437 cmn_err(CE_CONT, "?acpidev: ACPI device autoconfig " 438 "disabled by user.\n"); 439 ddi_prop_free(str); 440 acpidev_status = ACPIDEV_STATUS_DISABLED; 441 return; 442 } 443 ddi_prop_free(str); 444 } 445 446 /* Initialize acpica subsystem. */ 447 if (ACPI_FAILURE(acpica_init())) { 448 cmn_err(CE_WARN, 449 "!acpidev: failed to initialize acpica subsystem."); 450 acpidev_status = ACPIDEV_STATUS_FAILED; 451 return; 452 } 453 454 /* Check ACPICA subsystem status. */ 455 if (!acpica_get_core_feature(ACPI_FEATURE_FULL_INIT)) { 456 cmn_err(CE_WARN, "!acpidev: ACPICA hasn't been fully " 457 "initialized, ACPI device autoconfig will be disabled."); 458 acpidev_status = ACPIDEV_STATUS_DISABLED; 459 return; 460 } 461 462 /* Converts acpidev-options from type string to int, if any */ 463 if (ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_root_node(), 464 DDI_PROP_DONTPASS, "acpidev-options", &str) == DDI_PROP_SUCCESS) { 465 long data; 466 rc = ddi_strtol(str, NULL, 0, &data); 467 if (rc == 0) { 468 (void) e_ddi_prop_remove(DDI_DEV_T_NONE, 469 ddi_root_node(), "acpidev-options"); 470 (void) e_ddi_prop_update_int(DDI_DEV_T_NONE, 471 ddi_root_node(), "acpidev-options", data); 472 } 473 ddi_prop_free(str); 474 } 475 /* Get acpidev_options user options. */ 476 acpidev_options = ddi_prop_get_int(DDI_DEV_T_ANY, ddi_root_node(), 477 DDI_PROP_DONTPASS, "acpidev-options", acpidev_options); 478 479 /* Register all device class drivers. */ 480 if (ACPI_FAILURE(acpidev_class_list_init(&features))) { 481 cmn_err(CE_WARN, 482 "!acpidev: failed to initalize class driver lists."); 483 acpidev_status = ACPIDEV_STATUS_FAILED; 484 return; 485 } 486 487 /* Create root node for ACPI/firmware device subtree. */ 488 if (ACPI_FAILURE(acpidev_create_root_node())) { 489 cmn_err(CE_WARN, "!acpidev: failed to create root node " 490 "for acpi device tree."); 491 acpidev_class_list_fini(); 492 acpidev_status = ACPIDEV_STATUS_FAILED; 493 return; 494 } 495 496 /* Notify acpica to enable ACPI device auto configuration. */ 497 acpica_set_core_feature(ACPI_FEATURE_DEVCFG); 498 acpica_set_devcfg_feature(features); 499 500 ACPIDEV_DEBUG(CE_NOTE, "!acpidev: ACPI device autoconfig initialized."); 501 acpidev_status = ACPIDEV_STATUS_INITIALIZED; 502 } 503 504 /* 505 * Probe devices in ACPI namespace which can't be enumerated by other methods 506 * at boot time. 507 */ 508 static ACPI_STATUS 509 acpidev_boot_probe_device(acpidev_op_type_t op_type) 510 { 511 ACPI_STATUS rc = AE_OK; 512 acpidev_walk_info_t *infop; 513 514 ASSERT(acpidev_root_dip != NULL); 515 ASSERT(op_type == ACPIDEV_OP_BOOT_PROBE || 516 op_type == ACPIDEV_OP_BOOT_REPROBE); 517 518 infop = acpidev_alloc_walk_info(op_type, 0, ACPI_ROOT_OBJECT, 519 &acpidev_class_list_root, NULL); 520 if (infop == NULL) { 521 ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to allocate walk info " 522 "object in acpi_boot_probe_device()."); 523 return (AE_ERROR); 524 } 525 /* Enumerate ACPI devices. */ 526 rc = acpidev_probe_child(infop); 527 if (ACPI_FAILURE(rc)) { 528 cmn_err(CE_WARN, "!acpidev: failed to probe child object " 529 "under ACPI root node."); 530 } 531 acpidev_free_walk_info(infop); 532 533 return (rc); 534 } 535 536 /* 537 * Platform specific device prober for ACPI virtual bus. 538 * It will be called in single-threaded environment to enumerate devices in 539 * ACPI namespace at boot time. 540 */ 541 static void 542 acpidev_boot_probe(int type) 543 { 544 ACPI_STATUS rc; 545 546 /* Initialize subsystem on first pass. */ 547 mutex_enter(&acpidev_drv_lock); 548 if (type == 0) { 549 acpidev_initialize(); 550 if (acpidev_status != ACPIDEV_STATUS_INITIALIZED && 551 acpidev_status != ACPIDEV_STATUS_DISABLED) { 552 cmn_err(CE_WARN, "!acpidev: driver disabled due to " 553 "initalization failure."); 554 } 555 } 556 557 /* Probe ACPI devices */ 558 if (type == 0 && acpidev_status == ACPIDEV_STATUS_INITIALIZED) { 559 rc = acpidev_boot_probe_device(ACPIDEV_OP_BOOT_PROBE); 560 if (ACPI_SUCCESS(rc)) { 561 acpidev_status = ACPIDEV_STATUS_FIRST_PASS; 562 } else { 563 ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to probe ACPI " 564 "devices during boot."); 565 acpidev_status = ACPIDEV_STATUS_FAILED; 566 } 567 } else if (type != 0 && acpidev_status == ACPIDEV_STATUS_FIRST_PASS) { 568 rc = acpidev_boot_probe_device(ACPIDEV_OP_BOOT_REPROBE); 569 if (ACPI_SUCCESS(rc)) { 570 acpidev_status = ACPIDEV_STATUS_READY; 571 } else { 572 ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to reprobe " 573 "ACPI devices during boot."); 574 acpidev_status = ACPIDEV_STATUS_FAILED; 575 } 576 } else if (acpidev_status != ACPIDEV_STATUS_FAILED && 577 acpidev_status != ACPIDEV_STATUS_DISABLED && 578 acpidev_status != ACPIDEV_STATUS_READY) { 579 cmn_err(CE_WARN, 580 "!acpidev: invalid ACPI device autoconfig global status."); 581 } 582 mutex_exit(&acpidev_drv_lock); 583 } 584 585 ACPI_STATUS 586 acpidev_probe_child(acpidev_walk_info_t *infop) 587 { 588 int circ; 589 dev_info_t *pdip; 590 ACPI_STATUS res, rc = AE_OK; 591 ACPI_HANDLE child; 592 ACPI_OBJECT_TYPE type; 593 acpidev_class_list_t *it; 594 acpidev_walk_info_t *cinfop; 595 acpidev_data_handle_t datap; 596 597 /* Validate parameter first. */ 598 ASSERT(infop != NULL); 599 if (infop == NULL) { 600 ACPIDEV_DEBUG(CE_WARN, 601 "acpidev: infop is NULL in acpidev_probe_child()."); 602 return (AE_BAD_PARAMETER); 603 } 604 ASSERT(infop->awi_level < ACPIDEV_MAX_ENUM_LEVELS - 1); 605 if (infop->awi_level >= ACPIDEV_MAX_ENUM_LEVELS - 1) { 606 ACPIDEV_DEBUG(CE_WARN, "acpidev: recursive level is too deep " 607 "in acpidev_probe_child()."); 608 return (AE_BAD_PARAMETER); 609 } 610 ASSERT(infop->awi_class_list != NULL); 611 ASSERT(infop->awi_hdl != NULL); 612 ASSERT(infop->awi_info != NULL); 613 ASSERT(infop->awi_name != NULL); 614 ASSERT(infop->awi_data != NULL); 615 if (infop->awi_class_list == NULL || infop->awi_hdl == NULL || 616 infop->awi_info == NULL || infop->awi_name == NULL || 617 infop->awi_data == NULL) { 618 ACPIDEV_DEBUG(CE_WARN, 619 "acpidev: infop has NULL fields in acpidev_probe_child()."); 620 return (AE_BAD_PARAMETER); 621 } 622 pdip = acpidev_walk_info_get_pdip(infop); 623 if (pdip == NULL) { 624 ACPIDEV_DEBUG(CE_WARN, 625 "acpidev: pdip is NULL in acpidev_probe_child()."); 626 return (AE_BAD_PARAMETER); 627 } 628 629 ndi_devi_enter(pdip, &circ); 630 rw_enter(&acpidev_class_lock, RW_READER); 631 632 /* Call pre-probe callback functions. */ 633 for (it = *(infop->awi_class_list); it != NULL; it = it->acl_next) { 634 if (it->acl_class->adc_pre_probe == NULL) { 635 continue; 636 } 637 infop->awi_class_curr = it->acl_class; 638 if (ACPI_FAILURE(it->acl_class->adc_pre_probe(infop))) { 639 ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to pre-probe " 640 "device of type %s under %s.", 641 it->acl_class->adc_class_name, infop->awi_name); 642 } 643 } 644 645 /* Walk child objects. */ 646 child = NULL; 647 while (ACPI_SUCCESS(AcpiGetNextObject(ACPI_TYPE_ANY, 648 infop->awi_hdl, child, &child))) { 649 /* Skip object if we're not interested in it. */ 650 if (ACPI_FAILURE(AcpiGetType(child, &type)) || 651 type > ACPI_TYPE_NS_NODE_MAX || 652 BT_TEST(acpidev_object_type_mask, type) == 0) { 653 continue; 654 } 655 656 /* Allocate the walk info structure. */ 657 cinfop = acpidev_alloc_walk_info(infop->awi_op_type, 658 infop->awi_level + 1, child, NULL, infop); 659 if (cinfop == NULL) { 660 ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to allocate " 661 "walk info child object of %s.", 662 infop->awi_name); 663 /* Mark error and continue to handle next child. */ 664 rc = AE_ERROR; 665 continue; 666 } 667 668 /* 669 * Remember the class list used to handle this object. 670 * It should be the same list for different passes of scans. 671 */ 672 ASSERT(cinfop->awi_data != NULL); 673 datap = cinfop->awi_data; 674 if (cinfop->awi_op_type == ACPIDEV_OP_BOOT_PROBE) { 675 datap->aod_class_list = infop->awi_class_list; 676 } else if (datap->aod_class_list != infop->awi_class_list) { 677 ACPIDEV_DEBUG(CE_WARN, 678 "acpidev: class list for %s has been changed", 679 infop->awi_name); 680 acpidev_free_walk_info(cinfop); 681 continue; 682 } 683 684 /* Call registered process callbacks. */ 685 for (it = *(infop->awi_class_list); it != NULL; 686 it = it->acl_next) { 687 if (it->acl_class->adc_probe == NULL) { 688 continue; 689 } 690 cinfop->awi_class_curr = it->acl_class; 691 res = it->acl_class->adc_probe(cinfop); 692 if (ACPI_FAILURE(res)) { 693 rc = res; 694 ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to " 695 "process object of type %s under %s.", 696 it->acl_class->adc_class_name, 697 infop->awi_name); 698 } 699 } 700 701 /* Free resources. */ 702 acpidev_free_walk_info(cinfop); 703 } 704 705 /* Call post-probe callback functions. */ 706 for (it = *(infop->awi_class_list); it != NULL; it = it->acl_next) { 707 if (it->acl_class->adc_post_probe == NULL) { 708 continue; 709 } 710 infop->awi_class_curr = it->acl_class; 711 if (ACPI_FAILURE(it->acl_class->adc_post_probe(infop))) { 712 ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to post-probe " 713 "device of type %s under %s.", 714 it->acl_class->adc_class_name, infop->awi_name); 715 } 716 } 717 718 rw_exit(&acpidev_class_lock); 719 ndi_devi_exit(pdip, circ); 720 721 return (rc); 722 } 723 724 ACPI_STATUS 725 acpidev_process_object(acpidev_walk_info_t *infop, int flags) 726 { 727 ACPI_STATUS rc = AE_OK; 728 char *devname; 729 dev_info_t *dip, *pdip; 730 ACPI_HANDLE hdl; 731 ACPI_DEVICE_INFO *adip; 732 acpidev_class_t *clsp; 733 acpidev_data_handle_t datap; 734 acpidev_filter_result_t res; 735 736 /* Validate parameters first. */ 737 ASSERT(infop != NULL); 738 if (infop == NULL) { 739 ACPIDEV_DEBUG(CE_WARN, 740 "acpidev: infop is NULL in acpidev_process_object()."); 741 return (AE_BAD_PARAMETER); 742 } 743 ASSERT(infop->awi_hdl != NULL); 744 ASSERT(infop->awi_info != NULL); 745 ASSERT(infop->awi_data != NULL); 746 ASSERT(infop->awi_class_curr != NULL); 747 ASSERT(infop->awi_class_curr->adc_filter != NULL); 748 hdl = infop->awi_hdl; 749 adip = infop->awi_info; 750 datap = infop->awi_data; 751 clsp = infop->awi_class_curr; 752 if (hdl == NULL || datap == NULL || adip == NULL || clsp == NULL || 753 clsp->adc_filter == NULL) { 754 ACPIDEV_DEBUG(CE_WARN, "acpidev: infop has NULL pointer in " 755 "acpidev_process_object()."); 756 return (AE_BAD_PARAMETER); 757 } 758 pdip = acpidev_walk_info_get_pdip(infop); 759 if (pdip == NULL) { 760 ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to get pdip for %s " 761 "in acpidev_process_object().", infop->awi_name); 762 return (AE_BAD_PARAMETER); 763 } 764 765 /* 766 * Check whether the object has already been handled. 767 * Tag and child dip pointer are used to indicate the object has been 768 * handled by the ACPI auto configure driver. It has the 769 * following usages: 770 * 1) Prevent creating dip for objects which already have a dip 771 * when reloading the ACPI auto configure driver. 772 * 2) Prevent creating multiple dips for ACPI objects with ACPI 773 * aliases. Currently ACPICA framework has no way to tell whether 774 * an object is an alias or not for some types of object. So tag 775 * is used to indicate that the object has been handled. 776 * 3) Prevent multiple class drivers from creating multiple devices for 777 * the same ACPI object. 778 */ 779 if ((flags & ACPIDEV_PROCESS_FLAG_CREATE) && 780 (flags & ACPIDEV_PROCESS_FLAG_CHECK) && 781 !(infop->awi_flags & ACPIDEV_WI_DISABLE_CREATE) && 782 (infop->awi_flags & ACPIDEV_WI_DEVICE_CREATED)) { 783 ASSERT(infop->awi_dip != NULL); 784 ACPIDEV_DEBUG(CE_NOTE, 785 "acpidev: device has already been created for object %s.", 786 infop->awi_name); 787 return (AE_ALREADY_EXISTS); 788 } 789 790 /* 791 * Determine action according to following rules based on device 792 * status returned by _STA method. Please refer to ACPI3.0b section 793 * 6.3.1 and 6.5.1. 794 * present functioning enabled Action 795 * 0 0 x Do nothing 796 * 1 x 0 Create node in OFFLINE and scan child 797 * 1 x 1 Create node and scan child 798 * x 1 0 Create node in OFFLINE and scan child 799 * x 1 1 Create node and scan child 800 */ 801 if ((datap->aod_iflag & ACPIDEV_ODF_STATUS_VALID) == 0 || 802 (flags & ACPIDEV_PROCESS_FLAG_SYNCSTATUS)) { 803 if (adip->Valid & ACPI_VALID_STA) { 804 datap->aod_status = adip->CurrentStatus; 805 } else { 806 datap->aod_status = acpidev_query_device_status(hdl); 807 } 808 datap->aod_iflag |= ACPIDEV_ODF_STATUS_VALID; 809 } 810 if (!acpidev_check_device_present(datap->aod_status)) { 811 ACPIDEV_DEBUG(CE_NOTE, "acpidev: object %s doesn't exist.", 812 infop->awi_name); 813 return (AE_NOT_EXIST); 814 } 815 816 ASSERT(infop->awi_data != NULL); 817 ASSERT(infop->awi_parent != NULL); 818 ASSERT(infop->awi_parent->awi_data != NULL); 819 /* Put device into offline state if parent is in offline state. */ 820 if (infop->awi_parent->awi_data->aod_iflag & 821 ACPIDEV_ODF_DEVINFO_OFFLINE) { 822 flags |= ACPIDEV_PROCESS_FLAG_OFFLINE; 823 /* Put device into offline state if it's disabled. */ 824 } else if (!acpidev_check_device_enabled(datap->aod_status)) { 825 flags |= ACPIDEV_PROCESS_FLAG_OFFLINE; 826 } 827 /* 828 * Mark current node status as OFFLINE even if a device node will not 829 * be created for it. This is needed to handle the case when the current 830 * node is SKIPPED (no device node will be created for it), so that all 831 * descedants of current nodes could be correctly marked as OFFLINE. 832 */ 833 if (flags & ACPIDEV_PROCESS_FLAG_OFFLINE) { 834 infop->awi_data->aod_iflag |= ACPIDEV_ODF_DEVINFO_OFFLINE; 835 } 836 837 /* Evaluate filtering rules and generate device name. */ 838 devname = kmem_zalloc(ACPIDEV_MAX_NAMELEN + 1, KM_SLEEP); 839 (void) memcpy(devname, (char *)&adip->Name, sizeof (adip->Name)); 840 if (flags & ACPIDEV_PROCESS_FLAG_CREATE) { 841 res = clsp->adc_filter(infop, devname, ACPIDEV_MAX_NAMELEN); 842 } else { 843 res = clsp->adc_filter(infop, NULL, 0); 844 } 845 846 /* Create device if requested. */ 847 if ((flags & ACPIDEV_PROCESS_FLAG_CREATE) && 848 !(infop->awi_flags & ACPIDEV_WI_DISABLE_CREATE) && 849 !(infop->awi_flags & ACPIDEV_WI_DEVICE_CREATED) && 850 (res == ACPIDEV_FILTER_DEFAULT || res == ACPIDEV_FILTER_CREATE)) { 851 int ret; 852 853 /* 854 * Allocate dip and set default properties. 855 * Properties can be overriden in class specific init routines. 856 */ 857 ASSERT(infop->awi_dip == NULL); 858 ndi_devi_alloc_sleep(pdip, devname, (pnode_t)DEVI_SID_NODEID, 859 &dip); 860 infop->awi_dip = dip; 861 ret = ndi_prop_update_string(DDI_DEV_T_NONE, dip, 862 OBP_DEVICETYPE, clsp->adc_dev_type); 863 if (ret != NDI_SUCCESS) { 864 ACPIDEV_DEBUG(CE_WARN, 865 "acpidev: failed to set device property for %s.", 866 infop->awi_name); 867 (void) ddi_remove_child(dip, 0); 868 infop->awi_dip = NULL; 869 kmem_free(devname, ACPIDEV_MAX_NAMELEN + 1); 870 return (AE_ERROR); 871 } 872 873 /* Build cross reference between dip and ACPI object. */ 874 if ((flags & ACPIDEV_PROCESS_FLAG_NOTAG) == 0 && 875 ACPI_FAILURE(acpica_tag_devinfo(dip, hdl))) { 876 cmn_err(CE_WARN, 877 "!acpidev: failed to tag object %s.", 878 infop->awi_name); 879 (void) ddi_remove_child(dip, 0); 880 infop->awi_dip = NULL; 881 kmem_free(devname, ACPIDEV_MAX_NAMELEN + 1); 882 return (AE_ERROR); 883 } 884 885 /* Call class specific initialization callback. */ 886 if (clsp->adc_init != NULL && 887 ACPI_FAILURE(clsp->adc_init(infop))) { 888 ACPIDEV_DEBUG(CE_WARN, 889 "acpidev: failed to initialize device %s.", 890 infop->awi_name); 891 if ((flags & ACPIDEV_PROCESS_FLAG_NOTAG) == 0) { 892 (void) acpica_untag_devinfo(dip, hdl); 893 } 894 (void) ddi_remove_child(dip, 0); 895 infop->awi_dip = NULL; 896 kmem_free(devname, ACPIDEV_MAX_NAMELEN + 1); 897 return (AE_ERROR); 898 } 899 900 /* Set device into offline state if requested. */ 901 if (flags & ACPIDEV_PROCESS_FLAG_OFFLINE) { 902 mutex_enter(&(DEVI(dip)->devi_lock)); 903 DEVI_SET_DEVICE_OFFLINE(dip); 904 mutex_exit(&(DEVI(dip)->devi_lock)); 905 } 906 907 /* Mark status */ 908 infop->awi_flags |= ACPIDEV_WI_DEVICE_CREATED; 909 datap->aod_iflag |= ACPIDEV_ODF_DEVINFO_CREATED; 910 datap->aod_dip = dip; 911 datap->aod_class = clsp; 912 /* Hold reference count on class driver. */ 913 atomic_inc_32(&clsp->adc_refcnt); 914 if ((flags & ACPIDEV_PROCESS_FLAG_NOTAG) == 0) { 915 datap->aod_iflag |= ACPIDEV_ODF_DEVINFO_TAGGED; 916 } 917 918 /* Bind device driver. */ 919 if ((flags & ACPIDEV_PROCESS_FLAG_NOBIND) != 0) { 920 mutex_enter(&(DEVI(dip)->devi_lock)); 921 DEVI(dip)->devi_flags |= DEVI_NO_BIND; 922 mutex_exit(&(DEVI(dip)->devi_lock)); 923 } else { 924 (void) ndi_devi_bind_driver(dip, 0); 925 } 926 } 927 928 /* Free resources */ 929 kmem_free(devname, ACPIDEV_MAX_NAMELEN + 1); 930 rc = AE_OK; 931 932 /* Recursively scan child objects if requested. */ 933 switch (res) { 934 case ACPIDEV_FILTER_DEFAULT: 935 /* FALLTHROUGH */ 936 case ACPIDEV_FILTER_SCAN: 937 /* Check if we need to scan child. */ 938 if ((flags & ACPIDEV_PROCESS_FLAG_SCAN) && 939 !(infop->awi_flags & ACPIDEV_WI_DISABLE_SCAN) && 940 !(infop->awi_flags & ACPIDEV_WI_CHILD_SCANNED)) { 941 /* probe child object. */ 942 rc = acpidev_probe_child(infop); 943 if (ACPI_FAILURE(rc)) { 944 ACPIDEV_DEBUG(CE_WARN, 945 "acpidev: failed to probe subtree of %s.", 946 infop->awi_name); 947 rc = AE_ERROR; 948 } 949 /* Mark object as scanned. */ 950 infop->awi_flags |= ACPIDEV_WI_CHILD_SCANNED; 951 } 952 break; 953 954 case ACPIDEV_FILTER_CREATE: 955 /* FALLTHROUGH */ 956 case ACPIDEV_FILTER_CONTINUE: 957 /* FALLTHROUGH */ 958 case ACPIDEV_FILTER_SKIP: 959 break; 960 961 case ACPIDEV_FILTER_FAILED: 962 ACPIDEV_DEBUG(CE_WARN, 963 "acpidev: failed to probe device for %s.", 964 infop->awi_name); 965 rc = AE_ERROR; 966 break; 967 968 default: 969 cmn_err(CE_WARN, 970 "!acpidev: unknown filter result code %d.", res); 971 rc = AE_ERROR; 972 break; 973 } 974 975 return (rc); 976 } 977 978 /*ARGSUSED*/ 979 acpidev_filter_result_t 980 acpidev_filter_default(acpidev_walk_info_t *infop, ACPI_HANDLE hdl, 981 acpidev_filter_rule_t *afrp, char *devname, int len) 982 { 983 ASSERT(afrp != NULL); 984 ASSERT(devname == NULL || len >= ACPIDEV_MAX_NAMELEN); 985 if (infop->awi_level < afrp->adf_minlvl || 986 infop->awi_level > afrp->adf_maxlvl) { 987 return (ACPIDEV_FILTER_CONTINUE); 988 } else if (afrp->adf_pattern != NULL && 989 strncmp(afrp->adf_pattern, 990 (char *)&infop->awi_info->Name, 991 sizeof (infop->awi_info->Name))) { 992 return (ACPIDEV_FILTER_CONTINUE); 993 } 994 if (afrp->adf_replace != NULL && devname != NULL) { 995 (void) strncpy(devname, afrp->adf_replace, len - 1); 996 devname[len - 1] = 0; 997 } 998 999 return (afrp->adf_retcode); 1000 } 1001 1002 acpidev_filter_result_t 1003 acpidev_filter_device(acpidev_walk_info_t *infop, ACPI_HANDLE hdl, 1004 acpidev_filter_rule_t *afrp, int entries, char *devname, int len) 1005 { 1006 acpidev_filter_result_t res; 1007 1008 /* Evaluate filtering rules. */ 1009 for (; entries > 0; entries--, afrp++) { 1010 if (afrp->adf_filter_func != NULL) { 1011 res = afrp->adf_filter_func(infop, hdl, afrp, 1012 devname, len); 1013 } else { 1014 res = acpidev_filter_default(infop, hdl, afrp, 1015 devname, len); 1016 } 1017 if (res == ACPIDEV_FILTER_DEFAULT || 1018 res == ACPIDEV_FILTER_SCAN) { 1019 infop->awi_class_list = afrp->adf_class_list; 1020 break; 1021 } 1022 } 1023 1024 return (res); 1025 } 1026 1027 dev_info_t * 1028 acpidev_root_node(void) 1029 { 1030 return (acpidev_root_dip); 1031 } 1032 1033 ACPI_STATUS 1034 acpidev_register_class(acpidev_class_list_t **listpp, acpidev_class_t *clsp, 1035 boolean_t tail) 1036 { 1037 ACPI_STATUS rc; 1038 acpidev_class_list_t *item; 1039 acpidev_class_list_t *temp; 1040 1041 ASSERT(clsp != NULL); 1042 ASSERT(listpp != NULL); 1043 if (listpp == NULL || clsp == NULL) { 1044 ACPIDEV_DEBUG(CE_WARN, 1045 "acpidev: invalid parameter in acpidev_register_class()."); 1046 return (AE_BAD_PARAMETER); 1047 } else if (clsp->adc_version != ACPIDEV_CLASS_REV) { 1048 cmn_err(CE_WARN, 1049 "!acpidev: class driver %s version mismatch.", 1050 clsp->adc_class_name); 1051 return (AE_BAD_DATA); 1052 } 1053 1054 rc = AE_OK; 1055 item = kmem_zalloc(sizeof (*item), KM_SLEEP); 1056 item->acl_class = clsp; 1057 rw_enter(&acpidev_class_lock, RW_WRITER); 1058 /* Check for duplicated item. */ 1059 for (temp = *listpp; temp != NULL; temp = temp->acl_next) { 1060 if (temp->acl_class == clsp) { 1061 cmn_err(CE_WARN, 1062 "!acpidev: register duplicate class driver %s.", 1063 clsp->adc_class_name); 1064 rc = AE_ALREADY_EXISTS; 1065 break; 1066 } 1067 } 1068 if (ACPI_SUCCESS(rc)) { 1069 if (tail) { 1070 while (*listpp) { 1071 listpp = &(*listpp)->acl_next; 1072 } 1073 } 1074 item->acl_next = *listpp; 1075 *listpp = item; 1076 } 1077 rw_exit(&acpidev_class_lock); 1078 if (ACPI_FAILURE(rc)) { 1079 kmem_free(item, sizeof (*item)); 1080 } 1081 1082 return (rc); 1083 } 1084 1085 ACPI_STATUS 1086 acpidev_unregister_class(acpidev_class_list_t **listpp, 1087 acpidev_class_t *clsp) 1088 { 1089 ACPI_STATUS rc = AE_NOT_FOUND; 1090 acpidev_class_list_t *temp; 1091 1092 ASSERT(clsp != NULL); 1093 ASSERT(listpp != NULL); 1094 if (listpp == NULL || clsp == NULL) { 1095 ACPIDEV_DEBUG(CE_WARN, "acpidev: invalid parameter " 1096 "in acpidev_unregister_class()."); 1097 return (AE_BAD_PARAMETER); 1098 } 1099 1100 rw_enter(&acpidev_class_lock, RW_WRITER); 1101 for (temp = NULL; *listpp; listpp = &(*listpp)->acl_next) { 1102 if ((*listpp)->acl_class == clsp) { 1103 temp = *listpp; 1104 *listpp = (*listpp)->acl_next; 1105 break; 1106 } 1107 } 1108 if (temp == NULL) { 1109 ACPIDEV_DEBUG(CE_WARN, "acpidev: class %p(%s) doesn't exist " 1110 "in acpidev_unregister_class().", 1111 (void *)clsp, clsp->adc_class_name); 1112 rc = AE_NOT_FOUND; 1113 } else if (temp->acl_class->adc_refcnt != 0) { 1114 ACPIDEV_DEBUG(CE_WARN, "acpidev: class %p(%s) is still in use " 1115 "in acpidev_unregister_class()..", 1116 (void *)clsp, clsp->adc_class_name); 1117 rc = AE_ERROR; 1118 } else { 1119 kmem_free(temp, sizeof (*temp)); 1120 rc = AE_OK; 1121 } 1122 rw_exit(&acpidev_class_lock); 1123 1124 return (rc); 1125 } 1126