1204431Sraj/*
2204431Sraj * libfdt - Flat Device Tree manipulation
3204431Sraj * Copyright (C) 2006 David Gibson, IBM Corporation.
4204431Sraj *
5204431Sraj * libfdt is dual licensed: you can use it either under the terms of
6204431Sraj * the GPL, or the BSD license, at your option.
7204431Sraj *
8204431Sraj *  a) This library is free software; you can redistribute it and/or
9204431Sraj *     modify it under the terms of the GNU General Public License as
10204431Sraj *     published by the Free Software Foundation; either version 2 of the
11204431Sraj *     License, or (at your option) any later version.
12204431Sraj *
13204431Sraj *     This library is distributed in the hope that it will be useful,
14204431Sraj *     but WITHOUT ANY WARRANTY; without even the implied warranty of
15204431Sraj *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16204431Sraj *     GNU General Public License for more details.
17204431Sraj *
18204431Sraj *     You should have received a copy of the GNU General Public
19204431Sraj *     License along with this library; if not, write to the Free
20204431Sraj *     Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
21204431Sraj *     MA 02110-1301 USA
22204431Sraj *
23204431Sraj * Alternatively,
24204431Sraj *
25204431Sraj *  b) Redistribution and use in source and binary forms, with or
26204431Sraj *     without modification, are permitted provided that the following
27204431Sraj *     conditions are met:
28204431Sraj *
29204431Sraj *     1. Redistributions of source code must retain the above
30204431Sraj *        copyright notice, this list of conditions and the following
31204431Sraj *        disclaimer.
32204431Sraj *     2. Redistributions in binary form must reproduce the above
33204431Sraj *        copyright notice, this list of conditions and the following
34204431Sraj *        disclaimer in the documentation and/or other materials
35204431Sraj *        provided with the distribution.
36204431Sraj *
37204431Sraj *     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
38204431Sraj *     CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
39204431Sraj *     INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
40204431Sraj *     MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
41204431Sraj *     DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
42204431Sraj *     CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
43204431Sraj *     SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
44204431Sraj *     NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
45204431Sraj *     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
46204431Sraj *     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
47204431Sraj *     CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
48204431Sraj *     OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
49204431Sraj *     EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
50204431Sraj */
51204431Sraj#include "libfdt_env.h"
52204431Sraj
53204431Sraj#include <fdt.h>
54204431Sraj#include <libfdt.h>
55204431Sraj
56204431Sraj#include "libfdt_internal.h"
57204431Sraj
58328459Skevansstatic int fdt_nodename_eq_(const void *fdt, int offset,
59204431Sraj			    const char *s, int len)
60204431Sraj{
61328824Skevans	int olen;
62328824Skevans	const char *p = fdt_get_name(fdt, offset, &olen);
63204431Sraj
64328824Skevans	if (!p || olen < len)
65204431Sraj		/* short match */
66204431Sraj		return 0;
67204431Sraj
68204431Sraj	if (memcmp(p, s, len) != 0)
69204431Sraj		return 0;
70204431Sraj
71204431Sraj	if (p[len] == '\0')
72204431Sraj		return 1;
73204431Sraj	else if (!memchr(s, '@', len) && (p[len] == '@'))
74204431Sraj		return 1;
75204431Sraj	else
76204431Sraj		return 0;
77204431Sraj}
78204431Sraj
79204431Srajconst char *fdt_string(const void *fdt, int stroffset)
80204431Sraj{
81204431Sraj	return (const char *)fdt + fdt_off_dt_strings(fdt) + stroffset;
82204431Sraj}
83204431Sraj
84328459Skevansstatic int fdt_string_eq_(const void *fdt, int stroffset,
85204433Sraj			  const char *s, int len)
86204433Sraj{
87204433Sraj	const char *p = fdt_string(fdt, stroffset);
88204433Sraj
89204433Sraj	return (strlen(p) == len) && (memcmp(p, s, len) == 0);
90204433Sraj}
91204433Sraj
92328459Skevansuint32_t fdt_get_max_phandle(const void *fdt)
93328459Skevans{
94328459Skevans	uint32_t max_phandle = 0;
95328459Skevans	int offset;
96328459Skevans
97328459Skevans	for (offset = fdt_next_node(fdt, -1, NULL);;
98328459Skevans	     offset = fdt_next_node(fdt, offset, NULL)) {
99328459Skevans		uint32_t phandle;
100328459Skevans
101328459Skevans		if (offset == -FDT_ERR_NOTFOUND)
102328459Skevans			return max_phandle;
103328459Skevans
104328459Skevans		if (offset < 0)
105328459Skevans			return (uint32_t)-1;
106328459Skevans
107328459Skevans		phandle = fdt_get_phandle(fdt, offset);
108328459Skevans		if (phandle == (uint32_t)-1)
109328459Skevans			continue;
110328459Skevans
111328459Skevans		if (phandle > max_phandle)
112328459Skevans			max_phandle = phandle;
113328459Skevans	}
114328459Skevans
115328459Skevans	return 0;
116328459Skevans}
117328459Skevans
118204431Srajint fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size)
119204431Sraj{
120204431Sraj	FDT_CHECK_HEADER(fdt);
121328459Skevans	*address = fdt64_to_cpu(fdt_mem_rsv_(fdt, n)->address);
122328459Skevans	*size = fdt64_to_cpu(fdt_mem_rsv_(fdt, n)->size);
123204431Sraj	return 0;
124204431Sraj}
125204431Sraj
126204431Srajint fdt_num_mem_rsv(const void *fdt)
127204431Sraj{
128204431Sraj	int i = 0;
129204431Sraj
130328459Skevans	while (fdt64_to_cpu(fdt_mem_rsv_(fdt, i)->size) != 0)
131204431Sraj		i++;
132204431Sraj	return i;
133204431Sraj}
134204431Sraj
135328459Skevansstatic int nextprop_(const void *fdt, int offset)
136238742Simp{
137238742Simp	uint32_t tag;
138238742Simp	int nextoffset;
139238742Simp
140238742Simp	do {
141238742Simp		tag = fdt_next_tag(fdt, offset, &nextoffset);
142238742Simp
143238742Simp		switch (tag) {
144238742Simp		case FDT_END:
145238742Simp			if (nextoffset >= 0)
146238742Simp				return -FDT_ERR_BADSTRUCTURE;
147238742Simp			else
148238742Simp				return nextoffset;
149238742Simp
150238742Simp		case FDT_PROP:
151238742Simp			return offset;
152238742Simp		}
153238742Simp		offset = nextoffset;
154238742Simp	} while (tag == FDT_NOP);
155238742Simp
156238742Simp	return -FDT_ERR_NOTFOUND;
157238742Simp}
158238742Simp
159204431Srajint fdt_subnode_offset_namelen(const void *fdt, int offset,
160204431Sraj			       const char *name, int namelen)
161204431Sraj{
162204431Sraj	int depth;
163204431Sraj
164204431Sraj	FDT_CHECK_HEADER(fdt);
165204431Sraj
166204431Sraj	for (depth = 0;
167204433Sraj	     (offset >= 0) && (depth >= 0);
168204433Sraj	     offset = fdt_next_node(fdt, offset, &depth))
169204433Sraj		if ((depth == 1)
170328459Skevans		    && fdt_nodename_eq_(fdt, offset, name, namelen))
171204431Sraj			return offset;
172204431Sraj
173204433Sraj	if (depth < 0)
174204433Sraj		return -FDT_ERR_NOTFOUND;
175204431Sraj	return offset; /* error */
176204431Sraj}
177204431Sraj
178204431Srajint fdt_subnode_offset(const void *fdt, int parentoffset,
179204431Sraj		       const char *name)
180204431Sraj{
181204431Sraj	return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name));
182204431Sraj}
183204431Sraj
184328459Skevansint fdt_path_offset_namelen(const void *fdt, const char *path, int namelen)
185204431Sraj{
186328459Skevans	const char *end = path + namelen;
187204431Sraj	const char *p = path;
188204431Sraj	int offset = 0;
189204431Sraj
190204431Sraj	FDT_CHECK_HEADER(fdt);
191204431Sraj
192204433Sraj	/* see if we have an alias */
193204433Sraj	if (*path != '/') {
194328459Skevans		const char *q = memchr(path, '/', end - p);
195204431Sraj
196204433Sraj		if (!q)
197204433Sraj			q = end;
198204433Sraj
199204433Sraj		p = fdt_get_alias_namelen(fdt, p, q - p);
200204433Sraj		if (!p)
201204433Sraj			return -FDT_ERR_BADPATH;
202204433Sraj		offset = fdt_path_offset(fdt, p);
203204433Sraj
204204433Sraj		p = q;
205204433Sraj	}
206204433Sraj
207328459Skevans	while (p < end) {
208204431Sraj		const char *q;
209204431Sraj
210328459Skevans		while (*p == '/') {
211204431Sraj			p++;
212328459Skevans			if (p == end)
213328459Skevans				return offset;
214328459Skevans		}
215328459Skevans		q = memchr(p, '/', end - p);
216204431Sraj		if (! q)
217204431Sraj			q = end;
218204431Sraj
219204431Sraj		offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p);
220204431Sraj		if (offset < 0)
221204431Sraj			return offset;
222204431Sraj
223204431Sraj		p = q;
224204431Sraj	}
225204431Sraj
226204431Sraj	return offset;
227204431Sraj}
228204431Sraj
229328459Skevansint fdt_path_offset(const void *fdt, const char *path)
230328459Skevans{
231328459Skevans	return fdt_path_offset_namelen(fdt, path, strlen(path));
232328459Skevans}
233328459Skevans
234204431Srajconst char *fdt_get_name(const void *fdt, int nodeoffset, int *len)
235204431Sraj{
236328459Skevans	const struct fdt_node_header *nh = fdt_offset_ptr_(fdt, nodeoffset);
237328824Skevans	const char *nameptr;
238204431Sraj	int err;
239204431Sraj
240204431Sraj	if (((err = fdt_check_header(fdt)) != 0)
241328459Skevans	    || ((err = fdt_check_node_offset_(fdt, nodeoffset)) < 0))
242204431Sraj			goto fail;
243204431Sraj
244328824Skevans	nameptr = nh->name;
245328824Skevans
246328824Skevans	if (fdt_version(fdt) < 0x10) {
247328824Skevans		/*
248328824Skevans		 * For old FDT versions, match the naming conventions of V16:
249328824Skevans		 * give only the leaf name (after all /). The actual tree
250328824Skevans		 * contents are loosely checked.
251328824Skevans		 */
252328824Skevans		const char *leaf;
253328824Skevans		leaf = strrchr(nameptr, '/');
254328824Skevans		if (leaf == NULL) {
255328824Skevans			err = -FDT_ERR_BADSTRUCTURE;
256328824Skevans			goto fail;
257328824Skevans		}
258328824Skevans		nameptr = leaf+1;
259328824Skevans	}
260328824Skevans
261204431Sraj	if (len)
262328824Skevans		*len = strlen(nameptr);
263204431Sraj
264328824Skevans	return nameptr;
265204431Sraj
266204431Sraj fail:
267204431Sraj	if (len)
268204431Sraj		*len = err;
269204431Sraj	return NULL;
270204431Sraj}
271204431Sraj
272238742Simpint fdt_first_property_offset(const void *fdt, int nodeoffset)
273204431Sraj{
274238742Simp	int offset;
275238742Simp
276328459Skevans	if ((offset = fdt_check_node_offset_(fdt, nodeoffset)) < 0)
277238742Simp		return offset;
278238742Simp
279328459Skevans	return nextprop_(fdt, offset);
280238742Simp}
281238742Simp
282238742Simpint fdt_next_property_offset(const void *fdt, int offset)
283238742Simp{
284328459Skevans	if ((offset = fdt_check_prop_offset_(fdt, offset)) < 0)
285238742Simp		return offset;
286238742Simp
287328459Skevans	return nextprop_(fdt, offset);
288238742Simp}
289238742Simp
290328824Skevansstatic const struct fdt_property *fdt_get_property_by_offset_(const void *fdt,
291328824Skevans						              int offset,
292328824Skevans						              int *lenp)
293238742Simp{
294238742Simp	int err;
295204431Sraj	const struct fdt_property *prop;
296204431Sraj
297328459Skevans	if ((err = fdt_check_prop_offset_(fdt, offset)) < 0) {
298238742Simp		if (lenp)
299238742Simp			*lenp = err;
300238742Simp		return NULL;
301238742Simp	}
302204431Sraj
303328459Skevans	prop = fdt_offset_ptr_(fdt, offset);
304204431Sraj
305238742Simp	if (lenp)
306238742Simp		*lenp = fdt32_to_cpu(prop->len);
307204431Sraj
308238742Simp	return prop;
309238742Simp}
310204431Sraj
311328824Skevansconst struct fdt_property *fdt_get_property_by_offset(const void *fdt,
312328824Skevans						      int offset,
313328824Skevans						      int *lenp)
314238742Simp{
315328824Skevans	/* Prior to version 16, properties may need realignment
316328824Skevans	 * and this API does not work. fdt_getprop_*() will, however. */
317328824Skevans
318328824Skevans	if (fdt_version(fdt) < 0x10) {
319328824Skevans		if (lenp)
320328824Skevans			*lenp = -FDT_ERR_BADVERSION;
321328824Skevans		return NULL;
322328824Skevans	}
323328824Skevans
324328824Skevans	return fdt_get_property_by_offset_(fdt, offset, lenp);
325328824Skevans}
326328824Skevans
327328824Skevansstatic const struct fdt_property *fdt_get_property_namelen_(const void *fdt,
328328824Skevans						            int offset,
329328824Skevans						            const char *name,
330328824Skevans						            int namelen,
331328824Skevans							    int *lenp,
332328824Skevans							    int *poffset)
333328824Skevans{
334238742Simp	for (offset = fdt_first_property_offset(fdt, offset);
335238742Simp	     (offset >= 0);
336238742Simp	     (offset = fdt_next_property_offset(fdt, offset))) {
337238742Simp		const struct fdt_property *prop;
338238742Simp
339328824Skevans		if (!(prop = fdt_get_property_by_offset_(fdt, offset, lenp))) {
340238742Simp			offset = -FDT_ERR_INTERNAL;
341204431Sraj			break;
342204431Sraj		}
343328459Skevans		if (fdt_string_eq_(fdt, fdt32_to_cpu(prop->nameoff),
344328824Skevans				   name, namelen)) {
345328824Skevans			if (poffset)
346328824Skevans				*poffset = offset;
347238742Simp			return prop;
348328824Skevans		}
349238742Simp	}
350204431Sraj
351204431Sraj	if (lenp)
352238742Simp		*lenp = offset;
353204431Sraj	return NULL;
354204431Sraj}
355204431Sraj
356328824Skevans
357328824Skevansconst struct fdt_property *fdt_get_property_namelen(const void *fdt,
358328824Skevans						    int offset,
359328824Skevans						    const char *name,
360328824Skevans						    int namelen, int *lenp)
361328824Skevans{
362328824Skevans	/* Prior to version 16, properties may need realignment
363328824Skevans	 * and this API does not work. fdt_getprop_*() will, however. */
364328824Skevans	if (fdt_version(fdt) < 0x10) {
365328824Skevans		if (lenp)
366328824Skevans			*lenp = -FDT_ERR_BADVERSION;
367328824Skevans		return NULL;
368328824Skevans	}
369328824Skevans
370328824Skevans	return fdt_get_property_namelen_(fdt, offset, name, namelen, lenp,
371328824Skevans					 NULL);
372328824Skevans}
373328824Skevans
374328824Skevans
375204433Srajconst struct fdt_property *fdt_get_property(const void *fdt,
376204433Sraj					    int nodeoffset,
377204433Sraj					    const char *name, int *lenp)
378204431Sraj{
379204433Sraj	return fdt_get_property_namelen(fdt, nodeoffset, name,
380204433Sraj					strlen(name), lenp);
381204433Sraj}
382204433Sraj
383204433Srajconst void *fdt_getprop_namelen(const void *fdt, int nodeoffset,
384204433Sraj				const char *name, int namelen, int *lenp)
385204433Sraj{
386328824Skevans	int poffset;
387204431Sraj	const struct fdt_property *prop;
388204431Sraj
389328824Skevans	prop = fdt_get_property_namelen_(fdt, nodeoffset, name, namelen, lenp,
390328824Skevans					 &poffset);
391328459Skevans	if (!prop)
392204431Sraj		return NULL;
393204431Sraj
394328824Skevans	/* Handle realignment */
395328824Skevans	if (fdt_version(fdt) < 0x10 && (poffset + sizeof(*prop)) % 8 &&
396328824Skevans	    fdt32_to_cpu(prop->len) >= 8)
397328824Skevans		return prop->data + 4;
398204431Sraj	return prop->data;
399204431Sraj}
400204431Sraj
401238742Simpconst void *fdt_getprop_by_offset(const void *fdt, int offset,
402238742Simp				  const char **namep, int *lenp)
403238742Simp{
404238742Simp	const struct fdt_property *prop;
405238742Simp
406328824Skevans	prop = fdt_get_property_by_offset_(fdt, offset, lenp);
407238742Simp	if (!prop)
408238742Simp		return NULL;
409238742Simp	if (namep)
410238742Simp		*namep = fdt_string(fdt, fdt32_to_cpu(prop->nameoff));
411328824Skevans
412328824Skevans	/* Handle realignment */
413328824Skevans	if (fdt_version(fdt) < 0x10 && (offset + sizeof(*prop)) % 8 &&
414328824Skevans	    fdt32_to_cpu(prop->len) >= 8)
415328824Skevans		return prop->data + 4;
416238742Simp	return prop->data;
417238742Simp}
418238742Simp
419204433Srajconst void *fdt_getprop(const void *fdt, int nodeoffset,
420204433Sraj			const char *name, int *lenp)
421204433Sraj{
422204433Sraj	return fdt_getprop_namelen(fdt, nodeoffset, name, strlen(name), lenp);
423204433Sraj}
424204433Sraj
425204431Srajuint32_t fdt_get_phandle(const void *fdt, int nodeoffset)
426204431Sraj{
427328459Skevans	const fdt32_t *php;
428204431Sraj	int len;
429204431Sraj
430204433Sraj	/* FIXME: This is a bit sub-optimal, since we potentially scan
431204433Sraj	 * over all the properties twice. */
432204433Sraj	php = fdt_getprop(fdt, nodeoffset, "phandle", &len);
433204433Sraj	if (!php || (len != sizeof(*php))) {
434204433Sraj		php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len);
435204433Sraj		if (!php || (len != sizeof(*php)))
436204433Sraj			return 0;
437204433Sraj	}
438204431Sraj
439204431Sraj	return fdt32_to_cpu(*php);
440204431Sraj}
441204431Sraj
442204433Srajconst char *fdt_get_alias_namelen(const void *fdt,
443204433Sraj				  const char *name, int namelen)
444204433Sraj{
445204433Sraj	int aliasoffset;
446204433Sraj
447204433Sraj	aliasoffset = fdt_path_offset(fdt, "/aliases");
448204433Sraj	if (aliasoffset < 0)
449204433Sraj		return NULL;
450204433Sraj
451204433Sraj	return fdt_getprop_namelen(fdt, aliasoffset, name, namelen, NULL);
452204433Sraj}
453204433Sraj
454204433Srajconst char *fdt_get_alias(const void *fdt, const char *name)
455204433Sraj{
456204433Sraj	return fdt_get_alias_namelen(fdt, name, strlen(name));
457204433Sraj}
458204433Sraj
459204431Srajint fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen)
460204431Sraj{
461204431Sraj	int pdepth = 0, p = 0;
462204431Sraj	int offset, depth, namelen;
463204431Sraj	const char *name;
464204431Sraj
465204431Sraj	FDT_CHECK_HEADER(fdt);
466204431Sraj
467204431Sraj	if (buflen < 2)
468204431Sraj		return -FDT_ERR_NOSPACE;
469204431Sraj
470204431Sraj	for (offset = 0, depth = 0;
471204431Sraj	     (offset >= 0) && (offset <= nodeoffset);
472204431Sraj	     offset = fdt_next_node(fdt, offset, &depth)) {
473204431Sraj		while (pdepth > depth) {
474204431Sraj			do {
475204431Sraj				p--;
476204431Sraj			} while (buf[p-1] != '/');
477204431Sraj			pdepth--;
478204431Sraj		}
479204431Sraj
480204433Sraj		if (pdepth >= depth) {
481204433Sraj			name = fdt_get_name(fdt, offset, &namelen);
482204433Sraj			if (!name)
483204433Sraj				return namelen;
484204433Sraj			if ((p + namelen + 1) <= buflen) {
485204433Sraj				memcpy(buf + p, name, namelen);
486204433Sraj				p += namelen;
487204433Sraj				buf[p++] = '/';
488204433Sraj				pdepth++;
489204433Sraj			}
490204431Sraj		}
491204431Sraj
492204431Sraj		if (offset == nodeoffset) {
493204431Sraj			if (pdepth < (depth + 1))
494204431Sraj				return -FDT_ERR_NOSPACE;
495204431Sraj
496204431Sraj			if (p > 1) /* special case so that root path is "/", not "" */
497204431Sraj				p--;
498204431Sraj			buf[p] = '\0';
499204433Sraj			return 0;
500204431Sraj		}
501204431Sraj	}
502204431Sraj
503204431Sraj	if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
504204431Sraj		return -FDT_ERR_BADOFFSET;
505204431Sraj	else if (offset == -FDT_ERR_BADOFFSET)
506204431Sraj		return -FDT_ERR_BADSTRUCTURE;
507204431Sraj
508204431Sraj	return offset; /* error from fdt_next_node() */
509204431Sraj}
510204431Sraj
511204431Srajint fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset,
512204431Sraj				 int supernodedepth, int *nodedepth)
513204431Sraj{
514204431Sraj	int offset, depth;
515204431Sraj	int supernodeoffset = -FDT_ERR_INTERNAL;
516204431Sraj
517204431Sraj	FDT_CHECK_HEADER(fdt);
518204431Sraj
519204431Sraj	if (supernodedepth < 0)
520204431Sraj		return -FDT_ERR_NOTFOUND;
521204431Sraj
522204431Sraj	for (offset = 0, depth = 0;
523204431Sraj	     (offset >= 0) && (offset <= nodeoffset);
524204431Sraj	     offset = fdt_next_node(fdt, offset, &depth)) {
525204431Sraj		if (depth == supernodedepth)
526204431Sraj			supernodeoffset = offset;
527204431Sraj
528204431Sraj		if (offset == nodeoffset) {
529204431Sraj			if (nodedepth)
530204431Sraj				*nodedepth = depth;
531204431Sraj
532204431Sraj			if (supernodedepth > depth)
533204431Sraj				return -FDT_ERR_NOTFOUND;
534204431Sraj			else
535204431Sraj				return supernodeoffset;
536204431Sraj		}
537204431Sraj	}
538204431Sraj
539204431Sraj	if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
540204431Sraj		return -FDT_ERR_BADOFFSET;
541204431Sraj	else if (offset == -FDT_ERR_BADOFFSET)
542204431Sraj		return -FDT_ERR_BADSTRUCTURE;
543204431Sraj
544204431Sraj	return offset; /* error from fdt_next_node() */
545204431Sraj}
546204431Sraj
547204431Srajint fdt_node_depth(const void *fdt, int nodeoffset)
548204431Sraj{
549204431Sraj	int nodedepth;
550204431Sraj	int err;
551204431Sraj
552204431Sraj	err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth);
553204431Sraj	if (err)
554204431Sraj		return (err < 0) ? err : -FDT_ERR_INTERNAL;
555204431Sraj	return nodedepth;
556204431Sraj}
557204431Sraj
558204431Srajint fdt_parent_offset(const void *fdt, int nodeoffset)
559204431Sraj{
560204431Sraj	int nodedepth = fdt_node_depth(fdt, nodeoffset);
561204431Sraj
562204431Sraj	if (nodedepth < 0)
563204431Sraj		return nodedepth;
564204431Sraj	return fdt_supernode_atdepth_offset(fdt, nodeoffset,
565204431Sraj					    nodedepth - 1, NULL);
566204431Sraj}
567204431Sraj
568204431Srajint fdt_node_offset_by_prop_value(const void *fdt, int startoffset,
569204431Sraj				  const char *propname,
570204431Sraj				  const void *propval, int proplen)
571204431Sraj{
572204431Sraj	int offset;
573204431Sraj	const void *val;
574204431Sraj	int len;
575204431Sraj
576204431Sraj	FDT_CHECK_HEADER(fdt);
577204431Sraj
578204431Sraj	/* FIXME: The algorithm here is pretty horrible: we scan each
579204431Sraj	 * property of a node in fdt_getprop(), then if that didn't
580204431Sraj	 * find what we want, we scan over them again making our way
581204431Sraj	 * to the next node.  Still it's the easiest to implement
582204431Sraj	 * approach; performance can come later. */
583204431Sraj	for (offset = fdt_next_node(fdt, startoffset, NULL);
584204431Sraj	     offset >= 0;
585204431Sraj	     offset = fdt_next_node(fdt, offset, NULL)) {
586204431Sraj		val = fdt_getprop(fdt, offset, propname, &len);
587204431Sraj		if (val && (len == proplen)
588204431Sraj		    && (memcmp(val, propval, len) == 0))
589204431Sraj			return offset;
590204431Sraj	}
591204431Sraj
592204431Sraj	return offset; /* error from fdt_next_node() */
593204431Sraj}
594204431Sraj
595204431Srajint fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle)
596204431Sraj{
597204433Sraj	int offset;
598204433Sraj
599204431Sraj	if ((phandle == 0) || (phandle == -1))
600204431Sraj		return -FDT_ERR_BADPHANDLE;
601204433Sraj
602204433Sraj	FDT_CHECK_HEADER(fdt);
603204433Sraj
604204433Sraj	/* FIXME: The algorithm here is pretty horrible: we
605204433Sraj	 * potentially scan each property of a node in
606204433Sraj	 * fdt_get_phandle(), then if that didn't find what
607204433Sraj	 * we want, we scan over them again making our way to the next
608204433Sraj	 * node.  Still it's the easiest to implement approach;
609204433Sraj	 * performance can come later. */
610204433Sraj	for (offset = fdt_next_node(fdt, -1, NULL);
611204433Sraj	     offset >= 0;
612204433Sraj	     offset = fdt_next_node(fdt, offset, NULL)) {
613204433Sraj		if (fdt_get_phandle(fdt, offset) == phandle)
614204433Sraj			return offset;
615204433Sraj	}
616204433Sraj
617204433Sraj	return offset; /* error from fdt_next_node() */
618204431Sraj}
619204431Sraj
620328459Skevansint fdt_stringlist_contains(const char *strlist, int listlen, const char *str)
621204431Sraj{
622204431Sraj	int len = strlen(str);
623204431Sraj	const char *p;
624204431Sraj
625204431Sraj	while (listlen >= len) {
626204431Sraj		if (memcmp(str, strlist, len+1) == 0)
627204431Sraj			return 1;
628204431Sraj		p = memchr(strlist, '\0', listlen);
629204431Sraj		if (!p)
630204431Sraj			return 0; /* malformed strlist.. */
631204431Sraj		listlen -= (p-strlist) + 1;
632204431Sraj		strlist = p + 1;
633204431Sraj	}
634204431Sraj	return 0;
635204431Sraj}
636204431Sraj
637328459Skevansint fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property)
638328459Skevans{
639328459Skevans	const char *list, *end;
640328459Skevans	int length, count = 0;
641328459Skevans
642328459Skevans	list = fdt_getprop(fdt, nodeoffset, property, &length);
643328459Skevans	if (!list)
644328459Skevans		return length;
645328459Skevans
646328459Skevans	end = list + length;
647328459Skevans
648328459Skevans	while (list < end) {
649328459Skevans		length = strnlen(list, end - list) + 1;
650328459Skevans
651328459Skevans		/* Abort if the last string isn't properly NUL-terminated. */
652328459Skevans		if (list + length > end)
653328459Skevans			return -FDT_ERR_BADVALUE;
654328459Skevans
655328459Skevans		list += length;
656328459Skevans		count++;
657328459Skevans	}
658328459Skevans
659328459Skevans	return count;
660328459Skevans}
661328459Skevans
662328459Skevansint fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property,
663328459Skevans			  const char *string)
664328459Skevans{
665328459Skevans	int length, len, idx = 0;
666328459Skevans	const char *list, *end;
667328459Skevans
668328459Skevans	list = fdt_getprop(fdt, nodeoffset, property, &length);
669328459Skevans	if (!list)
670328459Skevans		return length;
671328459Skevans
672328459Skevans	len = strlen(string) + 1;
673328459Skevans	end = list + length;
674328459Skevans
675328459Skevans	while (list < end) {
676328459Skevans		length = strnlen(list, end - list) + 1;
677328459Skevans
678328459Skevans		/* Abort if the last string isn't properly NUL-terminated. */
679328459Skevans		if (list + length > end)
680328459Skevans			return -FDT_ERR_BADVALUE;
681328459Skevans
682328459Skevans		if (length == len && memcmp(list, string, length) == 0)
683328459Skevans			return idx;
684328459Skevans
685328459Skevans		list += length;
686328459Skevans		idx++;
687328459Skevans	}
688328459Skevans
689328459Skevans	return -FDT_ERR_NOTFOUND;
690328459Skevans}
691328459Skevans
692328459Skevansconst char *fdt_stringlist_get(const void *fdt, int nodeoffset,
693328459Skevans			       const char *property, int idx,
694328459Skevans			       int *lenp)
695328459Skevans{
696328459Skevans	const char *list, *end;
697328459Skevans	int length;
698328459Skevans
699328459Skevans	list = fdt_getprop(fdt, nodeoffset, property, &length);
700328459Skevans	if (!list) {
701328459Skevans		if (lenp)
702328459Skevans			*lenp = length;
703328459Skevans
704328459Skevans		return NULL;
705328459Skevans	}
706328459Skevans
707328459Skevans	end = list + length;
708328459Skevans
709328459Skevans	while (list < end) {
710328459Skevans		length = strnlen(list, end - list) + 1;
711328459Skevans
712328459Skevans		/* Abort if the last string isn't properly NUL-terminated. */
713328459Skevans		if (list + length > end) {
714328459Skevans			if (lenp)
715328459Skevans				*lenp = -FDT_ERR_BADVALUE;
716328459Skevans
717328459Skevans			return NULL;
718328459Skevans		}
719328459Skevans
720328459Skevans		if (idx == 0) {
721328459Skevans			if (lenp)
722328459Skevans				*lenp = length - 1;
723328459Skevans
724328459Skevans			return list;
725328459Skevans		}
726328459Skevans
727328459Skevans		list += length;
728328459Skevans		idx--;
729328459Skevans	}
730328459Skevans
731328459Skevans	if (lenp)
732328459Skevans		*lenp = -FDT_ERR_NOTFOUND;
733328459Skevans
734328459Skevans	return NULL;
735328459Skevans}
736328459Skevans
737204431Srajint fdt_node_check_compatible(const void *fdt, int nodeoffset,
738204431Sraj			      const char *compatible)
739204431Sraj{
740204431Sraj	const void *prop;
741204431Sraj	int len;
742204431Sraj
743204431Sraj	prop = fdt_getprop(fdt, nodeoffset, "compatible", &len);
744204431Sraj	if (!prop)
745204431Sraj		return len;
746328459Skevans
747328459Skevans	return !fdt_stringlist_contains(prop, len, compatible);
748204431Sraj}
749204431Sraj
750204431Srajint fdt_node_offset_by_compatible(const void *fdt, int startoffset,
751204431Sraj				  const char *compatible)
752204431Sraj{
753204431Sraj	int offset, err;
754204431Sraj
755204431Sraj	FDT_CHECK_HEADER(fdt);
756204431Sraj
757204431Sraj	/* FIXME: The algorithm here is pretty horrible: we scan each
758204431Sraj	 * property of a node in fdt_node_check_compatible(), then if
759204431Sraj	 * that didn't find what we want, we scan over them again
760204431Sraj	 * making our way to the next node.  Still it's the easiest to
761204431Sraj	 * implement approach; performance can come later. */
762204431Sraj	for (offset = fdt_next_node(fdt, startoffset, NULL);
763204431Sraj	     offset >= 0;
764204431Sraj	     offset = fdt_next_node(fdt, offset, NULL)) {
765204431Sraj		err = fdt_node_check_compatible(fdt, offset, compatible);
766204431Sraj		if ((err < 0) && (err != -FDT_ERR_NOTFOUND))
767204431Sraj			return err;
768204431Sraj		else if (err == 0)
769204431Sraj			return offset;
770204431Sraj	}
771204431Sraj
772204431Sraj	return offset; /* error from fdt_next_node() */
773204431Sraj}
774