linux/scripts/dtc/libfdt/fdt.c
<<
>>
Prefs
   1// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
   2/*
   3 * libfdt - Flat Device Tree manipulation
   4 * Copyright (C) 2006 David Gibson, IBM Corporation.
   5 */
   6#include "libfdt_env.h"
   7
   8#include <fdt.h>
   9#include <libfdt.h>
  10
  11#include "libfdt_internal.h"
  12
  13/*
  14 * Minimal sanity check for a read-only tree. fdt_ro_probe_() checks
  15 * that the given buffer contains what appears to be a flattened
  16 * device tree with sane information in its header.
  17 */
  18int32_t fdt_ro_probe_(const void *fdt)
  19{
  20        uint32_t totalsize = fdt_totalsize(fdt);
  21
  22        if (can_assume(VALID_DTB))
  23                return totalsize;
  24
  25        /* The device tree must be at an 8-byte aligned address */
  26        if ((uintptr_t)fdt & 7)
  27                return -FDT_ERR_ALIGNMENT;
  28
  29        if (fdt_magic(fdt) == FDT_MAGIC) {
  30                /* Complete tree */
  31                if (!can_assume(LATEST)) {
  32                        if (fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION)
  33                                return -FDT_ERR_BADVERSION;
  34                        if (fdt_last_comp_version(fdt) >
  35                                        FDT_LAST_SUPPORTED_VERSION)
  36                                return -FDT_ERR_BADVERSION;
  37                }
  38        } else if (fdt_magic(fdt) == FDT_SW_MAGIC) {
  39                /* Unfinished sequential-write blob */
  40                if (!can_assume(VALID_INPUT) && fdt_size_dt_struct(fdt) == 0)
  41                        return -FDT_ERR_BADSTATE;
  42        } else {
  43                return -FDT_ERR_BADMAGIC;
  44        }
  45
  46        if (totalsize < INT32_MAX)
  47                return totalsize;
  48        else
  49                return -FDT_ERR_TRUNCATED;
  50}
  51
  52static int check_off_(uint32_t hdrsize, uint32_t totalsize, uint32_t off)
  53{
  54        return (off >= hdrsize) && (off <= totalsize);
  55}
  56
  57static int check_block_(uint32_t hdrsize, uint32_t totalsize,
  58                        uint32_t base, uint32_t size)
  59{
  60        if (!check_off_(hdrsize, totalsize, base))
  61                return 0; /* block start out of bounds */
  62        if ((base + size) < base)
  63                return 0; /* overflow */
  64        if (!check_off_(hdrsize, totalsize, base + size))
  65                return 0; /* block end out of bounds */
  66        return 1;
  67}
  68
  69size_t fdt_header_size_(uint32_t version)
  70{
  71        if (version <= 1)
  72                return FDT_V1_SIZE;
  73        else if (version <= 2)
  74                return FDT_V2_SIZE;
  75        else if (version <= 3)
  76                return FDT_V3_SIZE;
  77        else if (version <= 16)
  78                return FDT_V16_SIZE;
  79        else
  80                return FDT_V17_SIZE;
  81}
  82
  83size_t fdt_header_size(const void *fdt)
  84{
  85        return can_assume(LATEST) ? FDT_V17_SIZE :
  86                fdt_header_size_(fdt_version(fdt));
  87}
  88
  89int fdt_check_header(const void *fdt)
  90{
  91        size_t hdrsize;
  92
  93        if (fdt_magic(fdt) != FDT_MAGIC)
  94                return -FDT_ERR_BADMAGIC;
  95        if (!can_assume(LATEST)) {
  96                if ((fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION)
  97                    || (fdt_last_comp_version(fdt) >
  98                        FDT_LAST_SUPPORTED_VERSION))
  99                        return -FDT_ERR_BADVERSION;
 100                if (fdt_version(fdt) < fdt_last_comp_version(fdt))
 101                        return -FDT_ERR_BADVERSION;
 102        }
 103        hdrsize = fdt_header_size(fdt);
 104        if (!can_assume(VALID_DTB)) {
 105
 106                if ((fdt_totalsize(fdt) < hdrsize)
 107                    || (fdt_totalsize(fdt) > INT_MAX))
 108                        return -FDT_ERR_TRUNCATED;
 109
 110                /* Bounds check memrsv block */
 111                if (!check_off_(hdrsize, fdt_totalsize(fdt),
 112                                fdt_off_mem_rsvmap(fdt)))
 113                        return -FDT_ERR_TRUNCATED;
 114        }
 115
 116        if (!can_assume(VALID_DTB)) {
 117                /* Bounds check structure block */
 118                if (!can_assume(LATEST) && fdt_version(fdt) < 17) {
 119                        if (!check_off_(hdrsize, fdt_totalsize(fdt),
 120                                        fdt_off_dt_struct(fdt)))
 121                                return -FDT_ERR_TRUNCATED;
 122                } else {
 123                        if (!check_block_(hdrsize, fdt_totalsize(fdt),
 124                                          fdt_off_dt_struct(fdt),
 125                                          fdt_size_dt_struct(fdt)))
 126                                return -FDT_ERR_TRUNCATED;
 127                }
 128
 129                /* Bounds check strings block */
 130                if (!check_block_(hdrsize, fdt_totalsize(fdt),
 131                                  fdt_off_dt_strings(fdt),
 132                                  fdt_size_dt_strings(fdt)))
 133                        return -FDT_ERR_TRUNCATED;
 134        }
 135
 136        return 0;
 137}
 138
 139const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int len)
 140{
 141        unsigned int uoffset = offset;
 142        unsigned int absoffset = offset + fdt_off_dt_struct(fdt);
 143
 144        if (offset < 0)
 145                return NULL;
 146
 147        if (!can_assume(VALID_INPUT))
 148                if ((absoffset < uoffset)
 149                    || ((absoffset + len) < absoffset)
 150                    || (absoffset + len) > fdt_totalsize(fdt))
 151                        return NULL;
 152
 153        if (can_assume(LATEST) || fdt_version(fdt) >= 0x11)
 154                if (((uoffset + len) < uoffset)
 155                    || ((offset + len) > fdt_size_dt_struct(fdt)))
 156                        return NULL;
 157
 158        return fdt_offset_ptr_(fdt, offset);
 159}
 160
 161uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset)
 162{
 163        const fdt32_t *tagp, *lenp;
 164        uint32_t tag;
 165        int offset = startoffset;
 166        const char *p;
 167
 168        *nextoffset = -FDT_ERR_TRUNCATED;
 169        tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE);
 170        if (!can_assume(VALID_DTB) && !tagp)
 171                return FDT_END; /* premature end */
 172        tag = fdt32_to_cpu(*tagp);
 173        offset += FDT_TAGSIZE;
 174
 175        *nextoffset = -FDT_ERR_BADSTRUCTURE;
 176        switch (tag) {
 177        case FDT_BEGIN_NODE:
 178                /* skip name */
 179                do {
 180                        p = fdt_offset_ptr(fdt, offset++, 1);
 181                } while (p && (*p != '\0'));
 182                if (!can_assume(VALID_DTB) && !p)
 183                        return FDT_END; /* premature end */
 184                break;
 185
 186        case FDT_PROP:
 187                lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp));
 188                if (!can_assume(VALID_DTB) && !lenp)
 189                        return FDT_END; /* premature end */
 190                /* skip-name offset, length and value */
 191                offset += sizeof(struct fdt_property) - FDT_TAGSIZE
 192                        + fdt32_to_cpu(*lenp);
 193                if (!can_assume(LATEST) &&
 194                    fdt_version(fdt) < 0x10 && fdt32_to_cpu(*lenp) >= 8 &&
 195                    ((offset - fdt32_to_cpu(*lenp)) % 8) != 0)
 196                        offset += 4;
 197                break;
 198
 199        case FDT_END:
 200        case FDT_END_NODE:
 201        case FDT_NOP:
 202                break;
 203
 204        default:
 205                return FDT_END;
 206        }
 207
 208        if (!fdt_offset_ptr(fdt, startoffset, offset - startoffset))
 209                return FDT_END; /* premature end */
 210
 211        *nextoffset = FDT_TAGALIGN(offset);
 212        return tag;
 213}
 214
 215int fdt_check_node_offset_(const void *fdt, int offset)
 216{
 217        if (!can_assume(VALID_INPUT)
 218            && ((offset < 0) || (offset % FDT_TAGSIZE)))
 219                return -FDT_ERR_BADOFFSET;
 220
 221        if (fdt_next_tag(fdt, offset, &offset) != FDT_BEGIN_NODE)
 222                return -FDT_ERR_BADOFFSET;
 223
 224        return offset;
 225}
 226
 227int fdt_check_prop_offset_(const void *fdt, int offset)
 228{
 229        if (!can_assume(VALID_INPUT)
 230            && ((offset < 0) || (offset % FDT_TAGSIZE)))
 231                return -FDT_ERR_BADOFFSET;
 232
 233        if (fdt_next_tag(fdt, offset, &offset) != FDT_PROP)
 234                return -FDT_ERR_BADOFFSET;
 235
 236        return offset;
 237}
 238
 239int fdt_next_node(const void *fdt, int offset, int *depth)
 240{
 241        int nextoffset = 0;
 242        uint32_t tag;
 243
 244        if (offset >= 0)
 245                if ((nextoffset = fdt_check_node_offset_(fdt, offset)) < 0)
 246                        return nextoffset;
 247
 248        do {
 249                offset = nextoffset;
 250                tag = fdt_next_tag(fdt, offset, &nextoffset);
 251
 252                switch (tag) {
 253                case FDT_PROP:
 254                case FDT_NOP:
 255                        break;
 256
 257                case FDT_BEGIN_NODE:
 258                        if (depth)
 259                                (*depth)++;
 260                        break;
 261
 262                case FDT_END_NODE:
 263                        if (depth && ((--(*depth)) < 0))
 264                                return nextoffset;
 265                        break;
 266
 267                case FDT_END:
 268                        if ((nextoffset >= 0)
 269                            || ((nextoffset == -FDT_ERR_TRUNCATED) && !depth))
 270                                return -FDT_ERR_NOTFOUND;
 271                        else
 272                                return nextoffset;
 273                }
 274        } while (tag != FDT_BEGIN_NODE);
 275
 276        return offset;
 277}
 278
 279int fdt_first_subnode(const void *fdt, int offset)
 280{
 281        int depth = 0;
 282
 283        offset = fdt_next_node(fdt, offset, &depth);
 284        if (offset < 0 || depth != 1)
 285                return -FDT_ERR_NOTFOUND;
 286
 287        return offset;
 288}
 289
 290int fdt_next_subnode(const void *fdt, int offset)
 291{
 292        int depth = 1;
 293
 294        /*
 295         * With respect to the parent, the depth of the next subnode will be
 296         * the same as the last.
 297         */
 298        do {
 299                offset = fdt_next_node(fdt, offset, &depth);
 300                if (offset < 0 || depth < 1)
 301                        return -FDT_ERR_NOTFOUND;
 302        } while (depth > 1);
 303
 304        return offset;
 305}
 306
 307const char *fdt_find_string_(const char *strtab, int tabsize, const char *s)
 308{
 309        int len = strlen(s) + 1;
 310        const char *last = strtab + tabsize - len;
 311        const char *p;
 312
 313        for (p = strtab; p <= last; p++)
 314                if (memcmp(p, s, len) == 0)
 315                        return p;
 316        return NULL;
 317}
 318
 319int fdt_move(const void *fdt, void *buf, int bufsize)
 320{
 321        if (!can_assume(VALID_INPUT) && bufsize < 0)
 322                return -FDT_ERR_NOSPACE;
 323
 324        FDT_RO_PROBE(fdt);
 325
 326        if (fdt_totalsize(fdt) > (unsigned int)bufsize)
 327                return -FDT_ERR_NOSPACE;
 328
 329        memmove(buf, fdt, fdt_totalsize(fdt));
 330        return 0;
 331}
 332