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