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  * Copyright (c) 2012 Gary Mills
  26  * Copyright 2013 Nexenta Systems, Inc.  All rights reserved.
  27  */
  28 
  29 /*
  30  * ACPI enumerator
  31  */
  32 
  33 #include <sys/ddi.h>
  34 #include <sys/sunddi.h>
  35 #include <sys/sunndi.h>
  36 #include <sys/note.h>
  37 #include <util/sscanf.h>
  38 
  39 #include <acpica/include/acpi.h>
  40 #include <sys/acpica.h>
  41 
  42 static char keyboard_alias[] = "keyboard";
  43 static char mouse_alias[] = "mouse";
  44 #define ACPI_ENUM_DEBUG         "acpi_enum_debug"
  45 #define PARSE_RESOURCES_DEBUG   0x0001
  46 #define MASTER_LOOKUP_DEBUG     0x0002
  47 #define DEVICES_NOT_ENUMED      0x0004
  48 #define PARSE_RES_IRQ           0x0008
  49 #define PARSE_RES_DMA           0x0010
  50 #define PARSE_RES_MEMORY        0x0020
  51 #define PARSE_RES_IO            0x0040
  52 #define PARSE_RES_ADDRESS       0x0080
  53 #define ISA_DEVICE_ENUM         0x1000
  54 #define PROCESS_CIDS            0x2000
  55 static unsigned long acpi_enum_debug = 0x00;
  56 
  57 static char USED_RESOURCES[] = "used-resources";
  58 static dev_info_t *usedrdip = NULL;
  59 static unsigned short used_interrupts = 0;
  60 static unsigned short used_dmas = 0;
  61 typedef struct used_io_mem {
  62         unsigned int start_addr;
  63         unsigned int length;
  64         struct used_io_mem *next;
  65 } used_io_mem_t;
  66 static used_io_mem_t *used_io_head = NULL;
  67 static used_io_mem_t *used_mem_head = NULL;
  68 static int used_io_count = 0;
  69 static int used_mem_count = 0;
  70 
  71 #define MAX_PARSED_ACPI_RESOURCES       255
  72 #define ACPI_ISA_LIMIT  16
  73 static int interrupt[ACPI_ISA_LIMIT], dma[ACPI_ISA_LIMIT];
  74 #define ACPI_ELEMENT_PACKAGE_LIMIT      32
  75 #define EISA_ID_SIZE    7
  76 
  77 /*
  78  * insert used io/mem in increasing order
  79  */
  80 static void
  81 insert_used_resource(used_io_mem_t *used, int *used_count, used_io_mem_t **head)
  82 {
  83         used_io_mem_t *curr, *prev;
  84 
  85         (*used_count)++;
  86         if (*head == NULL) {
  87                 *head = used;
  88                 return;
  89         }
  90         curr = prev = *head;
  91         /* find a place to insert */
  92         while ((curr != NULL) &&
  93             (curr->start_addr < used->start_addr)) {
  94                 prev = curr;
  95                 curr = curr->next;
  96         }
  97         if (prev == curr) {
  98                 /* head */
  99                 *head = used;
 100                 used->next = curr;
 101                 return;
 102         } else {
 103                 prev->next = used;
 104         }
 105         used->next = curr;
 106 }
 107 
 108 static void
 109 add_used_io_mem(struct regspec *io, int io_count)
 110 {
 111         int i;
 112         used_io_mem_t *used;
 113 
 114         for (i = 0; i < io_count; i++) {
 115                 used = (used_io_mem_t *)kmem_zalloc(sizeof (used_io_mem_t),
 116                     KM_SLEEP);
 117                 used->start_addr = io[i].regspec_addr;
 118                 used->length = io[i].regspec_size;
 119                 if (io[i].regspec_bustype == 1) {
 120                         insert_used_resource(used, &used_io_count,
 121                             &used_io_head);
 122                 } else {
 123                         insert_used_resource(used, &used_mem_count,
 124                             &used_mem_head);
 125                 }
 126         }
 127 }
 128 
 129 static void
 130 parse_resources_irq(ACPI_RESOURCE *resource_ptr, int *interrupt_count)
 131 {
 132         int i;
 133 
 134         for (i = 0; i < resource_ptr->Data.Irq.InterruptCount; i++) {
 135                 interrupt[(*interrupt_count)++] =
 136                     resource_ptr->Data.Irq.Interrupts[i];
 137                 used_interrupts |= 1 << resource_ptr->Data.Irq.Interrupts[i];
 138                 if (acpi_enum_debug & PARSE_RES_IRQ) {
 139                         cmn_err(CE_NOTE, "parse_resources() "\
 140                             "IRQ num %u, intr # = %u",
 141                             i, resource_ptr->Data.Irq.Interrupts[i]);
 142                 }
 143         }
 144 }
 145 
 146 static void
 147 parse_resources_dma(ACPI_RESOURCE *resource_ptr, int *dma_count)
 148 {
 149         int i;
 150 
 151         for (i = 0; i < resource_ptr->Data.Dma.ChannelCount; i++) {
 152                 dma[(*dma_count)++] = resource_ptr->Data.Dma.Channels[i];
 153                 used_dmas |= 1 << resource_ptr->Data.Dma.Channels[i];
 154                 if (acpi_enum_debug & PARSE_RES_DMA) {
 155                         cmn_err(CE_NOTE, "parse_resources() "\
 156                             "DMA num %u, channel # = %u",
 157                             i, resource_ptr->Data.Dma.Channels[i]);
 158                 }
 159         }
 160 }
 161 
 162 static void
 163 parse_resources_io(ACPI_RESOURCE *resource_ptr, struct regspec *io,
 164     int *io_count)
 165 {
 166         ACPI_RESOURCE_IO acpi_io = resource_ptr->Data.Io;
 167 
 168         if (acpi_io.AddressLength == 0)
 169                 return;
 170 
 171         io[*io_count].regspec_bustype = 1; /* io */
 172         io[*io_count].regspec_size = acpi_io.AddressLength;
 173         io[*io_count].regspec_addr = acpi_io.Minimum;
 174         if (acpi_enum_debug & PARSE_RES_IO) {
 175                 cmn_err(CE_NOTE, "parse_resources() "\
 176                     "IO min 0x%X, max 0x%X, length: 0x%X",
 177                     acpi_io.Minimum,
 178                     acpi_io.Maximum,
 179                     acpi_io.AddressLength);
 180         }
 181         (*io_count)++;
 182 }
 183 
 184 static void
 185 parse_resources_fixed_io(ACPI_RESOURCE *resource_ptr, struct regspec *io,
 186     int *io_count)
 187 {
 188         ACPI_RESOURCE_FIXED_IO fixed_io = resource_ptr->Data.FixedIo;
 189 
 190         if (fixed_io.AddressLength == 0)
 191                 return;
 192 
 193         io[*io_count].regspec_bustype = 1; /* io */
 194         io[*io_count].regspec_addr = fixed_io.Address;
 195         io[*io_count].regspec_size = fixed_io.AddressLength;
 196         if (acpi_enum_debug & PARSE_RES_IO) {
 197                 cmn_err(CE_NOTE, "parse_resources() "\
 198                     "Fixed IO 0x%X, length: 0x%X",
 199                     fixed_io.Address, fixed_io.AddressLength);
 200         }
 201         (*io_count)++;
 202 }
 203 
 204 static void
 205 parse_resources_fixed_mem32(ACPI_RESOURCE *resource_ptr, struct regspec *io,
 206     int *io_count)
 207 {
 208         ACPI_RESOURCE_FIXED_MEMORY32 fixed_mem32 =
 209             resource_ptr->Data.FixedMemory32;
 210 
 211         if (fixed_mem32.AddressLength == 0)
 212                 return;
 213 
 214         io[*io_count].regspec_bustype = 0; /* memory */
 215         io[*io_count].regspec_addr = fixed_mem32.Address;
 216         io[*io_count].regspec_size = fixed_mem32.AddressLength;
 217         if (acpi_enum_debug & PARSE_RES_MEMORY) {
 218                 cmn_err(CE_NOTE, "parse_resources() "\
 219                     "Fixed Mem 32 %ul, length: %ul",
 220                     fixed_mem32.Address, fixed_mem32.AddressLength);
 221         }
 222         (*io_count)++;
 223 }
 224 
 225 static void
 226 parse_resources_mem32(ACPI_RESOURCE *resource_ptr, struct regspec *io,
 227     int *io_count)
 228 {
 229         ACPI_RESOURCE_MEMORY32 mem32 = resource_ptr->Data.Memory32;
 230 
 231         if (mem32.AddressLength == 0)
 232                 return;
 233 
 234         if (resource_ptr->Data.Memory32.Minimum ==
 235             resource_ptr->Data.Memory32.Maximum) {
 236                 io[*io_count].regspec_bustype = 0; /* memory */
 237                 io[*io_count].regspec_addr = mem32.Minimum;
 238                 io[*io_count].regspec_size = mem32.AddressLength;
 239                 (*io_count)++;
 240                 if (acpi_enum_debug & PARSE_RES_MEMORY) {
 241                         cmn_err(CE_NOTE, "parse_resources() "\
 242                             "Mem 32 0x%X, length: 0x%X",
 243                             mem32.Minimum, mem32.AddressLength);
 244                 }
 245                 return;
 246         }
 247         if (acpi_enum_debug & PARSE_RES_MEMORY) {
 248                 cmn_err(CE_NOTE, "parse_resources() "\
 249                     "MEM32 Min Max not equal!");
 250                 cmn_err(CE_NOTE, "parse_resources() "\
 251                     "Mem 32 Minimum 0x%X, Maximum: 0x%X",
 252                     mem32.Minimum, mem32.Maximum);
 253         }
 254 }
 255 
 256 static void
 257 parse_resources_addr16(ACPI_RESOURCE *resource_ptr, struct regspec *io,
 258     int *io_count)
 259 {
 260         ACPI_RESOURCE_ADDRESS16 addr16 =
 261             resource_ptr->Data.Address16;
 262 
 263         if (addr16.AddressLength == 0)
 264                 return;
 265 
 266         if (acpi_enum_debug & PARSE_RES_ADDRESS) {
 267                 if (addr16.ResourceType == ACPI_MEMORY_RANGE) {
 268                         cmn_err(CE_NOTE, "parse_resources() "\
 269                             "ADDRESS 16 MEMORY RANGE");
 270                 } else
 271                 if (addr16.ResourceType == ACPI_IO_RANGE) {
 272                         cmn_err(CE_NOTE, "parse_resources() "\
 273                             "ADDRESS 16 IO RANGE");
 274                 } else {
 275                         cmn_err(CE_NOTE, "parse_resources() "\
 276                             "ADDRESS 16 OTHER");
 277                 }
 278                 cmn_err(CE_NOTE, "parse_resources() "\
 279                     "%s "\
 280                     "MinAddressFixed 0x%X, "\
 281                     "MaxAddressFixed 0x%X, "\
 282                     "Minimum 0x%X, "\
 283                     "Maximum 0x%X, "\
 284                     "length: 0x%X\n",
 285                     addr16.ProducerConsumer == ACPI_CONSUMER ?
 286                     "CONSUMER" : "PRODUCER",
 287                     addr16.MinAddressFixed,
 288                     addr16.MaxAddressFixed,
 289                     addr16.Minimum,
 290                     addr16.Maximum,
 291                     addr16.AddressLength);
 292         }
 293         if (addr16.ProducerConsumer == ACPI_PRODUCER ||
 294             (addr16.ResourceType != ACPI_MEMORY_RANGE &&
 295             addr16.ResourceType != ACPI_IO_RANGE)) {
 296                 return;
 297         }
 298         if (addr16.AddressLength > 0) {
 299                 if (addr16.ResourceType == ACPI_MEMORY_RANGE) {
 300                         /* memory */
 301                         io[*io_count].regspec_bustype = 0;
 302                 } else {
 303                         /* io */
 304                         io[*io_count].regspec_bustype = 1;
 305                 }
 306                 io[*io_count].regspec_addr = addr16.Minimum;
 307                 io[*io_count].regspec_size = addr16.AddressLength;
 308                 (*io_count)++;
 309         }
 310 }
 311 
 312 static void
 313 parse_resources_addr32(ACPI_RESOURCE *resource_ptr, struct regspec *io,
 314     int *io_count)
 315 {
 316         ACPI_RESOURCE_ADDRESS32 addr32 =
 317             resource_ptr->Data.Address32;
 318 
 319         if (addr32.AddressLength == 0)
 320                 return;
 321 
 322         if (acpi_enum_debug & PARSE_RES_ADDRESS) {
 323                 if (addr32.ResourceType == ACPI_MEMORY_RANGE) {
 324                         cmn_err(CE_NOTE, "parse_resources() "\
 325                             "ADDRESS 32 MEMORY RANGE");
 326                 } else
 327                 if (addr32.ResourceType == ACPI_IO_RANGE) {
 328                         cmn_err(CE_NOTE, "parse_resources() "\
 329                             "ADDRESS 32 IO RANGE");
 330                 } else {
 331                         cmn_err(CE_NOTE, "parse_resources() "\
 332                             "ADDRESS 32 OTHER");
 333                 }
 334                 cmn_err(CE_NOTE, "parse_resources() "\
 335                     "%s "\
 336                     "MinAddressFixed 0x%X, "\
 337                     "MaxAddressFixed 0x%X, "\
 338                     "Minimum 0x%X, "\
 339                     "Maximum 0x%X, "\
 340                     "length: 0x%X\n",
 341                     addr32.ProducerConsumer == ACPI_CONSUMER ?
 342                     "CONSUMER" : "PRODUCER",
 343                     addr32.MinAddressFixed,
 344                     addr32.MaxAddressFixed,
 345                     addr32.Minimum,
 346                     addr32.Maximum,
 347                     addr32.AddressLength);
 348         }
 349         if (addr32.ProducerConsumer == ACPI_PRODUCER ||
 350             (addr32.ResourceType != ACPI_MEMORY_RANGE &&
 351             addr32.ResourceType != ACPI_IO_RANGE)) {
 352                 return;
 353         }
 354         if (addr32.AddressLength > 0) {
 355                 if (addr32.ResourceType == ACPI_MEMORY_RANGE) {
 356                         /* memory */
 357                         io[*io_count].regspec_bustype = 0;
 358                 } else {
 359                         /* io */
 360                         io[*io_count].regspec_bustype = 1;
 361                 }
 362                 io[*io_count].regspec_addr = addr32.Minimum;
 363                 io[*io_count].regspec_size = addr32.AddressLength;
 364                 (*io_count)++;
 365         }
 366 }
 367 
 368 static void
 369 parse_resources_addr64(ACPI_RESOURCE *resource_ptr, struct regspec *io,
 370     int *io_count)
 371 {
 372         ACPI_RESOURCE_ADDRESS64 addr64 =
 373             resource_ptr->Data.Address64;
 374 
 375         if (addr64.AddressLength == 0)
 376                 return;
 377 
 378         if (acpi_enum_debug & PARSE_RES_ADDRESS) {
 379                 if (addr64.ResourceType == ACPI_MEMORY_RANGE) {
 380                         cmn_err(CE_NOTE, "parse_resources() "\
 381                             "ADDRESS 64 MEMORY RANGE");
 382                 } else
 383                 if (addr64.ResourceType == ACPI_IO_RANGE) {
 384                         cmn_err(CE_NOTE, "parse_resources() "\
 385                             "ADDRESS 64 IO RANGE");
 386                 } else {
 387                         cmn_err(CE_NOTE, "parse_resources() "\
 388                             "ADDRESS 64 OTHER");
 389                 }
 390 #ifdef _LP64
 391                 cmn_err(CE_NOTE, "parse_resources() "\
 392                     "%s "\
 393                     "MinAddressFixed 0x%X, "\
 394                     "MaxAddressFixed 0x%X, "\
 395                     "Minimum 0x%lX, "\
 396                     "Maximum 0x%lX, "\
 397                     "length: 0x%lX\n",
 398                     addr64.ProducerConsumer == ACPI_CONSUMER ?
 399                     "CONSUMER" : "PRODUCER",
 400                     addr64.MinAddressFixed,
 401                     addr64.MaxAddressFixed,
 402                     addr64.Minimum,
 403                     addr64.Maximum,
 404                     addr64.AddressLength);
 405 #else
 406                 cmn_err(CE_NOTE, "parse_resources() "\
 407                     "%s "\
 408                     "MinAddressFixed 0x%X, "\
 409                     "MaxAddressFixed 0x%X, "\
 410                     "Minimum 0x%llX, "\
 411                     "Maximum 0x%llX, "\
 412                     "length: 0x%llX\n",
 413                     addr64.ProducerConsumer == ACPI_CONSUMER ?
 414                     "CONSUMER" : "PRODUCER",
 415                     addr64.MinAddressFixed,
 416                     addr64.MaxAddressFixed,
 417                     addr64.Minimum,
 418                     addr64.Maximum,
 419                     addr64.AddressLength);
 420 #endif
 421         }
 422         if (addr64.ProducerConsumer == ACPI_PRODUCER ||
 423             (addr64.ResourceType != ACPI_MEMORY_RANGE &&
 424             addr64.ResourceType != ACPI_IO_RANGE)) {
 425                 return;
 426         }
 427         if (addr64.AddressLength > 0) {
 428                 if (addr64.ResourceType == ACPI_MEMORY_RANGE) {
 429                         /* memory */
 430                         io[*io_count].regspec_bustype = 0;
 431                 } else {
 432                         /* io */
 433                         io[*io_count].regspec_bustype = 1;
 434                 }
 435                 io[*io_count].regspec_addr = addr64.Minimum;
 436                 io[*io_count].regspec_size = addr64.AddressLength;
 437                 (*io_count)++;
 438         }
 439 }
 440 
 441 static ACPI_STATUS
 442 parse_resources(ACPI_HANDLE handle, dev_info_t *xdip, char *path)
 443 {
 444         ACPI_BUFFER     buf;
 445         ACPI_RESOURCE   *resource_ptr;
 446         ACPI_STATUS     status;
 447         char            *current_ptr, *last_ptr;
 448         struct          regspec *io;
 449         int             io_count = 0, interrupt_count = 0, dma_count = 0;
 450         int             i;
 451 
 452         buf.Length = ACPI_ALLOCATE_BUFFER;
 453         status = AcpiGetCurrentResources(handle, &buf);
 454         switch (status) {
 455         case AE_OK:
 456                 break;
 457         case AE_NOT_FOUND:
 458                 /*
 459                  * Workaround for faulty DSDT tables that omit the _CRS
 460                  * method for the UAR3 device but have a valid _PRS method
 461                  * for that device.
 462                  */
 463                 status = AcpiGetPossibleResources(handle, &buf);
 464                 if (status != AE_OK) {
 465                         return (status);
 466                 }
 467                 break;
 468         default:
 469                 cmn_err(CE_WARN,
 470                     "!AcpiGetCurrentResources failed for %s, exception: %s",
 471                     path, AcpiFormatException(status));
 472                 return (status);
 473                 break;
 474         }
 475         io = (struct regspec *)kmem_zalloc(sizeof (struct regspec) *
 476             MAX_PARSED_ACPI_RESOURCES, KM_SLEEP);
 477         current_ptr = buf.Pointer;
 478         last_ptr = (char *)buf.Pointer + buf.Length;
 479         while (current_ptr < last_ptr) {
 480                 if (io_count >= MAX_PARSED_ACPI_RESOURCES) {
 481                         break;
 482                 }
 483                 resource_ptr = (ACPI_RESOURCE *)current_ptr;
 484                 current_ptr += resource_ptr->Length;
 485                 switch (resource_ptr->Type) {
 486                 case ACPI_RESOURCE_TYPE_END_TAG:
 487                         current_ptr = last_ptr;
 488                         break;
 489                 case ACPI_RESOURCE_TYPE_IO:
 490                         parse_resources_io(resource_ptr, io, &io_count);
 491                         break;
 492                 case ACPI_RESOURCE_TYPE_FIXED_IO:
 493                         parse_resources_fixed_io(resource_ptr, io, &io_count);
 494                         break;
 495                 case ACPI_RESOURCE_TYPE_FIXED_MEMORY32:
 496                         parse_resources_fixed_mem32(resource_ptr, io,
 497                             &io_count);
 498                         break;
 499                 case ACPI_RESOURCE_TYPE_MEMORY32:
 500                         parse_resources_mem32(resource_ptr, io, &io_count);
 501                         break;
 502                 case ACPI_RESOURCE_TYPE_ADDRESS16:
 503                         parse_resources_addr16(resource_ptr, io, &io_count);
 504                         break;
 505                 case ACPI_RESOURCE_TYPE_ADDRESS32:
 506                         parse_resources_addr32(resource_ptr, io, &io_count);
 507                         break;
 508                 case ACPI_RESOURCE_TYPE_ADDRESS64:
 509                         parse_resources_addr64(resource_ptr, io, &io_count);
 510                         break;
 511                 case ACPI_RESOURCE_TYPE_IRQ:
 512                         parse_resources_irq(resource_ptr, &interrupt_count);
 513                         break;
 514                 case ACPI_RESOURCE_TYPE_DMA:
 515                         parse_resources_dma(resource_ptr, &dma_count);
 516                         break;
 517                 case ACPI_RESOURCE_TYPE_START_DEPENDENT:
 518                         cmn_err(CE_NOTE,
 519                             "!ACPI source type"
 520                             " ACPI_RESOURCE_TYPE_START_DEPENDENT"
 521                             " not supported");
 522                         break;
 523                 case ACPI_RESOURCE_TYPE_END_DEPENDENT:
 524                         cmn_err(CE_NOTE,
 525                             "!ACPI source type"
 526                             " ACPI_RESOURCE_TYPE_END_DEPENDENT"
 527                             " not supported");
 528                         break;
 529                 case ACPI_RESOURCE_TYPE_VENDOR:
 530                         cmn_err(CE_NOTE,
 531                             "!ACPI source type"
 532                             " ACPI_RESOURCE_TYPE_VENDOR"
 533                             " not supported");
 534                         break;
 535                 case ACPI_RESOURCE_TYPE_MEMORY24:
 536                         cmn_err(CE_NOTE,
 537                             "!ACPI source type"
 538                             " ACPI_RESOURCE_TYPE_MEMORY24"
 539                             " not supported");
 540                         break;
 541                 case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
 542                         cmn_err(CE_NOTE,
 543                             "!ACPI source type"
 544                             " ACPI_RESOURCE_TYPE_EXT_IRQ"
 545                             " not supported");
 546                         break;
 547                 default:
 548                 /* Some types are not yet implemented (See CA 6.4) */
 549                         cmn_err(CE_NOTE,
 550                             "!ACPI resource type (0X%X) not yet supported",
 551                             resource_ptr->Type);
 552                         break;
 553                 }
 554         }
 555 
 556         if (io_count) {
 557                 /*
 558                  * on LX50, you get interrupts of mouse and keyboard
 559                  * from separate PNP id...
 560                  */
 561                 if (io_count == 2) {
 562                         if ((io[0].regspec_addr == 0x60 &&
 563                             io[1].regspec_addr == 0x64) ||
 564                             (io[0].regspec_addr == 0x64 &&
 565                             io[1].regspec_addr == 0x60)) {
 566                                 interrupt[0] = 0x1;
 567                                 interrupt[1] = 0xc;
 568                                 interrupt_count = 2;
 569                                 used_interrupts |=
 570                                     1 << interrupt[0];
 571                                 used_interrupts |=
 572                                     1 << interrupt[1];
 573                         }
 574                 }
 575                 add_used_io_mem(io, io_count);
 576                 if (xdip != NULL) {
 577                         (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, xdip,
 578                             "reg", (int *)io, 3*io_count);
 579                 }
 580         }
 581         if (interrupt_count && (xdip != NULL)) {
 582                 (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, xdip,
 583                     "interrupts", (int *)interrupt, interrupt_count);
 584         }
 585         if (dma_count && (xdip != NULL)) {
 586                 (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, xdip,
 587                     "dma-channels", (int *)dma, dma_count);
 588         }
 589         AcpiOsFree(buf.Pointer);
 590         kmem_free(io, sizeof (struct regspec) * MAX_PARSED_ACPI_RESOURCES);
 591         return (status);
 592 }
 593 
 594 /* keyboard mouse is under i8042, everything else under isa */
 595 static dev_info_t *
 596 get_bus_dip(char *nodename, dev_info_t *isa_dip)
 597 {
 598         static dev_info_t *i8042_dip = NULL;
 599         struct regspec i8042_regs[] = {
 600                 {1, 0x60, 0x1},
 601                 {1, 0x64, 0x1}
 602         };
 603         int i8042_intrs[] = {0x1, 0xc};
 604 
 605         if (strcmp(nodename, keyboard_alias) != 0 &&
 606             strcmp(nodename, mouse_alias) != 0)
 607                 return (isa_dip);
 608 
 609         if (i8042_dip)
 610                 return (i8042_dip);
 611 
 612         ndi_devi_alloc_sleep(isa_dip, "i8042", (pnode_t)DEVI_SID_NODEID,
 613             &i8042_dip);
 614         (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, i8042_dip,
 615             "reg", (int *)i8042_regs, 6);
 616         (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, i8042_dip,
 617             "interrupts", (int *)i8042_intrs, 2);
 618         (void) ndi_prop_update_string(DDI_DEV_T_NONE, i8042_dip,
 619             "unit-address", "1,60");
 620         (void) ndi_devi_bind_driver(i8042_dip, 0);
 621         return (i8042_dip);
 622 }
 623 
 624 /*
 625  * put content of properties (if any) to dev info tree at branch xdip
 626  * return non-zero if a "compatible" property was processed, zero otherwise
 627  *
 628  */
 629 static int
 630 process_properties(dev_info_t *xdip, property_t *properties)
 631 {
 632         int     rv = 0;
 633 
 634         while (properties != NULL) {
 635                 (void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip,
 636                     properties->name, properties->value);
 637                 if (strcmp(properties->name, "compatible") == 0)
 638                         rv = 1;
 639                 properties = properties->next;
 640         }
 641 
 642         return (rv);
 643 }
 644 
 645 void
 646 eisa_to_str(ACPI_INTEGER id, char *np)
 647 {
 648         static const char hextab[] = "0123456789ABCDEF";
 649 
 650         /*
 651          *  Expand an EISA device name:
 652          *
 653          * This routine converts a 32-bit EISA device "id" to a
 654          * 7-byte ASCII device name, which is stored at "np".
 655          */
 656 
 657         *np++ = '@' + ((id >> 2)  & 0x1F);
 658         *np++ = '@' + ((id << 3)  & 0x18) + ((id >> 13) & 0x07);
 659         *np++ = '@' + ((id >> 8)  & 0x1F);
 660         *np++ = hextab[(id >> 20) & 0x0F];
 661         *np++ = hextab[(id >> 16) & 0x0F];
 662         *np++ = hextab[(id >> 28) & 0x0F];
 663         *np++ = hextab[(id >> 24) & 0x0F];
 664         *np = 0;
 665 }
 666 
 667 /*
 668  * process_cids() -- process multiple CIDs in a package
 669  */
 670 static void
 671 process_cids(ACPI_OBJECT *rv, device_id_t **dd)
 672 {
 673         device_id_t *d;
 674         char tmp_cidstr[8];     /* 7-character EISA ID */
 675         int i;
 676 
 677         if ((rv->Package.Count == 0) || rv->Package.Elements == NULL)
 678                 return; /* empty package */
 679 
 680         /*
 681          * Work the package 'backwards' so the resulting list is
 682          * in original order of preference.
 683          */
 684         for (i = rv->Package.Count - 1; i >= 0; i--) {
 685                 /* get the actual acpi_object */
 686                 ACPI_OBJECT obj = rv->Package.Elements[i];
 687                 switch (obj.Type) {
 688                 case ACPI_TYPE_INTEGER:
 689                         eisa_to_str(obj.Integer.Value, tmp_cidstr);
 690                         d = mf_alloc_device_id();
 691                         d->id = strdup(tmp_cidstr);
 692                         d->next = *dd;
 693                         *dd = d;
 694                         break;
 695                 case ACPI_TYPE_STRING:
 696                         d = mf_alloc_device_id();
 697                         d->id = strdup(obj.String.Pointer);
 698                         d->next = *dd;
 699                         *dd = d;
 700                         break;
 701                 default:
 702                         if (acpi_enum_debug & PROCESS_CIDS) {
 703                                 cmn_err(CE_NOTE, "unexpected CID type: %d",
 704                                     obj.Type);
 705                         }
 706                         break;
 707                 }
 708         }
 709 }
 710 
 711 /*
 712  * Convert "raw" PNP and ACPI IDs to IEEE 1275-compliant form.
 713  * Some liberty is taken here, treating "ACPI" as a special form
 714  * of PNP vendor ID.  strsize specifies size of buffer.
 715  */
 716 static void
 717 convert_to_pnp1275(char *pnpid, char *str, int strsize)
 718 {
 719         char    vendor[5];
 720         uint_t  id;
 721 
 722         if (strncmp(pnpid, "ACPI", 4) == 0) {
 723                 /* Assume ACPI ID: ACPIxxxx */
 724                 sscanf(pnpid, "%4s%x", vendor, &id);
 725         } else {
 726                 /* Assume PNP ID: aaaxxxx */
 727                 sscanf(pnpid, "%3s%x", vendor, &id);
 728         }
 729 
 730         snprintf(str, strsize, "pnp%s,%x", vendor, id);
 731 }
 732 
 733 /*
 734  * Given a list of device ID elements in most-to-least-specific
 735  * order, create a "compatible" property.
 736  */
 737 static void
 738 create_compatible_property(dev_info_t *dip, device_id_t *ids)
 739 {
 740         char            **strs;
 741         int             list_len, i;
 742         device_id_t     *d;
 743 
 744         /* count list length */
 745         list_len = 0;
 746         d = ids;
 747         while (d != NULL) {
 748                 list_len++;
 749                 d = d->next;
 750         }
 751 
 752         /* create string array */
 753         strs = (char **)kmem_zalloc(list_len * sizeof (char *), KM_SLEEP);
 754         i = 0;
 755         d = ids;
 756         while (d != NULL) {
 757                 /* strlen("pnpXXXX,xxxx") + 1 = 13 */
 758                 strs[i] = kmem_zalloc(13, KM_SLEEP);
 759                 convert_to_pnp1275(d->id, strs[i++], 13);
 760                 d = d->next;
 761         }
 762 
 763         /* update property */
 764         (void) ndi_prop_update_string_array(DDI_DEV_T_NONE, dip,
 765             "compatible", strs, list_len);
 766 
 767 
 768         /* free memory */
 769         for (i = 0; i < list_len; i++)
 770                 kmem_free(strs[i], 13);
 771 
 772         kmem_free(strs, list_len * sizeof (char *));
 773 }
 774 
 775 /*
 776  * isa_acpi_callback()
 777  */
 778 static ACPI_STATUS
 779 isa_acpi_callback(ACPI_HANDLE ObjHandle, uint32_t NestingLevel, void *a,
 780     void **b)
 781 {
 782         _NOTE(ARGUNUSED(NestingLevel, b))
 783 
 784         ACPI_BUFFER     rb;
 785         ACPI_DEVICE_INFO *info = NULL;
 786         char            *path = NULL;
 787         char            *hidstr = NULL;
 788         char            tmp_cidstr[8];  /* EISAID size */
 789         dev_info_t      *dip = (dev_info_t *)a;
 790         dev_info_t      *xdip = NULL;
 791         device_id_t     *d, *device_ids = NULL;
 792         const master_rec_t      *m;
 793         int             compatible_present = 0;
 794 
 795         /*
 796          * get full ACPI pathname for object
 797          */
 798         rb.Length = ACPI_ALLOCATE_BUFFER;
 799         rb.Pointer = NULL;
 800         if (AcpiGetName(ObjHandle, ACPI_FULL_PATHNAME, &rb) != AE_OK) {
 801                 cmn_err(CE_WARN, "!acpi_enum: could not get pathname");
 802                 goto done;
 803         }
 804         path = (char *)rb.Pointer;
 805 
 806         /*
 807          * Get device info object
 808          */
 809         if (AcpiGetObjectInfo(ObjHandle, &info) != AE_OK) {
 810                 cmn_err(CE_WARN, "!acpi_enum: could not get device"
 811                     " info for %s", path);
 812                 goto done;
 813         }
 814 
 815         /*
 816          * If device isn't present, we don't enumerate
 817          * NEEDSWORK: what about docking bays and the like?
 818          */
 819         if (info->Valid & ACPI_VALID_STA) {
 820                 /*
 821                  * CA 6.3.6 _STA method
 822                  * Bit 0 -- device is present
 823                  * Bit 1 -- device is enabled
 824                  * Bit 2 -- device is shown in UI
 825                  */
 826                 if (!((info->CurrentStatus & 0x7) == 7)) {
 827                         if (acpi_enum_debug & DEVICES_NOT_ENUMED) {
 828                                 cmn_err(CE_NOTE, "parse_resources() "
 829                                     "Bad status 0x%x for %s",
 830                                     info->CurrentStatus, path);
 831                         }
 832                         goto done;
 833                 }
 834         } else {
 835                 cmn_err(CE_WARN, "!acpi_enum: no _STA for %s", path);
 836                 goto done;
 837         }
 838 
 839         /*
 840          * Keep track of _HID value
 841          */
 842         if (!(info->Valid & ACPI_VALID_HID)) {
 843                 /* No _HID, we skip this node */
 844                 if (acpi_enum_debug & DEVICES_NOT_ENUMED) {
 845                         cmn_err(CE_NOTE, "parse_resources() "
 846                             "No _HID for %s", path);
 847                 }
 848                 goto done;
 849         }
 850         hidstr = info->HardwareId.String;
 851 
 852         /*
 853          * Attempt to get _CID value
 854          */
 855         rb.Length = ACPI_ALLOCATE_BUFFER;
 856         rb.Pointer = NULL;
 857         if (AcpiEvaluateObject(ObjHandle, "_CID", NULL, &rb) == AE_OK &&
 858             rb.Length != 0) {
 859                 ACPI_OBJECT *rv = rb.Pointer;
 860 
 861                 switch (rv->Type) {
 862                 case ACPI_TYPE_INTEGER:
 863                         eisa_to_str(rv->Integer.Value, tmp_cidstr);
 864                         d = mf_alloc_device_id();
 865                         d->id = strdup(tmp_cidstr);
 866                         d->next = device_ids;
 867                         device_ids = d;
 868                         break;
 869                 case ACPI_TYPE_STRING:
 870                         d = mf_alloc_device_id();
 871                         d->id = strdup(rv->String.Pointer);
 872                         d->next = device_ids;
 873                         device_ids = d;
 874                         break;
 875                 case ACPI_TYPE_PACKAGE:
 876                         process_cids(rv, &device_ids);
 877                         break;
 878                 default:
 879                         break;
 880                 }
 881                 AcpiOsFree(rb.Pointer);
 882         }
 883 
 884         /*
 885          * Add _HID last so it's at the head of the list
 886          */
 887         d = mf_alloc_device_id();
 888         d->id = strdup(hidstr);
 889         d->next = device_ids;
 890         device_ids = d;
 891 
 892         /*
 893          * master_file_lookup() expects _HID first in device_ids
 894          */
 895         if ((m = master_file_lookup(device_ids)) !=  NULL) {
 896                 /* PNP description found in master table */
 897                 if (!(strncmp(hidstr, "ACPI", 4))) {
 898                         dip = ddi_root_node();
 899                 } else {
 900                         dip = get_bus_dip(m->name, dip);
 901                 }
 902                 ndi_devi_alloc_sleep(dip, m->name,
 903                     (pnode_t)DEVI_SID_NODEID, &xdip);
 904                 (void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip,
 905                     "model", m->description);
 906                 compatible_present = process_properties(xdip, m->properties);
 907         } else {
 908                 /* for ISA devices not known to the master file */
 909                 if (!(strncmp(hidstr, "PNP03", 5))) {
 910                         /* a keyboard device includes PNP03xx */
 911                         dip = get_bus_dip(keyboard_alias, dip);
 912                         ndi_devi_alloc_sleep(dip, keyboard_alias,
 913                             (pnode_t)DEVI_SID_NODEID, &xdip);
 914                         (void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip,
 915                             "compatible", "pnpPNP,303");
 916                         (void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip,
 917                             "model", "PNP03xx keyboard");
 918                 } else {
 919                         if (!(strncmp(hidstr, "PNP0F", 5))) {
 920                                 /* a mouse device include PNP0Fxx */
 921                                 dip = get_bus_dip(mouse_alias, dip);
 922                                 ndi_devi_alloc_sleep(dip, mouse_alias,
 923                                     (pnode_t)DEVI_SID_NODEID, &xdip);
 924                                 (void) ndi_prop_update_string(DDI_DEV_T_NONE,
 925                                     xdip, "compatible", "pnpPNP,f03");
 926                                 (void) ndi_prop_update_string(DDI_DEV_T_NONE,
 927                                     xdip, "model", "PNP0Fxx mouse");
 928                         } else {
 929                                 (void) parse_resources(ObjHandle, xdip, path);
 930                                 goto done;
 931                         }
 932                 }
 933         }
 934 
 935         (void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip, "acpi-namespace",
 936             path);
 937 
 938         (void) parse_resources(ObjHandle, xdip, path);
 939 
 940         /* Special processing for mouse and keyboard devices per IEEE 1275 */
 941         /* if master entry doesn't contain "compatible" then we add default */
 942         if (strcmp(m->name, keyboard_alias) == 0) {
 943                 (void) ndi_prop_update_int(DDI_DEV_T_NONE, xdip, "reg", 0);
 944                 (void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip,
 945                     "device-type", keyboard_alias);
 946                 if (!compatible_present)
 947                         (void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip,
 948                             "compatible", "pnpPNP,303");
 949         } else if (strcmp(m->name, mouse_alias) == 0) {
 950                 (void) ndi_prop_update_int(DDI_DEV_T_NONE, xdip, "reg", 1);
 951                 (void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip,
 952                     "device-type", mouse_alias);
 953                 if (!compatible_present)
 954                         (void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip,
 955                             "compatible", "pnpPNP,f03");
 956         }
 957 
 958         /*
 959          * Create default "compatible" property if required
 960          */
 961         if (!ddi_prop_exists(DDI_DEV_T_ANY, xdip,
 962             DDI_PROP_DONTPASS, "compatible"))
 963                 create_compatible_property(xdip, device_ids);
 964 
 965         (void) ndi_devi_bind_driver(xdip, 0);
 966 
 967 done:
 968         /* discard _HID/_CID list */
 969         d = device_ids;
 970         while (d != NULL) {
 971                 device_id_t *next;
 972 
 973                 next = d->next;
 974                 mf_free_device_id(d);
 975                 d = next;
 976         }
 977 
 978         if (path != NULL)
 979                 AcpiOsFree(path);
 980         if (info != NULL)
 981                 AcpiOsFree(info);
 982 
 983         return (AE_OK);
 984 }
 985 
 986 static void
 987 used_res_interrupts(void)
 988 {
 989         int intr[ACPI_ISA_LIMIT];
 990         int count = 0;
 991         int i;
 992 
 993         for (i = 0; i < ACPI_ISA_LIMIT; i++) {
 994                 if ((used_interrupts >> i) & 1) {
 995                         intr[count++] = i;
 996                 }
 997         }
 998         (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, usedrdip,
 999             "interrupts", (int *)intr, count);
1000 }
1001 
1002 static void
1003 used_res_dmas(void)
1004 {
1005         int dma[ACPI_ISA_LIMIT];
1006         int count = 0;
1007         int i;
1008 
1009         for (i = 0; i < ACPI_ISA_LIMIT; i++) {
1010                 if ((used_dmas >> i) & 1) {
1011                         dma[count++] = i;
1012                 }
1013         }
1014         (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, usedrdip,
1015             "dma-channels", (int *)dma, count);
1016 }
1017 
1018 static void
1019 used_res_io_mem(char *nodename, int *count, used_io_mem_t **head)
1020 {
1021         int *io;
1022         used_io_mem_t *used = *head;
1023         int i;
1024 
1025         *count *= 2;
1026         io = (int *)kmem_zalloc(sizeof (int)*(*count), KM_SLEEP);
1027         for (i = 0; i < *count; i += 2) {
1028                 used_io_mem_t *prev;
1029                 if (used != NULL) {
1030                         io[i] = used->start_addr;
1031                         io[i+1] = used->length;
1032                         prev = used;
1033                         used = used->next;
1034                         kmem_free(prev, sizeof (used_io_mem_t));
1035                 }
1036         }
1037         (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, usedrdip,
1038             nodename, (int *)io, *count);
1039         kmem_free(io, sizeof (int)*(*count));
1040         *head = NULL;
1041 }
1042 
1043 /*
1044  * acpi_isa_device_enum() -- call from isa nexus driver
1045  * returns 1 if deviced enumeration is successful
1046  *         0 if deviced enumeration fails
1047  */
1048 int
1049 acpi_isa_device_enum(dev_info_t *isa_dip)
1050 {
1051         char *acpi_prop;
1052 
1053         if (ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_root_node(),
1054             DDI_PROP_DONTPASS, ACPI_ENUM_DEBUG, &acpi_prop) ==
1055             DDI_PROP_SUCCESS) {
1056                 long data;
1057                 if (ddi_strtol(acpi_prop, NULL, 0, &data) == 0) {
1058                         acpi_enum_debug = (unsigned long)data;
1059                         e_ddi_prop_remove(DDI_DEV_T_NONE, ddi_root_node(),
1060                             ACPI_ENUM_DEBUG);
1061                         e_ddi_prop_update_int(DDI_DEV_T_NONE,
1062                             ddi_root_node(), ACPI_ENUM_DEBUG, data);
1063                 }
1064                 ddi_prop_free(acpi_prop);
1065         }
1066 
1067         if (acpi_enum_debug & ISA_DEVICE_ENUM) {
1068                 cmn_err(CE_NOTE, "acpi_isa_device_enum() called");
1069         }
1070 
1071         if (acpica_init() != AE_OK) {
1072                 cmn_err(CE_WARN, "!isa_enum: init failed");
1073                 /* Note, pickup by i8042 nexus */
1074                 (void) e_ddi_prop_update_string(DDI_DEV_T_NONE,
1075                     ddi_root_node(), "acpi-enum", "off");
1076                 return (0);
1077         }
1078 
1079         usedrdip = ddi_find_devinfo(USED_RESOURCES, -1, 0);
1080         if (usedrdip == NULL) {
1081                 ndi_devi_alloc_sleep(ddi_root_node(), USED_RESOURCES,
1082                     (pnode_t)DEVI_SID_NODEID, &usedrdip);
1083 
1084         }
1085 
1086         process_master_file();
1087 
1088         /*
1089          * Do the actual enumeration.  Avoid AcpiGetDevices because it
1090          * has an unnecessary internal callback that duplicates
1091          * determining if the device is present.
1092          */
1093         (void) AcpiWalkNamespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
1094             UINT32_MAX, isa_acpi_callback, NULL, isa_dip, NULL);
1095 
1096         free_master_data();
1097         used_res_interrupts();
1098         used_res_dmas();
1099         used_res_io_mem("device-memory", &used_mem_count, &used_mem_head);
1100         used_res_io_mem("io-space", &used_io_count, &used_io_head);
1101         (void) ndi_devi_bind_driver(usedrdip, 0);
1102 
1103         return (1);
1104 }