xref: /rk3399_rockchip-uboot/lib/libfdt/fdt_region.c (revision c3c4c00563abfca63f5b846f4f4f6fc390e58563)
1*c3c4c005SSimon Glass /*
2*c3c4c005SSimon Glass  * libfdt - Flat Device Tree manipulation
3*c3c4c005SSimon Glass  * Copyright (C) 2013 Google, Inc
4*c3c4c005SSimon Glass  * Written by Simon Glass <sjg@chromium.org>
5*c3c4c005SSimon Glass  * SPDX-License-Identifier:	GPL-2.0+ BSD-2-Clause
6*c3c4c005SSimon Glass  */
7*c3c4c005SSimon Glass 
8*c3c4c005SSimon Glass #include "libfdt_env.h"
9*c3c4c005SSimon Glass 
10*c3c4c005SSimon Glass #ifndef USE_HOSTCC
11*c3c4c005SSimon Glass #include <fdt.h>
12*c3c4c005SSimon Glass #include <libfdt.h>
13*c3c4c005SSimon Glass #else
14*c3c4c005SSimon Glass #include "fdt_host.h"
15*c3c4c005SSimon Glass #endif
16*c3c4c005SSimon Glass 
17*c3c4c005SSimon Glass #include "libfdt_internal.h"
18*c3c4c005SSimon Glass 
19*c3c4c005SSimon Glass /**
20*c3c4c005SSimon Glass  * fdt_add_region() - Add a new region to our list
21*c3c4c005SSimon Glass  *
22*c3c4c005SSimon Glass  * The region is added if there is space, but in any case we increment the
23*c3c4c005SSimon Glass  * count. If permitted, and the new region overlaps the last one, we merge
24*c3c4c005SSimon Glass  * them.
25*c3c4c005SSimon Glass  *
26*c3c4c005SSimon Glass  * @info: State information
27*c3c4c005SSimon Glass  * @offset: Start offset of region
28*c3c4c005SSimon Glass  * @size: Size of region
29*c3c4c005SSimon Glass  */
30*c3c4c005SSimon Glass static int fdt_add_region(struct fdt_region_state *info, int offset, int size)
31*c3c4c005SSimon Glass {
32*c3c4c005SSimon Glass 	struct fdt_region *reg;
33*c3c4c005SSimon Glass 
34*c3c4c005SSimon Glass 	reg = info->region ? &info->region[info->count - 1] : NULL;
35*c3c4c005SSimon Glass 	if (info->can_merge && info->count &&
36*c3c4c005SSimon Glass 	    info->count <= info->max_regions &&
37*c3c4c005SSimon Glass 	    reg && offset <= reg->offset + reg->size) {
38*c3c4c005SSimon Glass 		reg->size = offset + size - reg->offset;
39*c3c4c005SSimon Glass 	} else if (info->count++ < info->max_regions) {
40*c3c4c005SSimon Glass 		if (reg) {
41*c3c4c005SSimon Glass 			reg++;
42*c3c4c005SSimon Glass 			reg->offset = offset;
43*c3c4c005SSimon Glass 			reg->size = size;
44*c3c4c005SSimon Glass 		}
45*c3c4c005SSimon Glass 	} else {
46*c3c4c005SSimon Glass 		return -1;
47*c3c4c005SSimon Glass 	}
48*c3c4c005SSimon Glass 
49*c3c4c005SSimon Glass 	return 0;
50*c3c4c005SSimon Glass }
51*c3c4c005SSimon Glass 
52*c3c4c005SSimon Glass static int region_list_contains_offset(struct fdt_region_state *info,
53*c3c4c005SSimon Glass 				       const void *fdt, int target)
54*c3c4c005SSimon Glass {
55*c3c4c005SSimon Glass 	struct fdt_region *reg;
56*c3c4c005SSimon Glass 	int num;
57*c3c4c005SSimon Glass 
58*c3c4c005SSimon Glass 	target += fdt_off_dt_struct(fdt);
59*c3c4c005SSimon Glass 	for (reg = info->region, num = 0; num < info->count; reg++, num++) {
60*c3c4c005SSimon Glass 		if (target >= reg->offset && target < reg->offset + reg->size)
61*c3c4c005SSimon Glass 			return 1;
62*c3c4c005SSimon Glass 	}
63*c3c4c005SSimon Glass 
64*c3c4c005SSimon Glass 	return 0;
65*c3c4c005SSimon Glass }
66*c3c4c005SSimon Glass 
67*c3c4c005SSimon Glass int fdt_add_alias_regions(const void *fdt, struct fdt_region *region, int count,
68*c3c4c005SSimon Glass 			  int max_regions, struct fdt_region_state *info)
69*c3c4c005SSimon Glass {
70*c3c4c005SSimon Glass 	int base = fdt_off_dt_struct(fdt);
71*c3c4c005SSimon Glass 	int node, node_end, offset;
72*c3c4c005SSimon Glass 	int did_alias_header;
73*c3c4c005SSimon Glass 
74*c3c4c005SSimon Glass 	node = fdt_subnode_offset(fdt, 0, "aliases");
75*c3c4c005SSimon Glass 	if (node < 0)
76*c3c4c005SSimon Glass 		return -FDT_ERR_NOTFOUND;
77*c3c4c005SSimon Glass 
78*c3c4c005SSimon Glass 	/* The aliases node must come before the others */
79*c3c4c005SSimon Glass 	node_end = fdt_next_subnode(fdt, node);
80*c3c4c005SSimon Glass 	if (node_end <= 0)
81*c3c4c005SSimon Glass 		return -FDT_ERR_BADLAYOUT;
82*c3c4c005SSimon Glass 	node_end -= sizeof(fdt32_t);
83*c3c4c005SSimon Glass 
84*c3c4c005SSimon Glass 	did_alias_header = 0;
85*c3c4c005SSimon Glass 	info->region = region;
86*c3c4c005SSimon Glass 	info->count = count;
87*c3c4c005SSimon Glass 	info->can_merge = 0;
88*c3c4c005SSimon Glass 	info->max_regions = max_regions;
89*c3c4c005SSimon Glass 
90*c3c4c005SSimon Glass 	for (offset = fdt_first_property_offset(fdt, node);
91*c3c4c005SSimon Glass 	     offset >= 0;
92*c3c4c005SSimon Glass 	     offset = fdt_next_property_offset(fdt, offset)) {
93*c3c4c005SSimon Glass 		const struct fdt_property *prop;
94*c3c4c005SSimon Glass 		const char *name;
95*c3c4c005SSimon Glass 		int target, next;
96*c3c4c005SSimon Glass 
97*c3c4c005SSimon Glass 		prop = fdt_get_property_by_offset(fdt, offset, NULL);
98*c3c4c005SSimon Glass 		name = fdt_string(fdt, fdt32_to_cpu(prop->nameoff));
99*c3c4c005SSimon Glass 		target = fdt_path_offset(fdt, name);
100*c3c4c005SSimon Glass 		if (!region_list_contains_offset(info, fdt, target))
101*c3c4c005SSimon Glass 			continue;
102*c3c4c005SSimon Glass 		next = fdt_next_property_offset(fdt, offset);
103*c3c4c005SSimon Glass 		if (next < 0)
104*c3c4c005SSimon Glass 			next = node_end - sizeof(fdt32_t);
105*c3c4c005SSimon Glass 
106*c3c4c005SSimon Glass 		if (!did_alias_header) {
107*c3c4c005SSimon Glass 			fdt_add_region(info, base + node, 12);
108*c3c4c005SSimon Glass 			did_alias_header = 1;
109*c3c4c005SSimon Glass 		}
110*c3c4c005SSimon Glass 		fdt_add_region(info, base + offset, next - offset);
111*c3c4c005SSimon Glass 	}
112*c3c4c005SSimon Glass 
113*c3c4c005SSimon Glass 	/* Add the 'end' tag */
114*c3c4c005SSimon Glass 	if (did_alias_header)
115*c3c4c005SSimon Glass 		fdt_add_region(info, base + node_end, sizeof(fdt32_t));
116*c3c4c005SSimon Glass 
117*c3c4c005SSimon Glass 	return info->count < max_regions ? info->count : -FDT_ERR_NOSPACE;
118*c3c4c005SSimon Glass }
119*c3c4c005SSimon Glass 
120*c3c4c005SSimon Glass /**
121*c3c4c005SSimon Glass  * fdt_include_supernodes() - Include supernodes required by this node
122*c3c4c005SSimon Glass  *
123*c3c4c005SSimon Glass  * When we decided to include a node or property which is not at the top
124*c3c4c005SSimon Glass  * level, this function forces the inclusion of higher level nodes. For
125*c3c4c005SSimon Glass  * example, given this tree:
126*c3c4c005SSimon Glass  *
127*c3c4c005SSimon Glass  * / {
128*c3c4c005SSimon Glass  *     testing {
129*c3c4c005SSimon Glass  *     }
130*c3c4c005SSimon Glass  * }
131*c3c4c005SSimon Glass  *
132*c3c4c005SSimon Glass  * If we decide to include testing then we need the root node to have a valid
133*c3c4c005SSimon Glass  * tree. This function adds those regions.
134*c3c4c005SSimon Glass  *
135*c3c4c005SSimon Glass  * @info: State information
136*c3c4c005SSimon Glass  * @depth: Current stack depth
137*c3c4c005SSimon Glass  */
138*c3c4c005SSimon Glass static int fdt_include_supernodes(struct fdt_region_state *info, int depth)
139*c3c4c005SSimon Glass {
140*c3c4c005SSimon Glass 	int base = fdt_off_dt_struct(info->fdt);
141*c3c4c005SSimon Glass 	int start, stop_at;
142*c3c4c005SSimon Glass 	int i;
143*c3c4c005SSimon Glass 
144*c3c4c005SSimon Glass 	/*
145*c3c4c005SSimon Glass 	 * Work down the stack looking for supernodes that we didn't include.
146*c3c4c005SSimon Glass 	 * The algortihm here is actually pretty simple, since we know that
147*c3c4c005SSimon Glass 	 * no previous subnode had to include these nodes, or if it did, we
148*c3c4c005SSimon Glass 	 * marked them as included (on the stack) already.
149*c3c4c005SSimon Glass 	 */
150*c3c4c005SSimon Glass 	for (i = 0; i <= depth; i++) {
151*c3c4c005SSimon Glass 		if (!info->stack[i].included) {
152*c3c4c005SSimon Glass 			start = info->stack[i].offset;
153*c3c4c005SSimon Glass 
154*c3c4c005SSimon Glass 			/* Add the FDT_BEGIN_NODE tag of this supernode */
155*c3c4c005SSimon Glass 			fdt_next_tag(info->fdt, start, &stop_at);
156*c3c4c005SSimon Glass 			if (fdt_add_region(info, base + start, stop_at - start))
157*c3c4c005SSimon Glass 				return -1;
158*c3c4c005SSimon Glass 
159*c3c4c005SSimon Glass 			/* Remember that this supernode is now included */
160*c3c4c005SSimon Glass 			info->stack[i].included = 1;
161*c3c4c005SSimon Glass 			info->can_merge = 1;
162*c3c4c005SSimon Glass 		}
163*c3c4c005SSimon Glass 
164*c3c4c005SSimon Glass 		/* Force (later) generation of the FDT_END_NODE tag */
165*c3c4c005SSimon Glass 		if (!info->stack[i].want)
166*c3c4c005SSimon Glass 			info->stack[i].want = WANT_NODES_ONLY;
167*c3c4c005SSimon Glass 	}
168*c3c4c005SSimon Glass 
169*c3c4c005SSimon Glass 	return 0;
170*c3c4c005SSimon Glass }
171*c3c4c005SSimon Glass 
172*c3c4c005SSimon Glass enum {
173*c3c4c005SSimon Glass 	FDT_DONE_NOTHING,
174*c3c4c005SSimon Glass 	FDT_DONE_MEM_RSVMAP,
175*c3c4c005SSimon Glass 	FDT_DONE_STRUCT,
176*c3c4c005SSimon Glass 	FDT_DONE_END,
177*c3c4c005SSimon Glass 	FDT_DONE_STRINGS,
178*c3c4c005SSimon Glass 	FDT_DONE_ALL,
179*c3c4c005SSimon Glass };
180*c3c4c005SSimon Glass 
181*c3c4c005SSimon Glass int fdt_first_region(const void *fdt,
182*c3c4c005SSimon Glass 		int (*h_include)(void *priv, const void *fdt, int offset,
183*c3c4c005SSimon Glass 				 int type, const char *data, int size),
184*c3c4c005SSimon Glass 		void *priv, struct fdt_region *region,
185*c3c4c005SSimon Glass 		char *path, int path_len, int flags,
186*c3c4c005SSimon Glass 		struct fdt_region_state *info)
187*c3c4c005SSimon Glass {
188*c3c4c005SSimon Glass 	struct fdt_region_ptrs *p = &info->ptrs;
189*c3c4c005SSimon Glass 
190*c3c4c005SSimon Glass 	/* Set up our state */
191*c3c4c005SSimon Glass 	info->fdt = fdt;
192*c3c4c005SSimon Glass 	info->can_merge = 1;
193*c3c4c005SSimon Glass 	info->max_regions = 1;
194*c3c4c005SSimon Glass 	info->start = -1;
195*c3c4c005SSimon Glass 	p->want = WANT_NOTHING;
196*c3c4c005SSimon Glass 	p->end = path;
197*c3c4c005SSimon Glass 	*p->end = '\0';
198*c3c4c005SSimon Glass 	p->nextoffset = 0;
199*c3c4c005SSimon Glass 	p->depth = -1;
200*c3c4c005SSimon Glass 	p->done = FDT_DONE_NOTHING;
201*c3c4c005SSimon Glass 
202*c3c4c005SSimon Glass 	return fdt_next_region(fdt, h_include, priv, region,
203*c3c4c005SSimon Glass 			       path, path_len, flags, info);
204*c3c4c005SSimon Glass }
205*c3c4c005SSimon Glass 
206*c3c4c005SSimon Glass /*
207*c3c4c005SSimon Glass  * Theory of operation
208*c3c4c005SSimon Glass  *
209*c3c4c005SSimon Glass  *
210*c3c4c005SSimon Glass  *
211*c3c4c005SSimon Glass 
212*c3c4c005SSimon Glass Note: in this description 'included' means that a node (or other part of
213*c3c4c005SSimon Glass the tree) should be included in the region list, i.e. it will have a region
214*c3c4c005SSimon Glass which covers its part of the tree.
215*c3c4c005SSimon Glass 
216*c3c4c005SSimon Glass This function maintains some state from the last time it is called. It
217*c3c4c005SSimon Glass checks the next part of the tree that it is supposed to look at
218*c3c4c005SSimon Glass (p.nextoffset) to see if that should be included or not. When it finds
219*c3c4c005SSimon Glass something to include, it sets info->start to its offset. This marks the
220*c3c4c005SSimon Glass start of the region we want to include.
221*c3c4c005SSimon Glass 
222*c3c4c005SSimon Glass Once info->start is set to the start (i.e. not -1), we continue scanning
223*c3c4c005SSimon Glass until we find something that we don't want included. This will be the end
224*c3c4c005SSimon Glass of a region. At this point we can close off the region and add it to the
225*c3c4c005SSimon Glass list. So we do so, and reset info->start to -1.
226*c3c4c005SSimon Glass 
227*c3c4c005SSimon Glass One complication here is that we want to merge regions. So when we come to
228*c3c4c005SSimon Glass add another region later, we may in fact merge it with the previous one if
229*c3c4c005SSimon Glass one ends where the other starts.
230*c3c4c005SSimon Glass 
231*c3c4c005SSimon Glass The function fdt_add_region() will return -1 if it fails to add the region,
232*c3c4c005SSimon Glass because we already have a region ready to be returned, and the new one
233*c3c4c005SSimon Glass cannot be merged in with it. In this case, we must return the region we
234*c3c4c005SSimon Glass found, and wait for another call to this function. When it comes, we will
235*c3c4c005SSimon Glass repeat the processing of the tag and again try to add a region. This time it
236*c3c4c005SSimon Glass will succeed.
237*c3c4c005SSimon Glass 
238*c3c4c005SSimon Glass The current state of the pointers (stack, offset, etc.) is maintained in
239*c3c4c005SSimon Glass a ptrs member. At the start of every loop iteration we make a copy of it.
240*c3c4c005SSimon Glass The copy is then updated as the tag is processed. Only if we get to the end
241*c3c4c005SSimon Glass of the loop iteration (and successfully call fdt_add_region() if we need
242*c3c4c005SSimon Glass to) can we commit the changes we have made to these pointers. For example,
243*c3c4c005SSimon Glass if we see an FDT_END_NODE tag we will decrement the depth value. But if we
244*c3c4c005SSimon Glass need to add a region for this tag (let's say because the previous tag is
245*c3c4c005SSimon Glass included and this FDT_END_NODE tag is not included) then we will only commit
246*c3c4c005SSimon Glass the result if we were able to add the region. That allows us to retry again
247*c3c4c005SSimon Glass next time.
248*c3c4c005SSimon Glass 
249*c3c4c005SSimon Glass We keep track of a variable called 'want' which tells us what we want to
250*c3c4c005SSimon Glass include when there is no specific information provided by the h_include
251*c3c4c005SSimon Glass function for a particular property. This basically handles the inclusion of
252*c3c4c005SSimon Glass properties which are pulled in by virtue of the node they are in. So if you
253*c3c4c005SSimon Glass include a node, its properties are also included. In this case 'want' will
254*c3c4c005SSimon Glass be WANT_NODES_AND_PROPS. The FDT_REG_DIRECT_SUBNODES feature also makes use
255*c3c4c005SSimon Glass of 'want'. While we are inside the subnode, 'want' will be set to
256*c3c4c005SSimon Glass WANT_NODES_ONLY, so that only the subnode's FDT_BEGIN_NODE and FDT_END_NODE
257*c3c4c005SSimon Glass tags will be included, and properties will be skipped. If WANT_NOTHING is
258*c3c4c005SSimon Glass selected, then we will just rely on what the h_include() function tells us.
259*c3c4c005SSimon Glass 
260*c3c4c005SSimon Glass Using 'want' we work out 'include', which tells us whether this current tag
261*c3c4c005SSimon Glass should be included or not. As you can imagine, if the value of 'include'
262*c3c4c005SSimon Glass changes, that means we are on a boundary between nodes to include and nodes
263*c3c4c005SSimon Glass to exclude. At this point we either close off a previous region and add it
264*c3c4c005SSimon Glass to the list, or mark the start of a new region.
265*c3c4c005SSimon Glass 
266*c3c4c005SSimon Glass Apart from the nodes, we have mem_rsvmap, the FDT_END tag and the string
267*c3c4c005SSimon Glass list. Each of these dealt with as a whole (i.e. we create a region for each
268*c3c4c005SSimon Glass if it is to be included). For mem_rsvmap we don't allow it to merge with
269*c3c4c005SSimon Glass the first struct region. For the stringlist we don't allow it to merge with
270*c3c4c005SSimon Glass the last struct region (which contains at minimum the FDT_END tag).
271*c3c4c005SSimon Glass */
272*c3c4c005SSimon Glass int fdt_next_region(const void *fdt,
273*c3c4c005SSimon Glass 		int (*h_include)(void *priv, const void *fdt, int offset,
274*c3c4c005SSimon Glass 				 int type, const char *data, int size),
275*c3c4c005SSimon Glass 		void *priv, struct fdt_region *region,
276*c3c4c005SSimon Glass 		char *path, int path_len, int flags,
277*c3c4c005SSimon Glass 		struct fdt_region_state *info)
278*c3c4c005SSimon Glass {
279*c3c4c005SSimon Glass 	int base = fdt_off_dt_struct(fdt);
280*c3c4c005SSimon Glass 	int last_node = 0;
281*c3c4c005SSimon Glass 	const char *str;
282*c3c4c005SSimon Glass 
283*c3c4c005SSimon Glass 	info->region = region;
284*c3c4c005SSimon Glass 	info->count = 0;
285*c3c4c005SSimon Glass 	if (info->ptrs.done < FDT_DONE_MEM_RSVMAP &&
286*c3c4c005SSimon Glass 	    (flags & FDT_REG_ADD_MEM_RSVMAP)) {
287*c3c4c005SSimon Glass 		/* Add the memory reserve map into its own region */
288*c3c4c005SSimon Glass 		if (fdt_add_region(info, fdt_off_mem_rsvmap(fdt),
289*c3c4c005SSimon Glass 				   fdt_off_dt_struct(fdt) -
290*c3c4c005SSimon Glass 				   fdt_off_mem_rsvmap(fdt)))
291*c3c4c005SSimon Glass 			return 0;
292*c3c4c005SSimon Glass 		info->can_merge = 0;	/* Don't allow merging with this */
293*c3c4c005SSimon Glass 		info->ptrs.done = FDT_DONE_MEM_RSVMAP;
294*c3c4c005SSimon Glass 	}
295*c3c4c005SSimon Glass 
296*c3c4c005SSimon Glass 	/*
297*c3c4c005SSimon Glass 	 * Work through the tags one by one, deciding whether each needs to
298*c3c4c005SSimon Glass 	 * be included or not. We set the variable 'include' to indicate our
299*c3c4c005SSimon Glass 	 * decision. 'want' is used to track what we want to include - it
300*c3c4c005SSimon Glass 	 * allows us to pick up all the properties (and/or subnode tags) of
301*c3c4c005SSimon Glass 	 * a node.
302*c3c4c005SSimon Glass 	 */
303*c3c4c005SSimon Glass 	while (info->ptrs.done < FDT_DONE_STRUCT) {
304*c3c4c005SSimon Glass 		const struct fdt_property *prop;
305*c3c4c005SSimon Glass 		struct fdt_region_ptrs p;
306*c3c4c005SSimon Glass 		const char *name;
307*c3c4c005SSimon Glass 		int include = 0;
308*c3c4c005SSimon Glass 		int stop_at = 0;
309*c3c4c005SSimon Glass 		uint32_t tag;
310*c3c4c005SSimon Glass 		int offset;
311*c3c4c005SSimon Glass 		int val;
312*c3c4c005SSimon Glass 		int len;
313*c3c4c005SSimon Glass 
314*c3c4c005SSimon Glass 		/*
315*c3c4c005SSimon Glass 		 * Make a copy of our pointers. If we make it to the end of
316*c3c4c005SSimon Glass 		 * this block then we will commit them back to info->ptrs.
317*c3c4c005SSimon Glass 		 * Otherwise we can try again from the same starting state
318*c3c4c005SSimon Glass 		 * next time we are called.
319*c3c4c005SSimon Glass 		 */
320*c3c4c005SSimon Glass 		p = info->ptrs;
321*c3c4c005SSimon Glass 
322*c3c4c005SSimon Glass 		/*
323*c3c4c005SSimon Glass 		 * Find the tag, and the offset of the next one. If we need to
324*c3c4c005SSimon Glass 		 * stop including tags, then by default we stop *after*
325*c3c4c005SSimon Glass 		 * including the current tag
326*c3c4c005SSimon Glass 		 */
327*c3c4c005SSimon Glass 		offset = p.nextoffset;
328*c3c4c005SSimon Glass 		tag = fdt_next_tag(fdt, offset, &p.nextoffset);
329*c3c4c005SSimon Glass 		stop_at = p.nextoffset;
330*c3c4c005SSimon Glass 
331*c3c4c005SSimon Glass 		switch (tag) {
332*c3c4c005SSimon Glass 		case FDT_PROP:
333*c3c4c005SSimon Glass 			stop_at = offset;
334*c3c4c005SSimon Glass 			prop = fdt_get_property_by_offset(fdt, offset, NULL);
335*c3c4c005SSimon Glass 			str = fdt_string(fdt, fdt32_to_cpu(prop->nameoff));
336*c3c4c005SSimon Glass 			val = h_include(priv, fdt, last_node, FDT_IS_PROP, str,
337*c3c4c005SSimon Glass 					    strlen(str) + 1);
338*c3c4c005SSimon Glass 			if (val == -1) {
339*c3c4c005SSimon Glass 				include = p.want >= WANT_NODES_AND_PROPS;
340*c3c4c005SSimon Glass 			} else {
341*c3c4c005SSimon Glass 				include = val;
342*c3c4c005SSimon Glass 				/*
343*c3c4c005SSimon Glass 				 * Make sure we include the } for this block.
344*c3c4c005SSimon Glass 				 * It might be more correct to have this done
345*c3c4c005SSimon Glass 				 * by the call to fdt_include_supernodes() in
346*c3c4c005SSimon Glass 				 * the case where it adds the node we are
347*c3c4c005SSimon Glass 				 * currently in, but this is equivalent.
348*c3c4c005SSimon Glass 				 */
349*c3c4c005SSimon Glass 				if ((flags & FDT_REG_SUPERNODES) && val &&
350*c3c4c005SSimon Glass 				    !p.want)
351*c3c4c005SSimon Glass 					p.want = WANT_NODES_ONLY;
352*c3c4c005SSimon Glass 			}
353*c3c4c005SSimon Glass 
354*c3c4c005SSimon Glass 			/* Value grepping is not yet supported */
355*c3c4c005SSimon Glass 			break;
356*c3c4c005SSimon Glass 
357*c3c4c005SSimon Glass 		case FDT_NOP:
358*c3c4c005SSimon Glass 			include = p.want >= WANT_NODES_AND_PROPS;
359*c3c4c005SSimon Glass 			stop_at = offset;
360*c3c4c005SSimon Glass 			break;
361*c3c4c005SSimon Glass 
362*c3c4c005SSimon Glass 		case FDT_BEGIN_NODE:
363*c3c4c005SSimon Glass 			last_node = offset;
364*c3c4c005SSimon Glass 			p.depth++;
365*c3c4c005SSimon Glass 			if (p.depth == FDT_MAX_DEPTH)
366*c3c4c005SSimon Glass 				return -FDT_ERR_TOODEEP;
367*c3c4c005SSimon Glass 			name = fdt_get_name(fdt, offset, &len);
368*c3c4c005SSimon Glass 			if (p.end - path + 2 + len >= path_len)
369*c3c4c005SSimon Glass 				return -FDT_ERR_NOSPACE;
370*c3c4c005SSimon Glass 
371*c3c4c005SSimon Glass 			/* Build the full path of this node */
372*c3c4c005SSimon Glass 			if (p.end != path + 1)
373*c3c4c005SSimon Glass 				*p.end++ = '/';
374*c3c4c005SSimon Glass 			strcpy(p.end, name);
375*c3c4c005SSimon Glass 			p.end += len;
376*c3c4c005SSimon Glass 			info->stack[p.depth].want = p.want;
377*c3c4c005SSimon Glass 			info->stack[p.depth].offset = offset;
378*c3c4c005SSimon Glass 
379*c3c4c005SSimon Glass 			/*
380*c3c4c005SSimon Glass 			 * If we are not intending to include this node unless
381*c3c4c005SSimon Glass 			 * it matches, make sure we stop *before* its tag.
382*c3c4c005SSimon Glass 			 */
383*c3c4c005SSimon Glass 			if (p.want == WANT_NODES_ONLY ||
384*c3c4c005SSimon Glass 			    !(flags & (FDT_REG_DIRECT_SUBNODES |
385*c3c4c005SSimon Glass 				       FDT_REG_ALL_SUBNODES))) {
386*c3c4c005SSimon Glass 				stop_at = offset;
387*c3c4c005SSimon Glass 				p.want = WANT_NOTHING;
388*c3c4c005SSimon Glass 			}
389*c3c4c005SSimon Glass 			val = h_include(priv, fdt, offset, FDT_IS_NODE, path,
390*c3c4c005SSimon Glass 					p.end - path + 1);
391*c3c4c005SSimon Glass 
392*c3c4c005SSimon Glass 			/* Include this if requested */
393*c3c4c005SSimon Glass 			if (val) {
394*c3c4c005SSimon Glass 				p.want = (flags & FDT_REG_ALL_SUBNODES) ?
395*c3c4c005SSimon Glass 					WANT_ALL_NODES_AND_PROPS :
396*c3c4c005SSimon Glass 					WANT_NODES_AND_PROPS;
397*c3c4c005SSimon Glass 			}
398*c3c4c005SSimon Glass 
399*c3c4c005SSimon Glass 			/* If not requested, decay our 'p.want' value */
400*c3c4c005SSimon Glass 			else if (p.want) {
401*c3c4c005SSimon Glass 				if (p.want != WANT_ALL_NODES_AND_PROPS)
402*c3c4c005SSimon Glass 					p.want--;
403*c3c4c005SSimon Glass 
404*c3c4c005SSimon Glass 			/* Not including this tag, so stop now */
405*c3c4c005SSimon Glass 			} else {
406*c3c4c005SSimon Glass 				stop_at = offset;
407*c3c4c005SSimon Glass 			}
408*c3c4c005SSimon Glass 
409*c3c4c005SSimon Glass 			/*
410*c3c4c005SSimon Glass 			 * Decide whether to include this tag, and update our
411*c3c4c005SSimon Glass 			 * stack with the state for this node
412*c3c4c005SSimon Glass 			 */
413*c3c4c005SSimon Glass 			include = p.want;
414*c3c4c005SSimon Glass 			info->stack[p.depth].included = include;
415*c3c4c005SSimon Glass 			break;
416*c3c4c005SSimon Glass 
417*c3c4c005SSimon Glass 		case FDT_END_NODE:
418*c3c4c005SSimon Glass 			include = p.want;
419*c3c4c005SSimon Glass 			if (p.depth < 0)
420*c3c4c005SSimon Glass 				return -FDT_ERR_BADSTRUCTURE;
421*c3c4c005SSimon Glass 
422*c3c4c005SSimon Glass 			/*
423*c3c4c005SSimon Glass 			 * If we don't want this node, stop right away, unless
424*c3c4c005SSimon Glass 			 * we are including subnodes
425*c3c4c005SSimon Glass 			 */
426*c3c4c005SSimon Glass 			if (!p.want && !(flags & FDT_REG_DIRECT_SUBNODES))
427*c3c4c005SSimon Glass 				stop_at = offset;
428*c3c4c005SSimon Glass 			p.want = info->stack[p.depth].want;
429*c3c4c005SSimon Glass 			p.depth--;
430*c3c4c005SSimon Glass 			while (p.end > path && *--p.end != '/')
431*c3c4c005SSimon Glass 				;
432*c3c4c005SSimon Glass 			*p.end = '\0';
433*c3c4c005SSimon Glass 			break;
434*c3c4c005SSimon Glass 
435*c3c4c005SSimon Glass 		case FDT_END:
436*c3c4c005SSimon Glass 			/* We always include the end tag */
437*c3c4c005SSimon Glass 			include = 1;
438*c3c4c005SSimon Glass 			p.done = FDT_DONE_STRUCT;
439*c3c4c005SSimon Glass 			break;
440*c3c4c005SSimon Glass 		}
441*c3c4c005SSimon Glass 
442*c3c4c005SSimon Glass 		/* If this tag is to be included, mark it as region start */
443*c3c4c005SSimon Glass 		if (include && info->start == -1) {
444*c3c4c005SSimon Glass 			/* Include any supernodes required by this one */
445*c3c4c005SSimon Glass 			if (flags & FDT_REG_SUPERNODES) {
446*c3c4c005SSimon Glass 				if (fdt_include_supernodes(info, p.depth))
447*c3c4c005SSimon Glass 					return 0;
448*c3c4c005SSimon Glass 			}
449*c3c4c005SSimon Glass 			info->start = offset;
450*c3c4c005SSimon Glass 		}
451*c3c4c005SSimon Glass 
452*c3c4c005SSimon Glass 		/*
453*c3c4c005SSimon Glass 		 * If this tag is not to be included, finish up the current
454*c3c4c005SSimon Glass 		 * region.
455*c3c4c005SSimon Glass 		 */
456*c3c4c005SSimon Glass 		if (!include && info->start != -1) {
457*c3c4c005SSimon Glass 			if (fdt_add_region(info, base + info->start,
458*c3c4c005SSimon Glass 					   stop_at - info->start))
459*c3c4c005SSimon Glass 				return 0;
460*c3c4c005SSimon Glass 			info->start = -1;
461*c3c4c005SSimon Glass 			info->can_merge = 1;
462*c3c4c005SSimon Glass 		}
463*c3c4c005SSimon Glass 
464*c3c4c005SSimon Glass 		/* If we have made it this far, we can commit our pointers */
465*c3c4c005SSimon Glass 		info->ptrs = p;
466*c3c4c005SSimon Glass 	}
467*c3c4c005SSimon Glass 
468*c3c4c005SSimon Glass 	/* Add a region for the END tag and a separate one for string table */
469*c3c4c005SSimon Glass 	if (info->ptrs.done < FDT_DONE_END) {
470*c3c4c005SSimon Glass 		if (info->ptrs.nextoffset != fdt_size_dt_struct(fdt))
471*c3c4c005SSimon Glass 			return -FDT_ERR_BADSTRUCTURE;
472*c3c4c005SSimon Glass 
473*c3c4c005SSimon Glass 		if (fdt_add_region(info, base + info->start,
474*c3c4c005SSimon Glass 				   info->ptrs.nextoffset - info->start))
475*c3c4c005SSimon Glass 			return 0;
476*c3c4c005SSimon Glass 		info->ptrs.done++;
477*c3c4c005SSimon Glass 	}
478*c3c4c005SSimon Glass 	if (info->ptrs.done < FDT_DONE_STRINGS) {
479*c3c4c005SSimon Glass 		if (flags & FDT_REG_ADD_STRING_TAB) {
480*c3c4c005SSimon Glass 			info->can_merge = 0;
481*c3c4c005SSimon Glass 			if (fdt_off_dt_strings(fdt) <
482*c3c4c005SSimon Glass 			    base + info->ptrs.nextoffset)
483*c3c4c005SSimon Glass 				return -FDT_ERR_BADLAYOUT;
484*c3c4c005SSimon Glass 			if (fdt_add_region(info, fdt_off_dt_strings(fdt),
485*c3c4c005SSimon Glass 					   fdt_size_dt_strings(fdt)))
486*c3c4c005SSimon Glass 				return 0;
487*c3c4c005SSimon Glass 		}
488*c3c4c005SSimon Glass 		info->ptrs.done++;
489*c3c4c005SSimon Glass 	}
490*c3c4c005SSimon Glass 
491*c3c4c005SSimon Glass 	return info->count > 0 ? 0 : -FDT_ERR_NOTFOUND;
492*c3c4c005SSimon Glass }
493