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 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 return (struct ufdt_node *)res; 33 } else { 34 struct ufdt_node_fdt_node *res = (struct ufdt_node_fdt_node *)buf; 35 if (res == NULL) return NULL; 36 res->parent.fdt_tag_ptr = fdt_tag_ptr; 37 res->parent.sibling = NULL; 38 res->child = NULL; 39 res->last_child_p = &res->child; 40 return (struct ufdt_node *)res; 41 } 42 } 43 44 void ufdt_node_destruct(struct ufdt_node *node, struct ufdt_node_pool *pool) { 45 if (node == NULL) return; 46 47 if (ufdt_node_tag(node) == FDT_BEGIN_NODE) { 48 struct ufdt_node *it = ((struct ufdt_node_fdt_node *)node)->child; 49 while (it != NULL) { 50 struct ufdt_node *next = it->sibling; 51 ufdt_node_destruct(it, pool); 52 it = next; 53 } 54 } 55 56 ufdt_node_pool_free(pool, node); 57 } 58 59 int ufdt_node_add_child(struct ufdt_node *parent, struct ufdt_node *child) { 60 if (!parent || !child) return -1; 61 if (ufdt_node_tag(parent) != FDT_BEGIN_NODE) return -1; 62 63 int err = 0; 64 uint32_t child_tag = ufdt_node_tag(child); 65 switch (child_tag) { 66 case FDT_PROP: 67 case FDT_BEGIN_NODE: 68 // Append the child node to the last child of parant node 69 *((struct ufdt_node_fdt_node *)parent)->last_child_p = child; 70 ((struct ufdt_node_fdt_node *)parent)->last_child_p = &child->sibling; 71 break; 72 73 default: 74 err = -1; 75 dto_error("invalid children tag type\n"); 76 } 77 78 return err; 79 } 80 81 /* 82 * BEGIN of FDT_PROP related methods. 83 */ 84 85 struct ufdt_node *ufdt_node_get_subnode_by_name_len(const struct ufdt_node *node, 86 const char *name, int len) { 87 struct ufdt_node **it = NULL; 88 for_each_node(it, node) { 89 if (ufdt_node_name_eq(*it, name, len)) return *it; 90 } 91 return NULL; 92 } 93 94 struct ufdt_node *ufdt_node_get_subnode_by_name(const struct ufdt_node *node, 95 const char *name) { 96 return ufdt_node_get_subnode_by_name_len(node, name, strlen(name)); 97 } 98 99 struct ufdt_node *ufdt_node_get_property_by_name_len( 100 const struct ufdt_node *node, const char *name, int len) { 101 if (!node) return NULL; 102 103 struct ufdt_node **it = NULL; 104 for_each_prop(it, node) { 105 if (ufdt_node_name_eq(*it, name, len)) return *it; 106 } 107 return NULL; 108 } 109 110 struct ufdt_node *ufdt_node_get_property_by_name(const struct ufdt_node *node, 111 const char *name) { 112 return ufdt_node_get_property_by_name_len(node, name, dto_strlen(name)); 113 } 114 115 char *ufdt_node_get_fdt_prop_data(const struct ufdt_node *node, int *out_len) { 116 if (!node || ufdt_node_tag(node) != FDT_PROP) { 117 return NULL; 118 } 119 const struct fdt_property *prop = (struct fdt_property *)node->fdt_tag_ptr; 120 if (out_len != NULL) { 121 *out_len = fdt32_to_cpu(prop->len); 122 } 123 return (char *)prop->data; 124 } 125 126 char *ufdt_node_get_fdt_prop_data_by_name_len(const struct ufdt_node *node, 127 const char *name, int len, 128 int *out_len) { 129 return ufdt_node_get_fdt_prop_data( 130 ufdt_node_get_property_by_name_len(node, name, len), out_len); 131 } 132 133 char *ufdt_node_get_fdt_prop_data_by_name(const struct ufdt_node *node, 134 const char *name, int *out_len) { 135 return ufdt_node_get_fdt_prop_data(ufdt_node_get_property_by_name(node, name), 136 out_len); 137 } 138 139 /* 140 * END of FDT_PROP related methods. 141 */ 142 143 /* 144 * BEGIN of searching-in-ufdt_node methods. 145 */ 146 147 uint32_t ufdt_node_get_phandle(const struct ufdt_node *node) { 148 if (!node || ufdt_node_tag(node) != FDT_BEGIN_NODE) { 149 return 0; 150 } 151 int len = 0; 152 void *ptr = ufdt_node_get_fdt_prop_data_by_name(node, "phandle", &len); 153 if (!ptr || len != sizeof(fdt32_t)) { 154 ptr = ufdt_node_get_fdt_prop_data_by_name(node, "linux,phandle", &len); 155 if (!ptr || len != sizeof(fdt32_t)) { 156 return 0; 157 } 158 } 159 return fdt32_to_cpu(*((fdt32_t *)ptr)); 160 } 161 162 struct ufdt_node *ufdt_node_get_node_by_path_len(const struct ufdt_node *node, 163 const char *path, int len) { 164 const char *end = path + len; 165 166 struct ufdt_node *cur = (struct ufdt_node *)node; 167 168 while (path < end) { 169 while (path[0] == '/') path++; 170 if (path == end) return cur; 171 172 const char *next_slash; 173 next_slash = dto_memchr(path, '/', end - path); 174 if (!next_slash) next_slash = end; 175 176 struct ufdt_node *next = NULL; 177 178 next = ufdt_node_get_subnode_by_name_len(cur, path, next_slash - path); 179 180 cur = next; 181 path = next_slash; 182 if (!cur) return cur; 183 } 184 185 return cur; 186 } 187 188 struct ufdt_node *ufdt_node_get_node_by_path(const struct ufdt_node *node, 189 const char *path) { 190 return ufdt_node_get_node_by_path_len(node, path, dto_strlen(path)); 191 } 192 193 bool ufdt_node_name_eq(const struct ufdt_node *node, const char *name, int len) { 194 if (!node) return false; 195 if (!name) return false; 196 if (dto_strncmp(ufdt_node_name(node), name, len) != 0) return false; 197 if (ufdt_node_name(node)[len] != '\0') return false; 198 return true; 199 } 200 201 /* 202 * END of searching-in-ufdt_node methods. 203 */ 204 205 static int merge_children(struct ufdt_node *node_a, struct ufdt_node *node_b, 206 struct ufdt_node_pool *pool) { 207 int err = 0; 208 struct ufdt_node *it; 209 for (it = ((struct ufdt_node_fdt_node *)node_b)->child; it;) { 210 struct ufdt_node *cur_node = it; 211 it = it->sibling; 212 cur_node->sibling = NULL; 213 struct ufdt_node *target_node = NULL; 214 if (ufdt_node_tag(cur_node) == FDT_BEGIN_NODE) { 215 target_node = 216 ufdt_node_get_subnode_by_name(node_a, ufdt_node_name(cur_node)); 217 } else { 218 target_node = 219 ufdt_node_get_property_by_name(node_a, ufdt_node_name(cur_node)); 220 } 221 if (target_node == NULL) { 222 err = ufdt_node_add_child(node_a, cur_node); 223 } else { 224 err = ufdt_node_merge_into(target_node, cur_node, pool); 225 ufdt_node_pool_free(pool, cur_node); 226 } 227 if (err < 0) return -1; 228 } 229 /* 230 * The ufdt_node* in node_b will be copied to node_a. 231 * To prevent the ufdt_node from being freed twice 232 * (main_tree and overlay_tree) at the end of function 233 * ufdt_apply_overlay(), set this node in node_b 234 * (overlay_tree) to NULL. 235 */ 236 ((struct ufdt_node_fdt_node *)node_b)->child = NULL; 237 238 return 0; 239 } 240 241 int ufdt_node_merge_into(struct ufdt_node *node_a, struct ufdt_node *node_b, 242 struct ufdt_node_pool *pool) { 243 if (ufdt_node_tag(node_a) == FDT_PROP) { 244 node_a->fdt_tag_ptr = node_b->fdt_tag_ptr; 245 return 0; 246 } 247 248 int err = 0; 249 err = merge_children(node_a, node_b, pool); 250 if (err < 0) return -1; 251 252 return 0; 253 } 254 255 #define TAB_SIZE 2 256 257 void ufdt_node_print(const struct ufdt_node *node, int depth) { 258 if (!node) return; 259 260 int i; 261 for (i = 0; i < depth * TAB_SIZE; i++) dto_print(" "); 262 263 uint32_t tag; 264 tag = ufdt_node_tag(node); 265 266 switch (tag) { 267 case FDT_BEGIN_NODE: 268 dto_print("NODE "); 269 break; 270 case FDT_PROP: 271 dto_print("PROP "); 272 break; 273 default: 274 dto_print("UNKNOWN "); 275 break; 276 } 277 278 if (ufdt_node_name(node)) { 279 dto_print(":%s:\n", ufdt_node_name(node)); 280 } else { 281 dto_print("node name is NULL.\n"); 282 } 283 284 if (ufdt_node_tag(node) == FDT_BEGIN_NODE) { 285 struct ufdt_node **it; 286 287 for_each_prop(it, node) ufdt_node_print(*it, depth + 1); 288 289 for_each_node(it, node) ufdt_node_print(*it, depth + 1); 290 } 291 } 292