162306a36Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
262306a36Sopenharmony_ci/******************************************************************************
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Module Name: nsprepkg - Validation of package objects for predefined names
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Copyright (C) 2000 - 2023, Intel Corp.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci *****************************************************************************/
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <acpi/acpi.h>
1162306a36Sopenharmony_ci#include "accommon.h"
1262306a36Sopenharmony_ci#include "acnamesp.h"
1362306a36Sopenharmony_ci#include "acpredef.h"
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#define _COMPONENT          ACPI_NAMESPACE
1662306a36Sopenharmony_ciACPI_MODULE_NAME("nsprepkg")
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci/* Local prototypes */
1962306a36Sopenharmony_cistatic acpi_status
2062306a36Sopenharmony_ciacpi_ns_check_package_list(struct acpi_evaluate_info *info,
2162306a36Sopenharmony_ci			   const union acpi_predefined_info *package,
2262306a36Sopenharmony_ci			   union acpi_operand_object **elements, u32 count);
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cistatic acpi_status
2562306a36Sopenharmony_ciacpi_ns_check_package_elements(struct acpi_evaluate_info *info,
2662306a36Sopenharmony_ci			       union acpi_operand_object **elements,
2762306a36Sopenharmony_ci			       u8 type1,
2862306a36Sopenharmony_ci			       u32 count1,
2962306a36Sopenharmony_ci			       u8 type2, u32 count2, u32 start_index);
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistatic acpi_status
3262306a36Sopenharmony_ciacpi_ns_custom_package(struct acpi_evaluate_info *info,
3362306a36Sopenharmony_ci		       union acpi_operand_object **elements, u32 count);
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci/*******************************************************************************
3662306a36Sopenharmony_ci *
3762306a36Sopenharmony_ci * FUNCTION:    acpi_ns_check_package
3862306a36Sopenharmony_ci *
3962306a36Sopenharmony_ci * PARAMETERS:  info                - Method execution information block
4062306a36Sopenharmony_ci *              return_object_ptr   - Pointer to the object returned from the
4162306a36Sopenharmony_ci *                                    evaluation of a method or object
4262306a36Sopenharmony_ci *
4362306a36Sopenharmony_ci * RETURN:      Status
4462306a36Sopenharmony_ci *
4562306a36Sopenharmony_ci * DESCRIPTION: Check a returned package object for the correct count and
4662306a36Sopenharmony_ci *              correct type of all sub-objects.
4762306a36Sopenharmony_ci *
4862306a36Sopenharmony_ci ******************************************************************************/
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ciacpi_status
5162306a36Sopenharmony_ciacpi_ns_check_package(struct acpi_evaluate_info *info,
5262306a36Sopenharmony_ci		      union acpi_operand_object **return_object_ptr)
5362306a36Sopenharmony_ci{
5462306a36Sopenharmony_ci	union acpi_operand_object *return_object = *return_object_ptr;
5562306a36Sopenharmony_ci	const union acpi_predefined_info *package;
5662306a36Sopenharmony_ci	union acpi_operand_object **elements;
5762306a36Sopenharmony_ci	acpi_status status = AE_OK;
5862306a36Sopenharmony_ci	u32 expected_count;
5962306a36Sopenharmony_ci	u32 count;
6062306a36Sopenharmony_ci	u32 i;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	ACPI_FUNCTION_TRACE(ns_check_package);
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	/* The package info for this name is in the next table entry */
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	package = info->predefined + 1;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	ACPI_DEBUG_PRINT((ACPI_DB_NAMES,
6962306a36Sopenharmony_ci			  "%s Validating return Package of Type %X, Count %X\n",
7062306a36Sopenharmony_ci			  info->full_pathname, package->ret_info.type,
7162306a36Sopenharmony_ci			  return_object->package.count));
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	/*
7462306a36Sopenharmony_ci	 * For variable-length Packages, we can safely remove all embedded
7562306a36Sopenharmony_ci	 * and trailing NULL package elements
7662306a36Sopenharmony_ci	 */
7762306a36Sopenharmony_ci	acpi_ns_remove_null_elements(info, package->ret_info.type,
7862306a36Sopenharmony_ci				     return_object);
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	/* Extract package count and elements array */
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	elements = return_object->package.elements;
8362306a36Sopenharmony_ci	count = return_object->package.count;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	/*
8662306a36Sopenharmony_ci	 * Most packages must have at least one element. The only exception
8762306a36Sopenharmony_ci	 * is the variable-length package (ACPI_PTYPE1_VAR).
8862306a36Sopenharmony_ci	 */
8962306a36Sopenharmony_ci	if (!count) {
9062306a36Sopenharmony_ci		if (package->ret_info.type == ACPI_PTYPE1_VAR) {
9162306a36Sopenharmony_ci			return_ACPI_STATUS(AE_OK);
9262306a36Sopenharmony_ci		}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci		ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname,
9562306a36Sopenharmony_ci				      info->node_flags,
9662306a36Sopenharmony_ci				      "Return Package has no elements (empty)"));
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci		return_ACPI_STATUS(AE_AML_OPERAND_VALUE);
9962306a36Sopenharmony_ci	}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	/*
10262306a36Sopenharmony_ci	 * Decode the type of the expected package contents
10362306a36Sopenharmony_ci	 *
10462306a36Sopenharmony_ci	 * PTYPE1 packages contain no subpackages
10562306a36Sopenharmony_ci	 * PTYPE2 packages contain subpackages
10662306a36Sopenharmony_ci	 */
10762306a36Sopenharmony_ci	switch (package->ret_info.type) {
10862306a36Sopenharmony_ci	case ACPI_PTYPE_CUSTOM:
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci		status = acpi_ns_custom_package(info, elements, count);
11162306a36Sopenharmony_ci		break;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	case ACPI_PTYPE1_FIXED:
11462306a36Sopenharmony_ci		/*
11562306a36Sopenharmony_ci		 * The package count is fixed and there are no subpackages
11662306a36Sopenharmony_ci		 *
11762306a36Sopenharmony_ci		 * If package is too small, exit.
11862306a36Sopenharmony_ci		 * If package is larger than expected, issue warning but continue
11962306a36Sopenharmony_ci		 */
12062306a36Sopenharmony_ci		expected_count =
12162306a36Sopenharmony_ci		    package->ret_info.count1 + package->ret_info.count2;
12262306a36Sopenharmony_ci		if (count < expected_count) {
12362306a36Sopenharmony_ci			goto package_too_small;
12462306a36Sopenharmony_ci		} else if (count > expected_count) {
12562306a36Sopenharmony_ci			ACPI_DEBUG_PRINT((ACPI_DB_REPAIR,
12662306a36Sopenharmony_ci					  "%s: Return Package is larger than needed - "
12762306a36Sopenharmony_ci					  "found %u, expected %u\n",
12862306a36Sopenharmony_ci					  info->full_pathname, count,
12962306a36Sopenharmony_ci					  expected_count));
13062306a36Sopenharmony_ci		}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci		/* Validate all elements of the returned package */
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci		status = acpi_ns_check_package_elements(info, elements,
13562306a36Sopenharmony_ci							package->ret_info.
13662306a36Sopenharmony_ci							object_type1,
13762306a36Sopenharmony_ci							package->ret_info.
13862306a36Sopenharmony_ci							count1,
13962306a36Sopenharmony_ci							package->ret_info.
14062306a36Sopenharmony_ci							object_type2,
14162306a36Sopenharmony_ci							package->ret_info.
14262306a36Sopenharmony_ci							count2, 0);
14362306a36Sopenharmony_ci		break;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	case ACPI_PTYPE1_VAR:
14662306a36Sopenharmony_ci		/*
14762306a36Sopenharmony_ci		 * The package count is variable, there are no subpackages, and all
14862306a36Sopenharmony_ci		 * elements must be of the same type
14962306a36Sopenharmony_ci		 */
15062306a36Sopenharmony_ci		for (i = 0; i < count; i++) {
15162306a36Sopenharmony_ci			status = acpi_ns_check_object_type(info, elements,
15262306a36Sopenharmony_ci							   package->ret_info.
15362306a36Sopenharmony_ci							   object_type1, i);
15462306a36Sopenharmony_ci			if (ACPI_FAILURE(status)) {
15562306a36Sopenharmony_ci				return_ACPI_STATUS(status);
15662306a36Sopenharmony_ci			}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci			elements++;
15962306a36Sopenharmony_ci		}
16062306a36Sopenharmony_ci		break;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	case ACPI_PTYPE1_OPTION:
16362306a36Sopenharmony_ci		/*
16462306a36Sopenharmony_ci		 * The package count is variable, there are no subpackages. There are
16562306a36Sopenharmony_ci		 * a fixed number of required elements, and a variable number of
16662306a36Sopenharmony_ci		 * optional elements.
16762306a36Sopenharmony_ci		 *
16862306a36Sopenharmony_ci		 * Check if package is at least as large as the minimum required
16962306a36Sopenharmony_ci		 */
17062306a36Sopenharmony_ci		expected_count = package->ret_info3.count;
17162306a36Sopenharmony_ci		if (count < expected_count) {
17262306a36Sopenharmony_ci			goto package_too_small;
17362306a36Sopenharmony_ci		}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci		/* Variable number of sub-objects */
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci		for (i = 0; i < count; i++) {
17862306a36Sopenharmony_ci			if (i < package->ret_info3.count) {
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci				/* These are the required package elements (0, 1, or 2) */
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci				status =
18362306a36Sopenharmony_ci				    acpi_ns_check_object_type(info, elements,
18462306a36Sopenharmony_ci							      package->
18562306a36Sopenharmony_ci							      ret_info3.
18662306a36Sopenharmony_ci							      object_type[i],
18762306a36Sopenharmony_ci							      i);
18862306a36Sopenharmony_ci				if (ACPI_FAILURE(status)) {
18962306a36Sopenharmony_ci					return_ACPI_STATUS(status);
19062306a36Sopenharmony_ci				}
19162306a36Sopenharmony_ci			} else {
19262306a36Sopenharmony_ci				/* These are the optional package elements */
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci				status =
19562306a36Sopenharmony_ci				    acpi_ns_check_object_type(info, elements,
19662306a36Sopenharmony_ci							      package->
19762306a36Sopenharmony_ci							      ret_info3.
19862306a36Sopenharmony_ci							      tail_object_type,
19962306a36Sopenharmony_ci							      i);
20062306a36Sopenharmony_ci				if (ACPI_FAILURE(status)) {
20162306a36Sopenharmony_ci					return_ACPI_STATUS(status);
20262306a36Sopenharmony_ci				}
20362306a36Sopenharmony_ci			}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci			elements++;
20662306a36Sopenharmony_ci		}
20762306a36Sopenharmony_ci		break;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	case ACPI_PTYPE2_REV_FIXED:
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci		/* First element is the (Integer) revision */
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci		status =
21462306a36Sopenharmony_ci		    acpi_ns_check_object_type(info, elements,
21562306a36Sopenharmony_ci					      ACPI_RTYPE_INTEGER, 0);
21662306a36Sopenharmony_ci		if (ACPI_FAILURE(status)) {
21762306a36Sopenharmony_ci			return_ACPI_STATUS(status);
21862306a36Sopenharmony_ci		}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci		elements++;
22162306a36Sopenharmony_ci		count--;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci		/* Examine the subpackages */
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci		status =
22662306a36Sopenharmony_ci		    acpi_ns_check_package_list(info, package, elements, count);
22762306a36Sopenharmony_ci		break;
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	case ACPI_PTYPE2_PKG_COUNT:
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci		/* First element is the (Integer) count of subpackages to follow */
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci		status =
23462306a36Sopenharmony_ci		    acpi_ns_check_object_type(info, elements,
23562306a36Sopenharmony_ci					      ACPI_RTYPE_INTEGER, 0);
23662306a36Sopenharmony_ci		if (ACPI_FAILURE(status)) {
23762306a36Sopenharmony_ci			return_ACPI_STATUS(status);
23862306a36Sopenharmony_ci		}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci		/*
24162306a36Sopenharmony_ci		 * Count cannot be larger than the parent package length, but allow it
24262306a36Sopenharmony_ci		 * to be smaller. The >= accounts for the Integer above.
24362306a36Sopenharmony_ci		 */
24462306a36Sopenharmony_ci		expected_count = (u32)(*elements)->integer.value;
24562306a36Sopenharmony_ci		if (expected_count >= count) {
24662306a36Sopenharmony_ci			goto package_too_small;
24762306a36Sopenharmony_ci		}
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci		count = expected_count;
25062306a36Sopenharmony_ci		elements++;
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci		/* Examine the subpackages */
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci		status =
25562306a36Sopenharmony_ci		    acpi_ns_check_package_list(info, package, elements, count);
25662306a36Sopenharmony_ci		break;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	case ACPI_PTYPE2:
25962306a36Sopenharmony_ci	case ACPI_PTYPE2_FIXED:
26062306a36Sopenharmony_ci	case ACPI_PTYPE2_MIN:
26162306a36Sopenharmony_ci	case ACPI_PTYPE2_COUNT:
26262306a36Sopenharmony_ci	case ACPI_PTYPE2_FIX_VAR:
26362306a36Sopenharmony_ci		/*
26462306a36Sopenharmony_ci		 * These types all return a single Package that consists of a
26562306a36Sopenharmony_ci		 * variable number of subpackages.
26662306a36Sopenharmony_ci		 *
26762306a36Sopenharmony_ci		 * First, ensure that the first element is a subpackage. If not,
26862306a36Sopenharmony_ci		 * the BIOS may have incorrectly returned the object as a single
26962306a36Sopenharmony_ci		 * package instead of a Package of Packages (a common error if
27062306a36Sopenharmony_ci		 * there is only one entry). We may be able to repair this by
27162306a36Sopenharmony_ci		 * wrapping the returned Package with a new outer Package.
27262306a36Sopenharmony_ci		 */
27362306a36Sopenharmony_ci		if (*elements
27462306a36Sopenharmony_ci		    && ((*elements)->common.type != ACPI_TYPE_PACKAGE)) {
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci			/* Create the new outer package and populate it */
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci			status =
27962306a36Sopenharmony_ci			    acpi_ns_wrap_with_package(info, return_object,
28062306a36Sopenharmony_ci						      return_object_ptr);
28162306a36Sopenharmony_ci			if (ACPI_FAILURE(status)) {
28262306a36Sopenharmony_ci				return_ACPI_STATUS(status);
28362306a36Sopenharmony_ci			}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci			/* Update locals to point to the new package (of 1 element) */
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci			return_object = *return_object_ptr;
28862306a36Sopenharmony_ci			elements = return_object->package.elements;
28962306a36Sopenharmony_ci			count = 1;
29062306a36Sopenharmony_ci		}
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci		/* Examine the subpackages */
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci		status =
29562306a36Sopenharmony_ci		    acpi_ns_check_package_list(info, package, elements, count);
29662306a36Sopenharmony_ci		break;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	case ACPI_PTYPE2_VAR_VAR:
29962306a36Sopenharmony_ci		/*
30062306a36Sopenharmony_ci		 * Returns a variable list of packages, each with a variable list
30162306a36Sopenharmony_ci		 * of objects.
30262306a36Sopenharmony_ci		 */
30362306a36Sopenharmony_ci		break;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	case ACPI_PTYPE2_UUID_PAIR:
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci		/* The package must contain pairs of (UUID + type) */
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci		if (count & 1) {
31062306a36Sopenharmony_ci			expected_count = count + 1;
31162306a36Sopenharmony_ci			goto package_too_small;
31262306a36Sopenharmony_ci		}
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci		while (count > 0) {
31562306a36Sopenharmony_ci			status = acpi_ns_check_object_type(info, elements,
31662306a36Sopenharmony_ci							   package->ret_info.
31762306a36Sopenharmony_ci							   object_type1, 0);
31862306a36Sopenharmony_ci			if (ACPI_FAILURE(status)) {
31962306a36Sopenharmony_ci				return_ACPI_STATUS(status);
32062306a36Sopenharmony_ci			}
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci			/* Validate length of the UUID buffer */
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci			if ((*elements)->buffer.length != 16) {
32562306a36Sopenharmony_ci				ACPI_WARN_PREDEFINED((AE_INFO,
32662306a36Sopenharmony_ci						      info->full_pathname,
32762306a36Sopenharmony_ci						      info->node_flags,
32862306a36Sopenharmony_ci						      "Invalid length for UUID Buffer"));
32962306a36Sopenharmony_ci				return_ACPI_STATUS(AE_AML_OPERAND_VALUE);
33062306a36Sopenharmony_ci			}
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci			status = acpi_ns_check_object_type(info, elements + 1,
33362306a36Sopenharmony_ci							   package->ret_info.
33462306a36Sopenharmony_ci							   object_type2, 0);
33562306a36Sopenharmony_ci			if (ACPI_FAILURE(status)) {
33662306a36Sopenharmony_ci				return_ACPI_STATUS(status);
33762306a36Sopenharmony_ci			}
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci			elements += 2;
34062306a36Sopenharmony_ci			count -= 2;
34162306a36Sopenharmony_ci		}
34262306a36Sopenharmony_ci		break;
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	default:
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci		/* Should not get here if predefined info table is correct */
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci		ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname,
34962306a36Sopenharmony_ci				      info->node_flags,
35062306a36Sopenharmony_ci				      "Invalid internal return type in table entry: %X",
35162306a36Sopenharmony_ci				      package->ret_info.type));
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci		return_ACPI_STATUS(AE_AML_INTERNAL);
35462306a36Sopenharmony_ci	}
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	return_ACPI_STATUS(status);
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_cipackage_too_small:
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	/* Error exit for the case with an incorrect package count */
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname, info->node_flags,
36362306a36Sopenharmony_ci			      "Return Package is too small - found %u elements, expected %u",
36462306a36Sopenharmony_ci			      count, expected_count));
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	return_ACPI_STATUS(AE_AML_OPERAND_VALUE);
36762306a36Sopenharmony_ci}
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci/*******************************************************************************
37062306a36Sopenharmony_ci *
37162306a36Sopenharmony_ci * FUNCTION:    acpi_ns_check_package_list
37262306a36Sopenharmony_ci *
37362306a36Sopenharmony_ci * PARAMETERS:  info            - Method execution information block
37462306a36Sopenharmony_ci *              package         - Pointer to package-specific info for method
37562306a36Sopenharmony_ci *              elements        - Element list of parent package. All elements
37662306a36Sopenharmony_ci *                                of this list should be of type Package.
37762306a36Sopenharmony_ci *              count           - Count of subpackages
37862306a36Sopenharmony_ci *
37962306a36Sopenharmony_ci * RETURN:      Status
38062306a36Sopenharmony_ci *
38162306a36Sopenharmony_ci * DESCRIPTION: Examine a list of subpackages
38262306a36Sopenharmony_ci *
38362306a36Sopenharmony_ci ******************************************************************************/
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_cistatic acpi_status
38662306a36Sopenharmony_ciacpi_ns_check_package_list(struct acpi_evaluate_info *info,
38762306a36Sopenharmony_ci			   const union acpi_predefined_info *package,
38862306a36Sopenharmony_ci			   union acpi_operand_object **elements, u32 count)
38962306a36Sopenharmony_ci{
39062306a36Sopenharmony_ci	union acpi_operand_object *sub_package;
39162306a36Sopenharmony_ci	union acpi_operand_object **sub_elements;
39262306a36Sopenharmony_ci	acpi_status status;
39362306a36Sopenharmony_ci	u32 expected_count;
39462306a36Sopenharmony_ci	u32 i;
39562306a36Sopenharmony_ci	u32 j;
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	/*
39862306a36Sopenharmony_ci	 * Validate each subpackage in the parent Package
39962306a36Sopenharmony_ci	 *
40062306a36Sopenharmony_ci	 * NOTE: assumes list of subpackages contains no NULL elements.
40162306a36Sopenharmony_ci	 * Any NULL elements should have been removed by earlier call
40262306a36Sopenharmony_ci	 * to acpi_ns_remove_null_elements.
40362306a36Sopenharmony_ci	 */
40462306a36Sopenharmony_ci	for (i = 0; i < count; i++) {
40562306a36Sopenharmony_ci		sub_package = *elements;
40662306a36Sopenharmony_ci		sub_elements = sub_package->package.elements;
40762306a36Sopenharmony_ci		info->parent_package = sub_package;
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci		/* Each sub-object must be of type Package */
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci		status = acpi_ns_check_object_type(info, &sub_package,
41262306a36Sopenharmony_ci						   ACPI_RTYPE_PACKAGE, i);
41362306a36Sopenharmony_ci		if (ACPI_FAILURE(status)) {
41462306a36Sopenharmony_ci			return (status);
41562306a36Sopenharmony_ci		}
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci		/* Examine the different types of expected subpackages */
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci		info->parent_package = sub_package;
42062306a36Sopenharmony_ci		switch (package->ret_info.type) {
42162306a36Sopenharmony_ci		case ACPI_PTYPE2:
42262306a36Sopenharmony_ci		case ACPI_PTYPE2_PKG_COUNT:
42362306a36Sopenharmony_ci		case ACPI_PTYPE2_REV_FIXED:
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci			/* Each subpackage has a fixed number of elements */
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci			expected_count =
42862306a36Sopenharmony_ci			    package->ret_info.count1 + package->ret_info.count2;
42962306a36Sopenharmony_ci			if (sub_package->package.count < expected_count) {
43062306a36Sopenharmony_ci				goto package_too_small;
43162306a36Sopenharmony_ci			}
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci			status =
43462306a36Sopenharmony_ci			    acpi_ns_check_package_elements(info, sub_elements,
43562306a36Sopenharmony_ci							   package->ret_info.
43662306a36Sopenharmony_ci							   object_type1,
43762306a36Sopenharmony_ci							   package->ret_info.
43862306a36Sopenharmony_ci							   count1,
43962306a36Sopenharmony_ci							   package->ret_info.
44062306a36Sopenharmony_ci							   object_type2,
44162306a36Sopenharmony_ci							   package->ret_info.
44262306a36Sopenharmony_ci							   count2, 0);
44362306a36Sopenharmony_ci			if (ACPI_FAILURE(status)) {
44462306a36Sopenharmony_ci				return (status);
44562306a36Sopenharmony_ci			}
44662306a36Sopenharmony_ci			break;
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci		case ACPI_PTYPE2_FIX_VAR:
44962306a36Sopenharmony_ci			/*
45062306a36Sopenharmony_ci			 * Each subpackage has a fixed number of elements and an
45162306a36Sopenharmony_ci			 * optional element
45262306a36Sopenharmony_ci			 */
45362306a36Sopenharmony_ci			expected_count =
45462306a36Sopenharmony_ci			    package->ret_info.count1 + package->ret_info.count2;
45562306a36Sopenharmony_ci			if (sub_package->package.count < expected_count) {
45662306a36Sopenharmony_ci				goto package_too_small;
45762306a36Sopenharmony_ci			}
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci			status =
46062306a36Sopenharmony_ci			    acpi_ns_check_package_elements(info, sub_elements,
46162306a36Sopenharmony_ci							   package->ret_info.
46262306a36Sopenharmony_ci							   object_type1,
46362306a36Sopenharmony_ci							   package->ret_info.
46462306a36Sopenharmony_ci							   count1,
46562306a36Sopenharmony_ci							   package->ret_info.
46662306a36Sopenharmony_ci							   object_type2,
46762306a36Sopenharmony_ci							   sub_package->package.
46862306a36Sopenharmony_ci							   count -
46962306a36Sopenharmony_ci							   package->ret_info.
47062306a36Sopenharmony_ci							   count1, 0);
47162306a36Sopenharmony_ci			if (ACPI_FAILURE(status)) {
47262306a36Sopenharmony_ci				return (status);
47362306a36Sopenharmony_ci			}
47462306a36Sopenharmony_ci			break;
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci		case ACPI_PTYPE2_VAR_VAR:
47762306a36Sopenharmony_ci			/*
47862306a36Sopenharmony_ci			 * Each subpackage has a fixed or variable number of elements
47962306a36Sopenharmony_ci			 */
48062306a36Sopenharmony_ci			break;
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci		case ACPI_PTYPE2_FIXED:
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci			/* Each subpackage has a fixed length */
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci			expected_count = package->ret_info2.count;
48762306a36Sopenharmony_ci			if (sub_package->package.count < expected_count) {
48862306a36Sopenharmony_ci				goto package_too_small;
48962306a36Sopenharmony_ci			}
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci			/* Check the type of each subpackage element */
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci			for (j = 0; j < expected_count; j++) {
49462306a36Sopenharmony_ci				status =
49562306a36Sopenharmony_ci				    acpi_ns_check_object_type(info,
49662306a36Sopenharmony_ci							      &sub_elements[j],
49762306a36Sopenharmony_ci							      package->
49862306a36Sopenharmony_ci							      ret_info2.
49962306a36Sopenharmony_ci							      object_type[j],
50062306a36Sopenharmony_ci							      j);
50162306a36Sopenharmony_ci				if (ACPI_FAILURE(status)) {
50262306a36Sopenharmony_ci					return (status);
50362306a36Sopenharmony_ci				}
50462306a36Sopenharmony_ci			}
50562306a36Sopenharmony_ci			break;
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci		case ACPI_PTYPE2_MIN:
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci			/* Each subpackage has a variable but minimum length */
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci			expected_count = package->ret_info.count1;
51262306a36Sopenharmony_ci			if (sub_package->package.count < expected_count) {
51362306a36Sopenharmony_ci				goto package_too_small;
51462306a36Sopenharmony_ci			}
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci			/* Check the type of each subpackage element */
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci			status =
51962306a36Sopenharmony_ci			    acpi_ns_check_package_elements(info, sub_elements,
52062306a36Sopenharmony_ci							   package->ret_info.
52162306a36Sopenharmony_ci							   object_type1,
52262306a36Sopenharmony_ci							   sub_package->package.
52362306a36Sopenharmony_ci							   count, 0, 0, 0);
52462306a36Sopenharmony_ci			if (ACPI_FAILURE(status)) {
52562306a36Sopenharmony_ci				return (status);
52662306a36Sopenharmony_ci			}
52762306a36Sopenharmony_ci			break;
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci		case ACPI_PTYPE2_COUNT:
53062306a36Sopenharmony_ci			/*
53162306a36Sopenharmony_ci			 * First element is the (Integer) count of elements, including
53262306a36Sopenharmony_ci			 * the count field (the ACPI name is num_elements)
53362306a36Sopenharmony_ci			 */
53462306a36Sopenharmony_ci			status = acpi_ns_check_object_type(info, sub_elements,
53562306a36Sopenharmony_ci							   ACPI_RTYPE_INTEGER,
53662306a36Sopenharmony_ci							   0);
53762306a36Sopenharmony_ci			if (ACPI_FAILURE(status)) {
53862306a36Sopenharmony_ci				return (status);
53962306a36Sopenharmony_ci			}
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci			/*
54262306a36Sopenharmony_ci			 * Make sure package is large enough for the Count and is
54362306a36Sopenharmony_ci			 * is as large as the minimum size
54462306a36Sopenharmony_ci			 */
54562306a36Sopenharmony_ci			expected_count = (u32)(*sub_elements)->integer.value;
54662306a36Sopenharmony_ci			if (sub_package->package.count < expected_count) {
54762306a36Sopenharmony_ci				goto package_too_small;
54862306a36Sopenharmony_ci			}
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci			if (sub_package->package.count <
55162306a36Sopenharmony_ci			    package->ret_info.count1) {
55262306a36Sopenharmony_ci				expected_count = package->ret_info.count1;
55362306a36Sopenharmony_ci				goto package_too_small;
55462306a36Sopenharmony_ci			}
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci			if (expected_count == 0) {
55762306a36Sopenharmony_ci				/*
55862306a36Sopenharmony_ci				 * Either the num_entries element was originally zero or it was
55962306a36Sopenharmony_ci				 * a NULL element and repaired to an Integer of value zero.
56062306a36Sopenharmony_ci				 * In either case, repair it by setting num_entries to be the
56162306a36Sopenharmony_ci				 * actual size of the subpackage.
56262306a36Sopenharmony_ci				 */
56362306a36Sopenharmony_ci				expected_count = sub_package->package.count;
56462306a36Sopenharmony_ci				(*sub_elements)->integer.value = expected_count;
56562306a36Sopenharmony_ci			}
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci			/* Check the type of each subpackage element */
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci			status =
57062306a36Sopenharmony_ci			    acpi_ns_check_package_elements(info,
57162306a36Sopenharmony_ci							   (sub_elements + 1),
57262306a36Sopenharmony_ci							   package->ret_info.
57362306a36Sopenharmony_ci							   object_type1,
57462306a36Sopenharmony_ci							   (expected_count - 1),
57562306a36Sopenharmony_ci							   0, 0, 1);
57662306a36Sopenharmony_ci			if (ACPI_FAILURE(status)) {
57762306a36Sopenharmony_ci				return (status);
57862306a36Sopenharmony_ci			}
57962306a36Sopenharmony_ci			break;
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci		default:	/* Should not get here, type was validated by caller */
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci			ACPI_ERROR((AE_INFO, "Invalid Package type: %X",
58462306a36Sopenharmony_ci				    package->ret_info.type));
58562306a36Sopenharmony_ci			return (AE_AML_INTERNAL);
58662306a36Sopenharmony_ci		}
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci		elements++;
58962306a36Sopenharmony_ci	}
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	return (AE_OK);
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_cipackage_too_small:
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	/* The subpackage count was smaller than required */
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname, info->node_flags,
59862306a36Sopenharmony_ci			      "Return SubPackage[%u] is too small - found %u elements, expected %u",
59962306a36Sopenharmony_ci			      i, sub_package->package.count, expected_count));
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	return (AE_AML_OPERAND_VALUE);
60262306a36Sopenharmony_ci}
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci/*******************************************************************************
60562306a36Sopenharmony_ci *
60662306a36Sopenharmony_ci * FUNCTION:    acpi_ns_custom_package
60762306a36Sopenharmony_ci *
60862306a36Sopenharmony_ci * PARAMETERS:  info                - Method execution information block
60962306a36Sopenharmony_ci *              elements            - Pointer to the package elements array
61062306a36Sopenharmony_ci *              count               - Element count for the package
61162306a36Sopenharmony_ci *
61262306a36Sopenharmony_ci * RETURN:      Status
61362306a36Sopenharmony_ci *
61462306a36Sopenharmony_ci * DESCRIPTION: Check a returned package object for the correct count and
61562306a36Sopenharmony_ci *              correct type of all sub-objects.
61662306a36Sopenharmony_ci *
61762306a36Sopenharmony_ci * NOTE: Currently used for the _BIX method only. When needed for two or more
61862306a36Sopenharmony_ci * methods, probably a detect/dispatch mechanism will be required.
61962306a36Sopenharmony_ci *
62062306a36Sopenharmony_ci ******************************************************************************/
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_cistatic acpi_status
62362306a36Sopenharmony_ciacpi_ns_custom_package(struct acpi_evaluate_info *info,
62462306a36Sopenharmony_ci		       union acpi_operand_object **elements, u32 count)
62562306a36Sopenharmony_ci{
62662306a36Sopenharmony_ci	u32 expected_count;
62762306a36Sopenharmony_ci	u32 version;
62862306a36Sopenharmony_ci	acpi_status status = AE_OK;
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	ACPI_FUNCTION_NAME(ns_custom_package);
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	/* Get version number, must be Integer */
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	if ((*elements)->common.type != ACPI_TYPE_INTEGER) {
63562306a36Sopenharmony_ci		ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname,
63662306a36Sopenharmony_ci				      info->node_flags,
63762306a36Sopenharmony_ci				      "Return Package has invalid object type for version number"));
63862306a36Sopenharmony_ci		return_ACPI_STATUS(AE_AML_OPERAND_TYPE);
63962306a36Sopenharmony_ci	}
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	version = (u32)(*elements)->integer.value;
64262306a36Sopenharmony_ci	expected_count = 21;	/* Version 1 */
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	if (version == 0) {
64562306a36Sopenharmony_ci		expected_count = 20;	/* Version 0 */
64662306a36Sopenharmony_ci	}
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci	if (count < expected_count) {
64962306a36Sopenharmony_ci		ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname,
65062306a36Sopenharmony_ci				      info->node_flags,
65162306a36Sopenharmony_ci				      "Return Package is too small - found %u elements, expected %u",
65262306a36Sopenharmony_ci				      count, expected_count));
65362306a36Sopenharmony_ci		return_ACPI_STATUS(AE_AML_OPERAND_VALUE);
65462306a36Sopenharmony_ci	} else if (count > expected_count) {
65562306a36Sopenharmony_ci		ACPI_DEBUG_PRINT((ACPI_DB_REPAIR,
65662306a36Sopenharmony_ci				  "%s: Return Package is larger than needed - "
65762306a36Sopenharmony_ci				  "found %u, expected %u\n",
65862306a36Sopenharmony_ci				  info->full_pathname, count, expected_count));
65962306a36Sopenharmony_ci	}
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	/* Validate all elements of the returned package */
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	status = acpi_ns_check_package_elements(info, elements,
66462306a36Sopenharmony_ci						ACPI_RTYPE_INTEGER, 16,
66562306a36Sopenharmony_ci						ACPI_RTYPE_STRING, 4, 0);
66662306a36Sopenharmony_ci	if (ACPI_FAILURE(status)) {
66762306a36Sopenharmony_ci		return_ACPI_STATUS(status);
66862306a36Sopenharmony_ci	}
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci	/* Version 1 has a single trailing integer */
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	if (version > 0) {
67362306a36Sopenharmony_ci		status = acpi_ns_check_package_elements(info, elements + 20,
67462306a36Sopenharmony_ci							ACPI_RTYPE_INTEGER, 1,
67562306a36Sopenharmony_ci							0, 0, 20);
67662306a36Sopenharmony_ci	}
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	return_ACPI_STATUS(status);
67962306a36Sopenharmony_ci}
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci/*******************************************************************************
68262306a36Sopenharmony_ci *
68362306a36Sopenharmony_ci * FUNCTION:    acpi_ns_check_package_elements
68462306a36Sopenharmony_ci *
68562306a36Sopenharmony_ci * PARAMETERS:  info            - Method execution information block
68662306a36Sopenharmony_ci *              elements        - Pointer to the package elements array
68762306a36Sopenharmony_ci *              type1           - Object type for first group
68862306a36Sopenharmony_ci *              count1          - Count for first group
68962306a36Sopenharmony_ci *              type2           - Object type for second group
69062306a36Sopenharmony_ci *              count2          - Count for second group
69162306a36Sopenharmony_ci *              start_index     - Start of the first group of elements
69262306a36Sopenharmony_ci *
69362306a36Sopenharmony_ci * RETURN:      Status
69462306a36Sopenharmony_ci *
69562306a36Sopenharmony_ci * DESCRIPTION: Check that all elements of a package are of the correct object
69662306a36Sopenharmony_ci *              type. Supports up to two groups of different object types.
69762306a36Sopenharmony_ci *
69862306a36Sopenharmony_ci ******************************************************************************/
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_cistatic acpi_status
70162306a36Sopenharmony_ciacpi_ns_check_package_elements(struct acpi_evaluate_info *info,
70262306a36Sopenharmony_ci			       union acpi_operand_object **elements,
70362306a36Sopenharmony_ci			       u8 type1,
70462306a36Sopenharmony_ci			       u32 count1,
70562306a36Sopenharmony_ci			       u8 type2, u32 count2, u32 start_index)
70662306a36Sopenharmony_ci{
70762306a36Sopenharmony_ci	union acpi_operand_object **this_element = elements;
70862306a36Sopenharmony_ci	acpi_status status;
70962306a36Sopenharmony_ci	u32 i;
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	ACPI_FUNCTION_TRACE(ns_check_package_elements);
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci	/*
71462306a36Sopenharmony_ci	 * Up to two groups of package elements are supported by the data
71562306a36Sopenharmony_ci	 * structure. All elements in each group must be of the same type.
71662306a36Sopenharmony_ci	 * The second group can have a count of zero.
71762306a36Sopenharmony_ci	 */
71862306a36Sopenharmony_ci	for (i = 0; i < count1; i++) {
71962306a36Sopenharmony_ci		status = acpi_ns_check_object_type(info, this_element,
72062306a36Sopenharmony_ci						   type1, i + start_index);
72162306a36Sopenharmony_ci		if (ACPI_FAILURE(status)) {
72262306a36Sopenharmony_ci			return_ACPI_STATUS(status);
72362306a36Sopenharmony_ci		}
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci		this_element++;
72662306a36Sopenharmony_ci	}
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	for (i = 0; i < count2; i++) {
72962306a36Sopenharmony_ci		status = acpi_ns_check_object_type(info, this_element,
73062306a36Sopenharmony_ci						   type2,
73162306a36Sopenharmony_ci						   (i + count1 + start_index));
73262306a36Sopenharmony_ci		if (ACPI_FAILURE(status)) {
73362306a36Sopenharmony_ci			return_ACPI_STATUS(status);
73462306a36Sopenharmony_ci		}
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci		this_element++;
73762306a36Sopenharmony_ci	}
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci	return_ACPI_STATUS(AE_OK);
74062306a36Sopenharmony_ci}
741