xref: /optee_os/core/lib/libfdt/fdt_ro.c (revision 578bc4fe77ccbb3145211713eaf2b6129245b93e)
117f326ebSJerome Forissier // SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
2b908c675SJens Wiklander /*
3b908c675SJens Wiklander  * libfdt - Flat Device Tree manipulation
4b908c675SJens Wiklander  * Copyright (C) 2006 David Gibson, IBM Corporation.
5b908c675SJens Wiklander  */
6b908c675SJens Wiklander #include "libfdt_env.h"
7b908c675SJens Wiklander 
8b908c675SJens Wiklander #include <fdt.h>
9*578bc4feSEtienne Carriere #include <kernel/dt.h>	/* fdt_find_cached_xxx() */
10b908c675SJens Wiklander #include <libfdt.h>
11b908c675SJens Wiklander 
12b908c675SJens Wiklander #include "libfdt_internal.h"
13b908c675SJens Wiklander 
fdt_nodename_eq_(const void * fdt,int offset,const char * s,int len)1401d6a9daSBryan O'Donoghue static int fdt_nodename_eq_(const void *fdt, int offset,
15b908c675SJens Wiklander 			    const char *s, int len)
16b908c675SJens Wiklander {
1701d6a9daSBryan O'Donoghue 	int olen;
1801d6a9daSBryan O'Donoghue 	const char *p = fdt_get_name(fdt, offset, &olen);
19b908c675SJens Wiklander 
2001d6a9daSBryan O'Donoghue 	if (!p || olen < len)
21b908c675SJens Wiklander 		/* short match */
22b908c675SJens Wiklander 		return 0;
23b908c675SJens Wiklander 
24b908c675SJens Wiklander 	if (memcmp(p, s, len) != 0)
25b908c675SJens Wiklander 		return 0;
26b908c675SJens Wiklander 
27b908c675SJens Wiklander 	if (p[len] == '\0')
28b908c675SJens Wiklander 		return 1;
29b908c675SJens Wiklander 	else if (!memchr(s, '@', len) && (p[len] == '@'))
30b908c675SJens Wiklander 		return 1;
31b908c675SJens Wiklander 	else
32b908c675SJens Wiklander 		return 0;
33b908c675SJens Wiklander }
34b908c675SJens Wiklander 
fdt_get_string(const void * fdt,int stroffset,int * lenp)3517f326ebSJerome Forissier const char *fdt_get_string(const void *fdt, int stroffset, int *lenp)
3617f326ebSJerome Forissier {
3717f326ebSJerome Forissier 	int32_t totalsize = fdt_ro_probe_(fdt);
3817f326ebSJerome Forissier 	uint32_t absoffset = stroffset + fdt_off_dt_strings(fdt);
3917f326ebSJerome Forissier 	size_t len;
4017f326ebSJerome Forissier 	int err;
4117f326ebSJerome Forissier 	const char *s, *n;
4217f326ebSJerome Forissier 
4317f326ebSJerome Forissier 	err = totalsize;
4417f326ebSJerome Forissier 	if (totalsize < 0)
4517f326ebSJerome Forissier 		goto fail;
4617f326ebSJerome Forissier 
4717f326ebSJerome Forissier 	err = -FDT_ERR_BADOFFSET;
4817f326ebSJerome Forissier 	if (absoffset >= totalsize)
4917f326ebSJerome Forissier 		goto fail;
5017f326ebSJerome Forissier 	len = totalsize - absoffset;
5117f326ebSJerome Forissier 
5217f326ebSJerome Forissier 	if (fdt_magic(fdt) == FDT_MAGIC) {
5317f326ebSJerome Forissier 		if (stroffset < 0)
5417f326ebSJerome Forissier 			goto fail;
5517f326ebSJerome Forissier 		if (fdt_version(fdt) >= 17) {
5617f326ebSJerome Forissier 			if (stroffset >= fdt_size_dt_strings(fdt))
5717f326ebSJerome Forissier 				goto fail;
5817f326ebSJerome Forissier 			if ((fdt_size_dt_strings(fdt) - stroffset) < len)
5917f326ebSJerome Forissier 				len = fdt_size_dt_strings(fdt) - stroffset;
6017f326ebSJerome Forissier 		}
6117f326ebSJerome Forissier 	} else if (fdt_magic(fdt) == FDT_SW_MAGIC) {
6217f326ebSJerome Forissier 		if ((stroffset >= 0)
6317f326ebSJerome Forissier 		    || (stroffset < -fdt_size_dt_strings(fdt)))
6417f326ebSJerome Forissier 			goto fail;
6517f326ebSJerome Forissier 		if ((-stroffset) < len)
6617f326ebSJerome Forissier 			len = -stroffset;
6717f326ebSJerome Forissier 	} else {
6817f326ebSJerome Forissier 		err = -FDT_ERR_INTERNAL;
6917f326ebSJerome Forissier 		goto fail;
7017f326ebSJerome Forissier 	}
7117f326ebSJerome Forissier 
7217f326ebSJerome Forissier 	s = (const char *)fdt + absoffset;
7317f326ebSJerome Forissier 	n = memchr(s, '\0', len);
7417f326ebSJerome Forissier 	if (!n) {
7517f326ebSJerome Forissier 		/* missing terminating NULL */
7617f326ebSJerome Forissier 		err = -FDT_ERR_TRUNCATED;
7717f326ebSJerome Forissier 		goto fail;
7817f326ebSJerome Forissier 	}
7917f326ebSJerome Forissier 
8017f326ebSJerome Forissier 	if (lenp)
8117f326ebSJerome Forissier 		*lenp = n - s;
8217f326ebSJerome Forissier 	return s;
8317f326ebSJerome Forissier 
8417f326ebSJerome Forissier fail:
8517f326ebSJerome Forissier 	if (lenp)
8617f326ebSJerome Forissier 		*lenp = err;
8717f326ebSJerome Forissier 	return NULL;
8817f326ebSJerome Forissier }
8917f326ebSJerome Forissier 
fdt_string(const void * fdt,int stroffset)90b908c675SJens Wiklander const char *fdt_string(const void *fdt, int stroffset)
91b908c675SJens Wiklander {
9217f326ebSJerome Forissier 	return fdt_get_string(fdt, stroffset, NULL);
93b908c675SJens Wiklander }
94b908c675SJens Wiklander 
fdt_string_eq_(const void * fdt,int stroffset,const char * s,int len)9501d6a9daSBryan O'Donoghue static int fdt_string_eq_(const void *fdt, int stroffset,
96b908c675SJens Wiklander 			  const char *s, int len)
97b908c675SJens Wiklander {
9817f326ebSJerome Forissier 	int slen;
9917f326ebSJerome Forissier 	const char *p = fdt_get_string(fdt, stroffset, &slen);
100b908c675SJens Wiklander 
10117f326ebSJerome Forissier 	return p && (slen == len) && (memcmp(p, s, len) == 0);
102b908c675SJens Wiklander }
103b908c675SJens Wiklander 
fdt_find_max_phandle(const void * fdt,uint32_t * phandle)10417f326ebSJerome Forissier int fdt_find_max_phandle(const void *fdt, uint32_t *phandle)
10501d6a9daSBryan O'Donoghue {
10617f326ebSJerome Forissier 	uint32_t max = 0;
10717f326ebSJerome Forissier 	int offset = -1;
10801d6a9daSBryan O'Donoghue 
10917f326ebSJerome Forissier 	while (true) {
11017f326ebSJerome Forissier 		uint32_t value;
11101d6a9daSBryan O'Donoghue 
11217f326ebSJerome Forissier 		offset = fdt_next_node(fdt, offset, NULL);
11317f326ebSJerome Forissier 		if (offset < 0) {
11401d6a9daSBryan O'Donoghue 			if (offset == -FDT_ERR_NOTFOUND)
11517f326ebSJerome Forissier 				break;
11601d6a9daSBryan O'Donoghue 
11717f326ebSJerome Forissier 			return offset;
11801d6a9daSBryan O'Donoghue 		}
11901d6a9daSBryan O'Donoghue 
12017f326ebSJerome Forissier 		value = fdt_get_phandle(fdt, offset);
12117f326ebSJerome Forissier 
12217f326ebSJerome Forissier 		if (value > max)
12317f326ebSJerome Forissier 			max = value;
12417f326ebSJerome Forissier 	}
12517f326ebSJerome Forissier 
12617f326ebSJerome Forissier 	if (phandle)
12717f326ebSJerome Forissier 		*phandle = max;
12817f326ebSJerome Forissier 
12901d6a9daSBryan O'Donoghue 	return 0;
13001d6a9daSBryan O'Donoghue }
13101d6a9daSBryan O'Donoghue 
fdt_generate_phandle(const void * fdt,uint32_t * phandle)13217f326ebSJerome Forissier int fdt_generate_phandle(const void *fdt, uint32_t *phandle)
13317f326ebSJerome Forissier {
13417f326ebSJerome Forissier 	uint32_t max;
13517f326ebSJerome Forissier 	int err;
13617f326ebSJerome Forissier 
13717f326ebSJerome Forissier 	err = fdt_find_max_phandle(fdt, &max);
13817f326ebSJerome Forissier 	if (err < 0)
13917f326ebSJerome Forissier 		return err;
14017f326ebSJerome Forissier 
14117f326ebSJerome Forissier 	if (max == FDT_MAX_PHANDLE)
14217f326ebSJerome Forissier 		return -FDT_ERR_NOPHANDLES;
14317f326ebSJerome Forissier 
14417f326ebSJerome Forissier 	if (phandle)
14517f326ebSJerome Forissier 		*phandle = max + 1;
14617f326ebSJerome Forissier 
14717f326ebSJerome Forissier 	return 0;
14817f326ebSJerome Forissier }
14917f326ebSJerome Forissier 
fdt_mem_rsv(const void * fdt,int n)15017f326ebSJerome Forissier static const struct fdt_reserve_entry *fdt_mem_rsv(const void *fdt, int n)
15117f326ebSJerome Forissier {
15217f326ebSJerome Forissier 	int offset = n * sizeof(struct fdt_reserve_entry);
15317f326ebSJerome Forissier 	int absoffset = fdt_off_mem_rsvmap(fdt) + offset;
15417f326ebSJerome Forissier 
15517f326ebSJerome Forissier 	if (absoffset < fdt_off_mem_rsvmap(fdt))
15617f326ebSJerome Forissier 		return NULL;
15717f326ebSJerome Forissier 	if (absoffset > fdt_totalsize(fdt) - sizeof(struct fdt_reserve_entry))
15817f326ebSJerome Forissier 		return NULL;
15917f326ebSJerome Forissier 	return fdt_mem_rsv_(fdt, n);
16017f326ebSJerome Forissier }
16117f326ebSJerome Forissier 
fdt_get_mem_rsv(const void * fdt,int n,uint64_t * address,uint64_t * size)162b908c675SJens Wiklander int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size)
163b908c675SJens Wiklander {
16417f326ebSJerome Forissier 	const struct fdt_reserve_entry *re;
16517f326ebSJerome Forissier 
16617f326ebSJerome Forissier 	FDT_RO_PROBE(fdt);
16717f326ebSJerome Forissier 	re = fdt_mem_rsv(fdt, n);
16817f326ebSJerome Forissier 	if (!re)
16917f326ebSJerome Forissier 		return -FDT_ERR_BADOFFSET;
17017f326ebSJerome Forissier 
17117f326ebSJerome Forissier 	*address = fdt64_ld(&re->address);
17217f326ebSJerome Forissier 	*size = fdt64_ld(&re->size);
173b908c675SJens Wiklander 	return 0;
174b908c675SJens Wiklander }
175b908c675SJens Wiklander 
fdt_num_mem_rsv(const void * fdt)176b908c675SJens Wiklander int fdt_num_mem_rsv(const void *fdt)
177b908c675SJens Wiklander {
17817f326ebSJerome Forissier 	int i;
17917f326ebSJerome Forissier 	const struct fdt_reserve_entry *re;
180b908c675SJens Wiklander 
18117f326ebSJerome Forissier 	for (i = 0; (re = fdt_mem_rsv(fdt, i)) != NULL; i++) {
18217f326ebSJerome Forissier 		if (fdt64_ld(&re->size) == 0)
183b908c675SJens Wiklander 			return i;
184b908c675SJens Wiklander 	}
18517f326ebSJerome Forissier 	return -FDT_ERR_TRUNCATED;
18617f326ebSJerome Forissier }
187b908c675SJens Wiklander 
nextprop_(const void * fdt,int offset)18801d6a9daSBryan O'Donoghue static int nextprop_(const void *fdt, int offset)
189b908c675SJens Wiklander {
190b908c675SJens Wiklander 	uint32_t tag;
191b908c675SJens Wiklander 	int nextoffset;
192b908c675SJens Wiklander 
193b908c675SJens Wiklander 	do {
194b908c675SJens Wiklander 		tag = fdt_next_tag(fdt, offset, &nextoffset);
195b908c675SJens Wiklander 
196b908c675SJens Wiklander 		switch (tag) {
197b908c675SJens Wiklander 		case FDT_END:
198b908c675SJens Wiklander 			if (nextoffset >= 0)
199b908c675SJens Wiklander 				return -FDT_ERR_BADSTRUCTURE;
200b908c675SJens Wiklander 			else
201b908c675SJens Wiklander 				return nextoffset;
202b908c675SJens Wiklander 
203b908c675SJens Wiklander 		case FDT_PROP:
204b908c675SJens Wiklander 			return offset;
205b908c675SJens Wiklander 		}
206b908c675SJens Wiklander 		offset = nextoffset;
207b908c675SJens Wiklander 	} while (tag == FDT_NOP);
208b908c675SJens Wiklander 
209b908c675SJens Wiklander 	return -FDT_ERR_NOTFOUND;
210b908c675SJens Wiklander }
211b908c675SJens Wiklander 
fdt_subnode_offset_namelen(const void * fdt,int offset,const char * name,int namelen)212b908c675SJens Wiklander int fdt_subnode_offset_namelen(const void *fdt, int offset,
213b908c675SJens Wiklander 			       const char *name, int namelen)
214b908c675SJens Wiklander {
215b908c675SJens Wiklander 	int depth;
216b908c675SJens Wiklander 
21717f326ebSJerome Forissier 	FDT_RO_PROBE(fdt);
218b908c675SJens Wiklander 
219b908c675SJens Wiklander 	for (depth = 0;
220b908c675SJens Wiklander 	     (offset >= 0) && (depth >= 0);
221b908c675SJens Wiklander 	     offset = fdt_next_node(fdt, offset, &depth))
222b908c675SJens Wiklander 		if ((depth == 1)
22301d6a9daSBryan O'Donoghue 		    && fdt_nodename_eq_(fdt, offset, name, namelen))
224b908c675SJens Wiklander 			return offset;
225b908c675SJens Wiklander 
226b908c675SJens Wiklander 	if (depth < 0)
227b908c675SJens Wiklander 		return -FDT_ERR_NOTFOUND;
228b908c675SJens Wiklander 	return offset; /* error */
229b908c675SJens Wiklander }
230b908c675SJens Wiklander 
fdt_subnode_offset(const void * fdt,int parentoffset,const char * name)231b908c675SJens Wiklander int fdt_subnode_offset(const void *fdt, int parentoffset,
232b908c675SJens Wiklander 		       const char *name)
233b908c675SJens Wiklander {
234b908c675SJens Wiklander 	return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name));
235b908c675SJens Wiklander }
236b908c675SJens Wiklander 
fdt_path_offset_namelen(const void * fdt,const char * path,int namelen)23701d6a9daSBryan O'Donoghue int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen)
238b908c675SJens Wiklander {
23901d6a9daSBryan O'Donoghue 	const char *end = path + namelen;
240b908c675SJens Wiklander 	const char *p = path;
241b908c675SJens Wiklander 	int offset = 0;
242b908c675SJens Wiklander 
24317f326ebSJerome Forissier 	FDT_RO_PROBE(fdt);
244b908c675SJens Wiklander 
245b908c675SJens Wiklander 	/* see if we have an alias */
246b908c675SJens Wiklander 	if (*path != '/') {
24701d6a9daSBryan O'Donoghue 		const char *q = memchr(path, '/', end - p);
248b908c675SJens Wiklander 
249b908c675SJens Wiklander 		if (!q)
250b908c675SJens Wiklander 			q = end;
251b908c675SJens Wiklander 
252b908c675SJens Wiklander 		p = fdt_get_alias_namelen(fdt, p, q - p);
253b908c675SJens Wiklander 		if (!p)
254b908c675SJens Wiklander 			return -FDT_ERR_BADPATH;
255b908c675SJens Wiklander 		offset = fdt_path_offset(fdt, p);
256b908c675SJens Wiklander 
257b908c675SJens Wiklander 		p = q;
258b908c675SJens Wiklander 	}
259b908c675SJens Wiklander 
26001d6a9daSBryan O'Donoghue 	while (p < end) {
261b908c675SJens Wiklander 		const char *q;
262b908c675SJens Wiklander 
26301d6a9daSBryan O'Donoghue 		while (*p == '/') {
264b908c675SJens Wiklander 			p++;
26501d6a9daSBryan O'Donoghue 			if (p == end)
266b908c675SJens Wiklander 				return offset;
26701d6a9daSBryan O'Donoghue 		}
26801d6a9daSBryan O'Donoghue 		q = memchr(p, '/', end - p);
269b908c675SJens Wiklander 		if (! q)
270b908c675SJens Wiklander 			q = end;
271b908c675SJens Wiklander 
272b908c675SJens Wiklander 		offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p);
273b908c675SJens Wiklander 		if (offset < 0)
274b908c675SJens Wiklander 			return offset;
275b908c675SJens Wiklander 
276b908c675SJens Wiklander 		p = q;
277b908c675SJens Wiklander 	}
278b908c675SJens Wiklander 
279b908c675SJens Wiklander 	return offset;
280b908c675SJens Wiklander }
281b908c675SJens Wiklander 
fdt_path_offset(const void * fdt,const char * path)28201d6a9daSBryan O'Donoghue int fdt_path_offset(const void *fdt, const char *path)
28301d6a9daSBryan O'Donoghue {
28401d6a9daSBryan O'Donoghue 	return fdt_path_offset_namelen(fdt, path, strlen(path));
28501d6a9daSBryan O'Donoghue }
28601d6a9daSBryan O'Donoghue 
fdt_get_name(const void * fdt,int nodeoffset,int * len)287b908c675SJens Wiklander const char *fdt_get_name(const void *fdt, int nodeoffset, int *len)
288b908c675SJens Wiklander {
28901d6a9daSBryan O'Donoghue 	const struct fdt_node_header *nh = fdt_offset_ptr_(fdt, nodeoffset);
29001d6a9daSBryan O'Donoghue 	const char *nameptr;
291b908c675SJens Wiklander 	int err;
292b908c675SJens Wiklander 
29317f326ebSJerome Forissier 	if (((err = fdt_ro_probe_(fdt)) < 0)
29401d6a9daSBryan O'Donoghue 	    || ((err = fdt_check_node_offset_(fdt, nodeoffset)) < 0))
295b908c675SJens Wiklander 			goto fail;
296b908c675SJens Wiklander 
29701d6a9daSBryan O'Donoghue 	nameptr = nh->name;
298b908c675SJens Wiklander 
29901d6a9daSBryan O'Donoghue 	if (fdt_version(fdt) < 0x10) {
30001d6a9daSBryan O'Donoghue 		/*
30101d6a9daSBryan O'Donoghue 		 * For old FDT versions, match the naming conventions of V16:
30201d6a9daSBryan O'Donoghue 		 * give only the leaf name (after all /). The actual tree
30301d6a9daSBryan O'Donoghue 		 * contents are loosely checked.
30401d6a9daSBryan O'Donoghue 		 */
30501d6a9daSBryan O'Donoghue 		const char *leaf;
30601d6a9daSBryan O'Donoghue 		leaf = strrchr(nameptr, '/');
30701d6a9daSBryan O'Donoghue 		if (leaf == NULL) {
30801d6a9daSBryan O'Donoghue 			err = -FDT_ERR_BADSTRUCTURE;
30901d6a9daSBryan O'Donoghue 			goto fail;
31001d6a9daSBryan O'Donoghue 		}
31101d6a9daSBryan O'Donoghue 		nameptr = leaf+1;
31201d6a9daSBryan O'Donoghue 	}
31301d6a9daSBryan O'Donoghue 
31401d6a9daSBryan O'Donoghue 	if (len)
31501d6a9daSBryan O'Donoghue 		*len = strlen(nameptr);
31601d6a9daSBryan O'Donoghue 
31701d6a9daSBryan O'Donoghue 	return nameptr;
318b908c675SJens Wiklander 
319b908c675SJens Wiklander  fail:
320b908c675SJens Wiklander 	if (len)
321b908c675SJens Wiklander 		*len = err;
322b908c675SJens Wiklander 	return NULL;
323b908c675SJens Wiklander }
324b908c675SJens Wiklander 
fdt_first_property_offset(const void * fdt,int nodeoffset)325b908c675SJens Wiklander int fdt_first_property_offset(const void *fdt, int nodeoffset)
326b908c675SJens Wiklander {
327b908c675SJens Wiklander 	int offset;
328b908c675SJens Wiklander 
32901d6a9daSBryan O'Donoghue 	if ((offset = fdt_check_node_offset_(fdt, nodeoffset)) < 0)
330b908c675SJens Wiklander 		return offset;
331b908c675SJens Wiklander 
33201d6a9daSBryan O'Donoghue 	return nextprop_(fdt, offset);
333b908c675SJens Wiklander }
334b908c675SJens Wiklander 
fdt_next_property_offset(const void * fdt,int offset)335b908c675SJens Wiklander int fdt_next_property_offset(const void *fdt, int offset)
336b908c675SJens Wiklander {
33701d6a9daSBryan O'Donoghue 	if ((offset = fdt_check_prop_offset_(fdt, offset)) < 0)
338b908c675SJens Wiklander 		return offset;
339b908c675SJens Wiklander 
34001d6a9daSBryan O'Donoghue 	return nextprop_(fdt, offset);
341b908c675SJens Wiklander }
342b908c675SJens Wiklander 
fdt_get_property_by_offset_(const void * fdt,int offset,int * lenp)34301d6a9daSBryan O'Donoghue static const struct fdt_property *fdt_get_property_by_offset_(const void *fdt,
344b908c675SJens Wiklander 						              int offset,
345b908c675SJens Wiklander 						              int *lenp)
346b908c675SJens Wiklander {
347b908c675SJens Wiklander 	int err;
348b908c675SJens Wiklander 	const struct fdt_property *prop;
349b908c675SJens Wiklander 
35001d6a9daSBryan O'Donoghue 	if ((err = fdt_check_prop_offset_(fdt, offset)) < 0) {
351b908c675SJens Wiklander 		if (lenp)
352b908c675SJens Wiklander 			*lenp = err;
353b908c675SJens Wiklander 		return NULL;
354b908c675SJens Wiklander 	}
355b908c675SJens Wiklander 
35601d6a9daSBryan O'Donoghue 	prop = fdt_offset_ptr_(fdt, offset);
357b908c675SJens Wiklander 
358b908c675SJens Wiklander 	if (lenp)
35917f326ebSJerome Forissier 		*lenp = fdt32_ld(&prop->len);
360b908c675SJens Wiklander 
361b908c675SJens Wiklander 	return prop;
362b908c675SJens Wiklander }
363b908c675SJens Wiklander 
fdt_get_property_by_offset(const void * fdt,int offset,int * lenp)36401d6a9daSBryan O'Donoghue const struct fdt_property *fdt_get_property_by_offset(const void *fdt,
36501d6a9daSBryan O'Donoghue 						      int offset,
36601d6a9daSBryan O'Donoghue 						      int *lenp)
36701d6a9daSBryan O'Donoghue {
36801d6a9daSBryan O'Donoghue 	/* Prior to version 16, properties may need realignment
36901d6a9daSBryan O'Donoghue 	 * and this API does not work. fdt_getprop_*() will, however. */
37001d6a9daSBryan O'Donoghue 
37101d6a9daSBryan O'Donoghue 	if (fdt_version(fdt) < 0x10) {
37201d6a9daSBryan O'Donoghue 		if (lenp)
37301d6a9daSBryan O'Donoghue 			*lenp = -FDT_ERR_BADVERSION;
37401d6a9daSBryan O'Donoghue 		return NULL;
37501d6a9daSBryan O'Donoghue 	}
37601d6a9daSBryan O'Donoghue 
37701d6a9daSBryan O'Donoghue 	return fdt_get_property_by_offset_(fdt, offset, lenp);
37801d6a9daSBryan O'Donoghue }
37901d6a9daSBryan O'Donoghue 
fdt_get_property_namelen_(const void * fdt,int offset,const char * name,int namelen,int * lenp,int * poffset)38001d6a9daSBryan O'Donoghue static const struct fdt_property *fdt_get_property_namelen_(const void *fdt,
381b908c675SJens Wiklander 						            int offset,
382b908c675SJens Wiklander 						            const char *name,
38301d6a9daSBryan O'Donoghue 						            int namelen,
38401d6a9daSBryan O'Donoghue 							    int *lenp,
38501d6a9daSBryan O'Donoghue 							    int *poffset)
386b908c675SJens Wiklander {
387b908c675SJens Wiklander 	for (offset = fdt_first_property_offset(fdt, offset);
388b908c675SJens Wiklander 	     (offset >= 0);
389b908c675SJens Wiklander 	     (offset = fdt_next_property_offset(fdt, offset))) {
390b908c675SJens Wiklander 		const struct fdt_property *prop;
391b908c675SJens Wiklander 
39201d6a9daSBryan O'Donoghue 		if (!(prop = fdt_get_property_by_offset_(fdt, offset, lenp))) {
393b908c675SJens Wiklander 			offset = -FDT_ERR_INTERNAL;
394b908c675SJens Wiklander 			break;
395b908c675SJens Wiklander 		}
39617f326ebSJerome Forissier 		if (fdt_string_eq_(fdt, fdt32_ld(&prop->nameoff),
39701d6a9daSBryan O'Donoghue 				   name, namelen)) {
39801d6a9daSBryan O'Donoghue 			if (poffset)
39901d6a9daSBryan O'Donoghue 				*poffset = offset;
400b908c675SJens Wiklander 			return prop;
401b908c675SJens Wiklander 		}
40201d6a9daSBryan O'Donoghue 	}
403b908c675SJens Wiklander 
404b908c675SJens Wiklander 	if (lenp)
405b908c675SJens Wiklander 		*lenp = offset;
406b908c675SJens Wiklander 	return NULL;
407b908c675SJens Wiklander }
408b908c675SJens Wiklander 
40901d6a9daSBryan O'Donoghue 
fdt_get_property_namelen(const void * fdt,int offset,const char * name,int namelen,int * lenp)41001d6a9daSBryan O'Donoghue const struct fdt_property *fdt_get_property_namelen(const void *fdt,
41101d6a9daSBryan O'Donoghue 						    int offset,
41201d6a9daSBryan O'Donoghue 						    const char *name,
41301d6a9daSBryan O'Donoghue 						    int namelen, int *lenp)
41401d6a9daSBryan O'Donoghue {
41501d6a9daSBryan O'Donoghue 	/* Prior to version 16, properties may need realignment
41601d6a9daSBryan O'Donoghue 	 * and this API does not work. fdt_getprop_*() will, however. */
41701d6a9daSBryan O'Donoghue 	if (fdt_version(fdt) < 0x10) {
41801d6a9daSBryan O'Donoghue 		if (lenp)
41901d6a9daSBryan O'Donoghue 			*lenp = -FDT_ERR_BADVERSION;
42001d6a9daSBryan O'Donoghue 		return NULL;
42101d6a9daSBryan O'Donoghue 	}
42201d6a9daSBryan O'Donoghue 
42301d6a9daSBryan O'Donoghue 	return fdt_get_property_namelen_(fdt, offset, name, namelen, lenp,
42401d6a9daSBryan O'Donoghue 					 NULL);
42501d6a9daSBryan O'Donoghue }
42601d6a9daSBryan O'Donoghue 
42701d6a9daSBryan O'Donoghue 
fdt_get_property(const void * fdt,int nodeoffset,const char * name,int * lenp)428b908c675SJens Wiklander const struct fdt_property *fdt_get_property(const void *fdt,
429b908c675SJens Wiklander 					    int nodeoffset,
430b908c675SJens Wiklander 					    const char *name, int *lenp)
431b908c675SJens Wiklander {
432b908c675SJens Wiklander 	return fdt_get_property_namelen(fdt, nodeoffset, name,
433b908c675SJens Wiklander 					strlen(name), lenp);
434b908c675SJens Wiklander }
435b908c675SJens Wiklander 
fdt_getprop_namelen(const void * fdt,int nodeoffset,const char * name,int namelen,int * lenp)436b908c675SJens Wiklander const void *fdt_getprop_namelen(const void *fdt, int nodeoffset,
437b908c675SJens Wiklander 				const char *name, int namelen, int *lenp)
438b908c675SJens Wiklander {
43901d6a9daSBryan O'Donoghue 	int poffset;
440b908c675SJens Wiklander 	const struct fdt_property *prop;
441b908c675SJens Wiklander 
44201d6a9daSBryan O'Donoghue 	prop = fdt_get_property_namelen_(fdt, nodeoffset, name, namelen, lenp,
44301d6a9daSBryan O'Donoghue 					 &poffset);
444b908c675SJens Wiklander 	if (!prop)
445b908c675SJens Wiklander 		return NULL;
446b908c675SJens Wiklander 
44701d6a9daSBryan O'Donoghue 	/* Handle realignment */
44801d6a9daSBryan O'Donoghue 	if (fdt_version(fdt) < 0x10 && (poffset + sizeof(*prop)) % 8 &&
44917f326ebSJerome Forissier 	    fdt32_ld(&prop->len) >= 8)
45001d6a9daSBryan O'Donoghue 		return prop->data + 4;
451b908c675SJens Wiklander 	return prop->data;
452b908c675SJens Wiklander }
453b908c675SJens Wiklander 
fdt_getprop_by_offset(const void * fdt,int offset,const char ** namep,int * lenp)454b908c675SJens Wiklander const void *fdt_getprop_by_offset(const void *fdt, int offset,
455b908c675SJens Wiklander 				  const char **namep, int *lenp)
456b908c675SJens Wiklander {
457b908c675SJens Wiklander 	const struct fdt_property *prop;
458b908c675SJens Wiklander 
45901d6a9daSBryan O'Donoghue 	prop = fdt_get_property_by_offset_(fdt, offset, lenp);
460b908c675SJens Wiklander 	if (!prop)
461b908c675SJens Wiklander 		return NULL;
46217f326ebSJerome Forissier 	if (namep) {
46317f326ebSJerome Forissier 		const char *name;
46417f326ebSJerome Forissier 		int namelen;
46517f326ebSJerome Forissier 		name = fdt_get_string(fdt, fdt32_ld(&prop->nameoff),
46617f326ebSJerome Forissier 				      &namelen);
46717f326ebSJerome Forissier 		if (!name) {
46817f326ebSJerome Forissier 			if (lenp)
46917f326ebSJerome Forissier 				*lenp = namelen;
47017f326ebSJerome Forissier 			return NULL;
47117f326ebSJerome Forissier 		}
47217f326ebSJerome Forissier 		*namep = name;
47317f326ebSJerome Forissier 	}
47401d6a9daSBryan O'Donoghue 
47501d6a9daSBryan O'Donoghue 	/* Handle realignment */
47601d6a9daSBryan O'Donoghue 	if (fdt_version(fdt) < 0x10 && (offset + sizeof(*prop)) % 8 &&
47717f326ebSJerome Forissier 	    fdt32_ld(&prop->len) >= 8)
47801d6a9daSBryan O'Donoghue 		return prop->data + 4;
479b908c675SJens Wiklander 	return prop->data;
480b908c675SJens Wiklander }
481b908c675SJens Wiklander 
fdt_getprop(const void * fdt,int nodeoffset,const char * name,int * lenp)482b908c675SJens Wiklander const void *fdt_getprop(const void *fdt, int nodeoffset,
483b908c675SJens Wiklander 			const char *name, int *lenp)
484b908c675SJens Wiklander {
485b908c675SJens Wiklander 	return fdt_getprop_namelen(fdt, nodeoffset, name, strlen(name), lenp);
486b908c675SJens Wiklander }
487b908c675SJens Wiklander 
fdt_get_phandle(const void * fdt,int nodeoffset)488b908c675SJens Wiklander uint32_t fdt_get_phandle(const void *fdt, int nodeoffset)
489b908c675SJens Wiklander {
490b908c675SJens Wiklander 	const fdt32_t *php;
491b908c675SJens Wiklander 	int len;
492b908c675SJens Wiklander 
493b908c675SJens Wiklander 	/* FIXME: This is a bit sub-optimal, since we potentially scan
494b908c675SJens Wiklander 	 * over all the properties twice. */
495b908c675SJens Wiklander 	php = fdt_getprop(fdt, nodeoffset, "phandle", &len);
496b908c675SJens Wiklander 	if (!php || (len != sizeof(*php))) {
497b908c675SJens Wiklander 		php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len);
498b908c675SJens Wiklander 		if (!php || (len != sizeof(*php)))
499b908c675SJens Wiklander 			return 0;
500b908c675SJens Wiklander 	}
501b908c675SJens Wiklander 
50217f326ebSJerome Forissier 	return fdt32_ld(php);
503b908c675SJens Wiklander }
504b908c675SJens Wiklander 
fdt_get_alias_namelen(const void * fdt,const char * name,int namelen)505b908c675SJens Wiklander const char *fdt_get_alias_namelen(const void *fdt,
506b908c675SJens Wiklander 				  const char *name, int namelen)
507b908c675SJens Wiklander {
508b908c675SJens Wiklander 	int aliasoffset;
509b908c675SJens Wiklander 
510b908c675SJens Wiklander 	aliasoffset = fdt_path_offset(fdt, "/aliases");
511b908c675SJens Wiklander 	if (aliasoffset < 0)
512b908c675SJens Wiklander 		return NULL;
513b908c675SJens Wiklander 
514b908c675SJens Wiklander 	return fdt_getprop_namelen(fdt, aliasoffset, name, namelen, NULL);
515b908c675SJens Wiklander }
516b908c675SJens Wiklander 
fdt_get_alias(const void * fdt,const char * name)517b908c675SJens Wiklander const char *fdt_get_alias(const void *fdt, const char *name)
518b908c675SJens Wiklander {
519b908c675SJens Wiklander 	return fdt_get_alias_namelen(fdt, name, strlen(name));
520b908c675SJens Wiklander }
521b908c675SJens Wiklander 
fdt_get_path(const void * fdt,int nodeoffset,char * buf,int buflen)522b908c675SJens Wiklander int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen)
523b908c675SJens Wiklander {
524b908c675SJens Wiklander 	int pdepth = 0, p = 0;
525b908c675SJens Wiklander 	int offset, depth, namelen;
526b908c675SJens Wiklander 	const char *name;
527b908c675SJens Wiklander 
52817f326ebSJerome Forissier 	FDT_RO_PROBE(fdt);
529b908c675SJens Wiklander 
530b908c675SJens Wiklander 	if (buflen < 2)
531b908c675SJens Wiklander 		return -FDT_ERR_NOSPACE;
532b908c675SJens Wiklander 
533b908c675SJens Wiklander 	for (offset = 0, depth = 0;
534b908c675SJens Wiklander 	     (offset >= 0) && (offset <= nodeoffset);
535b908c675SJens Wiklander 	     offset = fdt_next_node(fdt, offset, &depth)) {
536b908c675SJens Wiklander 		while (pdepth > depth) {
537b908c675SJens Wiklander 			do {
538b908c675SJens Wiklander 				p--;
539b908c675SJens Wiklander 			} while (buf[p-1] != '/');
540b908c675SJens Wiklander 			pdepth--;
541b908c675SJens Wiklander 		}
542b908c675SJens Wiklander 
543b908c675SJens Wiklander 		if (pdepth >= depth) {
544b908c675SJens Wiklander 			name = fdt_get_name(fdt, offset, &namelen);
545b908c675SJens Wiklander 			if (!name)
546b908c675SJens Wiklander 				return namelen;
547b908c675SJens Wiklander 			if ((p + namelen + 1) <= buflen) {
548b908c675SJens Wiklander 				memcpy(buf + p, name, namelen);
549b908c675SJens Wiklander 				p += namelen;
550b908c675SJens Wiklander 				buf[p++] = '/';
551b908c675SJens Wiklander 				pdepth++;
552b908c675SJens Wiklander 			}
553b908c675SJens Wiklander 		}
554b908c675SJens Wiklander 
555b908c675SJens Wiklander 		if (offset == nodeoffset) {
556b908c675SJens Wiklander 			if (pdepth < (depth + 1))
557b908c675SJens Wiklander 				return -FDT_ERR_NOSPACE;
558b908c675SJens Wiklander 
559b908c675SJens Wiklander 			if (p > 1) /* special case so that root path is "/", not "" */
560b908c675SJens Wiklander 				p--;
561b908c675SJens Wiklander 			buf[p] = '\0';
562b908c675SJens Wiklander 			return 0;
563b908c675SJens Wiklander 		}
564b908c675SJens Wiklander 	}
565b908c675SJens Wiklander 
566b908c675SJens Wiklander 	if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
567b908c675SJens Wiklander 		return -FDT_ERR_BADOFFSET;
568b908c675SJens Wiklander 	else if (offset == -FDT_ERR_BADOFFSET)
569b908c675SJens Wiklander 		return -FDT_ERR_BADSTRUCTURE;
570b908c675SJens Wiklander 
571b908c675SJens Wiklander 	return offset; /* error from fdt_next_node() */
572b908c675SJens Wiklander }
573b908c675SJens Wiklander 
fdt_supernode_atdepth_offset(const void * fdt,int nodeoffset,int supernodedepth,int * nodedepth)574b908c675SJens Wiklander int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset,
575b908c675SJens Wiklander 				 int supernodedepth, int *nodedepth)
576b908c675SJens Wiklander {
577b908c675SJens Wiklander 	int offset, depth;
578b908c675SJens Wiklander 	int supernodeoffset = -FDT_ERR_INTERNAL;
579b908c675SJens Wiklander 
58017f326ebSJerome Forissier 	FDT_RO_PROBE(fdt);
581b908c675SJens Wiklander 
582b908c675SJens Wiklander 	if (supernodedepth < 0)
583b908c675SJens Wiklander 		return -FDT_ERR_NOTFOUND;
584b908c675SJens Wiklander 
585b908c675SJens Wiklander 	for (offset = 0, depth = 0;
586b908c675SJens Wiklander 	     (offset >= 0) && (offset <= nodeoffset);
587b908c675SJens Wiklander 	     offset = fdt_next_node(fdt, offset, &depth)) {
588b908c675SJens Wiklander 		if (depth == supernodedepth)
589b908c675SJens Wiklander 			supernodeoffset = offset;
590b908c675SJens Wiklander 
591b908c675SJens Wiklander 		if (offset == nodeoffset) {
592b908c675SJens Wiklander 			if (nodedepth)
593b908c675SJens Wiklander 				*nodedepth = depth;
594b908c675SJens Wiklander 
595b908c675SJens Wiklander 			if (supernodedepth > depth)
596b908c675SJens Wiklander 				return -FDT_ERR_NOTFOUND;
597b908c675SJens Wiklander 			else
598b908c675SJens Wiklander 				return supernodeoffset;
599b908c675SJens Wiklander 		}
600b908c675SJens Wiklander 	}
601b908c675SJens Wiklander 
602b908c675SJens Wiklander 	if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
603b908c675SJens Wiklander 		return -FDT_ERR_BADOFFSET;
604b908c675SJens Wiklander 	else if (offset == -FDT_ERR_BADOFFSET)
605b908c675SJens Wiklander 		return -FDT_ERR_BADSTRUCTURE;
606b908c675SJens Wiklander 
607b908c675SJens Wiklander 	return offset; /* error from fdt_next_node() */
608b908c675SJens Wiklander }
609b908c675SJens Wiklander 
fdt_node_depth(const void * fdt,int nodeoffset)610b908c675SJens Wiklander int fdt_node_depth(const void *fdt, int nodeoffset)
611b908c675SJens Wiklander {
612b908c675SJens Wiklander 	int nodedepth;
613b908c675SJens Wiklander 	int err;
614b908c675SJens Wiklander 
615b908c675SJens Wiklander 	err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth);
616b908c675SJens Wiklander 	if (err)
617b908c675SJens Wiklander 		return (err < 0) ? err : -FDT_ERR_INTERNAL;
618b908c675SJens Wiklander 	return nodedepth;
619b908c675SJens Wiklander }
620b908c675SJens Wiklander 
fdt_parent_offset(const void * fdt,int nodeoffset)621b908c675SJens Wiklander int fdt_parent_offset(const void *fdt, int nodeoffset)
622b908c675SJens Wiklander {
623*578bc4feSEtienne Carriere 	int parent_offset = 0;
624*578bc4feSEtienne Carriere 	int nodedepth = 0;
625*578bc4feSEtienne Carriere 
626*578bc4feSEtienne Carriere 	if (fdt_find_cached_parent_node(fdt, nodeoffset, &parent_offset) == 0)
627*578bc4feSEtienne Carriere 		return parent_offset;
628*578bc4feSEtienne Carriere 
629*578bc4feSEtienne Carriere 	nodedepth = fdt_node_depth(fdt, nodeoffset);
630b908c675SJens Wiklander 
631b908c675SJens Wiklander 	if (nodedepth < 0)
632b908c675SJens Wiklander 		return nodedepth;
633b908c675SJens Wiklander 	return fdt_supernode_atdepth_offset(fdt, nodeoffset,
634b908c675SJens Wiklander 					    nodedepth - 1, NULL);
635b908c675SJens Wiklander }
636b908c675SJens Wiklander 
fdt_node_offset_by_prop_value(const void * fdt,int startoffset,const char * propname,const void * propval,int proplen)637b908c675SJens Wiklander int fdt_node_offset_by_prop_value(const void *fdt, int startoffset,
638b908c675SJens Wiklander 				  const char *propname,
639b908c675SJens Wiklander 				  const void *propval, int proplen)
640b908c675SJens Wiklander {
641b908c675SJens Wiklander 	int offset;
642b908c675SJens Wiklander 	const void *val;
643b908c675SJens Wiklander 	int len;
644b908c675SJens Wiklander 
64517f326ebSJerome Forissier 	FDT_RO_PROBE(fdt);
646b908c675SJens Wiklander 
647b908c675SJens Wiklander 	/* FIXME: The algorithm here is pretty horrible: we scan each
648b908c675SJens Wiklander 	 * property of a node in fdt_getprop(), then if that didn't
649b908c675SJens Wiklander 	 * find what we want, we scan over them again making our way
650b908c675SJens Wiklander 	 * to the next node.  Still it's the easiest to implement
651b908c675SJens Wiklander 	 * approach; performance can come later. */
652b908c675SJens Wiklander 	for (offset = fdt_next_node(fdt, startoffset, NULL);
653b908c675SJens Wiklander 	     offset >= 0;
654b908c675SJens Wiklander 	     offset = fdt_next_node(fdt, offset, NULL)) {
655b908c675SJens Wiklander 		val = fdt_getprop(fdt, offset, propname, &len);
656b908c675SJens Wiklander 		if (val && (len == proplen)
657b908c675SJens Wiklander 		    && (memcmp(val, propval, len) == 0))
658b908c675SJens Wiklander 			return offset;
659b908c675SJens Wiklander 	}
660b908c675SJens Wiklander 
661b908c675SJens Wiklander 	return offset; /* error from fdt_next_node() */
662b908c675SJens Wiklander }
663b908c675SJens Wiklander 
fdt_node_offset_by_phandle(const void * fdt,uint32_t phandle)664b908c675SJens Wiklander int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle)
665b908c675SJens Wiklander {
666b908c675SJens Wiklander 	int offset;
667b908c675SJens Wiklander 
668b908c675SJens Wiklander 	if ((phandle == 0) || (phandle == -1))
669b908c675SJens Wiklander 		return -FDT_ERR_BADPHANDLE;
670b908c675SJens Wiklander 
671*578bc4feSEtienne Carriere 	if (fdt_find_cached_node_phandle(fdt, phandle, &offset) == 0)
672*578bc4feSEtienne Carriere 		return offset;
673*578bc4feSEtienne Carriere 
67417f326ebSJerome Forissier 	FDT_RO_PROBE(fdt);
675b908c675SJens Wiklander 
676b908c675SJens Wiklander 	/* FIXME: The algorithm here is pretty horrible: we
677b908c675SJens Wiklander 	 * potentially scan each property of a node in
678b908c675SJens Wiklander 	 * fdt_get_phandle(), then if that didn't find what
679b908c675SJens Wiklander 	 * we want, we scan over them again making our way to the next
680b908c675SJens Wiklander 	 * node.  Still it's the easiest to implement approach;
681b908c675SJens Wiklander 	 * performance can come later. */
682b908c675SJens Wiklander 	for (offset = fdt_next_node(fdt, -1, NULL);
683b908c675SJens Wiklander 	     offset >= 0;
684b908c675SJens Wiklander 	     offset = fdt_next_node(fdt, offset, NULL)) {
685b908c675SJens Wiklander 		if (fdt_get_phandle(fdt, offset) == phandle)
686b908c675SJens Wiklander 			return offset;
687b908c675SJens Wiklander 	}
688b908c675SJens Wiklander 
689b908c675SJens Wiklander 	return offset; /* error from fdt_next_node() */
690b908c675SJens Wiklander }
691b908c675SJens Wiklander 
fdt_stringlist_contains(const char * strlist,int listlen,const char * str)692b908c675SJens Wiklander int fdt_stringlist_contains(const char *strlist, int listlen, const char *str)
693b908c675SJens Wiklander {
694b908c675SJens Wiklander 	int len = strlen(str);
695b908c675SJens Wiklander 	const char *p;
696b908c675SJens Wiklander 
697b908c675SJens Wiklander 	while (listlen >= len) {
698b908c675SJens Wiklander 		if (memcmp(str, strlist, len+1) == 0)
699b908c675SJens Wiklander 			return 1;
700b908c675SJens Wiklander 		p = memchr(strlist, '\0', listlen);
701b908c675SJens Wiklander 		if (!p)
702b908c675SJens Wiklander 			return 0; /* malformed strlist.. */
703b908c675SJens Wiklander 		listlen -= (p-strlist) + 1;
704b908c675SJens Wiklander 		strlist = p + 1;
705b908c675SJens Wiklander 	}
706b908c675SJens Wiklander 	return 0;
707b908c675SJens Wiklander }
708b908c675SJens Wiklander 
fdt_stringlist_count(const void * fdt,int nodeoffset,const char * property)70901d6a9daSBryan O'Donoghue int fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property)
71001d6a9daSBryan O'Donoghue {
71101d6a9daSBryan O'Donoghue 	const char *list, *end;
71201d6a9daSBryan O'Donoghue 	int length, count = 0;
71301d6a9daSBryan O'Donoghue 
71401d6a9daSBryan O'Donoghue 	list = fdt_getprop(fdt, nodeoffset, property, &length);
71501d6a9daSBryan O'Donoghue 	if (!list)
71601d6a9daSBryan O'Donoghue 		return length;
71701d6a9daSBryan O'Donoghue 
71801d6a9daSBryan O'Donoghue 	end = list + length;
71901d6a9daSBryan O'Donoghue 
72001d6a9daSBryan O'Donoghue 	while (list < end) {
72101d6a9daSBryan O'Donoghue 		length = strnlen(list, end - list) + 1;
72201d6a9daSBryan O'Donoghue 
72301d6a9daSBryan O'Donoghue 		/* Abort if the last string isn't properly NUL-terminated. */
72401d6a9daSBryan O'Donoghue 		if (list + length > end)
72501d6a9daSBryan O'Donoghue 			return -FDT_ERR_BADVALUE;
72601d6a9daSBryan O'Donoghue 
72701d6a9daSBryan O'Donoghue 		list += length;
72801d6a9daSBryan O'Donoghue 		count++;
72901d6a9daSBryan O'Donoghue 	}
73001d6a9daSBryan O'Donoghue 
73101d6a9daSBryan O'Donoghue 	return count;
73201d6a9daSBryan O'Donoghue }
73301d6a9daSBryan O'Donoghue 
fdt_stringlist_search(const void * fdt,int nodeoffset,const char * property,const char * string)73401d6a9daSBryan O'Donoghue int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property,
73501d6a9daSBryan O'Donoghue 			  const char *string)
73601d6a9daSBryan O'Donoghue {
73701d6a9daSBryan O'Donoghue 	int length, len, idx = 0;
73801d6a9daSBryan O'Donoghue 	const char *list, *end;
73901d6a9daSBryan O'Donoghue 
74001d6a9daSBryan O'Donoghue 	list = fdt_getprop(fdt, nodeoffset, property, &length);
74101d6a9daSBryan O'Donoghue 	if (!list)
74201d6a9daSBryan O'Donoghue 		return length;
74301d6a9daSBryan O'Donoghue 
74401d6a9daSBryan O'Donoghue 	len = strlen(string) + 1;
74501d6a9daSBryan O'Donoghue 	end = list + length;
74601d6a9daSBryan O'Donoghue 
74701d6a9daSBryan O'Donoghue 	while (list < end) {
74801d6a9daSBryan O'Donoghue 		length = strnlen(list, end - list) + 1;
74901d6a9daSBryan O'Donoghue 
75001d6a9daSBryan O'Donoghue 		/* Abort if the last string isn't properly NUL-terminated. */
75101d6a9daSBryan O'Donoghue 		if (list + length > end)
75201d6a9daSBryan O'Donoghue 			return -FDT_ERR_BADVALUE;
75301d6a9daSBryan O'Donoghue 
75401d6a9daSBryan O'Donoghue 		if (length == len && memcmp(list, string, length) == 0)
75501d6a9daSBryan O'Donoghue 			return idx;
75601d6a9daSBryan O'Donoghue 
75701d6a9daSBryan O'Donoghue 		list += length;
75801d6a9daSBryan O'Donoghue 		idx++;
75901d6a9daSBryan O'Donoghue 	}
76001d6a9daSBryan O'Donoghue 
76101d6a9daSBryan O'Donoghue 	return -FDT_ERR_NOTFOUND;
76201d6a9daSBryan O'Donoghue }
76301d6a9daSBryan O'Donoghue 
fdt_stringlist_get(const void * fdt,int nodeoffset,const char * property,int idx,int * lenp)76401d6a9daSBryan O'Donoghue const char *fdt_stringlist_get(const void *fdt, int nodeoffset,
76501d6a9daSBryan O'Donoghue 			       const char *property, int idx,
76601d6a9daSBryan O'Donoghue 			       int *lenp)
76701d6a9daSBryan O'Donoghue {
76801d6a9daSBryan O'Donoghue 	const char *list, *end;
76901d6a9daSBryan O'Donoghue 	int length;
77001d6a9daSBryan O'Donoghue 
77101d6a9daSBryan O'Donoghue 	list = fdt_getprop(fdt, nodeoffset, property, &length);
77201d6a9daSBryan O'Donoghue 	if (!list) {
77301d6a9daSBryan O'Donoghue 		if (lenp)
77401d6a9daSBryan O'Donoghue 			*lenp = length;
77501d6a9daSBryan O'Donoghue 
77601d6a9daSBryan O'Donoghue 		return NULL;
77701d6a9daSBryan O'Donoghue 	}
77801d6a9daSBryan O'Donoghue 
77901d6a9daSBryan O'Donoghue 	end = list + length;
78001d6a9daSBryan O'Donoghue 
78101d6a9daSBryan O'Donoghue 	while (list < end) {
78201d6a9daSBryan O'Donoghue 		length = strnlen(list, end - list) + 1;
78301d6a9daSBryan O'Donoghue 
78401d6a9daSBryan O'Donoghue 		/* Abort if the last string isn't properly NUL-terminated. */
78501d6a9daSBryan O'Donoghue 		if (list + length > end) {
78601d6a9daSBryan O'Donoghue 			if (lenp)
78701d6a9daSBryan O'Donoghue 				*lenp = -FDT_ERR_BADVALUE;
78801d6a9daSBryan O'Donoghue 
78901d6a9daSBryan O'Donoghue 			return NULL;
79001d6a9daSBryan O'Donoghue 		}
79101d6a9daSBryan O'Donoghue 
79201d6a9daSBryan O'Donoghue 		if (idx == 0) {
79301d6a9daSBryan O'Donoghue 			if (lenp)
79401d6a9daSBryan O'Donoghue 				*lenp = length - 1;
79501d6a9daSBryan O'Donoghue 
79601d6a9daSBryan O'Donoghue 			return list;
79701d6a9daSBryan O'Donoghue 		}
79801d6a9daSBryan O'Donoghue 
79901d6a9daSBryan O'Donoghue 		list += length;
80001d6a9daSBryan O'Donoghue 		idx--;
80101d6a9daSBryan O'Donoghue 	}
80201d6a9daSBryan O'Donoghue 
80301d6a9daSBryan O'Donoghue 	if (lenp)
80401d6a9daSBryan O'Donoghue 		*lenp = -FDT_ERR_NOTFOUND;
80501d6a9daSBryan O'Donoghue 
80601d6a9daSBryan O'Donoghue 	return NULL;
80701d6a9daSBryan O'Donoghue }
80801d6a9daSBryan O'Donoghue 
fdt_node_check_compatible(const void * fdt,int nodeoffset,const char * compatible)809b908c675SJens Wiklander int fdt_node_check_compatible(const void *fdt, int nodeoffset,
810b908c675SJens Wiklander 			      const char *compatible)
811b908c675SJens Wiklander {
812b908c675SJens Wiklander 	const void *prop;
813b908c675SJens Wiklander 	int len;
814b908c675SJens Wiklander 
815b908c675SJens Wiklander 	prop = fdt_getprop(fdt, nodeoffset, "compatible", &len);
816b908c675SJens Wiklander 	if (!prop)
817b908c675SJens Wiklander 		return len;
81801d6a9daSBryan O'Donoghue 
81901d6a9daSBryan O'Donoghue 	return !fdt_stringlist_contains(prop, len, compatible);
820b908c675SJens Wiklander }
821b908c675SJens Wiklander 
fdt_node_offset_by_compatible(const void * fdt,int startoffset,const char * compatible)822b908c675SJens Wiklander int fdt_node_offset_by_compatible(const void *fdt, int startoffset,
823b908c675SJens Wiklander 				  const char *compatible)
824b908c675SJens Wiklander {
825b908c675SJens Wiklander 	int offset, err;
826b908c675SJens Wiklander 
82717f326ebSJerome Forissier 	FDT_RO_PROBE(fdt);
828b908c675SJens Wiklander 
829b908c675SJens Wiklander 	/* FIXME: The algorithm here is pretty horrible: we scan each
830b908c675SJens Wiklander 	 * property of a node in fdt_node_check_compatible(), then if
831b908c675SJens Wiklander 	 * that didn't find what we want, we scan over them again
832b908c675SJens Wiklander 	 * making our way to the next node.  Still it's the easiest to
833b908c675SJens Wiklander 	 * implement approach; performance can come later. */
834b908c675SJens Wiklander 	for (offset = fdt_next_node(fdt, startoffset, NULL);
835b908c675SJens Wiklander 	     offset >= 0;
836b908c675SJens Wiklander 	     offset = fdt_next_node(fdt, offset, NULL)) {
837b908c675SJens Wiklander 		err = fdt_node_check_compatible(fdt, offset, compatible);
838b908c675SJens Wiklander 		if ((err < 0) && (err != -FDT_ERR_NOTFOUND))
839b908c675SJens Wiklander 			return err;
840b908c675SJens Wiklander 		else if (err == 0)
841b908c675SJens Wiklander 			return offset;
842b908c675SJens Wiklander 	}
843b908c675SJens Wiklander 
844b908c675SJens Wiklander 	return offset; /* error from fdt_next_node() */
845b908c675SJens Wiklander }
84617f326ebSJerome Forissier 
fdt_check_full(const void * fdt,size_t bufsize)84717f326ebSJerome Forissier int fdt_check_full(const void *fdt, size_t bufsize)
84817f326ebSJerome Forissier {
84917f326ebSJerome Forissier 	int err;
85017f326ebSJerome Forissier 	int num_memrsv;
85117f326ebSJerome Forissier 	int offset, nextoffset = 0;
85217f326ebSJerome Forissier 	uint32_t tag;
85317f326ebSJerome Forissier 	unsigned depth = 0;
85417f326ebSJerome Forissier 	const void *prop;
85517f326ebSJerome Forissier 	const char *propname;
85617f326ebSJerome Forissier 
85717f326ebSJerome Forissier 	if (bufsize < FDT_V1_SIZE)
85817f326ebSJerome Forissier 		return -FDT_ERR_TRUNCATED;
85917f326ebSJerome Forissier 	err = fdt_check_header(fdt);
86017f326ebSJerome Forissier 	if (err != 0)
86117f326ebSJerome Forissier 		return err;
86217f326ebSJerome Forissier 	if (bufsize < fdt_totalsize(fdt))
86317f326ebSJerome Forissier 		return -FDT_ERR_TRUNCATED;
86417f326ebSJerome Forissier 
86517f326ebSJerome Forissier 	num_memrsv = fdt_num_mem_rsv(fdt);
86617f326ebSJerome Forissier 	if (num_memrsv < 0)
86717f326ebSJerome Forissier 		return num_memrsv;
86817f326ebSJerome Forissier 
86917f326ebSJerome Forissier 	while (1) {
87017f326ebSJerome Forissier 		offset = nextoffset;
87117f326ebSJerome Forissier 		tag = fdt_next_tag(fdt, offset, &nextoffset);
87217f326ebSJerome Forissier 
87317f326ebSJerome Forissier 		if (nextoffset < 0)
87417f326ebSJerome Forissier 			return nextoffset;
87517f326ebSJerome Forissier 
87617f326ebSJerome Forissier 		switch (tag) {
87717f326ebSJerome Forissier 		case FDT_NOP:
87817f326ebSJerome Forissier 			break;
87917f326ebSJerome Forissier 
88017f326ebSJerome Forissier 		case FDT_END:
88117f326ebSJerome Forissier 			if (depth != 0)
88217f326ebSJerome Forissier 				return -FDT_ERR_BADSTRUCTURE;
88317f326ebSJerome Forissier 			return 0;
88417f326ebSJerome Forissier 
88517f326ebSJerome Forissier 		case FDT_BEGIN_NODE:
88617f326ebSJerome Forissier 			depth++;
88717f326ebSJerome Forissier 			if (depth > INT_MAX)
88817f326ebSJerome Forissier 				return -FDT_ERR_BADSTRUCTURE;
88917f326ebSJerome Forissier 			break;
89017f326ebSJerome Forissier 
89117f326ebSJerome Forissier 		case FDT_END_NODE:
89217f326ebSJerome Forissier 			if (depth == 0)
89317f326ebSJerome Forissier 				return -FDT_ERR_BADSTRUCTURE;
89417f326ebSJerome Forissier 			depth--;
89517f326ebSJerome Forissier 			break;
89617f326ebSJerome Forissier 
89717f326ebSJerome Forissier 		case FDT_PROP:
89817f326ebSJerome Forissier 			prop = fdt_getprop_by_offset(fdt, offset, &propname,
89917f326ebSJerome Forissier 						     &err);
90017f326ebSJerome Forissier 			if (!prop)
90117f326ebSJerome Forissier 				return err;
90217f326ebSJerome Forissier 			break;
90317f326ebSJerome Forissier 
90417f326ebSJerome Forissier 		default:
90517f326ebSJerome Forissier 			return -FDT_ERR_INTERNAL;
90617f326ebSJerome Forissier 		}
90717f326ebSJerome Forissier 	}
90817f326ebSJerome Forissier }
909