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 /* 23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 /* 27 * Copyright (c) 2009-2010, Intel Corporation. 28 * All rights reserved. 29 */ 30 31 #include <sys/types.h> 32 #include <sys/cmn_err.h> 33 #include <sys/note.h> 34 #include <sys/sysmacros.h> 35 #include <sys/sunddi.h> 36 #include <sys/sunndi.h> 37 #include <sys/acpi/acpi.h> 38 #include <sys/acpica.h> 39 #include <sys/acpidev.h> 40 #include <sys/acpidev_impl.h> 41 #include <util/sscanf.h> 42 43 /* Data structures used to extract the numeric unit address from string _UID. */ 44 static acpidev_pseudo_uid_head_t acpidev_uid_heads[ACPIDEV_CLASS_ID_MAX]; 45 static char *acpidev_uid_formats[] = { 46 "%u", 47 }; 48 49 static char *acpidev_unknown_object_name = "<unknown>"; 50 51 int 52 acpidev_query_device_status(ACPI_HANDLE hdl) 53 { 54 int status; 55 56 ASSERT(hdl != NULL); 57 if (hdl == NULL) { 58 ACPIDEV_DEBUG(CE_WARN, 59 "!acpidev: hdl is NULL in acpidev_query_device_status()."); 60 return (0); 61 } 62 63 if (ACPI_FAILURE(acpica_eval_int(hdl, METHOD_NAME__STA, &status))) { 64 /* 65 * Set the default value according to ACPI3.0b sec 6.3.7: 66 * If a device object (including the processor object) does 67 * not have an _STA object, then OSPM assumes that all of the 68 * above bits are set (in other words, the device is present, 69 * enabled, shown in the UI, and functioning). 70 */ 71 status = 0xF; 72 } 73 74 return (status); 75 } 76 77 boolean_t 78 acpidev_check_device_present(int status) 79 { 80 /* 81 * According to ACPI3.0 Spec, if either the ACPI_STA_DEVICE_PRESENT bit 82 * or the ACPI_STA_DEVICE_FUNCTIONING bit is set, the device exists. 83 */ 84 if (status & (ACPI_STA_DEVICE_PRESENT | ACPI_STA_DEVICE_FUNCTIONING)) { 85 return (B_TRUE); 86 } 87 88 return (B_FALSE); 89 } 90 91 boolean_t 92 acpidev_check_device_enabled(int stat) 93 { 94 /* 95 * According to ACPI3.0 Spec, if either the ACPI_STA_DEVICE_PRESENT bit 96 * or the ACPI_STA_DEVICE_FUNCTIONING bit is set, the device exists. 97 * Return true if device exists and has been enabled. 98 */ 99 if ((stat & (ACPI_STA_DEVICE_PRESENT | ACPI_STA_DEVICE_FUNCTIONING)) && 100 (stat & ACPI_STA_DEVICE_ENABLED)) { 101 return (B_TRUE); 102 } 103 104 return (B_FALSE); 105 } 106 107 boolean_t 108 acpidev_match_device_id(ACPI_DEVICE_INFO *infop, char **ids, int count) 109 { 110 int i, j; 111 112 ASSERT(infop != NULL); 113 ASSERT(ids != NULL || count == 0); 114 /* Special case to match all devices if count is 0. */ 115 if (count == 0) { 116 return (B_TRUE); 117 } else if (infop == NULL || ids == NULL) { 118 ACPIDEV_DEBUG(CE_WARN, "!acpidev: invalid parameters in " 119 "acpidev_match_device_id()."); 120 return (B_FALSE); 121 } 122 123 /* Match _HID first. */ 124 if (infop->Valid & ACPI_VALID_HID) { 125 for (i = 0; i < count; i++) { 126 if (strncmp(ids[i], infop->HardwareId.String, 127 infop->HardwareId.Length) == 0) { 128 return (B_TRUE); 129 } 130 } 131 } 132 133 /* Match _CID next. */ 134 if (infop->Valid & ACPI_VALID_CID) { 135 for (i = 0; i < count; i++) { 136 for (j = 0; j < infop->CompatibleIdList.Count; j++) { 137 if (strncmp(ids[i], 138 infop->CompatibleIdList.Ids[j].String, 139 infop->CompatibleIdList.Ids[j].Length) 140 == 0) { 141 return (B_TRUE); 142 } 143 } 144 } 145 } 146 147 return (B_FALSE); 148 } 149 150 struct acpidev_get_device_arg { 151 boolean_t skip_non_exist; 152 int id_count; 153 char **device_ids; 154 void *user_arg; 155 ACPI_WALK_CALLBACK user_func; 156 }; 157 158 static ACPI_STATUS 159 acpidev_get_device_callback(ACPI_HANDLE hdl, UINT32 level, void *arg, 160 void **retval) 161 { 162 ACPI_STATUS rc; 163 ACPI_DEVICE_INFO *infop; 164 struct acpidev_get_device_arg *argp; 165 166 argp = (struct acpidev_get_device_arg *)arg; 167 ASSERT(argp != NULL); 168 ASSERT(hdl != NULL); 169 170 /* Query object information. */ 171 rc = AcpiGetObjectInfo(hdl, &infop); 172 if (ACPI_FAILURE(rc)) { 173 cmn_err(CE_WARN, "!acpidev: failed to get ACPI object info " 174 "in acpidev_get_device_callback()."); 175 return (AE_CTRL_DEPTH); 176 } 177 178 /* 179 * Skip scanning of children if the device is neither PRESENT nor 180 * FUNCTIONING. 181 * Please refer to ACPI Spec3.0b Sec 6.3.1 and 6.5.1. 182 */ 183 if (argp->skip_non_exist && (infop->Valid & ACPI_VALID_STA) && 184 !acpidev_check_device_present(infop->CurrentStatus)) { 185 rc = AE_CTRL_DEPTH; 186 /* Call user callback if matched. */ 187 } else if (acpidev_match_device_id(infop, argp->device_ids, 188 argp->id_count)) { 189 rc = argp->user_func(hdl, level, argp->user_arg, retval); 190 } else { 191 rc = AE_OK; 192 } 193 194 /* Free ACPI object info buffer. */ 195 AcpiOsFree(infop); 196 197 return (rc); 198 } 199 200 ACPI_STATUS 201 acpidev_get_device_by_id(ACPI_HANDLE hdl, char **ids, int count, 202 int maxdepth, boolean_t skip_non_exist, 203 ACPI_WALK_CALLBACK userfunc, void *userarg, void **retval) 204 { 205 ACPI_STATUS rc; 206 struct acpidev_get_device_arg arg; 207 208 ASSERT(userfunc != NULL); 209 if (hdl == NULL || userfunc == NULL || (ids == NULL && count != 0)) { 210 ACPIDEV_DEBUG(CE_WARN, "!acpidev: invalid parameters " 211 "in acpidev_get_device_by_id()."); 212 return (AE_BAD_PARAMETER); 213 } 214 215 /* Enumerate all descendant objects. */ 216 arg.skip_non_exist = skip_non_exist; 217 arg.device_ids = ids; 218 arg.id_count = count; 219 arg.user_arg = userarg; 220 arg.user_func = userfunc; 221 rc = AcpiWalkNamespace(ACPI_TYPE_DEVICE, hdl, maxdepth, 222 &acpidev_get_device_callback, NULL, &arg, retval); 223 224 return (rc); 225 } 226 227 ACPI_STATUS 228 acpidev_walk_apic(ACPI_BUFFER *bufp, ACPI_HANDLE hdl, char *method, 229 acpidev_apic_walker_t func, void *context) 230 { 231 ACPI_STATUS rc; 232 ssize_t len; 233 ACPI_BUFFER buf; 234 ACPI_OBJECT *obj; 235 ACPI_SUBTABLE_HEADER *ap; 236 ACPI_TABLE_MADT *mp = NULL; 237 238 ASSERT(func != NULL); 239 if (func == NULL) { 240 ACPIDEV_DEBUG(CE_WARN, 241 "!acpidev: invalid parameters for acpidev_walk_apic()."); 242 return (AE_BAD_PARAMETER); 243 } 244 245 buf.Pointer = NULL; 246 buf.Length = ACPI_ALLOCATE_BUFFER; 247 248 /* A walk buffer was passed in if bufp isn't NULL. */ 249 if (bufp != NULL) { 250 ap = (ACPI_SUBTABLE_HEADER *)(bufp->Pointer); 251 len = bufp->Length; 252 } else if (method != NULL) { 253 /* 254 * Otherwise, if we have an evaluate method, we get the walk 255 * buffer from a successful invocation of 256 * AcpiEvaluateObjectTyped(). 257 */ 258 ASSERT(hdl != NULL); 259 rc = AcpiEvaluateObjectTyped(hdl, method, NULL, &buf, 260 ACPI_TYPE_BUFFER); 261 if (ACPI_SUCCESS(rc)) { 262 ASSERT(buf.Length >= sizeof (*obj)); 263 obj = buf.Pointer; 264 ap = (ACPI_SUBTABLE_HEADER *)obj->Buffer.Pointer; 265 len = obj->Buffer.Length; 266 } else { 267 if (rc != AE_NOT_FOUND) 268 cmn_err(CE_WARN, "!acpidev: failed to evaluate " 269 "%s in acpidev_walk_apic().", method); 270 return (rc); 271 } 272 } else { 273 /* As a last resort, walk the MADT table. */ 274 rc = AcpiGetTable(ACPI_SIG_MADT, 1, (ACPI_TABLE_HEADER **)&mp); 275 if (ACPI_FAILURE(rc)) { 276 cmn_err(CE_WARN, "!acpidev: failed to get MADT table " 277 "in acpidev_walk_apic()."); 278 return (rc); 279 } 280 ap = (ACPI_SUBTABLE_HEADER *)(mp + 1); 281 len = mp->Header.Length - sizeof (*mp); 282 } 283 284 ASSERT(len >= 0); 285 for (rc = AE_OK; len > 0 && ACPI_SUCCESS(rc); len -= ap->Length, 286 ap = (ACPI_SUBTABLE_HEADER *)(((char *)ap) + ap->Length)) { 287 ASSERT(len >= sizeof (ACPI_SUBTABLE_HEADER)); 288 if (len <= sizeof (ACPI_SUBTABLE_HEADER) || 289 ap->Length <= sizeof (ACPI_SUBTABLE_HEADER) || 290 len < ap->Length) { 291 cmn_err(CE_WARN, 292 "!acpidev: invalid APIC entry in MADT/_MAT."); 293 break; 294 } 295 rc = (*func)(ap, context); 296 } 297 298 if (buf.Pointer != NULL) { 299 AcpiOsFree(buf.Pointer); 300 } 301 302 return (rc); 303 } 304 305 char * 306 acpidev_get_object_name(ACPI_HANDLE hdl) 307 { 308 ACPI_BUFFER buf; 309 char *objname = acpidev_unknown_object_name; 310 311 buf.Length = ACPI_ALLOCATE_BUFFER; 312 buf.Pointer = NULL; 313 if (ACPI_SUCCESS(AcpiGetName(hdl, ACPI_FULL_PATHNAME, &buf))) { 314 ASSERT(buf.Pointer != NULL); 315 objname = (char *)buf.Pointer; 316 } 317 318 return (objname); 319 } 320 321 void 322 acpidev_free_object_name(char *objname) 323 { 324 if (objname != acpidev_unknown_object_name && objname != NULL) { 325 AcpiOsFree(objname); 326 } 327 } 328 329 acpidev_walk_info_t * 330 acpidev_alloc_walk_info(acpidev_op_type_t op_type, int lvl, ACPI_HANDLE hdl, 331 acpidev_class_list_t **listpp, acpidev_walk_info_t *pinfop) 332 { 333 acpidev_walk_info_t *infop = NULL; 334 acpidev_data_handle_t datap = NULL; 335 336 ASSERT(0 <= lvl && lvl < ACPIDEV_MAX_ENUM_LEVELS); 337 infop = kmem_zalloc(sizeof (*infop), KM_SLEEP); 338 infop->awi_op_type = op_type; 339 infop->awi_level = lvl; 340 infop->awi_parent = pinfop; 341 infop->awi_class_list = listpp; 342 infop->awi_hdl = hdl; 343 infop->awi_name = acpidev_get_object_name(hdl); 344 345 /* Cache ACPI device information. */ 346 if (ACPI_FAILURE(AcpiGetObjectInfo(hdl, &infop->awi_info))) { 347 cmn_err(CE_WARN, "!acpidev: failed to get object info for %s " 348 "in acpidev_alloc_walk_info().", infop->awi_name); 349 acpidev_free_object_name(infop->awi_name); 350 kmem_free(infop, sizeof (*infop)); 351 return (NULL); 352 } 353 354 /* 355 * Get or create an ACPI object data handle, which will be used to 356 * maintain object status information. 357 */ 358 if ((datap = acpidev_data_get_handle(hdl)) != NULL) { 359 ASSERT(datap->aod_hdl == hdl); 360 ASSERT(datap->aod_level == lvl); 361 } else if ((datap = acpidev_data_create_handle(hdl)) != NULL) { 362 datap->aod_level = lvl; 363 datap->aod_hdl = hdl; 364 } else { 365 ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to create object " 366 "handle for %s in acpidev_alloc_walk_info().", 367 infop->awi_name); 368 AcpiOsFree(infop->awi_info); 369 acpidev_free_object_name(infop->awi_name); 370 kmem_free(infop, sizeof (*infop)); 371 return (NULL); 372 } 373 infop->awi_data = datap; 374 /* Sync DEVICE_CREATED flag. */ 375 if (datap->aod_iflag & ACPIDEV_ODF_DEVINFO_CREATED) { 376 ASSERT(datap->aod_dip != NULL); 377 ASSERT(datap->aod_class != NULL); 378 infop->awi_dip = datap->aod_dip; 379 infop->awi_flags |= ACPIDEV_WI_DEVICE_CREATED; 380 } 381 382 return (infop); 383 } 384 385 void 386 acpidev_free_walk_info(acpidev_walk_info_t *infop) 387 { 388 /* 389 * The ACPI object data handle will only be released when the 390 * corresponding object is going to be destroyed. 391 */ 392 if (infop != NULL) { 393 if (infop->awi_info != NULL) { 394 AcpiOsFree(infop->awi_info); 395 } 396 if (infop->awi_name != NULL) { 397 acpidev_free_object_name(infop->awi_name); 398 } 399 kmem_free(infop, sizeof (*infop)); 400 } 401 } 402 403 dev_info_t * 404 acpidev_walk_info_get_pdip(acpidev_walk_info_t *infop) 405 { 406 while (infop != NULL) { 407 if (infop->awi_dip != NULL) { 408 return (infop->awi_dip); 409 } 410 infop = infop->awi_parent; 411 } 412 413 return (NULL); 414 } 415 416 /* 417 * Called to release resources when the corresponding object is going 418 * to be destroyed. 419 */ 420 static void 421 acpidev_free_object_handler(ACPI_HANDLE hdl, void *data) 422 { 423 _NOTE(ARGUNUSED(hdl)); 424 425 acpidev_data_handle_t objhdl = data; 426 427 if (objhdl->aod_class != NULL) { 428 atomic_dec_32(&objhdl->aod_class->adc_refcnt); 429 objhdl->aod_class = NULL; 430 } 431 kmem_free(objhdl, sizeof (acpidev_data_handle_t)); 432 } 433 434 acpidev_data_handle_t 435 acpidev_data_get_handle(ACPI_HANDLE hdl) 436 { 437 void *ptr; 438 acpidev_data_handle_t objhdl = NULL; 439 440 if (ACPI_SUCCESS(AcpiGetData(hdl, acpidev_free_object_handler, &ptr))) { 441 objhdl = (acpidev_data_handle_t)ptr; 442 } 443 444 return (objhdl); 445 } 446 447 acpidev_data_handle_t 448 acpidev_data_create_handle(ACPI_HANDLE hdl) 449 { 450 acpidev_data_handle_t objhdl; 451 452 objhdl = kmem_zalloc(sizeof (*objhdl), KM_SLEEP); 453 objhdl->aod_bdtype = ACPIDEV_INVALID_BOARD; 454 objhdl->aod_bdnum = UINT32_MAX; 455 objhdl->aod_portid = UINT32_MAX; 456 objhdl->aod_class_id = ACPIDEV_CLASS_ID_INVALID; 457 458 if (ACPI_FAILURE(AcpiAttachData(hdl, acpidev_free_object_handler, 459 (void *)objhdl))) { 460 cmn_err(CE_WARN, 461 "!acpidev: failed to attach handle data to object."); 462 kmem_free(objhdl, sizeof (*objhdl)); 463 return (NULL); 464 } 465 466 return (objhdl); 467 } 468 469 void 470 acpidev_data_destroy_handle(ACPI_HANDLE hdl) 471 { 472 void *ptr; 473 acpidev_data_handle_t objhdl = NULL; 474 475 if (ACPI_SUCCESS(AcpiGetData(hdl, acpidev_free_object_handler, &ptr)) && 476 ACPI_SUCCESS(AcpiDetachData(hdl, acpidev_free_object_handler))) { 477 objhdl = ptr; 478 if (objhdl->aod_class != NULL) { 479 atomic_dec_32(&objhdl->aod_class->adc_refcnt); 480 objhdl->aod_class = NULL; 481 } 482 kmem_free(ptr, sizeof (acpidev_data_handle_t)); 483 } 484 } 485 486 ACPI_HANDLE 487 acpidev_data_get_object(acpidev_data_handle_t hdl) 488 { 489 ASSERT(hdl != NULL); 490 return ((hdl != NULL) ? hdl->aod_hdl : NULL); 491 } 492 493 dev_info_t * 494 acpidev_data_get_devinfo(acpidev_data_handle_t hdl) 495 { 496 ASSERT(hdl != NULL); 497 if (hdl == NULL || 498 (hdl->aod_iflag & ACPIDEV_ODF_DEVINFO_CREATED) == 0) { 499 return (NULL); 500 } else { 501 ASSERT(hdl->aod_dip != NULL); 502 return (hdl->aod_dip); 503 } 504 } 505 506 int 507 acpidev_data_get_status(acpidev_data_handle_t hdl) 508 { 509 ASSERT(hdl != NULL); 510 if (hdl == NULL || 511 (hdl->aod_iflag & ACPIDEV_ODF_STATUS_VALID) == 0) { 512 return (0); 513 } else { 514 return (hdl->aod_status); 515 } 516 } 517 518 void 519 acpidev_data_set_flag(acpidev_data_handle_t hdl, uint32_t flag) 520 { 521 ASSERT(hdl != NULL); 522 atomic_or_32(&hdl->aod_eflag, flag); 523 } 524 525 void 526 acpidev_data_clear_flag(acpidev_data_handle_t hdl, uint32_t flag) 527 { 528 ASSERT(hdl != NULL); 529 atomic_and_32(&hdl->aod_eflag, ~flag); 530 } 531 532 uint32_t 533 acpidev_data_get_flag(acpidev_data_handle_t hdl, uint32_t flag) 534 { 535 ASSERT(hdl != NULL); 536 return (hdl->aod_eflag & flag); 537 } 538 539 boolean_t 540 acpidev_data_dr_capable(acpidev_data_handle_t hdl) 541 { 542 ASSERT(hdl != NULL); 543 return (hdl->aod_iflag & ACPIDEV_ODF_HOTPLUG_CAPABLE); 544 } 545 546 boolean_t 547 acpidev_data_dr_ready(acpidev_data_handle_t hdl) 548 { 549 ASSERT(hdl != NULL); 550 return (hdl->aod_iflag & ACPIDEV_ODF_HOTPLUG_READY); 551 } 552 553 boolean_t 554 acpidev_data_dr_failed(acpidev_data_handle_t hdl) 555 { 556 ASSERT(hdl != NULL); 557 return (hdl->aod_iflag & ACPIDEV_ODF_HOTPLUG_FAILED); 558 } 559 560 static char * 561 acpidev_generate_pseudo_unitaddr(char *uid, acpidev_class_id_t cid, 562 char *buf, size_t len) 563 { 564 acpidev_pseudo_uid_t *up, **pp; 565 566 ASSERT(len >= 64); 567 ASSERT(cid >= 0 && cid < ACPIDEV_CLASS_ID_MAX); 568 if (cid < 0 || cid >= ACPIDEV_CLASS_ID_MAX) { 569 return (NULL); 570 } 571 572 mutex_enter(&acpidev_uid_heads[cid].apuh_lock); 573 for (pp = &acpidev_uid_heads[cid].apuh_first; *pp != NULL; 574 pp = &(*pp)->apu_next) { 575 if (strcmp(uid, (*pp)->apu_uid) == 0 && 576 (*pp)->apu_cid == cid) { 577 break; 578 } 579 } 580 /* uid doesn't exist, create one and insert it into the list. */ 581 if (*pp == NULL) { 582 up = kmem_zalloc(sizeof (*up), KM_SLEEP); 583 up->apu_uid = ddi_strdup(uid, KM_SLEEP); 584 up->apu_cid = cid; 585 up->apu_nid = acpidev_uid_heads[cid].apuh_id++; 586 *pp = up; 587 } 588 ASSERT(*pp != NULL); 589 mutex_exit(&acpidev_uid_heads[cid].apuh_lock); 590 591 /* 592 * Generate a special format unit address with three fields to 593 * guarantee uniqueness. Normal unit addresses for ACPI devices have 594 * either one or two fields. 595 */ 596 if (snprintf(buf, len, "%u,%u,0", (*pp)->apu_nid, cid) > len) { 597 return (NULL); 598 } 599 600 return (buf); 601 } 602 603 static char * 604 acpidev_gen_unitaddr(char *uid, char *fmt, char *buf, size_t len) 605 { 606 size_t i, cnt; 607 uint_t id1, id2; 608 609 ASSERT(len >= 64); 610 if (fmt == NULL || strlen(fmt) == 0) { 611 return (NULL); 612 } 613 614 /* 615 * Count '%' in format string to protect sscanf(). 616 * Only support '%u' and '%x', and maximum 2 conversions. 617 */ 618 for (cnt = 0, i = 0; fmt[i] != 0 && cnt <= 2; i++) { 619 if (fmt[i] != '%') { 620 continue; 621 } else if (fmt[i + 1] == 'u' || fmt[i + 1] == 'x') { 622 /* Skip next character. */ 623 i++; 624 cnt++; 625 } else { 626 /* Invalid conversion, stop walking. */ 627 cnt = SIZE_MAX; 628 } 629 } 630 if (cnt != 1 && cnt != 2) { 631 ACPIDEV_DEBUG(CE_WARN, 632 "!acpidev: invalid uid format string '%s'.", fmt); 633 return (NULL); 634 } 635 636 /* Scan uid and generate unitaddr. */ 637 if (sscanf(uid, fmt, &id1, &id2) != cnt) { 638 return (NULL); 639 } 640 /* 641 * Reverse the order of the two IDs to match the requirements of the 642 * hotplug driver. 643 */ 644 if (cnt == 2 && snprintf(buf, len, "%u,%u", id2, id1) >= len) { 645 ACPIDEV_DEBUG(CE_WARN, 646 "!acpidev: generated unitaddr is too long."); 647 return (NULL); 648 } else if (cnt == 1 && snprintf(buf, len, "%u", id1) >= len) { 649 ACPIDEV_DEBUG(CE_WARN, 650 "!acpidev: generated unitaddr is too long."); 651 return (NULL); 652 } 653 654 return (buf); 655 } 656 657 char * 658 acpidev_generate_unitaddr(char *uid, char **fmts, size_t nfmt, 659 char *buf, size_t len) 660 { 661 size_t i; 662 uint_t count = 0; 663 ulong_t val; 664 char **formats = NULL; 665 char *rbuf = NULL; 666 char *endp = NULL; 667 668 ASSERT(len >= 64); 669 670 /* Use _UID as unit address if it's a decimal integer. */ 671 if (ddi_strtoul(uid, &endp, 10, &val) == 0 && 672 (endp == NULL || *endp == 0)) { 673 if (snprintf(buf, len, "%s", uid) >= len) { 674 return (NULL); 675 } else { 676 return (buf); 677 } 678 } 679 680 /* First handle uid format strings from device property. */ 681 if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, ddi_root_node(), 682 DDI_PROP_DONTPASS, 683 ACPIDEV_PROP_NAME_UID_FORMAT, &formats, &count) == DDI_SUCCESS) { 684 /* Walk through format strings and try to generate unitaddr. */ 685 for (i = 0; i < count && rbuf == NULL; i++) { 686 rbuf = acpidev_gen_unitaddr(uid, formats[i], buf, len); 687 } 688 ddi_prop_free(formats); 689 } 690 691 /* Then handle embedded uid format strings. */ 692 if (fmts != NULL) { 693 for (i = 0; i < nfmt && rbuf == NULL; i++) { 694 rbuf = acpidev_gen_unitaddr(uid, fmts[i], buf, len); 695 } 696 } 697 698 return (rbuf); 699 } 700 701 /* 702 * The Solaris device "unit-address" property is composed of a comma-delimited 703 * list of hexadecimal values. According to the ACPI spec, the ACPI _UID method 704 * could return an integer or a string. If it returns an integer, it is used 705 * as the unit-address as is. If _UID returns a string, we try to extract some 706 * meaningful integers to compose the unit-address property. If we fail to 707 * extract any integers, a pseudo-sequential number will be generated for the 708 * unit-address. 709 */ 710 ACPI_STATUS 711 acpidev_set_unitaddr(acpidev_walk_info_t *infop, char **fmts, size_t nfmt, 712 char *unitaddr) 713 { 714 char unit[64]; 715 716 ASSERT(infop != NULL); 717 ASSERT(infop->awi_dip != NULL); 718 ASSERT(infop->awi_info != NULL); 719 if (infop == NULL || infop->awi_dip == NULL || 720 infop->awi_info == NULL) { 721 ACPIDEV_DEBUG(CE_WARN, 722 "!acpidev: invalid parameters in acpidev_set_unitaddr()."); 723 return (AE_BAD_PARAMETER); 724 } 725 726 if (infop->awi_info->Valid & ACPI_VALID_UID) { 727 if (ndi_prop_update_string(DDI_DEV_T_NONE, infop->awi_dip, 728 ACPIDEV_PROP_NAME_ACPI_UID, 729 infop->awi_info->UniqueId.String) != NDI_SUCCESS) { 730 cmn_err(CE_WARN, 731 "!acpidev: failed to set UID property for %s.", 732 infop->awi_name); 733 return (AE_ERROR); 734 } 735 } 736 737 if (unitaddr == NULL && (infop->awi_info->Valid & ACPI_VALID_UID)) { 738 /* Try to generate unit address from _UID. */ 739 if (fmts == NULL) { 740 fmts = acpidev_uid_formats; 741 nfmt = sizeof (acpidev_uid_formats) / sizeof (char *); 742 } 743 unitaddr = acpidev_generate_unitaddr( 744 infop->awi_info->UniqueId.String, fmts, nfmt, 745 unit, sizeof (unit)); 746 /* Generate pseudo sequential unit address. */ 747 if (unitaddr == NULL) { 748 unitaddr = acpidev_generate_pseudo_unitaddr( 749 infop->awi_info->UniqueId.String, 750 infop->awi_class_curr->adc_class_id, 751 unit, sizeof (unit)); 752 } 753 if (unitaddr == NULL) { 754 cmn_err(CE_WARN, "!acpidev: failed to generate unit " 755 "address from %s.", 756 infop->awi_info->UniqueId.String); 757 return (AE_ERROR); 758 } 759 } 760 if (unitaddr == NULL) { 761 /* 762 * Some ACPI objects may have no _UID method available, so we 763 * can't generate the "unit-address" property for them. 764 * On the other hand, it's legal to support such a device 765 * without a unit address, so return success here. 766 */ 767 return (AE_OK); 768 } 769 770 if (ndi_prop_update_string(DDI_DEV_T_NONE, infop->awi_dip, 771 ACPIDEV_PROP_NAME_UNIT_ADDR, unitaddr) != NDI_SUCCESS) { 772 cmn_err(CE_WARN, "!acpidev: failed to set unitaddr for %s.", 773 infop->awi_name); 774 return (AE_ERROR); 775 } 776 777 return (AE_OK); 778 } 779 780 ACPI_STATUS 781 acpidev_set_compatible(acpidev_walk_info_t *infop, char **compat, int acount) 782 { 783 int count, i, j; 784 char **compatible = NULL; 785 ACPI_DEVICE_INFO *di; 786 787 /* 788 * Generate compatible list for device based on: 789 * * Device HID if available 790 * * Device CIDs if available 791 * * property array passed in 792 */ 793 ASSERT(infop != NULL); 794 ASSERT(infop->awi_dip != NULL); 795 ASSERT(infop->awi_info != NULL); 796 ASSERT(compat != NULL || acount == 0); 797 if (infop == NULL || infop->awi_dip == NULL || 798 infop->awi_info == NULL || (compat == NULL && acount != 0)) { 799 ACPIDEV_DEBUG(CE_WARN, "!acpidev: invalid parameters " 800 "in acpidev_set_compatible()."); 801 return (AE_BAD_PARAMETER); 802 } 803 804 /* Compute string count. */ 805 count = acount; 806 di = infop->awi_info; 807 if (di->Valid & ACPI_VALID_HID) { 808 count++; 809 } 810 if (di->Valid & ACPI_VALID_CID) { 811 count += di->CompatibleIdList.Count; 812 } 813 compatible = kmem_zalloc(sizeof (char *) * count, KM_SLEEP); 814 815 /* Generate string array. */ 816 i = 0; 817 if (di->Valid & ACPI_VALID_HID) { 818 compatible[i++] = di->HardwareId.String; 819 } 820 if (di->Valid & ACPI_VALID_CID) { 821 for (j = 0; j < di->CompatibleIdList.Count; j++) { 822 compatible[i++] = di->CompatibleIdList.Ids[j].String; 823 } 824 } 825 for (j = 0; j < acount; j++) { 826 compatible[i++] = compat[j]; 827 } 828 ASSERT(i == count); 829 830 /* Set "compatible" property. */ 831 if (ndi_prop_update_string_array(DDI_DEV_T_NONE, infop->awi_dip, 832 OBP_COMPATIBLE, compatible, count) != NDI_SUCCESS) { 833 cmn_err(CE_WARN, "!acpidev: failed to set compatible " 834 "property for %s in acpidev_set_compatible().", 835 infop->awi_name); 836 kmem_free(compatible, count * sizeof (char *)); 837 return (AE_ERROR); 838 } 839 kmem_free(compatible, count * sizeof (char *)); 840 841 return (AE_OK); 842 } 843 844 /* Evaluate _OST method under object, which is used to support hotplug event. */ 845 ACPI_STATUS 846 acpidev_eval_ost(ACPI_HANDLE hdl, uint32_t code, uint32_t status, 847 char *bufp, size_t len) 848 { 849 ACPI_STATUS rc; 850 ACPI_OBJECT args[3]; 851 ACPI_OBJECT_LIST arglist; 852 853 args[0].Type = ACPI_TYPE_INTEGER; 854 args[0].Integer.Value = code; 855 args[1].Type = ACPI_TYPE_INTEGER; 856 args[1].Integer.Value = status; 857 args[2].Type = ACPI_TYPE_BUFFER; 858 args[2].Buffer.Pointer = (UINT8 *)bufp; 859 args[2].Buffer.Length = (UINT32)len; 860 if (bufp == NULL || len == 0) { 861 arglist.Count = 2; 862 } else { 863 arglist.Count = 3; 864 } 865 arglist.Pointer = args; 866 rc = AcpiEvaluateObject(hdl, ACPIDEV_METHOD_NAME_OST, &arglist, NULL); 867 if (rc != AE_OK && rc != AE_NOT_FOUND) { 868 ACPIDEV_DEBUG(CE_WARN, 869 "!acpidev: failed to evaluate _OST method, code 0x%x.", rc); 870 } 871 872 return (rc); 873 } 874 875 ACPI_STATUS 876 acpidev_eval_ej0(ACPI_HANDLE hdl) 877 { 878 ACPI_STATUS rc; 879 ACPI_OBJECT args[1]; 880 ACPI_OBJECT_LIST arglist; 881 882 /* 883 * Quotation from ACPI spec 4.0 section 6.3.3. 884 * Arg0 An Integer containing a device ejection control 885 * 0 Cancel a mark for ejection request (EJ0 will never be called 886 * with this value) 887 * 1 Hot eject or mark for ejection 888 */ 889 args[0].Type = ACPI_TYPE_INTEGER; 890 args[0].Integer.Value = 1; 891 arglist.Count = 1; 892 arglist.Pointer = args; 893 rc = AcpiEvaluateObject(hdl, ACPIDEV_METHOD_NAME_EJ0, &arglist, NULL); 894 if (rc != AE_OK) { 895 ACPIDEV_DEBUG(CE_WARN, 896 "!acpidev: failed to evaluate _EJ0 method, code 0x%x.", rc); 897 } 898 899 return (rc); 900 } 901 902 ACPI_STATUS 903 acpidev_eval_pxm(ACPI_HANDLE hdl, uint32_t *idp) 904 { 905 int pxmid; 906 907 ASSERT(idp != NULL); 908 909 /* 910 * Try to evaluate ACPI _PXM method to get proximity doamin id. 911 * Quotation from ACPI4.0: 912 * If the Local APIC ID / Local SAPIC ID / Local x2APIC ID of a 913 * dynamically added processor is not present in the System Resource 914 * Affinity Table (SRAT), a _PXM object must exist for the processor's 915 * device or one of its ancestors in the ACPI Namespace. 916 */ 917 while (hdl != NULL) { 918 if (ACPI_SUCCESS(acpica_eval_int(hdl, 919 ACPIDEV_METHOD_NAME_PXM, &pxmid))) { 920 *idp = (uint32_t)pxmid; 921 return (AE_OK); 922 } 923 if (ACPI_FAILURE(AcpiGetParent(hdl, &hdl))) { 924 break; 925 } 926 } 927 928 return (AE_NOT_FOUND); 929 }