• Home
  • History
  • Annotate
  • 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    res.data = dto_malloc(sizeof(struct ufdt_phandle_table_entry) * res.len);
295    int cur = 0;
296    set_phandle_table_entry(tree->root, res.data, &cur);
297    dto_qsort(res.data, res.len, sizeof(struct ufdt_phandle_table_entry),
298              phandle_table_entry_cmp);
299    return res;
300  }
301  
ufdt_from_fdt(void * fdtp,size_t fdt_size,struct ufdt_node_pool * pool)302  struct ufdt *ufdt_from_fdt(void *fdtp, size_t fdt_size,
303                             struct ufdt_node_pool *pool) {
304    (void)(fdt_size); /* unused parameter */
305  
306    int start_offset = fdt_path_offset(fdtp, "/");
307    if (start_offset < 0) {
308      return ufdt_construct(NULL, pool);
309    }
310  
311    int end_offset;
312    int start_tag = fdt_next_tag(fdtp, start_offset, &end_offset);
313  
314    if (start_tag != FDT_BEGIN_NODE) {
315      return ufdt_construct(NULL, pool);
316    }
317  
318    struct ufdt *res_tree = ufdt_construct(fdtp, pool);
319    if (res_tree == NULL) return NULL;
320  
321    res_tree->root =
322        fdt_to_ufdt_tree(fdtp, start_offset, &end_offset, start_tag, pool);
323  
324    res_tree->phandle_table = build_phandle_table(res_tree);
325  
326    return res_tree;
327  }
328  
_ufdt_get_property_nameoff(const struct ufdt * tree,const char * name,const struct ufdt_prop_dict * dict)329  static int _ufdt_get_property_nameoff(const struct ufdt *tree, const char *name,
330                                        const struct ufdt_prop_dict *dict) {
331    int res;
332    const struct fdt_property *same_name_prop = ufdt_prop_dict_find(dict, name);
333    if (same_name_prop != NULL) {
334      /* There is a property with same name, just use its string offset */
335      res = fdt32_to_cpu(same_name_prop->nameoff);
336    } else {
337      /* Get the string offset from the string table of the current tree */
338      res = ufdt_get_string_off(tree, name);
339      if (res == 0) {
340        dto_error("Cannot find property name in string table: %s\n", name);
341        return 0;
342      }
343    }
344    return res;
345  }
346  
_ufdt_output_property_to_fdt(const struct ufdt * tree,void * fdtp,const struct ufdt_node_fdt_prop * prop_node,struct ufdt_prop_dict * dict)347  static int _ufdt_output_property_to_fdt(
348      const struct ufdt *tree, void *fdtp,
349      const struct ufdt_node_fdt_prop *prop_node, struct ufdt_prop_dict *dict) {
350    int nameoff = _ufdt_get_property_nameoff(tree, prop_node->name, dict);
351    if (nameoff == 0) return -1;
352  
353    int data_len = 0;
354    void *data = ufdt_node_get_fdt_prop_data(&prop_node->parent, &data_len);
355    if (!data) {
356      dto_error("Failed to get property data.\n");
357      return -1;
358    }
359  
360    unsigned int aligned_data_len =
361        ((unsigned int)data_len + (FDT_TAGSIZE - 1u)) & ~(FDT_TAGSIZE - 1u);
362  
363    unsigned int new_propoff = fdt_size_dt_struct(fdtp);
364    unsigned int new_prop_size = sizeof(struct fdt_property) + aligned_data_len;
365    struct fdt_property *new_prop =
366        (struct fdt_property *)((char *)fdtp + fdt_off_dt_struct(fdtp) +
367                                new_propoff);
368    char *fdt_end = (char *)fdtp + fdt_totalsize(fdtp);
369    if ((char *)new_prop + new_prop_size > fdt_end) {
370      dto_error("Not enough space for adding property.\n");
371      return -1;
372    }
373    fdt_set_size_dt_struct(fdtp, new_propoff + new_prop_size);
374  
375    new_prop->tag = cpu_to_fdt32(FDT_PROP);
376    new_prop->nameoff = cpu_to_fdt32(nameoff);
377    new_prop->len = cpu_to_fdt32(data_len);
378    dto_memcpy(new_prop->data, data, data_len);
379  
380    ufdt_prop_dict_add(dict, new_prop);
381  
382    return 0;
383  }
384  
_ufdt_output_node_to_fdt(const struct ufdt * tree,void * fdtp,const struct ufdt_node * node,struct ufdt_prop_dict * dict)385  static int _ufdt_output_node_to_fdt(const struct ufdt *tree, void *fdtp,
386                                      const struct ufdt_node *node,
387                                      struct ufdt_prop_dict *dict) {
388    uint32_t tag = ufdt_node_tag(node);
389  
390    if (tag == FDT_PROP) {
391      return _ufdt_output_property_to_fdt(
392          tree, fdtp, (const struct ufdt_node_fdt_prop *)node, dict);
393    }
394  
395    int err = fdt_begin_node(fdtp, ufdt_node_name(node));
396    if (err < 0) return -1;
397  
398    struct ufdt_node **it;
399    for_each_prop(it, node) {
400      err = _ufdt_output_node_to_fdt(tree, fdtp, *it, dict);
401      if (err < 0) return -1;
402    }
403  
404    for_each_node(it, node) {
405      err = _ufdt_output_node_to_fdt(tree, fdtp, *it, dict);
406      if (err < 0) return -1;
407    }
408  
409    err = fdt_end_node(fdtp);
410    if (err < 0) return -1;
411  
412    return 0;
413  }
414  
_ufdt_output_strtab_to_fdt(const struct ufdt * tree,void * fdt)415  static int _ufdt_output_strtab_to_fdt(const struct ufdt *tree, void *fdt) {
416    /* Currently, we don't know the final dt_struct size, so we copy all
417       string tables to the end of the target fdt buffer in reversed order.
418       At last, fdt_finish() will adjust dt_string offset */
419    const char *struct_top =
420        (char *)fdt + fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt);
421    char *dest = (char *)fdt + fdt_totalsize(fdt);
422  
423    int dest_size = 0;
424    for (int i = 0; i < tree->num_used_fdtps; i++) {
425      void *src_fdt = tree->fdtps[i];
426      const char *src_strtab = (const char *)src_fdt + fdt_off_dt_strings(src_fdt);
427      int strtab_size = fdt_size_dt_strings(src_fdt);
428  
429      dest -= strtab_size;
430      if (dest < struct_top) {
431        dto_error("Not enough space for string table.\n");
432        return -1;
433      }
434  
435      dto_memcpy(dest, src_strtab, strtab_size);
436  
437      dest_size += strtab_size;
438    }
439  
440    fdt_set_size_dt_strings(fdt, dest_size);
441  
442    return 0;
443  }
444  
ufdt_to_fdt(const struct ufdt * tree,void * buf,int buf_size)445  int ufdt_to_fdt(const struct ufdt *tree, void *buf, int buf_size) {
446    if (tree->num_used_fdtps == 0) return -1;
447    if (tree->root == NULL) return -1;
448  
449    int err;
450    err = fdt_create(buf, buf_size);
451    if (err < 0) return -1;
452  
453    /* Here we output the memory reserve map of the ONLY FIRST fdt,
454       to be in compliance with the DTO behavior of libfdt. */
455    int n_mem_rsv = fdt_num_mem_rsv(tree->fdtps[0]);
456    for (int i = 0; i < n_mem_rsv; i++) {
457      uint64_t addr, size;
458      fdt_get_mem_rsv(tree->fdtps[0], i, &addr, &size);
459      fdt_add_reservemap_entry(buf, addr, size);
460    }
461  
462    err = fdt_finish_reservemap(buf);
463    if (err < 0) return -1;
464  
465    err = _ufdt_output_strtab_to_fdt(tree, buf);
466    if (err < 0) return -1;
467  
468    struct ufdt_prop_dict dict;
469    err = ufdt_prop_dict_construct(&dict, buf);
470    if (err < 0) return -1;
471  
472    err = _ufdt_output_node_to_fdt(tree, buf, tree->root, &dict);
473  
474    // Ensure property_dict is freed, even on error path.
475    ufdt_prop_dict_destruct(&dict);
476  
477    if (err < 0) return -1;
478  
479    err = fdt_finish(buf);
480    if (err < 0) return -1;
481  
482    /*
483     * IMPORTANT: fdt_totalsize(buf) might be less than buf_size
484     * so this is needed to make use of remain spaces.
485     */
486    return fdt_open_into(buf, buf, buf_size);
487  }
488