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 58204431Srajstatic 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; \ 69204431Sraj if ((err = _fdt_sw_check_header(fdt)) != 0) \ 70204431Sraj return err; \ 71204431Sraj } 72204431Sraj 73204433Srajstatic 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); 85204433Sraj 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 110204431Srajint fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size) 111204431Sraj{ 112204431Sraj struct fdt_reserve_entry *re; 113204431Sraj int offset; 114204431Sraj 115204431Sraj FDT_SW_CHECK_HEADER(fdt); 116204431Sraj 117204431Sraj if (fdt_size_dt_struct(fdt)) 118204431Sraj return -FDT_ERR_BADSTATE; 119204431Sraj 120204431Sraj offset = fdt_off_dt_struct(fdt); 121204431Sraj if ((offset + sizeof(*re)) > fdt_totalsize(fdt)) 122204431Sraj return -FDT_ERR_NOSPACE; 123204431Sraj 124204431Sraj re = (struct fdt_reserve_entry *)((char *)fdt + offset); 125204431Sraj re->address = cpu_to_fdt64(addr); 126204431Sraj re->size = cpu_to_fdt64(size); 127204431Sraj 128204431Sraj fdt_set_off_dt_struct(fdt, offset + sizeof(*re)); 129204431Sraj 130204431Sraj return 0; 131204431Sraj} 132204431Sraj 133204431Srajint fdt_finish_reservemap(void *fdt) 134204431Sraj{ 135204431Sraj return fdt_add_reservemap_entry(fdt, 0, 0); 136204431Sraj} 137204431Sraj 138204431Srajint fdt_begin_node(void *fdt, const char *name) 139204431Sraj{ 140204431Sraj struct fdt_node_header *nh; 141204431Sraj int namelen = strlen(name) + 1; 142204431Sraj 143204431Sraj FDT_SW_CHECK_HEADER(fdt); 144204431Sraj 145204431Sraj nh = _fdt_grab_space(fdt, sizeof(*nh) + FDT_TAGALIGN(namelen)); 146204431Sraj if (! nh) 147204431Sraj return -FDT_ERR_NOSPACE; 148204431Sraj 149204431Sraj nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE); 150204431Sraj memcpy(nh->name, name, namelen); 151204431Sraj return 0; 152204431Sraj} 153204431Sraj 154204431Srajint fdt_end_node(void *fdt) 155204431Sraj{ 156204431Sraj uint32_t *en; 157204431Sraj 158204431Sraj FDT_SW_CHECK_HEADER(fdt); 159204431Sraj 160204431Sraj en = _fdt_grab_space(fdt, FDT_TAGSIZE); 161204431Sraj if (! en) 162204431Sraj return -FDT_ERR_NOSPACE; 163204431Sraj 164204431Sraj *en = cpu_to_fdt32(FDT_END_NODE); 165204431Sraj return 0; 166204431Sraj} 167204431Sraj 168204431Srajstatic int _fdt_find_add_string(void *fdt, const char *s) 169204431Sraj{ 170204431Sraj char *strtab = (char *)fdt + fdt_totalsize(fdt); 171204431Sraj const char *p; 172204431Sraj int strtabsize = fdt_size_dt_strings(fdt); 173204431Sraj int len = strlen(s) + 1; 174204431Sraj int struct_top, offset; 175204431Sraj 176204431Sraj p = _fdt_find_string(strtab - strtabsize, strtabsize, s); 177204431Sraj if (p) 178204431Sraj return p - strtab; 179204431Sraj 180204431Sraj /* Add it */ 181204431Sraj offset = -strtabsize - len; 182204431Sraj struct_top = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt); 183204431Sraj if (fdt_totalsize(fdt) + offset < struct_top) 184204431Sraj return 0; /* no more room :( */ 185204431Sraj 186204431Sraj memcpy(strtab + offset, s, len); 187204431Sraj fdt_set_size_dt_strings(fdt, strtabsize + len); 188204431Sraj return offset; 189204431Sraj} 190204431Sraj 191204431Srajint fdt_property(void *fdt, const char *name, const void *val, int len) 192204431Sraj{ 193204431Sraj struct fdt_property *prop; 194204431Sraj int nameoff; 195204431Sraj 196204431Sraj FDT_SW_CHECK_HEADER(fdt); 197204431Sraj 198204431Sraj nameoff = _fdt_find_add_string(fdt, name); 199204431Sraj if (nameoff == 0) 200204431Sraj return -FDT_ERR_NOSPACE; 201204431Sraj 202204431Sraj prop = _fdt_grab_space(fdt, sizeof(*prop) + FDT_TAGALIGN(len)); 203204431Sraj if (! prop) 204204431Sraj return -FDT_ERR_NOSPACE; 205204431Sraj 206204431Sraj prop->tag = cpu_to_fdt32(FDT_PROP); 207204431Sraj prop->nameoff = cpu_to_fdt32(nameoff); 208204431Sraj prop->len = cpu_to_fdt32(len); 209204431Sraj memcpy(prop->data, val, len); 210204431Sraj return 0; 211204431Sraj} 212204431Sraj 213204431Srajint fdt_finish(void *fdt) 214204431Sraj{ 215204431Sraj char *p = (char *)fdt; 216204431Sraj uint32_t *end; 217204431Sraj int oldstroffset, newstroffset; 218204431Sraj uint32_t tag; 219204431Sraj int offset, nextoffset; 220204431Sraj 221204431Sraj FDT_SW_CHECK_HEADER(fdt); 222204431Sraj 223204431Sraj /* Add terminator */ 224204431Sraj end = _fdt_grab_space(fdt, sizeof(*end)); 225204431Sraj if (! end) 226204431Sraj return -FDT_ERR_NOSPACE; 227204431Sraj *end = cpu_to_fdt32(FDT_END); 228204431Sraj 229204431Sraj /* Relocate the string table */ 230204431Sraj oldstroffset = fdt_totalsize(fdt) - fdt_size_dt_strings(fdt); 231204431Sraj newstroffset = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt); 232204431Sraj memmove(p + newstroffset, p + oldstroffset, fdt_size_dt_strings(fdt)); 233204431Sraj fdt_set_off_dt_strings(fdt, newstroffset); 234204431Sraj 235204431Sraj /* Walk the structure, correcting string offsets */ 236204431Sraj offset = 0; 237204431Sraj while ((tag = fdt_next_tag(fdt, offset, &nextoffset)) != FDT_END) { 238204431Sraj if (tag == FDT_PROP) { 239204431Sraj struct fdt_property *prop = 240204433Sraj _fdt_offset_ptr_w(fdt, offset); 241204431Sraj int nameoff; 242204431Sraj 243204431Sraj nameoff = fdt32_to_cpu(prop->nameoff); 244204431Sraj nameoff += fdt_size_dt_strings(fdt); 245204431Sraj prop->nameoff = cpu_to_fdt32(nameoff); 246204431Sraj } 247204431Sraj offset = nextoffset; 248204431Sraj } 249204433Sraj if (nextoffset < 0) 250204433Sraj return nextoffset; 251204431Sraj 252204431Sraj /* Finally, adjust the header */ 253204431Sraj fdt_set_totalsize(fdt, newstroffset + fdt_size_dt_strings(fdt)); 254204431Sraj fdt_set_magic(fdt, FDT_MAGIC); 255204431Sraj return 0; 256204431Sraj} 257