xref: /openbmc/linux/drivers/acpi/acpica/nswalk.c (revision 2263576cfc6e8f6ab038126c3254404b9fcb1c33)
195b482a8SLen Brown /******************************************************************************
295b482a8SLen Brown  *
395b482a8SLen Brown  * Module Name: nswalk - Functions for walking the ACPI namespace
495b482a8SLen Brown  *
595b482a8SLen Brown  *****************************************************************************/
695b482a8SLen Brown 
795b482a8SLen Brown /*
895b482a8SLen Brown  * Copyright (C) 2000 - 2008, Intel Corp.
995b482a8SLen Brown  * All rights reserved.
1095b482a8SLen Brown  *
1195b482a8SLen Brown  * Redistribution and use in source and binary forms, with or without
1295b482a8SLen Brown  * modification, are permitted provided that the following conditions
1395b482a8SLen Brown  * are met:
1495b482a8SLen Brown  * 1. Redistributions of source code must retain the above copyright
1595b482a8SLen Brown  *    notice, this list of conditions, and the following disclaimer,
1695b482a8SLen Brown  *    without modification.
1795b482a8SLen Brown  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
1895b482a8SLen Brown  *    substantially similar to the "NO WARRANTY" disclaimer below
1995b482a8SLen Brown  *    ("Disclaimer") and any redistribution must be conditioned upon
2095b482a8SLen Brown  *    including a substantially similar Disclaimer requirement for further
2195b482a8SLen Brown  *    binary redistribution.
2295b482a8SLen Brown  * 3. Neither the names of the above-listed copyright holders nor the names
2395b482a8SLen Brown  *    of any contributors may be used to endorse or promote products derived
2495b482a8SLen Brown  *    from this software without specific prior written permission.
2595b482a8SLen Brown  *
2695b482a8SLen Brown  * Alternatively, this software may be distributed under the terms of the
2795b482a8SLen Brown  * GNU General Public License ("GPL") version 2 as published by the Free
2895b482a8SLen Brown  * Software Foundation.
2995b482a8SLen Brown  *
3095b482a8SLen Brown  * NO WARRANTY
3195b482a8SLen Brown  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
3295b482a8SLen Brown  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
3395b482a8SLen Brown  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
3495b482a8SLen Brown  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
3595b482a8SLen Brown  * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
3695b482a8SLen Brown  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
3795b482a8SLen Brown  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3895b482a8SLen Brown  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
3995b482a8SLen Brown  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
4095b482a8SLen Brown  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
4195b482a8SLen Brown  * POSSIBILITY OF SUCH DAMAGES.
4295b482a8SLen Brown  */
4395b482a8SLen Brown 
4495b482a8SLen Brown #include <acpi/acpi.h>
45e2f7a777SLen Brown #include "accommon.h"
46e2f7a777SLen Brown #include "acnamesp.h"
4795b482a8SLen Brown 
4895b482a8SLen Brown #define _COMPONENT          ACPI_NAMESPACE
4995b482a8SLen Brown ACPI_MODULE_NAME("nswalk")
5095b482a8SLen Brown 
5195b482a8SLen Brown /*******************************************************************************
5295b482a8SLen Brown  *
5395b482a8SLen Brown  * FUNCTION:    acpi_ns_get_next_node
5495b482a8SLen Brown  *
558c725bf9SBob Moore  * PARAMETERS:  parent_node         - Parent node whose children we are
568c725bf9SBob Moore  *                                    getting
578c725bf9SBob Moore  *              child_node          - Previous child that was found.
588c725bf9SBob Moore  *                                    The NEXT child will be returned
598c725bf9SBob Moore  *
608c725bf9SBob Moore  * RETURN:      struct acpi_namespace_node - Pointer to the NEXT child or NULL if
618c725bf9SBob Moore  *                                    none is found.
628c725bf9SBob Moore  *
638c725bf9SBob Moore  * DESCRIPTION: Return the next peer node within the namespace.  If Handle
648c725bf9SBob Moore  *              is valid, Scope is ignored.  Otherwise, the first node
658c725bf9SBob Moore  *              within Scope is returned.
668c725bf9SBob Moore  *
678c725bf9SBob Moore  ******************************************************************************/
688c725bf9SBob Moore struct acpi_namespace_node *acpi_ns_get_next_node(struct acpi_namespace_node
698c725bf9SBob Moore 						  *parent_node,
708c725bf9SBob Moore 						  struct acpi_namespace_node
718c725bf9SBob Moore 						  *child_node)
728c725bf9SBob Moore {
738c725bf9SBob Moore 	ACPI_FUNCTION_ENTRY();
748c725bf9SBob Moore 
758c725bf9SBob Moore 	if (!child_node) {
768c725bf9SBob Moore 
778c725bf9SBob Moore 		/* It's really the parent's _scope_ that we want */
788c725bf9SBob Moore 
798c725bf9SBob Moore 		return parent_node->child;
808c725bf9SBob Moore 	}
818c725bf9SBob Moore 
828c725bf9SBob Moore 	/*
838c725bf9SBob Moore 	 * Get the next node.
848c725bf9SBob Moore 	 *
858c725bf9SBob Moore 	 * If we are at the end of this peer list, return NULL
868c725bf9SBob Moore 	 */
878c725bf9SBob Moore 	if (child_node->flags & ANOBJ_END_OF_PEER_LIST) {
888c725bf9SBob Moore 		return NULL;
898c725bf9SBob Moore 	}
908c725bf9SBob Moore 
918c725bf9SBob Moore 	/* Otherwise just return the next peer */
928c725bf9SBob Moore 
938c725bf9SBob Moore 	return child_node->peer;
948c725bf9SBob Moore }
958c725bf9SBob Moore 
968c725bf9SBob Moore /*******************************************************************************
978c725bf9SBob Moore  *
988c725bf9SBob Moore  * FUNCTION:    acpi_ns_get_next_node_typed
998c725bf9SBob Moore  *
10095b482a8SLen Brown  * PARAMETERS:  Type                - Type of node to be searched for
10195b482a8SLen Brown  *              parent_node         - Parent node whose children we are
10295b482a8SLen Brown  *                                    getting
10395b482a8SLen Brown  *              child_node          - Previous child that was found.
10495b482a8SLen Brown  *                                    The NEXT child will be returned
10595b482a8SLen Brown  *
10695b482a8SLen Brown  * RETURN:      struct acpi_namespace_node - Pointer to the NEXT child or NULL if
10795b482a8SLen Brown  *                                    none is found.
10895b482a8SLen Brown  *
10995b482a8SLen Brown  * DESCRIPTION: Return the next peer node within the namespace.  If Handle
11095b482a8SLen Brown  *              is valid, Scope is ignored.  Otherwise, the first node
11195b482a8SLen Brown  *              within Scope is returned.
11295b482a8SLen Brown  *
11395b482a8SLen Brown  ******************************************************************************/
1148c725bf9SBob Moore 
1158c725bf9SBob Moore struct acpi_namespace_node *acpi_ns_get_next_node_typed(acpi_object_type type,
1168c725bf9SBob Moore 							struct
1178c725bf9SBob Moore 							acpi_namespace_node
1188c725bf9SBob Moore 							*parent_node,
1198c725bf9SBob Moore 							struct
1208c725bf9SBob Moore 							acpi_namespace_node
12195b482a8SLen Brown 							*child_node)
12295b482a8SLen Brown {
12395b482a8SLen Brown 	struct acpi_namespace_node *next_node = NULL;
12495b482a8SLen Brown 
12595b482a8SLen Brown 	ACPI_FUNCTION_ENTRY();
12695b482a8SLen Brown 
1278c725bf9SBob Moore 	next_node = acpi_ns_get_next_node(parent_node, child_node);
12895b482a8SLen Brown 
12995b482a8SLen Brown 
13095b482a8SLen Brown 	/* If any type is OK, we are done */
13195b482a8SLen Brown 
13295b482a8SLen Brown 	if (type == ACPI_TYPE_ANY) {
13395b482a8SLen Brown 
13495b482a8SLen Brown 		/* next_node is NULL if we are at the end-of-list */
13595b482a8SLen Brown 
13695b482a8SLen Brown 		return (next_node);
13795b482a8SLen Brown 	}
13895b482a8SLen Brown 
13995b482a8SLen Brown 	/* Must search for the node -- but within this scope only */
14095b482a8SLen Brown 
14195b482a8SLen Brown 	while (next_node) {
14295b482a8SLen Brown 
14395b482a8SLen Brown 		/* If type matches, we are done */
14495b482a8SLen Brown 
14595b482a8SLen Brown 		if (next_node->type == type) {
14695b482a8SLen Brown 			return (next_node);
14795b482a8SLen Brown 		}
14895b482a8SLen Brown 
14995b482a8SLen Brown 		/* Otherwise, move on to the next node */
15095b482a8SLen Brown 
15195b482a8SLen Brown 		next_node = acpi_ns_get_next_valid_node(next_node);
15295b482a8SLen Brown 	}
15395b482a8SLen Brown 
15495b482a8SLen Brown 	/* Not found */
15595b482a8SLen Brown 
15695b482a8SLen Brown 	return (NULL);
15795b482a8SLen Brown }
15895b482a8SLen Brown 
15995b482a8SLen Brown /*******************************************************************************
16095b482a8SLen Brown  *
16195b482a8SLen Brown  * FUNCTION:    acpi_ns_walk_namespace
16295b482a8SLen Brown  *
16395b482a8SLen Brown  * PARAMETERS:  Type                - acpi_object_type to search for
16495b482a8SLen Brown  *              start_node          - Handle in namespace where search begins
16595b482a8SLen Brown  *              max_depth           - Depth to which search is to reach
16695b482a8SLen Brown  *              Flags               - Whether to unlock the NS before invoking
16795b482a8SLen Brown  *                                    the callback routine
168*2263576cSLin Ming  *              pre_order_visit     - Called during tree pre-order visit
169*2263576cSLin Ming  *                                    when an object of "Type" is found
170*2263576cSLin Ming  *              post_order_visit    - Called during tree post-order visit
171*2263576cSLin Ming  *                                    when an object of "Type" is found
172*2263576cSLin Ming  *              Context             - Passed to user function(s) above
173*2263576cSLin Ming  *              return_value        - from the user_function if terminated
174*2263576cSLin Ming  *                                    early. Otherwise, returns NULL.
17595b482a8SLen Brown  * RETURNS:     Status
17695b482a8SLen Brown  *
17795b482a8SLen Brown  * DESCRIPTION: Performs a modified depth-first walk of the namespace tree,
17895b482a8SLen Brown  *              starting (and ending) at the node specified by start_handle.
179*2263576cSLin Ming  *              The callback function is called whenever a node that matches
180*2263576cSLin Ming  *              the type parameter is found. If the callback function returns
181d4913dc6SBob Moore  *              a non-zero value, the search is terminated immediately and
182d4913dc6SBob Moore  *              this value is returned to the caller.
18395b482a8SLen Brown  *
18495b482a8SLen Brown  *              The point of this procedure is to provide a generic namespace
18595b482a8SLen Brown  *              walk routine that can be called from multiple places to
186*2263576cSLin Ming  *              provide multiple services; the callback function(s) can be
187*2263576cSLin Ming  *              tailored to each task, whether it is a print function,
188*2263576cSLin Ming  *              a compare function, etc.
18995b482a8SLen Brown  *
19095b482a8SLen Brown  ******************************************************************************/
19195b482a8SLen Brown 
19295b482a8SLen Brown acpi_status
19395b482a8SLen Brown acpi_ns_walk_namespace(acpi_object_type type,
19495b482a8SLen Brown 		       acpi_handle start_node,
19595b482a8SLen Brown 		       u32 max_depth,
19695b482a8SLen Brown 		       u32 flags,
197*2263576cSLin Ming 		       acpi_walk_callback pre_order_visit,
198*2263576cSLin Ming 		       acpi_walk_callback post_order_visit,
19995b482a8SLen Brown 		       void *context, void **return_value)
20095b482a8SLen Brown {
20195b482a8SLen Brown 	acpi_status status;
20295b482a8SLen Brown 	acpi_status mutex_status;
20395b482a8SLen Brown 	struct acpi_namespace_node *child_node;
20495b482a8SLen Brown 	struct acpi_namespace_node *parent_node;
20595b482a8SLen Brown 	acpi_object_type child_type;
20695b482a8SLen Brown 	u32 level;
207*2263576cSLin Ming 	u8 node_previously_visited = FALSE;
20895b482a8SLen Brown 
20995b482a8SLen Brown 	ACPI_FUNCTION_TRACE(ns_walk_namespace);
21095b482a8SLen Brown 
21195b482a8SLen Brown 	/* Special case for the namespace Root Node */
21295b482a8SLen Brown 
21395b482a8SLen Brown 	if (start_node == ACPI_ROOT_OBJECT) {
21495b482a8SLen Brown 		start_node = acpi_gbl_root_node;
21595b482a8SLen Brown 	}
21695b482a8SLen Brown 
21795b482a8SLen Brown 	/* Null child means "get first node" */
21895b482a8SLen Brown 
21995b482a8SLen Brown 	parent_node = start_node;
220*2263576cSLin Ming 	child_node = acpi_ns_get_next_node(parent_node, NULL);
22195b482a8SLen Brown 	child_type = ACPI_TYPE_ANY;
22295b482a8SLen Brown 	level = 1;
22395b482a8SLen Brown 
22495b482a8SLen Brown 	/*
22595b482a8SLen Brown 	 * Traverse the tree of nodes until we bubble back up to where we
22695b482a8SLen Brown 	 * started. When Level is zero, the loop is done because we have
22795b482a8SLen Brown 	 * bubbled up to (and passed) the original parent handle (start_entry)
22895b482a8SLen Brown 	 */
229*2263576cSLin Ming 	while (level > 0 && child_node) {
23095b482a8SLen Brown 		status = AE_OK;
23195b482a8SLen Brown 
23295b482a8SLen Brown 		/* Found next child, get the type if we are not searching for ANY */
23395b482a8SLen Brown 
23495b482a8SLen Brown 		if (type != ACPI_TYPE_ANY) {
23595b482a8SLen Brown 			child_type = child_node->type;
23695b482a8SLen Brown 		}
23795b482a8SLen Brown 
23895b482a8SLen Brown 		/*
23995b482a8SLen Brown 		 * Ignore all temporary namespace nodes (created during control
24095b482a8SLen Brown 		 * method execution) unless told otherwise. These temporary nodes
241d4913dc6SBob Moore 		 * can cause a race condition because they can be deleted during
242d4913dc6SBob Moore 		 * the execution of the user function (if the namespace is
243d4913dc6SBob Moore 		 * unlocked before invocation of the user function.) Only the
244d4913dc6SBob Moore 		 * debugger namespace dump will examine the temporary nodes.
24595b482a8SLen Brown 		 */
24695b482a8SLen Brown 		if ((child_node->flags & ANOBJ_TEMPORARY) &&
24795b482a8SLen Brown 		    !(flags & ACPI_NS_WALK_TEMP_NODES)) {
24895b482a8SLen Brown 			status = AE_CTRL_DEPTH;
24995b482a8SLen Brown 		}
25095b482a8SLen Brown 
25195b482a8SLen Brown 		/* Type must match requested type */
25295b482a8SLen Brown 
25395b482a8SLen Brown 		else if (child_type == type) {
25495b482a8SLen Brown 			/*
25595b482a8SLen Brown 			 * Found a matching node, invoke the user callback function.
25695b482a8SLen Brown 			 * Unlock the namespace if flag is set.
25795b482a8SLen Brown 			 */
25895b482a8SLen Brown 			if (flags & ACPI_NS_WALK_UNLOCK) {
25995b482a8SLen Brown 				mutex_status =
260*2263576cSLin Ming 				    acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
26195b482a8SLen Brown 				if (ACPI_FAILURE(mutex_status)) {
262*2263576cSLin Ming 					return_ACPI_STATUS(mutex_status);
26395b482a8SLen Brown 				}
26495b482a8SLen Brown 			}
26595b482a8SLen Brown 
266*2263576cSLin Ming 			/*
267*2263576cSLin Ming 			 * Invoke the user function, either pre-order or post-order
268*2263576cSLin Ming 			 * or both.
269*2263576cSLin Ming 			 */
270*2263576cSLin Ming 			if (!node_previously_visited) {
271*2263576cSLin Ming 				if (pre_order_visit) {
27295b482a8SLen Brown 					status =
273*2263576cSLin Ming 					    pre_order_visit(child_node, level,
274*2263576cSLin Ming 							    context,
27595b482a8SLen Brown 							    return_value);
276*2263576cSLin Ming 				}
277*2263576cSLin Ming 			} else {
278*2263576cSLin Ming 				if (post_order_visit) {
279*2263576cSLin Ming 					status =
280*2263576cSLin Ming 					    post_order_visit(child_node, level,
281*2263576cSLin Ming 							     context,
282*2263576cSLin Ming 							     return_value);
283*2263576cSLin Ming 				}
284*2263576cSLin Ming 			}
28595b482a8SLen Brown 
28695b482a8SLen Brown 			if (flags & ACPI_NS_WALK_UNLOCK) {
28795b482a8SLen Brown 				mutex_status =
288*2263576cSLin Ming 				    acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
28995b482a8SLen Brown 				if (ACPI_FAILURE(mutex_status)) {
290*2263576cSLin Ming 					return_ACPI_STATUS(mutex_status);
29195b482a8SLen Brown 				}
29295b482a8SLen Brown 			}
29395b482a8SLen Brown 
29495b482a8SLen Brown 			switch (status) {
29595b482a8SLen Brown 			case AE_OK:
29695b482a8SLen Brown 			case AE_CTRL_DEPTH:
29795b482a8SLen Brown 
29895b482a8SLen Brown 				/* Just keep going */
29995b482a8SLen Brown 				break;
30095b482a8SLen Brown 
30195b482a8SLen Brown 			case AE_CTRL_TERMINATE:
30295b482a8SLen Brown 
30395b482a8SLen Brown 				/* Exit now, with OK status */
30495b482a8SLen Brown 
30595b482a8SLen Brown 				return_ACPI_STATUS(AE_OK);
30695b482a8SLen Brown 
30795b482a8SLen Brown 			default:
30895b482a8SLen Brown 
30995b482a8SLen Brown 				/* All others are valid exceptions */
31095b482a8SLen Brown 
31195b482a8SLen Brown 				return_ACPI_STATUS(status);
31295b482a8SLen Brown 			}
31395b482a8SLen Brown 		}
31495b482a8SLen Brown 
31595b482a8SLen Brown 		/*
31695b482a8SLen Brown 		 * Depth first search: Attempt to go down another level in the
31795b482a8SLen Brown 		 * namespace if we are allowed to.  Don't go any further if we have
31895b482a8SLen Brown 		 * reached the caller specified maximum depth or if the user
31995b482a8SLen Brown 		 * function has specified that the maximum depth has been reached.
32095b482a8SLen Brown 		 */
321*2263576cSLin Ming 		if (!node_previously_visited &&
322*2263576cSLin Ming 		    (level < max_depth) && (status != AE_CTRL_DEPTH)) {
3238c725bf9SBob Moore 			if (child_node->child) {
32495b482a8SLen Brown 
32595b482a8SLen Brown 				/* There is at least one child of this node, visit it */
32695b482a8SLen Brown 
32795b482a8SLen Brown 				level++;
32895b482a8SLen Brown 				parent_node = child_node;
329*2263576cSLin Ming 				child_node =
330*2263576cSLin Ming 				    acpi_ns_get_next_node(parent_node, NULL);
331*2263576cSLin Ming 				continue;
33295b482a8SLen Brown 			}
33395b482a8SLen Brown 		}
334*2263576cSLin Ming 
335*2263576cSLin Ming 		/* No more children, re-visit this node */
336*2263576cSLin Ming 
337*2263576cSLin Ming 		if (!node_previously_visited) {
338*2263576cSLin Ming 			node_previously_visited = TRUE;
339*2263576cSLin Ming 			continue;
340*2263576cSLin Ming 		}
341*2263576cSLin Ming 
342*2263576cSLin Ming 		/* No more children, visit peers */
343*2263576cSLin Ming 
344*2263576cSLin Ming 		child_node = acpi_ns_get_next_node(parent_node, child_node);
345*2263576cSLin Ming 		if (child_node) {
346*2263576cSLin Ming 			node_previously_visited = FALSE;
347*2263576cSLin Ming 		}
348*2263576cSLin Ming 
349*2263576cSLin Ming 		/* No peers, re-visit parent */
350*2263576cSLin Ming 
351*2263576cSLin Ming 		else {
35295b482a8SLen Brown 			/*
35395b482a8SLen Brown 			 * No more children of this node (acpi_ns_get_next_node failed), go
35495b482a8SLen Brown 			 * back upwards in the namespace tree to the node's parent.
35595b482a8SLen Brown 			 */
35695b482a8SLen Brown 			level--;
35795b482a8SLen Brown 			child_node = parent_node;
35895b482a8SLen Brown 			parent_node = acpi_ns_get_parent_node(parent_node);
359*2263576cSLin Ming 
360*2263576cSLin Ming 			node_previously_visited = TRUE;
36195b482a8SLen Brown 		}
36295b482a8SLen Brown 	}
36395b482a8SLen Brown 
36495b482a8SLen Brown 	/* Complete walk, not terminated by user function */
36595b482a8SLen Brown 
36695b482a8SLen Brown 	return_ACPI_STATUS(AE_OK);
36795b482a8SLen Brown }
368