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