diff options
Diffstat (limited to 'arch/ppc64/kernel/prom.c')
-rw-r--r-- | arch/ppc64/kernel/prom.c | 175 |
1 files changed, 145 insertions, 30 deletions
diff --git a/arch/ppc64/kernel/prom.c b/arch/ppc64/kernel/prom.c index 5aca01ddd81f..255c39ae1b48 100644 --- a/arch/ppc64/kernel/prom.c +++ b/arch/ppc64/kernel/prom.c | |||
@@ -625,8 +625,8 @@ void __init finish_device_tree(void) | |||
625 | 625 | ||
626 | static inline char *find_flat_dt_string(u32 offset) | 626 | static inline char *find_flat_dt_string(u32 offset) |
627 | { | 627 | { |
628 | return ((char *)initial_boot_params) + initial_boot_params->off_dt_strings | 628 | return ((char *)initial_boot_params) + |
629 | + offset; | 629 | initial_boot_params->off_dt_strings + offset; |
630 | } | 630 | } |
631 | 631 | ||
632 | /** | 632 | /** |
@@ -635,26 +635,33 @@ static inline char *find_flat_dt_string(u32 offset) | |||
635 | * unflatten the tree | 635 | * unflatten the tree |
636 | */ | 636 | */ |
637 | static int __init scan_flat_dt(int (*it)(unsigned long node, | 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 | void *data) | 640 | void *data) |
640 | { | 641 | { |
641 | unsigned long p = ((unsigned long)initial_boot_params) + | 642 | unsigned long p = ((unsigned long)initial_boot_params) + |
642 | initial_boot_params->off_dt_struct; | 643 | initial_boot_params->off_dt_struct; |
643 | int rc = 0; | 644 | int rc = 0; |
645 | int depth = -1; | ||
644 | 646 | ||
645 | do { | 647 | do { |
646 | u32 tag = *((u32 *)p); | 648 | u32 tag = *((u32 *)p); |
647 | char *pathp; | 649 | char *pathp; |
648 | 650 | ||
649 | p += 4; | 651 | p += 4; |
650 | if (tag == OF_DT_END_NODE) | 652 | if (tag == OF_DT_END_NODE) { |
653 | depth --; | ||
654 | continue; | ||
655 | } | ||
656 | if (tag == OF_DT_NOP) | ||
651 | continue; | 657 | continue; |
652 | if (tag == OF_DT_END) | 658 | if (tag == OF_DT_END) |
653 | break; | 659 | break; |
654 | if (tag == OF_DT_PROP) { | 660 | if (tag == OF_DT_PROP) { |
655 | u32 sz = *((u32 *)p); | 661 | u32 sz = *((u32 *)p); |
656 | p += 8; | 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 | p += sz; | 665 | p += sz; |
659 | p = _ALIGN(p, 4); | 666 | p = _ALIGN(p, 4); |
660 | continue; | 667 | continue; |
@@ -664,9 +671,18 @@ static int __init scan_flat_dt(int (*it)(unsigned long node, | |||
664 | " device tree !\n", tag); | 671 | " device tree !\n", tag); |
665 | return -EINVAL; | 672 | return -EINVAL; |
666 | } | 673 | } |
674 | depth++; | ||
667 | pathp = (char *)p; | 675 | pathp = (char *)p; |
668 | p = _ALIGN(p + strlen(pathp) + 1, 4); | 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 | if (rc != 0) | 686 | if (rc != 0) |
671 | break; | 687 | break; |
672 | } while(1); | 688 | } while(1); |
@@ -689,17 +705,21 @@ static void* __init get_flat_dt_prop(unsigned long node, const char *name, | |||
689 | const char *nstr; | 705 | const char *nstr; |
690 | 706 | ||
691 | p += 4; | 707 | p += 4; |
708 | if (tag == OF_DT_NOP) | ||
709 | continue; | ||
692 | if (tag != OF_DT_PROP) | 710 | if (tag != OF_DT_PROP) |
693 | return NULL; | 711 | return NULL; |
694 | 712 | ||
695 | sz = *((u32 *)p); | 713 | sz = *((u32 *)p); |
696 | noff = *((u32 *)(p + 4)); | 714 | noff = *((u32 *)(p + 4)); |
697 | p += 8; | 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 | nstr = find_flat_dt_string(noff); | 719 | nstr = find_flat_dt_string(noff); |
701 | if (nstr == NULL) { | 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 | return NULL; | 723 | return NULL; |
704 | } | 724 | } |
705 | if (strcmp(name, nstr) == 0) { | 725 | if (strcmp(name, nstr) == 0) { |
@@ -713,7 +733,7 @@ static void* __init get_flat_dt_prop(unsigned long node, const char *name, | |||
713 | } | 733 | } |
714 | 734 | ||
715 | static void *__init unflatten_dt_alloc(unsigned long *mem, unsigned long size, | 735 | static void *__init unflatten_dt_alloc(unsigned long *mem, unsigned long size, |
716 | unsigned long align) | 736 | unsigned long align) |
717 | { | 737 | { |
718 | void *res; | 738 | void *res; |
719 | 739 | ||
@@ -727,13 +747,16 @@ static void *__init unflatten_dt_alloc(unsigned long *mem, unsigned long size, | |||
727 | static unsigned long __init unflatten_dt_node(unsigned long mem, | 747 | static unsigned long __init unflatten_dt_node(unsigned long mem, |
728 | unsigned long *p, | 748 | unsigned long *p, |
729 | struct device_node *dad, | 749 | struct device_node *dad, |
730 | struct device_node ***allnextpp) | 750 | struct device_node ***allnextpp, |
751 | unsigned long fpsize) | ||
731 | { | 752 | { |
732 | struct device_node *np; | 753 | struct device_node *np; |
733 | struct property *pp, **prev_pp = NULL; | 754 | struct property *pp, **prev_pp = NULL; |
734 | char *pathp; | 755 | char *pathp; |
735 | u32 tag; | 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 | tag = *((u32 *)(*p)); | 761 | tag = *((u32 *)(*p)); |
739 | if (tag != OF_DT_BEGIN_NODE) { | 762 | if (tag != OF_DT_BEGIN_NODE) { |
@@ -742,21 +765,62 @@ static unsigned long __init unflatten_dt_node(unsigned long mem, | |||
742 | } | 765 | } |
743 | *p += 4; | 766 | *p += 4; |
744 | pathp = (char *)*p; | 767 | pathp = (char *)*p; |
745 | l = strlen(pathp) + 1; | 768 | l = allocl = strlen(pathp) + 1; |
746 | *p = _ALIGN(*p + l, 4); | 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 | __alignof__(struct device_node)); | 797 | __alignof__(struct device_node)); |
750 | if (allnextpp) { | 798 | if (allnextpp) { |
751 | memset(np, 0, sizeof(*np)); | 799 | memset(np, 0, sizeof(*np)); |
752 | np->full_name = ((char*)np) + sizeof(struct device_node); | 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 | prev_pp = &np->properties; | 818 | prev_pp = &np->properties; |
755 | **allnextpp = np; | 819 | **allnextpp = np; |
756 | *allnextpp = &np->allnext; | 820 | *allnextpp = &np->allnext; |
757 | if (dad != NULL) { | 821 | if (dad != NULL) { |
758 | np->parent = dad; | 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 | if (dad->next == 0) | 824 | if (dad->next == 0) |
761 | dad->child = np; | 825 | dad->child = np; |
762 | else | 826 | else |
@@ -770,18 +834,26 @@ static unsigned long __init unflatten_dt_node(unsigned long mem, | |||
770 | char *pname; | 834 | char *pname; |
771 | 835 | ||
772 | tag = *((u32 *)(*p)); | 836 | tag = *((u32 *)(*p)); |
837 | if (tag == OF_DT_NOP) { | ||
838 | *p += 4; | ||
839 | continue; | ||
840 | } | ||
773 | if (tag != OF_DT_PROP) | 841 | if (tag != OF_DT_PROP) |
774 | break; | 842 | break; |
775 | *p += 4; | 843 | *p += 4; |
776 | sz = *((u32 *)(*p)); | 844 | sz = *((u32 *)(*p)); |
777 | noff = *((u32 *)((*p) + 4)); | 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 | pname = find_flat_dt_string(noff); | 850 | pname = find_flat_dt_string(noff); |
781 | if (pname == NULL) { | 851 | if (pname == NULL) { |
782 | printk("Can't find property name in list !\n"); | 852 | printk("Can't find property name in list !\n"); |
783 | break; | 853 | break; |
784 | } | 854 | } |
855 | if (strcmp(pname, "name") == 0) | ||
856 | has_name = 1; | ||
785 | l = strlen(pname) + 1; | 857 | l = strlen(pname) + 1; |
786 | pp = unflatten_dt_alloc(&mem, sizeof(struct property), | 858 | pp = unflatten_dt_alloc(&mem, sizeof(struct property), |
787 | __alignof__(struct property)); | 859 | __alignof__(struct property)); |
@@ -801,6 +873,36 @@ static unsigned long __init unflatten_dt_node(unsigned long mem, | |||
801 | } | 873 | } |
802 | *p = _ALIGN((*p) + sz, 4); | 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 | if (allnextpp) { | 906 | if (allnextpp) { |
805 | *prev_pp = NULL; | 907 | *prev_pp = NULL; |
806 | np->name = get_property(np, "name", NULL); | 908 | np->name = get_property(np, "name", NULL); |
@@ -812,7 +914,7 @@ static unsigned long __init unflatten_dt_node(unsigned long mem, | |||
812 | np->type = "<NULL>"; | 914 | np->type = "<NULL>"; |
813 | } | 915 | } |
814 | while (tag == OF_DT_BEGIN_NODE) { | 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 | tag = *((u32 *)(*p)); | 918 | tag = *((u32 *)(*p)); |
817 | } | 919 | } |
818 | if (tag != OF_DT_END_NODE) { | 920 | if (tag != OF_DT_END_NODE) { |
@@ -842,21 +944,27 @@ void __init unflatten_device_tree(void) | |||
842 | /* First pass, scan for size */ | 944 | /* First pass, scan for size */ |
843 | start = ((unsigned long)initial_boot_params) + | 945 | start = ((unsigned long)initial_boot_params) + |
844 | initial_boot_params->off_dt_struct; | 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 | DBG(" size is %lx, allocating...\n", size); | 950 | DBG(" size is %lx, allocating...\n", size); |
848 | 951 | ||
849 | /* Allocate memory for the expanded device tree */ | 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 | __alignof__(struct device_node))); | 954 | __alignof__(struct device_node))); |
955 | ((u32 *)mem)[size / 4] = 0xdeadbeef; | ||
956 | |||
852 | DBG(" unflattening...\n", mem); | 957 | DBG(" unflattening...\n", mem); |
853 | 958 | ||
854 | /* Second pass, do actual unflattening */ | 959 | /* Second pass, do actual unflattening */ |
855 | start = ((unsigned long)initial_boot_params) + | 960 | start = ((unsigned long)initial_boot_params) + |
856 | initial_boot_params->off_dt_struct; | 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 | if (*((u32 *)start) != OF_DT_END) | 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 | *allnextp = NULL; | 968 | *allnextp = NULL; |
861 | 969 | ||
862 | /* Get pointer to OF "/chosen" node for use everywhere */ | 970 | /* Get pointer to OF "/chosen" node for use everywhere */ |
@@ -880,7 +988,7 @@ void __init unflatten_device_tree(void) | |||
880 | 988 | ||
881 | 989 | ||
882 | static int __init early_init_dt_scan_cpus(unsigned long node, | 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 | char *type = get_flat_dt_prop(node, "device_type", NULL); | 993 | char *type = get_flat_dt_prop(node, "device_type", NULL); |
886 | u32 *prop; | 994 | u32 *prop; |
@@ -947,13 +1055,15 @@ static int __init early_init_dt_scan_cpus(unsigned long node, | |||
947 | } | 1055 | } |
948 | 1056 | ||
949 | static int __init early_init_dt_scan_chosen(unsigned long node, | 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 | u32 *prop; | 1060 | u32 *prop; |
953 | u64 *prop64; | 1061 | u64 *prop64; |
954 | extern unsigned long memory_limit, tce_alloc_start, tce_alloc_end; | 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 | return 0; | 1067 | return 0; |
958 | 1068 | ||
959 | /* get platform type */ | 1069 | /* get platform type */ |
@@ -1003,18 +1113,20 @@ static int __init early_init_dt_scan_chosen(unsigned long node, | |||
1003 | } | 1113 | } |
1004 | 1114 | ||
1005 | static int __init early_init_dt_scan_root(unsigned long node, | 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 | u32 *prop; | 1118 | u32 *prop; |
1009 | 1119 | ||
1010 | if (strcmp(full_path, "/") != 0) | 1120 | if (depth != 0) |
1011 | return 0; | 1121 | return 0; |
1012 | 1122 | ||
1013 | prop = (u32 *)get_flat_dt_prop(node, "#size-cells", NULL); | 1123 | prop = (u32 *)get_flat_dt_prop(node, "#size-cells", NULL); |
1014 | dt_root_size_cells = (prop == NULL) ? 1 : *prop; | 1124 | dt_root_size_cells = (prop == NULL) ? 1 : *prop; |
1015 | 1125 | DBG("dt_root_size_cells = %x\n", dt_root_size_cells); | |
1126 | |||
1016 | prop = (u32 *)get_flat_dt_prop(node, "#address-cells", NULL); | 1127 | prop = (u32 *)get_flat_dt_prop(node, "#address-cells", NULL); |
1017 | dt_root_addr_cells = (prop == NULL) ? 2 : *prop; | 1128 | dt_root_addr_cells = (prop == NULL) ? 2 : *prop; |
1129 | DBG("dt_root_addr_cells = %x\n", dt_root_addr_cells); | ||
1018 | 1130 | ||
1019 | /* break now */ | 1131 | /* break now */ |
1020 | return 1; | 1132 | return 1; |
@@ -1042,7 +1154,7 @@ static unsigned long __init dt_mem_next_cell(int s, cell_t **cellp) | |||
1042 | 1154 | ||
1043 | 1155 | ||
1044 | static int __init early_init_dt_scan_memory(unsigned long node, | 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 | char *type = get_flat_dt_prop(node, "device_type", NULL); | 1159 | char *type = get_flat_dt_prop(node, "device_type", NULL); |
1048 | cell_t *reg, *endp; | 1160 | cell_t *reg, *endp; |
@@ -1058,7 +1170,9 @@ static int __init early_init_dt_scan_memory(unsigned long node, | |||
1058 | 1170 | ||
1059 | endp = reg + (l / sizeof(cell_t)); | 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 | while ((endp - reg) >= (dt_root_addr_cells + dt_root_size_cells)) { | 1176 | while ((endp - reg) >= (dt_root_addr_cells + dt_root_size_cells)) { |
1063 | unsigned long base, size; | 1177 | unsigned long base, size; |
1064 | 1178 | ||
@@ -1469,10 +1583,11 @@ struct device_node *of_find_node_by_path(const char *path) | |||
1469 | struct device_node *np = allnodes; | 1583 | struct device_node *np = allnodes; |
1470 | 1584 | ||
1471 | read_lock(&devtree_lock); | 1585 | read_lock(&devtree_lock); |
1472 | for (; np != 0; np = np->allnext) | 1586 | for (; np != 0; np = np->allnext) { |
1473 | if (np->full_name != 0 && strcasecmp(np->full_name, path) == 0 | 1587 | if (np->full_name != 0 && strcasecmp(np->full_name, path) == 0 |
1474 | && of_node_get(np)) | 1588 | && of_node_get(np)) |
1475 | break; | 1589 | break; |
1590 | } | ||
1476 | read_unlock(&devtree_lock); | 1591 | read_unlock(&devtree_lock); |
1477 | return np; | 1592 | return np; |
1478 | } | 1593 | } |