]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - lib/libfdt/fdt_region.c
karo: tx6ul: fix bogus GPIO numbers
[karo-tx-uboot.git] / lib / libfdt / fdt_region.c
1 /*
2  * libfdt - Flat Device Tree manipulation
3  * Copyright (C) 2013 Google, Inc
4  * Written by Simon Glass <sjg@chromium.org>
5  * SPDX-License-Identifier:     GPL-2.0+ BSD-2-Clause
6  */
7
8 #include "libfdt_env.h"
9
10 #ifndef USE_HOSTCC
11 #include <fdt.h>
12 #include <libfdt.h>
13 #else
14 #include "fdt_host.h"
15 #endif
16
17 #include "libfdt_internal.h"
18
19 /**
20  * fdt_add_region() - Add a new region to our list
21  *
22  * The region is added if there is space, but in any case we increment the
23  * count. If permitted, and the new region overlaps the last one, we merge
24  * them.
25  *
26  * @info: State information
27  * @offset: Start offset of region
28  * @size: Size of region
29  */
30 static int fdt_add_region(struct fdt_region_state *info, int offset, int size)
31 {
32         struct fdt_region *reg;
33
34         reg = info->region ? &info->region[info->count - 1] : NULL;
35         if (info->can_merge && info->count &&
36             info->count <= info->max_regions &&
37             reg && offset <= reg->offset + reg->size) {
38                 reg->size = offset + size - reg->offset;
39         } else if (info->count++ < info->max_regions) {
40                 if (reg) {
41                         reg++;
42                         reg->offset = offset;
43                         reg->size = size;
44                 }
45         } else {
46                 return -1;
47         }
48
49         return 0;
50 }
51
52 static int region_list_contains_offset(struct fdt_region_state *info,
53                                        const void *fdt, int target)
54 {
55         struct fdt_region *reg;
56         int num;
57
58         target += fdt_off_dt_struct(fdt);
59         for (reg = info->region, num = 0; num < info->count; reg++, num++) {
60                 if (target >= reg->offset && target < reg->offset + reg->size)
61                         return 1;
62         }
63
64         return 0;
65 }
66
67 int fdt_add_alias_regions(const void *fdt, struct fdt_region *region, int count,
68                           int max_regions, struct fdt_region_state *info)
69 {
70         int base = fdt_off_dt_struct(fdt);
71         int node, node_end, offset;
72         int did_alias_header;
73
74         node = fdt_subnode_offset(fdt, 0, "aliases");
75         if (node < 0)
76                 return -FDT_ERR_NOTFOUND;
77
78         /* The aliases node must come before the others */
79         node_end = fdt_next_subnode(fdt, node);
80         if (node_end <= 0)
81                 return -FDT_ERR_BADLAYOUT;
82         node_end -= sizeof(fdt32_t);
83
84         did_alias_header = 0;
85         info->region = region;
86         info->count = count;
87         info->can_merge = 0;
88         info->max_regions = max_regions;
89
90         for (offset = fdt_first_property_offset(fdt, node);
91              offset >= 0;
92              offset = fdt_next_property_offset(fdt, offset)) {
93                 const struct fdt_property *prop;
94                 const char *name;
95                 int target, next;
96
97                 prop = fdt_get_property_by_offset(fdt, offset, NULL);
98                 name = fdt_string(fdt, fdt32_to_cpu(prop->nameoff));
99                 target = fdt_path_offset(fdt, name);
100                 if (!region_list_contains_offset(info, fdt, target))
101                         continue;
102                 next = fdt_next_property_offset(fdt, offset);
103                 if (next < 0)
104                         next = node_end - sizeof(fdt32_t);
105
106                 if (!did_alias_header) {
107                         fdt_add_region(info, base + node, 12);
108                         did_alias_header = 1;
109                 }
110                 fdt_add_region(info, base + offset, next - offset);
111         }
112
113         /* Add the 'end' tag */
114         if (did_alias_header)
115                 fdt_add_region(info, base + node_end, sizeof(fdt32_t));
116
117         return info->count < max_regions ? info->count : -FDT_ERR_NOSPACE;
118 }
119
120 /**
121  * fdt_include_supernodes() - Include supernodes required by this node
122  *
123  * When we decided to include a node or property which is not at the top
124  * level, this function forces the inclusion of higher level nodes. For
125  * example, given this tree:
126  *
127  * / {
128  *     testing {
129  *     }
130  * }
131  *
132  * If we decide to include testing then we need the root node to have a valid
133  * tree. This function adds those regions.
134  *
135  * @info: State information
136  * @depth: Current stack depth
137  */
138 static int fdt_include_supernodes(struct fdt_region_state *info, int depth)
139 {
140         int base = fdt_off_dt_struct(info->fdt);
141         int start, stop_at;
142         int i;
143
144         /*
145          * Work down the stack looking for supernodes that we didn't include.
146          * The algortihm here is actually pretty simple, since we know that
147          * no previous subnode had to include these nodes, or if it did, we
148          * marked them as included (on the stack) already.
149          */
150         for (i = 0; i <= depth; i++) {
151                 if (!info->stack[i].included) {
152                         start = info->stack[i].offset;
153
154                         /* Add the FDT_BEGIN_NODE tag of this supernode */
155                         fdt_next_tag(info->fdt, start, &stop_at);
156                         if (fdt_add_region(info, base + start, stop_at - start))
157                                 return -1;
158
159                         /* Remember that this supernode is now included */
160                         info->stack[i].included = 1;
161                         info->can_merge = 1;
162                 }
163
164                 /* Force (later) generation of the FDT_END_NODE tag */
165                 if (!info->stack[i].want)
166                         info->stack[i].want = WANT_NODES_ONLY;
167         }
168
169         return 0;
170 }
171
172 enum {
173         FDT_DONE_NOTHING,
174         FDT_DONE_MEM_RSVMAP,
175         FDT_DONE_STRUCT,
176         FDT_DONE_END,
177         FDT_DONE_STRINGS,
178         FDT_DONE_ALL,
179 };
180
181 int fdt_first_region(const void *fdt,
182                 int (*h_include)(void *priv, const void *fdt, int offset,
183                                  int type, const char *data, int size),
184                 void *priv, struct fdt_region *region,
185                 char *path, int path_len, int flags,
186                 struct fdt_region_state *info)
187 {
188         struct fdt_region_ptrs *p = &info->ptrs;
189
190         /* Set up our state */
191         info->fdt = fdt;
192         info->can_merge = 1;
193         info->max_regions = 1;
194         info->start = -1;
195         p->want = WANT_NOTHING;
196         p->end = path;
197         *p->end = '\0';
198         p->nextoffset = 0;
199         p->depth = -1;
200         p->done = FDT_DONE_NOTHING;
201
202         return fdt_next_region(fdt, h_include, priv, region,
203                                path, path_len, flags, info);
204 }
205
206 /*
207  * Theory of operation
208  *
209  *
210  *
211
212 Note: in this description 'included' means that a node (or other part of
213 the tree) should be included in the region list, i.e. it will have a region
214 which covers its part of the tree.
215
216 This function maintains some state from the last time it is called. It
217 checks the next part of the tree that it is supposed to look at
218 (p.nextoffset) to see if that should be included or not. When it finds
219 something to include, it sets info->start to its offset. This marks the
220 start of the region we want to include.
221
222 Once info->start is set to the start (i.e. not -1), we continue scanning
223 until we find something that we don't want included. This will be the end
224 of a region. At this point we can close off the region and add it to the
225 list. So we do so, and reset info->start to -1.
226
227 One complication here is that we want to merge regions. So when we come to
228 add another region later, we may in fact merge it with the previous one if
229 one ends where the other starts.
230
231 The function fdt_add_region() will return -1 if it fails to add the region,
232 because we already have a region ready to be returned, and the new one
233 cannot be merged in with it. In this case, we must return the region we
234 found, and wait for another call to this function. When it comes, we will
235 repeat the processing of the tag and again try to add a region. This time it
236 will succeed.
237
238 The current state of the pointers (stack, offset, etc.) is maintained in
239 a ptrs member. At the start of every loop iteration we make a copy of it.
240 The copy is then updated as the tag is processed. Only if we get to the end
241 of the loop iteration (and successfully call fdt_add_region() if we need
242 to) can we commit the changes we have made to these pointers. For example,
243 if we see an FDT_END_NODE tag we will decrement the depth value. But if we
244 need to add a region for this tag (let's say because the previous tag is
245 included and this FDT_END_NODE tag is not included) then we will only commit
246 the result if we were able to add the region. That allows us to retry again
247 next time.
248
249 We keep track of a variable called 'want' which tells us what we want to
250 include when there is no specific information provided by the h_include
251 function for a particular property. This basically handles the inclusion of
252 properties which are pulled in by virtue of the node they are in. So if you
253 include a node, its properties are also included. In this case 'want' will
254 be WANT_NODES_AND_PROPS. The FDT_REG_DIRECT_SUBNODES feature also makes use
255 of 'want'. While we are inside the subnode, 'want' will be set to
256 WANT_NODES_ONLY, so that only the subnode's FDT_BEGIN_NODE and FDT_END_NODE
257 tags will be included, and properties will be skipped. If WANT_NOTHING is
258 selected, then we will just rely on what the h_include() function tells us.
259
260 Using 'want' we work out 'include', which tells us whether this current tag
261 should be included or not. As you can imagine, if the value of 'include'
262 changes, that means we are on a boundary between nodes to include and nodes
263 to exclude. At this point we either close off a previous region and add it
264 to the list, or mark the start of a new region.
265
266 Apart from the nodes, we have mem_rsvmap, the FDT_END tag and the string
267 list. Each of these dealt with as a whole (i.e. we create a region for each
268 if it is to be included). For mem_rsvmap we don't allow it to merge with
269 the first struct region. For the stringlist we don't allow it to merge with
270 the last struct region (which contains at minimum the FDT_END tag).
271 */
272 int fdt_next_region(const void *fdt,
273                 int (*h_include)(void *priv, const void *fdt, int offset,
274                                  int type, const char *data, int size),
275                 void *priv, struct fdt_region *region,
276                 char *path, int path_len, int flags,
277                 struct fdt_region_state *info)
278 {
279         int base = fdt_off_dt_struct(fdt);
280         int last_node = 0;
281         const char *str;
282
283         info->region = region;
284         info->count = 0;
285         if (info->ptrs.done < FDT_DONE_MEM_RSVMAP &&
286             (flags & FDT_REG_ADD_MEM_RSVMAP)) {
287                 /* Add the memory reserve map into its own region */
288                 if (fdt_add_region(info, fdt_off_mem_rsvmap(fdt),
289                                    fdt_off_dt_struct(fdt) -
290                                    fdt_off_mem_rsvmap(fdt)))
291                         return 0;
292                 info->can_merge = 0;    /* Don't allow merging with this */
293                 info->ptrs.done = FDT_DONE_MEM_RSVMAP;
294         }
295
296         /*
297          * Work through the tags one by one, deciding whether each needs to
298          * be included or not. We set the variable 'include' to indicate our
299          * decision. 'want' is used to track what we want to include - it
300          * allows us to pick up all the properties (and/or subnode tags) of
301          * a node.
302          */
303         while (info->ptrs.done < FDT_DONE_STRUCT) {
304                 const struct fdt_property *prop;
305                 struct fdt_region_ptrs p;
306                 const char *name;
307                 int include = 0;
308                 int stop_at = 0;
309                 uint32_t tag;
310                 int offset;
311                 int val;
312                 int len;
313
314                 /*
315                  * Make a copy of our pointers. If we make it to the end of
316                  * this block then we will commit them back to info->ptrs.
317                  * Otherwise we can try again from the same starting state
318                  * next time we are called.
319                  */
320                 p = info->ptrs;
321
322                 /*
323                  * Find the tag, and the offset of the next one. If we need to
324                  * stop including tags, then by default we stop *after*
325                  * including the current tag
326                  */
327                 offset = p.nextoffset;
328                 tag = fdt_next_tag(fdt, offset, &p.nextoffset);
329                 stop_at = p.nextoffset;
330
331                 switch (tag) {
332                 case FDT_PROP:
333                         stop_at = offset;
334                         prop = fdt_get_property_by_offset(fdt, offset, NULL);
335                         str = fdt_string(fdt, fdt32_to_cpu(prop->nameoff));
336                         val = h_include(priv, fdt, last_node, FDT_IS_PROP, str,
337                                             strlen(str) + 1);
338                         if (val == -1) {
339                                 include = p.want >= WANT_NODES_AND_PROPS;
340                         } else {
341                                 include = val;
342                                 /*
343                                  * Make sure we include the } for this block.
344                                  * It might be more correct to have this done
345                                  * by the call to fdt_include_supernodes() in
346                                  * the case where it adds the node we are
347                                  * currently in, but this is equivalent.
348                                  */
349                                 if ((flags & FDT_REG_SUPERNODES) && val &&
350                                     !p.want)
351                                         p.want = WANT_NODES_ONLY;
352                         }
353
354                         /* Value grepping is not yet supported */
355                         break;
356
357                 case FDT_NOP:
358                         include = p.want >= WANT_NODES_AND_PROPS;
359                         stop_at = offset;
360                         break;
361
362                 case FDT_BEGIN_NODE:
363                         last_node = offset;
364                         p.depth++;
365                         if (p.depth == FDT_MAX_DEPTH)
366                                 return -FDT_ERR_TOODEEP;
367                         name = fdt_get_name(fdt, offset, &len);
368                         if (p.end - path + 2 + len >= path_len)
369                                 return -FDT_ERR_NOSPACE;
370
371                         /* Build the full path of this node */
372                         if (p.end != path + 1)
373                                 *p.end++ = '/';
374                         strcpy(p.end, name);
375                         p.end += len;
376                         info->stack[p.depth].want = p.want;
377                         info->stack[p.depth].offset = offset;
378
379                         /*
380                          * If we are not intending to include this node unless
381                          * it matches, make sure we stop *before* its tag.
382                          */
383                         if (p.want == WANT_NODES_ONLY ||
384                             !(flags & (FDT_REG_DIRECT_SUBNODES |
385                                        FDT_REG_ALL_SUBNODES))) {
386                                 stop_at = offset;
387                                 p.want = WANT_NOTHING;
388                         }
389                         val = h_include(priv, fdt, offset, FDT_IS_NODE, path,
390                                         p.end - path + 1);
391
392                         /* Include this if requested */
393                         if (val) {
394                                 p.want = (flags & FDT_REG_ALL_SUBNODES) ?
395                                         WANT_ALL_NODES_AND_PROPS :
396                                         WANT_NODES_AND_PROPS;
397                         }
398
399                         /* If not requested, decay our 'p.want' value */
400                         else if (p.want) {
401                                 if (p.want != WANT_ALL_NODES_AND_PROPS)
402                                         p.want--;
403
404                         /* Not including this tag, so stop now */
405                         } else {
406                                 stop_at = offset;
407                         }
408
409                         /*
410                          * Decide whether to include this tag, and update our
411                          * stack with the state for this node
412                          */
413                         include = p.want;
414                         info->stack[p.depth].included = include;
415                         break;
416
417                 case FDT_END_NODE:
418                         include = p.want;
419                         if (p.depth < 0)
420                                 return -FDT_ERR_BADSTRUCTURE;
421
422                         /*
423                          * If we don't want this node, stop right away, unless
424                          * we are including subnodes
425                          */
426                         if (!p.want && !(flags & FDT_REG_DIRECT_SUBNODES))
427                                 stop_at = offset;
428                         p.want = info->stack[p.depth].want;
429                         p.depth--;
430                         while (p.end > path && *--p.end != '/')
431                                 ;
432                         *p.end = '\0';
433                         break;
434
435                 case FDT_END:
436                         /* We always include the end tag */
437                         include = 1;
438                         p.done = FDT_DONE_STRUCT;
439                         break;
440                 }
441
442                 /* If this tag is to be included, mark it as region start */
443                 if (include && info->start == -1) {
444                         /* Include any supernodes required by this one */
445                         if (flags & FDT_REG_SUPERNODES) {
446                                 if (fdt_include_supernodes(info, p.depth))
447                                         return 0;
448                         }
449                         info->start = offset;
450                 }
451
452                 /*
453                  * If this tag is not to be included, finish up the current
454                  * region.
455                  */
456                 if (!include && info->start != -1) {
457                         if (fdt_add_region(info, base + info->start,
458                                            stop_at - info->start))
459                                 return 0;
460                         info->start = -1;
461                         info->can_merge = 1;
462                 }
463
464                 /* If we have made it this far, we can commit our pointers */
465                 info->ptrs = p;
466         }
467
468         /* Add a region for the END tag and a separate one for string table */
469         if (info->ptrs.done < FDT_DONE_END) {
470                 if (info->ptrs.nextoffset != fdt_size_dt_struct(fdt))
471                         return -FDT_ERR_BADSTRUCTURE;
472
473                 if (fdt_add_region(info, base + info->start,
474                                    info->ptrs.nextoffset - info->start))
475                         return 0;
476                 info->ptrs.done++;
477         }
478         if (info->ptrs.done < FDT_DONE_STRINGS) {
479                 if (flags & FDT_REG_ADD_STRING_TAB) {
480                         info->can_merge = 0;
481                         if (fdt_off_dt_strings(fdt) <
482                             base + info->ptrs.nextoffset)
483                                 return -FDT_ERR_BADLAYOUT;
484                         if (fdt_add_region(info, fdt_off_dt_strings(fdt),
485                                            fdt_size_dt_strings(fdt)))
486                                 return 0;
487                 }
488                 info->ptrs.done++;
489         }
490
491         return info->count > 0 ? 0 : -FDT_ERR_NOTFOUND;
492 }