Merge git://git.denx.de/u-boot-dm
[oweals/u-boot.git] / lib / libfdt / fdt.c
1 /*
2  * libfdt - Flat Device Tree manipulation
3  * Copyright (C) 2006 David Gibson, IBM Corporation.
4  * SPDX-License-Identifier:     GPL-2.0+ BSD-2-Clause
5  */
6 #include <libfdt_env.h>
7
8 #ifndef USE_HOSTCC
9 #include <fdt.h>
10 #include <libfdt.h>
11 #else
12 #include "fdt_host.h"
13 #endif
14
15 #include "libfdt_internal.h"
16
17 int fdt_check_header(const void *fdt)
18 {
19         if (fdt_magic(fdt) == FDT_MAGIC) {
20                 /* Complete tree */
21                 if (fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION)
22                         return -FDT_ERR_BADVERSION;
23                 if (fdt_last_comp_version(fdt) > FDT_LAST_SUPPORTED_VERSION)
24                         return -FDT_ERR_BADVERSION;
25         } else if (fdt_magic(fdt) == FDT_SW_MAGIC) {
26                 /* Unfinished sequential-write blob */
27                 if (fdt_size_dt_struct(fdt) == 0)
28                         return -FDT_ERR_BADSTATE;
29         } else {
30                 return -FDT_ERR_BADMAGIC;
31         }
32
33         return 0;
34 }
35
36 const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int len)
37 {
38         unsigned absoffset = offset + fdt_off_dt_struct(fdt);
39
40         if ((absoffset < offset)
41             || ((absoffset + len) < absoffset)
42             || (absoffset + len) > fdt_totalsize(fdt))
43                 return NULL;
44
45         if (fdt_version(fdt) >= 0x11)
46                 if (((offset + len) < offset)
47                     || ((offset + len) > fdt_size_dt_struct(fdt)))
48                         return NULL;
49
50         return _fdt_offset_ptr(fdt, offset);
51 }
52
53 uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset)
54 {
55         const fdt32_t *tagp, *lenp;
56         uint32_t tag;
57         int offset = startoffset;
58         const char *p;
59
60         *nextoffset = -FDT_ERR_TRUNCATED;
61         tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE);
62         if (!tagp)
63                 return FDT_END; /* premature end */
64         tag = fdt32_to_cpu(*tagp);
65         offset += FDT_TAGSIZE;
66
67         *nextoffset = -FDT_ERR_BADSTRUCTURE;
68         switch (tag) {
69         case FDT_BEGIN_NODE:
70                 /* skip name */
71                 do {
72                         p = fdt_offset_ptr(fdt, offset++, 1);
73                 } while (p && (*p != '\0'));
74                 if (!p)
75                         return FDT_END; /* premature end */
76                 break;
77
78         case FDT_PROP:
79                 lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp));
80                 if (!lenp)
81                         return FDT_END; /* premature end */
82                 /* skip-name offset, length and value */
83                 offset += sizeof(struct fdt_property) - FDT_TAGSIZE
84                         + fdt32_to_cpu(*lenp);
85                 break;
86
87         case FDT_END:
88         case FDT_END_NODE:
89         case FDT_NOP:
90                 break;
91
92         default:
93                 return FDT_END;
94         }
95
96         if (!fdt_offset_ptr(fdt, startoffset, offset - startoffset))
97                 return FDT_END; /* premature end */
98
99         *nextoffset = FDT_TAGALIGN(offset);
100         return tag;
101 }
102
103 int _fdt_check_node_offset(const void *fdt, int offset)
104 {
105         if ((offset < 0) || (offset % FDT_TAGSIZE)
106             || (fdt_next_tag(fdt, offset, &offset) != FDT_BEGIN_NODE))
107                 return -FDT_ERR_BADOFFSET;
108
109         return offset;
110 }
111
112 int _fdt_check_prop_offset(const void *fdt, int offset)
113 {
114         if ((offset < 0) || (offset % FDT_TAGSIZE)
115             || (fdt_next_tag(fdt, offset, &offset) != FDT_PROP))
116                 return -FDT_ERR_BADOFFSET;
117
118         return offset;
119 }
120
121 int fdt_next_node(const void *fdt, int offset, int *depth)
122 {
123         int nextoffset = 0;
124         uint32_t tag;
125
126         if (offset >= 0)
127                 if ((nextoffset = _fdt_check_node_offset(fdt, offset)) < 0)
128                         return nextoffset;
129
130         do {
131                 offset = nextoffset;
132                 tag = fdt_next_tag(fdt, offset, &nextoffset);
133
134                 switch (tag) {
135                 case FDT_PROP:
136                 case FDT_NOP:
137                         break;
138
139                 case FDT_BEGIN_NODE:
140                         if (depth)
141                                 (*depth)++;
142                         break;
143
144                 case FDT_END_NODE:
145                         if (depth && ((--(*depth)) < 0))
146                                 return nextoffset;
147                         break;
148
149                 case FDT_END:
150                         if ((nextoffset >= 0)
151                             || ((nextoffset == -FDT_ERR_TRUNCATED) && !depth))
152                                 return -FDT_ERR_NOTFOUND;
153                         else
154                                 return nextoffset;
155                 }
156         } while (tag != FDT_BEGIN_NODE);
157
158         return offset;
159 }
160
161 int fdt_first_subnode(const void *fdt, int offset)
162 {
163         int depth = 0;
164
165         offset = fdt_next_node(fdt, offset, &depth);
166         if (offset < 0 || depth != 1)
167                 return -FDT_ERR_NOTFOUND;
168
169         return offset;
170 }
171
172 int fdt_next_subnode(const void *fdt, int offset)
173 {
174         int depth = 1;
175
176         /*
177          * With respect to the parent, the depth of the next subnode will be
178          * the same as the last.
179          */
180         do {
181                 offset = fdt_next_node(fdt, offset, &depth);
182                 if (offset < 0 || depth < 1)
183                         return -FDT_ERR_NOTFOUND;
184         } while (depth > 1);
185
186         return offset;
187 }
188
189 const char *_fdt_find_string(const char *strtab, int tabsize, const char *s)
190 {
191         int len = strlen(s) + 1;
192         const char *last = strtab + tabsize - len;
193         const char *p;
194
195         for (p = strtab; p <= last; p++)
196                 if (memcmp(p, s, len) == 0)
197                         return p;
198         return NULL;
199 }
200
201 int fdt_move(const void *fdt, void *buf, int bufsize)
202 {
203         FDT_CHECK_HEADER(fdt);
204
205         if (fdt_totalsize(fdt) > bufsize)
206                 return -FDT_ERR_NOSPACE;
207
208         memmove(buf, fdt, fdt_totalsize(fdt));
209         return 0;
210 }