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