--- /dev/null Fri Jun 26 08:09:32 2009 +++ new/usr/src/uts/i86pc/io/acpi/acpidev/acpidev_resource.c Fri Jun 26 08:09:31 2009 @@ -0,0 +1,1088 @@ +/* + * Copyright (c) 2009, Intel Corporation. + * All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ACPIDEV_RES_INIT_ITEMS 8 +#define ACPIDEV_RES_INCR_ITEMS 8 + +/* Data structure to hold parsed resources during walking. */ +struct acpidev_resource_handle { + boolean_t acpidev_consumer; + int acpidev_reg_count; + int acpidev_reg_max; + acpidev_phys_spec_t *acpidev_regp; + acpidev_phys_spec_t acpidev_regs[ACPIDEV_RES_INIT_ITEMS]; + int acpidev_range_count; + int acpidev_range_max; + acpidev_ranges_t *acpidev_rangep; + acpidev_ranges_t acpidev_ranges[ACPIDEV_RES_INIT_ITEMS]; + int acpidev_bus_count; + int acpidev_bus_max; + acpidev_bus_range_t *acpidev_busp; + acpidev_bus_range_t acpidev_buses[ACPIDEV_RES_INIT_ITEMS]; + int acpidev_irq_count; + int acpidev_irqp[ACPIDEV_RES_IRQ_MAX]; + int acpidev_dma_count; + int acpidev_dmap[ACPIDEV_RES_DMA_MAX]; +}; + +acpidev_resource_handle_t +acpidev_resource_handle_alloc(boolean_t consumer) +{ + acpidev_resource_handle_t rhdl; + + rhdl = kmem_zalloc(sizeof (*rhdl), KM_SLEEP); + rhdl->acpidev_consumer = consumer; + rhdl->acpidev_reg_max = ACPIDEV_RES_INIT_ITEMS; + rhdl->acpidev_regp = rhdl->acpidev_regs; + rhdl->acpidev_range_max = ACPIDEV_RES_INIT_ITEMS; + rhdl->acpidev_rangep = rhdl->acpidev_ranges; + rhdl->acpidev_bus_max = ACPIDEV_RES_INIT_ITEMS; + rhdl->acpidev_busp = rhdl->acpidev_buses; + + return (rhdl); +} + +void +acpidev_resource_handle_free(acpidev_resource_handle_t rhdl) +{ + size_t sz; + + ASSERT(rhdl != NULL); + if (rhdl != NULL) { + if (rhdl->acpidev_regp != rhdl->acpidev_regs) { + sz = sizeof (acpidev_phys_spec_t) * + rhdl->acpidev_reg_max; + kmem_free(rhdl->acpidev_regp, sz); + } + if (rhdl->acpidev_rangep != rhdl->acpidev_ranges) { + sz = sizeof (acpidev_ranges_t) * + rhdl->acpidev_range_max; + kmem_free(rhdl->acpidev_rangep, sz); + } + if (rhdl->acpidev_busp != rhdl->acpidev_buses) { + sz = sizeof (acpidev_bus_range_t) * + rhdl->acpidev_bus_max; + kmem_free(rhdl->acpidev_busp, sz); + } + kmem_free(rhdl, sizeof (struct acpidev_resource_handle)); + } +} + +static void +acpidev_resource_handle_grow(acpidev_resource_handle_t rhdl) +{ + size_t sz; + + if (rhdl->acpidev_reg_count == rhdl->acpidev_reg_max) { + acpidev_phys_spec_t *regp; + + /* Prefer linear incremental here. */ + rhdl->acpidev_reg_max += ACPIDEV_RES_INCR_ITEMS; + sz = sizeof (*regp) * rhdl->acpidev_reg_max; + regp = kmem_zalloc(sz, KM_SLEEP); + sz = sizeof (*regp) * rhdl->acpidev_reg_count; + bcopy(rhdl->acpidev_regp, regp, sz); + if (rhdl->acpidev_regp != rhdl->acpidev_regs) { + kmem_free(rhdl->acpidev_regp, sz); + } + rhdl->acpidev_regp = regp; + } + + if (rhdl->acpidev_range_count == rhdl->acpidev_range_max) { + acpidev_ranges_t *rngp; + + /* Prefer linear incremental here. */ + rhdl->acpidev_range_max += ACPIDEV_RES_INCR_ITEMS; + sz = sizeof (*rngp) * rhdl->acpidev_range_max; + rngp = kmem_zalloc(sz, KM_SLEEP); + sz = sizeof (*rngp) * rhdl->acpidev_range_count; + bcopy(rhdl->acpidev_rangep, rngp, sz); + if (rhdl->acpidev_rangep != rhdl->acpidev_ranges) { + kmem_free(rhdl->acpidev_rangep, sz); + } + rhdl->acpidev_rangep = rngp; + } + + if (rhdl->acpidev_bus_count == rhdl->acpidev_bus_max) { + acpidev_bus_range_t *busp; + + /* Prefer linear incremental here. */ + rhdl->acpidev_bus_max += ACPIDEV_RES_INCR_ITEMS; + sz = sizeof (*busp) * rhdl->acpidev_bus_max; + busp = kmem_zalloc(sz, KM_SLEEP); + sz = sizeof (*busp) * rhdl->acpidev_bus_count; + bcopy(rhdl->acpidev_busp, busp, sz); + if (rhdl->acpidev_busp != rhdl->acpidev_buses) { + kmem_free(rhdl->acpidev_busp, sz); + } + rhdl->acpidev_busp = busp; + } +} + +ACPI_STATUS +acpidev_resource_insert_reg(acpidev_resource_handle_t rhdl, + acpidev_regspec_t *regp) +{ + ASSERT(rhdl != NULL); + ASSERT(regp != NULL); + if (rhdl->acpidev_reg_count >= rhdl->acpidev_reg_max) { + acpidev_resource_handle_grow(rhdl); + } + ASSERT(rhdl->acpidev_reg_count < rhdl->acpidev_reg_max); + rhdl->acpidev_regp[rhdl->acpidev_reg_count] = *regp; + rhdl->acpidev_reg_count++; + + return (AE_OK); +} + +ACPI_STATUS +acpidev_resourec_get_regs(acpidev_resource_handle_t rhdl, + uint_t mask, uint_t value, acpidev_regspec_t *regp, uint_t *cntp) +{ + uint_t i, j; + + ASSERT(rhdl != NULL); + ASSERT(cntp != NULL); + if (rhdl == NULL || cntp == NULL || (regp == NULL && *cntp != 0)) { + return (AE_BAD_PARAMETER); + } + for (i = 0, j = 0; i < rhdl->acpidev_reg_count; i++) { + if ((rhdl->acpidev_regp[i].phys_hi & mask) == value) { + if (j < *cntp) { + regp[j] = rhdl->acpidev_regp[i]; + } + j++; + } + } + if (j >= *cntp) { + *cntp = j; + return (AE_LIMIT); + } else { + *cntp = j; + return (AE_OK); + } +} + +uint_t +acpidev_resource_get_reg_count(acpidev_resource_handle_t rhdl, + uint_t mask, uint_t value) +{ + uint_t i, j; + + ASSERT(rhdl != NULL); + for (i = 0, j = 0; i < rhdl->acpidev_reg_count; i++) { + if ((rhdl->acpidev_regp[i].phys_hi & mask) == value) { + j++; + } + } + + return (j); +} + +ACPI_STATUS +acpidev_resource_insert_range(acpidev_resource_handle_t rhdl, + acpidev_ranges_t *rangep) +{ + ASSERT(rhdl != NULL); + ASSERT(rangep != NULL); + if (rhdl->acpidev_range_count >= rhdl->acpidev_range_max) { + acpidev_resource_handle_grow(rhdl); + } + ASSERT(rhdl->acpidev_range_count < rhdl->acpidev_range_max); + rhdl->acpidev_rangep[rhdl->acpidev_range_count] = *rangep; + rhdl->acpidev_range_count++; + + return (AE_OK); +} + +ACPI_STATUS +acpidev_resourec_get_ranges(acpidev_resource_handle_t rhdl, + uint_t mask, uint_t value, acpidev_ranges_t *rangep, uint_t *cntp) +{ + uint_t i, j; + + ASSERT(rhdl != NULL); + ASSERT(cntp != NULL); + if (rhdl == NULL || cntp == NULL || (rangep == NULL && *cntp != 0)) { + return (AE_BAD_PARAMETER); + } + for (i = 0, j = 0; i < rhdl->acpidev_range_count; i++) { + if ((rhdl->acpidev_rangep[i].child_hi & mask) == value) { + if (j < *cntp) { + rangep[j] = rhdl->acpidev_rangep[i]; + } + j++; + } + } + if (j >= *cntp) { + *cntp = j; + return (AE_LIMIT); + } else { + *cntp = j; + return (AE_OK); + } +} + +uint_t +acpidev_resource_get_range_count(acpidev_resource_handle_t rhdl, + uint_t mask, uint_t value) +{ + uint_t i, j; + + ASSERT(rhdl != NULL); + for (i = 0, j = 0; i < rhdl->acpidev_range_count; i++) { + if ((rhdl->acpidev_rangep[i].child_hi & mask) == value) { + j++; + } + } + + return (j); +} + +ACPI_STATUS +acpidev_resource_insert_bus(acpidev_resource_handle_t rhdl, + acpidev_bus_range_t *busp) +{ + ASSERT(rhdl != NULL); + ASSERT(busp != NULL); + if (rhdl->acpidev_bus_count >= rhdl->acpidev_bus_max) { + acpidev_resource_handle_grow(rhdl); + } + ASSERT(rhdl->acpidev_bus_count < rhdl->acpidev_bus_max); + rhdl->acpidev_busp[rhdl->acpidev_bus_count] = *busp; + rhdl->acpidev_bus_count++; + + return (AE_OK); +} + +ACPI_STATUS +acpidev_resourec_get_buses(acpidev_resource_handle_t rhdl, + acpidev_bus_range_t *busp, uint_t *cntp) +{ + uint_t i, j; + + ASSERT(rhdl != NULL); + ASSERT(cntp != NULL); + if (rhdl == NULL || cntp == NULL || (busp == NULL && *cntp != 0)) { + return (AE_BAD_PARAMETER); + } + for (i = 0, j = 0; i < rhdl->acpidev_bus_count; i++) { + if (j < *cntp) { + busp[j] = rhdl->acpidev_busp[i]; + } + j++; + } + if (j >= *cntp) { + *cntp = j; + return (AE_LIMIT); + } else { + *cntp = j; + return (AE_OK); + } +} + +uint_t +acpidev_resource_get_bus_count(acpidev_resource_handle_t rhdl) +{ + ASSERT(rhdl != NULL); + return (rhdl->acpidev_bus_count); +} + +ACPI_STATUS +acpidev_resource_insert_dma(acpidev_resource_handle_t rhdl, int dma) +{ + ASSERT(rhdl != NULL); + if (rhdl->acpidev_dma_count >= ACPIDEV_RES_DMA_MAX) { + ACPIDEV_DEBUG(CE_WARN, + "acpidev: too many DMA resources, max %u.", + ACPIDEV_RES_DMA_MAX); + return (AE_LIMIT); + } + rhdl->acpidev_dmap[rhdl->acpidev_dma_count] = dma; + rhdl->acpidev_dma_count++; + + return (AE_OK); +} + +ACPI_STATUS +acpidev_resourec_get_dmas(acpidev_resource_handle_t rhdl, + uint_t *dmap, uint_t *cntp) +{ + uint_t i, j; + + ASSERT(rhdl != NULL); + ASSERT(cntp != NULL); + if (rhdl == NULL || cntp == NULL || (dmap == NULL && *cntp != 0)) { + return (AE_BAD_PARAMETER); + } + for (i = 0, j = 0; i < rhdl->acpidev_dma_count; i++) { + if (j < *cntp) { + dmap[j] = rhdl->acpidev_dmap[i]; + } + j++; + } + if (j >= *cntp) { + *cntp = j; + return (AE_LIMIT); + } else { + *cntp = j; + return (AE_OK); + } +} + +uint_t +acpidev_resource_get_dma_count(acpidev_resource_handle_t rhdl) +{ + ASSERT(rhdl != NULL); + return (rhdl->acpidev_dma_count); +} + +ACPI_STATUS +acpidev_resource_insert_irq(acpidev_resource_handle_t rhdl, int irq) +{ + ASSERT(rhdl != NULL); + if (rhdl->acpidev_irq_count >= ACPIDEV_RES_IRQ_MAX) { + ACPIDEV_DEBUG(CE_WARN, + "acpidev: too many IRQ resources, max %u.", + ACPIDEV_RES_IRQ_MAX); + return (AE_LIMIT); + } + rhdl->acpidev_irqp[rhdl->acpidev_irq_count] = irq; + rhdl->acpidev_irq_count++; + + return (AE_OK); +} + +ACPI_STATUS +acpidev_resourec_get_irqs(acpidev_resource_handle_t rhdl, + uint_t *irqp, uint_t *cntp) +{ + uint_t i, j; + + ASSERT(rhdl != NULL); + ASSERT(cntp != NULL); + if (rhdl == NULL || cntp == NULL || (irqp == NULL && *cntp != 0)) { + return (AE_BAD_PARAMETER); + } + for (i = 0, j = 0; i < rhdl->acpidev_irq_count; i++) { + if (j < *cntp) { + irqp[j] = rhdl->acpidev_irqp[i]; + } + j++; + } + if (j >= *cntp) { + *cntp = j; + return (AE_LIMIT); + } else { + *cntp = j; + return (AE_OK); + } +} + +uint_t +acpidev_resource_get_irq_count(acpidev_resource_handle_t rhdl) +{ + ASSERT(rhdl != NULL); + return (rhdl->acpidev_irq_count); +} + +static ACPI_STATUS +acpidev_resource_address64(acpidev_resource_handle_t rhdl, + ACPI_RESOURCE_ADDRESS64 *addrp) +{ + ACPI_STATUS rc = AE_OK; + uint_t high; + + ASSERT(addrp != NULL && rhdl != NULL); + if (addrp->AddressLength == 0) { + return (AE_OK); + } + + switch (addrp->ResourceType) { + case ACPI_MEMORY_RANGE: + high = ACPIDEV_REG_TYPE_MEMORY; + if (addrp->Decode == ACPI_SUB_DECODE) { + high |= ACPIDEV_REG_SUB_DEC; + } + if (addrp->Info.Mem.Translation) { + high |= ACPIDEV_REG_TRANSLATED; + } + if (addrp->Info.Mem.Caching == ACPI_NON_CACHEABLE_MEMORY) { + high |= ACPIDEV_REG_MEM_COHERENT_NC; + } else if (addrp->Info.Mem.Caching == ACPI_CACHABLE_MEMORY) { + high |= ACPIDEV_REG_MEM_COHERENT_CA; + } else if (addrp->Info.Mem.Caching == + ACPI_WRITE_COMBINING_MEMORY) { + high |= ACPIDEV_REG_MEM_COHERENT_WC; + } else if (addrp->Info.Mem.Caching == + ACPI_PREFETCHABLE_MEMORY) { + high |= ACPIDEV_REG_MEM_COHERENT_PF; + } else { + ACPIDEV_DEBUG(CE_WARN, + "acpidev: unknown memory caching type %u.", + addrp->Info.Mem.Caching); + rc = AE_ERROR; + break; + } + if (addrp->Info.Mem.WriteProtect == ACPI_READ_WRITE_MEMORY) { + high |= ACPIDEV_REG_MEM_WRITABLE; + } + + /* Generate 'reg' for producer. */ + if (addrp->ProducerConsumer == ACPI_CONSUMER && + rhdl->acpidev_consumer == B_TRUE) { + acpidev_regspec_t reg; + + reg.phys_hi = high; + reg.phys_mid = addrp->Minimum >> 32; + reg.phys_low = addrp->Minimum & 0xFFFFFFFF; + reg.size_hi = addrp->AddressLength >> 32; + reg.size_low = addrp->AddressLength & 0xFFFFFFFF; + rc = acpidev_resource_insert_reg(rhdl, ®); + if (ACPI_FAILURE(rc)) { + ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to " + "insert regspec into resource handle."); + } + /* Generate 'ranges' for producer. */ + } else if (addrp->ProducerConsumer == ACPI_PRODUCER && + rhdl->acpidev_consumer == B_FALSE) { + uint64_t paddr; + acpidev_ranges_t range; + + range.child_hi = high; + range.child_mid = addrp->Minimum >> 32; + range.child_low = addrp->Minimum & 0xFFFFFFFF; + /* It's IO on parent side if Translation is true. */ + if (addrp->Info.Mem.Translation) { + range.parent_hi = ACPIDEV_REG_TYPE_IO; + } else { + range.parent_hi = high; + } + paddr = addrp->Minimum + addrp->TranslationOffset; + range.parent_mid = paddr >> 32; + range.parent_low = paddr & 0xFFFFFFFF; + range.size_hi = addrp->AddressLength >> 32; + range.size_low = addrp->AddressLength & 0xFFFFFFFF; + rc = acpidev_resource_insert_range(rhdl, &range); + if (ACPI_FAILURE(rc)) { + ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to " + "insert range into resource handle."); + } + } + break; + + case ACPI_IO_RANGE: + high = ACPIDEV_REG_TYPE_IO; + if (addrp->Decode == ACPI_SUB_DECODE) { + high |= ACPIDEV_REG_SUB_DEC; + } + if (addrp->Info.Io.Translation) { + high |= ACPIDEV_REG_TRANSLATED; + } + if (addrp->Info.Io.RangeType == ACPI_NON_ISA_ONLY_RANGES) { + high |= ACPIDEV_REG_IO_RANGE_NONISA; + } else if (addrp->Info.Io.RangeType == ACPI_ISA_ONLY_RANGES) { + high |= ACPIDEV_REG_IO_RANGE_ISA; + } else if (addrp->Info.Io.RangeType == ACPI_ENTIRE_RANGE) { + high |= ACPIDEV_REG_IO_RANGE_FULL; + } else { + ACPIDEV_DEBUG(CE_WARN, + "acpidev: unknown IO range type %u.", + addrp->Info.Io.RangeType); + rc = AE_ERROR; + break; + } + if (addrp->Info.Io.TranslationType == ACPI_SPARSE_TRANSLATION) { + high |= ACPIDEV_REG_IO_SPARSE; + } + + /* Generate 'reg' for producer. */ + if (addrp->ProducerConsumer == ACPI_CONSUMER && + rhdl->acpidev_consumer == B_TRUE) { + acpidev_regspec_t reg; + + reg.phys_hi = high; + reg.phys_mid = addrp->Minimum >> 32; + reg.phys_low = addrp->Minimum & 0xFFFFFFFF; + reg.size_hi = addrp->AddressLength >> 32; + reg.size_low = addrp->AddressLength & 0xFFFFFFFF; + rc = acpidev_resource_insert_reg(rhdl, ®); + if (ACPI_FAILURE(rc)) { + ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to " + "insert regspec into resource handle."); + } + /* Generate 'ranges' for producer. */ + } else if (addrp->ProducerConsumer == ACPI_PRODUCER && + rhdl->acpidev_consumer == B_FALSE) { + uint64_t paddr; + acpidev_ranges_t range; + + range.child_hi = high; + range.child_mid = addrp->Minimum >> 32; + range.child_low = addrp->Minimum & 0xFFFFFFFF; + /* It's Memory on parent side if Translation is true. */ + if (addrp->Info.Io.Translation) { + range.parent_hi = ACPIDEV_REG_TYPE_MEMORY; + } else { + range.parent_hi = high; + } + paddr = addrp->Minimum + addrp->TranslationOffset; + range.parent_mid = paddr >> 32; + range.parent_low = paddr & 0xFFFFFFFF; + range.size_hi = addrp->AddressLength >> 32; + range.size_low = addrp->AddressLength & 0xFFFFFFFF; + rc = acpidev_resource_insert_range(rhdl, &range); + if (ACPI_FAILURE(rc)) { + ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to " + "insert range into resource handle."); + } + } + break; + + case ACPI_BUS_NUMBER_RANGE: + /* Only support producer of BUS. */ + if (addrp->ProducerConsumer == ACPI_PRODUCER && + rhdl->acpidev_consumer == B_FALSE) { + uint64_t end; + acpidev_bus_range_t bus; + + end = addrp->Minimum + addrp->AddressLength; + if (end < addrp->Minimum || end > UINT_MAX) { + ACPIDEV_DEBUG(CE_WARN, "acpidev: bus range " + "in ADDRESS64 is invalid."); + rc = AE_ERROR; + break; + } + bus.bus_start = addrp->Minimum & 0xFFFFFFFF; + bus.bus_end = end & 0xFFFFFFFF; + ASSERT(bus.bus_start <= bus.bus_end); + rc = acpidev_resource_insert_bus(rhdl, &bus); + if (ACPI_FAILURE(rc)) { + ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to " + "insert bus range into resource handle."); + } + } + break; + + default: + ACPIDEV_DEBUG(CE_WARN, + "acpidev: unknown resource type %u in ADDRESS64.", + addrp->ResourceType); + rc = AE_BAD_PARAMETER; + } + + return (rc); +} + +static ACPI_STATUS +acpidev_resource_walk_producer(ACPI_RESOURCE *rscp, void *ctxp) +{ + ACPI_STATUS rc = AE_OK; + acpidev_resource_handle_t rhdl; + + ASSERT(ctxp != NULL); + rhdl = (acpidev_resource_handle_t)ctxp; + ASSERT(rhdl->acpidev_consumer == B_FALSE); + + switch (rscp->Type) { + case ACPI_RESOURCE_TYPE_DMA: + case ACPI_RESOURCE_TYPE_IRQ: + case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: + case ACPI_RESOURCE_TYPE_FIXED_IO: + case ACPI_RESOURCE_TYPE_MEMORY24: + case ACPI_RESOURCE_TYPE_MEMORY32: + case ACPI_RESOURCE_TYPE_FIXED_MEMORY32: + case ACPI_RESOURCE_TYPE_GENERIC_REGISTER: + case ACPI_RESOURCE_TYPE_VENDOR: + ACPIDEV_DEBUG(CE_NOTE, + "acpidev: unsupported producer resource type %u, ignore.", + rscp->Type); + break; + + case ACPI_RESOURCE_TYPE_IO: + { + acpidev_ranges_t range; + + range.child_hi = ACPIDEV_REG_TYPE_IO; + range.child_hi |= ACPIDEV_REG_IO_RANGE_FULL; + if (rscp->Data.Io.IoDecode == ACPI_DECODE_16) { + range.child_hi |= ACPIDEV_REG_IO_DECODE16; + } + range.parent_hi = range.child_hi; + range.parent_mid = range.child_mid = 0; + range.parent_low = range.child_low = rscp->Data.Io.Minimum; + range.size_hi = 0; + range.size_low = rscp->Data.Io.AddressLength; + if ((uint64_t)range.child_low + range.size_low > UINT16_MAX) { + ACPIDEV_DEBUG(CE_WARN, "acpidev: invalid IO record, " + "IO max is out of range."); + rc = AE_ERROR; + } else if (range.size_low != 0) { + rc = acpidev_resource_insert_range(rhdl, &range); + if (ACPI_FAILURE(rc)) { + ACPIDEV_DEBUG(CE_WARN,"acpidev: failed to " + "insert range into resource handle."); + } + } + break; + } + + case ACPI_RESOURCE_TYPE_ADDRESS16: + case ACPI_RESOURCE_TYPE_ADDRESS32: + case ACPI_RESOURCE_TYPE_ADDRESS64: + { + ACPI_RESOURCE_ADDRESS64 addr64; + + if (rscp->Data.Address.ProducerConsumer != ACPI_PRODUCER) { + ACPIDEV_DEBUG(CE_NOTE, "acpidev: producer encounters " + "resource CONSUMER, ignore."); + } else if (ACPI_FAILURE(AcpiResourceToAddress64(rscp, + &addr64))) { + ACPIDEV_DEBUG(CE_WARN, + "acpidev: failed to convert resource to ADDR64."); + } else if (ACPI_FAILURE(rc = acpidev_resource_address64(rhdl, + &addr64))) { + ACPIDEV_DEBUG(CE_WARN, + "acpidev: failed to handle ADDRESS resource."); + } + break; + } + + case ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64: + { + ACPI_RESOURCE_ADDRESS64 addr64; + + if (rscp->Data.ExtAddress64.ProducerConsumer != ACPI_PRODUCER) { + ACPIDEV_DEBUG(CE_NOTE, "acpidev: producer encounters " + "resource CONSUMER, ignore."); + break; + } + + *(ACPI_RESOURCE_ADDRESS *)&addr64 = rscp->Data.Address; + addr64.Granularity = rscp->Data.ExtAddress64.Granularity; + addr64.Minimum = rscp->Data.ExtAddress64.Minimum; + addr64.Maximum = rscp->Data.ExtAddress64.Maximum; + addr64.TranslationOffset = + rscp->Data.ExtAddress64.TranslationOffset; + addr64.AddressLength = rscp->Data.ExtAddress64.AddressLength; + if (ACPI_FAILURE(rc = acpidev_resource_address64(rhdl, + &addr64))) { + ACPIDEV_DEBUG(CE_WARN, + "acpidev: failed to handle EXTADDRESS resource."); + } + break; + } + + case ACPI_RESOURCE_TYPE_START_DEPENDENT: + case ACPI_RESOURCE_TYPE_END_DEPENDENT: + ACPIDEV_DEBUG(CE_NOTE, "acpidev: producer encounters " + "START_DEPENDENT or END_DEPENDENT tag, ignore."); + break; + + case ACPI_RESOURCE_TYPE_END_TAG: + /* Finish walking when encounter END_TAG. */ + rc = AE_CTRL_TERMINATE; + break; + + default: + ACPIDEV_DEBUG(CE_NOTE, + "acpidev: unknown ACPI resource type %u, ignore.", + rscp->Type); + break; + } + + return (rc); +} + +static ACPI_STATUS +acpidev_resource_walk_consumer(ACPI_RESOURCE *rscp, void *ctxp) +{ + ACPI_STATUS rc = AE_OK; + acpidev_resource_handle_t rhdl; + + ASSERT(ctxp != NULL); + rhdl = (acpidev_resource_handle_t)ctxp; + ASSERT(rhdl->acpidev_consumer == B_TRUE); + + switch (rscp->Type) { + case ACPI_RESOURCE_TYPE_MEMORY24: + case ACPI_RESOURCE_TYPE_GENERIC_REGISTER: + case ACPI_RESOURCE_TYPE_VENDOR: + ACPIDEV_DEBUG(CE_NOTE, + "acpidev: unsupported consumer resource type %u, ignore.", + rscp->Type); + break; + + case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: + { + int i; + + if (rscp->Data.ExtendedIrq.ProducerConsumer != ACPI_CONSUMER) { + ACPIDEV_DEBUG(CE_NOTE, "acpidev: consumer encounters " + "resource PRODUCER, ignore."); + break; + } + for (i = 0; i < rscp->Data.ExtendedIrq.InterruptCount; i++) { + if (ACPI_SUCCESS(acpidev_resource_insert_irq(rhdl, + rscp->Data.ExtendedIrq.Interrupts[i]))) { + continue; + } + ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to insert" + "IRQ into resource handle."); + rc = AE_ERROR; + break; + } + break; + } + + case ACPI_RESOURCE_TYPE_IRQ: + { + int i; + + for (i = 0; i < rscp->Data.Irq.InterruptCount; i++) { + if (ACPI_SUCCESS(acpidev_resource_insert_irq(rhdl, + rscp->Data.Irq.Interrupts[i]))) { + continue; + } + ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to insert" + "IRQ into resource handle."); + rc = AE_ERROR; + break; + } + break; + } + + case ACPI_RESOURCE_TYPE_DMA: + { + int i; + + for (i = 0; i < rscp->Data.Dma.ChannelCount; i++) { + if (ACPI_SUCCESS(acpidev_resource_insert_dma(rhdl, + rscp->Data.Dma.Channels[i]))) { + continue; + } + ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to insert" + "dma into resource handle."); + rc = AE_ERROR; + break; + } + break; + } + + case ACPI_RESOURCE_TYPE_IO: + case ACPI_RESOURCE_TYPE_FIXED_IO: + { + acpidev_regspec_t reg; + + reg.phys_hi = ACPIDEV_REG_TYPE_IO; + reg.phys_hi |= ACPIDEV_REG_IO_RANGE_FULL; + if (rscp->Type == ACPI_RESOURCE_TYPE_IO) { + if (rscp->Data.Io.IoDecode == ACPI_DECODE_16) { + reg.phys_hi |= ACPIDEV_REG_IO_DECODE16; + } + reg.phys_low = rscp->Data.Io.Minimum; + reg.size_low = rscp->Data.Io.AddressLength; + } else { + reg.phys_hi |= ACPIDEV_REG_IO_DECODE16; + reg.phys_low = rscp->Data.FixedIo.Address; + reg.size_low = rscp->Data.FixedIo.AddressLength; + } + reg.phys_mid = 0; + reg.size_hi = 0; + if ((uint64_t)reg.phys_low + reg.size_low > UINT16_MAX) { + ACPIDEV_DEBUG(CE_WARN, "acpidev: invalid IO/FIXEDIO " + "record, IO max is out of range."); + rc = AE_ERROR; + } else if (reg.size_low != 0) { + rc = acpidev_resource_insert_reg(rhdl, ®); + if (ACPI_FAILURE(rc)) { + ACPIDEV_DEBUG(CE_WARN,"acpidev: failed to " + "insert reg into resource handle."); + } + } + break; + } + + case ACPI_RESOURCE_TYPE_MEMORY32: + case ACPI_RESOURCE_TYPE_FIXED_MEMORY32: + { + acpidev_regspec_t reg; + + reg.phys_hi = ACPIDEV_REG_TYPE_MEMORY; + reg.phys_hi |= ACPIDEV_REG_MEM_COHERENT_CA; + if (rscp->Type == ACPI_RESOURCE_TYPE_MEMORY32) { + if (rscp->Data.Memory32.WriteProtect == + ACPI_READ_WRITE_MEMORY) { + reg.phys_hi |= ACPIDEV_REG_MEM_WRITABLE; + } + reg.phys_low = rscp->Data.Memory32.Minimum; + reg.size_low = rscp->Data.Memory32.AddressLength; + } else { + if (rscp->Data.FixedMemory32.WriteProtect == + ACPI_READ_WRITE_MEMORY) { + reg.phys_hi |= ACPIDEV_REG_MEM_WRITABLE; + } + reg.phys_low = rscp->Data.FixedMemory32.Address; + reg.size_low = rscp->Data.FixedMemory32.AddressLength; + } + reg.phys_mid = 0; + reg.size_hi = 0; + if ((uint64_t)reg.phys_low + reg.size_low > UINT32_MAX) { + ACPIDEV_DEBUG(CE_WARN, + "acpidev: invalid MEMORY32/FIXEDMEMORY32 record, " + "memroy max is out of range."); + rc = AE_ERROR; + } else if (reg.size_low != 0) { + rc = acpidev_resource_insert_reg(rhdl, ®); + if (ACPI_FAILURE(rc)) { + ACPIDEV_DEBUG(CE_WARN,"acpidev: failed to " + "insert reg into resource handle."); + } + } + break; + } + + case ACPI_RESOURCE_TYPE_ADDRESS16: + case ACPI_RESOURCE_TYPE_ADDRESS32: + case ACPI_RESOURCE_TYPE_ADDRESS64: + { + ACPI_RESOURCE_ADDRESS64 addr64; + + if (rscp->Data.Address.ProducerConsumer != ACPI_CONSUMER) { + ACPIDEV_DEBUG(CE_NOTE, "acpidev: consumer encounters " + "resource PRODUCER, ignore."); + } else if (ACPI_FAILURE(AcpiResourceToAddress64(rscp, + &addr64))) { + ACPIDEV_DEBUG(CE_WARN, + "acpidev: failed to convert resource to ADDR64."); + } else if (ACPI_FAILURE(rc = acpidev_resource_address64(rhdl, + &addr64))) { + ACPIDEV_DEBUG(CE_WARN, + "acpidev: failed to handle ADDRESS resource."); + } + break; + } + + case ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64: + { + ACPI_RESOURCE_ADDRESS64 addr64; + + if (rscp->Data.ExtAddress64.ProducerConsumer != ACPI_CONSUMER) { + ACPIDEV_DEBUG(CE_NOTE, "acpidev: consumer encounters " + "resource PRODUCER, ignore."); + break; + } + + *(ACPI_RESOURCE_ADDRESS *)&addr64 = rscp->Data.Address; + addr64.Granularity = rscp->Data.ExtAddress64.Granularity; + addr64.Minimum = rscp->Data.ExtAddress64.Minimum; + addr64.Maximum = rscp->Data.ExtAddress64.Maximum; + addr64.TranslationOffset = + rscp->Data.ExtAddress64.TranslationOffset; + addr64.AddressLength = rscp->Data.ExtAddress64.AddressLength; + if (ACPI_FAILURE(rc = acpidev_resource_address64(rhdl, + &addr64))) { + ACPIDEV_DEBUG(CE_WARN, + "acpidev: failed to handle EXTADDRESS resource."); + } + break; + } + + case ACPI_RESOURCE_TYPE_START_DEPENDENT: + case ACPI_RESOURCE_TYPE_END_DEPENDENT: + ACPIDEV_DEBUG(CE_NOTE, "acpidev: consumer encounters " + "START_DEPENDENT or END_DEPENDENT tag, ignore."); + break; + + case ACPI_RESOURCE_TYPE_END_TAG: + /* Finish walking when encounter END_TAG. */ + rc = AE_CTRL_TERMINATE; + break; + + default: + ACPIDEV_DEBUG(CE_NOTE, + "acpidev: unknown ACPI resource type %u, ignore.", + rscp->Type); + break; + } + + return (rc); +} + +ACPI_STATUS +acpidev_resource_walk(ACPI_HANDLE hdl, char *method, + boolean_t consumer, acpidev_resource_handle_t *rhdlp) +{ + ACPI_STATUS rc = AE_OK; + ACPI_HANDLE mhdl = NULL; + acpidev_resource_handle_t rhdl = NULL; + + ASSERT(hdl != NULL); + ASSERT(method != NULL); + ASSERT(rhdlp != NULL); + if (hdl == NULL) { + ACPIDEV_DEBUG(CE_WARN, + "acpidev: hdl is NULL in walk_resource()."); + return (AE_BAD_PARAMETER); + } else if (method == NULL) { + ACPIDEV_DEBUG(CE_WARN, + "acpidev: method is NULL in walk_resource()."); + return (AE_BAD_PARAMETER); + } else if (rhdlp == NULL) { + ACPIDEV_DEBUG(CE_WARN, + "acpidev: resource handle ptr is NULL in walk_resource()."); + return (AE_BAD_PARAMETER); + } + + /* Check whether method exists under object. */ + if (ACPI_FAILURE(AcpiGetHandle(hdl, method, &mhdl))) { + char *objname = acpidev_get_object_name(hdl); + ACPIDEV_DEBUG(CE_NOTE, + "acpidev: method %s doesn't exist under %s", + method, objname); + acpidev_free_object_name(hdl); + return (AE_NOT_FOUND); + } + + /* Walk all resources. */ + rhdl = acpidev_resource_handle_alloc(consumer); + if (consumer) { + rc = AcpiWalkResources(hdl, method, + acpidev_resource_walk_consumer, rhdl); + } else { + rc = AcpiWalkResources(hdl, method, + acpidev_resource_walk_producer, rhdl); + } + if (ACPI_SUCCESS(rc)) { + *rhdlp = rhdl; + } else { + acpidev_resource_handle_free(rhdl); + } + if (ACPI_FAILURE(rc)) { + char *objname = acpidev_get_object_name(hdl); + ACPIDEV_DEBUG(CE_WARN, + "acpidev: failed to walk resource from method %s under %s.", + method, objname); + acpidev_free_object_name(hdl); + } + + return (rc); +} + +ACPI_STATUS +acpidev_resource_process(acpidev_walk_info_t *infop, boolean_t consumer) +{ + ACPI_STATUS rc; + char path[MAXPATHLEN]; + acpidev_resource_handle_t rhdl = NULL; + + ASSERT(infop != NULL); + if (infop == NULL) { + ACPIDEV_DEBUG(CE_WARN, + "acpidev: invalid parameter to resource_process()."); + return (AE_BAD_PARAMETER); + } + + /* Walk all resources. */ + (void) ddi_pathname(infop->awi_dip, path); + rc = acpidev_resource_walk(infop->awi_hdl, METHOD_NAME__CRS, + consumer, &rhdl); + if (ACPI_FAILURE(rc)) { + ACPIDEV_DEBUG(CE_WARN, + "acpidev: failed to walk ACPI resources of %s(%s).", + path, infop->awi_name); + return (rc); + } + + /* Create device properties for consumer. */ + if (consumer) { + /* Create 'reg' and 'assigned-addresses' properties. */ + if (rhdl->acpidev_reg_count > 0 && + ndi_prop_update_int_array( DDI_DEV_T_NONE, infop->awi_dip, + "reg", (int *)rhdl->acpidev_regp, + rhdl->acpidev_reg_count * sizeof (acpidev_regspec_t) / + sizeof (int)) != NDI_SUCCESS) { + ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to set " + "'reg' property for %s.", path); + rc = AE_ERROR; + goto out; + } + if (rhdl->acpidev_reg_count > 0 && + ndi_prop_update_int_array( DDI_DEV_T_NONE, infop->awi_dip, + "assigned-addresses", (int *)rhdl->acpidev_regp, + rhdl->acpidev_reg_count * sizeof (acpidev_regspec_t) / + sizeof (int)) != NDI_SUCCESS) { + ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to set " + "'assigned-addresses' property for %s.", path); + rc = AE_ERROR; + goto out; + } + + /* Create 'interrutps' property. */ + if (rhdl->acpidev_irq_count > 0 && + ndi_prop_update_int_array(DDI_DEV_T_NONE, infop->awi_dip, + "interrupts", (int *)rhdl->acpidev_irqp, + rhdl->acpidev_irq_count) != NDI_SUCCESS) { + ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to set " + "'interrupts' property for %s.", path); + rc = AE_ERROR; + goto out; + } + + /* Create 'dma-channels' property. */ + if (rhdl->acpidev_dma_count > 0 && + ndi_prop_update_int_array(DDI_DEV_T_NONE, infop->awi_dip, + "dma-channels", (int *)rhdl->acpidev_dmap, + rhdl->acpidev_dma_count) != NDI_SUCCESS) { + ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to set " + "'dma-channels' property for %s.", path); + rc = AE_ERROR; + goto out; + } + + /* Create device properties for producer. */ + } else { + /* Create 'ranges' property. */ + if (rhdl->acpidev_range_count > 0 && + ndi_prop_update_int_array(DDI_DEV_T_NONE, infop->awi_dip, + "ranges", (int *)rhdl->acpidev_rangep, + rhdl->acpidev_range_count * sizeof (acpidev_ranges_t) / + sizeof (int)) != NDI_SUCCESS) { + ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to set " + "'ranges' property for %s.", path); + rc = AE_ERROR; + goto out; + } + + /* Create 'bus-ranges' property. */ + if (rhdl->acpidev_bus_count > 0 && + ndi_prop_update_int_array(DDI_DEV_T_NONE, infop->awi_dip, + "bus-range", (int *)rhdl->acpidev_busp, + rhdl->acpidev_bus_count * sizeof (acpidev_bus_range_t) / + sizeof (int)) != NDI_SUCCESS) { + ACPIDEV_DEBUG(CE_WARN, "acpidev: failed to set " + "'bus-range' property for %s.", path); + rc = AE_ERROR; + goto out; + } + } + +out: + /* Free resources allocated by acpidev_resource_walk. */ + acpidev_resource_handle_free(rhdl); + + return (rc); +}