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 2016, Joyent, Inc.
26 */
27/*
28 * Copyright (c) 2009-2010, Intel Corporation.
29 * All rights reserved.
30 */
31
32#include <sys/types.h>
33#include <sys/cmn_err.h>
34#include <sys/sysmacros.h>
35#include <sys/sunddi.h>
36#include <sys/sunndi.h>
37#include <sys/acpi/acpi.h>
38#include <sys/acpica.h>
39#include <sys/acpidev.h>
40#include <sys/acpidev_rsc.h>
41#include <sys/acpidev_impl.h>
42
43#define	ACPIDEV_RES_INIT_ITEMS		8
44#define	ACPIDEV_RES_INCR_ITEMS		8
45
46/* Data structure to hold parsed resources during walking. */
47struct acpidev_resource_handle {
48	boolean_t			acpidev_consumer;
49	int				acpidev_reg_count;
50	int				acpidev_reg_max;
51	acpidev_phys_spec_t		*acpidev_regp;
52	acpidev_phys_spec_t		acpidev_regs[ACPIDEV_RES_INIT_ITEMS];
53	int				acpidev_range_count;
54	int				acpidev_range_max;
55	acpidev_ranges_t		*acpidev_rangep;
56	acpidev_ranges_t		acpidev_ranges[ACPIDEV_RES_INIT_ITEMS];
57	int				acpidev_bus_count;
58	int				acpidev_bus_max;
59	acpidev_bus_range_t		*acpidev_busp;
60	acpidev_bus_range_t		acpidev_buses[ACPIDEV_RES_INIT_ITEMS];
61	int				acpidev_irq_count;
62	int				acpidev_irqp[ACPIDEV_RES_IRQ_MAX];
63	int				acpidev_dma_count;
64	int				acpidev_dmap[ACPIDEV_RES_DMA_MAX];
65};
66
67acpidev_resource_handle_t
68acpidev_resource_handle_alloc(boolean_t consumer)
69{
70	acpidev_resource_handle_t rhdl;
71
72	rhdl = kmem_zalloc(sizeof (*rhdl), KM_SLEEP);
73	rhdl->acpidev_consumer = consumer;
74	rhdl->acpidev_reg_max = ACPIDEV_RES_INIT_ITEMS;
75	rhdl->acpidev_regp = rhdl->acpidev_regs;
76	rhdl->acpidev_range_max = ACPIDEV_RES_INIT_ITEMS;
77	rhdl->acpidev_rangep = rhdl->acpidev_ranges;
78	rhdl->acpidev_bus_max = ACPIDEV_RES_INIT_ITEMS;
79	rhdl->acpidev_busp = rhdl->acpidev_buses;
80
81	return (rhdl);
82}
83
84void
85acpidev_resource_handle_free(acpidev_resource_handle_t rhdl)
86{
87	size_t sz;
88
89	ASSERT(rhdl != NULL);
90	if (rhdl != NULL) {
91		if (rhdl->acpidev_regp != rhdl->acpidev_regs) {
92			sz = sizeof (acpidev_phys_spec_t) *
93			    rhdl->acpidev_reg_max;
94			kmem_free(rhdl->acpidev_regp, sz);
95		}
96		if (rhdl->acpidev_rangep != rhdl->acpidev_ranges) {
97			sz = sizeof (acpidev_ranges_t) *
98			    rhdl->acpidev_range_max;
99			kmem_free(rhdl->acpidev_rangep, sz);
100		}
101		if (rhdl->acpidev_busp != rhdl->acpidev_buses) {
102			sz = sizeof (acpidev_bus_range_t) *
103			    rhdl->acpidev_bus_max;
104			kmem_free(rhdl->acpidev_busp, sz);
105		}
106		kmem_free(rhdl, sizeof (struct acpidev_resource_handle));
107	}
108}
109
110static void
111acpidev_resource_handle_grow(acpidev_resource_handle_t rhdl)
112{
113	size_t sz;
114
115	if (rhdl->acpidev_reg_count == rhdl->acpidev_reg_max) {
116		acpidev_phys_spec_t *regp;
117
118		/* Prefer linear incremental here. */
119		rhdl->acpidev_reg_max += ACPIDEV_RES_INCR_ITEMS;
120		sz = sizeof (*regp) * rhdl->acpidev_reg_max;
121		regp = kmem_zalloc(sz, KM_SLEEP);
122		sz = sizeof (*regp) * rhdl->acpidev_reg_count;
123		bcopy(rhdl->acpidev_regp, regp, sz);
124		if (rhdl->acpidev_regp != rhdl->acpidev_regs) {
125			kmem_free(rhdl->acpidev_regp, sz);
126		}
127		rhdl->acpidev_regp = regp;
128	}
129
130	if (rhdl->acpidev_range_count == rhdl->acpidev_range_max) {
131		acpidev_ranges_t *rngp;
132
133		/* Prefer linear incremental here. */
134		rhdl->acpidev_range_max += ACPIDEV_RES_INCR_ITEMS;
135		sz = sizeof (*rngp) * rhdl->acpidev_range_max;
136		rngp = kmem_zalloc(sz, KM_SLEEP);
137		sz = sizeof (*rngp) * rhdl->acpidev_range_count;
138		bcopy(rhdl->acpidev_rangep, rngp, sz);
139		if (rhdl->acpidev_rangep != rhdl->acpidev_ranges) {
140			kmem_free(rhdl->acpidev_rangep, sz);
141		}
142		rhdl->acpidev_rangep = rngp;
143	}
144
145	if (rhdl->acpidev_bus_count == rhdl->acpidev_bus_max) {
146		acpidev_bus_range_t *busp;
147
148		/* Prefer linear incremental here. */
149		rhdl->acpidev_bus_max += ACPIDEV_RES_INCR_ITEMS;
150		sz = sizeof (*busp) * rhdl->acpidev_bus_max;
151		busp = kmem_zalloc(sz, KM_SLEEP);
152		sz = sizeof (*busp) * rhdl->acpidev_bus_count;
153		bcopy(rhdl->acpidev_busp, busp, sz);
154		if (rhdl->acpidev_busp != rhdl->acpidev_buses) {
155			kmem_free(rhdl->acpidev_busp, sz);
156		}
157		rhdl->acpidev_busp = busp;
158	}
159}
160
161ACPI_STATUS
162acpidev_resource_insert_reg(acpidev_resource_handle_t rhdl,
163    acpidev_regspec_t *regp)
164{
165	ASSERT(rhdl != NULL);
166	ASSERT(regp != NULL);
167	if (rhdl->acpidev_reg_count >= rhdl->acpidev_reg_max) {
168		acpidev_resource_handle_grow(rhdl);
169	}
170	ASSERT(rhdl->acpidev_reg_count < rhdl->acpidev_reg_max);
171	rhdl->acpidev_regp[rhdl->acpidev_reg_count] = *regp;
172	rhdl->acpidev_reg_count++;
173
174	return (AE_OK);
175}
176
177ACPI_STATUS
178acpidev_resource_get_regs(acpidev_resource_handle_t rhdl,
179    uint_t mask, uint_t value, acpidev_regspec_t *regp, uint_t *cntp)
180{
181	uint_t i, j;
182
183	ASSERT(rhdl != NULL);
184	ASSERT(cntp != NULL);
185	if (rhdl == NULL || cntp == NULL || (regp == NULL && *cntp != 0)) {
186		return (AE_BAD_PARAMETER);
187	}
188	for (i = 0, j = 0; i < rhdl->acpidev_reg_count; i++) {
189		if ((rhdl->acpidev_regp[i].phys_hi & mask) == value) {
190			if (j < *cntp) {
191				regp[j] = rhdl->acpidev_regp[i];
192			}
193			j++;
194		}
195	}
196	if (j >= *cntp) {
197		*cntp = j;
198		return (AE_LIMIT);
199	} else {
200		*cntp = j;
201		return (AE_OK);
202	}
203}
204
205uint_t
206acpidev_resource_get_reg_count(acpidev_resource_handle_t rhdl,
207    uint_t mask, uint_t value)
208{
209	uint_t i, j;
210
211	ASSERT(rhdl != NULL);
212	for (i = 0, j = 0; i < rhdl->acpidev_reg_count; i++) {
213		if ((rhdl->acpidev_regp[i].phys_hi & mask) == value) {
214			j++;
215		}
216	}
217
218	return (j);
219}
220
221ACPI_STATUS
222acpidev_resource_insert_range(acpidev_resource_handle_t rhdl,
223    acpidev_ranges_t *rangep)
224{
225	ASSERT(rhdl != NULL);
226	ASSERT(rangep != NULL);
227	if (rhdl->acpidev_range_count >= rhdl->acpidev_range_max) {
228		acpidev_resource_handle_grow(rhdl);
229	}
230	ASSERT(rhdl->acpidev_range_count < rhdl->acpidev_range_max);
231	rhdl->acpidev_rangep[rhdl->acpidev_range_count] = *rangep;
232	rhdl->acpidev_range_count++;
233
234	return (AE_OK);
235}
236
237ACPI_STATUS
238acpidev_resource_get_ranges(acpidev_resource_handle_t rhdl,
239    uint_t mask, uint_t value, acpidev_ranges_t *rangep, uint_t *cntp)
240{
241	uint_t i, j;
242
243	ASSERT(rhdl != NULL);
244	ASSERT(cntp != NULL);
245	if (rhdl == NULL || cntp == NULL || (rangep == NULL && *cntp != 0)) {
246		return (AE_BAD_PARAMETER);
247	}
248	for (i = 0, j = 0; i < rhdl->acpidev_range_count; i++) {
249		if ((rhdl->acpidev_rangep[i].child_hi & mask) == value) {
250			if (j < *cntp) {
251				rangep[j] = rhdl->acpidev_rangep[i];
252			}
253			j++;
254		}
255	}
256	if (j >= *cntp) {
257		*cntp = j;
258		return (AE_LIMIT);
259	} else {
260		*cntp = j;
261		return (AE_OK);
262	}
263}
264
265uint_t
266acpidev_resource_get_range_count(acpidev_resource_handle_t rhdl,
267    uint_t mask, uint_t value)
268{
269	uint_t i, j;
270
271	ASSERT(rhdl != NULL);
272	for (i = 0, j = 0; i < rhdl->acpidev_range_count; i++) {
273		if ((rhdl->acpidev_rangep[i].child_hi & mask) == value) {
274			j++;
275		}
276	}
277
278	return (j);
279}
280
281ACPI_STATUS
282acpidev_resource_insert_bus(acpidev_resource_handle_t rhdl,
283    acpidev_bus_range_t *busp)
284{
285	ASSERT(rhdl != NULL);
286	ASSERT(busp != NULL);
287	if (rhdl->acpidev_bus_count >= rhdl->acpidev_bus_max) {
288		acpidev_resource_handle_grow(rhdl);
289	}
290	ASSERT(rhdl->acpidev_bus_count < rhdl->acpidev_bus_max);
291	rhdl->acpidev_busp[rhdl->acpidev_bus_count] = *busp;
292	rhdl->acpidev_bus_count++;
293
294	return (AE_OK);
295}
296
297ACPI_STATUS
298acpidev_resource_get_buses(acpidev_resource_handle_t rhdl,
299    acpidev_bus_range_t *busp, uint_t *cntp)
300{
301	uint_t i, j;
302
303	ASSERT(rhdl != NULL);
304	ASSERT(cntp != NULL);
305	if (rhdl == NULL || cntp == NULL || (busp == NULL && *cntp != 0)) {
306		return (AE_BAD_PARAMETER);
307	}
308	for (i = 0, j = 0; i < rhdl->acpidev_bus_count; i++) {
309		if (j < *cntp) {
310			busp[j] = rhdl->acpidev_busp[i];
311		}
312		j++;
313	}
314	if (j >= *cntp) {
315		*cntp = j;
316		return (AE_LIMIT);
317	} else {
318		*cntp = j;
319		return (AE_OK);
320	}
321}
322
323uint_t
324acpidev_resource_get_bus_count(acpidev_resource_handle_t rhdl)
325{
326	ASSERT(rhdl != NULL);
327	return (rhdl->acpidev_bus_count);
328}
329
330ACPI_STATUS
331acpidev_resource_insert_dma(acpidev_resource_handle_t rhdl, int dma)
332{
333	ASSERT(rhdl != NULL);
334	if (rhdl->acpidev_dma_count >= ACPIDEV_RES_DMA_MAX) {
335		ACPIDEV_DEBUG(CE_WARN,
336		    "!acpidev: too many DMA resources, max %u.",
337		    ACPIDEV_RES_DMA_MAX);
338		return (AE_LIMIT);
339	}
340	rhdl->acpidev_dmap[rhdl->acpidev_dma_count] = dma;
341	rhdl->acpidev_dma_count++;
342
343	return (AE_OK);
344}
345
346ACPI_STATUS
347acpidev_resource_get_dmas(acpidev_resource_handle_t rhdl,
348    uint_t *dmap, uint_t *cntp)
349{
350	uint_t i, j;
351
352	ASSERT(rhdl != NULL);
353	ASSERT(cntp != NULL);
354	if (rhdl == NULL || cntp == NULL || (dmap == NULL && *cntp != 0)) {
355		return (AE_BAD_PARAMETER);
356	}
357	for (i = 0, j = 0; i < rhdl->acpidev_dma_count; i++) {
358		if (j < *cntp) {
359			dmap[j] = rhdl->acpidev_dmap[i];
360		}
361		j++;
362	}
363	if (j >= *cntp) {
364		*cntp = j;
365		return (AE_LIMIT);
366	} else {
367		*cntp = j;
368		return (AE_OK);
369	}
370}
371
372uint_t
373acpidev_resource_get_dma_count(acpidev_resource_handle_t rhdl)
374{
375	ASSERT(rhdl != NULL);
376	return (rhdl->acpidev_dma_count);
377}
378
379ACPI_STATUS
380acpidev_resource_insert_irq(acpidev_resource_handle_t rhdl, int irq)
381{
382	ASSERT(rhdl != NULL);
383	if (rhdl->acpidev_irq_count >= ACPIDEV_RES_IRQ_MAX) {
384		ACPIDEV_DEBUG(CE_WARN,
385		    "!acpidev: too many IRQ resources, max %u.",
386		    ACPIDEV_RES_IRQ_MAX);
387		return (AE_LIMIT);
388	}
389	rhdl->acpidev_irqp[rhdl->acpidev_irq_count] = irq;
390	rhdl->acpidev_irq_count++;
391
392	return (AE_OK);
393}
394
395ACPI_STATUS
396acpidev_resource_get_irqs(acpidev_resource_handle_t rhdl,
397    uint_t *irqp, uint_t *cntp)
398{
399	uint_t i, j;
400
401	ASSERT(rhdl != NULL);
402	ASSERT(cntp != NULL);
403	if (rhdl == NULL || cntp == NULL || (irqp == NULL && *cntp != 0)) {
404		return (AE_BAD_PARAMETER);
405	}
406	for (i = 0, j = 0; i < rhdl->acpidev_irq_count; i++) {
407		if (j < *cntp) {
408			irqp[j] = rhdl->acpidev_irqp[i];
409		}
410		j++;
411	}
412	if (j >= *cntp) {
413		*cntp = j;
414		return (AE_LIMIT);
415	} else {
416		*cntp = j;
417		return (AE_OK);
418	}
419}
420
421uint_t
422acpidev_resource_get_irq_count(acpidev_resource_handle_t rhdl)
423{
424	ASSERT(rhdl != NULL);
425	return (rhdl->acpidev_irq_count);
426}
427
428static ACPI_STATUS
429acpidev_resource_address64(acpidev_resource_handle_t rhdl,
430    ACPI_RESOURCE_ADDRESS64 *addrp)
431{
432	ACPI_STATUS rc = AE_OK;
433	uint_t high;
434
435	ASSERT(addrp != NULL && rhdl != NULL);
436	if (addrp->Address.AddressLength == 0) {
437		return (AE_OK);
438	}
439
440	switch (addrp->ResourceType) {
441	case ACPI_MEMORY_RANGE:
442		high = ACPIDEV_REG_TYPE_MEMORY;
443		if (addrp->Decode == ACPI_SUB_DECODE) {
444			high |= ACPIDEV_REG_SUB_DEC;
445		}
446		if (addrp->Info.Mem.Translation) {
447			high |= ACPIDEV_REG_TRANSLATED;
448		}
449		if (addrp->Info.Mem.Caching == ACPI_NON_CACHEABLE_MEMORY) {
450			high |= ACPIDEV_REG_MEM_COHERENT_NC;
451		} else if (addrp->Info.Mem.Caching == ACPI_CACHABLE_MEMORY) {
452			high |= ACPIDEV_REG_MEM_COHERENT_CA;
453		} else if (addrp->Info.Mem.Caching ==
454		    ACPI_WRITE_COMBINING_MEMORY) {
455			high |= ACPIDEV_REG_MEM_COHERENT_WC;
456		} else if (addrp->Info.Mem.Caching ==
457		    ACPI_PREFETCHABLE_MEMORY) {
458			high |= ACPIDEV_REG_MEM_COHERENT_PF;
459		} else {
460			ACPIDEV_DEBUG(CE_WARN,
461			    "!acpidev: unknown memory caching type %u.",
462			    addrp->Info.Mem.Caching);
463			rc = AE_ERROR;
464			break;
465		}
466		if (addrp->Info.Mem.WriteProtect == ACPI_READ_WRITE_MEMORY) {
467			high |= ACPIDEV_REG_MEM_WRITABLE;
468		}
469
470		/* Generate 'reg' for producer. */
471		if (addrp->ProducerConsumer == ACPI_CONSUMER &&
472		    rhdl->acpidev_consumer == B_TRUE) {
473			acpidev_regspec_t reg;
474
475			reg.phys_hi = high;
476			reg.phys_mid = addrp->Address.Minimum >> 32;
477			reg.phys_low = addrp->Address.Minimum & 0xFFFFFFFF;
478			reg.size_hi = addrp->Address.AddressLength >> 32;
479			reg.size_low = addrp->Address.AddressLength &
480			    0xFFFFFFFF;
481			rc = acpidev_resource_insert_reg(rhdl, &reg);
482			if (ACPI_FAILURE(rc)) {
483				ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to "
484				    "insert regspec into resource handle.");
485			}
486		/* Generate 'ranges' for producer. */
487		} else if (addrp->ProducerConsumer == ACPI_PRODUCER &&
488		    rhdl->acpidev_consumer == B_FALSE) {
489			uint64_t paddr;
490			acpidev_ranges_t range;
491
492			range.child_hi = high;
493			range.child_mid = addrp->Address.Minimum >> 32;
494			range.child_low = addrp->Address.Minimum & 0xFFFFFFFF;
495			/* It's IO on parent side if Translation is true. */
496			if (addrp->Info.Mem.Translation) {
497				range.parent_hi = ACPIDEV_REG_TYPE_IO;
498			} else {
499				range.parent_hi = high;
500			}
501			paddr = addrp->Address.Minimum +
502			    addrp->Address.TranslationOffset;
503			range.parent_mid = paddr >> 32;
504			range.parent_low = paddr & 0xFFFFFFFF;
505			range.size_hi = addrp->Address.AddressLength >> 32;
506			range.size_low = addrp->Address.AddressLength &
507			    0xFFFFFFFF;
508			rc = acpidev_resource_insert_range(rhdl, &range);
509			if (ACPI_FAILURE(rc)) {
510				ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to "
511				    "insert range into resource handle.");
512			}
513		}
514		break;
515
516	case ACPI_IO_RANGE:
517		high = ACPIDEV_REG_TYPE_IO;
518		if (addrp->Decode == ACPI_SUB_DECODE) {
519			high |= ACPIDEV_REG_SUB_DEC;
520		}
521		if (addrp->Info.Io.Translation) {
522			high |= ACPIDEV_REG_TRANSLATED;
523		}
524		if (addrp->Info.Io.RangeType == ACPI_NON_ISA_ONLY_RANGES) {
525			high |= ACPIDEV_REG_IO_RANGE_NONISA;
526		} else if (addrp->Info.Io.RangeType == ACPI_ISA_ONLY_RANGES) {
527			high |= ACPIDEV_REG_IO_RANGE_ISA;
528		} else if (addrp->Info.Io.RangeType == ACPI_ENTIRE_RANGE) {
529			high |= ACPIDEV_REG_IO_RANGE_FULL;
530		} else {
531			ACPIDEV_DEBUG(CE_WARN,
532			    "!acpidev: unknown IO range type %u.",
533			    addrp->Info.Io.RangeType);
534			rc = AE_ERROR;
535			break;
536		}
537		if (addrp->Info.Io.TranslationType == ACPI_SPARSE_TRANSLATION) {
538			high |= ACPIDEV_REG_IO_SPARSE;
539		}
540
541		/* Generate 'reg' for producer. */
542		if (addrp->ProducerConsumer == ACPI_CONSUMER &&
543		    rhdl->acpidev_consumer == B_TRUE) {
544			acpidev_regspec_t reg;
545
546			reg.phys_hi = high;
547			reg.phys_mid = addrp->Address.Minimum >> 32;
548			reg.phys_low = addrp->Address.Minimum & 0xFFFFFFFF;
549			reg.size_hi = addrp->Address.AddressLength >> 32;
550			reg.size_low = addrp->Address.AddressLength &
551			    0xFFFFFFFF;
552			rc = acpidev_resource_insert_reg(rhdl, &reg);
553			if (ACPI_FAILURE(rc)) {
554				ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to "
555				    "insert regspec into resource handle.");
556			}
557		/* Generate 'ranges' for producer. */
558		} else if (addrp->ProducerConsumer == ACPI_PRODUCER &&
559		    rhdl->acpidev_consumer == B_FALSE) {
560			uint64_t paddr;
561			acpidev_ranges_t range;
562
563			range.child_hi = high;
564			range.child_mid = addrp->Address.Minimum >> 32;
565			range.child_low = addrp->Address.Minimum & 0xFFFFFFFF;
566			/* It's Memory on parent side if Translation is true. */
567			if (addrp->Info.Io.Translation) {
568				range.parent_hi = ACPIDEV_REG_TYPE_MEMORY;
569			} else {
570				range.parent_hi = high;
571			}
572			paddr = addrp->Address.Minimum +
573			    addrp->Address.TranslationOffset;
574			range.parent_mid = paddr >> 32;
575			range.parent_low = paddr & 0xFFFFFFFF;
576			range.size_hi = addrp->Address.AddressLength >> 32;
577			range.size_low = addrp->Address.AddressLength &
578			    0xFFFFFFFF;
579			rc = acpidev_resource_insert_range(rhdl, &range);
580			if (ACPI_FAILURE(rc)) {
581				ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to "
582				    "insert range into resource handle.");
583			}
584		}
585		break;
586
587	case ACPI_BUS_NUMBER_RANGE:
588		/* Only support producer of BUS. */
589		if (addrp->ProducerConsumer == ACPI_PRODUCER &&
590		    rhdl->acpidev_consumer == B_FALSE) {
591			uint64_t end;
592			acpidev_bus_range_t bus;
593
594			end = addrp->Address.Minimum +
595			    addrp->Address.AddressLength;
596			if (end < addrp->Address.Minimum || end > UINT_MAX) {
597				ACPIDEV_DEBUG(CE_WARN, "!acpidev: bus range "
598				    "in ADDRESS64 is invalid.");
599				rc = AE_ERROR;
600				break;
601			}
602			bus.bus_start = addrp->Address.Minimum & 0xFFFFFFFF;
603			bus.bus_end = end & 0xFFFFFFFF;
604			ASSERT(bus.bus_start <= bus.bus_end);
605			rc = acpidev_resource_insert_bus(rhdl, &bus);
606			if (ACPI_FAILURE(rc)) {
607				ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to "
608				    "insert bus range into resource handle.");
609			}
610		}
611		break;
612
613	default:
614		ACPIDEV_DEBUG(CE_WARN,
615		    "!acpidev: unknown resource type %u in ADDRESS64.",
616		    addrp->ResourceType);
617		rc = AE_BAD_PARAMETER;
618	}
619
620	return (rc);
621}
622
623static ACPI_STATUS
624acpidev_resource_walk_producer(ACPI_RESOURCE *rscp, void *ctxp)
625{
626	ACPI_STATUS rc = AE_OK;
627	acpidev_resource_handle_t rhdl;
628
629	ASSERT(ctxp != NULL);
630	rhdl = (acpidev_resource_handle_t)ctxp;
631	ASSERT(rhdl->acpidev_consumer == B_FALSE);
632
633	switch (rscp->Type) {
634	case ACPI_RESOURCE_TYPE_DMA:
635	case ACPI_RESOURCE_TYPE_IRQ:
636	case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
637	case ACPI_RESOURCE_TYPE_FIXED_IO:
638	case ACPI_RESOURCE_TYPE_MEMORY24:
639	case ACPI_RESOURCE_TYPE_MEMORY32:
640	case ACPI_RESOURCE_TYPE_FIXED_MEMORY32:
641	case ACPI_RESOURCE_TYPE_GENERIC_REGISTER:
642	case ACPI_RESOURCE_TYPE_VENDOR:
643		ACPIDEV_DEBUG(CE_NOTE,
644		    "!acpidev: unsupported producer resource type %u, ignored.",
645		    rscp->Type);
646		break;
647
648	case ACPI_RESOURCE_TYPE_IO:
649	{
650		acpidev_ranges_t range;
651
652		range.child_hi = ACPIDEV_REG_TYPE_IO;
653		range.child_hi |= ACPIDEV_REG_IO_RANGE_FULL;
654		if (rscp->Data.Io.IoDecode == ACPI_DECODE_16) {
655			range.child_hi |= ACPIDEV_REG_IO_DECODE16;
656		}
657		range.parent_hi = range.child_hi;
658		range.parent_mid = range.child_mid = 0;
659		range.parent_low = range.child_low = rscp->Data.Io.Minimum;
660		range.size_hi = 0;
661		range.size_low = rscp->Data.Io.AddressLength;
662		if ((uint64_t)range.child_low + range.size_low > UINT16_MAX) {
663			ACPIDEV_DEBUG(CE_WARN, "!acpidev: invalid IO record, "
664			    "IO max is out of range.");
665			rc = AE_ERROR;
666		} else if (range.size_low != 0) {
667			rc = acpidev_resource_insert_range(rhdl, &range);
668			if (ACPI_FAILURE(rc)) {
669				ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to "
670				    "insert range into resource handle.");
671			}
672		}
673		break;
674	}
675
676	case ACPI_RESOURCE_TYPE_ADDRESS16:
677	case ACPI_RESOURCE_TYPE_ADDRESS32:
678	case ACPI_RESOURCE_TYPE_ADDRESS64:
679	{
680		ACPI_RESOURCE_ADDRESS64 addr64;
681
682		if (rscp->Data.Address.ProducerConsumer != ACPI_PRODUCER) {
683			ACPIDEV_DEBUG(CE_NOTE, "!acpidev: producer encountered "
684			    "a CONSUMER resource, ignored.");
685		} else if (ACPI_FAILURE(AcpiResourceToAddress64(rscp,
686		    &addr64))) {
687			ACPIDEV_DEBUG(CE_WARN,
688			    "!acpidev: failed to convert resource to ADDR64.");
689		} else if (ACPI_FAILURE(rc = acpidev_resource_address64(rhdl,
690		    &addr64))) {
691			ACPIDEV_DEBUG(CE_WARN,
692			    "!acpidev: failed to handle ADDRESS resource.");
693		}
694		break;
695	}
696
697	case ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64:
698	{
699		ACPI_RESOURCE_ADDRESS64 addr64;
700
701		if (rscp->Data.ExtAddress64.ProducerConsumer != ACPI_PRODUCER) {
702			ACPIDEV_DEBUG(CE_NOTE, "!acpidev: producer encountered "
703			    "a CONSUMER resource, ignored.");
704			break;
705		}
706
707		*(ACPI_RESOURCE_ADDRESS *)&addr64 = rscp->Data.Address;
708		addr64.Address.Granularity =
709		    rscp->Data.ExtAddress64.Address.Granularity;
710		addr64.Address.Minimum =
711		    rscp->Data.ExtAddress64.Address.Minimum;
712		addr64.Address.Maximum =
713		    rscp->Data.ExtAddress64.Address.Maximum;
714		addr64.Address.TranslationOffset =
715		    rscp->Data.ExtAddress64.Address.TranslationOffset;
716		addr64.Address.AddressLength =
717		    rscp->Data.ExtAddress64.Address.AddressLength;
718		if (ACPI_FAILURE(rc = acpidev_resource_address64(rhdl,
719		    &addr64))) {
720			ACPIDEV_DEBUG(CE_WARN,
721			    "!acpidev: failed to handle EXTADDRESS resource.");
722		}
723		break;
724	}
725
726	case ACPI_RESOURCE_TYPE_START_DEPENDENT:
727	case ACPI_RESOURCE_TYPE_END_DEPENDENT:
728		ACPIDEV_DEBUG(CE_NOTE, "!acpidev: producer encountered "
729		    "START_DEPENDENT or END_DEPENDENT tag, ignored.");
730		break;
731
732	case ACPI_RESOURCE_TYPE_END_TAG:
733		/* Finish walking when we encounter END_TAG. */
734		rc = AE_CTRL_TERMINATE;
735		break;
736
737	default:
738		ACPIDEV_DEBUG(CE_NOTE,
739		    "!acpidev: unknown ACPI resource type %u, ignored.",
740		    rscp->Type);
741		break;
742	}
743
744	return (rc);
745}
746
747static ACPI_STATUS
748acpidev_resource_walk_consumer(ACPI_RESOURCE *rscp, void *ctxp)
749{
750	ACPI_STATUS rc = AE_OK;
751	acpidev_resource_handle_t rhdl;
752
753	ASSERT(ctxp != NULL);
754	rhdl = (acpidev_resource_handle_t)ctxp;
755	ASSERT(rhdl->acpidev_consumer == B_TRUE);
756
757	switch (rscp->Type) {
758	case ACPI_RESOURCE_TYPE_MEMORY24:
759	case ACPI_RESOURCE_TYPE_GENERIC_REGISTER:
760	case ACPI_RESOURCE_TYPE_VENDOR:
761		ACPIDEV_DEBUG(CE_NOTE,
762		    "!acpidev: unsupported consumer resource type %u, ignored.",
763		    rscp->Type);
764		break;
765
766	case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
767	{
768		int i;
769
770		if (rscp->Data.ExtendedIrq.ProducerConsumer != ACPI_CONSUMER) {
771			ACPIDEV_DEBUG(CE_NOTE, "!acpidev: consumer encountered "
772			    "a PRODUCER resource, ignored.");
773			break;
774		}
775		for (i = 0; i < rscp->Data.ExtendedIrq.InterruptCount; i++) {
776			if (ACPI_SUCCESS(acpidev_resource_insert_irq(rhdl,
777			    rscp->Data.ExtendedIrq.Interrupts[i]))) {
778				continue;
779			}
780			ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to insert"
781			    "Extended IRQ into resource handle.");
782			rc = AE_ERROR;
783			break;
784		}
785		break;
786	}
787
788	case ACPI_RESOURCE_TYPE_IRQ:
789	{
790		int i;
791
792		for (i = 0; i < rscp->Data.Irq.InterruptCount; i++) {
793			if (ACPI_SUCCESS(acpidev_resource_insert_irq(rhdl,
794			    rscp->Data.Irq.Interrupts[i]))) {
795				continue;
796			}
797			ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to insert"
798			    "IRQ into resource handle.");
799			rc = AE_ERROR;
800			break;
801		}
802		break;
803	}
804
805	case ACPI_RESOURCE_TYPE_DMA:
806	{
807		int i;
808
809		for (i = 0; i < rscp->Data.Dma.ChannelCount; i++) {
810			if (ACPI_SUCCESS(acpidev_resource_insert_dma(rhdl,
811			    rscp->Data.Dma.Channels[i]))) {
812				continue;
813			}
814			ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to insert"
815			    "dma into resource handle.");
816			rc = AE_ERROR;
817			break;
818		}
819		break;
820	}
821
822	case ACPI_RESOURCE_TYPE_IO:
823	case ACPI_RESOURCE_TYPE_FIXED_IO:
824	{
825		acpidev_regspec_t reg;
826
827		reg.phys_hi = ACPIDEV_REG_TYPE_IO;
828		reg.phys_hi |= ACPIDEV_REG_IO_RANGE_FULL;
829		if (rscp->Type == ACPI_RESOURCE_TYPE_IO) {
830			if (rscp->Data.Io.IoDecode == ACPI_DECODE_16) {
831				reg.phys_hi |= ACPIDEV_REG_IO_DECODE16;
832			}
833			reg.phys_low = rscp->Data.Io.Minimum;
834			reg.size_low = rscp->Data.Io.AddressLength;
835		} else {
836			reg.phys_hi |= ACPIDEV_REG_IO_DECODE16;
837			reg.phys_low = rscp->Data.FixedIo.Address;
838			reg.size_low = rscp->Data.FixedIo.AddressLength;
839		}
840		reg.phys_mid = 0;
841		reg.size_hi = 0;
842		if ((uint64_t)reg.phys_low + reg.size_low > UINT16_MAX) {
843			ACPIDEV_DEBUG(CE_WARN, "!acpidev: invalid IO/FIXEDIO "
844			    "record, IO max is out of range.");
845			rc = AE_ERROR;
846		} else if (reg.size_low != 0) {
847			rc = acpidev_resource_insert_reg(rhdl, &reg);
848			if (ACPI_FAILURE(rc)) {
849				ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to "
850				    "insert reg into resource handle.");
851			}
852		}
853		break;
854	}
855
856	case ACPI_RESOURCE_TYPE_MEMORY32:
857	case ACPI_RESOURCE_TYPE_FIXED_MEMORY32:
858	{
859		acpidev_regspec_t reg;
860
861		reg.phys_hi = ACPIDEV_REG_TYPE_MEMORY;
862		reg.phys_hi |= ACPIDEV_REG_MEM_COHERENT_CA;
863		if (rscp->Type == ACPI_RESOURCE_TYPE_MEMORY32) {
864			if (rscp->Data.Memory32.WriteProtect ==
865			    ACPI_READ_WRITE_MEMORY) {
866				reg.phys_hi |= ACPIDEV_REG_MEM_WRITABLE;
867			}
868			reg.phys_low = rscp->Data.Memory32.Minimum;
869			reg.size_low = rscp->Data.Memory32.AddressLength;
870		} else {
871			if (rscp->Data.FixedMemory32.WriteProtect ==
872			    ACPI_READ_WRITE_MEMORY) {
873				reg.phys_hi |= ACPIDEV_REG_MEM_WRITABLE;
874			}
875			reg.phys_low = rscp->Data.FixedMemory32.Address;
876			reg.size_low = rscp->Data.FixedMemory32.AddressLength;
877		}
878		reg.phys_mid = 0;
879		reg.size_hi = 0;
880		if ((uint64_t)reg.phys_low + reg.size_low > UINT32_MAX) {
881			ACPIDEV_DEBUG(CE_WARN,
882			    "!acpidev: invalid MEMORY32/FIXEDMEMORY32 record, "
883			    "memory max is out of range.");
884			rc = AE_ERROR;
885		} else if (reg.size_low != 0) {
886			rc = acpidev_resource_insert_reg(rhdl, &reg);
887			if (ACPI_FAILURE(rc)) {
888				ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to "
889				    "insert reg into resource handle.");
890			}
891		}
892		break;
893	}
894
895	case ACPI_RESOURCE_TYPE_ADDRESS16:
896	case ACPI_RESOURCE_TYPE_ADDRESS32:
897	case ACPI_RESOURCE_TYPE_ADDRESS64:
898	{
899		ACPI_RESOURCE_ADDRESS64 addr64;
900
901		if (rscp->Data.Address.ProducerConsumer != ACPI_CONSUMER) {
902			ACPIDEV_DEBUG(CE_NOTE, "!acpidev: consumer encountered "
903			    "a PRODUCER resource, ignored.");
904		} else if (ACPI_FAILURE(AcpiResourceToAddress64(rscp,
905		    &addr64))) {
906			ACPIDEV_DEBUG(CE_WARN,
907			    "!acpidev: failed to convert resource to ADDR64.");
908		} else if (ACPI_FAILURE(rc = acpidev_resource_address64(rhdl,
909		    &addr64))) {
910			ACPIDEV_DEBUG(CE_WARN,
911			    "!acpidev: failed to handle ADDRESS resource.");
912		}
913		break;
914	}
915
916	case ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64:
917	{
918		ACPI_RESOURCE_ADDRESS64 addr64;
919
920		if (rscp->Data.ExtAddress64.ProducerConsumer != ACPI_CONSUMER) {
921			ACPIDEV_DEBUG(CE_NOTE, "!acpidev: consumer encountered "
922			    "a PRODUCER resource, ignored.");
923			break;
924		}
925
926		*(ACPI_RESOURCE_ADDRESS *)&addr64 = rscp->Data.Address;
927		addr64.Address.Granularity =
928		    rscp->Data.ExtAddress64.Address.Granularity;
929		addr64.Address.Minimum =
930		    rscp->Data.ExtAddress64.Address.Minimum;
931		addr64.Address.Maximum =
932		    rscp->Data.ExtAddress64.Address.Maximum;
933		addr64.Address.TranslationOffset =
934		    rscp->Data.ExtAddress64.Address.TranslationOffset;
935		addr64.Address.AddressLength =
936		    rscp->Data.ExtAddress64.Address.AddressLength;
937		if (ACPI_FAILURE(rc = acpidev_resource_address64(rhdl,
938		    &addr64))) {
939			ACPIDEV_DEBUG(CE_WARN,
940			    "!acpidev: failed to handle EXTADDRESS resource.");
941		}
942		break;
943	}
944
945	case ACPI_RESOURCE_TYPE_START_DEPENDENT:
946	case ACPI_RESOURCE_TYPE_END_DEPENDENT:
947		ACPIDEV_DEBUG(CE_NOTE, "!acpidev: consumer encountered "
948		    "START_DEPENDENT or END_DEPENDENT tag, ignored.");
949		break;
950
951	case ACPI_RESOURCE_TYPE_END_TAG:
952		/* Finish walking when we encounter END_TAG. */
953		rc = AE_CTRL_TERMINATE;
954		break;
955
956	default:
957		ACPIDEV_DEBUG(CE_NOTE,
958		    "!acpidev: unknown ACPI resource type %u, ignored.",
959		    rscp->Type);
960		break;
961	}
962
963	return (rc);
964}
965
966ACPI_STATUS
967acpidev_resource_walk(ACPI_HANDLE hdl, char *method,
968    boolean_t consumer, acpidev_resource_handle_t *rhdlp)
969{
970	ACPI_STATUS rc = AE_OK;
971	ACPI_HANDLE mhdl = NULL;
972	acpidev_resource_handle_t rhdl = NULL;
973
974	ASSERT(hdl != NULL);
975	ASSERT(method != NULL);
976	ASSERT(rhdlp != NULL);
977	if (hdl == NULL) {
978		ACPIDEV_DEBUG(CE_WARN,
979		    "!acpidev: hdl is NULL in acpidev_resource_walk().");
980		return (AE_BAD_PARAMETER);
981	} else if (method == NULL) {
982		ACPIDEV_DEBUG(CE_WARN,
983		    "!acpidev: method is NULL in acpidev_resource_walk().");
984		return (AE_BAD_PARAMETER);
985	} else if (rhdlp == NULL) {
986		ACPIDEV_DEBUG(CE_WARN, "!acpidev: resource handle ptr is NULL "
987		    "in acpidev_resource_walk().");
988		return (AE_BAD_PARAMETER);
989	}
990
991	/* Check whether method exists under object. */
992	if (ACPI_FAILURE(AcpiGetHandle(hdl, method, &mhdl))) {
993		char *objname = acpidev_get_object_name(hdl);
994		ACPIDEV_DEBUG(CE_NOTE,
995		    "!acpidev: method %s doesn't exist under %s",
996		    method, objname);
997		acpidev_free_object_name(objname);
998		return (AE_NOT_FOUND);
999	}
1000
1001	/* Walk all resources. */
1002	rhdl = acpidev_resource_handle_alloc(consumer);
1003	if (consumer) {
1004		rc = AcpiWalkResources(hdl, method,
1005		    acpidev_resource_walk_consumer, rhdl);
1006	} else {
1007		rc = AcpiWalkResources(hdl, method,
1008		    acpidev_resource_walk_producer, rhdl);
1009	}
1010	if (ACPI_SUCCESS(rc)) {
1011		*rhdlp = rhdl;
1012	} else {
1013		acpidev_resource_handle_free(rhdl);
1014	}
1015	if (ACPI_FAILURE(rc)) {
1016		char *objname = acpidev_get_object_name(hdl);
1017		ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to walk resource from "
1018		    "method %s under %s.", method, objname);
1019		acpidev_free_object_name(objname);
1020	}
1021
1022	return (rc);
1023}
1024
1025ACPI_STATUS
1026acpidev_resource_process(acpidev_walk_info_t *infop, boolean_t consumer)
1027{
1028	ACPI_STATUS rc;
1029	char path[MAXPATHLEN];
1030	acpidev_resource_handle_t rhdl = NULL;
1031
1032	ASSERT(infop != NULL);
1033	if (infop == NULL) {
1034		ACPIDEV_DEBUG(CE_WARN, "!acpidev: invalid parameter "
1035		    "in acpidev_resource_process().");
1036		return (AE_BAD_PARAMETER);
1037	}
1038
1039	/* Walk all resources. */
1040	(void) ddi_pathname(infop->awi_dip, path);
1041	rc = acpidev_resource_walk(infop->awi_hdl, METHOD_NAME__CRS,
1042	    consumer, &rhdl);
1043	if (ACPI_FAILURE(rc)) {
1044		ACPIDEV_DEBUG(CE_WARN,
1045		    "!acpidev: failed to walk ACPI resources of %s(%s).",
1046		    path, infop->awi_name);
1047		return (rc);
1048	}
1049
1050	if (consumer) {
1051		/* Create device properties for consumer. */
1052
1053		/* Create 'reg' and 'assigned-addresses' properties. */
1054		if (rhdl->acpidev_reg_count > 0 &&
1055		    ndi_prop_update_int_array(DDI_DEV_T_NONE, infop->awi_dip,
1056		    "reg", (int *)rhdl->acpidev_regp,
1057		    rhdl->acpidev_reg_count * sizeof (acpidev_regspec_t) /
1058		    sizeof (int)) != NDI_SUCCESS) {
1059			ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to set "
1060			    "'reg' property for %s.", path);
1061			rc = AE_ERROR;
1062			goto out;
1063		}
1064		if (rhdl->acpidev_reg_count > 0 &&
1065		    ndi_prop_update_int_array(DDI_DEV_T_NONE, infop->awi_dip,
1066		    "assigned-addresses", (int *)rhdl->acpidev_regp,
1067		    rhdl->acpidev_reg_count * sizeof (acpidev_regspec_t) /
1068		    sizeof (int)) != NDI_SUCCESS) {
1069			ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to set "
1070			    "'assigned-addresses' property for %s.", path);
1071			rc = AE_ERROR;
1072			goto out;
1073		}
1074
1075		/* Create 'interrupts' property. */
1076		if (rhdl->acpidev_irq_count > 0 &&
1077		    ndi_prop_update_int_array(DDI_DEV_T_NONE, infop->awi_dip,
1078		    "interrupts", (int *)rhdl->acpidev_irqp,
1079		    rhdl->acpidev_irq_count) != NDI_SUCCESS) {
1080			ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to set "
1081			    "'interrupts' property for %s.", path);
1082			rc = AE_ERROR;
1083			goto out;
1084		}
1085
1086		/* Create 'dma-channels' property. */
1087		if (rhdl->acpidev_dma_count > 0 &&
1088		    ndi_prop_update_int_array(DDI_DEV_T_NONE, infop->awi_dip,
1089		    "dma-channels", (int *)rhdl->acpidev_dmap,
1090		    rhdl->acpidev_dma_count) != NDI_SUCCESS) {
1091			ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to set "
1092			    "'dma-channels' property for %s.", path);
1093			rc = AE_ERROR;
1094			goto out;
1095		}
1096
1097	} else {
1098		/* Create device properties for producer. */
1099
1100		/* Create 'ranges' property. */
1101		if (rhdl->acpidev_range_count > 0 &&
1102		    ndi_prop_update_int_array(DDI_DEV_T_NONE, infop->awi_dip,
1103		    "ranges", (int *)rhdl->acpidev_rangep,
1104		    rhdl->acpidev_range_count * sizeof (acpidev_ranges_t) /
1105		    sizeof (int)) != NDI_SUCCESS) {
1106			ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to set "
1107			    "'ranges' property for %s.", path);
1108			rc = AE_ERROR;
1109			goto out;
1110		}
1111
1112		/* Create 'bus-range' property. */
1113		if (rhdl->acpidev_bus_count > 0 &&
1114		    ndi_prop_update_int_array(DDI_DEV_T_NONE, infop->awi_dip,
1115		    "bus-range", (int *)rhdl->acpidev_busp,
1116		    rhdl->acpidev_bus_count * sizeof (acpidev_bus_range_t) /
1117		    sizeof (int)) != NDI_SUCCESS) {
1118			ACPIDEV_DEBUG(CE_WARN, "!acpidev: failed to set "
1119			    "'bus-range' property for %s.", path);
1120			rc = AE_ERROR;
1121			goto out;
1122		}
1123	}
1124
1125out:
1126	/* Free resources allocated by acpidev_resource_walk. */
1127	acpidev_resource_handle_free(rhdl);
1128
1129	return (rc);
1130}
1131