fdt.c revision 1.1.1.2.4.2 1 /* $NetBSD: fdt.c,v 1.1.1.2.4.2 2017/12/03 11:38:02 jdolecek Exp $ */
2
3 /*
4 * libfdt - Flat Device Tree manipulation
5 * Copyright (C) 2006 David Gibson, IBM Corporation.
6 *
7 * libfdt is dual licensed: you can use it either under the terms of
8 * the GPL, or the BSD license, at your option.
9 *
10 * a) This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License as
12 * published by the Free Software Foundation; either version 2 of the
13 * License, or (at your option) any later version.
14 *
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public
21 * License along with this library; if not, write to the Free
22 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
23 * MA 02110-1301 USA
24 *
25 * Alternatively,
26 *
27 * b) Redistribution and use in source and binary forms, with or
28 * without modification, are permitted provided that the following
29 * conditions are met:
30 *
31 * 1. Redistributions of source code must retain the above
32 * copyright notice, this list of conditions and the following
33 * disclaimer.
34 * 2. Redistributions in binary form must reproduce the above
35 * copyright notice, this list of conditions and the following
36 * disclaimer in the documentation and/or other materials
37 * provided with the distribution.
38 *
39 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
40 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
41 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
42 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
43 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
44 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
45 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
46 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
47 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
48 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
49 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
50 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
51 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
52 */
53 #include "libfdt_env.h"
54
55 #include <fdt.h>
56 #include <libfdt.h>
57
58 #include "libfdt_internal.h"
59
60 int fdt_check_header(const void *fdt)
61 {
62 if (fdt_magic(fdt) == FDT_MAGIC) {
63 /* Complete tree */
64 if (fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION)
65 return -FDT_ERR_BADVERSION;
66 if (fdt_last_comp_version(fdt) > FDT_LAST_SUPPORTED_VERSION)
67 return -FDT_ERR_BADVERSION;
68 } else if (fdt_magic(fdt) == FDT_SW_MAGIC) {
69 /* Unfinished sequential-write blob */
70 if (fdt_size_dt_struct(fdt) == 0)
71 return -FDT_ERR_BADSTATE;
72 } else {
73 return -FDT_ERR_BADMAGIC;
74 }
75
76 return 0;
77 }
78
79 const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int len)
80 {
81 unsigned absoffset = offset + fdt_off_dt_struct(fdt);
82
83 if ((absoffset < offset)
84 || ((absoffset + len) < absoffset)
85 || (absoffset + len) > fdt_totalsize(fdt))
86 return NULL;
87
88 if (fdt_version(fdt) >= 0x11)
89 if (((offset + len) < offset)
90 || ((offset + len) > fdt_size_dt_struct(fdt)))
91 return NULL;
92
93 return _fdt_offset_ptr(fdt, offset);
94 }
95
96 uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset)
97 {
98 const fdt32_t *tagp, *lenp;
99 uint32_t tag;
100 int offset = startoffset;
101 const char *p;
102
103 *nextoffset = -FDT_ERR_TRUNCATED;
104 tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE);
105 if (!tagp)
106 return FDT_END; /* premature end */
107 tag = fdt32_to_cpu(*tagp);
108 offset += FDT_TAGSIZE;
109
110 *nextoffset = -FDT_ERR_BADSTRUCTURE;
111 switch (tag) {
112 case FDT_BEGIN_NODE:
113 /* skip name */
114 do {
115 p = fdt_offset_ptr(fdt, offset++, 1);
116 } while (p && (*p != '\0'));
117 if (!p)
118 return FDT_END; /* premature end */
119 break;
120
121 case FDT_PROP:
122 lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp));
123 if (!lenp)
124 return FDT_END; /* premature end */
125 /* skip-name offset, length and value */
126 offset += sizeof(struct fdt_property) - FDT_TAGSIZE
127 + fdt32_to_cpu(*lenp);
128 break;
129
130 case FDT_END:
131 case FDT_END_NODE:
132 case FDT_NOP:
133 break;
134
135 default:
136 return FDT_END;
137 }
138
139 if (!fdt_offset_ptr(fdt, startoffset, offset - startoffset))
140 return FDT_END; /* premature end */
141
142 *nextoffset = FDT_TAGALIGN(offset);
143 return tag;
144 }
145
146 int _fdt_check_node_offset(const void *fdt, int offset)
147 {
148 if ((offset < 0) || (offset % FDT_TAGSIZE)
149 || (fdt_next_tag(fdt, offset, &offset) != FDT_BEGIN_NODE))
150 return -FDT_ERR_BADOFFSET;
151
152 return offset;
153 }
154
155 int _fdt_check_prop_offset(const void *fdt, int offset)
156 {
157 if ((offset < 0) || (offset % FDT_TAGSIZE)
158 || (fdt_next_tag(fdt, offset, &offset) != FDT_PROP))
159 return -FDT_ERR_BADOFFSET;
160
161 return offset;
162 }
163
164 int fdt_next_node(const void *fdt, int offset, int *depth)
165 {
166 int nextoffset = 0;
167 uint32_t tag;
168
169 if (offset >= 0)
170 if ((nextoffset = _fdt_check_node_offset(fdt, offset)) < 0)
171 return nextoffset;
172
173 do {
174 offset = nextoffset;
175 tag = fdt_next_tag(fdt, offset, &nextoffset);
176
177 switch (tag) {
178 case FDT_PROP:
179 case FDT_NOP:
180 break;
181
182 case FDT_BEGIN_NODE:
183 if (depth)
184 (*depth)++;
185 break;
186
187 case FDT_END_NODE:
188 if (depth && ((--(*depth)) < 0))
189 return nextoffset;
190 break;
191
192 case FDT_END:
193 if ((nextoffset >= 0)
194 || ((nextoffset == -FDT_ERR_TRUNCATED) && !depth))
195 return -FDT_ERR_NOTFOUND;
196 else
197 return nextoffset;
198 }
199 } while (tag != FDT_BEGIN_NODE);
200
201 return offset;
202 }
203
204 int fdt_first_subnode(const void *fdt, int offset)
205 {
206 int depth = 0;
207
208 offset = fdt_next_node(fdt, offset, &depth);
209 if (offset < 0 || depth != 1)
210 return -FDT_ERR_NOTFOUND;
211
212 return offset;
213 }
214
215 int fdt_next_subnode(const void *fdt, int offset)
216 {
217 int depth = 1;
218
219 /*
220 * With respect to the parent, the depth of the next subnode will be
221 * the same as the last.
222 */
223 do {
224 offset = fdt_next_node(fdt, offset, &depth);
225 if (offset < 0 || depth < 1)
226 return -FDT_ERR_NOTFOUND;
227 } while (depth > 1);
228
229 return offset;
230 }
231
232 const char *_fdt_find_string(const char *strtab, int tabsize, const char *s)
233 {
234 int len = strlen(s) + 1;
235 const char *last = strtab + tabsize - len;
236 const char *p;
237
238 for (p = strtab; p <= last; p++)
239 if (memcmp(p, s, len) == 0)
240 return p;
241 return NULL;
242 }
243
244 int fdt_move(const void *fdt, void *buf, int bufsize)
245 {
246 FDT_CHECK_HEADER(fdt);
247
248 if (fdt_totalsize(fdt) > bufsize)
249 return -FDT_ERR_NOSPACE;
250
251 memmove(buf, fdt, fdt_totalsize(fdt));
252 return 0;
253 }
254