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