[new uImage] Add libfdt support to mkimage
[platform/kernel/u-boot.git] / libfdt / fdt_ro.c
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 #ifndef USE_HOSTCC
54 #include <fdt.h>
55 #include <libfdt.h>
56 #else
57 #include "fdt_host.h"
58 #endif
59
60 #include "libfdt_internal.h"
61
62 #define CHECK_HEADER(fdt) \
63         { \
64                 int err; \
65                 if ((err = fdt_check_header(fdt)) != 0) \
66                         return err; \
67         }
68
69 static int nodename_eq(const void *fdt, int offset,
70                        const char *s, int len)
71 {
72         const char *p = fdt_offset_ptr(fdt, offset, len+1);
73
74         if (! p)
75                 /* short match */
76                 return 0;
77
78         if (memcmp(p, s, len) != 0)
79                 return 0;
80
81         if (p[len] == '\0')
82                 return 1;
83         else if (!memchr(s, '@', len) && (p[len] == '@'))
84                 return 1;
85         else
86                 return 0;
87 }
88
89 const char *fdt_string(const void *fdt, int stroffset)
90 {
91         return (char *)fdt + fdt_off_dt_strings(fdt) + stroffset;
92 }
93
94 int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size)
95 {
96         CHECK_HEADER(fdt);
97         *address = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->address);
98         *size = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->size);
99         return 0;
100 }
101
102 int fdt_num_mem_rsv(const void *fdt)
103 {
104         int i = 0;
105
106         while (fdt64_to_cpu(_fdt_mem_rsv(fdt, i)->size) != 0)
107                 i++;
108         return i;
109 }
110
111 int fdt_subnode_offset_namelen(const void *fdt, int parentoffset,
112                                const char *name, int namelen)
113 {
114         int level = 0;
115         uint32_t tag;
116         int offset, nextoffset;
117
118         CHECK_HEADER(fdt);
119
120         tag = fdt_next_tag(fdt, parentoffset, &nextoffset);
121         if (tag != FDT_BEGIN_NODE)
122                 return -FDT_ERR_BADOFFSET;
123
124         do {
125                 offset = nextoffset;
126                 tag = fdt_next_tag(fdt, offset, &nextoffset);
127
128                 switch (tag) {
129                 case FDT_END:
130                         return -FDT_ERR_TRUNCATED;
131
132                 case FDT_BEGIN_NODE:
133                         level++;
134                         if (level != 1)
135                                 continue;
136                         if (nodename_eq(fdt, offset+FDT_TAGSIZE, name, namelen))
137                                 /* Found it! */
138                                 return offset;
139                         break;
140
141                 case FDT_END_NODE:
142                         level--;
143                         break;
144
145                 case FDT_PROP:
146                 case FDT_NOP:
147                         break;
148
149                 default:
150                         return -FDT_ERR_BADSTRUCTURE;
151                 }
152         } while (level >= 0);
153
154         return -FDT_ERR_NOTFOUND;
155 }
156
157 int fdt_subnode_offset(const void *fdt, int parentoffset,
158                        const char *name)
159 {
160         return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name));
161 }
162
163 int fdt_path_offset(const void *fdt, const char *path)
164 {
165         const char *end = path + strlen(path);
166         const char *p = path;
167         int offset = 0;
168
169         CHECK_HEADER(fdt);
170
171         if (*path != '/')
172                 return -FDT_ERR_BADPATH;
173
174         while (*p) {
175                 const char *q;
176
177                 while (*p == '/')
178                         p++;
179                 if (! *p)
180                         return offset;
181                 q = strchr(p, '/');
182                 if (! q)
183                         q = end;
184
185                 offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p);
186                 if (offset < 0)
187                         return offset;
188
189                 p = q;
190         }
191
192         return offset;
193 }
194
195 const char *fdt_get_name(const void *fdt, int nodeoffset, int *len)
196 {
197         const struct fdt_node_header *nh;
198         int err;
199
200         if ((err = fdt_check_header(fdt)) != 0)
201                 goto fail;
202
203         err = -FDT_ERR_BADOFFSET;
204         nh = fdt_offset_ptr(fdt, nodeoffset, sizeof(*nh));
205         if (!nh || (fdt32_to_cpu(nh->tag) != FDT_BEGIN_NODE))
206                 goto fail;
207
208         if (len)
209                 *len = strlen(nh->name);
210
211         return nh->name;
212
213  fail:
214         if (len)
215                 *len = err;
216         return NULL;
217 }
218
219 const struct fdt_property *fdt_get_property(const void *fdt,
220                                             int nodeoffset,
221                                             const char *name, int *lenp)
222 {
223         uint32_t tag;
224         const struct fdt_property *prop;
225         int namestroff;
226         int offset, nextoffset;
227         int err;
228
229         if ((err = fdt_check_header(fdt)) != 0)
230                 goto fail;
231
232         err = -FDT_ERR_BADOFFSET;
233         if (nodeoffset % FDT_TAGSIZE)
234                 goto fail;
235
236         tag = fdt_next_tag(fdt, nodeoffset, &nextoffset);
237         if (tag != FDT_BEGIN_NODE)
238                 goto fail;
239
240         do {
241                 offset = nextoffset;
242
243                 tag = fdt_next_tag(fdt, offset, &nextoffset);
244                 switch (tag) {
245                 case FDT_END:
246                         err = -FDT_ERR_TRUNCATED;
247                         goto fail;
248
249                 case FDT_BEGIN_NODE:
250                 case FDT_END_NODE:
251                 case FDT_NOP:
252                         break;
253
254                 case FDT_PROP:
255                         err = -FDT_ERR_BADSTRUCTURE;
256                         prop = fdt_offset_ptr(fdt, offset, sizeof(*prop));
257                         if (! prop)
258                                 goto fail;
259                         namestroff = fdt32_to_cpu(prop->nameoff);
260                         if (streq(fdt_string(fdt, namestroff), name)) {
261                                 /* Found it! */
262                                 int len = fdt32_to_cpu(prop->len);
263                                 prop = fdt_offset_ptr(fdt, offset,
264                                                       sizeof(*prop)+len);
265                                 if (! prop)
266                                         goto fail;
267
268                                 if (lenp)
269                                         *lenp = len;
270
271                                 return prop;
272                         }
273                         break;
274
275                 default:
276                         err = -FDT_ERR_BADSTRUCTURE;
277                         goto fail;
278                 }
279         } while ((tag != FDT_BEGIN_NODE) && (tag != FDT_END_NODE));
280
281         err = -FDT_ERR_NOTFOUND;
282  fail:
283         if (lenp)
284                 *lenp = err;
285         return NULL;
286 }
287
288 const void *fdt_getprop(const void *fdt, int nodeoffset,
289                   const char *name, int *lenp)
290 {
291         const struct fdt_property *prop;
292
293         prop = fdt_get_property(fdt, nodeoffset, name, lenp);
294         if (! prop)
295                 return NULL;
296
297         return prop->data;
298 }
299
300 uint32_t fdt_get_phandle(const void *fdt, int nodeoffset)
301 {
302         const uint32_t *php;
303         int len;
304
305         php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len);
306         if (!php || (len != sizeof(*php)))
307                 return 0;
308
309         return fdt32_to_cpu(*php);
310 }
311
312 int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen)
313 {
314         uint32_t tag;
315         int p = 0, overflow = 0;
316         int offset, nextoffset, namelen;
317         const char *name;
318
319         CHECK_HEADER(fdt);
320
321         tag = fdt_next_tag(fdt, 0, &nextoffset);
322         if (tag != FDT_BEGIN_NODE)
323                 return -FDT_ERR_BADSTRUCTURE;
324
325         if (buflen < 2)
326                 return -FDT_ERR_NOSPACE;
327         buf[0] = '/';
328         p = 1;
329
330         while (nextoffset <= nodeoffset) {
331                 offset = nextoffset;
332                 tag = fdt_next_tag(fdt, offset, &nextoffset);
333                 switch (tag) {
334                 case FDT_END:
335                         return -FDT_ERR_BADOFFSET;
336
337                 case FDT_BEGIN_NODE:
338                         name = fdt_get_name(fdt, offset, &namelen);
339                         if (!name)
340                                 return namelen;
341                         if (overflow || ((p + namelen + 1) > buflen)) {
342                                 overflow++;
343                                 break;
344                         }
345                         memcpy(buf + p, name, namelen);
346                         p += namelen;
347                         buf[p++] = '/';
348                         break;
349
350                 case FDT_END_NODE:
351                         if (overflow) {
352                                 overflow--;
353                                 break;
354                         }
355                         do {
356                                 p--;
357                         } while  (buf[p-1] != '/');
358                         break;
359
360                 case FDT_PROP:
361                 case FDT_NOP:
362                         break;
363
364                 default:
365                         return -FDT_ERR_BADSTRUCTURE;
366                 }
367         }
368
369         if (overflow)
370                 return -FDT_ERR_NOSPACE;
371
372         if (p > 1) /* special case so that root path is "/", not "" */
373                 p--;
374         buf[p] = '\0';
375         return p;
376 }
377
378 int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset,
379                                  int supernodedepth, int *nodedepth)
380 {
381         int level = -1;
382         uint32_t tag;
383         int offset, nextoffset = 0;
384         int supernodeoffset = -FDT_ERR_INTERNAL;
385
386         CHECK_HEADER(fdt);
387
388         if (supernodedepth < 0)
389                 return -FDT_ERR_NOTFOUND;
390
391         do {
392                 offset = nextoffset;
393                 tag = fdt_next_tag(fdt, offset, &nextoffset);
394                 switch (tag) {
395                 case FDT_END:
396                         return -FDT_ERR_BADOFFSET;
397
398                 case FDT_BEGIN_NODE:
399                         level++;
400                         if (level == supernodedepth)
401                                 supernodeoffset = offset;
402                         break;
403
404                 case FDT_END_NODE:
405                         level--;
406                         break;
407
408                 case FDT_PROP:
409                 case FDT_NOP:
410                         break;
411
412                 default:
413                         return -FDT_ERR_BADSTRUCTURE;
414                 }
415         } while (offset < nodeoffset);
416
417         if (nodedepth)
418                 *nodedepth = level;
419
420         if (supernodedepth > level)
421                 return -FDT_ERR_NOTFOUND;
422         return supernodeoffset;
423 }
424
425 int fdt_node_depth(const void *fdt, int nodeoffset)
426 {
427         int nodedepth;
428         int err;
429
430         err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth);
431         if (err)
432                 return (err < 0) ? err : -FDT_ERR_INTERNAL;
433         return nodedepth;
434 }
435
436 int fdt_parent_offset(const void *fdt, int nodeoffset)
437 {
438         int nodedepth = fdt_node_depth(fdt, nodeoffset);
439
440         if (nodedepth < 0)
441                 return nodedepth;
442         return fdt_supernode_atdepth_offset(fdt, nodeoffset,
443                                             nodedepth - 1, NULL);
444 }
445
446 int fdt_node_offset_by_prop_value(const void *fdt, int startoffset,
447                                   const char *propname,
448                                   const void *propval, int proplen)
449 {
450         uint32_t tag;
451         int offset, nextoffset;
452         const void *val;
453         int len;
454
455         CHECK_HEADER(fdt);
456
457         if (startoffset >= 0) {
458                 tag = fdt_next_tag(fdt, startoffset, &nextoffset);
459                 if (tag != FDT_BEGIN_NODE)
460                         return -FDT_ERR_BADOFFSET;
461         } else {
462                 nextoffset = 0;
463         }
464
465         /* FIXME: The algorithm here is pretty horrible: we scan each
466          * property of a node in fdt_getprop(), then if that didn't
467          * find what we want, we scan over them again making our way
468          * to the next node.  Still it's the easiest to implement
469          * approach; performance can come later. */
470         do {
471                 offset = nextoffset;
472                 tag = fdt_next_tag(fdt, offset, &nextoffset);
473
474                 switch (tag) {
475                 case FDT_BEGIN_NODE:
476                         val = fdt_getprop(fdt, offset, propname, &len);
477                         if (val
478                             && (len == proplen)
479                             && (memcmp(val, propval, len) == 0))
480                                 return offset;
481                         break;
482
483                 case FDT_PROP:
484                 case FDT_END:
485                 case FDT_END_NODE:
486                 case FDT_NOP:
487                         break;
488
489                 default:
490                         return -FDT_ERR_BADSTRUCTURE;
491                 }
492         } while (tag != FDT_END);
493
494         return -FDT_ERR_NOTFOUND;
495 }
496
497 int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle)
498 {
499         if ((phandle == 0) || (phandle == -1))
500                 return -FDT_ERR_BADPHANDLE;
501         phandle = cpu_to_fdt32(phandle);
502         return fdt_node_offset_by_prop_value(fdt, -1, "linux,phandle",
503                                              &phandle, sizeof(phandle));
504 }
505
506 int _stringlist_contains(const void *strlist, int listlen, const char *str)
507 {
508         int len = strlen(str);
509         const void *p;
510
511         while (listlen >= len) {
512                 if (memcmp(str, strlist, len+1) == 0)
513                         return 1;
514                 p = memchr(strlist, '\0', listlen);
515                 if (!p)
516                         return 0; /* malformed strlist.. */
517                 listlen -= (p-strlist) + 1;
518                 strlist = p + 1;
519         }
520         return 0;
521 }
522
523 int fdt_node_check_compatible(const void *fdt, int nodeoffset,
524                               const char *compatible)
525 {
526         const void *prop;
527         int len;
528
529         prop = fdt_getprop(fdt, nodeoffset, "compatible", &len);
530         if (!prop)
531                 return len;
532         if (_stringlist_contains(prop, len, compatible))
533                 return 0;
534         else
535                 return 1;
536 }
537
538 int fdt_node_offset_by_compatible(const void *fdt, int startoffset,
539                                   const char *compatible)
540 {
541         uint32_t tag;
542         int offset, nextoffset;
543         int err;
544
545         CHECK_HEADER(fdt);
546
547         if (startoffset >= 0) {
548                 tag = fdt_next_tag(fdt, startoffset, &nextoffset);
549                 if (tag != FDT_BEGIN_NODE)
550                         return -FDT_ERR_BADOFFSET;
551         } else {
552                 nextoffset = 0;
553         }
554
555         /* FIXME: The algorithm here is pretty horrible: we scan each
556          * property of a node in fdt_node_check_compatible(), then if
557          * that didn't find what we want, we scan over them again
558          * making our way to the next node.  Still it's the easiest to
559          * implement approach; performance can come later. */
560         do {
561                 offset = nextoffset;
562                 tag = fdt_next_tag(fdt, offset, &nextoffset);
563
564                 switch (tag) {
565                 case FDT_BEGIN_NODE:
566                         err = fdt_node_check_compatible(fdt, offset,
567                                                         compatible);
568                         if ((err < 0)
569                             && (err != -FDT_ERR_NOTFOUND))
570                                 return err;
571                         else if (err == 0)
572                                 return offset;
573                         break;
574
575                 case FDT_PROP:
576                 case FDT_END:
577                 case FDT_END_NODE:
578                 case FDT_NOP:
579                         break;
580
581                 default:
582                         return -FDT_ERR_BADSTRUCTURE;
583                 }
584         } while (tag != FDT_END);
585
586         return -FDT_ERR_NOTFOUND;
587 }