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