• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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