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