fdt_sw.c revision 266130
1183270Sobrien/* 2183270Sobrien * libfdt - Flat Device Tree manipulation 3183270Sobrien * Copyright (C) 2006 David Gibson, IBM Corporation. 4183270Sobrien * 5183270Sobrien * libfdt is dual licensed: you can use it either under the terms of 6183270Sobrien * the GPL, or the BSD license, at your option. 7183270Sobrien * 8183270Sobrien * a) This library is free software; you can redistribute it and/or 9183270Sobrien * modify it under the terms of the GNU General Public License as 10183270Sobrien * published by the Free Software Foundation; either version 2 of the 11183270Sobrien * License, or (at your option) any later version. 12183270Sobrien * 13183270Sobrien * This library is distributed in the hope that it will be useful, 14183270Sobrien * but WITHOUT ANY WARRANTY; without even the implied warranty of 15183270Sobrien * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16183270Sobrien * GNU General Public License for more details. 17183270Sobrien * 18183270Sobrien * You should have received a copy of the GNU General Public 19183270Sobrien * License along with this library; if not, write to the Free 20183270Sobrien * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, 21183270Sobrien * MA 02110-1301 USA 22183270Sobrien * 23183270Sobrien * Alternatively, 24183270Sobrien * 25183270Sobrien * b) Redistribution and use in source and binary forms, with or 26183270Sobrien * without modification, are permitted provided that the following 27183270Sobrien * conditions are met: 28183270Sobrien * 29183270Sobrien * 1. Redistributions of source code must retain the above 30183270Sobrien * copyright notice, this list of conditions and the following 31183270Sobrien * disclaimer. 32183270Sobrien * 2. Redistributions in binary form must reproduce the above 33183270Sobrien * copyright notice, this list of conditions and the following 34183270Sobrien * disclaimer in the documentation and/or other materials 35183270Sobrien * provided with the distribution. 36224778Srwatson * 37183270Sobrien * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 38183270Sobrien * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 39190529Sed * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 40183270Sobrien * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 41183270Sobrien * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 42219989Skib * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 43183270Sobrien * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 44219989Skib * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 45220280Skib * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 46183270Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 47183270Sobrien * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 48183270Sobrien * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 49183270Sobrien * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 50183270Sobrien */ 51183270Sobrien#include "libfdt_env.h" 52183270Sobrien 53183270Sobrien#include <fdt.h> 54183270Sobrien#include <libfdt.h> 55183270Sobrien 56183270Sobrien#include "libfdt_internal.h" 57183270Sobrien 58183270Sobrienstatic int _fdt_sw_check_header(void *fdt) 59183270Sobrien{ 60183273Sobrien if (fdt_magic(fdt) != FDT_SW_MAGIC) 61183273Sobrien return -FDT_ERR_BADMAGIC; 62219989Skib /* FIXME: should check more details about the header state */ 63220280Skib return 0; 64220280Skib} 65220280Skib 66183270Sobrien#define FDT_SW_CHECK_HEADER(fdt) \ 67183270Sobrien { \ 68183270Sobrien int err; \ 69183270Sobrien if ((err = _fdt_sw_check_header(fdt)) != 0) \ 70183270Sobrien return err; \ 71183270Sobrien } 72183270Sobrien 73183270Sobrienstatic void *_fdt_grab_space(void *fdt, size_t len) 74183270Sobrien{ 75219988Skib int offset = fdt_size_dt_struct(fdt); 76183270Sobrien int spaceleft; 77183270Sobrien 78183270Sobrien spaceleft = fdt_totalsize(fdt) - fdt_off_dt_struct(fdt) 79183270Sobrien - fdt_size_dt_strings(fdt); 80183270Sobrien 81183270Sobrien if ((offset + len < offset) || (offset + len > spaceleft)) 82183270Sobrien return NULL; 83183270Sobrien 84183270Sobrien fdt_set_size_dt_struct(fdt, offset + len); 85183270Sobrien return _fdt_offset_ptr_w(fdt, offset); 86183270Sobrien} 87183270Sobrien 88183270Sobrienint fdt_create(void *buf, int bufsize) 89183270Sobrien{ 90183270Sobrien void *fdt = buf; 91183270Sobrien 92183270Sobrien if (bufsize < sizeof(struct fdt_header)) 93183270Sobrien return -FDT_ERR_NOSPACE; 94183270Sobrien 95183270Sobrien memset(buf, 0, bufsize); 96183270Sobrien 97183270Sobrien fdt_set_magic(fdt, FDT_SW_MAGIC); 98183270Sobrien fdt_set_version(fdt, FDT_LAST_SUPPORTED_VERSION); 99183270Sobrien fdt_set_last_comp_version(fdt, FDT_FIRST_SUPPORTED_VERSION); 100183270Sobrien fdt_set_totalsize(fdt, bufsize); 101183270Sobrien 102183270Sobrien fdt_set_off_mem_rsvmap(fdt, FDT_ALIGN(sizeof(struct fdt_header), 103183270Sobrien sizeof(struct fdt_reserve_entry))); 104183270Sobrien fdt_set_off_dt_struct(fdt, fdt_off_mem_rsvmap(fdt)); 105183270Sobrien fdt_set_off_dt_strings(fdt, bufsize); 106183270Sobrien 107183270Sobrien return 0; 108183270Sobrien} 109183270Sobrien 110183270Sobrienint fdt_resize(void *fdt, void *buf, int bufsize) 111183270Sobrien{ 112183270Sobrien size_t headsize, tailsize; 113183270Sobrien char *oldtail, *newtail; 114183270Sobrien 115183270Sobrien FDT_SW_CHECK_HEADER(fdt); 116183270Sobrien 117183270Sobrien headsize = fdt_off_dt_struct(fdt); 118183270Sobrien tailsize = fdt_size_dt_strings(fdt); 119183270Sobrien 120183270Sobrien if ((headsize + tailsize) > bufsize) 121183270Sobrien return -FDT_ERR_NOSPACE; 122183270Sobrien 123183270Sobrien oldtail = (char *)fdt + fdt_totalsize(fdt) - tailsize; 124183270Sobrien newtail = (char *)buf + bufsize - tailsize; 125183270Sobrien 126183270Sobrien /* Two cases to avoid clobbering data if the old and new 127219988Skib * buffers partially overlap */ 128219988Skib if (buf <= fdt) { 129219988Skib memmove(buf, fdt, headsize); 130219988Skib memmove(newtail, oldtail, tailsize); 131219988Skib } else { 132219988Skib memmove(newtail, oldtail, tailsize); 133219988Skib memmove(buf, fdt, headsize); 134219988Skib } 135183270Sobrien 136183270Sobrien fdt_set_off_dt_strings(buf, bufsize); 137183270Sobrien fdt_set_totalsize(buf, bufsize); 138183270Sobrien 139183270Sobrien return 0; 140183270Sobrien} 141183273Sobrien 142183273Sobrienint fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size) 143183273Sobrien{ 144183273Sobrien struct fdt_reserve_entry *re; 145183273Sobrien int offset; 146183273Sobrien 147183273Sobrien FDT_SW_CHECK_HEADER(fdt); 148183273Sobrien 149183273Sobrien if (fdt_size_dt_struct(fdt)) 150183273Sobrien return -FDT_ERR_BADSTATE; 151183273Sobrien 152183273Sobrien offset = fdt_off_dt_struct(fdt); 153183273Sobrien if ((offset + sizeof(*re)) > fdt_totalsize(fdt)) 154183273Sobrien return -FDT_ERR_NOSPACE; 155183273Sobrien 156183273Sobrien re = (struct fdt_reserve_entry *)((char *)fdt + offset); 157183273Sobrien re->address = cpu_to_fdt64(addr); 158183273Sobrien re->size = cpu_to_fdt64(size); 159183273Sobrien 160183273Sobrien fdt_set_off_dt_struct(fdt, offset + sizeof(*re)); 161183273Sobrien 162183273Sobrien return 0; 163183273Sobrien} 164183273Sobrien 165183273Sobrienint fdt_finish_reservemap(void *fdt) 166183273Sobrien{ 167183273Sobrien return fdt_add_reservemap_entry(fdt, 0, 0); 168183273Sobrien} 169183273Sobrien 170183273Sobrienint fdt_begin_node(void *fdt, const char *name) 171183273Sobrien{ 172183273Sobrien struct fdt_node_header *nh; 173183273Sobrien int namelen = strlen(name) + 1; 174183273Sobrien 175183273Sobrien FDT_SW_CHECK_HEADER(fdt); 176183273Sobrien 177183273Sobrien nh = _fdt_grab_space(fdt, sizeof(*nh) + FDT_TAGALIGN(namelen)); 178183273Sobrien if (! nh) 179183273Sobrien return -FDT_ERR_NOSPACE; 180183273Sobrien 181183273Sobrien nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE); 182183273Sobrien memcpy(nh->name, name, namelen); 183183273Sobrien return 0; 184183273Sobrien} 185183273Sobrien 186190529Sedint fdt_end_node(void *fdt) 187190529Sed{ 188190529Sed fdt32_t *en; 189190529Sed 190190529Sed FDT_SW_CHECK_HEADER(fdt); 191190529Sed 192190529Sed en = _fdt_grab_space(fdt, FDT_TAGSIZE); 193183273Sobrien if (! en) 194190529Sed return -FDT_ERR_NOSPACE; 195190529Sed 196190529Sed *en = cpu_to_fdt32(FDT_END_NODE); 197190529Sed return 0; 198190529Sed} 199190529Sed 200190529Sedstatic int _fdt_find_add_string(void *fdt, const char *s) 201190529Sed{ 202219989Skib char *strtab = (char *)fdt + fdt_totalsize(fdt); 203219989Skib const char *p; 204219989Skib int strtabsize = fdt_size_dt_strings(fdt); 205219989Skib int len = strlen(s) + 1; 206219989Skib int struct_top, offset; 207219989Skib 208219989Skib p = _fdt_find_string(strtab - strtabsize, strtabsize, s); 209219989Skib if (p) 210219989Skib return p - strtab; 211219989Skib 212219989Skib /* Add it */ 213219989Skib offset = -strtabsize - len; 214219989Skib struct_top = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt); 215219989Skib if (fdt_totalsize(fdt) + offset < struct_top) 216219989Skib return 0; /* no more room :( */ 217219989Skib 218219989Skib memcpy(strtab + offset, s, len); 219219989Skib fdt_set_size_dt_strings(fdt, strtabsize + len); 220219989Skib return offset; 221219989Skib} 222219989Skib 223219989Skibint fdt_property(void *fdt, const char *name, const void *val, int len) 224219989Skib{ 225219989Skib struct fdt_property *prop; 226219989Skib int nameoff; 227219989Skib 228219989Skib FDT_SW_CHECK_HEADER(fdt); 229219989Skib 230219989Skib nameoff = _fdt_find_add_string(fdt, name); 231219989Skib if (nameoff == 0) 232219989Skib return -FDT_ERR_NOSPACE; 233219989Skib 234219989Skib prop = _fdt_grab_space(fdt, sizeof(*prop) + FDT_TAGALIGN(len)); 235219989Skib if (! prop) 236219989Skib return -FDT_ERR_NOSPACE; 237219989Skib 238219989Skib prop->tag = cpu_to_fdt32(FDT_PROP); 239219989Skib prop->nameoff = cpu_to_fdt32(nameoff); 240219989Skib prop->len = cpu_to_fdt32(len); 241219989Skib memcpy(prop->data, val, len); 242219989Skib return 0; 243219989Skib} 244219989Skib 245220281Skibint fdt_finish(void *fdt) 246220281Skib{ 247220281Skib char *p = (char *)fdt; 248220281Skib fdt32_t *end; 249220281Skib int oldstroffset, newstroffset; 250220281Skib uint32_t tag; 251220281Skib int offset, nextoffset; 252220281Skib 253220281Skib FDT_SW_CHECK_HEADER(fdt); 254220281Skib 255220281Skib /* Add terminator */ 256220281Skib end = _fdt_grab_space(fdt, sizeof(*end)); 257220281Skib if (! end) 258220281Skib return -FDT_ERR_NOSPACE; 259220281Skib *end = cpu_to_fdt32(FDT_END); 260220281Skib 261220281Skib /* Relocate the string table */ 262220281Skib oldstroffset = fdt_totalsize(fdt) - fdt_size_dt_strings(fdt); 263220281Skib newstroffset = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt); 264220281Skib memmove(p + newstroffset, p + oldstroffset, fdt_size_dt_strings(fdt)); 265220281Skib fdt_set_off_dt_strings(fdt, newstroffset); 266220281Skib 267220281Skib /* Walk the structure, correcting string offsets */ 268220281Skib offset = 0; 269220281Skib while ((tag = fdt_next_tag(fdt, offset, &nextoffset)) != FDT_END) { 270220281Skib if (tag == FDT_PROP) { 271220281Skib struct fdt_property *prop = 272220281Skib _fdt_offset_ptr_w(fdt, offset); 273220281Skib int nameoff; 274220281Skib 275220281Skib nameoff = fdt32_to_cpu(prop->nameoff); 276220281Skib nameoff += fdt_size_dt_strings(fdt); 277220281Skib prop->nameoff = cpu_to_fdt32(nameoff); 278220281Skib } 279220281Skib offset = nextoffset; 280220281Skib } 281220281Skib if (nextoffset < 0) 282220281Skib return nextoffset; 283220281Skib 284220281Skib /* Finally, adjust the header */ 285220281Skib fdt_set_totalsize(fdt, newstroffset + fdt_size_dt_strings(fdt)); 286220281Skib fdt_set_magic(fdt, FDT_MAGIC); 287220281Skib return 0; 288220281Skib} 289220281Skib