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 
node_cmp(const void * a,const void * b)19 int node_cmp(const void *a, const void *b) {
20   const struct ufdt_node *na = *(struct ufdt_node **)a;
21   const struct ufdt_node *nb = *(struct ufdt_node **)b;
22   return dto_strcmp(name_of(na), name_of(nb));
23 }
24 
node_name_eq(const struct ufdt_node * node,const char * name,int len)25 bool node_name_eq(const struct ufdt_node *node, const char *name, int len) {
26   if (!node) return false;
27   if (!name) return false;
28   if (dto_strncmp(name_of(node), name, len) != 0) return false;
29   if (name_of(node)[len] != '\0') return false;
30   return true;
31 }
32 
33 /*
34  * ufdt_node methods.
35  */
36 
ufdt_node_construct(void * fdtp,fdt32_t * fdt_tag_ptr)37 struct ufdt_node *ufdt_node_construct(void *fdtp, fdt32_t *fdt_tag_ptr) {
38   uint32_t tag = fdt32_to_cpu(*fdt_tag_ptr);
39   if (tag == FDT_PROP) {
40     const struct fdt_property *prop = (const struct fdt_property *)fdt_tag_ptr;
41     struct fdt_prop_ufdt_node *res = dto_malloc(sizeof(struct fdt_prop_ufdt_node));
42     if (res == NULL) return NULL;
43     res->parent.fdt_tag_ptr = fdt_tag_ptr;
44     res->parent.sibling = NULL;
45     res->name = fdt_string(fdtp, fdt32_to_cpu(prop->nameoff));
46     return (struct ufdt_node *)res;
47   } else {
48     struct fdt_node_ufdt_node *res = dto_malloc(sizeof(struct fdt_node_ufdt_node));
49     if (res == NULL) return NULL;
50     res->parent.fdt_tag_ptr = fdt_tag_ptr;
51     res->parent.sibling = NULL;
52     res->child = NULL;
53     res->last_child_p = &res->child;
54     return (struct ufdt_node *)res;
55   }
56 }
57 
ufdt_node_destruct(struct ufdt_node * node)58 void ufdt_node_destruct(struct ufdt_node *node) {
59   if (node == NULL) return;
60 
61   if (tag_of(node) == FDT_BEGIN_NODE) {
62     ufdt_node_destruct(((struct fdt_node_ufdt_node *)node)->child);
63   }
64 
65   ufdt_node_destruct(node->sibling);
66   dto_free(node);
67 
68   return;
69 }
70 
ufdt_node_add_child(struct ufdt_node * parent,struct ufdt_node * child)71 int ufdt_node_add_child(struct ufdt_node *parent, struct ufdt_node *child) {
72   if (!parent || !child) return -1;
73   if (tag_of(parent) != FDT_BEGIN_NODE) return -1;
74 
75   int err = 0;
76   uint32_t child_tag = tag_of(child);
77 
78   switch (child_tag) {
79     case FDT_PROP:
80     case FDT_BEGIN_NODE:
81       // Append the child node to the last child of parant node
82       *((struct fdt_node_ufdt_node *)parent)->last_child_p = child;
83       ((struct fdt_node_ufdt_node *)parent)->last_child_p = &child->sibling;
84       break;
85 
86     default:
87       err = -1;
88       dto_error("invalid children tag type\n");
89   }
90 
91   return err;
92 }
93 
94 /*
95  * BEGIN of FDT_PROP related methods.
96  */
97 
ufdt_node_get_subnode_by_name_len(const struct ufdt_node * node,const char * name,int len)98 struct ufdt_node *ufdt_node_get_subnode_by_name_len(const struct ufdt_node *node,
99                                                   const char *name, int len) {
100   struct ufdt_node **it = NULL;
101   for_each_node(it, node) {
102     if (node_name_eq(*it, name, len)) return *it;
103   }
104   return NULL;
105 }
106 
ufdt_node_get_subnode_by_name(const struct ufdt_node * node,const char * name)107 struct ufdt_node *ufdt_node_get_subnode_by_name(const struct ufdt_node *node,
108                                               const char *name) {
109   return ufdt_node_get_subnode_by_name_len(node, name, strlen(name));
110 }
111 
ufdt_node_get_property_by_name_len(const struct ufdt_node * node,const char * name,int len)112 struct ufdt_node *ufdt_node_get_property_by_name_len(
113     const struct ufdt_node *node, const char *name, int len) {
114   if (!node) return NULL;
115 
116   struct ufdt_node **it = NULL;
117   for_each_prop(it, node) {
118     if (node_name_eq(*it, name, len)) return *it;
119   }
120   return NULL;
121 }
122 
ufdt_node_get_property_by_name(const struct ufdt_node * node,const char * name)123 struct ufdt_node *ufdt_node_get_property_by_name(const struct ufdt_node *node,
124                                                  const char *name) {
125   return ufdt_node_get_property_by_name_len(node, name, dto_strlen(name));
126 }
127 
ufdt_node_get_fdt_prop_data(const struct ufdt_node * node,int * out_len)128 char *ufdt_node_get_fdt_prop_data(const struct ufdt_node *node, int *out_len) {
129   if (!node || tag_of(node) != FDT_PROP) {
130     return NULL;
131   }
132   const struct fdt_property *prop = (struct fdt_property *)node->fdt_tag_ptr;
133   if (out_len != NULL) {
134     *out_len = fdt32_to_cpu(prop->len);
135   }
136   return (char *)prop->data;
137 }
138 
ufdt_node_get_fdt_prop_data_by_name_len(const struct ufdt_node * node,const char * name,int len,int * out_len)139 char *ufdt_node_get_fdt_prop_data_by_name_len(const struct ufdt_node *node,
140                                               const char *name, int len,
141                                               int *out_len) {
142   return ufdt_node_get_fdt_prop_data(
143       ufdt_node_get_property_by_name_len(node, name, len), out_len);
144 }
145 
ufdt_node_get_fdt_prop_data_by_name(const struct ufdt_node * node,const char * name,int * out_len)146 char *ufdt_node_get_fdt_prop_data_by_name(const struct ufdt_node *node,
147                                           const char *name, int *out_len) {
148   return ufdt_node_get_fdt_prop_data(ufdt_node_get_property_by_name(node, name),
149                                      out_len);
150 }
151 
152 /*
153  * END of FDT_PROP related methods.
154  */
155 
156 /*
157  * BEGIN of searching-in-ufdt_node methods.
158  */
159 
ufdt_node_get_phandle(const struct ufdt_node * node)160 uint32_t ufdt_node_get_phandle(const struct ufdt_node *node) {
161   if (!node || tag_of(node) != FDT_BEGIN_NODE) {
162     return 0;
163   }
164   int len = 0;
165   void *ptr = ufdt_node_get_fdt_prop_data_by_name(node, "phandle", &len);
166   if (!ptr || len != sizeof(fdt32_t)) {
167     ptr = ufdt_node_get_fdt_prop_data_by_name(node, "linux,phandle", &len);
168     if (!ptr || len != sizeof(fdt32_t)) {
169       return 0;
170     }
171   }
172   return fdt32_to_cpu(*((fdt32_t *)ptr));
173 }
174 
ufdt_node_get_node_by_path_len(const struct ufdt_node * node,const char * path,int len)175 struct ufdt_node *ufdt_node_get_node_by_path_len(const struct ufdt_node *node,
176                                                  const char *path, int len) {
177   const char *end = path + len;
178 
179   struct ufdt_node *cur = (struct ufdt_node *)node;
180 
181   while (path < end) {
182     while (path[0] == '/') path++;
183     if (path == end) return cur;
184 
185     const char *next_slash;
186     next_slash = dto_memchr(path, '/', end - path);
187     if (!next_slash) next_slash = end;
188 
189     struct ufdt_node *next = NULL;
190 
191     next = ufdt_node_get_subnode_by_name_len(cur, path, next_slash - path);
192 
193     cur = next;
194     path = next_slash;
195     if (!cur) return cur;
196   }
197 
198   return cur;
199 }
200 
ufdt_node_get_node_by_path(const struct ufdt_node * node,const char * path)201 struct ufdt_node *ufdt_node_get_node_by_path(const struct ufdt_node *node,
202                                              const char *path) {
203   return ufdt_node_get_node_by_path_len(node, path, dto_strlen(path));
204 }
205 
206 /*
207  * END of searching-in-ufdt_node methods.
208  */
209 
210 #define TAB_SIZE 2
211 
ufdt_node_print(const struct ufdt_node * node,int depth)212 void ufdt_node_print(const struct ufdt_node *node, int depth) {
213   if (!node) return;
214 
215   int i;
216   for (i = 0; i < depth * TAB_SIZE; i++) dto_print(" ");
217 
218   uint32_t tag;
219   tag = tag_of(node);
220 
221   switch (tag) {
222     case FDT_BEGIN_NODE:
223       dto_print("NODE ");
224       break;
225     case FDT_PROP:
226       dto_print("PROP ");
227       break;
228     default:
229       dto_print("UNKNOWN ");
230       break;
231   }
232 
233   if (name_of(node)) {
234     dto_print(":%s:\n", name_of(node));
235   } else {
236     dto_print("node name is NULL.\n");
237   }
238 
239   if (tag_of(node) == FDT_BEGIN_NODE) {
240     struct ufdt_node **it;
241 
242     for_each_prop(it, node) ufdt_node_print(*it, depth + 1);
243 
244     for_each_node(it, node) ufdt_node_print(*it, depth + 1);
245   }
246 
247   return;
248 }
249 
ufdt_node_map(struct ufdt_node * node,struct ufdt_node_closure closure)250 void ufdt_node_map(struct ufdt_node *node, struct ufdt_node_closure closure) {
251   if (node == NULL) return;
252   closure.func(node, closure.env);
253   if (tag_of(node) == FDT_BEGIN_NODE) {
254     struct ufdt_node **it;
255     for_each_prop(it, node) ufdt_node_map(*it, closure);
256     for_each_node(it, node) ufdt_node_map(*it, closure);
257   }
258   return;
259 }
260