xref: /arm-trusted-firmware/lib/libfdt/fdt_ro.c (revision 91f16700b400a8c0651d24a598fc48ee2997a0d7)
1*91f16700Schasinglulu // SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
2*91f16700Schasinglulu /*
3*91f16700Schasinglulu  * libfdt - Flat Device Tree manipulation
4*91f16700Schasinglulu  * Copyright (C) 2006 David Gibson, IBM Corporation.
5*91f16700Schasinglulu  */
6*91f16700Schasinglulu #include "libfdt_env.h"
7*91f16700Schasinglulu 
8*91f16700Schasinglulu #include <fdt.h>
9*91f16700Schasinglulu #include <libfdt.h>
10*91f16700Schasinglulu 
11*91f16700Schasinglulu #include "libfdt_internal.h"
12*91f16700Schasinglulu 
13*91f16700Schasinglulu static int fdt_nodename_eq_(const void *fdt, int offset,
14*91f16700Schasinglulu 			    const char *s, int len)
15*91f16700Schasinglulu {
16*91f16700Schasinglulu 	int olen;
17*91f16700Schasinglulu 	const char *p = fdt_get_name(fdt, offset, &olen);
18*91f16700Schasinglulu 
19*91f16700Schasinglulu 	if (!p || olen < len)
20*91f16700Schasinglulu 		/* short match */
21*91f16700Schasinglulu 		return 0;
22*91f16700Schasinglulu 
23*91f16700Schasinglulu 	if (memcmp(p, s, len) != 0)
24*91f16700Schasinglulu 		return 0;
25*91f16700Schasinglulu 
26*91f16700Schasinglulu 	if (p[len] == '\0')
27*91f16700Schasinglulu 		return 1;
28*91f16700Schasinglulu 	else if (!memchr(s, '@', len) && (p[len] == '@'))
29*91f16700Schasinglulu 		return 1;
30*91f16700Schasinglulu 	else
31*91f16700Schasinglulu 		return 0;
32*91f16700Schasinglulu }
33*91f16700Schasinglulu 
34*91f16700Schasinglulu const char *fdt_get_string(const void *fdt, int stroffset, int *lenp)
35*91f16700Schasinglulu {
36*91f16700Schasinglulu 	int32_t totalsize;
37*91f16700Schasinglulu 	uint32_t absoffset;
38*91f16700Schasinglulu 	size_t len;
39*91f16700Schasinglulu 	int err;
40*91f16700Schasinglulu 	const char *s, *n;
41*91f16700Schasinglulu 
42*91f16700Schasinglulu 	if (can_assume(VALID_INPUT)) {
43*91f16700Schasinglulu 		s = (const char *)fdt + fdt_off_dt_strings(fdt) + stroffset;
44*91f16700Schasinglulu 
45*91f16700Schasinglulu 		if (lenp)
46*91f16700Schasinglulu 			*lenp = strlen(s);
47*91f16700Schasinglulu 		return s;
48*91f16700Schasinglulu 	}
49*91f16700Schasinglulu 	totalsize = fdt_ro_probe_(fdt);
50*91f16700Schasinglulu 	err = totalsize;
51*91f16700Schasinglulu 	if (totalsize < 0)
52*91f16700Schasinglulu 		goto fail;
53*91f16700Schasinglulu 
54*91f16700Schasinglulu 	err = -FDT_ERR_BADOFFSET;
55*91f16700Schasinglulu 	absoffset = stroffset + fdt_off_dt_strings(fdt);
56*91f16700Schasinglulu 	if (absoffset >= (unsigned)totalsize)
57*91f16700Schasinglulu 		goto fail;
58*91f16700Schasinglulu 	len = totalsize - absoffset;
59*91f16700Schasinglulu 
60*91f16700Schasinglulu 	if (fdt_magic(fdt) == FDT_MAGIC) {
61*91f16700Schasinglulu 		if (stroffset < 0)
62*91f16700Schasinglulu 			goto fail;
63*91f16700Schasinglulu 		if (can_assume(LATEST) || fdt_version(fdt) >= 17) {
64*91f16700Schasinglulu 			if ((unsigned)stroffset >= fdt_size_dt_strings(fdt))
65*91f16700Schasinglulu 				goto fail;
66*91f16700Schasinglulu 			if ((fdt_size_dt_strings(fdt) - stroffset) < len)
67*91f16700Schasinglulu 				len = fdt_size_dt_strings(fdt) - stroffset;
68*91f16700Schasinglulu 		}
69*91f16700Schasinglulu 	} else if (fdt_magic(fdt) == FDT_SW_MAGIC) {
70*91f16700Schasinglulu 		unsigned int sw_stroffset = -stroffset;
71*91f16700Schasinglulu 
72*91f16700Schasinglulu 		if ((stroffset >= 0) ||
73*91f16700Schasinglulu 		    (sw_stroffset > fdt_size_dt_strings(fdt)))
74*91f16700Schasinglulu 			goto fail;
75*91f16700Schasinglulu 		if (sw_stroffset < len)
76*91f16700Schasinglulu 			len = sw_stroffset;
77*91f16700Schasinglulu 	} else {
78*91f16700Schasinglulu 		err = -FDT_ERR_INTERNAL;
79*91f16700Schasinglulu 		goto fail;
80*91f16700Schasinglulu 	}
81*91f16700Schasinglulu 
82*91f16700Schasinglulu 	s = (const char *)fdt + absoffset;
83*91f16700Schasinglulu 	n = memchr(s, '\0', len);
84*91f16700Schasinglulu 	if (!n) {
85*91f16700Schasinglulu 		/* missing terminating NULL */
86*91f16700Schasinglulu 		err = -FDT_ERR_TRUNCATED;
87*91f16700Schasinglulu 		goto fail;
88*91f16700Schasinglulu 	}
89*91f16700Schasinglulu 
90*91f16700Schasinglulu 	if (lenp)
91*91f16700Schasinglulu 		*lenp = n - s;
92*91f16700Schasinglulu 	return s;
93*91f16700Schasinglulu 
94*91f16700Schasinglulu fail:
95*91f16700Schasinglulu 	if (lenp)
96*91f16700Schasinglulu 		*lenp = err;
97*91f16700Schasinglulu 	return NULL;
98*91f16700Schasinglulu }
99*91f16700Schasinglulu 
100*91f16700Schasinglulu const char *fdt_string(const void *fdt, int stroffset)
101*91f16700Schasinglulu {
102*91f16700Schasinglulu 	return fdt_get_string(fdt, stroffset, NULL);
103*91f16700Schasinglulu }
104*91f16700Schasinglulu 
105*91f16700Schasinglulu static int fdt_string_eq_(const void *fdt, int stroffset,
106*91f16700Schasinglulu 			  const char *s, int len)
107*91f16700Schasinglulu {
108*91f16700Schasinglulu 	int slen;
109*91f16700Schasinglulu 	const char *p = fdt_get_string(fdt, stroffset, &slen);
110*91f16700Schasinglulu 
111*91f16700Schasinglulu 	return p && (slen == len) && (memcmp(p, s, len) == 0);
112*91f16700Schasinglulu }
113*91f16700Schasinglulu 
114*91f16700Schasinglulu int fdt_find_max_phandle(const void *fdt, uint32_t *phandle)
115*91f16700Schasinglulu {
116*91f16700Schasinglulu 	uint32_t max = 0;
117*91f16700Schasinglulu 	int offset = -1;
118*91f16700Schasinglulu 
119*91f16700Schasinglulu 	while (true) {
120*91f16700Schasinglulu 		uint32_t value;
121*91f16700Schasinglulu 
122*91f16700Schasinglulu 		offset = fdt_next_node(fdt, offset, NULL);
123*91f16700Schasinglulu 		if (offset < 0) {
124*91f16700Schasinglulu 			if (offset == -FDT_ERR_NOTFOUND)
125*91f16700Schasinglulu 				break;
126*91f16700Schasinglulu 
127*91f16700Schasinglulu 			return offset;
128*91f16700Schasinglulu 		}
129*91f16700Schasinglulu 
130*91f16700Schasinglulu 		value = fdt_get_phandle(fdt, offset);
131*91f16700Schasinglulu 
132*91f16700Schasinglulu 		if (value > max)
133*91f16700Schasinglulu 			max = value;
134*91f16700Schasinglulu 	}
135*91f16700Schasinglulu 
136*91f16700Schasinglulu 	if (phandle)
137*91f16700Schasinglulu 		*phandle = max;
138*91f16700Schasinglulu 
139*91f16700Schasinglulu 	return 0;
140*91f16700Schasinglulu }
141*91f16700Schasinglulu 
142*91f16700Schasinglulu int fdt_generate_phandle(const void *fdt, uint32_t *phandle)
143*91f16700Schasinglulu {
144*91f16700Schasinglulu 	uint32_t max;
145*91f16700Schasinglulu 	int err;
146*91f16700Schasinglulu 
147*91f16700Schasinglulu 	err = fdt_find_max_phandle(fdt, &max);
148*91f16700Schasinglulu 	if (err < 0)
149*91f16700Schasinglulu 		return err;
150*91f16700Schasinglulu 
151*91f16700Schasinglulu 	if (max == FDT_MAX_PHANDLE)
152*91f16700Schasinglulu 		return -FDT_ERR_NOPHANDLES;
153*91f16700Schasinglulu 
154*91f16700Schasinglulu 	if (phandle)
155*91f16700Schasinglulu 		*phandle = max + 1;
156*91f16700Schasinglulu 
157*91f16700Schasinglulu 	return 0;
158*91f16700Schasinglulu }
159*91f16700Schasinglulu 
160*91f16700Schasinglulu static const struct fdt_reserve_entry *fdt_mem_rsv(const void *fdt, int n)
161*91f16700Schasinglulu {
162*91f16700Schasinglulu 	unsigned int offset = n * sizeof(struct fdt_reserve_entry);
163*91f16700Schasinglulu 	unsigned int absoffset = fdt_off_mem_rsvmap(fdt) + offset;
164*91f16700Schasinglulu 
165*91f16700Schasinglulu 	if (!can_assume(VALID_INPUT)) {
166*91f16700Schasinglulu 		if (absoffset < fdt_off_mem_rsvmap(fdt))
167*91f16700Schasinglulu 			return NULL;
168*91f16700Schasinglulu 		if (absoffset > fdt_totalsize(fdt) -
169*91f16700Schasinglulu 		    sizeof(struct fdt_reserve_entry))
170*91f16700Schasinglulu 			return NULL;
171*91f16700Schasinglulu 	}
172*91f16700Schasinglulu 	return fdt_mem_rsv_(fdt, n);
173*91f16700Schasinglulu }
174*91f16700Schasinglulu 
175*91f16700Schasinglulu int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size)
176*91f16700Schasinglulu {
177*91f16700Schasinglulu 	const struct fdt_reserve_entry *re;
178*91f16700Schasinglulu 
179*91f16700Schasinglulu 	FDT_RO_PROBE(fdt);
180*91f16700Schasinglulu 	re = fdt_mem_rsv(fdt, n);
181*91f16700Schasinglulu 	if (!can_assume(VALID_INPUT) && !re)
182*91f16700Schasinglulu 		return -FDT_ERR_BADOFFSET;
183*91f16700Schasinglulu 
184*91f16700Schasinglulu 	*address = fdt64_ld_(&re->address);
185*91f16700Schasinglulu 	*size = fdt64_ld_(&re->size);
186*91f16700Schasinglulu 	return 0;
187*91f16700Schasinglulu }
188*91f16700Schasinglulu 
189*91f16700Schasinglulu int fdt_num_mem_rsv(const void *fdt)
190*91f16700Schasinglulu {
191*91f16700Schasinglulu 	int i;
192*91f16700Schasinglulu 	const struct fdt_reserve_entry *re;
193*91f16700Schasinglulu 
194*91f16700Schasinglulu 	for (i = 0; (re = fdt_mem_rsv(fdt, i)) != NULL; i++) {
195*91f16700Schasinglulu 		if (fdt64_ld_(&re->size) == 0)
196*91f16700Schasinglulu 			return i;
197*91f16700Schasinglulu 	}
198*91f16700Schasinglulu 	return -FDT_ERR_TRUNCATED;
199*91f16700Schasinglulu }
200*91f16700Schasinglulu 
201*91f16700Schasinglulu static int nextprop_(const void *fdt, int offset)
202*91f16700Schasinglulu {
203*91f16700Schasinglulu 	uint32_t tag;
204*91f16700Schasinglulu 	int nextoffset;
205*91f16700Schasinglulu 
206*91f16700Schasinglulu 	do {
207*91f16700Schasinglulu 		tag = fdt_next_tag(fdt, offset, &nextoffset);
208*91f16700Schasinglulu 
209*91f16700Schasinglulu 		switch (tag) {
210*91f16700Schasinglulu 		case FDT_END:
211*91f16700Schasinglulu 			if (nextoffset >= 0)
212*91f16700Schasinglulu 				return -FDT_ERR_BADSTRUCTURE;
213*91f16700Schasinglulu 			else
214*91f16700Schasinglulu 				return nextoffset;
215*91f16700Schasinglulu 
216*91f16700Schasinglulu 		case FDT_PROP:
217*91f16700Schasinglulu 			return offset;
218*91f16700Schasinglulu 		}
219*91f16700Schasinglulu 		offset = nextoffset;
220*91f16700Schasinglulu 	} while (tag == FDT_NOP);
221*91f16700Schasinglulu 
222*91f16700Schasinglulu 	return -FDT_ERR_NOTFOUND;
223*91f16700Schasinglulu }
224*91f16700Schasinglulu 
225*91f16700Schasinglulu int fdt_subnode_offset_namelen(const void *fdt, int offset,
226*91f16700Schasinglulu 			       const char *name, int namelen)
227*91f16700Schasinglulu {
228*91f16700Schasinglulu 	int depth;
229*91f16700Schasinglulu 
230*91f16700Schasinglulu 	FDT_RO_PROBE(fdt);
231*91f16700Schasinglulu 
232*91f16700Schasinglulu 	for (depth = 0;
233*91f16700Schasinglulu 	     (offset >= 0) && (depth >= 0);
234*91f16700Schasinglulu 	     offset = fdt_next_node(fdt, offset, &depth))
235*91f16700Schasinglulu 		if ((depth == 1)
236*91f16700Schasinglulu 		    && fdt_nodename_eq_(fdt, offset, name, namelen))
237*91f16700Schasinglulu 			return offset;
238*91f16700Schasinglulu 
239*91f16700Schasinglulu 	if (depth < 0)
240*91f16700Schasinglulu 		return -FDT_ERR_NOTFOUND;
241*91f16700Schasinglulu 	return offset; /* error */
242*91f16700Schasinglulu }
243*91f16700Schasinglulu 
244*91f16700Schasinglulu int fdt_subnode_offset(const void *fdt, int parentoffset,
245*91f16700Schasinglulu 		       const char *name)
246*91f16700Schasinglulu {
247*91f16700Schasinglulu 	return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name));
248*91f16700Schasinglulu }
249*91f16700Schasinglulu 
250*91f16700Schasinglulu int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen)
251*91f16700Schasinglulu {
252*91f16700Schasinglulu 	const char *end = path + namelen;
253*91f16700Schasinglulu 	const char *p = path;
254*91f16700Schasinglulu 	int offset = 0;
255*91f16700Schasinglulu 
256*91f16700Schasinglulu 	FDT_RO_PROBE(fdt);
257*91f16700Schasinglulu 
258*91f16700Schasinglulu 	/* see if we have an alias */
259*91f16700Schasinglulu 	if (*path != '/') {
260*91f16700Schasinglulu 		const char *q = memchr(path, '/', end - p);
261*91f16700Schasinglulu 
262*91f16700Schasinglulu 		if (!q)
263*91f16700Schasinglulu 			q = end;
264*91f16700Schasinglulu 
265*91f16700Schasinglulu 		p = fdt_get_alias_namelen(fdt, p, q - p);
266*91f16700Schasinglulu 		if (!p)
267*91f16700Schasinglulu 			return -FDT_ERR_BADPATH;
268*91f16700Schasinglulu 		offset = fdt_path_offset(fdt, p);
269*91f16700Schasinglulu 
270*91f16700Schasinglulu 		p = q;
271*91f16700Schasinglulu 	}
272*91f16700Schasinglulu 
273*91f16700Schasinglulu 	while (p < end) {
274*91f16700Schasinglulu 		const char *q;
275*91f16700Schasinglulu 
276*91f16700Schasinglulu 		while (*p == '/') {
277*91f16700Schasinglulu 			p++;
278*91f16700Schasinglulu 			if (p == end)
279*91f16700Schasinglulu 				return offset;
280*91f16700Schasinglulu 		}
281*91f16700Schasinglulu 		q = memchr(p, '/', end - p);
282*91f16700Schasinglulu 		if (! q)
283*91f16700Schasinglulu 			q = end;
284*91f16700Schasinglulu 
285*91f16700Schasinglulu 		offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p);
286*91f16700Schasinglulu 		if (offset < 0)
287*91f16700Schasinglulu 			return offset;
288*91f16700Schasinglulu 
289*91f16700Schasinglulu 		p = q;
290*91f16700Schasinglulu 	}
291*91f16700Schasinglulu 
292*91f16700Schasinglulu 	return offset;
293*91f16700Schasinglulu }
294*91f16700Schasinglulu 
295*91f16700Schasinglulu int fdt_path_offset(const void *fdt, const char *path)
296*91f16700Schasinglulu {
297*91f16700Schasinglulu 	return fdt_path_offset_namelen(fdt, path, strlen(path));
298*91f16700Schasinglulu }
299*91f16700Schasinglulu 
300*91f16700Schasinglulu const char *fdt_get_name(const void *fdt, int nodeoffset, int *len)
301*91f16700Schasinglulu {
302*91f16700Schasinglulu 	const struct fdt_node_header *nh = fdt_offset_ptr_(fdt, nodeoffset);
303*91f16700Schasinglulu 	const char *nameptr;
304*91f16700Schasinglulu 	int err;
305*91f16700Schasinglulu 
306*91f16700Schasinglulu 	if (((err = fdt_ro_probe_(fdt)) < 0)
307*91f16700Schasinglulu 	    || ((err = fdt_check_node_offset_(fdt, nodeoffset)) < 0))
308*91f16700Schasinglulu 			goto fail;
309*91f16700Schasinglulu 
310*91f16700Schasinglulu 	nameptr = nh->name;
311*91f16700Schasinglulu 
312*91f16700Schasinglulu 	if (!can_assume(LATEST) && fdt_version(fdt) < 0x10) {
313*91f16700Schasinglulu 		/*
314*91f16700Schasinglulu 		 * For old FDT versions, match the naming conventions of V16:
315*91f16700Schasinglulu 		 * give only the leaf name (after all /). The actual tree
316*91f16700Schasinglulu 		 * contents are loosely checked.
317*91f16700Schasinglulu 		 */
318*91f16700Schasinglulu 		const char *leaf;
319*91f16700Schasinglulu 		leaf = strrchr(nameptr, '/');
320*91f16700Schasinglulu 		if (leaf == NULL) {
321*91f16700Schasinglulu 			err = -FDT_ERR_BADSTRUCTURE;
322*91f16700Schasinglulu 			goto fail;
323*91f16700Schasinglulu 		}
324*91f16700Schasinglulu 		nameptr = leaf+1;
325*91f16700Schasinglulu 	}
326*91f16700Schasinglulu 
327*91f16700Schasinglulu 	if (len)
328*91f16700Schasinglulu 		*len = strlen(nameptr);
329*91f16700Schasinglulu 
330*91f16700Schasinglulu 	return nameptr;
331*91f16700Schasinglulu 
332*91f16700Schasinglulu  fail:
333*91f16700Schasinglulu 	if (len)
334*91f16700Schasinglulu 		*len = err;
335*91f16700Schasinglulu 	return NULL;
336*91f16700Schasinglulu }
337*91f16700Schasinglulu 
338*91f16700Schasinglulu int fdt_first_property_offset(const void *fdt, int nodeoffset)
339*91f16700Schasinglulu {
340*91f16700Schasinglulu 	int offset;
341*91f16700Schasinglulu 
342*91f16700Schasinglulu 	if ((offset = fdt_check_node_offset_(fdt, nodeoffset)) < 0)
343*91f16700Schasinglulu 		return offset;
344*91f16700Schasinglulu 
345*91f16700Schasinglulu 	return nextprop_(fdt, offset);
346*91f16700Schasinglulu }
347*91f16700Schasinglulu 
348*91f16700Schasinglulu int fdt_next_property_offset(const void *fdt, int offset)
349*91f16700Schasinglulu {
350*91f16700Schasinglulu 	if ((offset = fdt_check_prop_offset_(fdt, offset)) < 0)
351*91f16700Schasinglulu 		return offset;
352*91f16700Schasinglulu 
353*91f16700Schasinglulu 	return nextprop_(fdt, offset);
354*91f16700Schasinglulu }
355*91f16700Schasinglulu 
356*91f16700Schasinglulu static const struct fdt_property *fdt_get_property_by_offset_(const void *fdt,
357*91f16700Schasinglulu 						              int offset,
358*91f16700Schasinglulu 						              int *lenp)
359*91f16700Schasinglulu {
360*91f16700Schasinglulu 	int err;
361*91f16700Schasinglulu 	const struct fdt_property *prop;
362*91f16700Schasinglulu 
363*91f16700Schasinglulu 	if (!can_assume(VALID_INPUT) &&
364*91f16700Schasinglulu 	    (err = fdt_check_prop_offset_(fdt, offset)) < 0) {
365*91f16700Schasinglulu 		if (lenp)
366*91f16700Schasinglulu 			*lenp = err;
367*91f16700Schasinglulu 		return NULL;
368*91f16700Schasinglulu 	}
369*91f16700Schasinglulu 
370*91f16700Schasinglulu 	prop = fdt_offset_ptr_(fdt, offset);
371*91f16700Schasinglulu 
372*91f16700Schasinglulu 	if (lenp)
373*91f16700Schasinglulu 		*lenp = fdt32_ld_(&prop->len);
374*91f16700Schasinglulu 
375*91f16700Schasinglulu 	return prop;
376*91f16700Schasinglulu }
377*91f16700Schasinglulu 
378*91f16700Schasinglulu const struct fdt_property *fdt_get_property_by_offset(const void *fdt,
379*91f16700Schasinglulu 						      int offset,
380*91f16700Schasinglulu 						      int *lenp)
381*91f16700Schasinglulu {
382*91f16700Schasinglulu 	/* Prior to version 16, properties may need realignment
383*91f16700Schasinglulu 	 * and this API does not work. fdt_getprop_*() will, however. */
384*91f16700Schasinglulu 
385*91f16700Schasinglulu 	if (!can_assume(LATEST) && fdt_version(fdt) < 0x10) {
386*91f16700Schasinglulu 		if (lenp)
387*91f16700Schasinglulu 			*lenp = -FDT_ERR_BADVERSION;
388*91f16700Schasinglulu 		return NULL;
389*91f16700Schasinglulu 	}
390*91f16700Schasinglulu 
391*91f16700Schasinglulu 	return fdt_get_property_by_offset_(fdt, offset, lenp);
392*91f16700Schasinglulu }
393*91f16700Schasinglulu 
394*91f16700Schasinglulu static const struct fdt_property *fdt_get_property_namelen_(const void *fdt,
395*91f16700Schasinglulu 						            int offset,
396*91f16700Schasinglulu 						            const char *name,
397*91f16700Schasinglulu 						            int namelen,
398*91f16700Schasinglulu 							    int *lenp,
399*91f16700Schasinglulu 							    int *poffset)
400*91f16700Schasinglulu {
401*91f16700Schasinglulu 	for (offset = fdt_first_property_offset(fdt, offset);
402*91f16700Schasinglulu 	     (offset >= 0);
403*91f16700Schasinglulu 	     (offset = fdt_next_property_offset(fdt, offset))) {
404*91f16700Schasinglulu 		const struct fdt_property *prop;
405*91f16700Schasinglulu 
406*91f16700Schasinglulu 		prop = fdt_get_property_by_offset_(fdt, offset, lenp);
407*91f16700Schasinglulu 		if (!can_assume(LIBFDT_FLAWLESS) && !prop) {
408*91f16700Schasinglulu 			offset = -FDT_ERR_INTERNAL;
409*91f16700Schasinglulu 			break;
410*91f16700Schasinglulu 		}
411*91f16700Schasinglulu 		if (fdt_string_eq_(fdt, fdt32_ld_(&prop->nameoff),
412*91f16700Schasinglulu 				   name, namelen)) {
413*91f16700Schasinglulu 			if (poffset)
414*91f16700Schasinglulu 				*poffset = offset;
415*91f16700Schasinglulu 			return prop;
416*91f16700Schasinglulu 		}
417*91f16700Schasinglulu 	}
418*91f16700Schasinglulu 
419*91f16700Schasinglulu 	if (lenp)
420*91f16700Schasinglulu 		*lenp = offset;
421*91f16700Schasinglulu 	return NULL;
422*91f16700Schasinglulu }
423*91f16700Schasinglulu 
424*91f16700Schasinglulu 
425*91f16700Schasinglulu const struct fdt_property *fdt_get_property_namelen(const void *fdt,
426*91f16700Schasinglulu 						    int offset,
427*91f16700Schasinglulu 						    const char *name,
428*91f16700Schasinglulu 						    int namelen, int *lenp)
429*91f16700Schasinglulu {
430*91f16700Schasinglulu 	/* Prior to version 16, properties may need realignment
431*91f16700Schasinglulu 	 * and this API does not work. fdt_getprop_*() will, however. */
432*91f16700Schasinglulu 	if (!can_assume(LATEST) && fdt_version(fdt) < 0x10) {
433*91f16700Schasinglulu 		if (lenp)
434*91f16700Schasinglulu 			*lenp = -FDT_ERR_BADVERSION;
435*91f16700Schasinglulu 		return NULL;
436*91f16700Schasinglulu 	}
437*91f16700Schasinglulu 
438*91f16700Schasinglulu 	return fdt_get_property_namelen_(fdt, offset, name, namelen, lenp,
439*91f16700Schasinglulu 					 NULL);
440*91f16700Schasinglulu }
441*91f16700Schasinglulu 
442*91f16700Schasinglulu 
443*91f16700Schasinglulu const struct fdt_property *fdt_get_property(const void *fdt,
444*91f16700Schasinglulu 					    int nodeoffset,
445*91f16700Schasinglulu 					    const char *name, int *lenp)
446*91f16700Schasinglulu {
447*91f16700Schasinglulu 	return fdt_get_property_namelen(fdt, nodeoffset, name,
448*91f16700Schasinglulu 					strlen(name), lenp);
449*91f16700Schasinglulu }
450*91f16700Schasinglulu 
451*91f16700Schasinglulu const void *fdt_getprop_namelen(const void *fdt, int nodeoffset,
452*91f16700Schasinglulu 				const char *name, int namelen, int *lenp)
453*91f16700Schasinglulu {
454*91f16700Schasinglulu 	int poffset;
455*91f16700Schasinglulu 	const struct fdt_property *prop;
456*91f16700Schasinglulu 
457*91f16700Schasinglulu 	prop = fdt_get_property_namelen_(fdt, nodeoffset, name, namelen, lenp,
458*91f16700Schasinglulu 					 &poffset);
459*91f16700Schasinglulu 	if (!prop)
460*91f16700Schasinglulu 		return NULL;
461*91f16700Schasinglulu 
462*91f16700Schasinglulu 	/* Handle realignment */
463*91f16700Schasinglulu 	if (!can_assume(LATEST) && fdt_version(fdt) < 0x10 &&
464*91f16700Schasinglulu 	    (poffset + sizeof(*prop)) % 8 && fdt32_ld_(&prop->len) >= 8)
465*91f16700Schasinglulu 		return prop->data + 4;
466*91f16700Schasinglulu 	return prop->data;
467*91f16700Schasinglulu }
468*91f16700Schasinglulu 
469*91f16700Schasinglulu const void *fdt_getprop_by_offset(const void *fdt, int offset,
470*91f16700Schasinglulu 				  const char **namep, int *lenp)
471*91f16700Schasinglulu {
472*91f16700Schasinglulu 	const struct fdt_property *prop;
473*91f16700Schasinglulu 
474*91f16700Schasinglulu 	prop = fdt_get_property_by_offset_(fdt, offset, lenp);
475*91f16700Schasinglulu 	if (!prop)
476*91f16700Schasinglulu 		return NULL;
477*91f16700Schasinglulu 	if (namep) {
478*91f16700Schasinglulu 		const char *name;
479*91f16700Schasinglulu 		int namelen;
480*91f16700Schasinglulu 
481*91f16700Schasinglulu 		if (!can_assume(VALID_INPUT)) {
482*91f16700Schasinglulu 			name = fdt_get_string(fdt, fdt32_ld_(&prop->nameoff),
483*91f16700Schasinglulu 					      &namelen);
484*91f16700Schasinglulu 			*namep = name;
485*91f16700Schasinglulu 			if (!name) {
486*91f16700Schasinglulu 				if (lenp)
487*91f16700Schasinglulu 					*lenp = namelen;
488*91f16700Schasinglulu 				return NULL;
489*91f16700Schasinglulu 			}
490*91f16700Schasinglulu 		} else {
491*91f16700Schasinglulu 			*namep = fdt_string(fdt, fdt32_ld_(&prop->nameoff));
492*91f16700Schasinglulu 		}
493*91f16700Schasinglulu 	}
494*91f16700Schasinglulu 
495*91f16700Schasinglulu 	/* Handle realignment */
496*91f16700Schasinglulu 	if (!can_assume(LATEST) && fdt_version(fdt) < 0x10 &&
497*91f16700Schasinglulu 	    (offset + sizeof(*prop)) % 8 && fdt32_ld_(&prop->len) >= 8)
498*91f16700Schasinglulu 		return prop->data + 4;
499*91f16700Schasinglulu 	return prop->data;
500*91f16700Schasinglulu }
501*91f16700Schasinglulu 
502*91f16700Schasinglulu const void *fdt_getprop(const void *fdt, int nodeoffset,
503*91f16700Schasinglulu 			const char *name, int *lenp)
504*91f16700Schasinglulu {
505*91f16700Schasinglulu 	return fdt_getprop_namelen(fdt, nodeoffset, name, strlen(name), lenp);
506*91f16700Schasinglulu }
507*91f16700Schasinglulu 
508*91f16700Schasinglulu uint32_t fdt_get_phandle(const void *fdt, int nodeoffset)
509*91f16700Schasinglulu {
510*91f16700Schasinglulu 	const fdt32_t *php;
511*91f16700Schasinglulu 	int len;
512*91f16700Schasinglulu 
513*91f16700Schasinglulu 	/* FIXME: This is a bit sub-optimal, since we potentially scan
514*91f16700Schasinglulu 	 * over all the properties twice. */
515*91f16700Schasinglulu 	php = fdt_getprop(fdt, nodeoffset, "phandle", &len);
516*91f16700Schasinglulu 	if (!php || (len != sizeof(*php))) {
517*91f16700Schasinglulu 		php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len);
518*91f16700Schasinglulu 		if (!php || (len != sizeof(*php)))
519*91f16700Schasinglulu 			return 0;
520*91f16700Schasinglulu 	}
521*91f16700Schasinglulu 
522*91f16700Schasinglulu 	return fdt32_ld_(php);
523*91f16700Schasinglulu }
524*91f16700Schasinglulu 
525*91f16700Schasinglulu const char *fdt_get_alias_namelen(const void *fdt,
526*91f16700Schasinglulu 				  const char *name, int namelen)
527*91f16700Schasinglulu {
528*91f16700Schasinglulu 	int aliasoffset;
529*91f16700Schasinglulu 
530*91f16700Schasinglulu 	aliasoffset = fdt_path_offset(fdt, "/aliases");
531*91f16700Schasinglulu 	if (aliasoffset < 0)
532*91f16700Schasinglulu 		return NULL;
533*91f16700Schasinglulu 
534*91f16700Schasinglulu 	return fdt_getprop_namelen(fdt, aliasoffset, name, namelen, NULL);
535*91f16700Schasinglulu }
536*91f16700Schasinglulu 
537*91f16700Schasinglulu const char *fdt_get_alias(const void *fdt, const char *name)
538*91f16700Schasinglulu {
539*91f16700Schasinglulu 	return fdt_get_alias_namelen(fdt, name, strlen(name));
540*91f16700Schasinglulu }
541*91f16700Schasinglulu 
542*91f16700Schasinglulu int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen)
543*91f16700Schasinglulu {
544*91f16700Schasinglulu 	int pdepth = 0, p = 0;
545*91f16700Schasinglulu 	int offset, depth, namelen;
546*91f16700Schasinglulu 	const char *name;
547*91f16700Schasinglulu 
548*91f16700Schasinglulu 	FDT_RO_PROBE(fdt);
549*91f16700Schasinglulu 
550*91f16700Schasinglulu 	if (buflen < 2)
551*91f16700Schasinglulu 		return -FDT_ERR_NOSPACE;
552*91f16700Schasinglulu 
553*91f16700Schasinglulu 	for (offset = 0, depth = 0;
554*91f16700Schasinglulu 	     (offset >= 0) && (offset <= nodeoffset);
555*91f16700Schasinglulu 	     offset = fdt_next_node(fdt, offset, &depth)) {
556*91f16700Schasinglulu 		while (pdepth > depth) {
557*91f16700Schasinglulu 			do {
558*91f16700Schasinglulu 				p--;
559*91f16700Schasinglulu 			} while (buf[p-1] != '/');
560*91f16700Schasinglulu 			pdepth--;
561*91f16700Schasinglulu 		}
562*91f16700Schasinglulu 
563*91f16700Schasinglulu 		if (pdepth >= depth) {
564*91f16700Schasinglulu 			name = fdt_get_name(fdt, offset, &namelen);
565*91f16700Schasinglulu 			if (!name)
566*91f16700Schasinglulu 				return namelen;
567*91f16700Schasinglulu 			if ((p + namelen + 1) <= buflen) {
568*91f16700Schasinglulu 				memcpy(buf + p, name, namelen);
569*91f16700Schasinglulu 				p += namelen;
570*91f16700Schasinglulu 				buf[p++] = '/';
571*91f16700Schasinglulu 				pdepth++;
572*91f16700Schasinglulu 			}
573*91f16700Schasinglulu 		}
574*91f16700Schasinglulu 
575*91f16700Schasinglulu 		if (offset == nodeoffset) {
576*91f16700Schasinglulu 			if (pdepth < (depth + 1))
577*91f16700Schasinglulu 				return -FDT_ERR_NOSPACE;
578*91f16700Schasinglulu 
579*91f16700Schasinglulu 			if (p > 1) /* special case so that root path is "/", not "" */
580*91f16700Schasinglulu 				p--;
581*91f16700Schasinglulu 			buf[p] = '\0';
582*91f16700Schasinglulu 			return 0;
583*91f16700Schasinglulu 		}
584*91f16700Schasinglulu 	}
585*91f16700Schasinglulu 
586*91f16700Schasinglulu 	if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
587*91f16700Schasinglulu 		return -FDT_ERR_BADOFFSET;
588*91f16700Schasinglulu 	else if (offset == -FDT_ERR_BADOFFSET)
589*91f16700Schasinglulu 		return -FDT_ERR_BADSTRUCTURE;
590*91f16700Schasinglulu 
591*91f16700Schasinglulu 	return offset; /* error from fdt_next_node() */
592*91f16700Schasinglulu }
593*91f16700Schasinglulu 
594*91f16700Schasinglulu int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset,
595*91f16700Schasinglulu 				 int supernodedepth, int *nodedepth)
596*91f16700Schasinglulu {
597*91f16700Schasinglulu 	int offset, depth;
598*91f16700Schasinglulu 	int supernodeoffset = -FDT_ERR_INTERNAL;
599*91f16700Schasinglulu 
600*91f16700Schasinglulu 	FDT_RO_PROBE(fdt);
601*91f16700Schasinglulu 
602*91f16700Schasinglulu 	if (supernodedepth < 0)
603*91f16700Schasinglulu 		return -FDT_ERR_NOTFOUND;
604*91f16700Schasinglulu 
605*91f16700Schasinglulu 	for (offset = 0, depth = 0;
606*91f16700Schasinglulu 	     (offset >= 0) && (offset <= nodeoffset);
607*91f16700Schasinglulu 	     offset = fdt_next_node(fdt, offset, &depth)) {
608*91f16700Schasinglulu 		if (depth == supernodedepth)
609*91f16700Schasinglulu 			supernodeoffset = offset;
610*91f16700Schasinglulu 
611*91f16700Schasinglulu 		if (offset == nodeoffset) {
612*91f16700Schasinglulu 			if (nodedepth)
613*91f16700Schasinglulu 				*nodedepth = depth;
614*91f16700Schasinglulu 
615*91f16700Schasinglulu 			if (supernodedepth > depth)
616*91f16700Schasinglulu 				return -FDT_ERR_NOTFOUND;
617*91f16700Schasinglulu 			else
618*91f16700Schasinglulu 				return supernodeoffset;
619*91f16700Schasinglulu 		}
620*91f16700Schasinglulu 	}
621*91f16700Schasinglulu 
622*91f16700Schasinglulu 	if (!can_assume(VALID_INPUT)) {
623*91f16700Schasinglulu 		if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
624*91f16700Schasinglulu 			return -FDT_ERR_BADOFFSET;
625*91f16700Schasinglulu 		else if (offset == -FDT_ERR_BADOFFSET)
626*91f16700Schasinglulu 			return -FDT_ERR_BADSTRUCTURE;
627*91f16700Schasinglulu 	}
628*91f16700Schasinglulu 
629*91f16700Schasinglulu 	return offset; /* error from fdt_next_node() */
630*91f16700Schasinglulu }
631*91f16700Schasinglulu 
632*91f16700Schasinglulu int fdt_node_depth(const void *fdt, int nodeoffset)
633*91f16700Schasinglulu {
634*91f16700Schasinglulu 	int nodedepth;
635*91f16700Schasinglulu 	int err;
636*91f16700Schasinglulu 
637*91f16700Schasinglulu 	err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth);
638*91f16700Schasinglulu 	if (err)
639*91f16700Schasinglulu 		return (can_assume(LIBFDT_FLAWLESS) || err < 0) ? err :
640*91f16700Schasinglulu 			-FDT_ERR_INTERNAL;
641*91f16700Schasinglulu 	return nodedepth;
642*91f16700Schasinglulu }
643*91f16700Schasinglulu 
644*91f16700Schasinglulu int fdt_parent_offset(const void *fdt, int nodeoffset)
645*91f16700Schasinglulu {
646*91f16700Schasinglulu 	int nodedepth = fdt_node_depth(fdt, nodeoffset);
647*91f16700Schasinglulu 
648*91f16700Schasinglulu 	if (nodedepth < 0)
649*91f16700Schasinglulu 		return nodedepth;
650*91f16700Schasinglulu 	return fdt_supernode_atdepth_offset(fdt, nodeoffset,
651*91f16700Schasinglulu 					    nodedepth - 1, NULL);
652*91f16700Schasinglulu }
653*91f16700Schasinglulu 
654*91f16700Schasinglulu int fdt_node_offset_by_prop_value(const void *fdt, int startoffset,
655*91f16700Schasinglulu 				  const char *propname,
656*91f16700Schasinglulu 				  const void *propval, int proplen)
657*91f16700Schasinglulu {
658*91f16700Schasinglulu 	int offset;
659*91f16700Schasinglulu 	const void *val;
660*91f16700Schasinglulu 	int len;
661*91f16700Schasinglulu 
662*91f16700Schasinglulu 	FDT_RO_PROBE(fdt);
663*91f16700Schasinglulu 
664*91f16700Schasinglulu 	/* FIXME: The algorithm here is pretty horrible: we scan each
665*91f16700Schasinglulu 	 * property of a node in fdt_getprop(), then if that didn't
666*91f16700Schasinglulu 	 * find what we want, we scan over them again making our way
667*91f16700Schasinglulu 	 * to the next node.  Still it's the easiest to implement
668*91f16700Schasinglulu 	 * approach; performance can come later. */
669*91f16700Schasinglulu 	for (offset = fdt_next_node(fdt, startoffset, NULL);
670*91f16700Schasinglulu 	     offset >= 0;
671*91f16700Schasinglulu 	     offset = fdt_next_node(fdt, offset, NULL)) {
672*91f16700Schasinglulu 		val = fdt_getprop(fdt, offset, propname, &len);
673*91f16700Schasinglulu 		if (val && (len == proplen)
674*91f16700Schasinglulu 		    && (memcmp(val, propval, len) == 0))
675*91f16700Schasinglulu 			return offset;
676*91f16700Schasinglulu 	}
677*91f16700Schasinglulu 
678*91f16700Schasinglulu 	return offset; /* error from fdt_next_node() */
679*91f16700Schasinglulu }
680*91f16700Schasinglulu 
681*91f16700Schasinglulu int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle)
682*91f16700Schasinglulu {
683*91f16700Schasinglulu 	int offset;
684*91f16700Schasinglulu 
685*91f16700Schasinglulu 	if ((phandle == 0) || (phandle == ~0U))
686*91f16700Schasinglulu 		return -FDT_ERR_BADPHANDLE;
687*91f16700Schasinglulu 
688*91f16700Schasinglulu 	FDT_RO_PROBE(fdt);
689*91f16700Schasinglulu 
690*91f16700Schasinglulu 	/* FIXME: The algorithm here is pretty horrible: we
691*91f16700Schasinglulu 	 * potentially scan each property of a node in
692*91f16700Schasinglulu 	 * fdt_get_phandle(), then if that didn't find what
693*91f16700Schasinglulu 	 * we want, we scan over them again making our way to the next
694*91f16700Schasinglulu 	 * node.  Still it's the easiest to implement approach;
695*91f16700Schasinglulu 	 * performance can come later. */
696*91f16700Schasinglulu 	for (offset = fdt_next_node(fdt, -1, NULL);
697*91f16700Schasinglulu 	     offset >= 0;
698*91f16700Schasinglulu 	     offset = fdt_next_node(fdt, offset, NULL)) {
699*91f16700Schasinglulu 		if (fdt_get_phandle(fdt, offset) == phandle)
700*91f16700Schasinglulu 			return offset;
701*91f16700Schasinglulu 	}
702*91f16700Schasinglulu 
703*91f16700Schasinglulu 	return offset; /* error from fdt_next_node() */
704*91f16700Schasinglulu }
705*91f16700Schasinglulu 
706*91f16700Schasinglulu int fdt_stringlist_contains(const char *strlist, int listlen, const char *str)
707*91f16700Schasinglulu {
708*91f16700Schasinglulu 	int len = strlen(str);
709*91f16700Schasinglulu 	const char *p;
710*91f16700Schasinglulu 
711*91f16700Schasinglulu 	while (listlen >= len) {
712*91f16700Schasinglulu 		if (memcmp(str, strlist, len+1) == 0)
713*91f16700Schasinglulu 			return 1;
714*91f16700Schasinglulu 		p = memchr(strlist, '\0', listlen);
715*91f16700Schasinglulu 		if (!p)
716*91f16700Schasinglulu 			return 0; /* malformed strlist.. */
717*91f16700Schasinglulu 		listlen -= (p-strlist) + 1;
718*91f16700Schasinglulu 		strlist = p + 1;
719*91f16700Schasinglulu 	}
720*91f16700Schasinglulu 	return 0;
721*91f16700Schasinglulu }
722*91f16700Schasinglulu 
723*91f16700Schasinglulu int fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property)
724*91f16700Schasinglulu {
725*91f16700Schasinglulu 	const char *list, *end;
726*91f16700Schasinglulu 	int length, count = 0;
727*91f16700Schasinglulu 
728*91f16700Schasinglulu 	list = fdt_getprop(fdt, nodeoffset, property, &length);
729*91f16700Schasinglulu 	if (!list)
730*91f16700Schasinglulu 		return length;
731*91f16700Schasinglulu 
732*91f16700Schasinglulu 	end = list + length;
733*91f16700Schasinglulu 
734*91f16700Schasinglulu 	while (list < end) {
735*91f16700Schasinglulu 		length = strnlen(list, end - list) + 1;
736*91f16700Schasinglulu 
737*91f16700Schasinglulu 		/* Abort if the last string isn't properly NUL-terminated. */
738*91f16700Schasinglulu 		if (list + length > end)
739*91f16700Schasinglulu 			return -FDT_ERR_BADVALUE;
740*91f16700Schasinglulu 
741*91f16700Schasinglulu 		list += length;
742*91f16700Schasinglulu 		count++;
743*91f16700Schasinglulu 	}
744*91f16700Schasinglulu 
745*91f16700Schasinglulu 	return count;
746*91f16700Schasinglulu }
747*91f16700Schasinglulu 
748*91f16700Schasinglulu int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property,
749*91f16700Schasinglulu 			  const char *string)
750*91f16700Schasinglulu {
751*91f16700Schasinglulu 	int length, len, idx = 0;
752*91f16700Schasinglulu 	const char *list, *end;
753*91f16700Schasinglulu 
754*91f16700Schasinglulu 	list = fdt_getprop(fdt, nodeoffset, property, &length);
755*91f16700Schasinglulu 	if (!list)
756*91f16700Schasinglulu 		return length;
757*91f16700Schasinglulu 
758*91f16700Schasinglulu 	len = strlen(string) + 1;
759*91f16700Schasinglulu 	end = list + length;
760*91f16700Schasinglulu 
761*91f16700Schasinglulu 	while (list < end) {
762*91f16700Schasinglulu 		length = strnlen(list, end - list) + 1;
763*91f16700Schasinglulu 
764*91f16700Schasinglulu 		/* Abort if the last string isn't properly NUL-terminated. */
765*91f16700Schasinglulu 		if (list + length > end)
766*91f16700Schasinglulu 			return -FDT_ERR_BADVALUE;
767*91f16700Schasinglulu 
768*91f16700Schasinglulu 		if (length == len && memcmp(list, string, length) == 0)
769*91f16700Schasinglulu 			return idx;
770*91f16700Schasinglulu 
771*91f16700Schasinglulu 		list += length;
772*91f16700Schasinglulu 		idx++;
773*91f16700Schasinglulu 	}
774*91f16700Schasinglulu 
775*91f16700Schasinglulu 	return -FDT_ERR_NOTFOUND;
776*91f16700Schasinglulu }
777*91f16700Schasinglulu 
778*91f16700Schasinglulu const char *fdt_stringlist_get(const void *fdt, int nodeoffset,
779*91f16700Schasinglulu 			       const char *property, int idx,
780*91f16700Schasinglulu 			       int *lenp)
781*91f16700Schasinglulu {
782*91f16700Schasinglulu 	const char *list, *end;
783*91f16700Schasinglulu 	int length;
784*91f16700Schasinglulu 
785*91f16700Schasinglulu 	list = fdt_getprop(fdt, nodeoffset, property, &length);
786*91f16700Schasinglulu 	if (!list) {
787*91f16700Schasinglulu 		if (lenp)
788*91f16700Schasinglulu 			*lenp = length;
789*91f16700Schasinglulu 
790*91f16700Schasinglulu 		return NULL;
791*91f16700Schasinglulu 	}
792*91f16700Schasinglulu 
793*91f16700Schasinglulu 	end = list + length;
794*91f16700Schasinglulu 
795*91f16700Schasinglulu 	while (list < end) {
796*91f16700Schasinglulu 		length = strnlen(list, end - list) + 1;
797*91f16700Schasinglulu 
798*91f16700Schasinglulu 		/* Abort if the last string isn't properly NUL-terminated. */
799*91f16700Schasinglulu 		if (list + length > end) {
800*91f16700Schasinglulu 			if (lenp)
801*91f16700Schasinglulu 				*lenp = -FDT_ERR_BADVALUE;
802*91f16700Schasinglulu 
803*91f16700Schasinglulu 			return NULL;
804*91f16700Schasinglulu 		}
805*91f16700Schasinglulu 
806*91f16700Schasinglulu 		if (idx == 0) {
807*91f16700Schasinglulu 			if (lenp)
808*91f16700Schasinglulu 				*lenp = length - 1;
809*91f16700Schasinglulu 
810*91f16700Schasinglulu 			return list;
811*91f16700Schasinglulu 		}
812*91f16700Schasinglulu 
813*91f16700Schasinglulu 		list += length;
814*91f16700Schasinglulu 		idx--;
815*91f16700Schasinglulu 	}
816*91f16700Schasinglulu 
817*91f16700Schasinglulu 	if (lenp)
818*91f16700Schasinglulu 		*lenp = -FDT_ERR_NOTFOUND;
819*91f16700Schasinglulu 
820*91f16700Schasinglulu 	return NULL;
821*91f16700Schasinglulu }
822*91f16700Schasinglulu 
823*91f16700Schasinglulu int fdt_node_check_compatible(const void *fdt, int nodeoffset,
824*91f16700Schasinglulu 			      const char *compatible)
825*91f16700Schasinglulu {
826*91f16700Schasinglulu 	const void *prop;
827*91f16700Schasinglulu 	int len;
828*91f16700Schasinglulu 
829*91f16700Schasinglulu 	prop = fdt_getprop(fdt, nodeoffset, "compatible", &len);
830*91f16700Schasinglulu 	if (!prop)
831*91f16700Schasinglulu 		return len;
832*91f16700Schasinglulu 
833*91f16700Schasinglulu 	return !fdt_stringlist_contains(prop, len, compatible);
834*91f16700Schasinglulu }
835*91f16700Schasinglulu 
836*91f16700Schasinglulu int fdt_node_offset_by_compatible(const void *fdt, int startoffset,
837*91f16700Schasinglulu 				  const char *compatible)
838*91f16700Schasinglulu {
839*91f16700Schasinglulu 	int offset, err;
840*91f16700Schasinglulu 
841*91f16700Schasinglulu 	FDT_RO_PROBE(fdt);
842*91f16700Schasinglulu 
843*91f16700Schasinglulu 	/* FIXME: The algorithm here is pretty horrible: we scan each
844*91f16700Schasinglulu 	 * property of a node in fdt_node_check_compatible(), then if
845*91f16700Schasinglulu 	 * that didn't find what we want, we scan over them again
846*91f16700Schasinglulu 	 * making our way to the next node.  Still it's the easiest to
847*91f16700Schasinglulu 	 * implement approach; performance can come later. */
848*91f16700Schasinglulu 	for (offset = fdt_next_node(fdt, startoffset, NULL);
849*91f16700Schasinglulu 	     offset >= 0;
850*91f16700Schasinglulu 	     offset = fdt_next_node(fdt, offset, NULL)) {
851*91f16700Schasinglulu 		err = fdt_node_check_compatible(fdt, offset, compatible);
852*91f16700Schasinglulu 		if ((err < 0) && (err != -FDT_ERR_NOTFOUND))
853*91f16700Schasinglulu 			return err;
854*91f16700Schasinglulu 		else if (err == 0)
855*91f16700Schasinglulu 			return offset;
856*91f16700Schasinglulu 	}
857*91f16700Schasinglulu 
858*91f16700Schasinglulu 	return offset; /* error from fdt_next_node() */
859*91f16700Schasinglulu }
860