diff options
Diffstat (limited to 'drivers/acpi/acpica/nsprepkg.c')
| -rw-r--r-- | drivers/acpi/acpica/nsprepkg.c | 624 | 
1 files changed, 624 insertions, 0 deletions
diff --git a/drivers/acpi/acpica/nsprepkg.c b/drivers/acpi/acpica/nsprepkg.c new file mode 100644 index 00000000000..68f725839eb --- /dev/null +++ b/drivers/acpi/acpica/nsprepkg.c @@ -0,0 +1,624 @@ +/****************************************************************************** + * + * Module Name: nsprepkg - Validation of package objects for predefined names + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2014, 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 "acnamesp.h" +#include "acpredef.h" + +#define _COMPONENT          ACPI_NAMESPACE +ACPI_MODULE_NAME("nsprepkg") + +/* Local prototypes */ +static acpi_status +acpi_ns_check_package_list(struct acpi_evaluate_info *info, +			   const union acpi_predefined_info *package, +			   union acpi_operand_object **elements, u32 count); + +static acpi_status +acpi_ns_check_package_elements(struct acpi_evaluate_info *info, +			       union acpi_operand_object **elements, +			       u8 type1, +			       u32 count1, +			       u8 type2, u32 count2, u32 start_index); + +/******************************************************************************* + * + * FUNCTION:    acpi_ns_check_package + * + * PARAMETERS:  info                - Method execution information block + *              return_object_ptr   - Pointer to the object returned from the + *                                    evaluation of a method or object + * + * RETURN:      Status + * + * DESCRIPTION: Check a returned package object for the correct count and + *              correct type of all sub-objects. + * + ******************************************************************************/ + +acpi_status +acpi_ns_check_package(struct acpi_evaluate_info *info, +		      union acpi_operand_object **return_object_ptr) +{ +	union acpi_operand_object *return_object = *return_object_ptr; +	const union acpi_predefined_info *package; +	union acpi_operand_object **elements; +	acpi_status status = AE_OK; +	u32 expected_count; +	u32 count; +	u32 i; + +	ACPI_FUNCTION_NAME(ns_check_package); + +	/* The package info for this name is in the next table entry */ + +	package = info->predefined + 1; + +	ACPI_DEBUG_PRINT((ACPI_DB_NAMES, +			  "%s Validating return Package of Type %X, Count %X\n", +			  info->full_pathname, package->ret_info.type, +			  return_object->package.count)); + +	/* +	 * For variable-length Packages, we can safely remove all embedded +	 * and trailing NULL package elements +	 */ +	acpi_ns_remove_null_elements(info, package->ret_info.type, +				     return_object); + +	/* Extract package count and elements array */ + +	elements = return_object->package.elements; +	count = return_object->package.count; + +	/* +	 * Most packages must have at least one element. The only exception +	 * is the variable-length package (ACPI_PTYPE1_VAR). +	 */ +	if (!count) { +		if (package->ret_info.type == ACPI_PTYPE1_VAR) { +			return (AE_OK); +		} + +		ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname, +				      info->node_flags, +				      "Return Package has no elements (empty)")); + +		return (AE_AML_OPERAND_VALUE); +	} + +	/* +	 * Decode the type of the expected package contents +	 * +	 * PTYPE1 packages contain no subpackages +	 * PTYPE2 packages contain subpackages +	 */ +	switch (package->ret_info.type) { +	case ACPI_PTYPE1_FIXED: +		/* +		 * The package count is fixed and there are no subpackages +		 * +		 * If package is too small, exit. +		 * If package is larger than expected, issue warning but continue +		 */ +		expected_count = +		    package->ret_info.count1 + package->ret_info.count2; +		if (count < expected_count) { +			goto package_too_small; +		} else if (count > expected_count) { +			ACPI_DEBUG_PRINT((ACPI_DB_REPAIR, +					  "%s: Return Package is larger than needed - " +					  "found %u, expected %u\n", +					  info->full_pathname, count, +					  expected_count)); +		} + +		/* Validate all elements of the returned package */ + +		status = acpi_ns_check_package_elements(info, elements, +							package->ret_info. +							object_type1, +							package->ret_info. +							count1, +							package->ret_info. +							object_type2, +							package->ret_info. +							count2, 0); +		break; + +	case ACPI_PTYPE1_VAR: +		/* +		 * The package count is variable, there are no subpackages, and all +		 * elements must be of the same type +		 */ +		for (i = 0; i < count; i++) { +			status = acpi_ns_check_object_type(info, elements, +							   package->ret_info. +							   object_type1, i); +			if (ACPI_FAILURE(status)) { +				return (status); +			} +			elements++; +		} +		break; + +	case ACPI_PTYPE1_OPTION: +		/* +		 * The package count is variable, there are no subpackages. There are +		 * a fixed number of required elements, and a variable number of +		 * optional elements. +		 * +		 * Check if package is at least as large as the minimum required +		 */ +		expected_count = package->ret_info3.count; +		if (count < expected_count) { +			goto package_too_small; +		} + +		/* Variable number of sub-objects */ + +		for (i = 0; i < count; i++) { +			if (i < package->ret_info3.count) { + +				/* These are the required package elements (0, 1, or 2) */ + +				status = +				    acpi_ns_check_object_type(info, elements, +							      package-> +							      ret_info3. +							      object_type[i], +							      i); +				if (ACPI_FAILURE(status)) { +					return (status); +				} +			} else { +				/* These are the optional package elements */ + +				status = +				    acpi_ns_check_object_type(info, elements, +							      package-> +							      ret_info3. +							      tail_object_type, +							      i); +				if (ACPI_FAILURE(status)) { +					return (status); +				} +			} +			elements++; +		} +		break; + +	case ACPI_PTYPE2_REV_FIXED: + +		/* First element is the (Integer) revision */ + +		status = acpi_ns_check_object_type(info, elements, +						   ACPI_RTYPE_INTEGER, 0); +		if (ACPI_FAILURE(status)) { +			return (status); +		} + +		elements++; +		count--; + +		/* Examine the subpackages */ + +		status = +		    acpi_ns_check_package_list(info, package, elements, count); +		break; + +	case ACPI_PTYPE2_PKG_COUNT: + +		/* First element is the (Integer) count of subpackages to follow */ + +		status = acpi_ns_check_object_type(info, elements, +						   ACPI_RTYPE_INTEGER, 0); +		if (ACPI_FAILURE(status)) { +			return (status); +		} + +		/* +		 * Count cannot be larger than the parent package length, but allow it +		 * to be smaller. The >= accounts for the Integer above. +		 */ +		expected_count = (u32)(*elements)->integer.value; +		if (expected_count >= count) { +			goto package_too_small; +		} + +		count = expected_count; +		elements++; + +		/* Examine the subpackages */ + +		status = +		    acpi_ns_check_package_list(info, package, elements, count); +		break; + +	case ACPI_PTYPE2: +	case ACPI_PTYPE2_FIXED: +	case ACPI_PTYPE2_MIN: +	case ACPI_PTYPE2_COUNT: +	case ACPI_PTYPE2_FIX_VAR: +		/* +		 * These types all return a single Package that consists of a +		 * variable number of subpackages. +		 * +		 * First, ensure that the first element is a subpackage. If not, +		 * the BIOS may have incorrectly returned the object as a single +		 * package instead of a Package of Packages (a common error if +		 * there is only one entry). We may be able to repair this by +		 * wrapping the returned Package with a new outer Package. +		 */ +		if (*elements +		    && ((*elements)->common.type != ACPI_TYPE_PACKAGE)) { + +			/* Create the new outer package and populate it */ + +			status = +			    acpi_ns_wrap_with_package(info, return_object, +						      return_object_ptr); +			if (ACPI_FAILURE(status)) { +				return (status); +			} + +			/* Update locals to point to the new package (of 1 element) */ + +			return_object = *return_object_ptr; +			elements = return_object->package.elements; +			count = 1; +		} + +		/* Examine the subpackages */ + +		status = +		    acpi_ns_check_package_list(info, package, elements, count); +		break; + +	default: + +		/* Should not get here if predefined info table is correct */ + +		ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname, +				      info->node_flags, +				      "Invalid internal return type in table entry: %X", +				      package->ret_info.type)); + +		return (AE_AML_INTERNAL); +	} + +	return (status); + +package_too_small: + +	/* Error exit for the case with an incorrect package count */ + +	ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname, info->node_flags, +			      "Return Package is too small - found %u elements, expected %u", +			      count, expected_count)); + +	return (AE_AML_OPERAND_VALUE); +} + +/******************************************************************************* + * + * FUNCTION:    acpi_ns_check_package_list + * + * PARAMETERS:  info            - Method execution information block + *              package         - Pointer to package-specific info for method + *              elements        - Element list of parent package. All elements + *                                of this list should be of type Package. + *              count           - Count of subpackages + * + * RETURN:      Status + * + * DESCRIPTION: Examine a list of subpackages + * + ******************************************************************************/ + +static acpi_status +acpi_ns_check_package_list(struct acpi_evaluate_info *info, +			   const union acpi_predefined_info *package, +			   union acpi_operand_object **elements, u32 count) +{ +	union acpi_operand_object *sub_package; +	union acpi_operand_object **sub_elements; +	acpi_status status; +	u32 expected_count; +	u32 i; +	u32 j; + +	/* +	 * Validate each subpackage in the parent Package +	 * +	 * NOTE: assumes list of subpackages contains no NULL elements. +	 * Any NULL elements should have been removed by earlier call +	 * to acpi_ns_remove_null_elements. +	 */ +	for (i = 0; i < count; i++) { +		sub_package = *elements; +		sub_elements = sub_package->package.elements; +		info->parent_package = sub_package; + +		/* Each sub-object must be of type Package */ + +		status = acpi_ns_check_object_type(info, &sub_package, +						   ACPI_RTYPE_PACKAGE, i); +		if (ACPI_FAILURE(status)) { +			return (status); +		} + +		/* Examine the different types of expected subpackages */ + +		info->parent_package = sub_package; +		switch (package->ret_info.type) { +		case ACPI_PTYPE2: +		case ACPI_PTYPE2_PKG_COUNT: +		case ACPI_PTYPE2_REV_FIXED: + +			/* Each subpackage has a fixed number of elements */ + +			expected_count = +			    package->ret_info.count1 + package->ret_info.count2; +			if (sub_package->package.count < expected_count) { +				goto package_too_small; +			} + +			status = +			    acpi_ns_check_package_elements(info, sub_elements, +							   package->ret_info. +							   object_type1, +							   package->ret_info. +							   count1, +							   package->ret_info. +							   object_type2, +							   package->ret_info. +							   count2, 0); +			if (ACPI_FAILURE(status)) { +				return (status); +			} +			break; + +		case ACPI_PTYPE2_FIX_VAR: +			/* +			 * Each subpackage has a fixed number of elements and an +			 * optional element +			 */ +			expected_count = +			    package->ret_info.count1 + package->ret_info.count2; +			if (sub_package->package.count < expected_count) { +				goto package_too_small; +			} + +			status = +			    acpi_ns_check_package_elements(info, sub_elements, +							   package->ret_info. +							   object_type1, +							   package->ret_info. +							   count1, +							   package->ret_info. +							   object_type2, +							   sub_package->package. +							   count - +							   package->ret_info. +							   count1, 0); +			if (ACPI_FAILURE(status)) { +				return (status); +			} +			break; + +		case ACPI_PTYPE2_FIXED: + +			/* Each subpackage has a fixed length */ + +			expected_count = package->ret_info2.count; +			if (sub_package->package.count < expected_count) { +				goto package_too_small; +			} + +			/* Check the type of each subpackage element */ + +			for (j = 0; j < expected_count; j++) { +				status = +				    acpi_ns_check_object_type(info, +							      &sub_elements[j], +							      package-> +							      ret_info2. +							      object_type[j], +							      j); +				if (ACPI_FAILURE(status)) { +					return (status); +				} +			} +			break; + +		case ACPI_PTYPE2_MIN: + +			/* Each subpackage has a variable but minimum length */ + +			expected_count = package->ret_info.count1; +			if (sub_package->package.count < expected_count) { +				goto package_too_small; +			} + +			/* Check the type of each subpackage element */ + +			status = +			    acpi_ns_check_package_elements(info, sub_elements, +							   package->ret_info. +							   object_type1, +							   sub_package->package. +							   count, 0, 0, 0); +			if (ACPI_FAILURE(status)) { +				return (status); +			} +			break; + +		case ACPI_PTYPE2_COUNT: +			/* +			 * First element is the (Integer) count of elements, including +			 * the count field (the ACPI name is num_elements) +			 */ +			status = acpi_ns_check_object_type(info, sub_elements, +							   ACPI_RTYPE_INTEGER, +							   0); +			if (ACPI_FAILURE(status)) { +				return (status); +			} + +			/* +			 * Make sure package is large enough for the Count and is +			 * is as large as the minimum size +			 */ +			expected_count = (u32)(*sub_elements)->integer.value; +			if (sub_package->package.count < expected_count) { +				goto package_too_small; +			} +			if (sub_package->package.count < +			    package->ret_info.count1) { +				expected_count = package->ret_info.count1; +				goto package_too_small; +			} +			if (expected_count == 0) { +				/* +				 * Either the num_entries element was originally zero or it was +				 * a NULL element and repaired to an Integer of value zero. +				 * In either case, repair it by setting num_entries to be the +				 * actual size of the subpackage. +				 */ +				expected_count = sub_package->package.count; +				(*sub_elements)->integer.value = expected_count; +			} + +			/* Check the type of each subpackage element */ + +			status = +			    acpi_ns_check_package_elements(info, +							   (sub_elements + 1), +							   package->ret_info. +							   object_type1, +							   (expected_count - 1), +							   0, 0, 1); +			if (ACPI_FAILURE(status)) { +				return (status); +			} +			break; + +		default:	/* Should not get here, type was validated by caller */ + +			return (AE_AML_INTERNAL); +		} + +		elements++; +	} + +	return (AE_OK); + +package_too_small: + +	/* The subpackage count was smaller than required */ + +	ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname, info->node_flags, +			      "Return SubPackage[%u] is too small - found %u elements, expected %u", +			      i, sub_package->package.count, expected_count)); + +	return (AE_AML_OPERAND_VALUE); +} + +/******************************************************************************* + * + * FUNCTION:    acpi_ns_check_package_elements + * + * PARAMETERS:  info            - Method execution information block + *              elements        - Pointer to the package elements array + *              type1           - Object type for first group + *              count1          - Count for first group + *              type2           - Object type for second group + *              count2          - Count for second group + *              start_index     - Start of the first group of elements + * + * RETURN:      Status + * + * DESCRIPTION: Check that all elements of a package are of the correct object + *              type. Supports up to two groups of different object types. + * + ******************************************************************************/ + +static acpi_status +acpi_ns_check_package_elements(struct acpi_evaluate_info *info, +			       union acpi_operand_object **elements, +			       u8 type1, +			       u32 count1, +			       u8 type2, u32 count2, u32 start_index) +{ +	union acpi_operand_object **this_element = elements; +	acpi_status status; +	u32 i; + +	/* +	 * Up to two groups of package elements are supported by the data +	 * structure. All elements in each group must be of the same type. +	 * The second group can have a count of zero. +	 */ +	for (i = 0; i < count1; i++) { +		status = acpi_ns_check_object_type(info, this_element, +						   type1, i + start_index); +		if (ACPI_FAILURE(status)) { +			return (status); +		} +		this_element++; +	} + +	for (i = 0; i < count2; i++) { +		status = acpi_ns_check_object_type(info, this_element, +						   type2, +						   (i + count1 + start_index)); +		if (ACPI_FAILURE(status)) { +			return (status); +		} +		this_element++; +	} + +	return (AE_OK); +}  | 
