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