linux/scripts/dtc/libfdt/fdt_sw.c
<<
>>
Prefs
   1/*
   2 * libfdt - Flat Device Tree manipulation
   3 * Copyright (C) 2006 David Gibson, IBM Corporation.
   4 *
   5 * libfdt is dual licensed: you can use it either under the terms of
   6 * the GPL, or the BSD license, at your option.
   7 *
   8 *  a) This library is free software; you can redistribute it and/or
   9 *     modify it under the terms of the GNU General Public License as
  10 *     published by the Free Software Foundation; either version 2 of the
  11 *     License, or (at your option) any later version.
  12 *
  13 *     This library is distributed in the hope that it will be useful,
  14 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16 *     GNU General Public License for more details.
  17 *
  18 *     You should have received a copy of the GNU General Public
  19 *     License along with this library; if not, write to the Free
  20 *     Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
  21 *     MA 02110-1301 USA
  22 *
  23 * Alternatively,
  24 *
  25 *  b) Redistribution and use in source and binary forms, with or
  26 *     without modification, are permitted provided that the following
  27 *     conditions are met:
  28 *
  29 *     1. Redistributions of source code must retain the above
  30 *        copyright notice, this list of conditions and the following
  31 *        disclaimer.
  32 *     2. Redistributions in binary form must reproduce the above
  33 *        copyright notice, this list of conditions and the following
  34 *        disclaimer in the documentation and/or other materials
  35 *        provided with the distribution.
  36 *
  37 *     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  38 *     CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  39 *     INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  40 *     MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  41 *     DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  42 *     CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  43 *     SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  44 *     NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  45 *     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  46 *     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  47 *     CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  48 *     OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
  49 *     EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  50 */
  51#include "libfdt_env.h"
  52
  53#include <fdt.h>
  54#include <libfdt.h>
  55
  56#include "libfdt_internal.h"
  57
  58static int _fdt_sw_check_header(void *fdt)
  59{
  60        if (fdt_magic(fdt) != FDT_SW_MAGIC)
  61                return -FDT_ERR_BADMAGIC;
  62        /* FIXME: should check more details about the header state */
  63        return 0;
  64}
  65
  66#define FDT_SW_CHECK_HEADER(fdt) \
  67        { \
  68                int err; \
  69                if ((err = _fdt_sw_check_header(fdt)) != 0) \
  70                        return err; \
  71        }
  72
  73static void *_fdt_grab_space(void *fdt, size_t len)
  74{
  75        int offset = fdt_size_dt_struct(fdt);
  76        int spaceleft;
  77
  78        spaceleft = fdt_totalsize(fdt) - fdt_off_dt_struct(fdt)
  79                - fdt_size_dt_strings(fdt);
  80
  81        if ((offset + len < offset) || (offset + len > spaceleft))
  82                return NULL;
  83
  84        fdt_set_size_dt_struct(fdt, offset + len);
  85        return _fdt_offset_ptr_w(fdt, offset);
  86}
  87
  88int fdt_create(void *buf, int bufsize)
  89{
  90        void *fdt = buf;
  91
  92        if (bufsize < sizeof(struct fdt_header))
  93                return -FDT_ERR_NOSPACE;
  94
  95        memset(buf, 0, bufsize);
  96
  97        fdt_set_magic(fdt, FDT_SW_MAGIC);
  98        fdt_set_version(fdt, FDT_LAST_SUPPORTED_VERSION);
  99        fdt_set_last_comp_version(fdt, FDT_FIRST_SUPPORTED_VERSION);
 100        fdt_set_totalsize(fdt,  bufsize);
 101
 102        fdt_set_off_mem_rsvmap(fdt, FDT_ALIGN(sizeof(struct fdt_header),
 103                                              sizeof(struct fdt_reserve_entry)));
 104        fdt_set_off_dt_struct(fdt, fdt_off_mem_rsvmap(fdt));
 105        fdt_set_off_dt_strings(fdt, bufsize);
 106
 107        return 0;
 108}
 109
 110int fdt_resize(void *fdt, void *buf, int bufsize)
 111{
 112        size_t headsize, tailsize;
 113        char *oldtail, *newtail;
 114
 115        FDT_SW_CHECK_HEADER(fdt);
 116
 117        headsize = fdt_off_dt_struct(fdt);
 118        tailsize = fdt_size_dt_strings(fdt);
 119
 120        if ((headsize + tailsize) > bufsize)
 121                return -FDT_ERR_NOSPACE;
 122
 123        oldtail = (char *)fdt + fdt_totalsize(fdt) - tailsize;
 124        newtail = (char *)buf + bufsize - tailsize;
 125
 126        /* Two cases to avoid clobbering data if the old and new
 127         * buffers partially overlap */
 128        if (buf <= fdt) {
 129                memmove(buf, fdt, headsize);
 130                memmove(newtail, oldtail, tailsize);
 131        } else {
 132                memmove(newtail, oldtail, tailsize);
 133                memmove(buf, fdt, headsize);
 134        }
 135
 136        fdt_set_off_dt_strings(buf, bufsize);
 137        fdt_set_totalsize(buf, bufsize);
 138
 139        return 0;
 140}
 141
 142int fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size)
 143{
 144        struct fdt_reserve_entry *re;
 145        int offset;
 146
 147        FDT_SW_CHECK_HEADER(fdt);
 148
 149        if (fdt_size_dt_struct(fdt))
 150                return -FDT_ERR_BADSTATE;
 151
 152        offset = fdt_off_dt_struct(fdt);
 153        if ((offset + sizeof(*re)) > fdt_totalsize(fdt))
 154                return -FDT_ERR_NOSPACE;
 155
 156        re = (struct fdt_reserve_entry *)((char *)fdt + offset);
 157        re->address = cpu_to_fdt64(addr);
 158        re->size = cpu_to_fdt64(size);
 159
 160        fdt_set_off_dt_struct(fdt, offset + sizeof(*re));
 161
 162        return 0;
 163}
 164
 165int fdt_finish_reservemap(void *fdt)
 166{
 167        return fdt_add_reservemap_entry(fdt, 0, 0);
 168}
 169
 170int fdt_begin_node(void *fdt, const char *name)
 171{
 172        struct fdt_node_header *nh;
 173        int namelen = strlen(name) + 1;
 174
 175        FDT_SW_CHECK_HEADER(fdt);
 176
 177        nh = _fdt_grab_space(fdt, sizeof(*nh) + FDT_TAGALIGN(namelen));
 178        if (! nh)
 179                return -FDT_ERR_NOSPACE;
 180
 181        nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE);
 182        memcpy(nh->name, name, namelen);
 183        return 0;
 184}
 185
 186int fdt_end_node(void *fdt)
 187{
 188        fdt32_t *en;
 189
 190        FDT_SW_CHECK_HEADER(fdt);
 191
 192        en = _fdt_grab_space(fdt, FDT_TAGSIZE);
 193        if (! en)
 194                return -FDT_ERR_NOSPACE;
 195
 196        *en = cpu_to_fdt32(FDT_END_NODE);
 197        return 0;
 198}
 199
 200static int _fdt_find_add_string(void *fdt, const char *s)
 201{
 202        char *strtab = (char *)fdt + fdt_totalsize(fdt);
 203        const char *p;
 204        int strtabsize = fdt_size_dt_strings(fdt);
 205        int len = strlen(s) + 1;
 206        int struct_top, offset;
 207
 208        p = _fdt_find_string(strtab - strtabsize, strtabsize, s);
 209        if (p)
 210                return p - strtab;
 211
 212        /* Add it */
 213        offset = -strtabsize - len;
 214        struct_top = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt);
 215        if (fdt_totalsize(fdt) + offset < struct_top)
 216                return 0; /* no more room :( */
 217
 218        memcpy(strtab + offset, s, len);
 219        fdt_set_size_dt_strings(fdt, strtabsize + len);
 220        return offset;
 221}
 222
 223int fdt_property(void *fdt, const char *name, const void *val, int len)
 224{
 225        struct fdt_property *prop;
 226        int nameoff;
 227
 228        FDT_SW_CHECK_HEADER(fdt);
 229
 230        nameoff = _fdt_find_add_string(fdt, name);
 231        if (nameoff == 0)
 232                return -FDT_ERR_NOSPACE;
 233
 234        prop = _fdt_grab_space(fdt, sizeof(*prop) + FDT_TAGALIGN(len));
 235        if (! prop)
 236                return -FDT_ERR_NOSPACE;
 237
 238        prop->tag = cpu_to_fdt32(FDT_PROP);
 239        prop->nameoff = cpu_to_fdt32(nameoff);
 240        prop->len = cpu_to_fdt32(len);
 241        memcpy(prop->data, val, len);
 242        return 0;
 243}
 244
 245int fdt_finish(void *fdt)
 246{
 247        char *p = (char *)fdt;
 248        fdt32_t *end;
 249        int oldstroffset, newstroffset;
 250        uint32_t tag;
 251        int offset, nextoffset;
 252
 253        FDT_SW_CHECK_HEADER(fdt);
 254
 255        /* Add terminator */
 256        end = _fdt_grab_space(fdt, sizeof(*end));
 257        if (! end)
 258                return -FDT_ERR_NOSPACE;
 259        *end = cpu_to_fdt32(FDT_END);
 260
 261        /* Relocate the string table */
 262        oldstroffset = fdt_totalsize(fdt) - fdt_size_dt_strings(fdt);
 263        newstroffset = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt);
 264        memmove(p + newstroffset, p + oldstroffset, fdt_size_dt_strings(fdt));
 265        fdt_set_off_dt_strings(fdt, newstroffset);
 266
 267        /* Walk the structure, correcting string offsets */
 268        offset = 0;
 269        while ((tag = fdt_next_tag(fdt, offset, &nextoffset)) != FDT_END) {
 270                if (tag == FDT_PROP) {
 271                        struct fdt_property *prop =
 272                                _fdt_offset_ptr_w(fdt, offset);
 273                        int nameoff;
 274
 275                        nameoff = fdt32_to_cpu(prop->nameoff);
 276                        nameoff += fdt_size_dt_strings(fdt);
 277                        prop->nameoff = cpu_to_fdt32(nameoff);
 278                }
 279                offset = nextoffset;
 280        }
 281        if (nextoffset < 0)
 282                return nextoffset;
 283
 284        /* Finally, adjust the header */
 285        fdt_set_totalsize(fdt, newstroffset + fdt_size_dt_strings(fdt));
 286        fdt_set_magic(fdt, FDT_MAGIC);
 287        return 0;
 288}
 289