1630b011fSAntonio Nino Diaz /* 2630b011fSAntonio Nino Diaz * libfdt - Flat Device Tree manipulation 3630b011fSAntonio Nino Diaz * Copyright (C) 2006 David Gibson, IBM Corporation. 4630b011fSAntonio Nino Diaz * 5630b011fSAntonio Nino Diaz * libfdt is dual licensed: you can use it either under the terms of 6630b011fSAntonio Nino Diaz * the GPL, or the BSD license, at your option. 7630b011fSAntonio Nino Diaz * 8630b011fSAntonio Nino Diaz * a) This library is free software; you can redistribute it and/or 9630b011fSAntonio Nino Diaz * modify it under the terms of the GNU General Public License as 10630b011fSAntonio Nino Diaz * published by the Free Software Foundation; either version 2 of the 11630b011fSAntonio Nino Diaz * License, or (at your option) any later version. 12630b011fSAntonio Nino Diaz * 13630b011fSAntonio Nino Diaz * This library is distributed in the hope that it will be useful, 14630b011fSAntonio Nino Diaz * but WITHOUT ANY WARRANTY; without even the implied warranty of 15630b011fSAntonio Nino Diaz * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16630b011fSAntonio Nino Diaz * GNU General Public License for more details. 17630b011fSAntonio Nino Diaz * 18630b011fSAntonio Nino Diaz * You should have received a copy of the GNU General Public 19630b011fSAntonio Nino Diaz * License along with this library; if not, write to the Free 20630b011fSAntonio Nino Diaz * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, 21630b011fSAntonio Nino Diaz * MA 02110-1301 USA 22630b011fSAntonio Nino Diaz * 23630b011fSAntonio Nino Diaz * Alternatively, 24630b011fSAntonio Nino Diaz * 25630b011fSAntonio Nino Diaz * b) Redistribution and use in source and binary forms, with or 26630b011fSAntonio Nino Diaz * without modification, are permitted provided that the following 27630b011fSAntonio Nino Diaz * conditions are met: 28630b011fSAntonio Nino Diaz * 29630b011fSAntonio Nino Diaz * 1. Redistributions of source code must retain the above 30630b011fSAntonio Nino Diaz * copyright notice, this list of conditions and the following 31630b011fSAntonio Nino Diaz * disclaimer. 32630b011fSAntonio Nino Diaz * 2. Redistributions in binary form must reproduce the above 33630b011fSAntonio Nino Diaz * copyright notice, this list of conditions and the following 34630b011fSAntonio Nino Diaz * disclaimer in the documentation and/or other materials 35630b011fSAntonio Nino Diaz * provided with the distribution. 36630b011fSAntonio Nino Diaz * 37630b011fSAntonio Nino Diaz * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 38630b011fSAntonio Nino Diaz * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 39630b011fSAntonio Nino Diaz * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 40630b011fSAntonio Nino Diaz * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 41630b011fSAntonio Nino Diaz * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 42630b011fSAntonio Nino Diaz * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 43630b011fSAntonio Nino Diaz * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 44630b011fSAntonio Nino Diaz * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 45630b011fSAntonio Nino Diaz * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 46630b011fSAntonio Nino Diaz * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 47630b011fSAntonio Nino Diaz * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 48630b011fSAntonio Nino Diaz * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 49630b011fSAntonio Nino Diaz * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 50630b011fSAntonio Nino Diaz */ 51630b011fSAntonio Nino Diaz #include "libfdt_env.h" 52630b011fSAntonio Nino Diaz 53630b011fSAntonio Nino Diaz #include <fdt.h> 54630b011fSAntonio Nino Diaz #include <libfdt.h> 55630b011fSAntonio Nino Diaz 56630b011fSAntonio Nino Diaz #include "libfdt_internal.h" 57630b011fSAntonio Nino Diaz 58*00f588bfSAntonio Nino Diaz static int fdt_sw_check_header_(void *fdt) 59630b011fSAntonio Nino Diaz { 60*00f588bfSAntonio Nino Diaz if (fdt_magic(fdt) != FDT_SW_MAGIC) 61630b011fSAntonio Nino Diaz return -FDT_ERR_BADMAGIC; 62*00f588bfSAntonio Nino Diaz /* FIXME: should check more details about the header state */ 63630b011fSAntonio Nino Diaz return 0; 64630b011fSAntonio Nino Diaz } 65630b011fSAntonio Nino Diaz 66*00f588bfSAntonio Nino Diaz #define FDT_SW_CHECK_HEADER(fdt) \ 67630b011fSAntonio Nino Diaz { \ 68630b011fSAntonio Nino Diaz int err; \ 69*00f588bfSAntonio Nino Diaz if ((err = fdt_sw_check_header_(fdt)) != 0) \ 70630b011fSAntonio Nino Diaz return err; \ 71630b011fSAntonio Nino Diaz } 72630b011fSAntonio Nino Diaz 73630b011fSAntonio Nino Diaz static void *fdt_grab_space_(void *fdt, size_t len) 74630b011fSAntonio Nino Diaz { 75630b011fSAntonio Nino Diaz int offset = fdt_size_dt_struct(fdt); 76630b011fSAntonio Nino Diaz int spaceleft; 77630b011fSAntonio Nino Diaz 78630b011fSAntonio Nino Diaz spaceleft = fdt_totalsize(fdt) - fdt_off_dt_struct(fdt) 79630b011fSAntonio Nino Diaz - fdt_size_dt_strings(fdt); 80630b011fSAntonio Nino Diaz 81630b011fSAntonio Nino Diaz if ((offset + len < offset) || (offset + len > spaceleft)) 82630b011fSAntonio Nino Diaz return NULL; 83630b011fSAntonio Nino Diaz 84630b011fSAntonio Nino Diaz fdt_set_size_dt_struct(fdt, offset + len); 85630b011fSAntonio Nino Diaz return fdt_offset_ptr_w_(fdt, offset); 86630b011fSAntonio Nino Diaz } 87630b011fSAntonio Nino Diaz 88630b011fSAntonio Nino Diaz int fdt_create(void *buf, int bufsize) 89630b011fSAntonio Nino Diaz { 90630b011fSAntonio Nino Diaz void *fdt = buf; 91630b011fSAntonio Nino Diaz 92*00f588bfSAntonio Nino Diaz if (bufsize < sizeof(struct fdt_header)) 93630b011fSAntonio Nino Diaz return -FDT_ERR_NOSPACE; 94630b011fSAntonio Nino Diaz 95630b011fSAntonio Nino Diaz memset(buf, 0, bufsize); 96630b011fSAntonio Nino Diaz 97630b011fSAntonio Nino Diaz fdt_set_magic(fdt, FDT_SW_MAGIC); 98630b011fSAntonio Nino Diaz fdt_set_version(fdt, FDT_LAST_SUPPORTED_VERSION); 99630b011fSAntonio Nino Diaz fdt_set_last_comp_version(fdt, FDT_FIRST_SUPPORTED_VERSION); 100630b011fSAntonio Nino Diaz fdt_set_totalsize(fdt, bufsize); 101630b011fSAntonio Nino Diaz 102*00f588bfSAntonio Nino Diaz fdt_set_off_mem_rsvmap(fdt, FDT_ALIGN(sizeof(struct fdt_header), 103*00f588bfSAntonio Nino Diaz sizeof(struct fdt_reserve_entry))); 104630b011fSAntonio Nino Diaz fdt_set_off_dt_struct(fdt, fdt_off_mem_rsvmap(fdt)); 105*00f588bfSAntonio Nino Diaz fdt_set_off_dt_strings(fdt, bufsize); 106630b011fSAntonio Nino Diaz 107630b011fSAntonio Nino Diaz return 0; 108630b011fSAntonio Nino Diaz } 109630b011fSAntonio Nino Diaz 110630b011fSAntonio Nino Diaz int fdt_resize(void *fdt, void *buf, int bufsize) 111630b011fSAntonio Nino Diaz { 112630b011fSAntonio Nino Diaz size_t headsize, tailsize; 113630b011fSAntonio Nino Diaz char *oldtail, *newtail; 114630b011fSAntonio Nino Diaz 115*00f588bfSAntonio Nino Diaz FDT_SW_CHECK_HEADER(fdt); 116630b011fSAntonio Nino Diaz 117*00f588bfSAntonio Nino Diaz headsize = fdt_off_dt_struct(fdt); 118630b011fSAntonio Nino Diaz tailsize = fdt_size_dt_strings(fdt); 119630b011fSAntonio Nino Diaz 120630b011fSAntonio Nino Diaz if ((headsize + tailsize) > bufsize) 121630b011fSAntonio Nino Diaz return -FDT_ERR_NOSPACE; 122630b011fSAntonio Nino Diaz 123630b011fSAntonio Nino Diaz oldtail = (char *)fdt + fdt_totalsize(fdt) - tailsize; 124630b011fSAntonio Nino Diaz newtail = (char *)buf + bufsize - tailsize; 125630b011fSAntonio Nino Diaz 126630b011fSAntonio Nino Diaz /* Two cases to avoid clobbering data if the old and new 127630b011fSAntonio Nino Diaz * buffers partially overlap */ 128630b011fSAntonio Nino Diaz if (buf <= fdt) { 129630b011fSAntonio Nino Diaz memmove(buf, fdt, headsize); 130630b011fSAntonio Nino Diaz memmove(newtail, oldtail, tailsize); 131630b011fSAntonio Nino Diaz } else { 132630b011fSAntonio Nino Diaz memmove(newtail, oldtail, tailsize); 133630b011fSAntonio Nino Diaz memmove(buf, fdt, headsize); 134630b011fSAntonio Nino Diaz } 135630b011fSAntonio Nino Diaz 136630b011fSAntonio Nino Diaz fdt_set_off_dt_strings(buf, bufsize); 137*00f588bfSAntonio Nino Diaz fdt_set_totalsize(buf, bufsize); 138630b011fSAntonio Nino Diaz 139630b011fSAntonio Nino Diaz return 0; 140630b011fSAntonio Nino Diaz } 141630b011fSAntonio Nino Diaz 142630b011fSAntonio Nino Diaz int fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size) 143630b011fSAntonio Nino Diaz { 144630b011fSAntonio Nino Diaz struct fdt_reserve_entry *re; 145630b011fSAntonio Nino Diaz int offset; 146630b011fSAntonio Nino Diaz 147*00f588bfSAntonio Nino Diaz FDT_SW_CHECK_HEADER(fdt); 148*00f588bfSAntonio Nino Diaz 149*00f588bfSAntonio Nino Diaz if (fdt_size_dt_struct(fdt)) 150*00f588bfSAntonio Nino Diaz return -FDT_ERR_BADSTATE; 151630b011fSAntonio Nino Diaz 152630b011fSAntonio Nino Diaz offset = fdt_off_dt_struct(fdt); 153630b011fSAntonio Nino Diaz if ((offset + sizeof(*re)) > fdt_totalsize(fdt)) 154630b011fSAntonio Nino Diaz return -FDT_ERR_NOSPACE; 155630b011fSAntonio Nino Diaz 156630b011fSAntonio Nino Diaz re = (struct fdt_reserve_entry *)((char *)fdt + offset); 157630b011fSAntonio Nino Diaz re->address = cpu_to_fdt64(addr); 158630b011fSAntonio Nino Diaz re->size = cpu_to_fdt64(size); 159630b011fSAntonio Nino Diaz 160630b011fSAntonio Nino Diaz fdt_set_off_dt_struct(fdt, offset + sizeof(*re)); 161630b011fSAntonio Nino Diaz 162630b011fSAntonio Nino Diaz return 0; 163630b011fSAntonio Nino Diaz } 164630b011fSAntonio Nino Diaz 165630b011fSAntonio Nino Diaz int fdt_finish_reservemap(void *fdt) 166630b011fSAntonio Nino Diaz { 167*00f588bfSAntonio Nino Diaz return fdt_add_reservemap_entry(fdt, 0, 0); 168630b011fSAntonio Nino Diaz } 169630b011fSAntonio Nino Diaz 170630b011fSAntonio Nino Diaz int fdt_begin_node(void *fdt, const char *name) 171630b011fSAntonio Nino Diaz { 172630b011fSAntonio Nino Diaz struct fdt_node_header *nh; 173*00f588bfSAntonio Nino Diaz int namelen = strlen(name) + 1; 174630b011fSAntonio Nino Diaz 175*00f588bfSAntonio Nino Diaz FDT_SW_CHECK_HEADER(fdt); 176630b011fSAntonio Nino Diaz 177630b011fSAntonio Nino Diaz nh = fdt_grab_space_(fdt, sizeof(*nh) + FDT_TAGALIGN(namelen)); 178630b011fSAntonio Nino Diaz if (! nh) 179630b011fSAntonio Nino Diaz return -FDT_ERR_NOSPACE; 180630b011fSAntonio Nino Diaz 181630b011fSAntonio Nino Diaz nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE); 182630b011fSAntonio Nino Diaz memcpy(nh->name, name, namelen); 183630b011fSAntonio Nino Diaz return 0; 184630b011fSAntonio Nino Diaz } 185630b011fSAntonio Nino Diaz 186630b011fSAntonio Nino Diaz int fdt_end_node(void *fdt) 187630b011fSAntonio Nino Diaz { 188630b011fSAntonio Nino Diaz fdt32_t *en; 189630b011fSAntonio Nino Diaz 190*00f588bfSAntonio Nino Diaz FDT_SW_CHECK_HEADER(fdt); 191630b011fSAntonio Nino Diaz 192630b011fSAntonio Nino Diaz en = fdt_grab_space_(fdt, FDT_TAGSIZE); 193630b011fSAntonio Nino Diaz if (! en) 194630b011fSAntonio Nino Diaz return -FDT_ERR_NOSPACE; 195630b011fSAntonio Nino Diaz 196630b011fSAntonio Nino Diaz *en = cpu_to_fdt32(FDT_END_NODE); 197630b011fSAntonio Nino Diaz return 0; 198630b011fSAntonio Nino Diaz } 199630b011fSAntonio Nino Diaz 200630b011fSAntonio Nino Diaz static int fdt_find_add_string_(void *fdt, const char *s) 201630b011fSAntonio Nino Diaz { 202630b011fSAntonio Nino Diaz char *strtab = (char *)fdt + fdt_totalsize(fdt); 203630b011fSAntonio Nino Diaz const char *p; 204630b011fSAntonio Nino Diaz int strtabsize = fdt_size_dt_strings(fdt); 205630b011fSAntonio Nino Diaz int len = strlen(s) + 1; 206630b011fSAntonio Nino Diaz int struct_top, offset; 207630b011fSAntonio Nino Diaz 208630b011fSAntonio Nino Diaz p = fdt_find_string_(strtab - strtabsize, strtabsize, s); 209630b011fSAntonio Nino Diaz if (p) 210630b011fSAntonio Nino Diaz return p - strtab; 211630b011fSAntonio Nino Diaz 212630b011fSAntonio Nino Diaz /* Add it */ 213630b011fSAntonio Nino Diaz offset = -strtabsize - len; 214630b011fSAntonio Nino Diaz struct_top = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt); 215630b011fSAntonio Nino Diaz if (fdt_totalsize(fdt) + offset < struct_top) 216630b011fSAntonio Nino Diaz return 0; /* no more room :( */ 217630b011fSAntonio Nino Diaz 218630b011fSAntonio Nino Diaz memcpy(strtab + offset, s, len); 219630b011fSAntonio Nino Diaz fdt_set_size_dt_strings(fdt, strtabsize + len); 220630b011fSAntonio Nino Diaz return offset; 221630b011fSAntonio Nino Diaz } 222630b011fSAntonio Nino Diaz 223630b011fSAntonio Nino Diaz int fdt_property_placeholder(void *fdt, const char *name, int len, void **valp) 224630b011fSAntonio Nino Diaz { 225630b011fSAntonio Nino Diaz struct fdt_property *prop; 226630b011fSAntonio Nino Diaz int nameoff; 227630b011fSAntonio Nino Diaz 228*00f588bfSAntonio Nino Diaz FDT_SW_CHECK_HEADER(fdt); 229630b011fSAntonio Nino Diaz 230630b011fSAntonio Nino Diaz nameoff = fdt_find_add_string_(fdt, name); 231630b011fSAntonio Nino Diaz if (nameoff == 0) 232630b011fSAntonio Nino Diaz return -FDT_ERR_NOSPACE; 233630b011fSAntonio Nino Diaz 234630b011fSAntonio Nino Diaz prop = fdt_grab_space_(fdt, sizeof(*prop) + FDT_TAGALIGN(len)); 235630b011fSAntonio Nino Diaz if (! prop) 236630b011fSAntonio Nino Diaz return -FDT_ERR_NOSPACE; 237630b011fSAntonio Nino Diaz 238630b011fSAntonio Nino Diaz prop->tag = cpu_to_fdt32(FDT_PROP); 239630b011fSAntonio Nino Diaz prop->nameoff = cpu_to_fdt32(nameoff); 240630b011fSAntonio Nino Diaz prop->len = cpu_to_fdt32(len); 241630b011fSAntonio Nino Diaz *valp = prop->data; 242630b011fSAntonio Nino Diaz return 0; 243630b011fSAntonio Nino Diaz } 244630b011fSAntonio Nino Diaz 245630b011fSAntonio Nino Diaz int fdt_property(void *fdt, const char *name, const void *val, int len) 246630b011fSAntonio Nino Diaz { 247630b011fSAntonio Nino Diaz void *ptr; 248630b011fSAntonio Nino Diaz int ret; 249630b011fSAntonio Nino Diaz 250630b011fSAntonio Nino Diaz ret = fdt_property_placeholder(fdt, name, len, &ptr); 251630b011fSAntonio Nino Diaz if (ret) 252630b011fSAntonio Nino Diaz return ret; 253630b011fSAntonio Nino Diaz memcpy(ptr, val, len); 254630b011fSAntonio Nino Diaz return 0; 255630b011fSAntonio Nino Diaz } 256630b011fSAntonio Nino Diaz 257630b011fSAntonio Nino Diaz int fdt_finish(void *fdt) 258630b011fSAntonio Nino Diaz { 259630b011fSAntonio Nino Diaz char *p = (char *)fdt; 260630b011fSAntonio Nino Diaz fdt32_t *end; 261630b011fSAntonio Nino Diaz int oldstroffset, newstroffset; 262630b011fSAntonio Nino Diaz uint32_t tag; 263630b011fSAntonio Nino Diaz int offset, nextoffset; 264630b011fSAntonio Nino Diaz 265*00f588bfSAntonio Nino Diaz FDT_SW_CHECK_HEADER(fdt); 266630b011fSAntonio Nino Diaz 267630b011fSAntonio Nino Diaz /* Add terminator */ 268630b011fSAntonio Nino Diaz end = fdt_grab_space_(fdt, sizeof(*end)); 269630b011fSAntonio Nino Diaz if (! end) 270630b011fSAntonio Nino Diaz return -FDT_ERR_NOSPACE; 271630b011fSAntonio Nino Diaz *end = cpu_to_fdt32(FDT_END); 272630b011fSAntonio Nino Diaz 273630b011fSAntonio Nino Diaz /* Relocate the string table */ 274630b011fSAntonio Nino Diaz oldstroffset = fdt_totalsize(fdt) - fdt_size_dt_strings(fdt); 275630b011fSAntonio Nino Diaz newstroffset = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt); 276630b011fSAntonio Nino Diaz memmove(p + newstroffset, p + oldstroffset, fdt_size_dt_strings(fdt)); 277630b011fSAntonio Nino Diaz fdt_set_off_dt_strings(fdt, newstroffset); 278630b011fSAntonio Nino Diaz 279630b011fSAntonio Nino Diaz /* Walk the structure, correcting string offsets */ 280630b011fSAntonio Nino Diaz offset = 0; 281630b011fSAntonio Nino Diaz while ((tag = fdt_next_tag(fdt, offset, &nextoffset)) != FDT_END) { 282630b011fSAntonio Nino Diaz if (tag == FDT_PROP) { 283630b011fSAntonio Nino Diaz struct fdt_property *prop = 284630b011fSAntonio Nino Diaz fdt_offset_ptr_w_(fdt, offset); 285630b011fSAntonio Nino Diaz int nameoff; 286630b011fSAntonio Nino Diaz 287630b011fSAntonio Nino Diaz nameoff = fdt32_to_cpu(prop->nameoff); 288630b011fSAntonio Nino Diaz nameoff += fdt_size_dt_strings(fdt); 289630b011fSAntonio Nino Diaz prop->nameoff = cpu_to_fdt32(nameoff); 290630b011fSAntonio Nino Diaz } 291630b011fSAntonio Nino Diaz offset = nextoffset; 292630b011fSAntonio Nino Diaz } 293630b011fSAntonio Nino Diaz if (nextoffset < 0) 294630b011fSAntonio Nino Diaz return nextoffset; 295630b011fSAntonio Nino Diaz 296630b011fSAntonio Nino Diaz /* Finally, adjust the header */ 297630b011fSAntonio Nino Diaz fdt_set_totalsize(fdt, newstroffset + fdt_size_dt_strings(fdt)); 298630b011fSAntonio Nino Diaz fdt_set_magic(fdt, FDT_MAGIC); 299630b011fSAntonio Nino Diaz return 0; 300630b011fSAntonio Nino Diaz } 301