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