source: rtems/cpukit/dtc/libfdt/fdt_ro.c @ 85d1769

5
Last change on this file since 85d1769 was 85d1769, checked in by David Gibson <david@…>, on 10/22/17 at 05:32:15

libfdt: Clean up header checking functions

Many of the libfdt entry points call some sort of sanity check function
before doing anything else. These need to do slightly different things for
the various classes of functions.

The read-only version is shared with the exported fdt_check_header(), which
limits us a bit in how we can improve it. For that reason split the two
functions apart (though the exported one just calls the ro one for now).

We also rename the functions for more consistency - they're all named
fdt_XX_probe_() where the XX indicates which class of functions they're
for. "probe" is a better "term" than the previous check, since they really
only do minimal validation.

Signed-off-by: David Gibson <david@…>
Tested-by: Alexey Kardashevskiy <aik@…>
Reviewed-by: Alexey Kardashevskiy <aik@…>

  • Property mode set to 100644
File size: 17.7 KB
Line 
1/*
2 * libfdt - Flat Device Tree manipulation
3 * Copyright (C) 2006 David Gibson, IBM Corporation.
4 *
5 * libfdt is dual licensed: you can use it either under the terms of
6 * the GPL, or the BSD license, at your option.
7 *
8 *  a) This library is free software; you can redistribute it and/or
9 *     modify it under the terms of the GNU General Public License as
10 *     published by the Free Software Foundation; either version 2 of the
11 *     License, or (at your option) any later version.
12 *
13 *     This library is distributed in the hope that it will be useful,
14 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
15 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 *     GNU General Public License for more details.
17 *
18 *     You should have received a copy of the GNU General Public
19 *     License along with this library; if not, write to the Free
20 *     Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
21 *     MA 02110-1301 USA
22 *
23 * Alternatively,
24 *
25 *  b) Redistribution and use in source and binary forms, with or
26 *     without modification, are permitted provided that the following
27 *     conditions are met:
28 *
29 *     1. Redistributions of source code must retain the above
30 *        copyright notice, this list of conditions and the following
31 *        disclaimer.
32 *     2. Redistributions in binary form must reproduce the above
33 *        copyright notice, this list of conditions and the following
34 *        disclaimer in the documentation and/or other materials
35 *        provided with the distribution.
36 *
37 *     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
38 *     CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
39 *     INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
40 *     MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
41 *     DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
42 *     CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
43 *     SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
44 *     NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
45 *     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
46 *     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
47 *     CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
48 *     OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
49 *     EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
50 */
51#include "libfdt_env.h"
52
53#include <fdt.h>
54#include <libfdt.h>
55
56#include "libfdt_internal.h"
57
58static int fdt_nodename_eq_(const void *fdt, int offset,
59                            const char *s, int len)
60{
61        int olen;
62        const char *p = fdt_get_name(fdt, offset, &olen);
63
64        if (!p || olen < len)
65                /* short match */
66                return 0;
67
68        if (memcmp(p, s, len) != 0)
69                return 0;
70
71        if (p[len] == '\0')
72                return 1;
73        else if (!memchr(s, '@', len) && (p[len] == '@'))
74                return 1;
75        else
76                return 0;
77}
78
79const char *fdt_string(const void *fdt, int stroffset)
80{
81        return (const char *)fdt + fdt_off_dt_strings(fdt) + stroffset;
82}
83
84static int fdt_string_eq_(const void *fdt, int stroffset,
85                          const char *s, int len)
86{
87        const char *p = fdt_string(fdt, stroffset);
88
89        return (strlen(p) == len) && (memcmp(p, s, len) == 0);
90}
91
92uint32_t fdt_get_max_phandle(const void *fdt)
93{
94        uint32_t max_phandle = 0;
95        int offset;
96
97        for (offset = fdt_next_node(fdt, -1, NULL);;
98             offset = fdt_next_node(fdt, offset, NULL)) {
99                uint32_t phandle;
100
101                if (offset == -FDT_ERR_NOTFOUND)
102                        return max_phandle;
103
104                if (offset < 0)
105                        return (uint32_t)-1;
106
107                phandle = fdt_get_phandle(fdt, offset);
108                if (phandle == (uint32_t)-1)
109                        continue;
110
111                if (phandle > max_phandle)
112                        max_phandle = phandle;
113        }
114
115        return 0;
116}
117
118int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size)
119{
120        FDT_RO_PROBE(fdt);
121        *address = fdt64_to_cpu(fdt_mem_rsv_(fdt, n)->address);
122        *size = fdt64_to_cpu(fdt_mem_rsv_(fdt, n)->size);
123        return 0;
124}
125
126int fdt_num_mem_rsv(const void *fdt)
127{
128        int i = 0;
129
130        while (fdt64_to_cpu(fdt_mem_rsv_(fdt, i)->size) != 0)
131                i++;
132        return i;
133}
134
135static int nextprop_(const void *fdt, int offset)
136{
137        uint32_t tag;
138        int nextoffset;
139
140        do {
141                tag = fdt_next_tag(fdt, offset, &nextoffset);
142
143                switch (tag) {
144                case FDT_END:
145                        if (nextoffset >= 0)
146                                return -FDT_ERR_BADSTRUCTURE;
147                        else
148                                return nextoffset;
149
150                case FDT_PROP:
151                        return offset;
152                }
153                offset = nextoffset;
154        } while (tag == FDT_NOP);
155
156        return -FDT_ERR_NOTFOUND;
157}
158
159int fdt_subnode_offset_namelen(const void *fdt, int offset,
160                               const char *name, int namelen)
161{
162        int depth;
163
164        FDT_RO_PROBE(fdt);
165
166        for (depth = 0;
167             (offset >= 0) && (depth >= 0);
168             offset = fdt_next_node(fdt, offset, &depth))
169                if ((depth == 1)
170                    && fdt_nodename_eq_(fdt, offset, name, namelen))
171                        return offset;
172
173        if (depth < 0)
174                return -FDT_ERR_NOTFOUND;
175        return offset; /* error */
176}
177
178int fdt_subnode_offset(const void *fdt, int parentoffset,
179                       const char *name)
180{
181        return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name));
182}
183
184int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen)
185{
186        const char *end = path + namelen;
187        const char *p = path;
188        int offset = 0;
189
190        FDT_RO_PROBE(fdt);
191
192        /* see if we have an alias */
193        if (*path != '/') {
194                const char *q = memchr(path, '/', end - p);
195
196                if (!q)
197                        q = end;
198
199                p = fdt_get_alias_namelen(fdt, p, q - p);
200                if (!p)
201                        return -FDT_ERR_BADPATH;
202                offset = fdt_path_offset(fdt, p);
203
204                p = q;
205        }
206
207        while (p < end) {
208                const char *q;
209
210                while (*p == '/') {
211                        p++;
212                        if (p == end)
213                                return offset;
214                }
215                q = memchr(p, '/', end - p);
216                if (! q)
217                        q = end;
218
219                offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p);
220                if (offset < 0)
221                        return offset;
222
223                p = q;
224        }
225
226        return offset;
227}
228
229int fdt_path_offset(const void *fdt, const char *path)
230{
231        return fdt_path_offset_namelen(fdt, path, strlen(path));
232}
233
234const char *fdt_get_name(const void *fdt, int nodeoffset, int *len)
235{
236        const struct fdt_node_header *nh = fdt_offset_ptr_(fdt, nodeoffset);
237        const char *nameptr;
238        int err;
239
240        if (((err = fdt_ro_probe_(fdt)) != 0)
241            || ((err = fdt_check_node_offset_(fdt, nodeoffset)) < 0))
242                        goto fail;
243
244        nameptr = nh->name;
245
246        if (fdt_version(fdt) < 0x10) {
247                /*
248                 * For old FDT versions, match the naming conventions of V16:
249                 * give only the leaf name (after all /). The actual tree
250                 * contents are loosely checked.
251                 */
252                const char *leaf;
253                leaf = strrchr(nameptr, '/');
254                if (leaf == NULL) {
255                        err = -FDT_ERR_BADSTRUCTURE;
256                        goto fail;
257                }
258                nameptr = leaf+1;
259        }
260
261        if (len)
262                *len = strlen(nameptr);
263
264        return nameptr;
265
266 fail:
267        if (len)
268                *len = err;
269        return NULL;
270}
271
272int fdt_first_property_offset(const void *fdt, int nodeoffset)
273{
274        int offset;
275
276        if ((offset = fdt_check_node_offset_(fdt, nodeoffset)) < 0)
277                return offset;
278
279        return nextprop_(fdt, offset);
280}
281
282int fdt_next_property_offset(const void *fdt, int offset)
283{
284        if ((offset = fdt_check_prop_offset_(fdt, offset)) < 0)
285                return offset;
286
287        return nextprop_(fdt, offset);
288}
289
290static const struct fdt_property *fdt_get_property_by_offset_(const void *fdt,
291                                                              int offset,
292                                                              int *lenp)
293{
294        int err;
295        const struct fdt_property *prop;
296
297        if ((err = fdt_check_prop_offset_(fdt, offset)) < 0) {
298                if (lenp)
299                        *lenp = err;
300                return NULL;
301        }
302
303        prop = fdt_offset_ptr_(fdt, offset);
304
305        if (lenp)
306                *lenp = fdt32_to_cpu(prop->len);
307
308        return prop;
309}
310
311const struct fdt_property *fdt_get_property_by_offset(const void *fdt,
312                                                      int offset,
313                                                      int *lenp)
314{
315        /* Prior to version 16, properties may need realignment
316         * and this API does not work. fdt_getprop_*() will, however. */
317
318        if (fdt_version(fdt) < 0x10) {
319                if (lenp)
320                        *lenp = -FDT_ERR_BADVERSION;
321                return NULL;
322        }
323
324        return fdt_get_property_by_offset_(fdt, offset, lenp);
325}
326
327static const struct fdt_property *fdt_get_property_namelen_(const void *fdt,
328                                                            int offset,
329                                                            const char *name,
330                                                            int namelen,
331                                                            int *lenp,
332                                                            int *poffset)
333{
334        for (offset = fdt_first_property_offset(fdt, offset);
335             (offset >= 0);
336             (offset = fdt_next_property_offset(fdt, offset))) {
337                const struct fdt_property *prop;
338
339                if (!(prop = fdt_get_property_by_offset_(fdt, offset, lenp))) {
340                        offset = -FDT_ERR_INTERNAL;
341                        break;
342                }
343                if (fdt_string_eq_(fdt, fdt32_to_cpu(prop->nameoff),
344                                   name, namelen)) {
345                        if (poffset)
346                                *poffset = offset;
347                        return prop;
348                }
349        }
350
351        if (lenp)
352                *lenp = offset;
353        return NULL;
354}
355
356
357const struct fdt_property *fdt_get_property_namelen(const void *fdt,
358                                                    int offset,
359                                                    const char *name,
360                                                    int namelen, int *lenp)
361{
362        /* Prior to version 16, properties may need realignment
363         * and this API does not work. fdt_getprop_*() will, however. */
364        if (fdt_version(fdt) < 0x10) {
365                if (lenp)
366                        *lenp = -FDT_ERR_BADVERSION;
367                return NULL;
368        }
369
370        return fdt_get_property_namelen_(fdt, offset, name, namelen, lenp,
371                                         NULL);
372}
373
374
375const struct fdt_property *fdt_get_property(const void *fdt,
376                                            int nodeoffset,
377                                            const char *name, int *lenp)
378{
379        return fdt_get_property_namelen(fdt, nodeoffset, name,
380                                        strlen(name), lenp);
381}
382
383const void *fdt_getprop_namelen(const void *fdt, int nodeoffset,
384                                const char *name, int namelen, int *lenp)
385{
386        int poffset;
387        const struct fdt_property *prop;
388
389        prop = fdt_get_property_namelen_(fdt, nodeoffset, name, namelen, lenp,
390                                         &poffset);
391        if (!prop)
392                return NULL;
393
394        /* Handle realignment */
395        if (fdt_version(fdt) < 0x10 && (poffset + sizeof(*prop)) % 8 &&
396            fdt32_to_cpu(prop->len) >= 8)
397                return prop->data + 4;
398        return prop->data;
399}
400
401const void *fdt_getprop_by_offset(const void *fdt, int offset,
402                                  const char **namep, int *lenp)
403{
404        const struct fdt_property *prop;
405
406        prop = fdt_get_property_by_offset_(fdt, offset, lenp);
407        if (!prop)
408                return NULL;
409        if (namep)
410                *namep = fdt_string(fdt, fdt32_to_cpu(prop->nameoff));
411
412        /* Handle realignment */
413        if (fdt_version(fdt) < 0x10 && (offset + sizeof(*prop)) % 8 &&
414            fdt32_to_cpu(prop->len) >= 8)
415                return prop->data + 4;
416        return prop->data;
417}
418
419const void *fdt_getprop(const void *fdt, int nodeoffset,
420                        const char *name, int *lenp)
421{
422        return fdt_getprop_namelen(fdt, nodeoffset, name, strlen(name), lenp);
423}
424
425uint32_t fdt_get_phandle(const void *fdt, int nodeoffset)
426{
427        const fdt32_t *php;
428        int len;
429
430        /* FIXME: This is a bit sub-optimal, since we potentially scan
431         * over all the properties twice. */
432        php = fdt_getprop(fdt, nodeoffset, "phandle", &len);
433        if (!php || (len != sizeof(*php))) {
434                php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len);
435                if (!php || (len != sizeof(*php)))
436                        return 0;
437        }
438
439        return fdt32_to_cpu(*php);
440}
441
442const char *fdt_get_alias_namelen(const void *fdt,
443                                  const char *name, int namelen)
444{
445        int aliasoffset;
446
447        aliasoffset = fdt_path_offset(fdt, "/aliases");
448        if (aliasoffset < 0)
449                return NULL;
450
451        return fdt_getprop_namelen(fdt, aliasoffset, name, namelen, NULL);
452}
453
454const char *fdt_get_alias(const void *fdt, const char *name)
455{
456        return fdt_get_alias_namelen(fdt, name, strlen(name));
457}
458
459int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen)
460{
461        int pdepth = 0, p = 0;
462        int offset, depth, namelen;
463        const char *name;
464
465        FDT_RO_PROBE(fdt);
466
467        if (buflen < 2)
468                return -FDT_ERR_NOSPACE;
469
470        for (offset = 0, depth = 0;
471             (offset >= 0) && (offset <= nodeoffset);
472             offset = fdt_next_node(fdt, offset, &depth)) {
473                while (pdepth > depth) {
474                        do {
475                                p--;
476                        } while (buf[p-1] != '/');
477                        pdepth--;
478                }
479
480                if (pdepth >= depth) {
481                        name = fdt_get_name(fdt, offset, &namelen);
482                        if (!name)
483                                return namelen;
484                        if ((p + namelen + 1) <= buflen) {
485                                memcpy(buf + p, name, namelen);
486                                p += namelen;
487                                buf[p++] = '/';
488                                pdepth++;
489                        }
490                }
491
492                if (offset == nodeoffset) {
493                        if (pdepth < (depth + 1))
494                                return -FDT_ERR_NOSPACE;
495
496                        if (p > 1) /* special case so that root path is "/", not "" */
497                                p--;
498                        buf[p] = '\0';
499                        return 0;
500                }
501        }
502
503        if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
504                return -FDT_ERR_BADOFFSET;
505        else if (offset == -FDT_ERR_BADOFFSET)
506                return -FDT_ERR_BADSTRUCTURE;
507
508        return offset; /* error from fdt_next_node() */
509}
510
511int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset,
512                                 int supernodedepth, int *nodedepth)
513{
514        int offset, depth;
515        int supernodeoffset = -FDT_ERR_INTERNAL;
516
517        FDT_RO_PROBE(fdt);
518
519        if (supernodedepth < 0)
520                return -FDT_ERR_NOTFOUND;
521
522        for (offset = 0, depth = 0;
523             (offset >= 0) && (offset <= nodeoffset);
524             offset = fdt_next_node(fdt, offset, &depth)) {
525                if (depth == supernodedepth)
526                        supernodeoffset = offset;
527
528                if (offset == nodeoffset) {
529                        if (nodedepth)
530                                *nodedepth = depth;
531
532                        if (supernodedepth > depth)
533                                return -FDT_ERR_NOTFOUND;
534                        else
535                                return supernodeoffset;
536                }
537        }
538
539        if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
540                return -FDT_ERR_BADOFFSET;
541        else if (offset == -FDT_ERR_BADOFFSET)
542                return -FDT_ERR_BADSTRUCTURE;
543
544        return offset; /* error from fdt_next_node() */
545}
546
547int fdt_node_depth(const void *fdt, int nodeoffset)
548{
549        int nodedepth;
550        int err;
551
552        err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth);
553        if (err)
554                return (err < 0) ? err : -FDT_ERR_INTERNAL;
555        return nodedepth;
556}
557
558int fdt_parent_offset(const void *fdt, int nodeoffset)
559{
560        int nodedepth = fdt_node_depth(fdt, nodeoffset);
561
562        if (nodedepth < 0)
563                return nodedepth;
564        return fdt_supernode_atdepth_offset(fdt, nodeoffset,
565                                            nodedepth - 1, NULL);
566}
567
568int fdt_node_offset_by_prop_value(const void *fdt, int startoffset,
569                                  const char *propname,
570                                  const void *propval, int proplen)
571{
572        int offset;
573        const void *val;
574        int len;
575
576        FDT_RO_PROBE(fdt);
577
578        /* FIXME: The algorithm here is pretty horrible: we scan each
579         * property of a node in fdt_getprop(), then if that didn't
580         * find what we want, we scan over them again making our way
581         * to the next node.  Still it's the easiest to implement
582         * approach; performance can come later. */
583        for (offset = fdt_next_node(fdt, startoffset, NULL);
584             offset >= 0;
585             offset = fdt_next_node(fdt, offset, NULL)) {
586                val = fdt_getprop(fdt, offset, propname, &len);
587                if (val && (len == proplen)
588                    && (memcmp(val, propval, len) == 0))
589                        return offset;
590        }
591
592        return offset; /* error from fdt_next_node() */
593}
594
595int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle)
596{
597        int offset;
598
599        if ((phandle == 0) || (phandle == -1))
600                return -FDT_ERR_BADPHANDLE;
601
602        FDT_RO_PROBE(fdt);
603
604        /* FIXME: The algorithm here is pretty horrible: we
605         * potentially scan each property of a node in
606         * fdt_get_phandle(), then if that didn't find what
607         * we want, we scan over them again making our way to the next
608         * node.  Still it's the easiest to implement approach;
609         * performance can come later. */
610        for (offset = fdt_next_node(fdt, -1, NULL);
611             offset >= 0;
612             offset = fdt_next_node(fdt, offset, NULL)) {
613                if (fdt_get_phandle(fdt, offset) == phandle)
614                        return offset;
615        }
616
617        return offset; /* error from fdt_next_node() */
618}
619
620int fdt_stringlist_contains(const char *strlist, int listlen, const char *str)
621{
622        int len = strlen(str);
623        const char *p;
624
625        while (listlen >= len) {
626                if (memcmp(str, strlist, len+1) == 0)
627                        return 1;
628                p = memchr(strlist, '\0', listlen);
629                if (!p)
630                        return 0; /* malformed strlist.. */
631                listlen -= (p-strlist) + 1;
632                strlist = p + 1;
633        }
634        return 0;
635}
636
637int fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property)
638{
639        const char *list, *end;
640        int length, count = 0;
641
642        list = fdt_getprop(fdt, nodeoffset, property, &length);
643        if (!list)
644                return length;
645
646        end = list + length;
647
648        while (list < end) {
649                length = strnlen(list, end - list) + 1;
650
651                /* Abort if the last string isn't properly NUL-terminated. */
652                if (list + length > end)
653                        return -FDT_ERR_BADVALUE;
654
655                list += length;
656                count++;
657        }
658
659        return count;
660}
661
662int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property,
663                          const char *string)
664{
665        int length, len, idx = 0;
666        const char *list, *end;
667
668        list = fdt_getprop(fdt, nodeoffset, property, &length);
669        if (!list)
670                return length;
671
672        len = strlen(string) + 1;
673        end = list + length;
674
675        while (list < end) {
676                length = strnlen(list, end - list) + 1;
677
678                /* Abort if the last string isn't properly NUL-terminated. */
679                if (list + length > end)
680                        return -FDT_ERR_BADVALUE;
681
682                if (length == len && memcmp(list, string, length) == 0)
683                        return idx;
684
685                list += length;
686                idx++;
687        }
688
689        return -FDT_ERR_NOTFOUND;
690}
691
692const char *fdt_stringlist_get(const void *fdt, int nodeoffset,
693                               const char *property, int idx,
694                               int *lenp)
695{
696        const char *list, *end;
697        int length;
698
699        list = fdt_getprop(fdt, nodeoffset, property, &length);
700        if (!list) {
701                if (lenp)
702                        *lenp = length;
703
704                return NULL;
705        }
706
707        end = list + length;
708
709        while (list < end) {
710                length = strnlen(list, end - list) + 1;
711
712                /* Abort if the last string isn't properly NUL-terminated. */
713                if (list + length > end) {
714                        if (lenp)
715                                *lenp = -FDT_ERR_BADVALUE;
716
717                        return NULL;
718                }
719
720                if (idx == 0) {
721                        if (lenp)
722                                *lenp = length - 1;
723
724                        return list;
725                }
726
727                list += length;
728                idx--;
729        }
730
731        if (lenp)
732                *lenp = -FDT_ERR_NOTFOUND;
733
734        return NULL;
735}
736
737int fdt_node_check_compatible(const void *fdt, int nodeoffset,
738                              const char *compatible)
739{
740        const void *prop;
741        int len;
742
743        prop = fdt_getprop(fdt, nodeoffset, "compatible", &len);
744        if (!prop)
745                return len;
746
747        return !fdt_stringlist_contains(prop, len, compatible);
748}
749
750int fdt_node_offset_by_compatible(const void *fdt, int startoffset,
751                                  const char *compatible)
752{
753        int offset, err;
754
755        FDT_RO_PROBE(fdt);
756
757        /* FIXME: The algorithm here is pretty horrible: we scan each
758         * property of a node in fdt_node_check_compatible(), then if
759         * that didn't find what we want, we scan over them again
760         * making our way to the next node.  Still it's the easiest to
761         * implement approach; performance can come later. */
762        for (offset = fdt_next_node(fdt, startoffset, NULL);
763             offset >= 0;
764             offset = fdt_next_node(fdt, offset, NULL)) {
765                err = fdt_node_check_compatible(fdt, offset, compatible);
766                if ((err < 0) && (err != -FDT_ERR_NOTFOUND))
767                        return err;
768                else if (err == 0)
769                        return offset;
770        }
771
772        return offset; /* error from fdt_next_node() */
773}
Note: See TracBrowser for help on using the repository browser.