1 /* $NetBSD: fdt_overlay.c,v 1.1.1.1 2017/06/08 15:53:12 skrll Exp $ */ 2 3 #include "libfdt_env.h" 4 5 #include <fdt.h> 6 #include <libfdt.h> 7 8 #include "libfdt_internal.h" 9 10 /** 11 * overlay_get_target_phandle - retrieves the target phandle of a fragment 12 * @fdto: pointer to the device tree overlay blob 13 * @fragment: node offset of the fragment in the overlay 14 * 15 * overlay_get_target_phandle() retrieves the target phandle of an 16 * overlay fragment when that fragment uses a phandle (target 17 * property) instead of a path (target-path property). 18 * 19 * returns: 20 * the phandle pointed by the target property 21 * 0, if the phandle was not found 22 * -1, if the phandle was malformed 23 */ 24 static uint32_t overlay_get_target_phandle(const void *fdto, int fragment) 25 { 26 const fdt32_t *val; 27 int len; 28 29 val = fdt_getprop(fdto, fragment, "target", &len); 30 if (!val) 31 return 0; 32 33 if ((len != sizeof(*val)) || (fdt32_to_cpu(*val) == (uint32_t)-1)) 34 return (uint32_t)-1; 35 36 return fdt32_to_cpu(*val); 37 } 38 39 /** 40 * overlay_get_target - retrieves the offset of a fragment's target 41 * @fdt: Base device tree blob 42 * @fdto: Device tree overlay blob 43 * @fragment: node offset of the fragment in the overlay 44 * 45 * overlay_get_target() retrieves the target offset in the base 46 * device tree of a fragment, no matter how the actual targetting is 47 * done (through a phandle or a path) 48 * 49 * returns: 50 * the targetted node offset in the base device tree 51 * Negative error code on error 52 */ 53 static int overlay_get_target(const void *fdt, const void *fdto, 54 int fragment) 55 { 56 uint32_t phandle; 57 const char *path; 58 int path_len; 59 60 /* Try first to do a phandle based lookup */ 61 phandle = overlay_get_target_phandle(fdto, fragment); 62 if (phandle == (uint32_t)-1) 63 return -FDT_ERR_BADPHANDLE; 64 65 if (phandle) 66 return fdt_node_offset_by_phandle(fdt, phandle); 67 68 /* And then a path based lookup */ 69 path = fdt_getprop(fdto, fragment, "target-path", &path_len); 70 if (!path) { 71 /* 72 * If we haven't found either a target or a 73 * target-path property in a node that contains a 74 * __overlay__ subnode (we wouldn't be called 75 * otherwise), consider it a improperly written 76 * overlay 77 */ 78 if (path_len == -FDT_ERR_NOTFOUND) 79 return -FDT_ERR_BADOVERLAY; 80 81 return path_len; 82 } 83 84 return fdt_path_offset(fdt, path); 85 } 86 87 /** 88 * overlay_phandle_add_offset - Increases a phandle by an offset 89 * @fdt: Base device tree blob 90 * @node: Device tree overlay blob 91 * @name: Name of the property to modify (phandle or linux,phandle) 92 * @delta: offset to apply 93 * 94 * overlay_phandle_add_offset() increments a node phandle by a given 95 * offset. 96 * 97 * returns: 98 * 0 on success. 99 * Negative error code on error 100 */ 101 static int overlay_phandle_add_offset(void *fdt, int node, 102 const char *name, uint32_t delta) 103 { 104 const fdt32_t *val; 105 uint32_t adj_val; 106 int len; 107 108 val = fdt_getprop(fdt, node, name, &len); 109 if (!val) 110 return len; 111 112 if (len != sizeof(*val)) 113 return -FDT_ERR_BADPHANDLE; 114 115 adj_val = fdt32_to_cpu(*val); 116 if ((adj_val + delta) < adj_val) 117 return -FDT_ERR_NOPHANDLES; 118 119 adj_val += delta; 120 if (adj_val == (uint32_t)-1) 121 return -FDT_ERR_NOPHANDLES; 122 123 return fdt_setprop_inplace_u32(fdt, node, name, adj_val); 124 } 125 126 /** 127 * overlay_adjust_node_phandles - Offsets the phandles of a node 128 * @fdto: Device tree overlay blob 129 * @node: Offset of the node we want to adjust 130 * @delta: Offset to shift the phandles of 131 * 132 * overlay_adjust_node_phandles() adds a constant to all the phandles 133 * of a given node. This is mainly use as part of the overlay 134 * application process, when we want to update all the overlay 135 * phandles to not conflict with the overlays of the base device tree. 136 * 137 * returns: 138 * 0 on success 139 * Negative error code on failure 140 */ 141 static int overlay_adjust_node_phandles(void *fdto, int node, 142 uint32_t delta) 143 { 144 int child; 145 int ret; 146 147 ret = overlay_phandle_add_offset(fdto, node, "phandle", delta); 148 if (ret && ret != -FDT_ERR_NOTFOUND) 149 return ret; 150 151 ret = overlay_phandle_add_offset(fdto, node, "linux,phandle", delta); 152 if (ret && ret != -FDT_ERR_NOTFOUND) 153 return ret; 154 155 fdt_for_each_subnode(child, fdto, node) { 156 ret = overlay_adjust_node_phandles(fdto, child, delta); 157 if (ret) 158 return ret; 159 } 160 161 return 0; 162 } 163 164 /** 165 * overlay_adjust_local_phandles - Adjust the phandles of a whole overlay 166 * @fdto: Device tree overlay blob 167 * @delta: Offset to shift the phandles of 168 * 169 * overlay_adjust_local_phandles() adds a constant to all the 170 * phandles of an overlay. This is mainly use as part of the overlay 171 * application process, when we want to update all the overlay 172 * phandles to not conflict with the overlays of the base device tree. 173 * 174 * returns: 175 * 0 on success 176 * Negative error code on failure 177 */ 178 static int overlay_adjust_local_phandles(void *fdto, uint32_t delta) 179 { 180 /* 181 * Start adjusting the phandles from the overlay root 182 */ 183 return overlay_adjust_node_phandles(fdto, 0, delta); 184 } 185 186 /** 187 * overlay_update_local_node_references - Adjust the overlay references 188 * @fdto: Device tree overlay blob 189 * @tree_node: Node offset of the node to operate on 190 * @fixup_node: Node offset of the matching local fixups node 191 * @delta: Offset to shift the phandles of 192 * 193 * overlay_update_local_nodes_references() update the phandles 194 * pointing to a node within the device tree overlay by adding a 195 * constant delta. 196 * 197 * This is mainly used as part of a device tree application process, 198 * where you want the device tree overlays phandles to not conflict 199 * with the ones from the base device tree before merging them. 200 * 201 * returns: 202 * 0 on success 203 * Negative error code on failure 204 */ 205 static int overlay_update_local_node_references(void *fdto, 206 int tree_node, 207 int fixup_node, 208 uint32_t delta) 209 { 210 int fixup_prop; 211 int fixup_child; 212 int ret; 213 214 fdt_for_each_property_offset(fixup_prop, fdto, fixup_node) { 215 const fdt32_t *fixup_val; 216 const char *tree_val; 217 const char *name; 218 int fixup_len; 219 int tree_len; 220 int i; 221 222 fixup_val = fdt_getprop_by_offset(fdto, fixup_prop, 223 &name, &fixup_len); 224 if (!fixup_val) 225 return fixup_len; 226 227 if (fixup_len % sizeof(uint32_t)) 228 return -FDT_ERR_BADOVERLAY; 229 230 tree_val = fdt_getprop(fdto, tree_node, name, &tree_len); 231 if (!tree_val) { 232 if (tree_len == -FDT_ERR_NOTFOUND) 233 return -FDT_ERR_BADOVERLAY; 234 235 return tree_len; 236 } 237 238 for (i = 0; i < (fixup_len / sizeof(uint32_t)); i++) { 239 fdt32_t adj_val; 240 uint32_t poffset; 241 242 poffset = fdt32_to_cpu(fixup_val[i]); 243 244 /* 245 * phandles to fixup can be unaligned. 246 * 247 * Use a memcpy for the architectures that do 248 * not support unaligned accesses. 249 */ 250 memcpy(&adj_val, tree_val + poffset, sizeof(adj_val)); 251 252 adj_val = cpu_to_fdt32(fdt32_to_cpu(adj_val) + delta); 253 254 ret = fdt_setprop_inplace_namelen_partial(fdto, 255 tree_node, 256 name, 257 strlen(name), 258 poffset, 259 &adj_val, 260 sizeof(adj_val)); 261 if (ret == -FDT_ERR_NOSPACE) 262 return -FDT_ERR_BADOVERLAY; 263 264 if (ret) 265 return ret; 266 } 267 } 268 269 fdt_for_each_subnode(fixup_child, fdto, fixup_node) { 270 const char *fixup_child_name = fdt_get_name(fdto, fixup_child, 271 NULL); 272 int tree_child; 273 274 tree_child = fdt_subnode_offset(fdto, tree_node, 275 fixup_child_name); 276 if (tree_child == -FDT_ERR_NOTFOUND) 277 return -FDT_ERR_BADOVERLAY; 278 if (tree_child < 0) 279 return tree_child; 280 281 ret = overlay_update_local_node_references(fdto, 282 tree_child, 283 fixup_child, 284 delta); 285 if (ret) 286 return ret; 287 } 288 289 return 0; 290 } 291 292 /** 293 * overlay_update_local_references - Adjust the overlay references 294 * @fdto: Device tree overlay blob 295 * @delta: Offset to shift the phandles of 296 * 297 * overlay_update_local_references() update all the phandles pointing 298 * to a node within the device tree overlay by adding a constant 299 * delta to not conflict with the base overlay. 300 * 301 * This is mainly used as part of a device tree application process, 302 * where you want the device tree overlays phandles to not conflict 303 * with the ones from the base device tree before merging them. 304 * 305 * returns: 306 * 0 on success 307 * Negative error code on failure 308 */ 309 static int overlay_update_local_references(void *fdto, uint32_t delta) 310 { 311 int fixups; 312 313 fixups = fdt_path_offset(fdto, "/__local_fixups__"); 314 if (fixups < 0) { 315 /* There's no local phandles to adjust, bail out */ 316 if (fixups == -FDT_ERR_NOTFOUND) 317 return 0; 318 319 return fixups; 320 } 321 322 /* 323 * Update our local references from the root of the tree 324 */ 325 return overlay_update_local_node_references(fdto, 0, fixups, 326 delta); 327 } 328 329 /** 330 * overlay_fixup_one_phandle - Set an overlay phandle to the base one 331 * @fdt: Base Device Tree blob 332 * @fdto: Device tree overlay blob 333 * @symbols_off: Node offset of the symbols node in the base device tree 334 * @path: Path to a node holding a phandle in the overlay 335 * @path_len: number of path characters to consider 336 * @name: Name of the property holding the phandle reference in the overlay 337 * @name_len: number of name characters to consider 338 * @poffset: Offset within the overlay property where the phandle is stored 339 * @label: Label of the node referenced by the phandle 340 * 341 * overlay_fixup_one_phandle() resolves an overlay phandle pointing to 342 * a node in the base device tree. 343 * 344 * This is part of the device tree overlay application process, when 345 * you want all the phandles in the overlay to point to the actual 346 * base dt nodes. 347 * 348 * returns: 349 * 0 on success 350 * Negative error code on failure 351 */ 352 static int overlay_fixup_one_phandle(void *fdt, void *fdto, 353 int symbols_off, 354 const char *path, uint32_t path_len, 355 const char *name, uint32_t name_len, 356 int poffset, const char *label) 357 { 358 const char *symbol_path; 359 uint32_t phandle; 360 fdt32_t phandle_prop; 361 int symbol_off, fixup_off; 362 int prop_len; 363 364 if (symbols_off < 0) 365 return symbols_off; 366 367 symbol_path = fdt_getprop(fdt, symbols_off, label, 368 &prop_len); 369 if (!symbol_path) 370 return prop_len; 371 372 symbol_off = fdt_path_offset(fdt, symbol_path); 373 if (symbol_off < 0) 374 return symbol_off; 375 376 phandle = fdt_get_phandle(fdt, symbol_off); 377 if (!phandle) 378 return -FDT_ERR_NOTFOUND; 379 380 fixup_off = fdt_path_offset_namelen(fdto, path, path_len); 381 if (fixup_off == -FDT_ERR_NOTFOUND) 382 return -FDT_ERR_BADOVERLAY; 383 if (fixup_off < 0) 384 return fixup_off; 385 386 phandle_prop = cpu_to_fdt32(phandle); 387 return fdt_setprop_inplace_namelen_partial(fdto, fixup_off, 388 name, name_len, poffset, 389 &phandle_prop, 390 sizeof(phandle_prop)); 391 }; 392 393 /** 394 * overlay_fixup_phandle - Set an overlay phandle to the base one 395 * @fdt: Base Device Tree blob 396 * @fdto: Device tree overlay blob 397 * @symbols_off: Node offset of the symbols node in the base device tree 398 * @property: Property offset in the overlay holding the list of fixups 399 * 400 * overlay_fixup_phandle() resolves all the overlay phandles pointed 401 * to in a __fixups__ property, and updates them to match the phandles 402 * in use in the base device tree. 403 * 404 * This is part of the device tree overlay application process, when 405 * you want all the phandles in the overlay to point to the actual 406 * base dt nodes. 407 * 408 * returns: 409 * 0 on success 410 * Negative error code on failure 411 */ 412 static int overlay_fixup_phandle(void *fdt, void *fdto, int symbols_off, 413 int property) 414 { 415 const char *value; 416 const char *label; 417 int len; 418 419 value = fdt_getprop_by_offset(fdto, property, 420 &label, &len); 421 if (!value) { 422 if (len == -FDT_ERR_NOTFOUND) 423 return -FDT_ERR_INTERNAL; 424 425 return len; 426 } 427 428 do { 429 const char *path, *name, *fixup_end; 430 const char *fixup_str = value; 431 uint32_t path_len, name_len; 432 uint32_t fixup_len; 433 char *sep, *endptr; 434 int poffset, ret; 435 436 fixup_end = memchr(value, '\0', len); 437 if (!fixup_end) 438 return -FDT_ERR_BADOVERLAY; 439 fixup_len = fixup_end - fixup_str; 440 441 len -= fixup_len + 1; 442 value += fixup_len + 1; 443 444 path = fixup_str; 445 sep = memchr(fixup_str, ':', fixup_len); 446 if (!sep || *sep != ':') 447 return -FDT_ERR_BADOVERLAY; 448 449 path_len = sep - path; 450 if (path_len == (fixup_len - 1)) 451 return -FDT_ERR_BADOVERLAY; 452 453 fixup_len -= path_len + 1; 454 name = sep + 1; 455 sep = memchr(name, ':', fixup_len); 456 if (!sep || *sep != ':') 457 return -FDT_ERR_BADOVERLAY; 458 459 name_len = sep - name; 460 if (!name_len) 461 return -FDT_ERR_BADOVERLAY; 462 463 poffset = strtoul(sep + 1, &endptr, 10); 464 if ((*endptr != '\0') || (endptr <= (sep + 1))) 465 return -FDT_ERR_BADOVERLAY; 466 467 ret = overlay_fixup_one_phandle(fdt, fdto, symbols_off, 468 path, path_len, name, name_len, 469 poffset, label); 470 if (ret) 471 return ret; 472 } while (len > 0); 473 474 return 0; 475 } 476 477 /** 478 * overlay_fixup_phandles - Resolve the overlay phandles to the base 479 * device tree 480 * @fdt: Base Device Tree blob 481 * @fdto: Device tree overlay blob 482 * 483 * overlay_fixup_phandles() resolves all the overlay phandles pointing 484 * to nodes in the base device tree. 485 * 486 * This is one of the steps of the device tree overlay application 487 * process, when you want all the phandles in the overlay to point to 488 * the actual base dt nodes. 489 * 490 * returns: 491 * 0 on success 492 * Negative error code on failure 493 */ 494 static int overlay_fixup_phandles(void *fdt, void *fdto) 495 { 496 int fixups_off, symbols_off; 497 int property; 498 499 /* We can have overlays without any fixups */ 500 fixups_off = fdt_path_offset(fdto, "/__fixups__"); 501 if (fixups_off == -FDT_ERR_NOTFOUND) 502 return 0; /* nothing to do */ 503 if (fixups_off < 0) 504 return fixups_off; 505 506 /* And base DTs without symbols */ 507 symbols_off = fdt_path_offset(fdt, "/__symbols__"); 508 if ((symbols_off < 0 && (symbols_off != -FDT_ERR_NOTFOUND))) 509 return symbols_off; 510 511 fdt_for_each_property_offset(property, fdto, fixups_off) { 512 int ret; 513 514 ret = overlay_fixup_phandle(fdt, fdto, symbols_off, property); 515 if (ret) 516 return ret; 517 } 518 519 return 0; 520 } 521 522 /** 523 * overlay_apply_node - Merges a node into the base device tree 524 * @fdt: Base Device Tree blob 525 * @target: Node offset in the base device tree to apply the fragment to 526 * @fdto: Device tree overlay blob 527 * @node: Node offset in the overlay holding the changes to merge 528 * 529 * overlay_apply_node() merges a node into a target base device tree 530 * node pointed. 531 * 532 * This is part of the final step in the device tree overlay 533 * application process, when all the phandles have been adjusted and 534 * resolved and you just have to merge overlay into the base device 535 * tree. 536 * 537 * returns: 538 * 0 on success 539 * Negative error code on failure 540 */ 541 static int overlay_apply_node(void *fdt, int target, 542 void *fdto, int node) 543 { 544 int property; 545 int subnode; 546 547 fdt_for_each_property_offset(property, fdto, node) { 548 const char *name; 549 const void *prop; 550 int prop_len; 551 int ret; 552 553 prop = fdt_getprop_by_offset(fdto, property, &name, 554 &prop_len); 555 if (prop_len == -FDT_ERR_NOTFOUND) 556 return -FDT_ERR_INTERNAL; 557 if (prop_len < 0) 558 return prop_len; 559 560 ret = fdt_setprop(fdt, target, name, prop, prop_len); 561 if (ret) 562 return ret; 563 } 564 565 fdt_for_each_subnode(subnode, fdto, node) { 566 const char *name = fdt_get_name(fdto, subnode, NULL); 567 int nnode; 568 int ret; 569 570 nnode = fdt_add_subnode(fdt, target, name); 571 if (nnode == -FDT_ERR_EXISTS) { 572 nnode = fdt_subnode_offset(fdt, target, name); 573 if (nnode == -FDT_ERR_NOTFOUND) 574 return -FDT_ERR_INTERNAL; 575 } 576 577 if (nnode < 0) 578 return nnode; 579 580 ret = overlay_apply_node(fdt, nnode, fdto, subnode); 581 if (ret) 582 return ret; 583 } 584 585 return 0; 586 } 587 588 /** 589 * overlay_merge - Merge an overlay into its base device tree 590 * @fdt: Base Device Tree blob 591 * @fdto: Device tree overlay blob 592 * 593 * overlay_merge() merges an overlay into its base device tree. 594 * 595 * This is the final step in the device tree overlay application 596 * process, when all the phandles have been adjusted and resolved and 597 * you just have to merge overlay into the base device tree. 598 * 599 * returns: 600 * 0 on success 601 * Negative error code on failure 602 */ 603 static int overlay_merge(void *fdt, void *fdto) 604 { 605 int fragment; 606 607 fdt_for_each_subnode(fragment, fdto, 0) { 608 int overlay; 609 int target; 610 int ret; 611 612 /* 613 * Each fragments will have an __overlay__ node. If 614 * they don't, it's not supposed to be merged 615 */ 616 overlay = fdt_subnode_offset(fdto, fragment, "__overlay__"); 617 if (overlay == -FDT_ERR_NOTFOUND) 618 continue; 619 620 if (overlay < 0) 621 return overlay; 622 623 target = overlay_get_target(fdt, fdto, fragment); 624 if (target < 0) 625 return target; 626 627 ret = overlay_apply_node(fdt, target, fdto, overlay); 628 if (ret) 629 return ret; 630 } 631 632 return 0; 633 } 634 635 int fdt_overlay_apply(void *fdt, void *fdto) 636 { 637 uint32_t delta = fdt_get_max_phandle(fdt); 638 int ret; 639 640 FDT_CHECK_HEADER(fdt); 641 FDT_CHECK_HEADER(fdto); 642 643 ret = overlay_adjust_local_phandles(fdto, delta); 644 if (ret) 645 goto err; 646 647 ret = overlay_update_local_references(fdto, delta); 648 if (ret) 649 goto err; 650 651 ret = overlay_fixup_phandles(fdt, fdto); 652 if (ret) 653 goto err; 654 655 ret = overlay_merge(fdt, fdto); 656 if (ret) 657 goto err; 658 659 /* 660 * The overlay has been damaged, erase its magic. 661 */ 662 fdt_set_magic(fdto, ~0); 663 664 return 0; 665 666 err: 667 /* 668 * The overlay might have been damaged, erase its magic. 669 */ 670 fdt_set_magic(fdto, ~0); 671 672 /* 673 * The base device tree might have been damaged, erase its 674 * magic. 675 */ 676 fdt_set_magic(fdt, ~0); 677 678 return ret; 679 } 680