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