Commit 34153fa3af45d84f3221d9b67ba2ab7e8a220d28
Committed by
Paul Mackerras
1 parent
e28f7faf05
Exists in
master
and in
4 other branches
[PATCH] flattened device tree changes
This patch updates the format of the flattened device-tree passed between the boot trampoline and the kernel to support a more compact representation, for use by embedded systems mostly. Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org> Signed-off-by: Paul Mackerras <paulus@samba.org>
Showing 3 changed files with 209 additions and 68 deletions Side-by-side Diff
arch/ppc64/kernel/prom.c
| ... | ... | @@ -625,8 +625,8 @@ |
| 625 | 625 | |
| 626 | 626 | static inline char *find_flat_dt_string(u32 offset) |
| 627 | 627 | { |
| 628 | - return ((char *)initial_boot_params) + initial_boot_params->off_dt_strings | |
| 629 | - + offset; | |
| 628 | + return ((char *)initial_boot_params) + | |
| 629 | + initial_boot_params->off_dt_strings + offset; | |
| 630 | 630 | } |
| 631 | 631 | |
| 632 | 632 | /** |
| 633 | 633 | |
| 634 | 634 | |
| 635 | 635 | |
| 636 | 636 | |
| ... | ... | @@ -635,26 +635,33 @@ |
| 635 | 635 | * unflatten the tree |
| 636 | 636 | */ |
| 637 | 637 | static int __init scan_flat_dt(int (*it)(unsigned long node, |
| 638 | - const char *full_path, void *data), | |
| 638 | + const char *uname, int depth, | |
| 639 | + void *data), | |
| 639 | 640 | void *data) |
| 640 | 641 | { |
| 641 | 642 | unsigned long p = ((unsigned long)initial_boot_params) + |
| 642 | 643 | initial_boot_params->off_dt_struct; |
| 643 | 644 | int rc = 0; |
| 645 | + int depth = -1; | |
| 644 | 646 | |
| 645 | 647 | do { |
| 646 | 648 | u32 tag = *((u32 *)p); |
| 647 | 649 | char *pathp; |
| 648 | 650 | |
| 649 | 651 | p += 4; |
| 650 | - if (tag == OF_DT_END_NODE) | |
| 652 | + if (tag == OF_DT_END_NODE) { | |
| 653 | + depth --; | |
| 651 | 654 | continue; |
| 655 | + } | |
| 656 | + if (tag == OF_DT_NOP) | |
| 657 | + continue; | |
| 652 | 658 | if (tag == OF_DT_END) |
| 653 | 659 | break; |
| 654 | 660 | if (tag == OF_DT_PROP) { |
| 655 | 661 | u32 sz = *((u32 *)p); |
| 656 | 662 | p += 8; |
| 657 | - p = _ALIGN(p, sz >= 8 ? 8 : 4); | |
| 663 | + if (initial_boot_params->version < 0x10) | |
| 664 | + p = _ALIGN(p, sz >= 8 ? 8 : 4); | |
| 658 | 665 | p += sz; |
| 659 | 666 | p = _ALIGN(p, 4); |
| 660 | 667 | continue; |
| 661 | 668 | |
| ... | ... | @@ -664,9 +671,18 @@ |
| 664 | 671 | " device tree !\n", tag); |
| 665 | 672 | return -EINVAL; |
| 666 | 673 | } |
| 674 | + depth++; | |
| 667 | 675 | pathp = (char *)p; |
| 668 | 676 | p = _ALIGN(p + strlen(pathp) + 1, 4); |
| 669 | - rc = it(p, pathp, data); | |
| 677 | + if ((*pathp) == '/') { | |
| 678 | + char *lp, *np; | |
| 679 | + for (lp = NULL, np = pathp; *np; np++) | |
| 680 | + if ((*np) == '/') | |
| 681 | + lp = np+1; | |
| 682 | + if (lp != NULL) | |
| 683 | + pathp = lp; | |
| 684 | + } | |
| 685 | + rc = it(p, pathp, depth, data); | |
| 670 | 686 | if (rc != 0) |
| 671 | 687 | break; |
| 672 | 688 | } while(1); |
| 673 | 689 | |
| 674 | 690 | |
| ... | ... | @@ -689,17 +705,21 @@ |
| 689 | 705 | const char *nstr; |
| 690 | 706 | |
| 691 | 707 | p += 4; |
| 708 | + if (tag == OF_DT_NOP) | |
| 709 | + continue; | |
| 692 | 710 | if (tag != OF_DT_PROP) |
| 693 | 711 | return NULL; |
| 694 | 712 | |
| 695 | 713 | sz = *((u32 *)p); |
| 696 | 714 | noff = *((u32 *)(p + 4)); |
| 697 | 715 | p += 8; |
| 698 | - p = _ALIGN(p, sz >= 8 ? 8 : 4); | |
| 716 | + if (initial_boot_params->version < 0x10) | |
| 717 | + p = _ALIGN(p, sz >= 8 ? 8 : 4); | |
| 699 | 718 | |
| 700 | 719 | nstr = find_flat_dt_string(noff); |
| 701 | 720 | if (nstr == NULL) { |
| 702 | - printk(KERN_WARNING "Can't find property index name !\n"); | |
| 721 | + printk(KERN_WARNING "Can't find property index" | |
| 722 | + " name !\n"); | |
| 703 | 723 | return NULL; |
| 704 | 724 | } |
| 705 | 725 | if (strcmp(name, nstr) == 0) { |
| ... | ... | @@ -713,7 +733,7 @@ |
| 713 | 733 | } |
| 714 | 734 | |
| 715 | 735 | static void *__init unflatten_dt_alloc(unsigned long *mem, unsigned long size, |
| 716 | - unsigned long align) | |
| 736 | + unsigned long align) | |
| 717 | 737 | { |
| 718 | 738 | void *res; |
| 719 | 739 | |
| 720 | 740 | |
| ... | ... | @@ -727,13 +747,16 @@ |
| 727 | 747 | static unsigned long __init unflatten_dt_node(unsigned long mem, |
| 728 | 748 | unsigned long *p, |
| 729 | 749 | struct device_node *dad, |
| 730 | - struct device_node ***allnextpp) | |
| 750 | + struct device_node ***allnextpp, | |
| 751 | + unsigned long fpsize) | |
| 731 | 752 | { |
| 732 | 753 | struct device_node *np; |
| 733 | 754 | struct property *pp, **prev_pp = NULL; |
| 734 | 755 | char *pathp; |
| 735 | 756 | u32 tag; |
| 736 | - unsigned int l; | |
| 757 | + unsigned int l, allocl; | |
| 758 | + int has_name = 0; | |
| 759 | + int new_format = 0; | |
| 737 | 760 | |
| 738 | 761 | tag = *((u32 *)(*p)); |
| 739 | 762 | if (tag != OF_DT_BEGIN_NODE) { |
| 740 | 763 | |
| 741 | 764 | |
| 742 | 765 | |
| ... | ... | @@ -742,21 +765,62 @@ |
| 742 | 765 | } |
| 743 | 766 | *p += 4; |
| 744 | 767 | pathp = (char *)*p; |
| 745 | - l = strlen(pathp) + 1; | |
| 768 | + l = allocl = strlen(pathp) + 1; | |
| 746 | 769 | *p = _ALIGN(*p + l, 4); |
| 747 | 770 | |
| 748 | - np = unflatten_dt_alloc(&mem, sizeof(struct device_node) + l, | |
| 771 | + /* version 0x10 has a more compact unit name here instead of the full | |
| 772 | + * path. we accumulate the full path size using "fpsize", we'll rebuild | |
| 773 | + * it later. We detect this because the first character of the name is | |
| 774 | + * not '/'. | |
| 775 | + */ | |
| 776 | + if ((*pathp) != '/') { | |
| 777 | + new_format = 1; | |
| 778 | + if (fpsize == 0) { | |
| 779 | + /* root node: special case. fpsize accounts for path | |
| 780 | + * plus terminating zero. root node only has '/', so | |
| 781 | + * fpsize should be 2, but we want to avoid the first | |
| 782 | + * level nodes to have two '/' so we use fpsize 1 here | |
| 783 | + */ | |
| 784 | + fpsize = 1; | |
| 785 | + allocl = 2; | |
| 786 | + } else { | |
| 787 | + /* account for '/' and path size minus terminal 0 | |
| 788 | + * already in 'l' | |
| 789 | + */ | |
| 790 | + fpsize += l; | |
| 791 | + allocl = fpsize; | |
| 792 | + } | |
| 793 | + } | |
| 794 | + | |
| 795 | + | |
| 796 | + np = unflatten_dt_alloc(&mem, sizeof(struct device_node) + allocl, | |
| 749 | 797 | __alignof__(struct device_node)); |
| 750 | 798 | if (allnextpp) { |
| 751 | 799 | memset(np, 0, sizeof(*np)); |
| 752 | 800 | np->full_name = ((char*)np) + sizeof(struct device_node); |
| 753 | - memcpy(np->full_name, pathp, l); | |
| 801 | + if (new_format) { | |
| 802 | + char *p = np->full_name; | |
| 803 | + /* rebuild full path for new format */ | |
| 804 | + if (dad && dad->parent) { | |
| 805 | + strcpy(p, dad->full_name); | |
| 806 | +#ifdef DEBUG | |
| 807 | + if ((strlen(p) + l + 1) != allocl) { | |
| 808 | + DBG("%s: p: %d, l: %d, a: %d\n", | |
| 809 | + pathp, strlen(p), l, allocl); | |
| 810 | + } | |
| 811 | +#endif | |
| 812 | + p += strlen(p); | |
| 813 | + } | |
| 814 | + *(p++) = '/'; | |
| 815 | + memcpy(p, pathp, l); | |
| 816 | + } else | |
| 817 | + memcpy(np->full_name, pathp, l); | |
| 754 | 818 | prev_pp = &np->properties; |
| 755 | 819 | **allnextpp = np; |
| 756 | 820 | *allnextpp = &np->allnext; |
| 757 | 821 | if (dad != NULL) { |
| 758 | 822 | np->parent = dad; |
| 759 | - /* we temporarily use the `next' field as `last_child'. */ | |
| 823 | + /* we temporarily use the next field as `last_child'*/ | |
| 760 | 824 | if (dad->next == 0) |
| 761 | 825 | dad->child = np; |
| 762 | 826 | else |
| 763 | 827 | |
| 764 | 828 | |
| ... | ... | @@ -770,18 +834,26 @@ |
| 770 | 834 | char *pname; |
| 771 | 835 | |
| 772 | 836 | tag = *((u32 *)(*p)); |
| 837 | + if (tag == OF_DT_NOP) { | |
| 838 | + *p += 4; | |
| 839 | + continue; | |
| 840 | + } | |
| 773 | 841 | if (tag != OF_DT_PROP) |
| 774 | 842 | break; |
| 775 | 843 | *p += 4; |
| 776 | 844 | sz = *((u32 *)(*p)); |
| 777 | 845 | noff = *((u32 *)((*p) + 4)); |
| 778 | - *p = _ALIGN((*p) + 8, sz >= 8 ? 8 : 4); | |
| 846 | + *p += 8; | |
| 847 | + if (initial_boot_params->version < 0x10) | |
| 848 | + *p = _ALIGN(*p, sz >= 8 ? 8 : 4); | |
| 779 | 849 | |
| 780 | 850 | pname = find_flat_dt_string(noff); |
| 781 | 851 | if (pname == NULL) { |
| 782 | 852 | printk("Can't find property name in list !\n"); |
| 783 | 853 | break; |
| 784 | 854 | } |
| 855 | + if (strcmp(pname, "name") == 0) | |
| 856 | + has_name = 1; | |
| 785 | 857 | l = strlen(pname) + 1; |
| 786 | 858 | pp = unflatten_dt_alloc(&mem, sizeof(struct property), |
| 787 | 859 | __alignof__(struct property)); |
| ... | ... | @@ -801,6 +873,36 @@ |
| 801 | 873 | } |
| 802 | 874 | *p = _ALIGN((*p) + sz, 4); |
| 803 | 875 | } |
| 876 | + /* with version 0x10 we may not have the name property, recreate | |
| 877 | + * it here from the unit name if absent | |
| 878 | + */ | |
| 879 | + if (!has_name) { | |
| 880 | + char *p = pathp, *ps = pathp, *pa = NULL; | |
| 881 | + int sz; | |
| 882 | + | |
| 883 | + while (*p) { | |
| 884 | + if ((*p) == '@') | |
| 885 | + pa = p; | |
| 886 | + if ((*p) == '/') | |
| 887 | + ps = p + 1; | |
| 888 | + p++; | |
| 889 | + } | |
| 890 | + if (pa < ps) | |
| 891 | + pa = p; | |
| 892 | + sz = (pa - ps) + 1; | |
| 893 | + pp = unflatten_dt_alloc(&mem, sizeof(struct property) + sz, | |
| 894 | + __alignof__(struct property)); | |
| 895 | + if (allnextpp) { | |
| 896 | + pp->name = "name"; | |
| 897 | + pp->length = sz; | |
| 898 | + pp->value = (unsigned char *)(pp + 1); | |
| 899 | + *prev_pp = pp; | |
| 900 | + prev_pp = &pp->next; | |
| 901 | + memcpy(pp->value, ps, sz - 1); | |
| 902 | + ((char *)pp->value)[sz - 1] = 0; | |
| 903 | + DBG("fixed up name for %s -> %s\n", pathp, pp->value); | |
| 904 | + } | |
| 905 | + } | |
| 804 | 906 | if (allnextpp) { |
| 805 | 907 | *prev_pp = NULL; |
| 806 | 908 | np->name = get_property(np, "name", NULL); |
| ... | ... | @@ -812,7 +914,7 @@ |
| 812 | 914 | np->type = "<NULL>"; |
| 813 | 915 | } |
| 814 | 916 | while (tag == OF_DT_BEGIN_NODE) { |
| 815 | - mem = unflatten_dt_node(mem, p, np, allnextpp); | |
| 917 | + mem = unflatten_dt_node(mem, p, np, allnextpp, fpsize); | |
| 816 | 918 | tag = *((u32 *)(*p)); |
| 817 | 919 | } |
| 818 | 920 | if (tag != OF_DT_END_NODE) { |
| 819 | 921 | |
| 820 | 922 | |
| 821 | 923 | |
| 822 | 924 | |
| ... | ... | @@ -842,21 +944,27 @@ |
| 842 | 944 | /* First pass, scan for size */ |
| 843 | 945 | start = ((unsigned long)initial_boot_params) + |
| 844 | 946 | initial_boot_params->off_dt_struct; |
| 845 | - size = unflatten_dt_node(0, &start, NULL, NULL); | |
| 947 | + size = unflatten_dt_node(0, &start, NULL, NULL, 0); | |
| 948 | + size = (size | 3) + 1; | |
| 846 | 949 | |
| 847 | 950 | DBG(" size is %lx, allocating...\n", size); |
| 848 | 951 | |
| 849 | 952 | /* Allocate memory for the expanded device tree */ |
| 850 | - mem = (unsigned long)abs_to_virt(lmb_alloc(size, | |
| 953 | + mem = (unsigned long)abs_to_virt(lmb_alloc(size + 4, | |
| 851 | 954 | __alignof__(struct device_node))); |
| 955 | + ((u32 *)mem)[size / 4] = 0xdeadbeef; | |
| 956 | + | |
| 852 | 957 | DBG(" unflattening...\n", mem); |
| 853 | 958 | |
| 854 | 959 | /* Second pass, do actual unflattening */ |
| 855 | 960 | start = ((unsigned long)initial_boot_params) + |
| 856 | 961 | initial_boot_params->off_dt_struct; |
| 857 | - unflatten_dt_node(mem, &start, NULL, &allnextp); | |
| 962 | + unflatten_dt_node(mem, &start, NULL, &allnextp, 0); | |
| 858 | 963 | if (*((u32 *)start) != OF_DT_END) |
| 859 | - printk(KERN_WARNING "Weird tag at end of tree: %x\n", *((u32 *)start)); | |
| 964 | + printk(KERN_WARNING "Weird tag at end of tree: %08x\n", *((u32 *)start)); | |
| 965 | + if (((u32 *)mem)[size / 4] != 0xdeadbeef) | |
| 966 | + printk(KERN_WARNING "End of tree marker overwritten: %08x\n", | |
| 967 | + ((u32 *)mem)[size / 4] ); | |
| 860 | 968 | *allnextp = NULL; |
| 861 | 969 | |
| 862 | 970 | /* Get pointer to OF "/chosen" node for use everywhere */ |
| ... | ... | @@ -880,7 +988,7 @@ |
| 880 | 988 | |
| 881 | 989 | |
| 882 | 990 | static int __init early_init_dt_scan_cpus(unsigned long node, |
| 883 | - const char *full_path, void *data) | |
| 991 | + const char *uname, int depth, void *data) | |
| 884 | 992 | { |
| 885 | 993 | char *type = get_flat_dt_prop(node, "device_type", NULL); |
| 886 | 994 | u32 *prop; |
| 887 | 995 | |
| ... | ... | @@ -947,13 +1055,15 @@ |
| 947 | 1055 | } |
| 948 | 1056 | |
| 949 | 1057 | static int __init early_init_dt_scan_chosen(unsigned long node, |
| 950 | - const char *full_path, void *data) | |
| 1058 | + const char *uname, int depth, void *data) | |
| 951 | 1059 | { |
| 952 | 1060 | u32 *prop; |
| 953 | 1061 | u64 *prop64; |
| 954 | 1062 | extern unsigned long memory_limit, tce_alloc_start, tce_alloc_end; |
| 955 | 1063 | |
| 956 | - if (strcmp(full_path, "/chosen") != 0) | |
| 1064 | + DBG("search \"chosen\", depth: %d, uname: %s\n", depth, uname); | |
| 1065 | + | |
| 1066 | + if (depth != 1 || strcmp(uname, "chosen") != 0) | |
| 957 | 1067 | return 0; |
| 958 | 1068 | |
| 959 | 1069 | /* get platform type */ |
| 960 | 1070 | |
| 961 | 1071 | |
| 962 | 1072 | |
| ... | ... | @@ -1003,18 +1113,20 @@ |
| 1003 | 1113 | } |
| 1004 | 1114 | |
| 1005 | 1115 | static int __init early_init_dt_scan_root(unsigned long node, |
| 1006 | - const char *full_path, void *data) | |
| 1116 | + const char *uname, int depth, void *data) | |
| 1007 | 1117 | { |
| 1008 | 1118 | u32 *prop; |
| 1009 | 1119 | |
| 1010 | - if (strcmp(full_path, "/") != 0) | |
| 1120 | + if (depth != 0) | |
| 1011 | 1121 | return 0; |
| 1012 | 1122 | |
| 1013 | 1123 | prop = (u32 *)get_flat_dt_prop(node, "#size-cells", NULL); |
| 1014 | 1124 | dt_root_size_cells = (prop == NULL) ? 1 : *prop; |
| 1015 | - | |
| 1125 | + DBG("dt_root_size_cells = %x\n", dt_root_size_cells); | |
| 1126 | + | |
| 1016 | 1127 | prop = (u32 *)get_flat_dt_prop(node, "#address-cells", NULL); |
| 1017 | 1128 | dt_root_addr_cells = (prop == NULL) ? 2 : *prop; |
| 1129 | + DBG("dt_root_addr_cells = %x\n", dt_root_addr_cells); | |
| 1018 | 1130 | |
| 1019 | 1131 | /* break now */ |
| 1020 | 1132 | return 1; |
| ... | ... | @@ -1042,7 +1154,7 @@ |
| 1042 | 1154 | |
| 1043 | 1155 | |
| 1044 | 1156 | static int __init early_init_dt_scan_memory(unsigned long node, |
| 1045 | - const char *full_path, void *data) | |
| 1157 | + const char *uname, int depth, void *data) | |
| 1046 | 1158 | { |
| 1047 | 1159 | char *type = get_flat_dt_prop(node, "device_type", NULL); |
| 1048 | 1160 | cell_t *reg, *endp; |
| ... | ... | @@ -1058,7 +1170,9 @@ |
| 1058 | 1170 | |
| 1059 | 1171 | endp = reg + (l / sizeof(cell_t)); |
| 1060 | 1172 | |
| 1061 | - DBG("memory scan node %s ...\n", full_path); | |
| 1173 | + DBG("memory scan node %s ..., reg size %ld, data: %x %x %x %x, ...\n", | |
| 1174 | + uname, l, reg[0], reg[1], reg[2], reg[3]); | |
| 1175 | + | |
| 1062 | 1176 | while ((endp - reg) >= (dt_root_addr_cells + dt_root_size_cells)) { |
| 1063 | 1177 | unsigned long base, size; |
| 1064 | 1178 | |
| 1065 | 1179 | |
| ... | ... | @@ -1469,10 +1583,11 @@ |
| 1469 | 1583 | struct device_node *np = allnodes; |
| 1470 | 1584 | |
| 1471 | 1585 | read_lock(&devtree_lock); |
| 1472 | - for (; np != 0; np = np->allnext) | |
| 1586 | + for (; np != 0; np = np->allnext) { | |
| 1473 | 1587 | if (np->full_name != 0 && strcasecmp(np->full_name, path) == 0 |
| 1474 | 1588 | && of_node_get(np)) |
| 1475 | 1589 | break; |
| 1590 | + } | |
| 1476 | 1591 | read_unlock(&devtree_lock); |
| 1477 | 1592 | return np; |
| 1478 | 1593 | } |
arch/ppc64/kernel/prom_init.c
| ... | ... | @@ -1534,7 +1534,8 @@ |
| 1534 | 1534 | */ |
| 1535 | 1535 | #define MAX_PROPERTY_NAME 64 |
| 1536 | 1536 | |
| 1537 | -static void __init scan_dt_build_strings(phandle node, unsigned long *mem_start, | |
| 1537 | +static void __init scan_dt_build_strings(phandle node, | |
| 1538 | + unsigned long *mem_start, | |
| 1538 | 1539 | unsigned long *mem_end) |
| 1539 | 1540 | { |
| 1540 | 1541 | unsigned long offset = reloc_offset(); |
| 1541 | 1542 | |
| 1542 | 1543 | |
| ... | ... | @@ -1547,16 +1548,21 @@ |
| 1547 | 1548 | /* get and store all property names */ |
| 1548 | 1549 | prev_name = RELOC(""); |
| 1549 | 1550 | for (;;) { |
| 1550 | - int rc; | |
| 1551 | - | |
| 1552 | 1551 | /* 64 is max len of name including nul. */ |
| 1553 | 1552 | namep = make_room(mem_start, mem_end, MAX_PROPERTY_NAME, 1); |
| 1554 | - rc = call_prom("nextprop", 3, 1, node, prev_name, namep); | |
| 1555 | - if (rc != 1) { | |
| 1553 | + if (call_prom("nextprop", 3, 1, node, prev_name, namep) != 1) { | |
| 1556 | 1554 | /* No more nodes: unwind alloc */ |
| 1557 | 1555 | *mem_start = (unsigned long)namep; |
| 1558 | 1556 | break; |
| 1559 | 1557 | } |
| 1558 | + | |
| 1559 | + /* skip "name" */ | |
| 1560 | + if (strcmp(namep, RELOC("name")) == 0) { | |
| 1561 | + *mem_start = (unsigned long)namep; | |
| 1562 | + prev_name = RELOC("name"); | |
| 1563 | + continue; | |
| 1564 | + } | |
| 1565 | + /* get/create string entry */ | |
| 1560 | 1566 | soff = dt_find_string(namep); |
| 1561 | 1567 | if (soff != 0) { |
| 1562 | 1568 | *mem_start = (unsigned long)namep; |
| ... | ... | @@ -1571,7 +1577,7 @@ |
| 1571 | 1577 | |
| 1572 | 1578 | /* do all our children */ |
| 1573 | 1579 | child = call_prom("child", 1, 1, node); |
| 1574 | - while (child != (phandle)0) { | |
| 1580 | + while (child != 0) { | |
| 1575 | 1581 | scan_dt_build_strings(child, mem_start, mem_end); |
| 1576 | 1582 | child = call_prom("peer", 1, 1, child); |
| 1577 | 1583 | } |
| 1578 | 1584 | |
| 1579 | 1585 | |
| 1580 | 1586 | |
| ... | ... | @@ -1580,17 +1586,14 @@ |
| 1580 | 1586 | static void __init scan_dt_build_struct(phandle node, unsigned long *mem_start, |
| 1581 | 1587 | unsigned long *mem_end) |
| 1582 | 1588 | { |
| 1583 | - int l, align; | |
| 1584 | 1589 | phandle child; |
| 1585 | - char *namep, *prev_name, *sstart, *p, *ep; | |
| 1590 | + char *namep, *prev_name, *sstart, *p, *ep, *lp, *path; | |
| 1586 | 1591 | unsigned long soff; |
| 1587 | 1592 | unsigned char *valp; |
| 1588 | 1593 | unsigned long offset = reloc_offset(); |
| 1589 | - char pname[MAX_PROPERTY_NAME]; | |
| 1590 | - char *path; | |
| 1594 | + static char pname[MAX_PROPERTY_NAME]; | |
| 1595 | + int l; | |
| 1591 | 1596 | |
| 1592 | - path = RELOC(prom_scratch); | |
| 1593 | - | |
| 1594 | 1597 | dt_push_token(OF_DT_BEGIN_NODE, mem_start, mem_end); |
| 1595 | 1598 | |
| 1596 | 1599 | /* get the node's full name */ |
| 1597 | 1600 | |
| 1598 | 1601 | |
| 1599 | 1602 | |
| 1600 | 1603 | |
| ... | ... | @@ -1599,23 +1602,33 @@ |
| 1599 | 1602 | namep, *mem_end - *mem_start); |
| 1600 | 1603 | if (l >= 0) { |
| 1601 | 1604 | /* Didn't fit? Get more room. */ |
| 1602 | - if (l+1 > *mem_end - *mem_start) { | |
| 1605 | + if ((l+1) > (*mem_end - *mem_start)) { | |
| 1603 | 1606 | namep = make_room(mem_start, mem_end, l+1, 1); |
| 1604 | 1607 | call_prom("package-to-path", 3, 1, node, namep, l); |
| 1605 | 1608 | } |
| 1606 | 1609 | namep[l] = '\0'; |
| 1610 | + | |
| 1607 | 1611 | /* Fixup an Apple bug where they have bogus \0 chars in the |
| 1608 | 1612 | * middle of the path in some properties |
| 1609 | 1613 | */ |
| 1610 | 1614 | for (p = namep, ep = namep + l; p < ep; p++) |
| 1611 | 1615 | if (*p == '\0') { |
| 1612 | 1616 | memmove(p, p+1, ep - p); |
| 1613 | - ep--; l--; | |
| 1617 | + ep--; l--; p--; | |
| 1614 | 1618 | } |
| 1615 | - *mem_start = _ALIGN(((unsigned long) namep) + strlen(namep) + 1, 4); | |
| 1619 | + | |
| 1620 | + /* now try to extract the unit name in that mess */ | |
| 1621 | + for (p = namep, lp = NULL; *p; p++) | |
| 1622 | + if (*p == '/') | |
| 1623 | + lp = p + 1; | |
| 1624 | + if (lp != NULL) | |
| 1625 | + memmove(namep, lp, strlen(lp) + 1); | |
| 1626 | + *mem_start = _ALIGN(((unsigned long) namep) + | |
| 1627 | + strlen(namep) + 1, 4); | |
| 1616 | 1628 | } |
| 1617 | 1629 | |
| 1618 | 1630 | /* get it again for debugging */ |
| 1631 | + path = RELOC(prom_scratch); | |
| 1619 | 1632 | memset(path, 0, PROM_SCRATCH_SIZE); |
| 1620 | 1633 | call_prom("package-to-path", 3, 1, node, path, PROM_SCRATCH_SIZE-1); |
| 1621 | 1634 | |
| 1622 | 1635 | |
| 1623 | 1636 | |
| 1624 | 1637 | |
| 1625 | 1638 | |
| ... | ... | @@ -1623,23 +1636,27 @@ |
| 1623 | 1636 | prev_name = RELOC(""); |
| 1624 | 1637 | sstart = (char *)RELOC(dt_string_start); |
| 1625 | 1638 | for (;;) { |
| 1626 | - int rc; | |
| 1627 | - | |
| 1628 | - rc = call_prom("nextprop", 3, 1, node, prev_name, pname); | |
| 1629 | - if (rc != 1) | |
| 1639 | + if (call_prom("nextprop", 3, 1, node, prev_name, | |
| 1640 | + RELOC(pname)) != 1) | |
| 1630 | 1641 | break; |
| 1631 | 1642 | |
| 1643 | + /* skip "name" */ | |
| 1644 | + if (strcmp(RELOC(pname), RELOC("name")) == 0) { | |
| 1645 | + prev_name = RELOC("name"); | |
| 1646 | + continue; | |
| 1647 | + } | |
| 1648 | + | |
| 1632 | 1649 | /* find string offset */ |
| 1633 | - soff = dt_find_string(pname); | |
| 1650 | + soff = dt_find_string(RELOC(pname)); | |
| 1634 | 1651 | if (soff == 0) { |
| 1635 | - prom_printf("WARNING: Can't find string index for <%s>, node %s\n", | |
| 1636 | - pname, path); | |
| 1652 | + prom_printf("WARNING: Can't find string index for" | |
| 1653 | + " <%s>, node %s\n", RELOC(pname), path); | |
| 1637 | 1654 | break; |
| 1638 | 1655 | } |
| 1639 | 1656 | prev_name = sstart + soff; |
| 1640 | 1657 | |
| 1641 | 1658 | /* get length */ |
| 1642 | - l = call_prom("getproplen", 2, 1, node, pname); | |
| 1659 | + l = call_prom("getproplen", 2, 1, node, RELOC(pname)); | |
| 1643 | 1660 | |
| 1644 | 1661 | /* sanity checks */ |
| 1645 | 1662 | if (l == PROM_ERROR) |
| ... | ... | @@ -1648,7 +1665,7 @@ |
| 1648 | 1665 | prom_printf("WARNING: ignoring large property "); |
| 1649 | 1666 | /* It seems OF doesn't null-terminate the path :-( */ |
| 1650 | 1667 | prom_printf("[%s] ", path); |
| 1651 | - prom_printf("%s length 0x%x\n", pname, l); | |
| 1668 | + prom_printf("%s length 0x%x\n", RELOC(pname), l); | |
| 1652 | 1669 | continue; |
| 1653 | 1670 | } |
| 1654 | 1671 | |
| 1655 | 1672 | |
| ... | ... | @@ -1658,17 +1675,16 @@ |
| 1658 | 1675 | dt_push_token(soff, mem_start, mem_end); |
| 1659 | 1676 | |
| 1660 | 1677 | /* push property content */ |
| 1661 | - align = (l >= 8) ? 8 : 4; | |
| 1662 | - valp = make_room(mem_start, mem_end, l, align); | |
| 1663 | - call_prom("getprop", 4, 1, node, pname, valp, l); | |
| 1678 | + valp = make_room(mem_start, mem_end, l, 4); | |
| 1679 | + call_prom("getprop", 4, 1, node, RELOC(pname), valp, l); | |
| 1664 | 1680 | *mem_start = _ALIGN(*mem_start, 4); |
| 1665 | 1681 | } |
| 1666 | 1682 | |
| 1667 | 1683 | /* Add a "linux,phandle" property. */ |
| 1668 | 1684 | soff = dt_find_string(RELOC("linux,phandle")); |
| 1669 | 1685 | if (soff == 0) |
| 1670 | - prom_printf("WARNING: Can't find string index for <linux-phandle>" | |
| 1671 | - " node %s\n", path); | |
| 1686 | + prom_printf("WARNING: Can't find string index for" | |
| 1687 | + " <linux-phandle> node %s\n", path); | |
| 1672 | 1688 | else { |
| 1673 | 1689 | dt_push_token(OF_DT_PROP, mem_start, mem_end); |
| 1674 | 1690 | dt_push_token(4, mem_start, mem_end); |
| ... | ... | @@ -1679,7 +1695,7 @@ |
| 1679 | 1695 | |
| 1680 | 1696 | /* do all our children */ |
| 1681 | 1697 | child = call_prom("child", 1, 1, node); |
| 1682 | - while (child != (phandle)0) { | |
| 1698 | + while (child != 0) { | |
| 1683 | 1699 | scan_dt_build_struct(child, mem_start, mem_end); |
| 1684 | 1700 | child = call_prom("peer", 1, 1, child); |
| 1685 | 1701 | } |
| ... | ... | @@ -1718,7 +1734,8 @@ |
| 1718 | 1734 | |
| 1719 | 1735 | /* Build header and make room for mem rsv map */ |
| 1720 | 1736 | mem_start = _ALIGN(mem_start, 4); |
| 1721 | - hdr = make_room(&mem_start, &mem_end, sizeof(struct boot_param_header), 4); | |
| 1737 | + hdr = make_room(&mem_start, &mem_end, | |
| 1738 | + sizeof(struct boot_param_header), 4); | |
| 1722 | 1739 | RELOC(dt_header_start) = (unsigned long)hdr; |
| 1723 | 1740 | rsvmap = make_room(&mem_start, &mem_end, sizeof(mem_reserve_map), 8); |
| 1724 | 1741 | |
| 1725 | 1742 | |
| ... | ... | @@ -1731,11 +1748,11 @@ |
| 1731 | 1748 | namep = make_room(&mem_start, &mem_end, 16, 1); |
| 1732 | 1749 | strcpy(namep, RELOC("linux,phandle")); |
| 1733 | 1750 | mem_start = (unsigned long)namep + strlen(namep) + 1; |
| 1734 | - RELOC(dt_string_end) = mem_start; | |
| 1735 | 1751 | |
| 1736 | 1752 | /* Build string array */ |
| 1737 | 1753 | prom_printf("Building dt strings...\n"); |
| 1738 | 1754 | scan_dt_build_strings(root, &mem_start, &mem_end); |
| 1755 | + RELOC(dt_string_end) = mem_start; | |
| 1739 | 1756 | |
| 1740 | 1757 | /* Build structure */ |
| 1741 | 1758 | mem_start = PAGE_ALIGN(mem_start); |
| 1742 | 1759 | |
| ... | ... | @@ -1750,9 +1767,11 @@ |
| 1750 | 1767 | hdr->totalsize = RELOC(dt_struct_end) - RELOC(dt_header_start); |
| 1751 | 1768 | hdr->off_dt_struct = RELOC(dt_struct_start) - RELOC(dt_header_start); |
| 1752 | 1769 | hdr->off_dt_strings = RELOC(dt_string_start) - RELOC(dt_header_start); |
| 1770 | + hdr->dt_strings_size = RELOC(dt_string_end) - RELOC(dt_string_start); | |
| 1753 | 1771 | hdr->off_mem_rsvmap = ((unsigned long)rsvmap) - RELOC(dt_header_start); |
| 1754 | 1772 | hdr->version = OF_DT_VERSION; |
| 1755 | - hdr->last_comp_version = 1; | |
| 1773 | + /* Version 16 is not backward compatible */ | |
| 1774 | + hdr->last_comp_version = 0x10; | |
| 1756 | 1775 | |
| 1757 | 1776 | /* Reserve the whole thing and copy the reserve map in, we |
| 1758 | 1777 | * also bump mem_reserve_cnt to cause further reservations to |
| ... | ... | @@ -1808,6 +1827,9 @@ |
| 1808 | 1827 | /* does it need fixup ? */ |
| 1809 | 1828 | if (prom_getproplen(i2c, "interrupts") > 0) |
| 1810 | 1829 | return; |
| 1830 | + | |
| 1831 | + prom_printf("fixing up bogus interrupts for u3 i2c...\n"); | |
| 1832 | + | |
| 1811 | 1833 | /* interrupt on this revision of u3 is number 0 and level */ |
| 1812 | 1834 | interrupts[0] = 0; |
| 1813 | 1835 | interrupts[1] = 1; |
include/asm-ppc64/prom.h
| ... | ... | @@ -22,13 +22,15 @@ |
| 22 | 22 | #define RELOC(x) (*PTRRELOC(&(x))) |
| 23 | 23 | |
| 24 | 24 | /* Definitions used by the flattened device tree */ |
| 25 | -#define OF_DT_HEADER 0xd00dfeed /* 4: version, 4: total size */ | |
| 26 | -#define OF_DT_BEGIN_NODE 0x1 /* Start node: full name */ | |
| 25 | +#define OF_DT_HEADER 0xd00dfeed /* marker */ | |
| 26 | +#define OF_DT_BEGIN_NODE 0x1 /* Start of node, full name */ | |
| 27 | 27 | #define OF_DT_END_NODE 0x2 /* End node */ |
| 28 | -#define OF_DT_PROP 0x3 /* Property: name off, size, content */ | |
| 28 | +#define OF_DT_PROP 0x3 /* Property: name off, size, | |
| 29 | + * content */ | |
| 30 | +#define OF_DT_NOP 0x4 /* nop */ | |
| 29 | 31 | #define OF_DT_END 0x9 |
| 30 | 32 | |
| 31 | -#define OF_DT_VERSION 1 | |
| 33 | +#define OF_DT_VERSION 0x10 | |
| 32 | 34 | |
| 33 | 35 | /* |
| 34 | 36 | * This is what gets passed to the kernel by prom_init or kexec |
| ... | ... | @@ -54,7 +56,9 @@ |
| 54 | 56 | u32 version; /* format version */ |
| 55 | 57 | u32 last_comp_version; /* last compatible version */ |
| 56 | 58 | /* version 2 fields below */ |
| 57 | - u32 boot_cpuid_phys; /* Which physical CPU id we're booting on */ | |
| 59 | + u32 boot_cpuid_phys; /* Physical CPU id we're booting on */ | |
| 60 | + /* version 3 fields below */ | |
| 61 | + u32 dt_strings_size; /* size of the DT strings block */ | |
| 58 | 62 | }; |
| 59 | 63 | |
| 60 | 64 |