1204431Sraj/* 2204431Sraj * libfdt - Flat Device Tree manipulation 3204431Sraj * Copyright (C) 2006 David Gibson, IBM Corporation. 4204431Sraj * 5204431Sraj * libfdt is dual licensed: you can use it either under the terms of 6204431Sraj * the GPL, or the BSD license, at your option. 7204431Sraj * 8204431Sraj * a) This library is free software; you can redistribute it and/or 9204431Sraj * modify it under the terms of the GNU General Public License as 10204431Sraj * published by the Free Software Foundation; either version 2 of the 11204431Sraj * License, or (at your option) any later version. 12204431Sraj * 13204431Sraj * This library is distributed in the hope that it will be useful, 14204431Sraj * but WITHOUT ANY WARRANTY; without even the implied warranty of 15204431Sraj * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16204431Sraj * GNU General Public License for more details. 17204431Sraj * 18204431Sraj * You should have received a copy of the GNU General Public 19204431Sraj * License along with this library; if not, write to the Free 20204431Sraj * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, 21204431Sraj * MA 02110-1301 USA 22204431Sraj * 23204431Sraj * Alternatively, 24204431Sraj * 25204431Sraj * b) Redistribution and use in source and binary forms, with or 26204431Sraj * without modification, are permitted provided that the following 27204431Sraj * conditions are met: 28204431Sraj * 29204431Sraj * 1. Redistributions of source code must retain the above 30204431Sraj * copyright notice, this list of conditions and the following 31204431Sraj * disclaimer. 32204431Sraj * 2. Redistributions in binary form must reproduce the above 33204431Sraj * copyright notice, this list of conditions and the following 34204431Sraj * disclaimer in the documentation and/or other materials 35204431Sraj * provided with the distribution. 36204431Sraj * 37204431Sraj * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 38204431Sraj * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 39204431Sraj * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 40204431Sraj * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 41204431Sraj * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 42204431Sraj * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 43204431Sraj * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 44204431Sraj * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 45204431Sraj * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 46204431Sraj * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 47204431Sraj * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 48204431Sraj * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 49204431Sraj * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 50204431Sraj */ 51204431Sraj#include "libfdt_env.h" 52204431Sraj 53204431Sraj#include <fdt.h> 54204431Sraj#include <libfdt.h> 55204431Sraj 56204431Sraj#include "libfdt_internal.h" 57204431Sraj 58328459Skevansstatic int fdt_sw_check_header_(void *fdt) 59204431Sraj{ 60204431Sraj if (fdt_magic(fdt) != FDT_SW_MAGIC) 61204431Sraj return -FDT_ERR_BADMAGIC; 62204431Sraj /* FIXME: should check more details about the header state */ 63204431Sraj return 0; 64204431Sraj} 65204431Sraj 66204431Sraj#define FDT_SW_CHECK_HEADER(fdt) \ 67204431Sraj { \ 68204431Sraj int err; \ 69328459Skevans if ((err = fdt_sw_check_header_(fdt)) != 0) \ 70204431Sraj return err; \ 71204431Sraj } 72204431Sraj 73328459Skevansstatic void *fdt_grab_space_(void *fdt, size_t len) 74204431Sraj{ 75204431Sraj int offset = fdt_size_dt_struct(fdt); 76204431Sraj int spaceleft; 77204431Sraj 78204431Sraj spaceleft = fdt_totalsize(fdt) - fdt_off_dt_struct(fdt) 79204431Sraj - fdt_size_dt_strings(fdt); 80204431Sraj 81204431Sraj if ((offset + len < offset) || (offset + len > spaceleft)) 82204431Sraj return NULL; 83204431Sraj 84204431Sraj fdt_set_size_dt_struct(fdt, offset + len); 85328459Skevans return fdt_offset_ptr_w_(fdt, offset); 86204431Sraj} 87204431Sraj 88204431Srajint fdt_create(void *buf, int bufsize) 89204431Sraj{ 90204431Sraj void *fdt = buf; 91204431Sraj 92204431Sraj if (bufsize < sizeof(struct fdt_header)) 93204431Sraj return -FDT_ERR_NOSPACE; 94204431Sraj 95204431Sraj memset(buf, 0, bufsize); 96204431Sraj 97204431Sraj fdt_set_magic(fdt, FDT_SW_MAGIC); 98204431Sraj fdt_set_version(fdt, FDT_LAST_SUPPORTED_VERSION); 99204431Sraj fdt_set_last_comp_version(fdt, FDT_FIRST_SUPPORTED_VERSION); 100204431Sraj fdt_set_totalsize(fdt, bufsize); 101204431Sraj 102204431Sraj fdt_set_off_mem_rsvmap(fdt, FDT_ALIGN(sizeof(struct fdt_header), 103204431Sraj sizeof(struct fdt_reserve_entry))); 104204431Sraj fdt_set_off_dt_struct(fdt, fdt_off_mem_rsvmap(fdt)); 105204431Sraj fdt_set_off_dt_strings(fdt, bufsize); 106204431Sraj 107204431Sraj return 0; 108204431Sraj} 109204431Sraj 110328459Skevansint fdt_resize(void *fdt, void *buf, int bufsize) 111328459Skevans{ 112328459Skevans size_t headsize, tailsize; 113328459Skevans char *oldtail, *newtail; 114328459Skevans 115328459Skevans FDT_SW_CHECK_HEADER(fdt); 116328459Skevans 117328459Skevans headsize = fdt_off_dt_struct(fdt); 118328459Skevans tailsize = fdt_size_dt_strings(fdt); 119328459Skevans 120328459Skevans if ((headsize + tailsize) > bufsize) 121328459Skevans return -FDT_ERR_NOSPACE; 122328459Skevans 123328459Skevans oldtail = (char *)fdt + fdt_totalsize(fdt) - tailsize; 124328459Skevans newtail = (char *)buf + bufsize - tailsize; 125328459Skevans 126328459Skevans /* Two cases to avoid clobbering data if the old and new 127328459Skevans * buffers partially overlap */ 128328459Skevans if (buf <= fdt) { 129328459Skevans memmove(buf, fdt, headsize); 130328459Skevans memmove(newtail, oldtail, tailsize); 131328459Skevans } else { 132328459Skevans memmove(newtail, oldtail, tailsize); 133328459Skevans memmove(buf, fdt, headsize); 134328459Skevans } 135328459Skevans 136328459Skevans fdt_set_off_dt_strings(buf, bufsize); 137328459Skevans fdt_set_totalsize(buf, bufsize); 138328459Skevans 139328459Skevans return 0; 140328459Skevans} 141328459Skevans 142204431Srajint fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size) 143204431Sraj{ 144204431Sraj struct fdt_reserve_entry *re; 145204431Sraj int offset; 146204431Sraj 147204431Sraj FDT_SW_CHECK_HEADER(fdt); 148204431Sraj 149204431Sraj if (fdt_size_dt_struct(fdt)) 150204431Sraj return -FDT_ERR_BADSTATE; 151204431Sraj 152204431Sraj offset = fdt_off_dt_struct(fdt); 153204431Sraj if ((offset + sizeof(*re)) > fdt_totalsize(fdt)) 154204431Sraj return -FDT_ERR_NOSPACE; 155204431Sraj 156204431Sraj re = (struct fdt_reserve_entry *)((char *)fdt + offset); 157204431Sraj re->address = cpu_to_fdt64(addr); 158204431Sraj re->size = cpu_to_fdt64(size); 159204431Sraj 160204431Sraj fdt_set_off_dt_struct(fdt, offset + sizeof(*re)); 161204431Sraj 162204431Sraj return 0; 163204431Sraj} 164204431Sraj 165204431Srajint fdt_finish_reservemap(void *fdt) 166204431Sraj{ 167204431Sraj return fdt_add_reservemap_entry(fdt, 0, 0); 168204431Sraj} 169204431Sraj 170204431Srajint fdt_begin_node(void *fdt, const char *name) 171204431Sraj{ 172204431Sraj struct fdt_node_header *nh; 173204431Sraj int namelen = strlen(name) + 1; 174204431Sraj 175204431Sraj FDT_SW_CHECK_HEADER(fdt); 176204431Sraj 177328459Skevans nh = fdt_grab_space_(fdt, sizeof(*nh) + FDT_TAGALIGN(namelen)); 178204431Sraj if (! nh) 179204431Sraj return -FDT_ERR_NOSPACE; 180204431Sraj 181204431Sraj nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE); 182204431Sraj memcpy(nh->name, name, namelen); 183204431Sraj return 0; 184204431Sraj} 185204431Sraj 186204431Srajint fdt_end_node(void *fdt) 187204431Sraj{ 188328459Skevans fdt32_t *en; 189204431Sraj 190204431Sraj FDT_SW_CHECK_HEADER(fdt); 191204431Sraj 192328459Skevans en = fdt_grab_space_(fdt, FDT_TAGSIZE); 193204431Sraj if (! en) 194204431Sraj return -FDT_ERR_NOSPACE; 195204431Sraj 196204431Sraj *en = cpu_to_fdt32(FDT_END_NODE); 197204431Sraj return 0; 198204431Sraj} 199204431Sraj 200328459Skevansstatic int fdt_find_add_string_(void *fdt, const char *s) 201204431Sraj{ 202204431Sraj char *strtab = (char *)fdt + fdt_totalsize(fdt); 203204431Sraj const char *p; 204204431Sraj int strtabsize = fdt_size_dt_strings(fdt); 205204431Sraj int len = strlen(s) + 1; 206204431Sraj int struct_top, offset; 207204431Sraj 208328459Skevans p = fdt_find_string_(strtab - strtabsize, strtabsize, s); 209204431Sraj if (p) 210204431Sraj return p - strtab; 211204431Sraj 212204431Sraj /* Add it */ 213204431Sraj offset = -strtabsize - len; 214204431Sraj struct_top = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt); 215204431Sraj if (fdt_totalsize(fdt) + offset < struct_top) 216204431Sraj return 0; /* no more room :( */ 217204431Sraj 218204431Sraj memcpy(strtab + offset, s, len); 219204431Sraj fdt_set_size_dt_strings(fdt, strtabsize + len); 220204431Sraj return offset; 221204431Sraj} 222204431Sraj 223328459Skevansint fdt_property_placeholder(void *fdt, const char *name, int len, void **valp) 224204431Sraj{ 225204431Sraj struct fdt_property *prop; 226204431Sraj int nameoff; 227204431Sraj 228204431Sraj FDT_SW_CHECK_HEADER(fdt); 229204431Sraj 230328459Skevans nameoff = fdt_find_add_string_(fdt, name); 231204431Sraj if (nameoff == 0) 232204431Sraj return -FDT_ERR_NOSPACE; 233204431Sraj 234328459Skevans prop = fdt_grab_space_(fdt, sizeof(*prop) + FDT_TAGALIGN(len)); 235204431Sraj if (! prop) 236204431Sraj return -FDT_ERR_NOSPACE; 237204431Sraj 238204431Sraj prop->tag = cpu_to_fdt32(FDT_PROP); 239204431Sraj prop->nameoff = cpu_to_fdt32(nameoff); 240204431Sraj prop->len = cpu_to_fdt32(len); 241328459Skevans *valp = prop->data; 242204431Sraj return 0; 243204431Sraj} 244204431Sraj 245328459Skevansint fdt_property(void *fdt, const char *name, const void *val, int len) 246328459Skevans{ 247328459Skevans void *ptr; 248328459Skevans int ret; 249328459Skevans 250328459Skevans ret = fdt_property_placeholder(fdt, name, len, &ptr); 251328459Skevans if (ret) 252328459Skevans return ret; 253328459Skevans memcpy(ptr, val, len); 254328459Skevans return 0; 255328459Skevans} 256328459Skevans 257204431Srajint fdt_finish(void *fdt) 258204431Sraj{ 259204431Sraj char *p = (char *)fdt; 260328459Skevans fdt32_t *end; 261204431Sraj int oldstroffset, newstroffset; 262204431Sraj uint32_t tag; 263204431Sraj int offset, nextoffset; 264204431Sraj 265204431Sraj FDT_SW_CHECK_HEADER(fdt); 266204431Sraj 267204431Sraj /* Add terminator */ 268328459Skevans end = fdt_grab_space_(fdt, sizeof(*end)); 269204431Sraj if (! end) 270204431Sraj return -FDT_ERR_NOSPACE; 271204431Sraj *end = cpu_to_fdt32(FDT_END); 272204431Sraj 273204431Sraj /* Relocate the string table */ 274204431Sraj oldstroffset = fdt_totalsize(fdt) - fdt_size_dt_strings(fdt); 275204431Sraj newstroffset = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt); 276204431Sraj memmove(p + newstroffset, p + oldstroffset, fdt_size_dt_strings(fdt)); 277204431Sraj fdt_set_off_dt_strings(fdt, newstroffset); 278204431Sraj 279204431Sraj /* Walk the structure, correcting string offsets */ 280204431Sraj offset = 0; 281204431Sraj while ((tag = fdt_next_tag(fdt, offset, &nextoffset)) != FDT_END) { 282204431Sraj if (tag == FDT_PROP) { 283204431Sraj struct fdt_property *prop = 284328459Skevans fdt_offset_ptr_w_(fdt, offset); 285204431Sraj int nameoff; 286204431Sraj 287204431Sraj nameoff = fdt32_to_cpu(prop->nameoff); 288204431Sraj nameoff += fdt_size_dt_strings(fdt); 289204431Sraj prop->nameoff = cpu_to_fdt32(nameoff); 290204431Sraj } 291204431Sraj offset = nextoffset; 292204431Sraj } 293204433Sraj if (nextoffset < 0) 294204433Sraj return nextoffset; 295204431Sraj 296204431Sraj /* Finally, adjust the header */ 297204431Sraj fdt_set_totalsize(fdt, newstroffset + fdt_size_dt_strings(fdt)); 298204431Sraj fdt_set_magic(fdt, FDT_MAGIC); 299204431Sraj return 0; 300204431Sraj} 301