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 (fdt_magic(fdt) == FDT_MAGIC) {
  23                /* Complete tree */
  24                if (fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION)
  25                        return -FDT_ERR_BADVERSION;
  26                if (fdt_last_comp_version(fdt) > FDT_LAST_SUPPORTED_VERSION)
  27                        return -FDT_ERR_BADVERSION;
  28        } else if (fdt_magic(fdt) == FDT_SW_MAGIC) {
  29                /* Unfinished sequential-write blob */
  30                if (fdt_size_dt_struct(fdt) == 0)
  31                        return -FDT_ERR_BADSTATE;
  32        } else {
  33                return -FDT_ERR_BADMAGIC;
  34        }
  35
  36        if (totalsize < INT32_MAX)
  37                return totalsize;
  38        else
  39                return -FDT_ERR_TRUNCATED;
  40}
  41
  42static int check_off_(uint32_t hdrsize, uint32_t totalsize, uint32_t off)
  43{
  44        return (off >= hdrsize) && (off <= totalsize);
  45}
  46
  47static int check_block_(uint32_t hdrsize, uint32_t totalsize,
  48                        uint32_t base, uint32_t size)
  49{
  50        if (!check_off_(hdrsize, totalsize, base))
  51                return 0; /* block start out of bounds */
  52        if ((base + size) < base)
  53                return 0; /* overflow */
  54        if (!check_off_(hdrsize, totalsize, base + size))
  55                return 0; /* block end out of bounds */
  56        return 1;
  57}
  58
  59size_t fdt_header_size_(uint32_t version)
  60{
  61        if (version <= 1)
  62                return FDT_V1_SIZE;
  63        else if (version <= 2)
  64                return FDT_V2_SIZE;
  65        else if (version <= 3)
  66                return FDT_V3_SIZE;
  67        else if (version <= 16)
  68                return FDT_V16_SIZE;
  69        else
  70                return FDT_V17_SIZE;
  71}
  72
  73int fdt_check_header(const void *fdt)
  74{
  75        size_t hdrsize;
  76
  77        if (fdt_magic(fdt) != FDT_MAGIC)
  78                return -FDT_ERR_BADMAGIC;
  79        hdrsize = fdt_header_size(fdt);
  80        if ((fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION)
  81            || (fdt_last_comp_version(fdt) > FDT_LAST_SUPPORTED_VERSION))
  82                return -FDT_ERR_BADVERSION;
  83        if (fdt_version(fdt) < fdt_last_comp_version(fdt))
  84                return -FDT_ERR_BADVERSION;
  85
  86        if ((fdt_totalsize(fdt) < hdrsize)
  87            || (fdt_totalsize(fdt) > INT_MAX))
  88                return -FDT_ERR_TRUNCATED;
  89
  90        /* Bounds check memrsv block */
  91        if (!check_off_(hdrsize, fdt_totalsize(fdt), fdt_off_mem_rsvmap(fdt)))
  92                return -FDT_ERR_TRUNCATED;
  93
  94        /* Bounds check structure block */
  95        if (fdt_version(fdt) < 17) {
  96                if (!check_off_(hdrsize, fdt_totalsize(fdt),
  97                                fdt_off_dt_struct(fdt)))
  98                        return -FDT_ERR_TRUNCATED;
  99        } else {
 100                if (!check_block_(hdrsize, fdt_totalsize(fdt),
 101                                  fdt_off_dt_struct(fdt),
 102                                  fdt_size_dt_struct(fdt)))
 103                        return -FDT_ERR_TRUNCATED;
 104        }
 105
 106        /* Bounds check strings block */
 107        if (!check_block_(hdrsize, fdt_totalsize(fdt),
 108                          fdt_off_dt_strings(fdt), fdt_size_dt_strings(fdt)))
 109                return -FDT_ERR_TRUNCATED;
 110
 111        return 0;
 112}
 113
 114const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int len)
 115{
 116        unsigned absoffset = offset + fdt_off_dt_struct(fdt);
 117
 118        if ((absoffset < offset)
 119            || ((absoffset + len) < absoffset)
 120            || (absoffset + len) > fdt_totalsize(fdt))
 121                return NULL;
 122
 123        if (fdt_version(fdt) >= 0x11)
 124                if (((offset + len) < offset)
 125                    || ((offset + len) > fdt_size_dt_struct(fdt)))
 126                        return NULL;
 127
 128        return fdt_offset_ptr_(fdt, offset);
 129}
 130
 131uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset)
 132{
 133        const fdt32_t *tagp, *lenp;
 134        uint32_t tag;
 135        int offset = startoffset;
 136        const char *p;
 137
 138        *nextoffset = -FDT_ERR_TRUNCATED;
 139        tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE);
 140        if (!tagp)
 141                return FDT_END; /* premature end */
 142        tag = fdt32_to_cpu(*tagp);
 143        offset += FDT_TAGSIZE;
 144
 145        *nextoffset = -FDT_ERR_BADSTRUCTURE;
 146        switch (tag) {
 147        case FDT_BEGIN_NODE:
 148                /* skip name */
 149                do {
 150                        p = fdt_offset_ptr(fdt, offset++, 1);
 151                } while (p && (*p != '\0'));
 152                if (!p)
 153                        return FDT_END; /* premature end */
 154                break;
 155
 156        case FDT_PROP:
 157                lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp));
 158                if (!lenp)
 159                        return FDT_END; /* premature end */
 160                /* skip-name offset, length and value */
 161                offset += sizeof(struct fdt_property) - FDT_TAGSIZE
 162                        + fdt32_to_cpu(*lenp);
 163                if (fdt_version(fdt) < 0x10 && fdt32_to_cpu(*lenp) >= 8 &&
 164                    ((offset - fdt32_to_cpu(*lenp)) % 8) != 0)
 165                        offset += 4;
 166                break;
 167
 168        case FDT_END:
 169        case FDT_END_NODE:
 170        case FDT_NOP:
 171                break;
 172
 173        default:
 174                return FDT_END;
 175        }
 176
 177        if (!fdt_offset_ptr(fdt, startoffset, offset - startoffset))
 178                return FDT_END; /* premature end */
 179
 180        *nextoffset = FDT_TAGALIGN(offset);
 181        return tag;
 182}
 183
 184int fdt_check_node_offset_(const void *fdt, int offset)
 185{
 186        if ((offset < 0) || (offset % FDT_TAGSIZE)
 187            || (fdt_next_tag(fdt, offset, &offset) != FDT_BEGIN_NODE))
 188                return -FDT_ERR_BADOFFSET;
 189
 190        return offset;
 191}
 192
 193int fdt_check_prop_offset_(const void *fdt, int offset)
 194{
 195        if ((offset < 0) || (offset % FDT_TAGSIZE)
 196            || (fdt_next_tag(fdt, offset, &offset) != FDT_PROP))
 197                return -FDT_ERR_BADOFFSET;
 198
 199        return offset;
 200}
 201
 202int fdt_next_node(const void *fdt, int offset, int *depth)
 203{
 204        int nextoffset = 0;
 205        uint32_t tag;
 206
 207        if (offset >= 0)
 208                if ((nextoffset = fdt_check_node_offset_(fdt, offset)) < 0)
 209                        return nextoffset;
 210
 211        do {
 212                offset = nextoffset;
 213                tag = fdt_next_tag(fdt, offset, &nextoffset);
 214
 215                switch (tag) {
 216                case FDT_PROP:
 217                case FDT_NOP:
 218                        break;
 219
 220                case FDT_BEGIN_NODE:
 221                        if (depth)
 222                                (*depth)++;
 223                        break;
 224
 225                case FDT_END_NODE:
 226                        if (depth && ((--(*depth)) < 0))
 227                                return nextoffset;
 228                        break;
 229
 230                case FDT_END:
 231                        if ((nextoffset >= 0)
 232                            || ((nextoffset == -FDT_ERR_TRUNCATED) && !depth))
 233                                return -FDT_ERR_NOTFOUND;
 234                        else
 235                                return nextoffset;
 236                }
 237        } while (tag != FDT_BEGIN_NODE);
 238
 239        return offset;
 240}
 241
 242int fdt_first_subnode(const void *fdt, int offset)
 243{
 244        int depth = 0;
 245
 246        offset = fdt_next_node(fdt, offset, &depth);
 247        if (offset < 0 || depth != 1)
 248                return -FDT_ERR_NOTFOUND;
 249
 250        return offset;
 251}
 252
 253int fdt_next_subnode(const void *fdt, int offset)
 254{
 255        int depth = 1;
 256
 257        /*
 258         * With respect to the parent, the depth of the next subnode will be
 259         * the same as the last.
 260         */
 261        do {
 262                offset = fdt_next_node(fdt, offset, &depth);
 263                if (offset < 0 || depth < 1)
 264                        return -FDT_ERR_NOTFOUND;
 265        } while (depth > 1);
 266
 267        return offset;
 268}
 269
 270const char *fdt_find_string_(const char *strtab, int tabsize, const char *s)
 271{
 272        int len = strlen(s) + 1;
 273        const char *last = strtab + tabsize - len;
 274        const char *p;
 275
 276        for (p = strtab; p <= last; p++)
 277                if (memcmp(p, s, len) == 0)
 278                        return p;
 279        return NULL;
 280}
 281
 282int fdt_move(const void *fdt, void *buf, int bufsize)
 283{
 284        FDT_RO_PROBE(fdt);
 285
 286        if (fdt_totalsize(fdt) > bufsize)
 287                return -FDT_ERR_NOSPACE;
 288
 289        memmove(buf, fdt, fdt_totalsize(fdt));
 290        return 0;
 291}
 292