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
add_interrupt(intrs_t * intrs,int irq)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
insert_used_resource(used_io_mem_t * used,int * used_count,used_io_mem_t ** head)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
add_used_io_mem(struct regspec * io,int io_count)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
parse_resources_irq(ACPI_RESOURCE * resource_ptr,intrs_t * intrs)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
parse_resources_extended_irq(ACPI_RESOURCE * resource_ptr,intrs_t * intrs)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
parse_resources_dma(ACPI_RESOURCE * resource_ptr,int * dma_count)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
parse_resources_io(ACPI_RESOURCE * resource_ptr,struct regspec * io,int * io_count)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
parse_resources_fixed_io(ACPI_RESOURCE * resource_ptr,struct regspec * io,int * io_count)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
parse_resources_fixed_mem32(ACPI_RESOURCE * resource_ptr,struct regspec * io,int * io_count)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
parse_resources_mem32(ACPI_RESOURCE * resource_ptr,struct regspec * io,int * io_count)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
parse_resources_addr16(ACPI_RESOURCE * resource_ptr,struct regspec * io,int * io_count)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
parse_resources_addr32(ACPI_RESOURCE * resource_ptr,struct regspec * io,int * io_count)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
parse_resources_addr64(ACPI_RESOURCE * resource_ptr,struct regspec * io,int * io_count)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
parse_resources(ACPI_HANDLE handle,dev_info_t * xdip,char * path)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 *
get_bus_dip(const char * nodename,dev_info_t * isa_dip)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
eisa_to_str(ACPI_INTEGER id,char * np)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
process_cids(ACPI_OBJECT * rv,device_id_t ** dd)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
convert_to_pnp1275(char * pnpid,char * str,int strsize)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
create_compatible_property(dev_info_t * dip,device_id_t * ids)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
isa_acpi_callback(ACPI_HANDLE ObjHandle,uint32_t NestingLevel,void * a,void ** b)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
irq_cmp(const void * a,const void * b)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
used_res_interrupts(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
used_res_dmas(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
used_res_io_mem(char * nodename,int * count,used_io_mem_t ** head)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
acpi_isa_device_enum(dev_info_t * isa_dip)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