1 /* 2 * (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation. 2005. 3 * 4 * 5 * This program is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU General Public License as 7 * published by the Free Software Foundation; either version 2 of the 8 * License, or (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program; if not, write to the Free Software 17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 18 * USA 19 */ 20 21 #include "dtc.h" 22 23 /* 24 * Tree building functions 25 */ 26 27 void add_label(struct label **labels, char *label) 28 { 29 struct label *new; 30 31 /* Make sure the label isn't already there */ 32 for_each_label_withdel(*labels, new) 33 if (streq(new->label, label)) { 34 new->deleted = 0; 35 return; 36 } 37 38 new = xmalloc(sizeof(*new)); 39 memset(new, 0, sizeof(*new)); 40 new->label = label; 41 new->next = *labels; 42 *labels = new; 43 } 44 45 void delete_labels(struct label **labels) 46 { 47 struct label *label; 48 49 for_each_label(*labels, label) 50 label->deleted = 1; 51 } 52 53 struct property *build_property(char *name, struct data val) 54 { 55 struct property *new = xmalloc(sizeof(*new)); 56 57 memset(new, 0, sizeof(*new)); 58 59 new->name = name; 60 new->val = val; 61 62 return new; 63 } 64 65 struct property *build_property_delete(char *name) 66 { 67 struct property *new = xmalloc(sizeof(*new)); 68 69 memset(new, 0, sizeof(*new)); 70 71 new->name = name; 72 new->deleted = 1; 73 74 return new; 75 } 76 77 struct property *chain_property(struct property *first, struct property *list) 78 { 79 assert(first->next == NULL); 80 81 first->next = list; 82 return first; 83 } 84 85 struct property *reverse_properties(struct property *first) 86 { 87 struct property *p = first; 88 struct property *head = NULL; 89 struct property *next; 90 91 while (p) { 92 next = p->next; 93 p->next = head; 94 head = p; 95 p = next; 96 } 97 return head; 98 } 99 100 struct node *build_node(struct property *proplist, struct node *children) 101 { 102 struct node *new = xmalloc(sizeof(*new)); 103 struct node *child; 104 105 memset(new, 0, sizeof(*new)); 106 107 new->proplist = reverse_properties(proplist); 108 new->children = children; 109 110 for_each_child(new, child) { 111 child->parent = new; 112 } 113 114 return new; 115 } 116 117 struct node *build_node_delete(void) 118 { 119 struct node *new = xmalloc(sizeof(*new)); 120 121 memset(new, 0, sizeof(*new)); 122 123 new->deleted = 1; 124 125 return new; 126 } 127 128 struct node *name_node(struct node *node, char *name) 129 { 130 assert(node->name == NULL); 131 132 node->name = name; 133 134 return node; 135 } 136 137 struct node *merge_nodes(struct node *old_node, struct node *new_node) 138 { 139 struct property *new_prop, *old_prop; 140 struct node *new_child, *old_child; 141 struct label *l; 142 143 old_node->deleted = 0; 144 145 /* Add new node labels to old node */ 146 for_each_label_withdel(new_node->labels, l) 147 add_label(&old_node->labels, l->label); 148 149 /* Move properties from the new node to the old node. If there 150 * is a collision, replace the old value with the new */ 151 while (new_node->proplist) { 152 /* Pop the property off the list */ 153 new_prop = new_node->proplist; 154 new_node->proplist = new_prop->next; 155 new_prop->next = NULL; 156 157 if (new_prop->deleted) { 158 delete_property_by_name(old_node, new_prop->name); 159 free(new_prop); 160 continue; 161 } 162 163 /* Look for a collision, set new value if there is */ 164 for_each_property_withdel(old_node, old_prop) { 165 if (streq(old_prop->name, new_prop->name)) { 166 /* Add new labels to old property */ 167 for_each_label_withdel(new_prop->labels, l) 168 add_label(&old_prop->labels, l->label); 169 170 old_prop->val = new_prop->val; 171 old_prop->deleted = 0; 172 free(new_prop); 173 new_prop = NULL; 174 break; 175 } 176 } 177 178 /* if no collision occurred, add property to the old node. */ 179 if (new_prop) 180 add_property(old_node, new_prop); 181 } 182 183 /* Move the override child nodes into the primary node. If 184 * there is a collision, then merge the nodes. */ 185 while (new_node->children) { 186 /* Pop the child node off the list */ 187 new_child = new_node->children; 188 new_node->children = new_child->next_sibling; 189 new_child->parent = NULL; 190 new_child->next_sibling = NULL; 191 192 if (new_child->deleted) { 193 delete_node_by_name(old_node, new_child->name); 194 free(new_child); 195 continue; 196 } 197 198 /* Search for a collision. Merge if there is */ 199 for_each_child_withdel(old_node, old_child) { 200 if (streq(old_child->name, new_child->name)) { 201 merge_nodes(old_child, new_child); 202 new_child = NULL; 203 break; 204 } 205 } 206 207 /* if no collision occurred, add child to the old node. */ 208 if (new_child) 209 add_child(old_node, new_child); 210 } 211 212 /* The new node contents are now merged into the old node. Free 213 * the new node. */ 214 free(new_node); 215 216 return old_node; 217 } 218 219 struct node *chain_node(struct node *first, struct node *list) 220 { 221 assert(first->next_sibling == NULL); 222 223 first->next_sibling = list; 224 return first; 225 } 226 227 void add_property(struct node *node, struct property *prop) 228 { 229 struct property **p; 230 231 prop->next = NULL; 232 233 p = &node->proplist; 234 while (*p) 235 p = &((*p)->next); 236 237 *p = prop; 238 } 239 240 void delete_property_by_name(struct node *node, char *name) 241 { 242 struct property *prop = node->proplist; 243 244 while (prop) { 245 if (streq(prop->name, name)) { 246 delete_property(prop); 247 return; 248 } 249 prop = prop->next; 250 } 251 } 252 253 void delete_property(struct property *prop) 254 { 255 prop->deleted = 1; 256 delete_labels(&prop->labels); 257 } 258 259 void add_child(struct node *parent, struct node *child) 260 { 261 struct node **p; 262 263 child->next_sibling = NULL; 264 child->parent = parent; 265 266 p = &parent->children; 267 while (*p) 268 p = &((*p)->next_sibling); 269 270 *p = child; 271 } 272 273 void delete_node_by_name(struct node *parent, char *name) 274 { 275 struct node *node = parent->children; 276 277 while (node) { 278 if (streq(node->name, name)) { 279 delete_node(node); 280 return; 281 } 282 node = node->next_sibling; 283 } 284 } 285 286 void delete_node(struct node *node) 287 { 288 struct property *prop; 289 struct node *child; 290 291 node->deleted = 1; 292 for_each_child(node, child) 293 delete_node(child); 294 for_each_property(node, prop) 295 delete_property(prop); 296 delete_labels(&node->labels); 297 } 298 299 void append_to_property(struct node *node, 300 char *name, const void *data, int len) 301 { 302 struct data d; 303 struct property *p; 304 305 p = get_property(node, name); 306 if (p) { 307 d = data_append_data(p->val, data, len); 308 p->val = d; 309 } else { 310 d = data_append_data(empty_data, data, len); 311 p = build_property(name, d); 312 add_property(node, p); 313 } 314 } 315 316 struct reserve_info *build_reserve_entry(uint64_t address, uint64_t size) 317 { 318 struct reserve_info *new = xmalloc(sizeof(*new)); 319 320 memset(new, 0, sizeof(*new)); 321 322 new->address = address; 323 new->size = size; 324 325 return new; 326 } 327 328 struct reserve_info *chain_reserve_entry(struct reserve_info *first, 329 struct reserve_info *list) 330 { 331 assert(first->next == NULL); 332 333 first->next = list; 334 return first; 335 } 336 337 struct reserve_info *add_reserve_entry(struct reserve_info *list, 338 struct reserve_info *new) 339 { 340 struct reserve_info *last; 341 342 new->next = NULL; 343 344 if (! list) 345 return new; 346 347 for (last = list; last->next; last = last->next) 348 ; 349 350 last->next = new; 351 352 return list; 353 } 354 355 struct dt_info *build_dt_info(unsigned int dtsflags, 356 struct reserve_info *reservelist, 357 struct node *tree, uint32_t boot_cpuid_phys) 358 { 359 struct dt_info *dti; 360 361 dti = xmalloc(sizeof(*dti)); 362 dti->dtsflags = dtsflags; 363 dti->reservelist = reservelist; 364 dti->dt = tree; 365 dti->boot_cpuid_phys = boot_cpuid_phys; 366 367 return dti; 368 } 369 370 /* 371 * Tree accessor functions 372 */ 373 374 const char *get_unitname(struct node *node) 375 { 376 if (node->name[node->basenamelen] == '\0') 377 return ""; 378 else 379 return node->name + node->basenamelen + 1; 380 } 381 382 struct property *get_property(struct node *node, const char *propname) 383 { 384 struct property *prop; 385 386 for_each_property(node, prop) 387 if (streq(prop->name, propname)) 388 return prop; 389 390 return NULL; 391 } 392 393 cell_t propval_cell(struct property *prop) 394 { 395 assert(prop->val.len == sizeof(cell_t)); 396 return fdt32_to_cpu(*((fdt32_t *)prop->val.val)); 397 } 398 399 struct property *get_property_by_label(struct node *tree, const char *label, 400 struct node **node) 401 { 402 struct property *prop; 403 struct node *c; 404 405 *node = tree; 406 407 for_each_property(tree, prop) { 408 struct label *l; 409 410 for_each_label(prop->labels, l) 411 if (streq(l->label, label)) 412 return prop; 413 } 414 415 for_each_child(tree, c) { 416 prop = get_property_by_label(c, label, node); 417 if (prop) 418 return prop; 419 } 420 421 *node = NULL; 422 return NULL; 423 } 424 425 struct marker *get_marker_label(struct node *tree, const char *label, 426 struct node **node, struct property **prop) 427 { 428 struct marker *m; 429 struct property *p; 430 struct node *c; 431 432 *node = tree; 433 434 for_each_property(tree, p) { 435 *prop = p; 436 m = p->val.markers; 437 for_each_marker_of_type(m, LABEL) 438 if (streq(m->ref, label)) 439 return m; 440 } 441 442 for_each_child(tree, c) { 443 m = get_marker_label(c, label, node, prop); 444 if (m) 445 return m; 446 } 447 448 *prop = NULL; 449 *node = NULL; 450 return NULL; 451 } 452 453 struct node *get_subnode(struct node *node, const char *nodename) 454 { 455 struct node *child; 456 457 for_each_child(node, child) 458 if (streq(child->name, nodename)) 459 return child; 460 461 return NULL; 462 } 463 464 struct node *get_node_by_path(struct node *tree, const char *path) 465 { 466 const char *p; 467 struct node *child; 468 469 if (!path || ! (*path)) { 470 if (tree->deleted) 471 return NULL; 472 return tree; 473 } 474 475 while (path[0] == '/') 476 path++; 477 478 p = strchr(path, '/'); 479 480 for_each_child(tree, child) { 481 if (p && (strlen(child->name) == p-path) && 482 strneq(path, child->name, p-path)) 483 return get_node_by_path(child, p+1); 484 else if (!p && streq(path, child->name)) 485 return child; 486 } 487 488 return NULL; 489 } 490 491 struct node *get_node_by_label(struct node *tree, const char *label) 492 { 493 struct node *child, *node; 494 struct label *l; 495 496 assert(label && (strlen(label) > 0)); 497 498 for_each_label(tree->labels, l) 499 if (streq(l->label, label)) 500 return tree; 501 502 for_each_child(tree, child) { 503 node = get_node_by_label(child, label); 504 if (node) 505 return node; 506 } 507 508 return NULL; 509 } 510 511 struct node *get_node_by_phandle(struct node *tree, cell_t phandle) 512 { 513 struct node *child, *node; 514 515 assert((phandle != 0) && (phandle != -1)); 516 517 if (tree->phandle == phandle) { 518 if (tree->deleted) 519 return NULL; 520 return tree; 521 } 522 523 for_each_child(tree, child) { 524 node = get_node_by_phandle(child, phandle); 525 if (node) 526 return node; 527 } 528 529 return NULL; 530 } 531 532 struct node *get_node_by_ref(struct node *tree, const char *ref) 533 { 534 if (streq(ref, "/")) 535 return tree; 536 else if (ref[0] == '/') 537 return get_node_by_path(tree, ref); 538 else 539 return get_node_by_label(tree, ref); 540 } 541 542 cell_t get_node_phandle(struct node *root, struct node *node) 543 { 544 static cell_t phandle = 1; /* FIXME: ick, static local */ 545 546 if ((node->phandle != 0) && (node->phandle != -1)) 547 return node->phandle; 548 549 while (get_node_by_phandle(root, phandle)) 550 phandle++; 551 552 node->phandle = phandle; 553 554 if (!get_property(node, "linux,phandle") 555 && (phandle_format & PHANDLE_LEGACY)) 556 add_property(node, 557 build_property("linux,phandle", 558 data_append_cell(empty_data, phandle))); 559 560 if (!get_property(node, "phandle") 561 && (phandle_format & PHANDLE_EPAPR)) 562 add_property(node, 563 build_property("phandle", 564 data_append_cell(empty_data, phandle))); 565 566 /* If the node *does* have a phandle property, we must 567 * be dealing with a self-referencing phandle, which will be 568 * fixed up momentarily in the caller */ 569 570 return node->phandle; 571 } 572 573 uint32_t guess_boot_cpuid(struct node *tree) 574 { 575 struct node *cpus, *bootcpu; 576 struct property *reg; 577 578 cpus = get_node_by_path(tree, "/cpus"); 579 if (!cpus) 580 return 0; 581 582 583 bootcpu = cpus->children; 584 if (!bootcpu) 585 return 0; 586 587 reg = get_property(bootcpu, "reg"); 588 if (!reg || (reg->val.len != sizeof(uint32_t))) 589 return 0; 590 591 /* FIXME: Sanity check node? */ 592 593 return propval_cell(reg); 594 } 595 596 static int cmp_reserve_info(const void *ax, const void *bx) 597 { 598 const struct reserve_info *a, *b; 599 600 a = *((const struct reserve_info * const *)ax); 601 b = *((const struct reserve_info * const *)bx); 602 603 if (a->address < b->address) 604 return -1; 605 else if (a->address > b->address) 606 return 1; 607 else if (a->size < b->size) 608 return -1; 609 else if (a->size > b->size) 610 return 1; 611 else 612 return 0; 613 } 614 615 static void sort_reserve_entries(struct dt_info *dti) 616 { 617 struct reserve_info *ri, **tbl; 618 int n = 0, i = 0; 619 620 for (ri = dti->reservelist; 621 ri; 622 ri = ri->next) 623 n++; 624 625 if (n == 0) 626 return; 627 628 tbl = xmalloc(n * sizeof(*tbl)); 629 630 for (ri = dti->reservelist; 631 ri; 632 ri = ri->next) 633 tbl[i++] = ri; 634 635 qsort(tbl, n, sizeof(*tbl), cmp_reserve_info); 636 637 dti->reservelist = tbl[0]; 638 for (i = 0; i < (n-1); i++) 639 tbl[i]->next = tbl[i+1]; 640 tbl[n-1]->next = NULL; 641 642 free(tbl); 643 } 644 645 static int cmp_prop(const void *ax, const void *bx) 646 { 647 const struct property *a, *b; 648 649 a = *((const struct property * const *)ax); 650 b = *((const struct property * const *)bx); 651 652 return strcmp(a->name, b->name); 653 } 654 655 static void sort_properties(struct node *node) 656 { 657 int n = 0, i = 0; 658 struct property *prop, **tbl; 659 660 for_each_property_withdel(node, prop) 661 n++; 662 663 if (n == 0) 664 return; 665 666 tbl = xmalloc(n * sizeof(*tbl)); 667 668 for_each_property_withdel(node, prop) 669 tbl[i++] = prop; 670 671 qsort(tbl, n, sizeof(*tbl), cmp_prop); 672 673 node->proplist = tbl[0]; 674 for (i = 0; i < (n-1); i++) 675 tbl[i]->next = tbl[i+1]; 676 tbl[n-1]->next = NULL; 677 678 free(tbl); 679 } 680 681 static int cmp_subnode(const void *ax, const void *bx) 682 { 683 const struct node *a, *b; 684 685 a = *((const struct node * const *)ax); 686 b = *((const struct node * const *)bx); 687 688 return strcmp(a->name, b->name); 689 } 690 691 static void sort_subnodes(struct node *node) 692 { 693 int n = 0, i = 0; 694 struct node *subnode, **tbl; 695 696 for_each_child_withdel(node, subnode) 697 n++; 698 699 if (n == 0) 700 return; 701 702 tbl = xmalloc(n * sizeof(*tbl)); 703 704 for_each_child_withdel(node, subnode) 705 tbl[i++] = subnode; 706 707 qsort(tbl, n, sizeof(*tbl), cmp_subnode); 708 709 node->children = tbl[0]; 710 for (i = 0; i < (n-1); i++) 711 tbl[i]->next_sibling = tbl[i+1]; 712 tbl[n-1]->next_sibling = NULL; 713 714 free(tbl); 715 } 716 717 static void sort_node(struct node *node) 718 { 719 struct node *c; 720 721 sort_properties(node); 722 sort_subnodes(node); 723 for_each_child_withdel(node, c) 724 sort_node(c); 725 } 726 727 void sort_tree(struct dt_info *dti) 728 { 729 sort_reserve_entries(dti); 730 sort_node(dti->dt); 731 } 732 733 /* utility helper to avoid code duplication */ 734 static struct node *build_and_name_child_node(struct node *parent, char *name) 735 { 736 struct node *node; 737 738 node = build_node(NULL, NULL); 739 name_node(node, xstrdup(name)); 740 add_child(parent, node); 741 742 return node; 743 } 744 745 static struct node *build_root_node(struct node *dt, char *name) 746 { 747 struct node *an; 748 749 an = get_subnode(dt, name); 750 if (!an) 751 an = build_and_name_child_node(dt, name); 752 753 if (!an) 754 die("Could not build root node /%s\n", name); 755 756 return an; 757 } 758 759 static bool any_label_tree(struct dt_info *dti, struct node *node) 760 { 761 struct node *c; 762 763 if (node->labels) 764 return true; 765 766 for_each_child(node, c) 767 if (any_label_tree(dti, c)) 768 return true; 769 770 return false; 771 } 772 773 static void generate_label_tree_internal(struct dt_info *dti, 774 struct node *an, struct node *node, 775 bool allocph) 776 { 777 struct node *dt = dti->dt; 778 struct node *c; 779 struct property *p; 780 struct label *l; 781 782 /* if there are labels */ 783 if (node->labels) { 784 785 /* now add the label in the node */ 786 for_each_label(node->labels, l) { 787 788 /* check whether the label already exists */ 789 p = get_property(an, l->label); 790 if (p) { 791 fprintf(stderr, "WARNING: label %s already" 792 " exists in /%s", l->label, 793 an->name); 794 continue; 795 } 796 797 /* insert it */ 798 p = build_property(l->label, 799 data_copy_mem(node->fullpath, 800 strlen(node->fullpath) + 1)); 801 add_property(an, p); 802 } 803 804 /* force allocation of a phandle for this node */ 805 if (allocph) 806 (void)get_node_phandle(dt, node); 807 } 808 809 for_each_child(node, c) 810 generate_label_tree_internal(dti, an, c, allocph); 811 } 812 813 static bool any_fixup_tree(struct dt_info *dti, struct node *node) 814 { 815 struct node *c; 816 struct property *prop; 817 struct marker *m; 818 819 for_each_property(node, prop) { 820 m = prop->val.markers; 821 for_each_marker_of_type(m, REF_PHANDLE) { 822 if (!get_node_by_ref(dti->dt, m->ref)) 823 return true; 824 } 825 } 826 827 for_each_child(node, c) { 828 if (any_fixup_tree(dti, c)) 829 return true; 830 } 831 832 return false; 833 } 834 835 static void add_fixup_entry(struct dt_info *dti, struct node *fn, 836 struct node *node, struct property *prop, 837 struct marker *m) 838 { 839 char *entry; 840 841 /* m->ref can only be a REF_PHANDLE, but check anyway */ 842 assert(m->type == REF_PHANDLE); 843 844 /* there shouldn't be any ':' in the arguments */ 845 if (strchr(node->fullpath, ':') || strchr(prop->name, ':')) 846 die("arguments should not contain ':'\n"); 847 848 xasprintf(&entry, "%s:%s:%u", 849 node->fullpath, prop->name, m->offset); 850 append_to_property(fn, m->ref, entry, strlen(entry) + 1); 851 852 free(entry); 853 } 854 855 static void generate_fixups_tree_internal(struct dt_info *dti, 856 struct node *fn, 857 struct node *node) 858 { 859 struct node *dt = dti->dt; 860 struct node *c; 861 struct property *prop; 862 struct marker *m; 863 struct node *refnode; 864 865 for_each_property(node, prop) { 866 m = prop->val.markers; 867 for_each_marker_of_type(m, REF_PHANDLE) { 868 refnode = get_node_by_ref(dt, m->ref); 869 if (!refnode) 870 add_fixup_entry(dti, fn, node, prop, m); 871 } 872 } 873 874 for_each_child(node, c) 875 generate_fixups_tree_internal(dti, fn, c); 876 } 877 878 static bool any_local_fixup_tree(struct dt_info *dti, struct node *node) 879 { 880 struct node *c; 881 struct property *prop; 882 struct marker *m; 883 884 for_each_property(node, prop) { 885 m = prop->val.markers; 886 for_each_marker_of_type(m, REF_PHANDLE) { 887 if (get_node_by_ref(dti->dt, m->ref)) 888 return true; 889 } 890 } 891 892 for_each_child(node, c) { 893 if (any_local_fixup_tree(dti, c)) 894 return true; 895 } 896 897 return false; 898 } 899 900 static void add_local_fixup_entry(struct dt_info *dti, 901 struct node *lfn, struct node *node, 902 struct property *prop, struct marker *m, 903 struct node *refnode) 904 { 905 struct node *wn, *nwn; /* local fixup node, walk node, new */ 906 fdt32_t value_32; 907 char **compp; 908 int i, depth; 909 910 /* walk back retreiving depth */ 911 depth = 0; 912 for (wn = node; wn; wn = wn->parent) 913 depth++; 914 915 /* allocate name array */ 916 compp = xmalloc(sizeof(*compp) * depth); 917 918 /* store names in the array */ 919 for (wn = node, i = depth - 1; wn; wn = wn->parent, i--) 920 compp[i] = wn->name; 921 922 /* walk the path components creating nodes if they don't exist */ 923 for (wn = lfn, i = 1; i < depth; i++, wn = nwn) { 924 /* if no node exists, create it */ 925 nwn = get_subnode(wn, compp[i]); 926 if (!nwn) 927 nwn = build_and_name_child_node(wn, compp[i]); 928 } 929 930 free(compp); 931 932 value_32 = cpu_to_fdt32(m->offset); 933 append_to_property(wn, prop->name, &value_32, sizeof(value_32)); 934 } 935 936 static void generate_local_fixups_tree_internal(struct dt_info *dti, 937 struct node *lfn, 938 struct node *node) 939 { 940 struct node *dt = dti->dt; 941 struct node *c; 942 struct property *prop; 943 struct marker *m; 944 struct node *refnode; 945 946 for_each_property(node, prop) { 947 m = prop->val.markers; 948 for_each_marker_of_type(m, REF_PHANDLE) { 949 refnode = get_node_by_ref(dt, m->ref); 950 if (refnode) 951 add_local_fixup_entry(dti, lfn, node, prop, m, refnode); 952 } 953 } 954 955 for_each_child(node, c) 956 generate_local_fixups_tree_internal(dti, lfn, c); 957 } 958 959 void generate_label_tree(struct dt_info *dti, char *name, bool allocph) 960 { 961 if (!any_label_tree(dti, dti->dt)) 962 return; 963 generate_label_tree_internal(dti, build_root_node(dti->dt, name), 964 dti->dt, allocph); 965 } 966 967 void generate_fixups_tree(struct dt_info *dti, char *name) 968 { 969 if (!any_fixup_tree(dti, dti->dt)) 970 return; 971 generate_fixups_tree_internal(dti, build_root_node(dti->dt, name), 972 dti->dt); 973 } 974 975 void generate_local_fixups_tree(struct dt_info *dti, char *name) 976 { 977 if (!any_local_fixup_tree(dti, dti->dt)) 978 return; 979 generate_local_fixups_tree_internal(dti, build_root_node(dti->dt, name), 980 dti->dt); 981 } 982