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