• 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 #include "ufdt_prop_dict.h"
21 
ufdt_construct(void * fdtp,struct ufdt_node_pool * pool)22 struct ufdt *ufdt_construct(void *fdtp, struct ufdt_node_pool *pool) {
23   (void)(pool); /* unused parameter */
24 
25   /* Inital size is 2, will be exponentially increased when it needed later.
26      (2 -> 4 -> 8 -> ...) */
27   const int DEFAULT_MEM_SIZE_FDTPS = 2;
28 
29   void **fdtps = NULL;
30   struct ufdt *res_ufdt = NULL;
31 
32   fdtps = (void **)dto_malloc(sizeof(void *) * DEFAULT_MEM_SIZE_FDTPS);
33   if (fdtps == NULL) goto error;
34   fdtps[0] = fdtp;
35 
36   res_ufdt = dto_malloc(sizeof(struct ufdt));
37   if (res_ufdt == NULL) goto error;
38 
39   res_ufdt->fdtps = fdtps;
40   res_ufdt->mem_size_fdtps = DEFAULT_MEM_SIZE_FDTPS;
41   res_ufdt->num_used_fdtps = (fdtp != NULL ? 1 : 0);
42   res_ufdt->root = NULL;
43   res_ufdt->phandle_table.data = NULL;
44   res_ufdt->phandle_table.len = 0;
45 
46   return res_ufdt;
47 
48 error:
49   if (res_ufdt) dto_free(res_ufdt);
50   if (fdtps) dto_free(fdtps);
51 
52   return NULL;
53 }
54 
ufdt_destruct(struct ufdt * tree,struct ufdt_node_pool * pool)55 void ufdt_destruct(struct ufdt *tree, struct ufdt_node_pool *pool) {
56   if (tree == NULL) return;
57 
58   ufdt_node_destruct(tree->root, pool);
59 
60   dto_free(tree->fdtps);
61   dto_free(tree->phandle_table.data);
62   dto_free(tree);
63 }
64 
ufdt_add_fdt(struct ufdt * tree,void * fdtp)65 int ufdt_add_fdt(struct ufdt *tree, void *fdtp) {
66   if (fdtp == NULL) {
67     return -1;
68   }
69 
70   int i = tree->num_used_fdtps;
71   if (i >= tree->mem_size_fdtps) {
72     int new_size = tree->mem_size_fdtps * 2;
73     void **new_fdtps = dto_malloc(sizeof(void *) * new_size);
74     if (new_fdtps == NULL) return -1;
75 
76     dto_memcpy(new_fdtps, tree->fdtps, sizeof(void *) * tree->mem_size_fdtps);
77     dto_free(tree->fdtps);
78 
79     tree->fdtps = new_fdtps;
80     tree->mem_size_fdtps = new_size;
81   }
82 
83   tree->fdtps[i] = fdtp;
84   tree->num_used_fdtps = i + 1;
85 
86   return 0;
87 }
88 
ufdt_get_string_off(const struct ufdt * tree,const char * s)89 int ufdt_get_string_off(const struct ufdt *tree, const char *s) {
90   /* fdt_create() sets the dt_string_off to the end of fdt buffer,
91      and _ufdt_output_strtab_to_fdt() copy all string tables in reversed order.
92      So, here the return offset value is base on the end of all string buffers,
93      and it should be a minus value. */
94   int res_off = 0;
95   for (int i = 0; i < tree->num_used_fdtps; i++) {
96     void *fdt = tree->fdtps[i];
97     const char *strtab_start = (const char *)fdt + fdt_off_dt_strings(fdt);
98     int strtab_size = fdt_size_dt_strings(fdt);
99     const char *strtab_end = strtab_start + strtab_size;
100 
101     /* Check if the string is in the string table */
102     if (s >= strtab_start && s < strtab_end) {
103       res_off += (s - strtab_end);
104       return res_off;
105     }
106 
107     res_off -= strtab_size;
108   }
109   /* Can not find the string, return 0 */
110   return 0;
111 }
112 
ufdt_new_node(void * fdtp,int node_offset,struct ufdt_node_pool * pool)113 static struct ufdt_node *ufdt_new_node(void *fdtp, int node_offset,
114                                        struct ufdt_node_pool *pool) {
115   if (fdtp == NULL) {
116     dto_error("Failed to get new_node because tree is NULL\n");
117     return NULL;
118   }
119 
120   fdt32_t *fdt_tag_ptr =
121       (fdt32_t *)fdt_offset_ptr(fdtp, node_offset, sizeof(fdt32_t));
122   struct ufdt_node *res = ufdt_node_construct(fdtp, fdt_tag_ptr, pool);
123   return res;
124 }
125 
fdt_to_ufdt_tree(void * fdtp,int cur_fdt_tag_offset,int * next_fdt_tag_offset,int cur_tag,struct ufdt_node_pool * pool)126 static struct ufdt_node *fdt_to_ufdt_tree(void *fdtp, int cur_fdt_tag_offset,
127                                           int *next_fdt_tag_offset, int cur_tag,
128                                           struct ufdt_node_pool *pool) {
129   if (fdtp == NULL) {
130     return NULL;
131   }
132   uint32_t tag;
133   struct ufdt_node *res, *child_node;
134 
135   res = NULL;
136   child_node = NULL;
137   tag = cur_tag;
138 
139   switch (tag) {
140     case FDT_END_NODE:
141     case FDT_NOP:
142     case FDT_END:
143       break;
144 
145     case FDT_PROP:
146       res = ufdt_new_node(fdtp, cur_fdt_tag_offset, pool);
147       break;
148 
149     case FDT_BEGIN_NODE:
150       res = ufdt_new_node(fdtp, cur_fdt_tag_offset, pool);
151 
152       do {
153         cur_fdt_tag_offset = *next_fdt_tag_offset;
154 
155         tag = fdt_next_tag(fdtp, cur_fdt_tag_offset, next_fdt_tag_offset);
156         if (tag == FDT_END) {
157           dto_error("failed to get next tag\n");
158           break;
159         }
160 
161         child_node = fdt_to_ufdt_tree(fdtp, cur_fdt_tag_offset,
162                                       next_fdt_tag_offset, tag, pool);
163         ufdt_node_add_child(res, child_node);
164       } while (tag != FDT_END_NODE);
165       break;
166 
167     default:
168       break;
169   }
170 
171   return res;
172 }
173 
ufdt_print(struct ufdt * tree)174 void ufdt_print(struct ufdt *tree) {
175   ufdt_node_print(tree->root, 0);
176 }
177 
ufdt_get_node_by_path_len(struct ufdt * tree,const char * path,int len)178 struct ufdt_node *ufdt_get_node_by_path_len(struct ufdt *tree, const char *path,
179                                             int len) {
180   /*
181    * RARE: aliases
182    * In device tree, we can assign some alias to specific nodes by defining
183    * these relation in "/aliases" node.
184    * The node has the form:
185    * {
186    *   a = "/a_for_apple";
187    *   b = "/b_for_banana";
188    * };
189    * So the path "a/subnode_1" should be expanded to "/a_for_apple/subnode_1".
190    */
191   if (*path != '/') {
192     const char *end = path + len;
193 
194     const char *next_slash;
195     next_slash = dto_memchr(path, '/', end - path);
196     if (!next_slash) next_slash = end;
197 
198     struct ufdt_node *aliases_node =
199         ufdt_node_get_node_by_path(tree->root, "/aliases");
200     aliases_node = ufdt_node_get_property_by_name_len(aliases_node, path,
201                                                       next_slash - path);
202 
203     int path_len = 0;
204     const char *alias_path =
205         ufdt_node_get_fdt_prop_data(aliases_node, &path_len);
206 
207     if (alias_path == NULL || path_len == 0) {
208       dto_error("Failed to find valid alias %s\n", path);
209       return NULL;
210     }
211 
212     /* property data must be a nul terminated string */
213     int alias_len = strnlen(alias_path, path_len);
214 
215     if (alias_len != path_len - 1 || alias_len == 0) {
216       dto_error("Invalid alias for %s\n", path);
217       return NULL;
218     }
219 
220     struct ufdt_node *target_node =
221         ufdt_node_get_node_by_path_len(tree->root, alias_path, alias_len);
222 
223     return ufdt_node_get_node_by_path_len(target_node, next_slash,
224                                           end - next_slash);
225   }
226   return ufdt_node_get_node_by_path_len(tree->root, path, len);
227 }
228 
ufdt_get_node_by_path(struct ufdt * tree,const char * path)229 struct ufdt_node *ufdt_get_node_by_path(struct ufdt *tree, const char *path) {
230   return ufdt_get_node_by_path_len(tree, path, dto_strlen(path));
231 }
232 
ufdt_get_node_by_phandle(struct ufdt * tree,uint32_t phandle)233 struct ufdt_node *ufdt_get_node_by_phandle(struct ufdt *tree,
234                                            uint32_t phandle) {
235   struct ufdt_node *res = NULL;
236   /*
237    * Do binary search in phandle_table.data.
238    * [s, e) means the possible range which contains target node.
239    */
240   int s = 0, e = tree->phandle_table.len;
241   while (e - s > 1) {
242     int mid = s + ((e - s) >> 1);
243     uint32_t mid_phandle = tree->phandle_table.data[mid].phandle;
244     if (phandle < mid_phandle)
245       e = mid;
246     else
247       s = mid;
248   }
249   if (e - s > 0 && tree->phandle_table.data[s].phandle == phandle) {
250     res = tree->phandle_table.data[s].node;
251   }
252   return res;
253 }
254 
count_phandle_node(struct ufdt_node * node)255 static int count_phandle_node(struct ufdt_node *node) {
256   if (node == NULL) return 0;
257   if (ufdt_node_tag(node) != FDT_BEGIN_NODE) return 0;
258   int res = 0;
259   if (ufdt_node_get_phandle(node) > 0) res++;
260   struct ufdt_node **it;
261   for_each_child(it, node) { res += count_phandle_node(*it); }
262   return res;
263 }
264 
set_phandle_table_entry(struct ufdt_node * node,struct ufdt_phandle_table_entry * data,int * cur)265 static void set_phandle_table_entry(struct ufdt_node *node,
266                                     struct ufdt_phandle_table_entry *data,
267                                     int *cur) {
268   if (node == NULL || ufdt_node_tag(node) != FDT_BEGIN_NODE) return;
269   uint32_t ph = ufdt_node_get_phandle(node);
270   if (ph > 0) {
271     data[*cur].phandle = ph;
272     data[*cur].node = node;
273     (*cur)++;
274   }
275   struct ufdt_node **it;
276   for_each_child(it, node) set_phandle_table_entry(*it, data, cur);
277   return;
278 }
279 
phandle_table_entry_cmp(const void * pa,const void * pb)280 int phandle_table_entry_cmp(const void *pa, const void *pb) {
281   uint32_t ph_a = ((const struct ufdt_phandle_table_entry *)pa)->phandle;
282   uint32_t ph_b = ((const struct ufdt_phandle_table_entry *)pb)->phandle;
283   if (ph_a < ph_b)
284     return -1;
285   else if (ph_a == ph_b)
286     return 0;
287   else
288     return 1;
289 }
290 
build_phandle_table(struct ufdt * tree)291 struct ufdt_static_phandle_table build_phandle_table(struct ufdt *tree) {
292   struct ufdt_static_phandle_table res;
293   res.len = count_phandle_node(tree->root);
294   if (!res.len) {
295     dto_debug("phandle table is empty\n");
296     res.data = NULL;
297     return res;
298   }
299   res.data = dto_malloc(sizeof(struct ufdt_phandle_table_entry) * res.len);
300   int cur = 0;
301   set_phandle_table_entry(tree->root, res.data, &cur);
302   dto_qsort(res.data, res.len, sizeof(struct ufdt_phandle_table_entry),
303             phandle_table_entry_cmp);
304   return res;
305 }
306 
ufdt_from_fdt(void * fdtp,size_t fdt_size,struct ufdt_node_pool * pool)307 struct ufdt *ufdt_from_fdt(void *fdtp, size_t fdt_size,
308                            struct ufdt_node_pool *pool) {
309   (void)(fdt_size); /* unused parameter */
310 
311   int start_offset = fdt_path_offset(fdtp, "/");
312   if (start_offset < 0) {
313     return ufdt_construct(NULL, pool);
314   }
315 
316   int end_offset;
317   int start_tag = fdt_next_tag(fdtp, start_offset, &end_offset);
318 
319   if (start_tag != FDT_BEGIN_NODE) {
320     return ufdt_construct(NULL, pool);
321   }
322 
323   struct ufdt *res_tree = ufdt_construct(fdtp, pool);
324   if (res_tree == NULL) return NULL;
325 
326   res_tree->root =
327       fdt_to_ufdt_tree(fdtp, start_offset, &end_offset, start_tag, pool);
328 
329   res_tree->phandle_table = build_phandle_table(res_tree);
330 
331   return res_tree;
332 }
333 
_ufdt_get_property_nameoff(const struct ufdt * tree,const char * name,const struct ufdt_prop_dict * dict)334 static int _ufdt_get_property_nameoff(const struct ufdt *tree, const char *name,
335                                       const struct ufdt_prop_dict *dict) {
336   int res;
337   const struct fdt_property *same_name_prop = ufdt_prop_dict_find(dict, name);
338   if (same_name_prop != NULL) {
339     /* There is a property with same name, just use its string offset */
340     res = fdt32_to_cpu(same_name_prop->nameoff);
341   } else {
342     /* Get the string offset from the string table of the current tree */
343     res = ufdt_get_string_off(tree, name);
344     if (res == 0) {
345       dto_error("Cannot find property name in string table: %s\n", name);
346       return 0;
347     }
348   }
349   return res;
350 }
351 
_ufdt_output_property_to_fdt(const struct ufdt * tree,void * fdtp,const struct ufdt_node_fdt_prop * prop_node,struct ufdt_prop_dict * dict)352 static int _ufdt_output_property_to_fdt(
353     const struct ufdt *tree, void *fdtp,
354     const struct ufdt_node_fdt_prop *prop_node, struct ufdt_prop_dict *dict) {
355   int nameoff = _ufdt_get_property_nameoff(tree, prop_node->name, dict);
356   if (nameoff == 0) return -1;
357 
358   int data_len = 0;
359   void *data = ufdt_node_get_fdt_prop_data(&prop_node->parent, &data_len);
360   if (!data) {
361     dto_error("Failed to get property data.\n");
362     return -1;
363   }
364 
365   unsigned int aligned_data_len =
366       ((unsigned int)data_len + (FDT_TAGSIZE - 1u)) & ~(FDT_TAGSIZE - 1u);
367 
368   unsigned int new_propoff = fdt_size_dt_struct(fdtp);
369   unsigned int new_prop_size = sizeof(struct fdt_property) + aligned_data_len;
370   struct fdt_property *new_prop =
371       (struct fdt_property *)((char *)fdtp + fdt_off_dt_struct(fdtp) +
372                               new_propoff);
373   char *fdt_end = (char *)fdtp + fdt_totalsize(fdtp);
374   if (fdt_end - (char *)new_prop < (ptrdiff_t)new_prop_size) {
375     dto_error("Not enough space for adding property.\n");
376     return -1;
377   }
378   fdt_set_size_dt_struct(fdtp, new_propoff + new_prop_size);
379 
380   new_prop->tag = cpu_to_fdt32(FDT_PROP);
381   new_prop->nameoff = cpu_to_fdt32(nameoff);
382   new_prop->len = cpu_to_fdt32(data_len);
383   dto_memcpy(new_prop->data, data, data_len);
384 
385   ufdt_prop_dict_add(dict, new_prop);
386 
387   return 0;
388 }
389 
_ufdt_output_node_to_fdt(const struct ufdt * tree,void * fdtp,const struct ufdt_node * node,struct ufdt_prop_dict * dict)390 static int _ufdt_output_node_to_fdt(const struct ufdt *tree, void *fdtp,
391                                     const struct ufdt_node *node,
392                                     struct ufdt_prop_dict *dict) {
393   uint32_t tag = ufdt_node_tag(node);
394 
395   if (tag == FDT_PROP) {
396     return _ufdt_output_property_to_fdt(
397         tree, fdtp, (const struct ufdt_node_fdt_prop *)node, dict);
398   }
399 
400   int err = fdt_begin_node(fdtp, ufdt_node_name(node));
401   if (err < 0) return -1;
402 
403   struct ufdt_node **it;
404   for_each_prop(it, node) {
405     err = _ufdt_output_node_to_fdt(tree, fdtp, *it, dict);
406     if (err < 0) return -1;
407   }
408 
409   for_each_node(it, node) {
410     err = _ufdt_output_node_to_fdt(tree, fdtp, *it, dict);
411     if (err < 0) return -1;
412   }
413 
414   err = fdt_end_node(fdtp);
415   if (err < 0) return -1;
416 
417   return 0;
418 }
419 
_ufdt_output_strtab_to_fdt(const struct ufdt * tree,void * fdt)420 static int _ufdt_output_strtab_to_fdt(const struct ufdt *tree, void *fdt) {
421   /* Currently, we don't know the final dt_struct size, so we copy all
422      string tables to the end of the target fdt buffer in reversed order.
423      At last, fdt_finish() will adjust dt_string offset */
424   const char *struct_top =
425       (char *)fdt + fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt);
426   char *dest = (char *)fdt + fdt_totalsize(fdt);
427 
428   int dest_size = 0;
429   for (int i = 0; i < tree->num_used_fdtps; i++) {
430     void *src_fdt = tree->fdtps[i];
431     const char *src_strtab = (const char *)src_fdt + fdt_off_dt_strings(src_fdt);
432     int strtab_size = fdt_size_dt_strings(src_fdt);
433 
434     if (strtab_size == 0) {
435       dto_debug("String table is empty in fdtp\n");
436       continue;
437     }
438 
439     dest -= strtab_size;
440     if (dest < struct_top) {
441       dto_error("Not enough space for string table.\n");
442       return -1;
443     }
444 
445     dto_memcpy(dest, src_strtab, strtab_size);
446 
447     dest_size += strtab_size;
448   }
449 
450   fdt_set_size_dt_strings(fdt, dest_size);
451 
452   return 0;
453 }
454 
ufdt_to_fdt(const struct ufdt * tree,void * buf,int buf_size)455 int ufdt_to_fdt(const struct ufdt *tree, void *buf, int buf_size) {
456   if (tree->num_used_fdtps == 0) return -1;
457   if (tree->root == NULL) return -1;
458 
459   int err;
460   err = fdt_create(buf, buf_size);
461   if (err < 0) return -1;
462 
463   /* Here we output the memory reserve map of the ONLY FIRST fdt,
464      to be in compliance with the DTO behavior of libfdt. */
465   int n_mem_rsv = fdt_num_mem_rsv(tree->fdtps[0]);
466   for (int i = 0; i < n_mem_rsv; i++) {
467     uint64_t addr, size;
468     fdt_get_mem_rsv(tree->fdtps[0], i, &addr, &size);
469     fdt_add_reservemap_entry(buf, addr, size);
470   }
471 
472   err = fdt_finish_reservemap(buf);
473   if (err < 0) return -1;
474 
475   err = _ufdt_output_strtab_to_fdt(tree, buf);
476   if (err < 0) return -1;
477 
478   struct ufdt_prop_dict dict;
479   err = ufdt_prop_dict_construct(&dict, buf);
480   if (err < 0) return -1;
481 
482   err = _ufdt_output_node_to_fdt(tree, buf, tree->root, &dict);
483 
484   // Ensure property_dict is freed, even on error path.
485   ufdt_prop_dict_destruct(&dict);
486 
487   if (err < 0) return -1;
488 
489   err = fdt_finish(buf);
490   if (err < 0) return -1;
491 
492   /*
493    * IMPORTANT: fdt_totalsize(buf) might be less than buf_size
494    * so this is needed to make use of remain spaces.
495    */
496   return fdt_open_into(buf, buf, buf_size);
497 }
498