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, ®); 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, ®); 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, ®); 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, ®); 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