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 <acpica/include/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 }