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_blocks_misordered_(const void *fdt, 60 int mem_rsv_size, int struct_size) 61 { 62 return (fdt_off_mem_rsvmap(fdt) < FDT_ALIGN(sizeof(struct fdt_header), 8)) 63 || (fdt_off_dt_struct(fdt) < 64 (fdt_off_mem_rsvmap(fdt) + mem_rsv_size)) 65 || (fdt_off_dt_strings(fdt) < 66 (fdt_off_dt_struct(fdt) + struct_size)) 67 || (fdt_totalsize(fdt) < 68 (fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt))); 69 } 70 71 static int fdt_rw_check_header_(void *fdt) 72 { 73 FDT_CHECK_HEADER(fdt); 74 75 if (fdt_version(fdt) < 17) 76 return -FDT_ERR_BADVERSION; 77 if (fdt_blocks_misordered_(fdt, sizeof(struct fdt_reserve_entry), 78 fdt_size_dt_struct(fdt))) 79 return -FDT_ERR_BADLAYOUT; 80 if (fdt_version(fdt) > 17) 81 fdt_set_version(fdt, 17); 82 83 return 0; 84 } 85 86 #define FDT_RW_CHECK_HEADER(fdt) \ 87 { \ 88 int err_; \ 89 if ((err_ = fdt_rw_check_header_(fdt)) != 0) \ 90 return err_; \ 91 } 92 93 static inline int fdt_data_size_(void *fdt) 94 { 95 return fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt); 96 } 97 98 static int fdt_splice_(void *fdt, void *splicepoint, int oldlen, int newlen) 99 { 100 char *p = splicepoint; 101 char *end = (char *)fdt + fdt_data_size_(fdt); 102 103 if (((p + oldlen) < p) || ((p + oldlen) > end)) 104 return -FDT_ERR_BADOFFSET; 105 if ((p < (char *)fdt) || ((end - oldlen + newlen) < (char *)fdt)) 106 return -FDT_ERR_BADOFFSET; 107 if ((end - oldlen + newlen) > ((char *)fdt + fdt_totalsize(fdt))) 108 return -FDT_ERR_NOSPACE; 109 memmove(p + newlen, p + oldlen, end - p - oldlen); 110 return 0; 111 } 112 113 static int fdt_splice_mem_rsv_(void *fdt, struct fdt_reserve_entry *p, 114 int oldn, int newn) 115 { 116 int delta = (newn - oldn) * sizeof(*p); 117 int err; 118 err = fdt_splice_(fdt, p, oldn * sizeof(*p), newn * sizeof(*p)); 119 if (err) 120 return err; 121 fdt_set_off_dt_struct(fdt, fdt_off_dt_struct(fdt) + delta); 122 fdt_set_off_dt_strings(fdt, fdt_off_dt_strings(fdt) + delta); 123 return 0; 124 } 125 126 static int fdt_splice_struct_(void *fdt, void *p, 127 int oldlen, int newlen) 128 { 129 int delta = newlen - oldlen; 130 int err; 131 132 if ((err = fdt_splice_(fdt, p, oldlen, newlen))) 133 return err; 134 135 fdt_set_size_dt_struct(fdt, fdt_size_dt_struct(fdt) + delta); 136 fdt_set_off_dt_strings(fdt, fdt_off_dt_strings(fdt) + delta); 137 return 0; 138 } 139 140 static int fdt_splice_string_(void *fdt, int newlen) 141 { 142 void *p = (char *)fdt 143 + fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt); 144 int err; 145 146 if ((err = fdt_splice_(fdt, p, 0, newlen))) 147 return err; 148 149 fdt_set_size_dt_strings(fdt, fdt_size_dt_strings(fdt) + newlen); 150 return 0; 151 } 152 153 static int fdt_find_add_string_(void *fdt, const char *s) 154 { 155 char *strtab = (char *)fdt + fdt_off_dt_strings(fdt); 156 const char *p; 157 char *new; 158 int len = strlen(s) + 1; 159 int err; 160 161 p = fdt_find_string_(strtab, fdt_size_dt_strings(fdt), s); 162 if (p) 163 /* found it */ 164 return (p - strtab); 165 166 new = strtab + fdt_size_dt_strings(fdt); 167 err = fdt_splice_string_(fdt, len); 168 if (err) 169 return err; 170 171 memcpy(new, s, len); 172 return (new - strtab); 173 } 174 175 int fdt_add_mem_rsv(void *fdt, uint64_t address, uint64_t size) 176 { 177 struct fdt_reserve_entry *re; 178 int err; 179 180 FDT_RW_CHECK_HEADER(fdt); 181 182 re = fdt_mem_rsv_w_(fdt, fdt_num_mem_rsv(fdt)); 183 err = fdt_splice_mem_rsv_(fdt, re, 0, 1); 184 if (err) 185 return err; 186 187 re->address = cpu_to_fdt64(address); 188 re->size = cpu_to_fdt64(size); 189 return 0; 190 } 191 192 int fdt_del_mem_rsv(void *fdt, int n) 193 { 194 struct fdt_reserve_entry *re = fdt_mem_rsv_w_(fdt, n); 195 196 FDT_RW_CHECK_HEADER(fdt); 197 198 if (n >= fdt_num_mem_rsv(fdt)) 199 return -FDT_ERR_NOTFOUND; 200 201 return fdt_splice_mem_rsv_(fdt, re, 1, 0); 202 } 203 204 static int fdt_resize_property_(void *fdt, int nodeoffset, const char *name, 205 int len, struct fdt_property **prop) 206 { 207 int oldlen; 208 int err; 209 210 *prop = fdt_get_property_w(fdt, nodeoffset, name, &oldlen); 211 if (!*prop) 212 return oldlen; 213 214 if ((err = fdt_splice_struct_(fdt, (*prop)->data, FDT_TAGALIGN(oldlen), 215 FDT_TAGALIGN(len)))) 216 return err; 217 218 (*prop)->len = cpu_to_fdt32(len); 219 return 0; 220 } 221 222 static int fdt_add_property_(void *fdt, int nodeoffset, const char *name, 223 int len, struct fdt_property **prop) 224 { 225 int proplen; 226 int nextoffset; 227 int namestroff; 228 int err; 229 230 if ((nextoffset = fdt_check_node_offset_(fdt, nodeoffset)) < 0) 231 return nextoffset; 232 233 namestroff = fdt_find_add_string_(fdt, name); 234 if (namestroff < 0) 235 return namestroff; 236 237 *prop = fdt_offset_ptr_w_(fdt, nextoffset); 238 proplen = sizeof(**prop) + FDT_TAGALIGN(len); 239 240 err = fdt_splice_struct_(fdt, *prop, 0, proplen); 241 if (err) 242 return err; 243 244 (*prop)->tag = cpu_to_fdt32(FDT_PROP); 245 (*prop)->nameoff = cpu_to_fdt32(namestroff); 246 (*prop)->len = cpu_to_fdt32(len); 247 return 0; 248 } 249 250 int fdt_set_name(void *fdt, int nodeoffset, const char *name) 251 { 252 char *namep; 253 int oldlen, newlen; 254 int err; 255 256 FDT_RW_CHECK_HEADER(fdt); 257 258 namep = (char *)(uintptr_t)fdt_get_name(fdt, nodeoffset, &oldlen); 259 if (!namep) 260 return oldlen; 261 262 newlen = strlen(name); 263 264 err = fdt_splice_struct_(fdt, namep, FDT_TAGALIGN(oldlen+1), 265 FDT_TAGALIGN(newlen+1)); 266 if (err) 267 return err; 268 269 memcpy(namep, name, newlen+1); 270 return 0; 271 } 272 273 int fdt_setprop_placeholder(void *fdt, int nodeoffset, const char *name, 274 int len, void **prop_data) 275 { 276 struct fdt_property *prop; 277 int err; 278 279 FDT_RW_CHECK_HEADER(fdt); 280 281 err = fdt_resize_property_(fdt, nodeoffset, name, len, &prop); 282 if (err == -FDT_ERR_NOTFOUND) 283 err = fdt_add_property_(fdt, nodeoffset, name, len, &prop); 284 if (err) 285 return err; 286 287 *prop_data = prop->data; 288 return 0; 289 } 290 291 int fdt_setprop(void *fdt, int nodeoffset, const char *name, 292 const void *val, int len) 293 { 294 void *prop_data; 295 int err; 296 297 err = fdt_setprop_placeholder(fdt, nodeoffset, name, len, &prop_data); 298 if (err) 299 return err; 300 301 if (len) 302 memcpy(prop_data, val, len); 303 return 0; 304 } 305 306 int fdt_appendprop(void *fdt, int nodeoffset, const char *name, 307 const void *val, int len) 308 { 309 struct fdt_property *prop; 310 int err, oldlen, newlen; 311 312 FDT_RW_CHECK_HEADER(fdt); 313 314 prop = fdt_get_property_w(fdt, nodeoffset, name, &oldlen); 315 if (prop) { 316 newlen = len + oldlen; 317 err = fdt_splice_struct_(fdt, prop->data, 318 FDT_TAGALIGN(oldlen), 319 FDT_TAGALIGN(newlen)); 320 if (err) 321 return err; 322 prop->len = cpu_to_fdt32(newlen); 323 memcpy(prop->data + oldlen, val, len); 324 } else { 325 err = fdt_add_property_(fdt, nodeoffset, name, len, &prop); 326 if (err) 327 return err; 328 memcpy(prop->data, val, len); 329 } 330 return 0; 331 } 332 333 int fdt_delprop(void *fdt, int nodeoffset, const char *name) 334 { 335 struct fdt_property *prop; 336 int len, proplen; 337 338 FDT_RW_CHECK_HEADER(fdt); 339 340 prop = fdt_get_property_w(fdt, nodeoffset, name, &len); 341 if (!prop) 342 return len; 343 344 proplen = sizeof(*prop) + FDT_TAGALIGN(len); 345 return fdt_splice_struct_(fdt, prop, proplen, 0); 346 } 347 348 int fdt_add_subnode_namelen(void *fdt, int parentoffset, 349 const char *name, int namelen) 350 { 351 struct fdt_node_header *nh; 352 int offset, nextoffset; 353 int nodelen; 354 int err; 355 uint32_t tag; 356 fdt32_t *endtag; 357 358 FDT_RW_CHECK_HEADER(fdt); 359 360 offset = fdt_subnode_offset_namelen(fdt, parentoffset, name, namelen); 361 if (offset >= 0) 362 return -FDT_ERR_EXISTS; 363 else if (offset != -FDT_ERR_NOTFOUND) 364 return offset; 365 366 /* Try to place the new node after the parent's properties */ 367 fdt_next_tag(fdt, parentoffset, &nextoffset); /* skip the BEGIN_NODE */ 368 do { 369 offset = nextoffset; 370 tag = fdt_next_tag(fdt, offset, &nextoffset); 371 } while ((tag == FDT_PROP) || (tag == FDT_NOP)); 372 373 nh = fdt_offset_ptr_w_(fdt, offset); 374 nodelen = sizeof(*nh) + FDT_TAGALIGN(namelen+1) + FDT_TAGSIZE; 375 376 err = fdt_splice_struct_(fdt, nh, 0, nodelen); 377 if (err) 378 return err; 379 380 nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE); 381 memset(nh->name, 0, FDT_TAGALIGN(namelen+1)); 382 memcpy(nh->name, name, namelen); 383 endtag = (fdt32_t *)((char *)nh + nodelen - FDT_TAGSIZE); 384 *endtag = cpu_to_fdt32(FDT_END_NODE); 385 386 return offset; 387 } 388 389 int fdt_add_subnode(void *fdt, int parentoffset, const char *name) 390 { 391 return fdt_add_subnode_namelen(fdt, parentoffset, name, strlen(name)); 392 } 393 394 int fdt_del_node(void *fdt, int nodeoffset) 395 { 396 int endoffset; 397 398 FDT_RW_CHECK_HEADER(fdt); 399 400 endoffset = fdt_node_end_offset_(fdt, nodeoffset); 401 if (endoffset < 0) 402 return endoffset; 403 404 return fdt_splice_struct_(fdt, fdt_offset_ptr_w_(fdt, nodeoffset), 405 endoffset - nodeoffset, 0); 406 } 407 408 static void fdt_packblocks_(const char *old, char *new, 409 int mem_rsv_size, int struct_size) 410 { 411 int mem_rsv_off, struct_off, strings_off; 412 413 mem_rsv_off = FDT_ALIGN(sizeof(struct fdt_header), 8); 414 struct_off = mem_rsv_off + mem_rsv_size; 415 strings_off = struct_off + struct_size; 416 417 memmove(new + mem_rsv_off, old + fdt_off_mem_rsvmap(old), mem_rsv_size); 418 fdt_set_off_mem_rsvmap(new, mem_rsv_off); 419 420 memmove(new + struct_off, old + fdt_off_dt_struct(old), struct_size); 421 fdt_set_off_dt_struct(new, struct_off); 422 fdt_set_size_dt_struct(new, struct_size); 423 424 memmove(new + strings_off, old + fdt_off_dt_strings(old), 425 fdt_size_dt_strings(old)); 426 fdt_set_off_dt_strings(new, strings_off); 427 fdt_set_size_dt_strings(new, fdt_size_dt_strings(old)); 428 } 429 430 int fdt_open_into(const void *fdt, void *buf, int bufsize) 431 { 432 int err; 433 int mem_rsv_size, struct_size; 434 int newsize; 435 const char *fdtstart = fdt; 436 const char *fdtend = fdtstart + fdt_totalsize(fdt); 437 char *tmp; 438 439 FDT_CHECK_HEADER(fdt); 440 441 mem_rsv_size = (fdt_num_mem_rsv(fdt)+1) 442 * sizeof(struct fdt_reserve_entry); 443 444 if (fdt_version(fdt) >= 17) { 445 struct_size = fdt_size_dt_struct(fdt); 446 } else { 447 struct_size = 0; 448 while (fdt_next_tag(fdt, struct_size, &struct_size) != FDT_END) 449 ; 450 if (struct_size < 0) 451 return struct_size; 452 } 453 454 if (!fdt_blocks_misordered_(fdt, mem_rsv_size, struct_size)) { 455 /* no further work necessary */ 456 err = fdt_move(fdt, buf, bufsize); 457 if (err) 458 return err; 459 fdt_set_version(buf, 17); 460 fdt_set_size_dt_struct(buf, struct_size); 461 fdt_set_totalsize(buf, bufsize); 462 return 0; 463 } 464 465 /* Need to reorder */ 466 newsize = FDT_ALIGN(sizeof(struct fdt_header), 8) + mem_rsv_size 467 + struct_size + fdt_size_dt_strings(fdt); 468 469 if (bufsize < newsize) 470 return -FDT_ERR_NOSPACE; 471 472 /* First attempt to build converted tree at beginning of buffer */ 473 tmp = buf; 474 /* But if that overlaps with the old tree... */ 475 if (((tmp + newsize) > fdtstart) && (tmp < fdtend)) { 476 /* Try right after the old tree instead */ 477 tmp = (char *)(uintptr_t)fdtend; 478 if ((tmp + newsize) > ((char *)buf + bufsize)) 479 return -FDT_ERR_NOSPACE; 480 } 481 482 fdt_packblocks_(fdt, tmp, mem_rsv_size, struct_size); 483 memmove(buf, tmp, newsize); 484 485 fdt_set_magic(buf, FDT_MAGIC); 486 fdt_set_totalsize(buf, bufsize); 487 fdt_set_version(buf, 17); 488 fdt_set_last_comp_version(buf, 16); 489 fdt_set_boot_cpuid_phys(buf, fdt_boot_cpuid_phys(fdt)); 490 491 return 0; 492 } 493 494 int fdt_pack(void *fdt) 495 { 496 int mem_rsv_size; 497 498 FDT_RW_CHECK_HEADER(fdt); 499 500 mem_rsv_size = (fdt_num_mem_rsv(fdt)+1) 501 * sizeof(struct fdt_reserve_entry); 502 fdt_packblocks_(fdt, fdt, mem_rsv_size, fdt_size_dt_struct(fdt)); 503 fdt_set_totalsize(fdt, fdt_data_size_(fdt)); 504 505 return 0; 506 } 507