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