19537Satgutier@umich.edu/*
29537Satgutier@umich.edu * libfdt - Flat Device Tree manipulation
39537Satgutier@umich.edu * Copyright (C) 2006 David Gibson, IBM Corporation.
49537Satgutier@umich.edu *
59537Satgutier@umich.edu *     Redistribution and use in source and binary forms, with or
69537Satgutier@umich.edu *     without modification, are permitted provided that the following
79537Satgutier@umich.edu *     conditions are met:
89537Satgutier@umich.edu *
99537Satgutier@umich.edu *     1. Redistributions of source code must retain the above
109537Satgutier@umich.edu *        copyright notice, this list of conditions and the following
119537Satgutier@umich.edu *        disclaimer.
129537Satgutier@umich.edu *     2. Redistributions in binary form must reproduce the above
139537Satgutier@umich.edu *        copyright notice, this list of conditions and the following
149537Satgutier@umich.edu *        disclaimer in the documentation and/or other materials
159537Satgutier@umich.edu *        provided with the distribution.
169537Satgutier@umich.edu *
179537Satgutier@umich.edu *     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
189537Satgutier@umich.edu *     CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
199537Satgutier@umich.edu *     INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
209537Satgutier@umich.edu *     MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
219537Satgutier@umich.edu *     DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
229537Satgutier@umich.edu *     CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
239537Satgutier@umich.edu *     SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
249537Satgutier@umich.edu *     NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
259537Satgutier@umich.edu *     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
269537Satgutier@umich.edu *     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
279537Satgutier@umich.edu *     CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
289537Satgutier@umich.edu *     OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
299537Satgutier@umich.edu *     EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
309537Satgutier@umich.edu */
319537Satgutier@umich.edu#include <fdt.h>
329537Satgutier@umich.edu#include <libfdt.h>
339537Satgutier@umich.edu
349537Satgutier@umich.edu#include "libfdt_env.h"
359537Satgutier@umich.edu#include "libfdt_internal.h"
369537Satgutier@umich.edu
379537Satgutier@umich.edustatic int _fdt_sw_check_header(void *fdt)
389537Satgutier@umich.edu{
399537Satgutier@umich.edu        if (fdt_magic(fdt) != FDT_SW_MAGIC)
409537Satgutier@umich.edu                return -FDT_ERR_BADMAGIC;
419537Satgutier@umich.edu        /* FIXME: should check more details about the header state */
429537Satgutier@umich.edu        return 0;
439537Satgutier@umich.edu}
449537Satgutier@umich.edu
459537Satgutier@umich.edu#define FDT_SW_CHECK_HEADER(fdt) \
469537Satgutier@umich.edu        { \
479537Satgutier@umich.edu                int err; \
489537Satgutier@umich.edu                if ((err = _fdt_sw_check_header(fdt)) != 0) \
499537Satgutier@umich.edu                        return err; \
509537Satgutier@umich.edu        }
519537Satgutier@umich.edu
529537Satgutier@umich.edustatic void *_fdt_grab_space(void *fdt, size_t len)
539537Satgutier@umich.edu{
549537Satgutier@umich.edu        int offset = fdt_size_dt_struct(fdt);
559537Satgutier@umich.edu        int spaceleft;
569537Satgutier@umich.edu
579537Satgutier@umich.edu        spaceleft = fdt_totalsize(fdt) - fdt_off_dt_struct(fdt)
589537Satgutier@umich.edu                - fdt_size_dt_strings(fdt);
599537Satgutier@umich.edu
609537Satgutier@umich.edu        if ((offset + len < offset) || (offset + len > spaceleft))
619537Satgutier@umich.edu                return NULL;
629537Satgutier@umich.edu
639537Satgutier@umich.edu        fdt_set_size_dt_struct(fdt, offset + len);
649537Satgutier@umich.edu        return _fdt_offset_ptr_w(fdt, offset);
659537Satgutier@umich.edu}
669537Satgutier@umich.edu
679537Satgutier@umich.eduint fdt_create(void *buf, int bufsize)
689537Satgutier@umich.edu{
699537Satgutier@umich.edu        void *fdt = buf;
709537Satgutier@umich.edu
719537Satgutier@umich.edu        if (bufsize < sizeof(struct fdt_header))
729537Satgutier@umich.edu                return -FDT_ERR_NOSPACE;
739537Satgutier@umich.edu
749537Satgutier@umich.edu        memset(buf, 0, bufsize);
759537Satgutier@umich.edu
769537Satgutier@umich.edu        fdt_set_magic(fdt, FDT_SW_MAGIC);
779537Satgutier@umich.edu        fdt_set_version(fdt, FDT_LAST_SUPPORTED_VERSION);
789537Satgutier@umich.edu        fdt_set_last_comp_version(fdt, FDT_FIRST_SUPPORTED_VERSION);
799537Satgutier@umich.edu        fdt_set_totalsize(fdt,  bufsize);
809537Satgutier@umich.edu
819537Satgutier@umich.edu        fdt_set_off_mem_rsvmap(fdt, FDT_ALIGN(sizeof(struct fdt_header),
829537Satgutier@umich.edu                                              sizeof(struct fdt_reserve_entry)));
839537Satgutier@umich.edu        fdt_set_off_dt_struct(fdt, fdt_off_mem_rsvmap(fdt));
849537Satgutier@umich.edu        fdt_set_off_dt_strings(fdt, bufsize);
859537Satgutier@umich.edu
869537Satgutier@umich.edu        return 0;
879537Satgutier@umich.edu}
889537Satgutier@umich.edu
899537Satgutier@umich.eduint fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size)
909537Satgutier@umich.edu{
919537Satgutier@umich.edu        struct fdt_reserve_entry *re;
929537Satgutier@umich.edu        int offset;
939537Satgutier@umich.edu
949537Satgutier@umich.edu        FDT_SW_CHECK_HEADER(fdt);
959537Satgutier@umich.edu
969537Satgutier@umich.edu        if (fdt_size_dt_struct(fdt))
979537Satgutier@umich.edu                return -FDT_ERR_BADSTATE;
989537Satgutier@umich.edu
999537Satgutier@umich.edu        offset = fdt_off_dt_struct(fdt);
1009537Satgutier@umich.edu        if ((offset + sizeof(*re)) > fdt_totalsize(fdt))
1019537Satgutier@umich.edu                return -FDT_ERR_NOSPACE;
1029537Satgutier@umich.edu
1039537Satgutier@umich.edu        re = (struct fdt_reserve_entry *)((char *)fdt + offset);
1049537Satgutier@umich.edu        re->address = cpu_to_fdt64(addr);
1059537Satgutier@umich.edu        re->size = cpu_to_fdt64(size);
1069537Satgutier@umich.edu
1079537Satgutier@umich.edu        fdt_set_off_dt_struct(fdt, offset + sizeof(*re));
1089537Satgutier@umich.edu
1099537Satgutier@umich.edu        return 0;
1109537Satgutier@umich.edu}
1119537Satgutier@umich.edu
1129537Satgutier@umich.eduint fdt_finish_reservemap(void *fdt)
1139537Satgutier@umich.edu{
1149537Satgutier@umich.edu        return fdt_add_reservemap_entry(fdt, 0, 0);
1159537Satgutier@umich.edu}
1169537Satgutier@umich.edu
1179537Satgutier@umich.eduint fdt_begin_node(void *fdt, const char *name)
1189537Satgutier@umich.edu{
1199537Satgutier@umich.edu        struct fdt_node_header *nh;
1209537Satgutier@umich.edu        int namelen = strlen(name) + 1;
1219537Satgutier@umich.edu
1229537Satgutier@umich.edu        FDT_SW_CHECK_HEADER(fdt);
1239537Satgutier@umich.edu
1249537Satgutier@umich.edu        nh = _fdt_grab_space(fdt, sizeof(*nh) + FDT_TAGALIGN(namelen));
1259537Satgutier@umich.edu        if (! nh)
1269537Satgutier@umich.edu                return -FDT_ERR_NOSPACE;
1279537Satgutier@umich.edu
1289537Satgutier@umich.edu        nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE);
1299537Satgutier@umich.edu        memcpy(nh->name, name, namelen);
1309537Satgutier@umich.edu        return 0;
1319537Satgutier@umich.edu}
1329537Satgutier@umich.edu
1339537Satgutier@umich.eduint fdt_end_node(void *fdt)
1349537Satgutier@umich.edu{
1359537Satgutier@umich.edu        fdt32_t *en;
1369537Satgutier@umich.edu
1379537Satgutier@umich.edu        FDT_SW_CHECK_HEADER(fdt);
1389537Satgutier@umich.edu
1399537Satgutier@umich.edu        en = _fdt_grab_space(fdt, FDT_TAGSIZE);
1409537Satgutier@umich.edu        if (! en)
1419537Satgutier@umich.edu                return -FDT_ERR_NOSPACE;
1429537Satgutier@umich.edu
1439537Satgutier@umich.edu        *en = cpu_to_fdt32(FDT_END_NODE);
1449537Satgutier@umich.edu        return 0;
1459537Satgutier@umich.edu}
1469537Satgutier@umich.edu
1479537Satgutier@umich.edustatic int _fdt_find_add_string(void *fdt, const char *s)
1489537Satgutier@umich.edu{
1499537Satgutier@umich.edu        char *strtab = (char *)fdt + fdt_totalsize(fdt);
1509537Satgutier@umich.edu        const char *p;
1519537Satgutier@umich.edu        int strtabsize = fdt_size_dt_strings(fdt);
1529537Satgutier@umich.edu        int len = strlen(s) + 1;
1539537Satgutier@umich.edu        int struct_top, offset;
1549537Satgutier@umich.edu
1559537Satgutier@umich.edu        p = _fdt_find_string(strtab - strtabsize, strtabsize, s);
1569537Satgutier@umich.edu        if (p)
1579537Satgutier@umich.edu                return p - strtab;
1589537Satgutier@umich.edu
1599537Satgutier@umich.edu        /* Add it */
1609537Satgutier@umich.edu        offset = -strtabsize - len;
1619537Satgutier@umich.edu        struct_top = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt);
1629537Satgutier@umich.edu        if (fdt_totalsize(fdt) + offset < struct_top)
1639537Satgutier@umich.edu                return 0; /* no more room :( */
1649537Satgutier@umich.edu
1659537Satgutier@umich.edu        memcpy(strtab + offset, s, len);
1669537Satgutier@umich.edu        fdt_set_size_dt_strings(fdt, strtabsize + len);
1679537Satgutier@umich.edu        return offset;
1689537Satgutier@umich.edu}
1699537Satgutier@umich.edu
1709537Satgutier@umich.eduint fdt_property(void *fdt, const char *name, const void *val, int len)
1719537Satgutier@umich.edu{
1729537Satgutier@umich.edu        struct fdt_property *prop;
1739537Satgutier@umich.edu        int nameoff;
1749537Satgutier@umich.edu
1759537Satgutier@umich.edu        FDT_SW_CHECK_HEADER(fdt);
1769537Satgutier@umich.edu
1779537Satgutier@umich.edu        nameoff = _fdt_find_add_string(fdt, name);
1789537Satgutier@umich.edu        if (nameoff == 0)
1799537Satgutier@umich.edu                return -FDT_ERR_NOSPACE;
1809537Satgutier@umich.edu
1819537Satgutier@umich.edu        prop = _fdt_grab_space(fdt, sizeof(*prop) + FDT_TAGALIGN(len));
1829537Satgutier@umich.edu        if (! prop)
1839537Satgutier@umich.edu                return -FDT_ERR_NOSPACE;
1849537Satgutier@umich.edu
1859537Satgutier@umich.edu        prop->tag = cpu_to_fdt32(FDT_PROP);
1869537Satgutier@umich.edu        prop->nameoff = cpu_to_fdt32(nameoff);
1879537Satgutier@umich.edu        prop->len = cpu_to_fdt32(len);
1889537Satgutier@umich.edu        memcpy(prop->data, val, len);
1899537Satgutier@umich.edu        return 0;
1909537Satgutier@umich.edu}
1919537Satgutier@umich.edu
1929537Satgutier@umich.eduint fdt_finish(void *fdt)
1939537Satgutier@umich.edu{
1949537Satgutier@umich.edu        char *p = (char *)fdt;
1959537Satgutier@umich.edu        fdt32_t *end;
1969537Satgutier@umich.edu        int oldstroffset, newstroffset;
1979537Satgutier@umich.edu        uint32_t tag;
1989537Satgutier@umich.edu        int offset, nextoffset;
1999537Satgutier@umich.edu
2009537Satgutier@umich.edu        FDT_SW_CHECK_HEADER(fdt);
2019537Satgutier@umich.edu
2029537Satgutier@umich.edu        /* Add terminator */
2039537Satgutier@umich.edu        end = _fdt_grab_space(fdt, sizeof(*end));
2049537Satgutier@umich.edu        if (! end)
2059537Satgutier@umich.edu                return -FDT_ERR_NOSPACE;
2069537Satgutier@umich.edu        *end = cpu_to_fdt32(FDT_END);
2079537Satgutier@umich.edu
2089537Satgutier@umich.edu        /* Relocate the string table */
2099537Satgutier@umich.edu        oldstroffset = fdt_totalsize(fdt) - fdt_size_dt_strings(fdt);
2109537Satgutier@umich.edu        newstroffset = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt);
2119537Satgutier@umich.edu        memmove(p + newstroffset, p + oldstroffset, fdt_size_dt_strings(fdt));
2129537Satgutier@umich.edu        fdt_set_off_dt_strings(fdt, newstroffset);
2139537Satgutier@umich.edu
2149537Satgutier@umich.edu        /* Walk the structure, correcting string offsets */
2159537Satgutier@umich.edu        offset = 0;
2169537Satgutier@umich.edu        while ((tag = fdt_next_tag(fdt, offset, &nextoffset)) != FDT_END) {
2179537Satgutier@umich.edu                if (tag == FDT_PROP) {
2189537Satgutier@umich.edu                        struct fdt_property *prop =
2199537Satgutier@umich.edu                                _fdt_offset_ptr_w(fdt, offset);
2209537Satgutier@umich.edu                        int nameoff;
2219537Satgutier@umich.edu
2229537Satgutier@umich.edu                        nameoff = fdt32_to_cpu(prop->nameoff);
2239537Satgutier@umich.edu                        nameoff += fdt_size_dt_strings(fdt);
2249537Satgutier@umich.edu                        prop->nameoff = cpu_to_fdt32(nameoff);
2259537Satgutier@umich.edu                }
2269537Satgutier@umich.edu                offset = nextoffset;
2279537Satgutier@umich.edu        }
2289537Satgutier@umich.edu        if (nextoffset < 0)
2299537Satgutier@umich.edu                return nextoffset;
2309537Satgutier@umich.edu
2319537Satgutier@umich.edu        /* Finally, adjust the header */
2329537Satgutier@umich.edu        fdt_set_totalsize(fdt, newstroffset + fdt_size_dt_strings(fdt));
2339537Satgutier@umich.edu        fdt_set_magic(fdt, FDT_MAGIC);
2349537Satgutier@umich.edu        return 0;
2359537Satgutier@umich.edu}
236