162306a36Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
262306a36Sopenharmony_ci/******************************************************************************
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Module Name: nswalk - Functions for walking the ACPI namespace
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
1462306a36Sopenharmony_ci#define _COMPONENT          ACPI_NAMESPACE
1562306a36Sopenharmony_ciACPI_MODULE_NAME("nswalk")
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci/*******************************************************************************
1862306a36Sopenharmony_ci *
1962306a36Sopenharmony_ci * FUNCTION:    acpi_ns_get_next_node
2062306a36Sopenharmony_ci *
2162306a36Sopenharmony_ci * PARAMETERS:  parent_node         - Parent node whose children we are
2262306a36Sopenharmony_ci *                                    getting
2362306a36Sopenharmony_ci *              child_node          - Previous child that was found.
2462306a36Sopenharmony_ci *                                    The NEXT child will be returned
2562306a36Sopenharmony_ci *
2662306a36Sopenharmony_ci * RETURN:      struct acpi_namespace_node - Pointer to the NEXT child or NULL if
2762306a36Sopenharmony_ci *                                    none is found.
2862306a36Sopenharmony_ci *
2962306a36Sopenharmony_ci * DESCRIPTION: Return the next peer node within the namespace. If Handle
3062306a36Sopenharmony_ci *              is valid, Scope is ignored. Otherwise, the first node
3162306a36Sopenharmony_ci *              within Scope is returned.
3262306a36Sopenharmony_ci *
3362306a36Sopenharmony_ci ******************************************************************************/
3462306a36Sopenharmony_cistruct acpi_namespace_node *acpi_ns_get_next_node(struct acpi_namespace_node
3562306a36Sopenharmony_ci						  *parent_node,
3662306a36Sopenharmony_ci						  struct acpi_namespace_node
3762306a36Sopenharmony_ci						  *child_node)
3862306a36Sopenharmony_ci{
3962306a36Sopenharmony_ci	ACPI_FUNCTION_ENTRY();
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	if (!child_node) {
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci		/* It's really the parent's _scope_ that we want */
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci		return (parent_node->child);
4662306a36Sopenharmony_ci	}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	/* Otherwise just return the next peer */
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	return (child_node->peer);
5162306a36Sopenharmony_ci}
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci/*******************************************************************************
5462306a36Sopenharmony_ci *
5562306a36Sopenharmony_ci * FUNCTION:    acpi_ns_get_next_node_typed
5662306a36Sopenharmony_ci *
5762306a36Sopenharmony_ci * PARAMETERS:  type                - Type of node to be searched for
5862306a36Sopenharmony_ci *              parent_node         - Parent node whose children we are
5962306a36Sopenharmony_ci *                                    getting
6062306a36Sopenharmony_ci *              child_node          - Previous child that was found.
6162306a36Sopenharmony_ci *                                    The NEXT child will be returned
6262306a36Sopenharmony_ci *
6362306a36Sopenharmony_ci * RETURN:      struct acpi_namespace_node - Pointer to the NEXT child or NULL if
6462306a36Sopenharmony_ci *                                    none is found.
6562306a36Sopenharmony_ci *
6662306a36Sopenharmony_ci * DESCRIPTION: Return the next peer node within the namespace. If Handle
6762306a36Sopenharmony_ci *              is valid, Scope is ignored. Otherwise, the first node
6862306a36Sopenharmony_ci *              within Scope is returned.
6962306a36Sopenharmony_ci *
7062306a36Sopenharmony_ci ******************************************************************************/
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistruct acpi_namespace_node *acpi_ns_get_next_node_typed(acpi_object_type type,
7362306a36Sopenharmony_ci							struct
7462306a36Sopenharmony_ci							acpi_namespace_node
7562306a36Sopenharmony_ci							*parent_node,
7662306a36Sopenharmony_ci							struct
7762306a36Sopenharmony_ci							acpi_namespace_node
7862306a36Sopenharmony_ci							*child_node)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	struct acpi_namespace_node *next_node = NULL;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	ACPI_FUNCTION_ENTRY();
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	next_node = acpi_ns_get_next_node(parent_node, child_node);
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	/* If any type is OK, we are done */
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	if (type == ACPI_TYPE_ANY) {
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci		/* next_node is NULL if we are at the end-of-list */
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci		return (next_node);
9462306a36Sopenharmony_ci	}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	/* Must search for the node -- but within this scope only */
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	while (next_node) {
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci		/* If type matches, we are done */
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci		if (next_node->type == type) {
10362306a36Sopenharmony_ci			return (next_node);
10462306a36Sopenharmony_ci		}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci		/* Otherwise, move on to the next peer node */
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci		next_node = next_node->peer;
10962306a36Sopenharmony_ci	}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	/* Not found */
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	return (NULL);
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci/*******************************************************************************
11762306a36Sopenharmony_ci *
11862306a36Sopenharmony_ci * FUNCTION:    acpi_ns_walk_namespace
11962306a36Sopenharmony_ci *
12062306a36Sopenharmony_ci * PARAMETERS:  type                - acpi_object_type to search for
12162306a36Sopenharmony_ci *              start_node          - Handle in namespace where search begins
12262306a36Sopenharmony_ci *              max_depth           - Depth to which search is to reach
12362306a36Sopenharmony_ci *              flags               - Whether to unlock the NS before invoking
12462306a36Sopenharmony_ci *                                    the callback routine
12562306a36Sopenharmony_ci *              descending_callback - Called during tree descent
12662306a36Sopenharmony_ci *                                    when an object of "Type" is found
12762306a36Sopenharmony_ci *              ascending_callback  - Called during tree ascent
12862306a36Sopenharmony_ci *                                    when an object of "Type" is found
12962306a36Sopenharmony_ci *              context             - Passed to user function(s) above
13062306a36Sopenharmony_ci *              return_value        - from the user_function if terminated
13162306a36Sopenharmony_ci *                                    early. Otherwise, returns NULL.
13262306a36Sopenharmony_ci * RETURNS:     Status
13362306a36Sopenharmony_ci *
13462306a36Sopenharmony_ci * DESCRIPTION: Performs a modified depth-first walk of the namespace tree,
13562306a36Sopenharmony_ci *              starting (and ending) at the node specified by start_handle.
13662306a36Sopenharmony_ci *              The callback function is called whenever a node that matches
13762306a36Sopenharmony_ci *              the type parameter is found. If the callback function returns
13862306a36Sopenharmony_ci *              a non-zero value, the search is terminated immediately and
13962306a36Sopenharmony_ci *              this value is returned to the caller.
14062306a36Sopenharmony_ci *
14162306a36Sopenharmony_ci *              The point of this procedure is to provide a generic namespace
14262306a36Sopenharmony_ci *              walk routine that can be called from multiple places to
14362306a36Sopenharmony_ci *              provide multiple services; the callback function(s) can be
14462306a36Sopenharmony_ci *              tailored to each task, whether it is a print function,
14562306a36Sopenharmony_ci *              a compare function, etc.
14662306a36Sopenharmony_ci *
14762306a36Sopenharmony_ci ******************************************************************************/
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ciacpi_status
15062306a36Sopenharmony_ciacpi_ns_walk_namespace(acpi_object_type type,
15162306a36Sopenharmony_ci		       acpi_handle start_node,
15262306a36Sopenharmony_ci		       u32 max_depth,
15362306a36Sopenharmony_ci		       u32 flags,
15462306a36Sopenharmony_ci		       acpi_walk_callback descending_callback,
15562306a36Sopenharmony_ci		       acpi_walk_callback ascending_callback,
15662306a36Sopenharmony_ci		       void *context, void **return_value)
15762306a36Sopenharmony_ci{
15862306a36Sopenharmony_ci	acpi_status status;
15962306a36Sopenharmony_ci	acpi_status mutex_status;
16062306a36Sopenharmony_ci	struct acpi_namespace_node *child_node;
16162306a36Sopenharmony_ci	struct acpi_namespace_node *parent_node;
16262306a36Sopenharmony_ci	acpi_object_type child_type;
16362306a36Sopenharmony_ci	u32 level;
16462306a36Sopenharmony_ci	u8 node_previously_visited = FALSE;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	ACPI_FUNCTION_TRACE(ns_walk_namespace);
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	/* Special case for the namespace Root Node */
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	if (start_node == ACPI_ROOT_OBJECT) {
17162306a36Sopenharmony_ci		start_node = acpi_gbl_root_node;
17262306a36Sopenharmony_ci		if (!start_node) {
17362306a36Sopenharmony_ci			return_ACPI_STATUS(AE_NO_NAMESPACE);
17462306a36Sopenharmony_ci		}
17562306a36Sopenharmony_ci	}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	/* Null child means "get first node" */
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	parent_node = start_node;
18062306a36Sopenharmony_ci	child_node = acpi_ns_get_next_node(parent_node, NULL);
18162306a36Sopenharmony_ci	child_type = ACPI_TYPE_ANY;
18262306a36Sopenharmony_ci	level = 1;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	/*
18562306a36Sopenharmony_ci	 * Traverse the tree of nodes until we bubble back up to where we
18662306a36Sopenharmony_ci	 * started. When Level is zero, the loop is done because we have
18762306a36Sopenharmony_ci	 * bubbled up to (and passed) the original parent handle (start_entry)
18862306a36Sopenharmony_ci	 */
18962306a36Sopenharmony_ci	while (level > 0 && child_node) {
19062306a36Sopenharmony_ci		status = AE_OK;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci		/* Found next child, get the type if we are not searching for ANY */
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci		if (type != ACPI_TYPE_ANY) {
19562306a36Sopenharmony_ci			child_type = child_node->type;
19662306a36Sopenharmony_ci		}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci		/*
19962306a36Sopenharmony_ci		 * Ignore all temporary namespace nodes (created during control
20062306a36Sopenharmony_ci		 * method execution) unless told otherwise. These temporary nodes
20162306a36Sopenharmony_ci		 * can cause a race condition because they can be deleted during
20262306a36Sopenharmony_ci		 * the execution of the user function (if the namespace is
20362306a36Sopenharmony_ci		 * unlocked before invocation of the user function.) Only the
20462306a36Sopenharmony_ci		 * debugger namespace dump will examine the temporary nodes.
20562306a36Sopenharmony_ci		 */
20662306a36Sopenharmony_ci		if ((child_node->flags & ANOBJ_TEMPORARY) &&
20762306a36Sopenharmony_ci		    !(flags & ACPI_NS_WALK_TEMP_NODES)) {
20862306a36Sopenharmony_ci			status = AE_CTRL_DEPTH;
20962306a36Sopenharmony_ci		}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci		/* Type must match requested type */
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci		else if (child_type == type) {
21462306a36Sopenharmony_ci			/*
21562306a36Sopenharmony_ci			 * Found a matching node, invoke the user callback function.
21662306a36Sopenharmony_ci			 * Unlock the namespace if flag is set.
21762306a36Sopenharmony_ci			 */
21862306a36Sopenharmony_ci			if (flags & ACPI_NS_WALK_UNLOCK) {
21962306a36Sopenharmony_ci				mutex_status =
22062306a36Sopenharmony_ci				    acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
22162306a36Sopenharmony_ci				if (ACPI_FAILURE(mutex_status)) {
22262306a36Sopenharmony_ci					return_ACPI_STATUS(mutex_status);
22362306a36Sopenharmony_ci				}
22462306a36Sopenharmony_ci			}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci			/*
22762306a36Sopenharmony_ci			 * Invoke the user function, either descending, ascending,
22862306a36Sopenharmony_ci			 * or both.
22962306a36Sopenharmony_ci			 */
23062306a36Sopenharmony_ci			if (!node_previously_visited) {
23162306a36Sopenharmony_ci				if (descending_callback) {
23262306a36Sopenharmony_ci					status =
23362306a36Sopenharmony_ci					    descending_callback(child_node,
23462306a36Sopenharmony_ci								level, context,
23562306a36Sopenharmony_ci								return_value);
23662306a36Sopenharmony_ci				}
23762306a36Sopenharmony_ci			} else {
23862306a36Sopenharmony_ci				if (ascending_callback) {
23962306a36Sopenharmony_ci					status =
24062306a36Sopenharmony_ci					    ascending_callback(child_node,
24162306a36Sopenharmony_ci							       level, context,
24262306a36Sopenharmony_ci							       return_value);
24362306a36Sopenharmony_ci				}
24462306a36Sopenharmony_ci			}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci			if (flags & ACPI_NS_WALK_UNLOCK) {
24762306a36Sopenharmony_ci				mutex_status =
24862306a36Sopenharmony_ci				    acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
24962306a36Sopenharmony_ci				if (ACPI_FAILURE(mutex_status)) {
25062306a36Sopenharmony_ci					return_ACPI_STATUS(mutex_status);
25162306a36Sopenharmony_ci				}
25262306a36Sopenharmony_ci			}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci			switch (status) {
25562306a36Sopenharmony_ci			case AE_OK:
25662306a36Sopenharmony_ci			case AE_CTRL_DEPTH:
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci				/* Just keep going */
25962306a36Sopenharmony_ci				break;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci			case AE_CTRL_TERMINATE:
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci				/* Exit now, with OK status */
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci				return_ACPI_STATUS(AE_OK);
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci			default:
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci				/* All others are valid exceptions */
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci				return_ACPI_STATUS(status);
27262306a36Sopenharmony_ci			}
27362306a36Sopenharmony_ci		}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci		/*
27662306a36Sopenharmony_ci		 * Depth first search: Attempt to go down another level in the
27762306a36Sopenharmony_ci		 * namespace if we are allowed to. Don't go any further if we have
27862306a36Sopenharmony_ci		 * reached the caller specified maximum depth or if the user
27962306a36Sopenharmony_ci		 * function has specified that the maximum depth has been reached.
28062306a36Sopenharmony_ci		 */
28162306a36Sopenharmony_ci		if (!node_previously_visited &&
28262306a36Sopenharmony_ci		    (level < max_depth) && (status != AE_CTRL_DEPTH)) {
28362306a36Sopenharmony_ci			if (child_node->child) {
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci				/* There is at least one child of this node, visit it */
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci				level++;
28862306a36Sopenharmony_ci				parent_node = child_node;
28962306a36Sopenharmony_ci				child_node =
29062306a36Sopenharmony_ci				    acpi_ns_get_next_node(parent_node, NULL);
29162306a36Sopenharmony_ci				continue;
29262306a36Sopenharmony_ci			}
29362306a36Sopenharmony_ci		}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci		/* No more children, re-visit this node */
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci		if (!node_previously_visited) {
29862306a36Sopenharmony_ci			node_previously_visited = TRUE;
29962306a36Sopenharmony_ci			continue;
30062306a36Sopenharmony_ci		}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci		/* No more children, visit peers */
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci		child_node = acpi_ns_get_next_node(parent_node, child_node);
30562306a36Sopenharmony_ci		if (child_node) {
30662306a36Sopenharmony_ci			node_previously_visited = FALSE;
30762306a36Sopenharmony_ci		}
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci		/* No peers, re-visit parent */
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci		else {
31262306a36Sopenharmony_ci			/*
31362306a36Sopenharmony_ci			 * No more children of this node (acpi_ns_get_next_node failed), go
31462306a36Sopenharmony_ci			 * back upwards in the namespace tree to the node's parent.
31562306a36Sopenharmony_ci			 */
31662306a36Sopenharmony_ci			level--;
31762306a36Sopenharmony_ci			child_node = parent_node;
31862306a36Sopenharmony_ci			parent_node = parent_node->parent;
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci			node_previously_visited = TRUE;
32162306a36Sopenharmony_ci		}
32262306a36Sopenharmony_ci	}
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	/* Complete walk, not terminated by user function */
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	return_ACPI_STATUS(AE_OK);
32762306a36Sopenharmony_ci}
328