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