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