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