/******************************************************************************* * * Module Name: rsmisc - Miscellaneous resource descriptors * ******************************************************************************/ /* * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * substantially similar to the "NO WARRANTY" disclaimer below * ("Disclaimer") and any redistribution must be conditioned upon * including a substantially similar Disclaimer requirement for further * binary redistribution. * 3. Neither the names of the above-listed copyright holders nor the names * of any contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * Alternatively, this software may be distributed under the terms of the * GNU General Public License ("GPL") version 2 as published by the Free * Software Foundation. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGES. */ #include <acpi/acpi.h> #include "accommon.h" #include "acresrc.h" #define _COMPONENT ACPI_RESOURCES ACPI_MODULE_NAME("rsmisc") #define INIT_RESOURCE_TYPE(i) i->resource_offset #define INIT_RESOURCE_LENGTH(i) i->aml_offset #define INIT_TABLE_LENGTH(i) i->value #define COMPARE_OPCODE(i) i->resource_offset #define COMPARE_TARGET(i) i->aml_offset #define COMPARE_VALUE(i) i->value /******************************************************************************* * * FUNCTION: acpi_rs_convert_aml_to_resource * * PARAMETERS: resource - Pointer to the resource descriptor * aml - Where the AML descriptor is returned * info - Pointer to appropriate conversion table * * RETURN: Status * * DESCRIPTION: Convert an external AML resource descriptor to the corresponding * internal resource descriptor * ******************************************************************************/ acpi_status acpi_rs_convert_aml_to_resource(struct acpi_resource *resource, union aml_resource *aml, struct acpi_rsconvert_info *info) { acpi_rs_length aml_resource_length; void *source; void *destination; char *target; u8 count; u8 flags_mode = FALSE; u16 item_count = 0; u16 temp16 = 0; ACPI_FUNCTION_TRACE(rs_convert_aml_to_resource); if (!info) { return_ACPI_STATUS(AE_BAD_PARAMETER); } if (((acpi_size) resource) & 0x3) { /* Each internal resource struct is expected to be 32-bit aligned */ ACPI_WARNING((AE_INFO, "Misaligned resource pointer (get): %p Type 0x%2.2X Length %u", resource, resource->type, resource->length)); } /* Extract the resource Length field (does not include header length) */ aml_resource_length = acpi_ut_get_resource_length(aml); /* * First table entry must be ACPI_RSC_INITxxx and must contain the * table length (# of table entries) */ count = INIT_TABLE_LENGTH(info); while (count) { /* * Source is the external AML byte stream buffer, * destination is the internal resource descriptor */ source = ACPI_ADD_PTR(void, aml, info->aml_offset); destination = ACPI_ADD_PTR(void, resource, info->resource_offset); switch (info->opcode) { case ACPI_RSC_INITGET: /* * Get the resource type and the initial (minimum) length */ memset(resource, 0, INIT_RESOURCE_LENGTH(info)); resource->type = INIT_RESOURCE_TYPE(info); resource->length = INIT_RESOURCE_LENGTH(info); break; case ACPI_RSC_INITSET: break; case ACPI_RSC_FLAGINIT: flags_mode = TRUE; break; case ACPI_RSC_1BITFLAG: /* * Mask and shift the flag bit */ ACPI_SET8(destination, ((ACPI_GET8(source) >> info->value) & 0x01)); break; case ACPI_RSC_2BITFLAG: /* * Mask and shift the flag bits */ ACPI_SET8(destination, ((ACPI_GET8(source) >> info->value) & 0x03)); break; case ACPI_RSC_3BITFLAG: /* * Mask and shift the flag bits */ ACPI_SET8(destination, ((ACPI_GET8(source) >> info->value) & 0x07)); break; case ACPI_RSC_COUNT: item_count = ACPI_GET8(source); ACPI_SET8(destination, item_count); resource->length = resource->length + (info->value * (item_count - 1)); break; case ACPI_RSC_COUNT16: item_count = aml_resource_length; ACPI_SET16(destination, item_count); resource->length = resource->length + (info->value * (item_count - 1)); break; case ACPI_RSC_COUNT_GPIO_PIN: target = ACPI_ADD_PTR(void, aml, info->value); item_count = ACPI_GET16(target) - ACPI_GET16(source); resource->length = resource->length + item_count; item_count = item_count / 2; ACPI_SET16(destination, item_count); break; case ACPI_RSC_COUNT_GPIO_VEN: item_count = ACPI_GET8(source); ACPI_SET8(destination, item_count); resource->length = resource->length + (info->value * item_count); break; case ACPI_RSC_COUNT_GPIO_RES: /* * Vendor data is optional (length/offset may both be zero) * Examine vendor data length field first */ target = ACPI_ADD_PTR(void, aml, (info->value + 2)); if (ACPI_GET16(target)) { /* Use vendor offset to get resource source length */ target = ACPI_ADD_PTR(void, aml, info->value); item_count = ACPI_GET16(target) - ACPI_GET16(source); } else { /* No vendor data to worry about */ item_count = aml->large_header.resource_length + sizeof(struct aml_resource_large_header) - ACPI_GET16(source); } resource->length = resource->length + item_count; ACPI_SET16(destination, item_count); break; case ACPI_RSC_COUNT_SERIAL_VEN: item_count = ACPI_GET16(source) - info->value; resource->length = resource->length + item_count; ACPI_SET16(destination, item_count); break; case ACPI_RSC_COUNT_SERIAL_RES: item_count = (aml_resource_length + sizeof(struct aml_resource_large_header)) - ACPI_GET16(source) - info->value; resource->length = resource->length + item_count; ACPI_SET16(destination, item_count); break; case ACPI_RSC_LENGTH: resource->length = resource->length + info->value; break; case ACPI_RSC_MOVE8: case ACPI_RSC_MOVE16: case ACPI_RSC_MOVE32: case ACPI_RSC_MOVE64: /* * Raw data move. Use the Info value field unless item_count has * been previously initialized via a COUNT opcode */ if (info->value) { item_count = info->value; } acpi_rs_move_data(destination, source, item_count, info->opcode); break; case ACPI_RSC_MOVE_GPIO_PIN: /* Generate and set the PIN data pointer */ target = (char *)ACPI_ADD_PTR(void, resource, (resource->length - item_count * 2)); *(u16 **)destination = ACPI_CAST_PTR(u16, target); /* Copy the PIN data */ source = ACPI_ADD_PTR(void, aml, ACPI_GET16(source)); acpi_rs_move_data(target, source, item_count, info->opcode); break; case ACPI_RSC_MOVE_GPIO_RES: /* Generate and set the resource_source string pointer */ target = (char *)ACPI_ADD_PTR(void, resource, (resource->length - item_count)); *(u8 **)destination = ACPI_CAST_PTR(u8, target); /* Copy the resource_source string */ source = ACPI_ADD_PTR(void, aml, ACPI_GET16(source)); acpi_rs_move_data(target, source, item_count, info->opcode); break; case ACPI_RSC_MOVE_SERIAL_VEN: /* Generate and set the Vendor Data pointer */ target = (char *)ACPI_ADD_PTR(void, resource, (resource->length - item_count)); *(u8 **)destination = ACPI_CAST_PTR(u8, target); /* Copy the Vendor Data */ source = ACPI_ADD_PTR(void, aml, info->value); acpi_rs_move_data(target, source, item_count, info->opcode); break; case ACPI_RSC_MOVE_SERIAL_RES: /* Generate and set the resource_source string pointer */ target = (char *)ACPI_ADD_PTR(void, resource, (resource->length - item_count)); *(u8 **)destination = ACPI_CAST_PTR(u8, target); /* Copy the resource_source string */ source = ACPI_ADD_PTR(void, aml, (ACPI_GET16(source) + info->value)); acpi_rs_move_data(target, source, item_count, info->opcode); break; case ACPI_RSC_SET8: memset(destination, info->aml_offset, info->value); break; case ACPI_RSC_DATA8: target = ACPI_ADD_PTR(char, resource, info->value); memcpy(destination, source, ACPI_GET16(target)); break; case ACPI_RSC_ADDRESS: /* * Common handler for address descriptor flags */ if (!acpi_rs_get_address_common(resource, aml)) { return_ACPI_STATUS (AE_AML_INVALID_RESOURCE_TYPE); } break; case ACPI_RSC_SOURCE: /* * Optional resource_source (Index and String) */ resource->length += acpi_rs_get_resource_source(aml_resource_length, info->value, destination, aml, NULL); break; case ACPI_RSC_SOURCEX: /* * Optional resource_source (Index and String). This is the more * complicated case used by the Interrupt() macro */ target = ACPI_ADD_PTR(char, resource, info->aml_offset + (item_count * 4)); resource->length += acpi_rs_get_resource_source(aml_resource_length, (acpi_rs_length) (((item_count - 1) * sizeof(u32)) + info->value), destination, aml, target); break; case ACPI_RSC_BITMASK: /* * 8-bit encoded bitmask (DMA macro) */ item_count = acpi_rs_decode_bitmask(ACPI_GET8(source), destination); if (item_count) { resource->length += (item_count - 1); } target = ACPI_ADD_PTR(char, resource, info->value); ACPI_SET8(target, item_count); break; case ACPI_RSC_BITMASK16: /* * 16-bit encoded bitmask (IRQ macro) */ ACPI_MOVE_16_TO_16(&temp16, source); item_count = acpi_rs_decode_bitmask(temp16, destination); if (item_count) { resource->length += (item_count - 1); } target = ACPI_ADD_PTR(char, resource, info->value); ACPI_SET8(target, item_count); break; case ACPI_RSC_EXIT_NE: /* * control - Exit conversion if not equal */ switch (info->resource_offset) { case ACPI_RSC_COMPARE_AML_LENGTH: if (aml_resource_length != info->value) { goto exit; } break; case ACPI_RSC_COMPARE_VALUE: if (ACPI_GET8(source) != info->value) { goto exit; } break; default: ACPI_ERROR((AE_INFO, "Invalid conversion sub-opcode")); return_ACPI_STATUS(AE_BAD_PARAMETER); } break; default: ACPI_ERROR((AE_INFO, "Invalid conversion opcode")); return_ACPI_STATUS(AE_BAD_PARAMETER); } count--; info++; } exit: if (!flags_mode) { /* Round the resource struct length up to the next boundary (32 or 64) */ resource->length = (u32) ACPI_ROUND_UP_TO_NATIVE_WORD(resource->length); } return_ACPI_STATUS(AE_OK); } /******************************************************************************* * * FUNCTION: acpi_rs_convert_resource_to_aml * * PARAMETERS: resource - Pointer to the resource descriptor * aml - Where the AML descriptor is returned * info - Pointer to appropriate conversion table * * RETURN: Status * * DESCRIPTION: Convert an internal resource descriptor to the corresponding * external AML resource descriptor. * ******************************************************************************/ acpi_status acpi_rs_convert_resource_to_aml(struct acpi_resource *resource, union aml_resource *aml, struct acpi_rsconvert_info *info) { void *source = NULL; void *destination; char *target; acpi_rsdesc_size aml_length = 0; u8 count; u16 temp16 = 0; u16 item_count = 0; ACPI_FUNCTION_TRACE(rs_convert_resource_to_aml); if (!info) { return_ACPI_STATUS(AE_BAD_PARAMETER); } /* * First table entry must be ACPI_RSC_INITxxx and must contain the * table length (# of table entries) */ count = INIT_TABLE_LENGTH(info); while (count) { /* * Source is the internal resource descriptor, * destination is the external AML byte stream buffer */ source = ACPI_ADD_PTR(void, resource, info->resource_offset); destination = ACPI_ADD_PTR(void, aml, info->aml_offset); switch (info->opcode) { case ACPI_RSC_INITSET: memset(aml, 0, INIT_RESOURCE_LENGTH(info)); aml_length = INIT_RESOURCE_LENGTH(info); acpi_rs_set_resource_header(INIT_RESOURCE_TYPE(info), aml_length, aml); break; case ACPI_RSC_INITGET: break; case ACPI_RSC_FLAGINIT: /* * Clear the flag byte */ ACPI_SET8(destination, 0); break; case ACPI_RSC_1BITFLAG: /* * Mask and shift the flag bit */ ACPI_SET_BIT(*ACPI_CAST8(destination), (u8) ((ACPI_GET8(source) & 0x01) << info-> value)); break; case ACPI_RSC_2BITFLAG: /* * Mask and shift the flag bits */ ACPI_SET_BIT(*ACPI_CAST8(destination), (u8) ((ACPI_GET8(source) & 0x03) << info-> value)); break; case ACPI_RSC_3BITFLAG: /* * Mask and shift the flag bits */ ACPI_SET_BIT(*ACPI_CAST8(destination), (u8) ((ACPI_GET8(source) & 0x07) << info-> value)); break; case ACPI_RSC_COUNT: item_count = ACPI_GET8(source); ACPI_SET8(destination, item_count); aml_length = (u16) (aml_length + (info->value * (item_count - 1))); break; case ACPI_RSC_COUNT16: item_count = ACPI_GET16(source); aml_length = (u16) (aml_length + item_count); acpi_rs_set_resource_length(aml_length, aml); break; case ACPI_RSC_COUNT_GPIO_PIN: item_count = ACPI_GET16(source); ACPI_SET16(destination, aml_length); aml_length = (u16)(aml_length + item_count * 2); target = ACPI_ADD_PTR(void, aml, info->value); ACPI_SET16(target, aml_length); acpi_rs_set_resource_length(aml_length, aml); break; case ACPI_RSC_COUNT_GPIO_VEN: item_count = ACPI_GET16(source); ACPI_SET16(destination, item_count); aml_length = (u16)(aml_length + (info->value * item_count)); acpi_rs_set_resource_length(aml_length, aml); break; case ACPI_RSC_COUNT_GPIO_RES: /* Set resource source string length */ item_count = ACPI_GET16(source); ACPI_SET16(destination, aml_length); /* Compute offset for the Vendor Data */ aml_length = (u16)(aml_length + item_count); target = ACPI_ADD_PTR(void, aml, info->value); /* Set vendor offset only if there is vendor data */ if (resource->data.gpio.vendor_length) { ACPI_SET16(target, aml_length); } acpi_rs_set_resource_length(aml_length, aml); break; case ACPI_RSC_COUNT_SERIAL_VEN: item_count = ACPI_GET16(source); ACPI_SET16(destination, item_count + info->value); aml_length = (u16)(aml_length + item_count); acpi_rs_set_resource_length(aml_length, aml); break; case ACPI_RSC_COUNT_SERIAL_RES: item_count = ACPI_GET16(source); aml_length = (u16)(aml_length + item_count); acpi_rs_set_resource_length(aml_length, aml); break; case ACPI_RSC_LENGTH: acpi_rs_set_resource_length(info->value, aml); break; case ACPI_RSC_MOVE8: case ACPI_RSC_MOVE16: case ACPI_RSC_MOVE32: case ACPI_RSC_MOVE64: if (info->value) { item_count = info->value; } acpi_rs_move_data(destination, source, item_count, info->opcode); break; case ACPI_RSC_MOVE_GPIO_PIN: destination = (char *)ACPI_ADD_PTR(void, aml, ACPI_GET16 (destination)); source = *(u16 **)source; acpi_rs_move_data(destination, source, item_count, info->opcode); break; case ACPI_RSC_MOVE_GPIO_RES: /* Used for both resource_source string and vendor_data */ destination = (char *)ACPI_ADD_PTR(void, aml, ACPI_GET16 (destination)); source = *(u8 **)source; acpi_rs_move_data(destination, source, item_count, info->opcode); break; case ACPI_RSC_MOVE_SERIAL_VEN: destination = (char *)ACPI_ADD_PTR(void, aml, (aml_length - item_count)); source = *(u8 **)source; acpi_rs_move_data(destination, source, item_count, info->opcode); break; case ACPI_RSC_MOVE_SERIAL_RES: destination = (char *)ACPI_ADD_PTR(void, aml, (aml_length - item_count)); source = *(u8 **)source; acpi_rs_move_data(destination, source, item_count, info->opcode); break; case ACPI_RSC_ADDRESS: /* Set the Resource Type, General Flags, and Type-Specific Flags */ acpi_rs_set_address_common(aml, resource); break; case ACPI_RSC_SOURCEX: /* * Optional resource_source (Index and String) */ aml_length = acpi_rs_set_resource_source(aml, (acpi_rs_length) aml_length, source); acpi_rs_set_resource_length(aml_length, aml); break; case ACPI_RSC_SOURCE: /* * Optional resource_source (Index and String). This is the more * complicated case used by the Interrupt() macro */ aml_length = acpi_rs_set_resource_source(aml, info->value, source); acpi_rs_set_resource_length(aml_length, aml); break; case ACPI_RSC_BITMASK: /* * 8-bit encoded bitmask (DMA macro) */ ACPI_SET8(destination, acpi_rs_encode_bitmask(source, *ACPI_ADD_PTR(u8, resource, info-> value))); break; case ACPI_RSC_BITMASK16: /* * 16-bit encoded bitmask (IRQ macro) */ temp16 = acpi_rs_encode_bitmask(source, *ACPI_ADD_PTR(u8, resource, info-> value)); ACPI_MOVE_16_TO_16(destination, &temp16); break; case ACPI_RSC_EXIT_LE: /* * control - Exit conversion if less than or equal */ if (item_count <= info->value) { goto exit; } break; case ACPI_RSC_EXIT_NE: /* * control - Exit conversion if not equal */ switch (COMPARE_OPCODE(info)) { case ACPI_RSC_COMPARE_VALUE: if (*ACPI_ADD_PTR(u8, resource, COMPARE_TARGET(info)) != COMPARE_VALUE(info)) { goto exit; } break; default: ACPI_ERROR((AE_INFO, "Invalid conversion sub-opcode")); return_ACPI_STATUS(AE_BAD_PARAMETER); } break; case ACPI_RSC_EXIT_EQ: /* * control - Exit conversion if equal */ if (*ACPI_ADD_PTR(u8, resource, COMPARE_TARGET(info)) == COMPARE_VALUE(info)) { goto exit; } break; default: ACPI_ERROR((AE_INFO, "Invalid conversion opcode")); return_ACPI_STATUS(AE_BAD_PARAMETER); } count--; info++; } exit: return_ACPI_STATUS(AE_OK); } #if 0 /* Previous resource validations */ if (aml->ext_address64.revision_ID != AML_RESOURCE_EXTENDED_ADDRESS_REVISION) { return_ACPI_STATUS(AE_SUPPORT); } if (resource->data.start_dpf.performance_robustness >= 3) { return_ACPI_STATUS(AE_AML_BAD_RESOURCE_VALUE); } if (((aml->irq.flags & 0x09) == 0x00) || ((aml->irq.flags & 0x09) == 0x09)) { /* * Only [active_high, edge_sensitive] or [active_low, level_sensitive] * polarity/trigger interrupts are allowed (ACPI spec, section * "IRQ Format"), so 0x00 and 0x09 are illegal. */ ACPI_ERROR((AE_INFO, "Invalid interrupt polarity/trigger in resource list, 0x%X", aml->irq.flags)); return_ACPI_STATUS(AE_BAD_DATA); } resource->data.extended_irq.interrupt_count = temp8; if (temp8 < 1) { /* Must have at least one IRQ */ return_ACPI_STATUS(AE_AML_BAD_RESOURCE_LENGTH); } if (resource->data.dma.transfer == 0x03) { ACPI_ERROR((AE_INFO, "Invalid DMA.Transfer preference (3)")); return_ACPI_STATUS(AE_BAD_DATA); } #endif