blob: db5281cbda8e315e08b64102bb38ec73df8c5339 [file] [log] [blame]
David Gibson3da0f9a2006-11-27 16:21:28 +11001/*
2 * libfdt - Flat Device Tree manipulation
3 * Copyright (C) 2006 David Gibson, IBM Corporation.
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public License
7 * as published by the Free Software Foundation; either version 2.1 of
8 * the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19#include "libfdt_env.h"
20
21#include <fdt.h>
22#include <libfdt.h>
23
24#include "libfdt_internal.h"
25
26static int check_header(const struct fdt_header *fdt)
27{
David Gibsonede25de2006-12-01 15:02:10 +110028 if (fdt_magic(fdt) == FDT_MAGIC) {
David Gibson063693a2006-11-29 16:45:46 +110029 /* Complete tree */
David Gibsonede25de2006-12-01 15:02:10 +110030 if (fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION)
David Gibson063693a2006-11-29 16:45:46 +110031 return FDT_ERR_BADVERSION;
David Gibsonede25de2006-12-01 15:02:10 +110032 if (fdt_last_comp_version(fdt) > FDT_LAST_SUPPORTED_VERSION)
David Gibson063693a2006-11-29 16:45:46 +110033 return FDT_ERR_BADVERSION;
David Gibsonede25de2006-12-01 15:02:10 +110034 } else if (fdt_magic(fdt) == SW_MAGIC) {
David Gibson063693a2006-11-29 16:45:46 +110035 /* Unfinished sequential-write blob */
David Gibsonede25de2006-12-01 15:02:10 +110036 if (sw_size_dt_struct(fdt) == 0)
David Gibson063693a2006-11-29 16:45:46 +110037 return FDT_ERR_BADSTATE;
38 } else {
David Gibson3da0f9a2006-11-27 16:21:28 +110039 return FDT_ERR_BADMAGIC;
David Gibson063693a2006-11-29 16:45:46 +110040 }
41
David Gibson3da0f9a2006-11-27 16:21:28 +110042 return 0;
43}
44
45#define OFFSET_CHECK_HEADER(fdt) \
46 { \
47 int err; \
48 if ((err = check_header(fdt)) != 0) \
49 return OFFSET_ERROR(err); \
50 }
51
52static int offset_streq(const struct fdt_header *fdt, int offset,
53 const char *s, int len)
54{
55 const char *p = fdt_offset_ptr(fdt, offset, len+1);
56
57 if (! p)
58 /* short match */
59 return 0;
60
61 if (memcmp(p, s, len) != 0)
62 return 0;
63
64 if (p[len] != '\0')
65 return 0;
66
67 return 1;
68}
69
David Gibson3aa4cfd2006-11-29 16:34:30 +110070char *fdt_string(const struct fdt_header *fdt, int stroffset)
71{
David Gibsonede25de2006-12-01 15:02:10 +110072 return (char *)fdt + fdt_off_dt_strings(fdt) + stroffset;
David Gibson3aa4cfd2006-11-29 16:34:30 +110073}
74
David Gibson3da0f9a2006-11-27 16:21:28 +110075int fdt_property_offset(const struct fdt_header *fdt, int nodeoffset,
76 const char *name)
77{
78 int level = 0;
79 uint32_t tag;
80 struct fdt_property *prop;
81 int namestroff;
82 int offset, nextoffset;
83
84 OFFSET_CHECK_HEADER(fdt);
85
86 if (nodeoffset % FDT_TAGSIZE)
87 return OFFSET_ERROR(FDT_ERR_BADOFFSET);
88
89 tag = _fdt_next_tag(fdt, nodeoffset, &nextoffset);
90 if (tag != FDT_BEGIN_NODE)
91 return OFFSET_ERROR(FDT_ERR_BADOFFSET);
92
93 do {
94 offset = nextoffset;
95 if (offset % FDT_TAGSIZE)
96 return OFFSET_ERROR(FDT_ERR_INTERNAL);
97
98 tag = _fdt_next_tag(fdt, offset, &nextoffset);
99 switch (tag) {
100 case FDT_END:
101 return OFFSET_ERROR(FDT_ERR_TRUNCATED);
102
103 case FDT_BEGIN_NODE:
104 level++;
105 break;
106
107 case FDT_END_NODE:
108 level--;
109 break;
110
111 case FDT_PROP:
112 if (level != 0)
113 continue;
114
115 prop = fdt_offset_ptr_typed(fdt, offset, prop);
116 if (! prop)
117 return OFFSET_ERROR(FDT_ERR_BADSTRUCTURE);
118 namestroff = fdt32_to_cpu(prop->nameoff);
David Gibson3aa4cfd2006-11-29 16:34:30 +1100119 if (streq(fdt_string(fdt, namestroff), name))
David Gibson3da0f9a2006-11-27 16:21:28 +1100120 /* Found it! */
121 return offset;
122 break;
123
124 case FDT_NOP:
125 break;
126
127 default:
128 return OFFSET_ERROR(FDT_ERR_BADSTRUCTURE);
129 }
130 } while (level >= 0);
131
132 return OFFSET_ERROR(FDT_ERR_NOTFOUND);
133}
134
135int fdt_subnode_offset_namelen(const struct fdt_header *fdt, int parentoffset,
136 const char *name, int namelen)
137{
138 int level = 0;
139 uint32_t tag;
140 int offset, nextoffset;
141
142 OFFSET_CHECK_HEADER(fdt);
143
144 tag = _fdt_next_tag(fdt, parentoffset, &nextoffset);
145 if (tag != FDT_BEGIN_NODE)
146 return OFFSET_ERROR(FDT_ERR_BADOFFSET);
147
148 do {
149 offset = nextoffset;
150 tag = _fdt_next_tag(fdt, offset, &nextoffset);
151
152 switch (tag) {
153 case FDT_END:
154 return OFFSET_ERROR(FDT_ERR_TRUNCATED);
155
156 case FDT_BEGIN_NODE:
157 level++;
158 if (level != 1)
159 continue;
160 if (offset_streq(fdt, offset+FDT_TAGSIZE, name, namelen))
161 /* Found it! */
162 return offset;
163 break;
164
165 case FDT_END_NODE:
166 level--;
167 break;
168
169 case FDT_PROP:
170 case FDT_NOP:
171 break;
172
173 default:
174 return OFFSET_ERROR(FDT_ERR_BADSTRUCTURE);
175 }
176 } while (level >= 0);
177
178 return OFFSET_ERROR(FDT_ERR_NOTFOUND);
179}
180
181int fdt_subnode_offset(const struct fdt_header *fdt, int parentoffset,
182 const char *name)
183{
184 return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name));
185}
186
187int fdt_path_offset(const struct fdt_header *fdt, const char *path)
188{
189 const char *end = path + strlen(path);
190 const char *p = path;
191 int offset = 0;
192
193 OFFSET_CHECK_HEADER(fdt);
194
195 if (*path != '/')
196 return OFFSET_ERROR(FDT_ERR_BADPATH);
197
198 while (*p) {
199 const char *q;
200
201 while (*p == '/')
202 p++;
203 if (! *p)
204 return OFFSET_ERROR(FDT_ERR_BADPATH);
205 q = strchr(p, '/');
206 if (! q)
207 q = end;
208
209 offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p);
210 if (fdt_offset_error(offset))
211 return offset;
212
213 p = q;
214 }
215
216 return offset;
217}
218
219struct fdt_property *_fdt_getprop(const struct fdt_header *fdt, int nodeoffset,
220 const char *name, int *lenp)
221{
222 int propoffset;
223 struct fdt_property *prop;
224 int err;
225 int len;
226
227 propoffset = fdt_property_offset(fdt, nodeoffset, name);
228 if ((err = fdt_offset_error(propoffset)))
229 return PTR_ERROR(err);
230
231 prop = fdt_offset_ptr(fdt, propoffset, sizeof(prop));
232 if (! prop)
233 return PTR_ERROR(FDT_ERR_BADSTRUCTURE);
234 len = fdt32_to_cpu(prop->len);
235 prop = fdt_offset_ptr(fdt, propoffset, sizeof(prop) + len);
236 if (! prop)
237 return PTR_ERROR(FDT_ERR_BADSTRUCTURE);
238
239 if (lenp)
240 *lenp = len;
241
242 return prop;
243}
244
245void *fdt_getprop(const struct fdt_header *fdt, int nodeoffset,
246 const char *name, int *lenp)
247{
248 const struct fdt_property *prop;
249 int err;
250
251 prop = _fdt_getprop(fdt, nodeoffset, name, lenp);
252 if ((err = fdt_ptr_error(prop)))
253 return PTR_ERROR(err);
254
255 return prop->data;
256}