linux/scripts/dtc/libfdt/fdt_sw.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
  13static int fdt_sw_probe_(void *fdt)
  14{
  15        if (!can_assume(VALID_INPUT)) {
  16                if (fdt_magic(fdt) == FDT_MAGIC)
  17                        return -FDT_ERR_BADSTATE;
  18                else if (fdt_magic(fdt) != FDT_SW_MAGIC)
  19                        return -FDT_ERR_BADMAGIC;
  20        }
  21
  22        return 0;
  23}
  24
  25#define FDT_SW_PROBE(fdt) \
  26        { \
  27                int err; \
  28                if ((err = fdt_sw_probe_(fdt)) != 0) \
  29                        return err; \
  30        }
  31
  32/* 'memrsv' state:      Initial state after fdt_create()
  33 *
  34 * Allowed functions:
  35 *      fdt_add_reservemap_entry()
  36 *      fdt_finish_reservemap()         [moves to 'struct' state]
  37 */
  38static int fdt_sw_probe_memrsv_(void *fdt)
  39{
  40        int err = fdt_sw_probe_(fdt);
  41        if (err)
  42                return err;
  43
  44        if (!can_assume(VALID_INPUT) && fdt_off_dt_strings(fdt) != 0)
  45                return -FDT_ERR_BADSTATE;
  46        return 0;
  47}
  48
  49#define FDT_SW_PROBE_MEMRSV(fdt) \
  50        { \
  51                int err; \
  52                if ((err = fdt_sw_probe_memrsv_(fdt)) != 0) \
  53                        return err; \
  54        }
  55
  56/* 'struct' state:      Enter this state after fdt_finish_reservemap()
  57 *
  58 * Allowed functions:
  59 *      fdt_begin_node()
  60 *      fdt_end_node()
  61 *      fdt_property*()
  62 *      fdt_finish()                    [moves to 'complete' state]
  63 */
  64static int fdt_sw_probe_struct_(void *fdt)
  65{
  66        int err = fdt_sw_probe_(fdt);
  67        if (err)
  68                return err;
  69
  70        if (!can_assume(VALID_INPUT) &&
  71            fdt_off_dt_strings(fdt) != fdt_totalsize(fdt))
  72                return -FDT_ERR_BADSTATE;
  73        return 0;
  74}
  75
  76#define FDT_SW_PROBE_STRUCT(fdt) \
  77        { \
  78                int err; \
  79                if ((err = fdt_sw_probe_struct_(fdt)) != 0) \
  80                        return err; \
  81        }
  82
  83static inline uint32_t sw_flags(void *fdt)
  84{
  85        /* assert: (fdt_magic(fdt) == FDT_SW_MAGIC) */
  86        return fdt_last_comp_version(fdt);
  87}
  88
  89/* 'complete' state:    Enter this state after fdt_finish()
  90 *
  91 * Allowed functions: none
  92 */
  93
  94static void *fdt_grab_space_(void *fdt, size_t len)
  95{
  96        unsigned int offset = fdt_size_dt_struct(fdt);
  97        unsigned int spaceleft;
  98
  99        spaceleft = fdt_totalsize(fdt) - fdt_off_dt_struct(fdt)
 100                - fdt_size_dt_strings(fdt);
 101
 102        if ((offset + len < offset) || (offset + len > spaceleft))
 103                return NULL;
 104
 105        fdt_set_size_dt_struct(fdt, offset + len);
 106        return fdt_offset_ptr_w_(fdt, offset);
 107}
 108
 109int fdt_create_with_flags(void *buf, int bufsize, uint32_t flags)
 110{
 111        const int hdrsize = FDT_ALIGN(sizeof(struct fdt_header),
 112                                      sizeof(struct fdt_reserve_entry));
 113        void *fdt = buf;
 114
 115        if (bufsize < hdrsize)
 116                return -FDT_ERR_NOSPACE;
 117
 118        if (flags & ~FDT_CREATE_FLAGS_ALL)
 119                return -FDT_ERR_BADFLAGS;
 120
 121        memset(buf, 0, bufsize);
 122
 123        /*
 124         * magic and last_comp_version keep intermediate state during the fdt
 125         * creation process, which is replaced with the proper FDT format by
 126         * fdt_finish().
 127         *
 128         * flags should be accessed with sw_flags().
 129         */
 130        fdt_set_magic(fdt, FDT_SW_MAGIC);
 131        fdt_set_version(fdt, FDT_LAST_SUPPORTED_VERSION);
 132        fdt_set_last_comp_version(fdt, flags);
 133
 134        fdt_set_totalsize(fdt,  bufsize);
 135
 136        fdt_set_off_mem_rsvmap(fdt, hdrsize);
 137        fdt_set_off_dt_struct(fdt, fdt_off_mem_rsvmap(fdt));
 138        fdt_set_off_dt_strings(fdt, 0);
 139
 140        return 0;
 141}
 142
 143int fdt_create(void *buf, int bufsize)
 144{
 145        return fdt_create_with_flags(buf, bufsize, 0);
 146}
 147
 148int fdt_resize(void *fdt, void *buf, int bufsize)
 149{
 150        size_t headsize, tailsize;
 151        char *oldtail, *newtail;
 152
 153        FDT_SW_PROBE(fdt);
 154
 155        if (bufsize < 0)
 156                return -FDT_ERR_NOSPACE;
 157
 158        headsize = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt);
 159        tailsize = fdt_size_dt_strings(fdt);
 160
 161        if (!can_assume(VALID_DTB) &&
 162            headsize + tailsize > fdt_totalsize(fdt))
 163                return -FDT_ERR_INTERNAL;
 164
 165        if ((headsize + tailsize) > (unsigned)bufsize)
 166                return -FDT_ERR_NOSPACE;
 167
 168        oldtail = (char *)fdt + fdt_totalsize(fdt) - tailsize;
 169        newtail = (char *)buf + bufsize - tailsize;
 170
 171        /* Two cases to avoid clobbering data if the old and new
 172         * buffers partially overlap */
 173        if (buf <= fdt) {
 174                memmove(buf, fdt, headsize);
 175                memmove(newtail, oldtail, tailsize);
 176        } else {
 177                memmove(newtail, oldtail, tailsize);
 178                memmove(buf, fdt, headsize);
 179        }
 180
 181        fdt_set_totalsize(buf, bufsize);
 182        if (fdt_off_dt_strings(buf))
 183                fdt_set_off_dt_strings(buf, bufsize);
 184
 185        return 0;
 186}
 187
 188int fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size)
 189{
 190        struct fdt_reserve_entry *re;
 191        int offset;
 192
 193        FDT_SW_PROBE_MEMRSV(fdt);
 194
 195        offset = fdt_off_dt_struct(fdt);
 196        if ((offset + sizeof(*re)) > fdt_totalsize(fdt))
 197                return -FDT_ERR_NOSPACE;
 198
 199        re = (struct fdt_reserve_entry *)((char *)fdt + offset);
 200        re->address = cpu_to_fdt64(addr);
 201        re->size = cpu_to_fdt64(size);
 202
 203        fdt_set_off_dt_struct(fdt, offset + sizeof(*re));
 204
 205        return 0;
 206}
 207
 208int fdt_finish_reservemap(void *fdt)
 209{
 210        int err = fdt_add_reservemap_entry(fdt, 0, 0);
 211
 212        if (err)
 213                return err;
 214
 215        fdt_set_off_dt_strings(fdt, fdt_totalsize(fdt));
 216        return 0;
 217}
 218
 219int fdt_begin_node(void *fdt, const char *name)
 220{
 221        struct fdt_node_header *nh;
 222        int namelen;
 223
 224        FDT_SW_PROBE_STRUCT(fdt);
 225
 226        namelen = strlen(name) + 1;
 227        nh = fdt_grab_space_(fdt, sizeof(*nh) + FDT_TAGALIGN(namelen));
 228        if (! nh)
 229                return -FDT_ERR_NOSPACE;
 230
 231        nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE);
 232        memcpy(nh->name, name, namelen);
 233        return 0;
 234}
 235
 236int fdt_end_node(void *fdt)
 237{
 238        fdt32_t *en;
 239
 240        FDT_SW_PROBE_STRUCT(fdt);
 241
 242        en = fdt_grab_space_(fdt, FDT_TAGSIZE);
 243        if (! en)
 244                return -FDT_ERR_NOSPACE;
 245
 246        *en = cpu_to_fdt32(FDT_END_NODE);
 247        return 0;
 248}
 249
 250static int fdt_add_string_(void *fdt, const char *s)
 251{
 252        char *strtab = (char *)fdt + fdt_totalsize(fdt);
 253        unsigned int strtabsize = fdt_size_dt_strings(fdt);
 254        unsigned int len = strlen(s) + 1;
 255        unsigned int struct_top, offset;
 256
 257        offset = strtabsize + len;
 258        struct_top = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt);
 259        if (fdt_totalsize(fdt) - offset < struct_top)
 260                return 0; /* no more room :( */
 261
 262        memcpy(strtab - offset, s, len);
 263        fdt_set_size_dt_strings(fdt, strtabsize + len);
 264        return -offset;
 265}
 266
 267/* Must only be used to roll back in case of error */
 268static void fdt_del_last_string_(void *fdt, const char *s)
 269{
 270        int strtabsize = fdt_size_dt_strings(fdt);
 271        int len = strlen(s) + 1;
 272
 273        fdt_set_size_dt_strings(fdt, strtabsize - len);
 274}
 275
 276static int fdt_find_add_string_(void *fdt, const char *s, int *allocated)
 277{
 278        char *strtab = (char *)fdt + fdt_totalsize(fdt);
 279        int strtabsize = fdt_size_dt_strings(fdt);
 280        const char *p;
 281
 282        *allocated = 0;
 283
 284        p = fdt_find_string_(strtab - strtabsize, strtabsize, s);
 285        if (p)
 286                return p - strtab;
 287
 288        *allocated = 1;
 289
 290        return fdt_add_string_(fdt, s);
 291}
 292
 293int fdt_property_placeholder(void *fdt, const char *name, int len, void **valp)
 294{
 295        struct fdt_property *prop;
 296        int nameoff;
 297        int allocated;
 298
 299        FDT_SW_PROBE_STRUCT(fdt);
 300
 301        /* String de-duplication can be slow, _NO_NAME_DEDUP skips it */
 302        if (sw_flags(fdt) & FDT_CREATE_FLAG_NO_NAME_DEDUP) {
 303                allocated = 1;
 304                nameoff = fdt_add_string_(fdt, name);
 305        } else {
 306                nameoff = fdt_find_add_string_(fdt, name, &allocated);
 307        }
 308        if (nameoff == 0)
 309                return -FDT_ERR_NOSPACE;
 310
 311        prop = fdt_grab_space_(fdt, sizeof(*prop) + FDT_TAGALIGN(len));
 312        if (! prop) {
 313                if (allocated)
 314                        fdt_del_last_string_(fdt, name);
 315                return -FDT_ERR_NOSPACE;
 316        }
 317
 318        prop->tag = cpu_to_fdt32(FDT_PROP);
 319        prop->nameoff = cpu_to_fdt32(nameoff);
 320        prop->len = cpu_to_fdt32(len);
 321        *valp = prop->data;
 322        return 0;
 323}
 324
 325int fdt_property(void *fdt, const char *name, const void *val, int len)
 326{
 327        void *ptr;
 328        int ret;
 329
 330        ret = fdt_property_placeholder(fdt, name, len, &ptr);
 331        if (ret)
 332                return ret;
 333        memcpy(ptr, val, len);
 334        return 0;
 335}
 336
 337int fdt_finish(void *fdt)
 338{
 339        char *p = (char *)fdt;
 340        fdt32_t *end;
 341        int oldstroffset, newstroffset;
 342        uint32_t tag;
 343        int offset, nextoffset;
 344
 345        FDT_SW_PROBE_STRUCT(fdt);
 346
 347        /* Add terminator */
 348        end = fdt_grab_space_(fdt, sizeof(*end));
 349        if (! end)
 350                return -FDT_ERR_NOSPACE;
 351        *end = cpu_to_fdt32(FDT_END);
 352
 353        /* Relocate the string table */
 354        oldstroffset = fdt_totalsize(fdt) - fdt_size_dt_strings(fdt);
 355        newstroffset = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt);
 356        memmove(p + newstroffset, p + oldstroffset, fdt_size_dt_strings(fdt));
 357        fdt_set_off_dt_strings(fdt, newstroffset);
 358
 359        /* Walk the structure, correcting string offsets */
 360        offset = 0;
 361        while ((tag = fdt_next_tag(fdt, offset, &nextoffset)) != FDT_END) {
 362                if (tag == FDT_PROP) {
 363                        struct fdt_property *prop =
 364                                fdt_offset_ptr_w_(fdt, offset);
 365                        int nameoff;
 366
 367                        nameoff = fdt32_to_cpu(prop->nameoff);
 368                        nameoff += fdt_size_dt_strings(fdt);
 369                        prop->nameoff = cpu_to_fdt32(nameoff);
 370                }
 371                offset = nextoffset;
 372        }
 373        if (nextoffset < 0)
 374                return nextoffset;
 375
 376        /* Finally, adjust the header */
 377        fdt_set_totalsize(fdt, newstroffset + fdt_size_dt_strings(fdt));
 378
 379        /* And fix up fields that were keeping intermediate state. */
 380        fdt_set_last_comp_version(fdt, FDT_LAST_COMPATIBLE_VERSION);
 381        fdt_set_magic(fdt, FDT_MAGIC);
 382
 383        return 0;
 384}
 385