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