1 /*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "libufdt.h"
18
19 #include "ufdt_node_pool.h"
20
ufdt_node_construct(void * fdtp,fdt32_t * fdt_tag_ptr,struct ufdt_node_pool * pool)21 struct ufdt_node *ufdt_node_construct(void *fdtp, fdt32_t *fdt_tag_ptr,
22 struct ufdt_node_pool *pool) {
23 void *buf = ufdt_node_pool_alloc(pool);
24 uint32_t tag = fdt32_to_cpu(*fdt_tag_ptr);
25 if (tag == FDT_PROP) {
26 const struct fdt_property *prop = (const struct fdt_property *)fdt_tag_ptr;
27 struct ufdt_node_fdt_prop *res = (struct ufdt_node_fdt_prop *)buf;
28 if (res == NULL) return NULL;
29 res->parent.fdt_tag_ptr = fdt_tag_ptr;
30 res->parent.sibling = NULL;
31 res->name = fdt_string(fdtp, fdt32_to_cpu(prop->nameoff));
32
33 /* fdt_string() may fail */
34 if (!res->name) {
35 dto_error("Failed to get property name\n");
36 ufdt_node_pool_free(pool, res);
37 res = NULL;
38 }
39
40 return (struct ufdt_node *)res;
41 } else {
42 struct ufdt_node_fdt_node *res = (struct ufdt_node_fdt_node *)buf;
43 if (res == NULL) return NULL;
44 res->parent.fdt_tag_ptr = fdt_tag_ptr;
45 res->parent.sibling = NULL;
46 res->child = NULL;
47 res->last_child_p = &res->child;
48 return (struct ufdt_node *)res;
49 }
50 }
51
ufdt_node_destruct(struct ufdt_node * node,struct ufdt_node_pool * pool)52 void ufdt_node_destruct(struct ufdt_node *node, struct ufdt_node_pool *pool) {
53 if (node == NULL) return;
54
55 if (ufdt_node_tag(node) == FDT_BEGIN_NODE) {
56 struct ufdt_node *it = ((struct ufdt_node_fdt_node *)node)->child;
57 while (it != NULL) {
58 struct ufdt_node *next = it->sibling;
59 ufdt_node_destruct(it, pool);
60 it = next;
61 }
62 }
63
64 ufdt_node_pool_free(pool, node);
65 }
66
ufdt_node_add_child(struct ufdt_node * parent,struct ufdt_node * child)67 int ufdt_node_add_child(struct ufdt_node *parent, struct ufdt_node *child) {
68 if (!parent || !child) return -1;
69 if (ufdt_node_tag(parent) != FDT_BEGIN_NODE) return -1;
70
71 int err = 0;
72 uint32_t child_tag = ufdt_node_tag(child);
73 switch (child_tag) {
74 case FDT_PROP:
75 case FDT_BEGIN_NODE:
76 // Append the child node to the last child of parant node
77 *((struct ufdt_node_fdt_node *)parent)->last_child_p = child;
78 ((struct ufdt_node_fdt_node *)parent)->last_child_p = &child->sibling;
79 break;
80
81 default:
82 err = -1;
83 dto_error("invalid children tag type\n");
84 }
85
86 return err;
87 }
88
89 /*
90 * BEGIN of FDT_PROP related methods.
91 */
92
ufdt_node_get_subnode_by_name_len(const struct ufdt_node * node,const char * name,int len)93 struct ufdt_node *ufdt_node_get_subnode_by_name_len(const struct ufdt_node *node,
94 const char *name, int len) {
95 struct ufdt_node **it = NULL;
96 for_each_node(it, node) {
97 if (ufdt_node_name_eq(*it, name, len)) return *it;
98 }
99 return NULL;
100 }
101
ufdt_node_get_subnode_by_name(const struct ufdt_node * node,const char * name)102 struct ufdt_node *ufdt_node_get_subnode_by_name(const struct ufdt_node *node,
103 const char *name) {
104 return ufdt_node_get_subnode_by_name_len(node, name, strlen(name));
105 }
106
ufdt_node_get_property_by_name_len(const struct ufdt_node * node,const char * name,int len)107 struct ufdt_node *ufdt_node_get_property_by_name_len(
108 const struct ufdt_node *node, const char *name, int len) {
109 if (!node) return NULL;
110
111 struct ufdt_node **it = NULL;
112 for_each_prop(it, node) {
113 if (ufdt_node_name_eq(*it, name, len)) return *it;
114 }
115 return NULL;
116 }
117
ufdt_node_get_property_by_name(const struct ufdt_node * node,const char * name)118 struct ufdt_node *ufdt_node_get_property_by_name(const struct ufdt_node *node,
119 const char *name) {
120 return ufdt_node_get_property_by_name_len(node, name, dto_strlen(name));
121 }
122
ufdt_node_get_fdt_prop_data(const struct ufdt_node * node,int * out_len)123 char *ufdt_node_get_fdt_prop_data(const struct ufdt_node *node, int *out_len) {
124 if (!node || ufdt_node_tag(node) != FDT_PROP) {
125 return NULL;
126 }
127 const struct fdt_property *prop = (struct fdt_property *)node->fdt_tag_ptr;
128 if (out_len != NULL) {
129 uint32_t prop_len = fdt32_to_cpu(prop->len);
130
131 if (prop_len > INT_MAX) {
132 return NULL;
133 }
134
135 *out_len = prop_len;
136 }
137 return (char *)prop->data;
138 }
139
ufdt_node_get_fdt_prop_data_by_name_len(const struct ufdt_node * node,const char * name,int len,int * out_len)140 char *ufdt_node_get_fdt_prop_data_by_name_len(const struct ufdt_node *node,
141 const char *name, int len,
142 int *out_len) {
143 return ufdt_node_get_fdt_prop_data(
144 ufdt_node_get_property_by_name_len(node, name, len), out_len);
145 }
146
ufdt_node_get_fdt_prop_data_by_name(const struct ufdt_node * node,const char * name,int * out_len)147 char *ufdt_node_get_fdt_prop_data_by_name(const struct ufdt_node *node,
148 const char *name, int *out_len) {
149 return ufdt_node_get_fdt_prop_data(ufdt_node_get_property_by_name(node, name),
150 out_len);
151 }
152
153 /*
154 * END of FDT_PROP related methods.
155 */
156
157 /*
158 * BEGIN of searching-in-ufdt_node methods.
159 */
160
ufdt_node_get_phandle(const struct ufdt_node * node)161 uint32_t ufdt_node_get_phandle(const struct ufdt_node *node) {
162 if (!node || ufdt_node_tag(node) != FDT_BEGIN_NODE) {
163 return 0;
164 }
165 int len = 0;
166 void *ptr = ufdt_node_get_fdt_prop_data_by_name(node, "phandle", &len);
167 if (!ptr || len != sizeof(fdt32_t)) {
168 ptr = ufdt_node_get_fdt_prop_data_by_name(node, "linux,phandle", &len);
169 if (!ptr || len != sizeof(fdt32_t)) {
170 return 0;
171 }
172 }
173 return fdt32_to_cpu(*((fdt32_t *)ptr));
174 }
175
ufdt_node_get_node_by_path_len(const struct ufdt_node * node,const char * path,int len)176 struct ufdt_node *ufdt_node_get_node_by_path_len(const struct ufdt_node *node,
177 const char *path, int len) {
178 const char *end = path + len;
179
180 struct ufdt_node *cur = (struct ufdt_node *)node;
181
182 while (path < end) {
183 while (path[0] == '/') path++;
184 if (path == end) return cur;
185
186 const char *next_slash;
187 next_slash = dto_memchr(path, '/', end - path);
188 if (!next_slash) next_slash = end;
189
190 struct ufdt_node *next = NULL;
191
192 next = ufdt_node_get_subnode_by_name_len(cur, path, next_slash - path);
193
194 cur = next;
195 path = next_slash;
196 if (!cur) return cur;
197 }
198
199 return cur;
200 }
201
ufdt_node_get_node_by_path(const struct ufdt_node * node,const char * path)202 struct ufdt_node *ufdt_node_get_node_by_path(const struct ufdt_node *node,
203 const char *path) {
204 return ufdt_node_get_node_by_path_len(node, path, dto_strlen(path));
205 }
206
ufdt_node_name_eq(const struct ufdt_node * node,const char * name,int len)207 bool ufdt_node_name_eq(const struct ufdt_node *node, const char *name, int len) {
208 if (!node) return false;
209 if (!name) return false;
210 if (dto_strncmp(ufdt_node_name(node), name, len) != 0) return false;
211 if (ufdt_node_name(node)[len] != '\0') return false;
212 return true;
213 }
214
215 /*
216 * END of searching-in-ufdt_node methods.
217 */
218
merge_children(struct ufdt_node * node_a,struct ufdt_node * node_b,struct ufdt_node_pool * pool)219 static int merge_children(struct ufdt_node *node_a, struct ufdt_node *node_b,
220 struct ufdt_node_pool *pool) {
221 int err = 0;
222 struct ufdt_node *it;
223 for (it = ((struct ufdt_node_fdt_node *)node_b)->child; it;) {
224 struct ufdt_node *cur_node = it;
225 it = it->sibling;
226 cur_node->sibling = NULL;
227 struct ufdt_node *target_node = NULL;
228 if (ufdt_node_tag(cur_node) == FDT_BEGIN_NODE) {
229 target_node =
230 ufdt_node_get_subnode_by_name(node_a, ufdt_node_name(cur_node));
231 } else {
232 target_node =
233 ufdt_node_get_property_by_name(node_a, ufdt_node_name(cur_node));
234 }
235 if (target_node == NULL) {
236 err = ufdt_node_add_child(node_a, cur_node);
237 } else {
238 err = ufdt_node_merge_into(target_node, cur_node, pool);
239 ufdt_node_pool_free(pool, cur_node);
240 }
241 if (err < 0) return -1;
242 }
243 /*
244 * The ufdt_node* in node_b will be copied to node_a.
245 * To prevent the ufdt_node from being freed twice
246 * (main_tree and overlay_tree) at the end of function
247 * ufdt_apply_overlay(), set this node in node_b
248 * (overlay_tree) to NULL.
249 */
250 ((struct ufdt_node_fdt_node *)node_b)->child = NULL;
251
252 return 0;
253 }
254
ufdt_node_merge_into(struct ufdt_node * node_a,struct ufdt_node * node_b,struct ufdt_node_pool * pool)255 int ufdt_node_merge_into(struct ufdt_node *node_a, struct ufdt_node *node_b,
256 struct ufdt_node_pool *pool) {
257 if (ufdt_node_tag(node_a) == FDT_PROP) {
258 node_a->fdt_tag_ptr = node_b->fdt_tag_ptr;
259 return 0;
260 }
261
262 int err = 0;
263 err = merge_children(node_a, node_b, pool);
264 if (err < 0) return -1;
265
266 return 0;
267 }
268
269 #define TAB_SIZE 2
270
ufdt_node_print(const struct ufdt_node * node,int depth)271 void ufdt_node_print(const struct ufdt_node *node, int depth) {
272 if (!node) return;
273
274 int i;
275 for (i = 0; i < depth * TAB_SIZE; i++) dto_print(" ");
276
277 uint32_t tag;
278 tag = ufdt_node_tag(node);
279
280 switch (tag) {
281 case FDT_BEGIN_NODE:
282 dto_print("NODE ");
283 break;
284 case FDT_PROP:
285 dto_print("PROP ");
286 break;
287 default:
288 dto_print("UNKNOWN ");
289 break;
290 }
291
292 if (ufdt_node_name(node)) {
293 dto_print(":%s:\n", ufdt_node_name(node));
294 } else {
295 dto_print("node name is NULL.\n");
296 }
297
298 if (ufdt_node_tag(node) == FDT_BEGIN_NODE) {
299 struct ufdt_node **it;
300
301 for_each_prop(it, node) ufdt_node_print(*it, depth + 1);
302
303 for_each_node(it, node) ufdt_node_print(*it, depth + 1);
304 }
305 }
306