diff mbox

trying to use https://github.com/beagleboard/linux.git with buildroot

Message ID n5gpaj$p9d$1@ger.gmane.org
State Not Applicable
Headers show

Commit Message

Chris LaRocque Dec. 24, 2015, 12:44 p.m. UTC
Craig Swank wrote:

> Here is another thing I've found:
> 
> https://github.com/beagleboard/linux/issues/49
> 
> In that issue the person had some addresses set incorrectly in uEnv.txt,
> but mine doesn't have any addresses set:
> 
> bootpart=0:1
> bootdir=
> uenvcmd=run loadimage;run loadramdisk;run findfdt;run loadfdt;run ram
> boot
> 
> Is there something that needs to be set in uEnv.txt?
> 
> 
> On Wed, Dec 23, 2015, at 02:52 PM, Thomas Petazzoni wrote:
>> Dear Craig Swank,
>> 
>> On Wed, 23 Dec 2015 14:45:36 -0800, Craig Swank wrote:
>> > Is it strange to you that when I do the build the output/build
>> > directory has both of these dirs?
>> > 
>> > ➜  buildroot git:(master) ✗ ls output/build/linux-*
>> > output/build/linux-4.1.4-ti-r9
>> > output/build/linux-headers-3.12.10
>> 
>> No, it is perfectly fine. linux-headers is used when building the
>> toolchain. linux is used to build the kernel itself.
>> 
>> Thomas
>> --
>> Thomas Petazzoni, CTO, Free Electrons
>> Embedded Linux, Kernel and Android engineering
>> http://free-electrons.com
> 
> 
Hello Craig

I use the beagleboard.org/linux and here's some of my kernel 4.1 config. I 
have a working build for kernel 3.8 as well if you'd like.

#
# Kernel Header Options
#
# BR2_KERNEL_HEADERS_3_2 is not set
# BR2_KERNEL_HEADERS_3_4 is not set
# BR2_KERNEL_HEADERS_3_10 is not set
# BR2_KERNEL_HEADERS_3_12 is not set
# BR2_KERNEL_HEADERS_3_14 is not set
# BR2_KERNEL_HEADERS_3_18 is not set
BR2_KERNEL_HEADERS_4_1=y
# BR2_KERNEL_HEADERS_4_2 is not set
# BR2_KERNEL_HEADERS_4_3 is not set
# BR2_KERNEL_HEADERS_VERSION is not set
BR2_DEFAULT_KERNEL_HEADERS="4.1.13"

and

# Kernel
#
BR2_LINUX_KERNEL=y
# BR2_LINUX_KERNEL_LATEST_VERSION is not set
# BR2_LINUX_KERNEL_SAME_AS_HEADERS is not set
# BR2_LINUX_KERNEL_CUSTOM_VERSION is not set
# BR2_LINUX_KERNEL_CUSTOM_TARBALL is not set
BR2_LINUX_KERNEL_CUSTOM_GIT=y
# BR2_LINUX_KERNEL_CUSTOM_HG is not set
# BR2_LINUX_KERNEL_CUSTOM_LOCAL is not set
BR2_LINUX_KERNEL_CUSTOM_REPO_URL="https://github.com/beagleboard/linux.git"
BR2_LINUX_KERNEL_CUSTOM_REPO_VERSION="4.1.13-ti-r36"
BR2_LINUX_KERNEL_VERSION="4.1.13-ti-r36"
BR2_LINUX_KERNEL_PATCH=""
# BR2_LINUX_KERNEL_USE_DEFCONFIG is not set
BR2_LINUX_KERNEL_USE_CUSTOM_CONFIG=y
BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="$(TOPDIR)/output/build/linux-4.1.13-ti-
r36/arch/arm/configs/bb.org_defconfig"
BR2_LINUX_KERNEL_CONFIG_FRAGMENT_FILES=""
# BR2_LINUX_KERNEL_UIMAGE is not set
# BR2_LINUX_KERNEL_APPENDED_UIMAGE is not set
BR2_LINUX_KERNEL_ZIMAGE=y
# BR2_LINUX_KERNEL_APPENDED_ZIMAGE is not set
# BR2_LINUX_KERNEL_VMLINUX is not set
# BR2_LINUX_KERNEL_IMAGE_TARGET_CUSTOM is not set
BR2_LINUX_KERNEL_DTS_SUPPORT=y
BR2_LINUX_KERNEL_USE_INTREE_DTS=y
# BR2_LINUX_KERNEL_USE_CUSTOM_DTS is not set
BR2_LINUX_KERNEL_INTREE_DTS_NAME="am335x-bone am335x-boneblack"
# BR2_LINUX_KERNEL_INSTALL_TARGET is not set

My uEnv.txt

bootfile=zImage
fdtfile=am335x-boneblack.dtb
loadaddr=0x80007fc0
fdtaddr=0x80F80000
loadfdt=fatload mmc 0:1 ${fdtaddr} ${fdtfile}
loaduimage=fatload mmc 0:1 ${loadaddr} ${bootfile}
console=ttyO0,115200n8
mmcroot=/dev/mmcblk0p2
mmcrootfstype=ext2
uenvcmd=mmc rescan; run loaduimage; run loadfdt; run fdtboot
fdtboot=run mmc_args; run mmcargs; bootz ${loadaddr} - ${fdtaddr}
mmc_args=setenv bootargs console=${console} ${optargs} root=${mmcroot} rw 
rootfstype=${mmcrootfstype} rootwait

You'll need to patch the host DTC (for the cape manager and cape overlays. 
Heres my patch (hack) derived from Robert Nelsons bb.org-overlays.

set BR2_GLOBAL_PATCH_DIR to a valid path

create the folder DTC in BR2_GLOBAL_PATCH_DIR and place the patch file 
named: host-dtc-1.4.1.patch within...


host-dtc-1.4.1.patch **********************************************


 
 dtc_tests () {
@@ -399,6 +401,8 @@
     tree1_tests dtc_tree1_merge.test.dtb test_tree1.dtb
     run_dtc_test -I dts -O dtb -o dtc_tree1_merge_labelled.test.dtb 
test_tree1_merge_labelled.dts
     tree1_tests dtc_tree1_merge_labelled.test.dtb test_tree1.dtb
+    run_dtc_test -I dts -O dtb -o dtc_tree1_label_noderef.test.dtb 
test_tree1_label_noderef.dts
+    run_test dtbs_equal_unordered dtc_tree1_label_noderef.test.dtb 
test_tree1.dtb
     run_dtc_test -I dts -O dtb -o multilabel_merge.test.dtb 
multilabel_merge.dts
     run_test references multilabel.test.dtb
     run_test dtbs_equal_ordered multilabel.test.dtb 
multilabel_merge.test.dtb
@@ -610,6 +614,28 @@
     run_wrap_test $DTPUT $dtb -cp /chosen
     run_wrap_test $DTPUT $dtb -cp /chosen/son
 
+    # Start again with a fresh dtb
+    run_dtc_test -O dtb -p $(stat -c %s $text) -o $dtb $dts
+
+    # Node delete
+    run_wrap_test $DTPUT $dtb -c /chosen/node1 /chosen/node2 /chosen/node3
+    run_fdtget_test "node3\nnode2\nnode1" $dtb -l  /chosen
+    run_wrap_test $DTPUT $dtb -r /chosen/node1 /chosen/node2
+    run_fdtget_test "node3" $dtb -l  /chosen
+
+    # Delete the non-existent node
+    run_wrap_error_test $DTPUT $dtb -r /non-existent/node
+
+    # Property delete
+    run_fdtput_test "eva" $dtb /chosen/ name "" -ts "eva"
+    run_fdtput_test "016" $dtb /chosen/ age  "" -ts "016"
+    run_fdtget_test "age\nname\nbootargs\nlinux,platform" $dtb -p  /chosen
+    run_wrap_test $DTPUT $dtb -d /chosen/ name age
+    run_fdtget_test "bootargs\nlinux,platform" $dtb -p  /chosen
+
+    # Delete the non-existent property
+    run_wrap_error_test $DTPUT $dtb -d /chosen   non-existent-prop
+
     # TODO: Add tests for verbose mode?
 }

Comments

Craig Swank Dec. 24, 2015, 7:35 p.m. UTC | #1
Thanks a lot Chris.  Unfortunately I'm getting the same hanging kernel
result.  I went over everything multiple times but get the same thing
each time.  Could you send me your entire .config file?


On Thu, Dec 24, 2015, at 04:44 AM, Chris LaRocque wrote:
> Craig Swank wrote:
> 
> > Here is another thing I've found:
> > 
> > https://github.com/beagleboard/linux/issues/49
> > 
> > In that issue the person had some addresses set incorrectly in uEnv.txt,
> > but mine doesn't have any addresses set:
> > 
> > bootpart=0:1
> > bootdir=
> > uenvcmd=run loadimage;run loadramdisk;run findfdt;run loadfdt;run ram
> > boot
> > 
> > Is there something that needs to be set in uEnv.txt?
> > 
> > 
> > On Wed, Dec 23, 2015, at 02:52 PM, Thomas Petazzoni wrote:
> >> Dear Craig Swank,
> >> 
> >> On Wed, 23 Dec 2015 14:45:36 -0800, Craig Swank wrote:
> >> > Is it strange to you that when I do the build the output/build
> >> > directory has both of these dirs?
> >> > 
> >> > ➜  buildroot git:(master) ✗ ls output/build/linux-*
> >> > output/build/linux-4.1.4-ti-r9
> >> > output/build/linux-headers-3.12.10
> >> 
> >> No, it is perfectly fine. linux-headers is used when building the
> >> toolchain. linux is used to build the kernel itself.
> >> 
> >> Thomas
> >> --
> >> Thomas Petazzoni, CTO, Free Electrons
> >> Embedded Linux, Kernel and Android engineering
> >> http://free-electrons.com
> > 
> > 
> Hello Craig
> 
> I use the beagleboard.org/linux and here's some of my kernel 4.1 config.
> I 
> have a working build for kernel 3.8 as well if you'd like.
> 
> #
> # Kernel Header Options
> #
> # BR2_KERNEL_HEADERS_3_2 is not set
> # BR2_KERNEL_HEADERS_3_4 is not set
> # BR2_KERNEL_HEADERS_3_10 is not set
> # BR2_KERNEL_HEADERS_3_12 is not set
> # BR2_KERNEL_HEADERS_3_14 is not set
> # BR2_KERNEL_HEADERS_3_18 is not set
> BR2_KERNEL_HEADERS_4_1=y
> # BR2_KERNEL_HEADERS_4_2 is not set
> # BR2_KERNEL_HEADERS_4_3 is not set
> # BR2_KERNEL_HEADERS_VERSION is not set
> BR2_DEFAULT_KERNEL_HEADERS="4.1.13"
> 
> and
> 
> # Kernel
> #
> BR2_LINUX_KERNEL=y
> # BR2_LINUX_KERNEL_LATEST_VERSION is not set
> # BR2_LINUX_KERNEL_SAME_AS_HEADERS is not set
> # BR2_LINUX_KERNEL_CUSTOM_VERSION is not set
> # BR2_LINUX_KERNEL_CUSTOM_TARBALL is not set
> BR2_LINUX_KERNEL_CUSTOM_GIT=y
> # BR2_LINUX_KERNEL_CUSTOM_HG is not set
> # BR2_LINUX_KERNEL_CUSTOM_LOCAL is not set
> BR2_LINUX_KERNEL_CUSTOM_REPO_URL="https://github.com/beagleboard/linux.git"
> BR2_LINUX_KERNEL_CUSTOM_REPO_VERSION="4.1.13-ti-r36"
> BR2_LINUX_KERNEL_VERSION="4.1.13-ti-r36"
> BR2_LINUX_KERNEL_PATCH=""
> # BR2_LINUX_KERNEL_USE_DEFCONFIG is not set
> BR2_LINUX_KERNEL_USE_CUSTOM_CONFIG=y
> BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="$(TOPDIR)/output/build/linux-4.1.13-ti-
> r36/arch/arm/configs/bb.org_defconfig"
> BR2_LINUX_KERNEL_CONFIG_FRAGMENT_FILES=""
> # BR2_LINUX_KERNEL_UIMAGE is not set
> # BR2_LINUX_KERNEL_APPENDED_UIMAGE is not set
> BR2_LINUX_KERNEL_ZIMAGE=y
> # BR2_LINUX_KERNEL_APPENDED_ZIMAGE is not set
> # BR2_LINUX_KERNEL_VMLINUX is not set
> # BR2_LINUX_KERNEL_IMAGE_TARGET_CUSTOM is not set
> BR2_LINUX_KERNEL_DTS_SUPPORT=y
> BR2_LINUX_KERNEL_USE_INTREE_DTS=y
> # BR2_LINUX_KERNEL_USE_CUSTOM_DTS is not set
> BR2_LINUX_KERNEL_INTREE_DTS_NAME="am335x-bone am335x-boneblack"
> # BR2_LINUX_KERNEL_INSTALL_TARGET is not set
> 
> My uEnv.txt
> 
> bootfile=zImage
> fdtfile=am335x-boneblack.dtb
> loadaddr=0x80007fc0
> fdtaddr=0x80F80000
> loadfdt=fatload mmc 0:1 ${fdtaddr} ${fdtfile}
> loaduimage=fatload mmc 0:1 ${loadaddr} ${bootfile}
> console=ttyO0,115200n8
> mmcroot=/dev/mmcblk0p2
> mmcrootfstype=ext2
> uenvcmd=mmc rescan; run loaduimage; run loadfdt; run fdtboot
> fdtboot=run mmc_args; run mmcargs; bootz ${loadaddr} - ${fdtaddr}
> mmc_args=setenv bootargs console=${console} ${optargs} root=${mmcroot} rw 
> rootfstype=${mmcrootfstype} rootwait
> 
> You'll need to patch the host DTC (for the cape manager and cape
> overlays. 
> Heres my patch (hack) derived from Robert Nelsons bb.org-overlays.
> 
> set BR2_GLOBAL_PATCH_DIR to a valid path
> 
> create the folder DTC in BR2_GLOBAL_PATCH_DIR and place the patch file 
> named: host-dtc-1.4.1.patch within...
> 
> 
> host-dtc-1.4.1.patch **********************************************
> 
> 
> --- a/checks.c  2015-12-10 14:50:21.537561920 -0500
> +++ b/checks.c  2015-12-01 17:11:59.266242047 -0500
> @@ -458,6 +458,8 @@
>  				     struct node *node, struct 
> property *prop)
>  {
>  	struct marker *m = prop->val.markers;
> +       struct fixup *f, **fp;
> +       struct fixup_entry *fe, **fep;
>  	struct node *refnode;
>  	cell_t phandle;
>  
> @@ -466,14 +468,73 @@
>  
>  		refnode = get_node_by_ref(dt, m->ref);
>  		if (! refnode) {
> +                       if (!dt->is_plugin) {
>  			FAIL(c, "Reference to non-existent node or 
> label \"%s\"\n",
>  			     m->ref);
>  			continue;
>  		}
>  
> +                       /* allocate fixup entry */
> +                       fe = xmalloc(sizeof(*fe));
> +
> +                       fe->node = node;
> +                       fe->prop = prop;
> +                       fe->offset = m->offset;
> +                       fe->next = NULL;
> +
> +                       /* search for an already existing fixup */
> +                       for_each_fixup(dt, f)
> +                               if (strcmp(f->ref, m->ref) == 0)
> +                                       break;
> +
> +                       /* no fixup found, add new */
> +                       if (f == NULL) {
> +                               f = xmalloc(sizeof(*f));
> +                               f->ref = m->ref;
> +                               f->entries = NULL;
> +                               f->next = NULL;
> +
> +                               /* add it to the tree */
> +                               fp = &dt->fixups;
> +                               while (*fp)
> +                                       fp = &(*fp)->next;
> +                               *fp = f;
> +                       }
> +
> +                       /* and now append fixup entry */
> +                       fep = &f->entries;
> +                       while (*fep)
> +                               fep = &(*fep)->next;
> +                       *fep = fe;
> +
> +                       /* mark the entry as unresolved */
> +                       *((cell_t *)(prop->val.val + m->offset)) =
> +                               cpu_to_fdt32(0xdeadbeef);
> +                       continue;
> +               }
> +
> +               /* if it's a local reference, we need to record it */
> +               if (symbol_fixup_support) {
> +
> +                       /* allocate a new local fixup entry */
> +                       fe = xmalloc(sizeof(*fe));
> +
> +                       fe->node = node;
> +                       fe->prop = prop;
> +                       fe->offset = m->offset;
> +                       fe->next = NULL;
> +
> +                       /* append it to the local fixups */
> +                       fep = &dt->local_fixups;
> +                       while (*fep)
> +                               fep = &(*fep)->next;
> +                       *fep = fe;
> +               }
> +
>  		phandle = get_node_phandle(dt, refnode);
>  		*((cell_t *)(prop->val.val + m->offset)) = 
> cpu_to_fdt32(phandle);
>  	}
> +
>  }
>  ERROR(phandle_references, NULL, NULL, fixup_phandle_references, NULL,
>        &duplicate_node_names, &explicit_phandles);
> @@ -652,6 +713,45 @@
>  }
>  TREE_WARNING(obsolete_chosen_interrupt_controller, NULL);
>  
> +static void check_auto_label_phandles(struct check *c, struct node *dt,
> +                                      struct node *node)
> +{
> +       struct label *l;
> +       struct symbol *s, **sp;
> +       int has_label;
> +
> +       if (!symbol_fixup_support)
> +               return;
> +
> +       has_label = 0;
> +       for_each_label(node->labels, l) {
> +               has_label = 1;
> +               break;
> +       }
> +
> +       if (!has_label)
> +               return;
> +
> +       /* force allocation of a phandle for this node */
> +       (void)get_node_phandle(dt, node);
> +
> +       /* add the symbol */
> +       for_each_label(node->labels, l) {
> +
> +               s = xmalloc(sizeof(*s));
> +               s->label = l;
> +               s->node = node;
> +               s->next = NULL;
> +
> +               /* add it to the symbols list */
> +               sp = &dt->symbols;
> +               while (*sp)
> +                       sp = &((*sp)->next);
> +               *sp = s;
> +       }
> +}
> +NODE_WARNING(auto_label_phandles, NULL);
> +
>  static struct check *check_table[] = {
>  	&duplicate_node_names, &duplicate_property_names,
>  	&node_name_chars, &node_name_format, &property_name_chars,
> @@ -670,6 +770,8 @@
>  	&avoid_default_addr_size,
>  	&obsolete_chosen_interrupt_controller,
>  
> +       &auto_label_phandles,
> +
>  	&always_fail,
>  };
>  
> --- a/Documentation/dt-object-internal.txt      1969-12-31
> 19:00:00.000000000 
> -0500
> +++ b/Documentation/dt-object-internal.txt      2015-12-01
> 17:11:59.249242025 
> -0500
> @@ -0,0 +1,301 @@
> +Device Tree Dynamic Object format internals
> +-------------------------------------------
> +
> +The Device Tree for most platforms is a static representation of
> +the hardware capabilities. This is insufficient for many platforms
> +that need to dynamically insert device tree fragments to the
> +running kernel's live tree.
> +
> +This document explains the the device tree object format and the
> +modifications made to the device tree compiler, which make it possible.
> +
> +1. Simplified Problem Definition
> +--------------------------------
> +
> +Assume we have a platform which boots using following simplified device 
> tree.
> +
> +---- foo.dts 
> -----------------------------------------------------------------
> +       /* FOO platform */
> +       / {
> +               compatible = "corp,foo";
> +
> +               /* shared resources */
> +               res: res {
> +               };
> +
> +               /* On chip peripherals */
> +               ocp: ocp {
> +                       /* peripherals that are always instantiated 
> */
> +                       peripheral1 { ... };
> +               }
> +       };
> +---- foo.dts 
> -----------------------------------------------------------------
> +
> +We have a number of peripherals that after probing (using some undefined 
> method)
> +should result in different device tree configuration.
> +
> +We cannot boot with this static tree because due to the configuration of 
> the
> +foo platform there exist multiple conficting peripherals DT fragments.
> +
> +So for the bar peripheral we would have this:
> +
> +---- foo+bar.dts 
> -------------------------------------------------------------
> +       /* FOO platform + bar peripheral */
> +       / {
> +               compatible = "corp,foo";
> +
> +               /* shared resources */
> +               res: res {
> +               };
> +
> +               /* On chip peripherals */
> +               ocp: ocp {
> +                       /* peripherals that are always instantiated 
> */
> +                       peripheral1 { ... };
> +
> +                       /* bar peripheral */
> +                       bar {
> +                               compatible = "corp,bar";
> +                               ... /* various properties and 
> child nodes */
> +                       }
> +               }
> +       };
> +---- foo+bar.dts 
> -------------------------------------------------------------
> +
> +While for the baz peripheral we would have this:
> +
> +---- foo+baz.dts 
> -------------------------------------------------------------
> +       /* FOO platform + baz peripheral */
> +       / {
> +               compatible = "corp,foo";
> +
> +               /* shared resources */
> +               res: res {
> +                       /* baz resources */
> +                       baz_res: res_baz { ... };
> +               };
> +
> +               /* On chip peripherals */
> +               ocp: ocp {
> +                       /* peripherals that are always instantiated 
> */
> +                       peripheral1 { ... };
> +
> +                       /* baz peripheral */
> +                       baz {
> +                               compatible = "corp,baz";
> +                               /* reference to another point in 
> the tree */
> +                               ref-to-res = <&baz_res>;
> +                               ... /* various properties and 
> child nodes */
> +                       }
> +               }
> +       };
> +---- foo+baz.dts 
> -------------------------------------------------------------
> +
> +We note that the baz case is more complicated, since the baz peripheral 
> needs to
> +reference another node in the DT tree.
> +
> +2. Device Tree Object Format Requirements
> +-----------------------------------------
> +
> +Since the device tree is used for booting a number of very different 
> hardware
> +platforms it is imperative that we tread very carefully.
> +
> +2.a) No changes to the Device Tree binary format. We cannot modify the
> tree
> +format at all and all the information we require should be encoded using 
> device
> +tree itself. We can add nodes that can be safely ignored by both 
> bootloaders and
> +the kernel.
> +
> +2.b) Changes to the DTS source format should be absolutely minimal, and 
> should
> +only be needed for the DT fragment definitions, and not the base boot
> DT.
> +
> +2.c) An explicit option should be used to instruct DTC to generate the 
> required
> +information needed for object resolution. Platforms that don't use the
> +dynamic object format can safely ignore it.
> +
> +2.d) Finally, DT syntax changes should be kept to a minimum. It should
> be
> +possible to express everything using the existing DT syntax.
> +
> +3. Implementation
> +-----------------
> +
> +The basic unit of addressing in Device Tree is the phandle. Turns out
> it's
> +relatively simple to extend the way phandles are generated and
> referenced
> +so that it's possible to dynamically convert symbolic references
> (labels)
> +to phandle values.
> +
> +We can roughly divide the operation into two steps.
> +
> +3.a) Compilation of the base board DTS file using the '-@' option
> +generates a valid DT blob with an added __symbols__ node at the root
> node,
> +containing a list of all nodes that are marked with a label.
> +
> +Using the foo.dts file above the following node will be generated;
> +
> +$ dtc -@ -O dtb -o foo.dtb -b 0 foo.dts
> +$ fdtdump foo.dtb
> +...
> +/ {
> +       ...
> +       res {
> +               ...
> +               linux,phandle = <0x00000001>;
> +               phandle = <0x00000001>;
> +               ...
> +       };
> +       ocp {
> +               ...
> +               linux,phandle = <0x00000002>;
> +               phandle = <0x00000002>;
> +               ...
> +       };
> +       __symbols__ {
> +               res="/res";
> +               ocp="/ocp";
> +       };
> +};
> +
> +Notice that all the nodes that had a label have been recorded, and that
> +phandles have been generated for them.
> +
> +This blob can be used to boot the board normally, the __symbols__ node
> will
> +be safely ignored both by the bootloader and the kernel (the only loss
> will
> +be a few bytes of memory and disk space).
> +
> +3.b) The Device Tree fragments must be compiled with the same option but 
> they
> +must also have a tag (/plugin/) that allows undefined references to
> labels
> +that are not present at compilation time to be recorded so that the
> runtime
> +loader can fix them.
> +
> +So the bar peripheral's DTS format would be of the form:
> +
> +/plugin/;      /* allow undefined label references and record them */
> +/ {
> +       ....    /* various properties for loader use; i.e. part id 
> etc. */
> +       fragment@0 {
> +               target = <&ocp>;
> +               __overlay__ {
> +                       /* bar peripheral */
> +                       bar {
> +                               compatible = "corp,bar";
> +                               ... /* various properties and 
> child nodes */
> +                       }
> +               };
> +       };
> +};
> +
> +Note that there's a target property that specifies the location where
> the
> +contents of the overlay node will be placed, and it references the label
> +in the foo.dts file.
> +
> +$ dtc -@ -O dtb -o bar.dtbo -b 0 bar.dts
> +$ fdtdump bar.dtbo
> +...
> +/ {
> +       ... /* properties */
> +       fragment@0 {
> +               target = <0xdeadbeef>;
> +               __overlay__ {
> +                       bar {
> +                               compatible = "corp,bar";
> +                               ... /* various properties and 
> child nodes */
> +                       }
> +               };
> +       };
> +       __fixups__ {
> +           ocp = "/fragment@0:target:0";
> +       };
> +};
> +
> +No __symbols__ has been generated (no label in bar.dts).
> +Note that the target's ocp label is undefined, so the phandle handle
> +value is filled with the illegal value '0xdeadbeef', while a __fixups__
> +node has been generated, which marks the location in the tree where
> +the label lookup should store the runtime phandle value of the ocp node.
> +
> +The format of the __fixups__ node entry is
> +
> +       <label> = "<local-full-path>:<property-name>:<offset>";
> +
> +<label>                Is the label we're referring
> +<local-full-path>      Is the full path of the node the reference is
> +<property-name>                Is the name of the property containing
> the
> +                       reference
> +<offset>               The offset (in bytes) of where the property's
> +                       phandle value is located.
> +
> +Doing the same with the baz peripheral's DTS format is a little bit more
> +involved, since baz contains references to local labels which require
> +local fixups.
> +
> +/plugin/;      /* allow undefined label references and record them */
> +/ {
> +       ....    /* various properties for loader use; i.e. part id 
> etc. */
> +       fragment@0 {
> +               target = <&res>;
> +               __overlay__ {
> +                       /* baz resources */
> +                       baz_res: res_baz { ... };
> +               };
> +       };
> +       fragment@1 {
> +               target = <&ocp>;
> +               __overlay__ {
> +                       /* baz peripheral */
> +                       baz {
> +                               compatible = "corp,baz";
> +                               /* reference to another point in 
> the tree */
> +                               ref-to-res = <&baz_res>;
> +                               ... /* various properties and 
> child nodes */
> +                       }
> +               };
> +       };
> +};
> +
> +Note that &bar_res reference.
> +
> +$ dtc -@ -O dtb -o baz.dtbo -b 0 baz.dts
> +$ fdtdump baz.dtbo
> +...
> +/ {
> +       ... /* properties */
> +       fragment@0 {
> +               target = <0xdeadbeef>;
> +               __overlay__ {
> +                       res_baz {
> +                               ....
> +                               linux,phandle = <0x00000001>;
> +                               phandle = <0x00000001>;
> +                       };
> +               };
> +       };
> +       fragment@1 {
> +               target = <0xdeadbeef>;
> +               __overlay__ {
> +                       baz {
> +                               compatible = "corp,baz";
> +                               ... /* various properties and 
> child nodes */
> +                               ref-to-res = <0x00000001>;
> +                       }
> +               };
> +       };
> +       __fixups__ {
> +               res = "/fragment@0:target:0";
> +               ocp = "/fragment@1:target:0";
> +       };
> +       __local_fixups__ {
> +               fragment@1 {
> +                       __overlay__ {
> +                               baz {
> +                                       ref-to-res = <0>;
> +                               };
> +                       };
> +               };
> +       };
> +};
> +
> +This is similar to the bar case, but the reference of a local label by
> the
> +baz node generates a __local_fixups__ entry that records the place that
> the
> +local reference is being made. Since phandles are allocated starting at
> 1
> +the run time loader must apply an offset to each phandle in every
> dynamic
> +DT object loaded. The __local_fixups__ node records the place of every
> +local reference so that the loader can apply the offset.
> 
> --- a/Documentation/manual.txt  2015-12-10 14:50:21.537561920 -0500
> +++ b/Documentation/manual.txt  2015-12-01 17:11:59.249242025 -0500
> @@ -119,6 +119,14 @@
>  	Make space for <number> reserve map entries
>  	Relevant for dtb and asm output only.
>  
> +    -@
> +        Generates a __symbols__ node at the root node of the resulting
> blob
> +       for any node labels used, and for any local references using 
> phandles
> +       it also generates a __local_fixups__ node that tracks them.
> +
> +       When using the /plugin/ tag all unresolved label references to
> +       be tracked in the __fixups__ node, making dynamic resolution 
> possible.
> +
>      -S <bytes>
>  	Ensure the blob at least <bytes> long, adding additional
>  	space if needed.
> @@ -153,6 +161,8 @@
>  
>      devicetree:   '/' nodedef
>  
> +    plugindecl:   '/' 'plugin' '/' ';'
> +
>      nodedef:      '{' list_of_property list_of_subnode '}' ';'
>  
>      property:     label PROPNAME '=' propdata ';'
> --- a/dtc.c     2015-12-10 14:50:21.537561920 -0500
> +++ b/dtc.c     2015-12-01 17:11:59.264242045 -0500
> @@ -31,6 +31,7 @@
>  int minsize;            /* Minimum blob size */
>  int padsize;            /* Additional padding to blob */
>  int phandle_format = PHANDLE_BOTH;      /* Use linux,phandle or phandle 
> properties */
> +int symbol_fixup_support = 0;
>  
>  static void fill_fullpaths(struct node *tree, const char *prefix)
>  {
> @@ -53,7 +54,7 @@
>  #define FDT_VERSION(version)    _FDT_VERSION(version)
>  #define _FDT_VERSION(version)   #version
>  static const char usage_synopsis[] = "dtc [options] <input file>";
> -static const char usage_short_opts[] =
> "qI:O:o:V:d:R:S:p:fb:i:H:sW:E:hv";
> +static const char usage_short_opts[] =
> "qI:O:o:V:d:R:S:p:fb:i:H:sW:E:@hv";
>  static struct option const usage_long_opts[] = {
>  	{"quiet",            no_argument, NULL, 'q'},
>  	{"in-format",         a_argument, NULL, 'I'},
> @@ -71,6 +72,7 @@
>  	{"phandle",           a_argument, NULL, 'H'},
>  	{"warning",           a_argument, NULL, 'W'},
>  	{"error",             a_argument, NULL, 'E'},
> +       {"symbols",          no_argument, NULL, '@'},
>  	{"help",             no_argument, NULL, 'h'},
>  	{"version",          no_argument, NULL, 'v'},
>  	{NULL,               no_argument, NULL, 0x0},
> @@ -101,6 +103,7 @@
>  	 "\t\tboth   - Both \"linux,phandle\" and \"phandle\" 
> properties",
>  	"\n\tEnable/disable warnings (prefix with \"no-\")",
>  	"\n\tEnable/disable errors (prefix with \"no-\")",
> +       "\n\tEnable symbols/fixup support",
>  	"\n\tPrint this help and exit",
>  	"\n\tPrint version and exit",
>  	NULL,
> @@ -233,7 +236,9 @@
>  		case 'E':
>  			parse_checks_option(false, true, optarg);
>  			break;
> -
> +               case '@':
> +                       symbol_fixup_support = 1;
> +                       break;
>  		case 'h':
>  			usage(NULL);
>  		default:
> --- a/dtc.h     2015-12-10 14:50:21.538561922 -0500
> +++ b/dtc.h     2015-12-01 17:11:59.250242026 -0500
> @@ -54,6 +54,7 @@
>  extern int minsize;             /* Minimum blob size */
>  extern int padsize;             /* Additional padding to blob */
>  extern int phandle_format;      /* Use linux,phandle or phandle
>  properties 
> */
> +extern int symbol_fixup_support;/* enable symbols & fixup support */
>  
>  #define PHANDLE_LEGACY  0x1
>  #define PHANDLE_EPAPR   0x2
> @@ -132,6 +133,26 @@
>  	struct label *next;
>  };
>  
> +struct fixup_entry {
> +       int offset;
> +       struct node *node;
> +       struct property *prop;
> +       struct fixup_entry *next;
> +       bool local_fixup_generated;
> +};
> +
> +struct fixup {
> +       char *ref;
> +       struct fixup_entry *entries;
> +       struct fixup *next;
> +};
> +
> +struct symbol {
> +       struct label *label;
> +       struct node *node;
> +       struct symbol *next;
> +};
> +
>  struct property {
>  	bool deleted;
>  	char *name;
> @@ -158,6 +179,13 @@
>  	int addr_cells, size_cells;
>  
>  	struct label *labels;
> +
> +       struct symbol *symbols;
> +       struct fixup_entry *local_fixups;
> +       bool emit_local_fixup_node;
> +
> +       bool is_plugin;
> +       struct fixup *fixups;
>  };
>  
>  #define for_each_label_withdel(l0, l) \
> @@ -181,6 +209,18 @@
>  	for_each_child_withdel(n, c) \
>  		if (!(c)->deleted)
>  
> +#define for_each_fixup(n, f) \
> +       for ((f) = (n)->fixups; (f); (f) = (f)->next)
> +
> +#define for_each_fixup_entry(f, fe) \
> +       for ((fe) = (f)->entries; (fe); (fe) = (fe)->next)
> +
> +#define for_each_symbol(n, s) \
> +       for ((s) = (n)->symbols; (s); (s) = (s)->next)
> +
> +#define for_each_local_fixup_entry(n, fe) \
> +       for ((fe) = (n)->local_fixups; (fe); (fe) = (fe)->next)
> +
>  void add_label(struct label **labels, char *label);
>  void delete_labels(struct label **labels);
>  
> --- a/dtc-lexer.l       2015-12-10 14:50:21.537561920 -0500
> +++ b/dtc-lexer.l       2015-12-01 17:11:59.249242025 -0500
> @@ -113,6 +113,11 @@
>  			return DT_V1;
>  		}
>  
> +<*>"/plugin/"  {
> +                       DPRINT("Keyword: /plugin/\n");
> +                       return DT_PLUGIN;
> +               }
> +
>  <*>"/memreserve/"       {
>  			DPRINT("Keyword: /memreserve/\n");
>  			BEGIN_DEFAULT();
> --- a/dtc-parser.y      2015-12-10 14:50:21.537561920 -0500
> +++ b/dtc-parser.y      2015-12-01 17:11:59.249242025 -0500
> @@ -19,6 +19,7 @@
>   */
>  %{
>  #include <stdio.h>
> +#include <inttypes.h>
>  
>  #include "dtc.h"
>  #include "srcpos.h"
> @@ -52,9 +53,11 @@
>  	struct node *nodelist;
>  	struct reserve_info *re;
>  	uint64_t integer;
> +       bool is_plugin;
>  }
>  
>  %token DT_V1
> +%token DT_PLUGIN
>  %token DT_MEMRESERVE
>  %token DT_LSHIFT DT_RSHIFT DT_LE DT_GE DT_EQ DT_NE DT_AND DT_OR
>  %token DT_BITS
> @@ -71,6 +74,7 @@
>  
>  %type <data> propdata
>  %type <data> propdataprefix
> +%type <is_plugin> plugindecl
>  %type <re> memreserve
>  %type <re> memreserves
>  %type <array> arrayprefix
> @@ -101,10 +105,22 @@
>  %%
>  
>  sourcefile:
> -         DT_V1 ';' memreserves devicetree
> +         DT_V1 ';' plugindecl memreserves devicetree
>  		{
> -                       the_boot_info = build_boot_info($3, $4,
> -                                                       
> guess_boot_cpuid($4));
> +                       $5->is_plugin = $3;
> +                       the_boot_info = build_boot_info($4, $5,
> +                                                       
> guess_boot_cpuid($5));
> +               }
> +       ;
> +
> +plugindecl:
> +       /* empty */
> +               {
> +                       $$ = false;
> +               }
> +       | DT_PLUGIN ';'
> +               {
> +                       $$ = true;
>  		}
>  	;
>  
> --- a/fdtdump.c 2015-12-10 14:50:21.538561922 -0500
> +++ b/fdtdump.c 2015-12-01 17:11:59.250242026 -0500
> @@ -8,6 +8,14 @@
>  #include <stdlib.h>
>  #include <string.h>
>  #include <ctype.h>
> +#include <sys/types.h>
> +#include <sys/stat.h>
> +#include <alloca.h>
> +#include <dirent.h>
> +#include <limits.h>
> +#include <unistd.h>
> +#include <fcntl.h>
> +#include <errno.h>
>  
>  #include <libfdt.h>
>  #include <libfdt_env.h>
> @@ -143,6 +151,95 @@
>  	}
>  }
>  
> +static void dump_live_internal(const char *path, bool debug, int depth)
> +{
> +       int maxsz = strlen(path) + 1 + PATH_MAX;
> +       char *new_path = alloca(maxsz + 1);
> +       struct stat sb;
> +       struct dirent *de;
> +       char *buf, *p;
> +       int buf_alloc, shift, chunk, left, fd, ret;
> +       DIR *d;
> +
> +       shift = 4;
> +       buf_alloc = 4 * 1024;   /* 4K (maximum chunk) */
> +       buf = alloca(buf_alloc + sizeof(uint32_t));
> +       buf[buf_alloc] = '\0';  /* always terminate (just in case) */
> +
> +       d = opendir(path);
> +       if (d == NULL)
> +               die("Could not open %s directory\n", path);
> +
> +       /* first dump the properties (files) */
> +       while ((de = readdir(d)) != NULL) {
> +               /* properties are files */
> +               if (de->d_type != DT_REG)
> +                       continue;
> +               snprintf(new_path, maxsz, "%s/%s", path, de->d_name);
> +               new_path[maxsz] = '\0';
> +               printf("%*s%s", depth * shift, "", de->d_name);
> +
> +               if (stat(new_path, &sb) != 0)
> +                       die("could not open: %s\n", new_path);
> +
> +               fd = open(new_path, O_RDONLY);
> +               if (fd == -1)
> +                       die("Could not open: %s\n", new_path);
> +
> +               chunk = sb.st_size > buf_alloc ? buf_alloc : 
> sb.st_size;
> +               p = buf;
> +               left = chunk;
> +               while (left > 0) {
> +                       do {
> +                               ret = read(fd, p, left);
> +                       } while (ret == -1 && (errno == EAGAIN || 
> errno == EINTR));
> +                       if (ret == -1)
> +                               die("Read failed on: %s\n", 
> new_path);
> +                       left -= ret;
> +                       p += ret;
> +               }
> +               close(fd);
> +
> +               if (chunk < sb.st_size)
> +                       printf(" (trunc)");
> +               utilfdt_print_data(buf, chunk);
> +               printf(";\n");
> +       }
> +
> +       /* now recurse to the directories */
> +       rewinddir(d);
> +       while ((de = readdir(d)) != NULL) {
> +               /* properties are files */
> +               if (de->d_type != DT_DIR)
> +                       continue;
> +               /* skip current and parent directories */
> +               if (strcmp(de->d_name, ".") == 0 ||
> +                               strcmp(de->d_name, "..") == 0)
> +                       continue;
> +               snprintf(new_path, maxsz, "%s/%s", path, de->d_name);
> +               new_path[maxsz] = '\0';
> +               printf("%*s%s {\n", depth * shift, "", de->d_name);
> +               dump_live_internal(new_path, debug, depth + 1);
> +               printf("%*s};\n", depth * shift, "");
> +       }
> +}
> +
> +static void dump_live(const char *path, bool debug)
> +{
> +       char *fixed_path = alloca(strlen(path) + 1);
> +       char *p;
> +
> +       /* strip trailing / */
> +       strcpy(fixed_path, path);
> +       p = fixed_path + strlen(fixed_path) - 1;
> +       while (*p == '/' && p > fixed_path)
> +               *p-- = '\0';
> +       printf("/* dump of live tree at %s */\n", fixed_path);
> +       printf("/ {\n");
> +       dump_live_internal(fixed_path, debug, 1);
> +       printf("};\n");
> +}
> +
>  /* Usage related data. */
>  static const char usage_synopsis[] = "fdtdump [options] <file>";
>  static const char usage_short_opts[] = "ds" USAGE_COMMON_SHORT_OPTS;
> @@ -165,6 +262,7 @@
>  	bool debug = false;
>  	bool scan = false;
>  	off_t len;
> +       struct stat sb;
>  
>  	while ((opt = util_getopt_long()) != EOF) {
>  		switch (opt) {
> @@ -182,6 +280,15 @@
>  		usage("missing input filename");
>  	file = argv[optind];
>  
> +       if (stat(file, &sb) != 0)
> +               die("could not open: %s\n", file);
> +
> +       /* dump live tree if it's a directory */
> +       if (S_ISDIR(sb.st_mode)) {
> +               dump_live(file, debug);
> +               return 0;
> +       }
> +
>  	buf = utilfdt_read_len(file, &len);
>  	if (!buf)
>  		die("could not read: %s\n", file);
> --- a/flattree.c        2015-12-10 14:50:21.538561922 -0500
> +++ b/flattree.c        2015-12-01 17:11:59.250242026 -0500
> @@ -255,6 +255,204 @@
>  	return i;
>  }
>  
> +static void emit_local_fixups(struct node *tree, struct emitter *emit,
> +               void *etarget, struct data *strbuf, struct 
> version_info *vi,
> +               struct node *node)
> +{
> +       struct fixup_entry *fe, *fen;
> +       struct node *child;
> +       int nameoff, count;
> +       cell_t *buf;
> +       struct data d;
> +
> +       if (node->emit_local_fixup_node) {
> +
> +               /* emit the external fixups (do not emit /) */
> +               if (node != tree) {
> +                       emit->beginnode(etarget, NULL);
> +                       emit->string(etarget, node->name, 0);
> +                       emit->align(etarget, sizeof(cell_t));
> +               }
> +
> +               for_each_local_fixup_entry(tree, fe) {
> +                       if (fe->node != node || fe-
> >local_fixup_generated)
> +                               continue;
> +
> +                       /* count the number of fixup entries */
> +                       count = 0;
> +                       for_each_local_fixup_entry(tree, fen) {
> +                               if (fen->prop != fe->prop)
> +                                       continue;
> +                               fen->local_fixup_generated = 
> true;
> +                               count++;
> +                       }
> +
> +                       /* allocate buffer */
> +                       buf = xmalloc(count * sizeof(cell_t));
> +
> +                       /* collect all the offsets in buffer */
> +                       count = 0;
> +                       for_each_local_fixup_entry(tree, fen) {
> +                               if (fen->prop != fe->prop)
> +                                       continue;
> +                               fen->local_fixup_generated = 
> true;
> +                               buf[count++] = cpu_to_fdt32(fen-
> >offset);
> +                       }
> +                       d = empty_data;
> +                       d.len = count * sizeof(cell_t);
> +                       d.val = (char *)buf;
> +
> +                       nameoff = stringtable_insert(strbuf, fe-
> >prop->name);
> +                       emit->property(etarget, fe->prop->labels);
> +                       emit->cell(etarget, count * 
> sizeof(cell_t));
> +                       emit->cell(etarget, nameoff);
> +
> +                       if ((vi->flags & FTF_VARALIGN) &&
> +                                       (count * 
> sizeof(cell_t)) >= 8)
> +                               emit->align(etarget, 8);
> +
> +                       emit->data(etarget, d);
> +                       emit->align(etarget, sizeof(cell_t));
> +
> +                       free(buf);
> +               }
> +       }
> +
> +       for_each_child(node, child)
> +               emit_local_fixups(tree, emit, etarget, strbuf, vi, 
> child);
> +
> +       if (node->emit_local_fixup_node && node != tree)
> +               emit->endnode(etarget, tree->labels);
> +}
> +
> +static void emit_symbols_node(struct node *tree, struct emitter *emit,
> +                             void *etarget, struct data *strbuf,
> +                             struct version_info *vi)
> +{
> +       struct symbol *sym;
> +       int nameoff, vallen;
> +
> +       /* do nothing if no symbols */
> +       if (!tree->symbols)
> +               return;
> +
> +       emit->beginnode(etarget, NULL);
> +       emit->string(etarget, "__symbols__", 0);
> +       emit->align(etarget, sizeof(cell_t));
> +
> +       for_each_symbol(tree, sym) {
> +
> +               vallen = strlen(sym->node->fullpath);
> +
> +               nameoff = stringtable_insert(strbuf, sym->label-
> >label);
> +
> +               emit->property(etarget, NULL);
> +               emit->cell(etarget, vallen + 1);
> +               emit->cell(etarget, nameoff);
> +
> +               if ((vi->flags & FTF_VARALIGN) && vallen >= 8)
> +                       emit->align(etarget, 8);
> +
> +               emit->string(etarget, sym->node->fullpath,
> +                               strlen(sym->node->fullpath));
> +               emit->align(etarget, sizeof(cell_t));
> +       }
> +
> +       emit->endnode(etarget, NULL);
> +}
> +
> +static void emit_local_fixups_node(struct node *tree, struct emitter
> *emit,
> +                                  void *etarget, struct data 
> *strbuf,
> +                                  struct version_info *vi)
> +{
> +       struct fixup_entry *fe;
> +       struct node *node;
> +
> +       /* do nothing if no local fixups */
> +       if (!tree->local_fixups)
> +               return;
> +
> +       /* mark all nodes that need a local fixup generated (and parents) 
> */
> +       for_each_local_fixup_entry(tree, fe) {
> +               node = fe->node;
> +               while (node != NULL && !node->emit_local_fixup_node) {
> +                       node->emit_local_fixup_node = true;
> +                       node = node->parent;
> +               }
> +       }
> +
> +       /* emit the local fixups node now */
> +       emit->beginnode(etarget, NULL);
> +       emit->string(etarget, "__local_fixups__", 0);
> +       emit->align(etarget, sizeof(cell_t));
> +
> +       emit_local_fixups(tree, emit, etarget, strbuf, vi, tree);
> +
> +       emit->endnode(etarget, tree->labels);
> +}
> +
> +static void emit_fixups_node(struct node *tree, struct emitter *emit,
> +                            void *etarget, struct data *strbuf,
> +                            struct version_info *vi)
> +{
> +       struct fixup *f;
> +       struct fixup_entry *fe;
> +       char *name, *s;
> +       const char *fullpath;
> +       int namesz, nameoff, vallen;
> +
> +       /* do nothing if no fixups */
> +       if (!tree->fixups)
> +               return;
> +
> +       /* emit the external fixups */
> +       emit->beginnode(etarget, NULL);
> +       emit->string(etarget, "__fixups__", 0);
> +       emit->align(etarget, sizeof(cell_t));
> +
> +       for_each_fixup(tree, f) {
> +
> +               namesz = 0;
> +               for_each_fixup_entry(f, fe) {
> +                       fullpath = fe->node->fullpath;
> +                       if (fullpath[0] == '\0')
> +                               fullpath = "/";
> +                       namesz += strlen(fullpath) + 1;
> +                       namesz += strlen(fe->prop->name) + 1;
> +                       namesz += 32;   /* space for 
> :<number> + '\0' */
> +               }
> +
> +               name = xmalloc(namesz);
> +
> +               s = name;
> +               for_each_fixup_entry(f, fe) {
> +                       fullpath = fe->node->fullpath;
> +                       if (fullpath[0] == '\0')
> +                               fullpath = "/";
> +                       snprintf(s, name + namesz - s, "%s:%s:%d", 
> fullpath,
> +                                       fe->prop->name, fe-
> >offset);
> +                       s += strlen(s) + 1;
> +               }
> +
> +               nameoff = stringtable_insert(strbuf, f->ref);
> +               vallen = s - name - 1;
> +
> +               emit->property(etarget, NULL);
> +               emit->cell(etarget, vallen + 1);
> +               emit->cell(etarget, nameoff);
> +
> +               if ((vi->flags & FTF_VARALIGN) && vallen >= 8)
> +                       emit->align(etarget, 8);
> +
> +               emit->string(etarget, name, vallen);
> +               emit->align(etarget, sizeof(cell_t));
> +
> +               free(name);
> +       }
> +
> +       emit->endnode(etarget, tree->labels);
> +}
> +
>  static void flatten_tree(struct node *tree, struct emitter *emit,
>  			 void *etarget, struct data *strbuf,
>  			 struct version_info *vi)
> @@ -310,6 +508,10 @@
>  		flatten_tree(child, emit, etarget, strbuf, vi);
>  	}
>  
> +       emit_symbols_node(tree, emit, etarget, strbuf, vi);
> +       emit_local_fixups_node(tree, emit, etarget, strbuf, vi);
> +       emit_fixups_node(tree, emit, etarget, strbuf, vi);
> +
>  	emit->endnode(etarget, tree->labels);
>  }
> 
> --- a/libfdt/fdt_ro.c   2014-11-11 22:29:16.000000000 -0500
> +++ b/libfdt/fdt_ro.c   2015-12-01 17:11:42.213219672 -0500
> @@ -154,9 +154,9 @@
>  	return fdt_subnode_offset_namelen(fdt, parentoffset, name, 
> strlen(name));
>  }
>  
> -int fdt_path_offset(const void *fdt, const char *path)
> +int fdt_path_offset_namelen(const void *fdt, const char *path, int
> namelen)
>  {
> -       const char *end = path + strlen(path);
> +       const char *end = path + namelen;
>  	const char *p = path;
>  	int offset = 0;
>  
> @@ -164,7 +164,7 @@
>  
>  	/* see if we have an alias */
>  	if (*path != '/') {
> -               const char *q = strchr(path, '/');
> +               const char *q = memchr(path, '/', end - p);
>  
>  		if (!q)
>  			q = end;
> @@ -177,14 +177,15 @@
>  		p = q;
>  	}
>  
> -       while (*p) {
> +       while (p < end) {
>  		const char *q;
>  
> -               while (*p == '/')
> +               while (*p == '/') {
>  			p++;
> -               if (! *p)
> -                       return offset;
> -               q = strchr(p, '/');
> +                       if (p == end)
> +                               return offset;
> +               }
> +               q = memchr(p, '/', end - p);
>  		if (! q)
>  			q = end;
>  
> @@ -198,6 +199,11 @@
>  	return offset;
>  }
>  
> +int fdt_path_offset(const void *fdt, const char *path)
> +{
> +       return fdt_path_offset_namelen(fdt, path, strlen(path));
> +}
> +
>  const char *fdt_get_name(const void *fdt, int nodeoffset, int *len)
>  {
>  	const struct fdt_node_header *nh = _fdt_offset_ptr(fdt, 
> nodeoffset);
> 
> --- a/libfdt/libfdt.h   2014-11-11 22:29:16.000000000 -0500
> +++ b/libfdt/libfdt.h   2015-12-01 17:11:42.213219672 -0500
> @@ -323,6 +323,17 @@
>  int fdt_subnode_offset(const void *fdt, int parentoffset, const char 
> *name);
>  
>  /**
> + * fdt_path_offset_namelen - find a tree node by its full path
> + * @fdt: pointer to the device tree blob
> + * @path: full path of the node to locate
> + * @namelen: number of characters of path to consider
> + *
> + * Identical to fdt_path_offset(), but only consider the first namelen
> + * characters of path as the path name.
> + */
> +int fdt_path_offset_namelen(const void *fdt, const char *path, int 
> namelen);
> +
> +/**
>   * fdt_path_offset - find a tree node by its full path
>   * @fdt: pointer to the device tree blob
>   * @path: full path of the node to locate
> 
> --- a/libfdt/version.lds        2014-11-11 22:29:16.000000000 -0500
> +++ b/libfdt/version.lds        2015-12-01 17:11:42.213219672 -0500
> @@ -8,6 +8,7 @@
>  		fdt_get_mem_rsv;
>  		fdt_subnode_offset_namelen;
>  		fdt_subnode_offset;
> +               fdt_path_offset_namelen;
>  		fdt_path_offset;
>  		fdt_get_name;
>  		fdt_get_property_namelen;
> @@ -54,6 +55,8 @@
>  		fdt_get_property_by_offset;
>  		fdt_getprop_by_offset;
>  		fdt_next_property_offset;
> +               fdt_first_subnode;
> +               fdt_next_subnode;
>  
>  	local:
>  		*;
> 
> 
> --- a/tests/run_tests.sh        2014-11-11 22:29:16.000000000 -0500
> +++ b/tests/run_tests.sh        2015-12-01 17:11:42.213219672 -0500
> @@ -42,20 +42,20 @@
>  
>  shorten_echo () {
>      limit=32
> -    echo -n "$1"
> +    printf "$1"
>      shift
>      for x; do
>  	if [ ${#x} -le $limit ]; then
> -           echo -n " $x"
> +           printf " $x"
>  	else
>  	    short=$(echo "$x" | head -c$limit)
> -           echo -n " \"$short\"...<${#x} bytes>"
> +           printf " \"$short\"...<${#x} bytes>"
>  	fi
>      done
>  }
>  
>  run_test () {
> -    echo -n "$@:       "
> +    printf "$*:        "
>      if [ -n "$VALGRIND" -a -f $1.supp ]; then
>  	VGSUPP="--suppressions=$1.supp"
>      fi
> @@ -63,7 +63,7 @@
>  }
>  
>  run_sh_test () {
> -    echo -n "$@:       "
> +    printf "$*:        "
>      base_run_test sh "$@"
>  }
>  
> @@ -106,12 +106,12 @@
>  
>  run_wrap_error_test () {
>      shorten_echo "$@"
> -    echo -n " {!= 0}:  "
> +    printf " {!= 0}:   "
>      base_run_test wrap_error "$@"
>  }
>  
>  run_dtc_test () {
> -    echo -n "dtc $@:   "
> +    printf "dtc $*:    "
>      base_run_test wrap_test $VALGRIND $DTC "$@"
>  }
>  
> @@ -126,7 +126,7 @@
>  run_fdtget_test () {
>      expect="$1"
>      shift
> -    echo -n "fdtget-runtest.sh "$expect" $@:   "
> +    printf "fdtget-runtest.sh %s $*:   " "$(echo $expect)"
>      base_run_test sh fdtget-runtest.sh "$expect" "$@"
>  }
>  
> @@ -134,14 +134,14 @@
>      expect="$1"
>      shift
>      shorten_echo fdtput-runtest.sh "$expect" "$@"
> -    echo -n ": "
> +    printf ":  "
>      base_run_test sh fdtput-runtest.sh "$expect" "$@"
>  }
>  
>  run_fdtdump_test() {
>      file="$1"
>      shorten_echo fdtdump-runtest.sh "$file"
> -    echo -n ": "
> +    printf ":  "
>      base_run_test sh fdtdump-runtest.sh "$file"
>  }
>  
> @@ -279,6 +279,8 @@
>      run_dtc_test -I dts -O dtb -o embedded_nul.test.dtb embedded_nul.dts
>      run_dtc_test -I dts -O dtb -o embedded_nul_equiv.test.dtb 
> embedded_nul_equiv.dts
>      run_test dtbs_equal_ordered embedded_nul.test.dtb 
> embedded_nul_equiv.test.dtb
> +
> +    run_dtc_test -I dts -O dtb bad-size-cells.dts
>  }
>  
>  dtc_tests () {
> @@ -399,6 +401,8 @@
>      tree1_tests dtc_tree1_merge.test.dtb test_tree1.dtb
>      run_dtc_test -I dts -O dtb -o dtc_tree1_merge_labelled.test.dtb 
> test_tree1_merge_labelled.dts
>      tree1_tests dtc_tree1_merge_labelled.test.dtb test_tree1.dtb
> +    run_dtc_test -I dts -O dtb -o dtc_tree1_label_noderef.test.dtb 
> test_tree1_label_noderef.dts
> +    run_test dtbs_equal_unordered dtc_tree1_label_noderef.test.dtb 
> test_tree1.dtb
>      run_dtc_test -I dts -O dtb -o multilabel_merge.test.dtb 
> multilabel_merge.dts
>      run_test references multilabel.test.dtb
>      run_test dtbs_equal_ordered multilabel.test.dtb 
> multilabel_merge.test.dtb
> @@ -610,6 +614,28 @@
>      run_wrap_test $DTPUT $dtb -cp /chosen
>      run_wrap_test $DTPUT $dtb -cp /chosen/son
>  
> +    # Start again with a fresh dtb
> +    run_dtc_test -O dtb -p $(stat -c %s $text) -o $dtb $dts
> +
> +    # Node delete
> +    run_wrap_test $DTPUT $dtb -c /chosen/node1 /chosen/node2
> /chosen/node3
> +    run_fdtget_test "node3\nnode2\nnode1" $dtb -l  /chosen
> +    run_wrap_test $DTPUT $dtb -r /chosen/node1 /chosen/node2
> +    run_fdtget_test "node3" $dtb -l  /chosen
> +
> +    # Delete the non-existent node
> +    run_wrap_error_test $DTPUT $dtb -r /non-existent/node
> +
> +    # Property delete
> +    run_fdtput_test "eva" $dtb /chosen/ name "" -ts "eva"
> +    run_fdtput_test "016" $dtb /chosen/ age  "" -ts "016"
> +    run_fdtget_test "age\nname\nbootargs\nlinux,platform" $dtb -p 
> /chosen
> +    run_wrap_test $DTPUT $dtb -d /chosen/ name age
> +    run_fdtget_test "bootargs\nlinux,platform" $dtb -p  /chosen
> +
> +    # Delete the non-existent property
> +    run_wrap_error_test $DTPUT $dtb -d /chosen   non-existent-prop
> +
>      # TODO: Add tests for verbose mode?
>  }
>  
> 
> 
> 
> 
> 
> 
> _______________________________________________
> buildroot mailing list
> buildroot@busybox.net
> http://lists.busybox.net/mailman/listinfo/buildroot
diff mbox

Patch

--- a/checks.c	2015-12-10 14:50:21.537561920 -0500
+++ b/checks.c	2015-12-01 17:11:59.266242047 -0500
@@ -458,6 +458,8 @@ 
 				     struct node *node, struct 
property *prop)
 {
 	struct marker *m = prop->val.markers;
+	struct fixup *f, **fp;
+	struct fixup_entry *fe, **fep;
 	struct node *refnode;
 	cell_t phandle;
 
@@ -466,14 +468,73 @@ 
 
 		refnode = get_node_by_ref(dt, m->ref);
 		if (! refnode) {
+			if (!dt->is_plugin) {
 			FAIL(c, "Reference to non-existent node or 
label \"%s\"\n",
 			     m->ref);
 			continue;
 		}
 
+			/* allocate fixup entry */
+			fe = xmalloc(sizeof(*fe));
+
+			fe->node = node;
+			fe->prop = prop;
+			fe->offset = m->offset;
+			fe->next = NULL;
+
+			/* search for an already existing fixup */
+			for_each_fixup(dt, f)
+				if (strcmp(f->ref, m->ref) == 0)
+					break;
+
+			/* no fixup found, add new */
+			if (f == NULL) {
+				f = xmalloc(sizeof(*f));
+				f->ref = m->ref;
+				f->entries = NULL;
+				f->next = NULL;
+
+				/* add it to the tree */
+				fp = &dt->fixups;
+				while (*fp)
+					fp = &(*fp)->next;
+				*fp = f;
+			}
+
+			/* and now append fixup entry */
+			fep = &f->entries;
+			while (*fep)
+				fep = &(*fep)->next;
+			*fep = fe;
+
+			/* mark the entry as unresolved */
+			*((cell_t *)(prop->val.val + m->offset)) =
+				cpu_to_fdt32(0xdeadbeef);
+			continue;
+		}
+
+		/* if it's a local reference, we need to record it */
+		if (symbol_fixup_support) {
+
+			/* allocate a new local fixup entry */
+			fe = xmalloc(sizeof(*fe));
+
+			fe->node = node;
+			fe->prop = prop;
+			fe->offset = m->offset;
+			fe->next = NULL;
+
+			/* append it to the local fixups */
+			fep = &dt->local_fixups;
+			while (*fep)
+				fep = &(*fep)->next;
+			*fep = fe;
+		}
+
 		phandle = get_node_phandle(dt, refnode);
 		*((cell_t *)(prop->val.val + m->offset)) = 
cpu_to_fdt32(phandle);
 	}
+
 }
 ERROR(phandle_references, NULL, NULL, fixup_phandle_references, NULL,
       &duplicate_node_names, &explicit_phandles);
@@ -652,6 +713,45 @@ 
 }
 TREE_WARNING(obsolete_chosen_interrupt_controller, NULL);
 
+static void check_auto_label_phandles(struct check *c, struct node *dt,
+				       struct node *node)
+{
+	struct label *l;
+	struct symbol *s, **sp;
+	int has_label;
+
+	if (!symbol_fixup_support)
+		return;
+
+	has_label = 0;
+	for_each_label(node->labels, l) {
+		has_label = 1;
+		break;
+	}
+
+	if (!has_label)
+		return;
+
+	/* force allocation of a phandle for this node */
+	(void)get_node_phandle(dt, node);
+
+	/* add the symbol */
+	for_each_label(node->labels, l) {
+
+		s = xmalloc(sizeof(*s));
+		s->label = l;
+		s->node = node;
+		s->next = NULL;
+
+		/* add it to the symbols list */
+		sp = &dt->symbols;
+		while (*sp)
+			sp = &((*sp)->next);
+		*sp = s;
+	}
+}
+NODE_WARNING(auto_label_phandles, NULL);
+
 static struct check *check_table[] = {
 	&duplicate_node_names, &duplicate_property_names,
 	&node_name_chars, &node_name_format, &property_name_chars,
@@ -670,6 +770,8 @@ 
 	&avoid_default_addr_size,
 	&obsolete_chosen_interrupt_controller,
 
+	&auto_label_phandles,
+
 	&always_fail,
 };
 
--- a/Documentation/dt-object-internal.txt	1969-12-31 19:00:00.000000000 
-0500
+++ b/Documentation/dt-object-internal.txt	2015-12-01 17:11:59.249242025 
-0500
@@ -0,0 +1,301 @@ 
+Device Tree Dynamic Object format internals
+-------------------------------------------
+
+The Device Tree for most platforms is a static representation of
+the hardware capabilities. This is insufficient for many platforms
+that need to dynamically insert device tree fragments to the
+running kernel's live tree.
+
+This document explains the the device tree object format and the
+modifications made to the device tree compiler, which make it possible.
+
+1. Simplified Problem Definition
+--------------------------------
+
+Assume we have a platform which boots using following simplified device 
tree.
+
+---- foo.dts 
-----------------------------------------------------------------
+	/* FOO platform */
+	/ {
+		compatible = "corp,foo";
+
+		/* shared resources */
+		res: res {
+		};
+
+		/* On chip peripherals */
+		ocp: ocp {
+			/* peripherals that are always instantiated 
*/
+			peripheral1 { ... };
+		}
+	};
+---- foo.dts 
-----------------------------------------------------------------
+
+We have a number of peripherals that after probing (using some undefined 
method)
+should result in different device tree configuration.
+
+We cannot boot with this static tree because due to the configuration of 
the
+foo platform there exist multiple conficting peripherals DT fragments.
+
+So for the bar peripheral we would have this:
+
+---- foo+bar.dts 
-------------------------------------------------------------
+	/* FOO platform + bar peripheral */
+	/ {
+		compatible = "corp,foo";
+
+		/* shared resources */
+		res: res {
+		};
+
+		/* On chip peripherals */
+		ocp: ocp {
+			/* peripherals that are always instantiated 
*/
+			peripheral1 { ... };
+
+			/* bar peripheral */
+			bar {
+				compatible = "corp,bar";
+				... /* various properties and 
child nodes */
+			}
+		}
+	};
+---- foo+bar.dts 
-------------------------------------------------------------
+
+While for the baz peripheral we would have this:
+
+---- foo+baz.dts 
-------------------------------------------------------------
+	/* FOO platform + baz peripheral */
+	/ {
+		compatible = "corp,foo";
+
+		/* shared resources */
+		res: res {
+			/* baz resources */
+			baz_res: res_baz { ... };
+		};
+
+		/* On chip peripherals */
+		ocp: ocp {
+			/* peripherals that are always instantiated 
*/
+			peripheral1 { ... };
+
+			/* baz peripheral */
+			baz {
+				compatible = "corp,baz";
+				/* reference to another point in 
the tree */
+				ref-to-res = <&baz_res>;
+				... /* various properties and 
child nodes */
+			}
+		}
+	};
+---- foo+baz.dts 
-------------------------------------------------------------
+
+We note that the baz case is more complicated, since the baz peripheral 
needs to
+reference another node in the DT tree.
+
+2. Device Tree Object Format Requirements
+-----------------------------------------
+
+Since the device tree is used for booting a number of very different 
hardware
+platforms it is imperative that we tread very carefully.
+
+2.a) No changes to the Device Tree binary format. We cannot modify the tree
+format at all and all the information we require should be encoded using 
device
+tree itself. We can add nodes that can be safely ignored by both 
bootloaders and
+the kernel.
+
+2.b) Changes to the DTS source format should be absolutely minimal, and 
should
+only be needed for the DT fragment definitions, and not the base boot DT.
+
+2.c) An explicit option should be used to instruct DTC to generate the 
required
+information needed for object resolution. Platforms that don't use the
+dynamic object format can safely ignore it.
+
+2.d) Finally, DT syntax changes should be kept to a minimum. It should be
+possible to express everything using the existing DT syntax.
+
+3. Implementation
+-----------------
+
+The basic unit of addressing in Device Tree is the phandle. Turns out it's
+relatively simple to extend the way phandles are generated and referenced
+so that it's possible to dynamically convert symbolic references (labels)
+to phandle values.
+
+We can roughly divide the operation into two steps.
+
+3.a) Compilation of the base board DTS file using the '-@' option
+generates a valid DT blob with an added __symbols__ node at the root node,
+containing a list of all nodes that are marked with a label.
+
+Using the foo.dts file above the following node will be generated;
+
+$ dtc -@ -O dtb -o foo.dtb -b 0 foo.dts
+$ fdtdump foo.dtb
+...
+/ {
+	...
+	res {
+		...
+		linux,phandle = <0x00000001>;
+		phandle = <0x00000001>;
+		...
+	};
+	ocp {
+		...
+		linux,phandle = <0x00000002>;
+		phandle = <0x00000002>;
+		...
+	};
+	__symbols__ {
+		res="/res";
+		ocp="/ocp";
+	};
+};
+
+Notice that all the nodes that had a label have been recorded, and that
+phandles have been generated for them.
+
+This blob can be used to boot the board normally, the __symbols__ node will
+be safely ignored both by the bootloader and the kernel (the only loss will
+be a few bytes of memory and disk space).
+
+3.b) The Device Tree fragments must be compiled with the same option but 
they
+must also have a tag (/plugin/) that allows undefined references to labels
+that are not present at compilation time to be recorded so that the runtime
+loader can fix them.
+
+So the bar peripheral's DTS format would be of the form:
+
+/plugin/;	/* allow undefined label references and record them */
+/ {
+	....	/* various properties for loader use; i.e. part id 
etc. */
+	fragment@0 {
+		target = <&ocp>;
+		__overlay__ {
+			/* bar peripheral */
+			bar {
+				compatible = "corp,bar";
+				... /* various properties and 
child nodes */
+			}
+		};
+	};
+};
+
+Note that there's a target property that specifies the location where the
+contents of the overlay node will be placed, and it references the label
+in the foo.dts file.
+
+$ dtc -@ -O dtb -o bar.dtbo -b 0 bar.dts
+$ fdtdump bar.dtbo
+...
+/ {
+	... /* properties */
+	fragment@0 {
+		target = <0xdeadbeef>;
+		__overlay__ {
+			bar {
+				compatible = "corp,bar";
+				... /* various properties and 
child nodes */
+			}
+		};
+	};
+	__fixups__ {
+	    ocp = "/fragment@0:target:0";
+	};
+};
+
+No __symbols__ has been generated (no label in bar.dts).
+Note that the target's ocp label is undefined, so the phandle handle
+value is filled with the illegal value '0xdeadbeef', while a __fixups__
+node has been generated, which marks the location in the tree where
+the label lookup should store the runtime phandle value of the ocp node.
+
+The format of the __fixups__ node entry is
+
+	<label> = "<local-full-path>:<property-name>:<offset>";
+
+<label> 		Is the label we're referring
+<local-full-path>	Is the full path of the node the reference is
+<property-name>		Is the name of the property containing the
+			reference
+<offset>		The offset (in bytes) of where the property's
+			phandle value is located.
+
+Doing the same with the baz peripheral's DTS format is a little bit more
+involved, since baz contains references to local labels which require
+local fixups.
+
+/plugin/;	/* allow undefined label references and record them */
+/ {
+	....	/* various properties for loader use; i.e. part id 
etc. */
+	fragment@0 {
+		target = <&res>;
+		__overlay__ {
+			/* baz resources */
+			baz_res: res_baz { ... };
+		};
+	};
+	fragment@1 {
+		target = <&ocp>;
+		__overlay__ {
+			/* baz peripheral */
+			baz {
+				compatible = "corp,baz";
+				/* reference to another point in 
the tree */
+				ref-to-res = <&baz_res>;
+				... /* various properties and 
child nodes */
+			}
+		};
+	};
+};
+
+Note that &bar_res reference.
+
+$ dtc -@ -O dtb -o baz.dtbo -b 0 baz.dts
+$ fdtdump baz.dtbo
+...
+/ {
+	... /* properties */
+	fragment@0 {
+		target = <0xdeadbeef>;
+		__overlay__ {
+			res_baz {
+				....
+				linux,phandle = <0x00000001>;
+				phandle = <0x00000001>;
+			};
+		};
+	};
+	fragment@1 {
+		target = <0xdeadbeef>;
+		__overlay__ {
+			baz {
+				compatible = "corp,baz";
+				... /* various properties and 
child nodes */
+				ref-to-res = <0x00000001>;
+			}
+		};
+	};
+	__fixups__ {
+		res = "/fragment@0:target:0";
+		ocp = "/fragment@1:target:0";
+	};
+	__local_fixups__ {
+		fragment@1 {
+			__overlay__ {
+				baz {
+					ref-to-res = <0>;
+				};
+			};
+		};
+	};
+};
+
+This is similar to the bar case, but the reference of a local label by the
+baz node generates a __local_fixups__ entry that records the place that the
+local reference is being made. Since phandles are allocated starting at 1
+the run time loader must apply an offset to each phandle in every dynamic
+DT object loaded. The __local_fixups__ node records the place of every
+local reference so that the loader can apply the offset.

--- a/Documentation/manual.txt	2015-12-10 14:50:21.537561920 -0500
+++ b/Documentation/manual.txt	2015-12-01 17:11:59.249242025 -0500
@@ -119,6 +119,14 @@ 
 	Make space for <number> reserve map entries
 	Relevant for dtb and asm output only.
 
+    -@
+        Generates a __symbols__ node at the root node of the resulting blob
+	for any node labels used, and for any local references using 
phandles
+	it also generates a __local_fixups__ node that tracks them.
+
+	When using the /plugin/ tag all unresolved label references to
+	be tracked in the __fixups__ node, making dynamic resolution 
possible.
+
     -S <bytes>
 	Ensure the blob at least <bytes> long, adding additional
 	space if needed.
@@ -153,6 +161,8 @@ 
 
     devicetree:   '/' nodedef
 
+    plugindecl:   '/' 'plugin' '/' ';'
+
     nodedef:      '{' list_of_property list_of_subnode '}' ';'
 
     property:     label PROPNAME '=' propdata ';'
--- a/dtc.c	2015-12-10 14:50:21.537561920 -0500
+++ b/dtc.c	2015-12-01 17:11:59.264242045 -0500
@@ -31,6 +31,7 @@ 
 int minsize;		/* Minimum blob size */
 int padsize;		/* Additional padding to blob */
 int phandle_format = PHANDLE_BOTH;	/* Use linux,phandle or phandle 
properties */
+int symbol_fixup_support = 0;
 
 static void fill_fullpaths(struct node *tree, const char *prefix)
 {
@@ -53,7 +54,7 @@ 
 #define FDT_VERSION(version)	_FDT_VERSION(version)
 #define _FDT_VERSION(version)	#version
 static const char usage_synopsis[] = "dtc [options] <input file>";
-static const char usage_short_opts[] = "qI:O:o:V:d:R:S:p:fb:i:H:sW:E:hv";
+static const char usage_short_opts[] = "qI:O:o:V:d:R:S:p:fb:i:H:sW:E:@hv";
 static struct option const usage_long_opts[] = {
 	{"quiet",            no_argument, NULL, 'q'},
 	{"in-format",         a_argument, NULL, 'I'},
@@ -71,6 +72,7 @@ 
 	{"phandle",           a_argument, NULL, 'H'},
 	{"warning",           a_argument, NULL, 'W'},
 	{"error",             a_argument, NULL, 'E'},
+	{"symbols",	     no_argument, NULL, '@'},
 	{"help",             no_argument, NULL, 'h'},
 	{"version",          no_argument, NULL, 'v'},
 	{NULL,               no_argument, NULL, 0x0},
@@ -101,6 +103,7 @@ 
 	 "\t\tboth   - Both \"linux,phandle\" and \"phandle\" 
properties",
 	"\n\tEnable/disable warnings (prefix with \"no-\")",
 	"\n\tEnable/disable errors (prefix with \"no-\")",
+	"\n\tEnable symbols/fixup support",
 	"\n\tPrint this help and exit",
 	"\n\tPrint version and exit",
 	NULL,
@@ -233,7 +236,9 @@ 
 		case 'E':
 			parse_checks_option(false, true, optarg);
 			break;
-
+		case '@':
+			symbol_fixup_support = 1;
+			break;
 		case 'h':
 			usage(NULL);
 		default:
--- a/dtc.h	2015-12-10 14:50:21.538561922 -0500
+++ b/dtc.h	2015-12-01 17:11:59.250242026 -0500
@@ -54,6 +54,7 @@ 
 extern int minsize;		/* Minimum blob size */
 extern int padsize;		/* Additional padding to blob */
 extern int phandle_format;	/* Use linux,phandle or phandle properties 
*/
+extern int symbol_fixup_support;/* enable symbols & fixup support */
 
 #define PHANDLE_LEGACY	0x1
 #define PHANDLE_EPAPR	0x2
@@ -132,6 +133,26 @@ 
 	struct label *next;
 };
 
+struct fixup_entry {
+	int offset;
+	struct node *node;
+	struct property *prop;
+	struct fixup_entry *next;
+	bool local_fixup_generated;
+};
+
+struct fixup {
+	char *ref;
+	struct fixup_entry *entries;
+	struct fixup *next;
+};
+
+struct symbol {
+	struct label *label;
+	struct node *node;
+	struct symbol *next;
+};
+
 struct property {
 	bool deleted;
 	char *name;
@@ -158,6 +179,13 @@ 
 	int addr_cells, size_cells;
 
 	struct label *labels;
+
+	struct symbol *symbols;
+	struct fixup_entry *local_fixups;
+	bool emit_local_fixup_node;
+
+	bool is_plugin;
+	struct fixup *fixups;
 };
 
 #define for_each_label_withdel(l0, l) \
@@ -181,6 +209,18 @@ 
 	for_each_child_withdel(n, c) \
 		if (!(c)->deleted)
 
+#define for_each_fixup(n, f) \
+	for ((f) = (n)->fixups; (f); (f) = (f)->next)
+
+#define for_each_fixup_entry(f, fe) \
+	for ((fe) = (f)->entries; (fe); (fe) = (fe)->next)
+
+#define for_each_symbol(n, s) \
+	for ((s) = (n)->symbols; (s); (s) = (s)->next)
+
+#define for_each_local_fixup_entry(n, fe) \
+	for ((fe) = (n)->local_fixups; (fe); (fe) = (fe)->next)
+
 void add_label(struct label **labels, char *label);
 void delete_labels(struct label **labels);
 
--- a/dtc-lexer.l	2015-12-10 14:50:21.537561920 -0500
+++ b/dtc-lexer.l	2015-12-01 17:11:59.249242025 -0500
@@ -113,6 +113,11 @@ 
 			return DT_V1;
 		}
 
+<*>"/plugin/"	{
+			DPRINT("Keyword: /plugin/\n");
+			return DT_PLUGIN;
+		}
+
 <*>"/memreserve/"	{
 			DPRINT("Keyword: /memreserve/\n");
 			BEGIN_DEFAULT();
--- a/dtc-parser.y	2015-12-10 14:50:21.537561920 -0500
+++ b/dtc-parser.y	2015-12-01 17:11:59.249242025 -0500
@@ -19,6 +19,7 @@ 
  */
 %{
 #include <stdio.h>
+#include <inttypes.h>
 
 #include "dtc.h"
 #include "srcpos.h"
@@ -52,9 +53,11 @@ 
 	struct node *nodelist;
 	struct reserve_info *re;
 	uint64_t integer;
+	bool is_plugin;
 }
 
 %token DT_V1
+%token DT_PLUGIN
 %token DT_MEMRESERVE
 %token DT_LSHIFT DT_RSHIFT DT_LE DT_GE DT_EQ DT_NE DT_AND DT_OR
 %token DT_BITS
@@ -71,6 +74,7 @@ 
 
 %type <data> propdata
 %type <data> propdataprefix
+%type <is_plugin> plugindecl
 %type <re> memreserve
 %type <re> memreserves
 %type <array> arrayprefix
@@ -101,10 +105,22 @@ 
 %%
 
 sourcefile:
-	  DT_V1 ';' memreserves devicetree
+	  DT_V1 ';' plugindecl memreserves devicetree
 		{
-			the_boot_info = build_boot_info($3, $4,
-							
guess_boot_cpuid($4));
+			$5->is_plugin = $3;
+			the_boot_info = build_boot_info($4, $5,
+							
guess_boot_cpuid($5));
+		}
+	;
+
+plugindecl:
+	/* empty */
+		{
+			$$ = false;
+		}
+	| DT_PLUGIN ';'
+		{
+			$$ = true;
 		}
 	;
 
--- a/fdtdump.c	2015-12-10 14:50:21.538561922 -0500
+++ b/fdtdump.c	2015-12-01 17:11:59.250242026 -0500
@@ -8,6 +8,14 @@ 
 #include <stdlib.h>
 #include <string.h>
 #include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <alloca.h>
+#include <dirent.h>
+#include <limits.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
 
 #include <libfdt.h>
 #include <libfdt_env.h>
@@ -143,6 +151,95 @@ 
 	}
 }
 
+static void dump_live_internal(const char *path, bool debug, int depth)
+{
+	int maxsz = strlen(path) + 1 + PATH_MAX;
+	char *new_path = alloca(maxsz + 1);
+	struct stat sb;
+	struct dirent *de;
+	char *buf, *p;
+	int buf_alloc, shift, chunk, left, fd, ret;
+	DIR *d;
+
+	shift = 4;
+	buf_alloc = 4 * 1024;	/* 4K (maximum chunk) */
+	buf = alloca(buf_alloc + sizeof(uint32_t));
+	buf[buf_alloc] = '\0';	/* always terminate (just in case) */
+
+	d = opendir(path);
+	if (d == NULL)
+		die("Could not open %s directory\n", path);
+
+	/* first dump the properties (files) */
+	while ((de = readdir(d)) != NULL) {
+		/* properties are files */
+		if (de->d_type != DT_REG)
+			continue;
+		snprintf(new_path, maxsz, "%s/%s", path, de->d_name);
+		new_path[maxsz] = '\0';
+		printf("%*s%s", depth * shift, "", de->d_name);
+
+		if (stat(new_path, &sb) != 0)
+			die("could not open: %s\n", new_path);
+
+		fd = open(new_path, O_RDONLY);
+		if (fd == -1)
+			die("Could not open: %s\n", new_path);
+
+		chunk = sb.st_size > buf_alloc ? buf_alloc : 
sb.st_size;
+		p = buf;
+		left = chunk;
+		while (left > 0) {
+			do {
+				ret = read(fd, p, left);
+			} while (ret == -1 && (errno == EAGAIN || 
errno == EINTR));
+			if (ret == -1)
+				die("Read failed on: %s\n", 
new_path);
+			left -= ret;
+			p += ret;
+		}
+		close(fd);
+
+		if (chunk < sb.st_size)
+			printf(" (trunc)");
+		utilfdt_print_data(buf, chunk);
+		printf(";\n");
+	}
+
+	/* now recurse to the directories */
+	rewinddir(d);
+	while ((de = readdir(d)) != NULL) {
+		/* properties are files */
+		if (de->d_type != DT_DIR)
+			continue;
+		/* skip current and parent directories */
+		if (strcmp(de->d_name, ".") == 0 ||
+				strcmp(de->d_name, "..") == 0)
+			continue;
+		snprintf(new_path, maxsz, "%s/%s", path, de->d_name);
+		new_path[maxsz] = '\0';
+		printf("%*s%s {\n", depth * shift, "", de->d_name);
+		dump_live_internal(new_path, debug, depth + 1);
+		printf("%*s};\n", depth * shift, "");
+	}
+}
+
+static void dump_live(const char *path, bool debug)
+{
+	char *fixed_path = alloca(strlen(path) + 1);
+	char *p;
+
+	/* strip trailing / */
+	strcpy(fixed_path, path);
+	p = fixed_path + strlen(fixed_path) - 1;
+	while (*p == '/' && p > fixed_path)
+		*p-- = '\0';
+	printf("/* dump of live tree at %s */\n", fixed_path);
+	printf("/ {\n");
+	dump_live_internal(fixed_path, debug, 1);
+	printf("};\n");
+}
+
 /* Usage related data. */
 static const char usage_synopsis[] = "fdtdump [options] <file>";
 static const char usage_short_opts[] = "ds" USAGE_COMMON_SHORT_OPTS;
@@ -165,6 +262,7 @@ 
 	bool debug = false;
 	bool scan = false;
 	off_t len;
+	struct stat sb;
 
 	while ((opt = util_getopt_long()) != EOF) {
 		switch (opt) {
@@ -182,6 +280,15 @@ 
 		usage("missing input filename");
 	file = argv[optind];
 
+	if (stat(file, &sb) != 0)
+		die("could not open: %s\n", file);
+
+	/* dump live tree if it's a directory */
+	if (S_ISDIR(sb.st_mode)) {
+		dump_live(file, debug);
+		return 0;
+	}
+
 	buf = utilfdt_read_len(file, &len);
 	if (!buf)
 		die("could not read: %s\n", file);
--- a/flattree.c	2015-12-10 14:50:21.538561922 -0500
+++ b/flattree.c	2015-12-01 17:11:59.250242026 -0500
@@ -255,6 +255,204 @@ 
 	return i;
 }
 
+static void emit_local_fixups(struct node *tree, struct emitter *emit,
+		void *etarget, struct data *strbuf, struct 
version_info *vi,
+		struct node *node)
+{
+	struct fixup_entry *fe, *fen;
+	struct node *child;
+	int nameoff, count;
+	cell_t *buf;
+	struct data d;
+
+	if (node->emit_local_fixup_node) {
+
+		/* emit the external fixups (do not emit /) */
+		if (node != tree) {
+			emit->beginnode(etarget, NULL);
+			emit->string(etarget, node->name, 0);
+			emit->align(etarget, sizeof(cell_t));
+		}
+
+		for_each_local_fixup_entry(tree, fe) {
+			if (fe->node != node || fe-
>local_fixup_generated)
+				continue;
+
+			/* count the number of fixup entries */
+			count = 0;
+			for_each_local_fixup_entry(tree, fen) {
+				if (fen->prop != fe->prop)
+					continue;
+				fen->local_fixup_generated = 
true;
+				count++;
+			}
+
+			/* allocate buffer */
+			buf = xmalloc(count * sizeof(cell_t));
+
+			/* collect all the offsets in buffer */
+			count = 0;
+			for_each_local_fixup_entry(tree, fen) {
+				if (fen->prop != fe->prop)
+					continue;
+				fen->local_fixup_generated = 
true;
+				buf[count++] = cpu_to_fdt32(fen-
>offset);
+			}
+			d = empty_data;
+			d.len = count * sizeof(cell_t);
+			d.val = (char *)buf;
+
+			nameoff = stringtable_insert(strbuf, fe-
>prop->name);
+			emit->property(etarget, fe->prop->labels);
+			emit->cell(etarget, count * 
sizeof(cell_t));
+			emit->cell(etarget, nameoff);
+
+			if ((vi->flags & FTF_VARALIGN) &&
+					(count * 
sizeof(cell_t)) >= 8)
+				emit->align(etarget, 8);
+
+			emit->data(etarget, d);
+			emit->align(etarget, sizeof(cell_t));
+
+			free(buf);
+		}
+	}
+
+	for_each_child(node, child)
+		emit_local_fixups(tree, emit, etarget, strbuf, vi, 
child);
+
+	if (node->emit_local_fixup_node && node != tree)
+		emit->endnode(etarget, tree->labels);
+}
+
+static void emit_symbols_node(struct node *tree, struct emitter *emit,
+			      void *etarget, struct data *strbuf,
+			      struct version_info *vi)
+{
+	struct symbol *sym;
+	int nameoff, vallen;
+
+	/* do nothing if no symbols */
+	if (!tree->symbols)
+		return;
+
+	emit->beginnode(etarget, NULL);
+	emit->string(etarget, "__symbols__", 0);
+	emit->align(etarget, sizeof(cell_t));
+
+	for_each_symbol(tree, sym) {
+
+		vallen = strlen(sym->node->fullpath);
+
+		nameoff = stringtable_insert(strbuf, sym->label-
>label);
+
+		emit->property(etarget, NULL);
+		emit->cell(etarget, vallen + 1);
+		emit->cell(etarget, nameoff);
+
+		if ((vi->flags & FTF_VARALIGN) && vallen >= 8)
+			emit->align(etarget, 8);
+
+		emit->string(etarget, sym->node->fullpath,
+				strlen(sym->node->fullpath));
+		emit->align(etarget, sizeof(cell_t));
+	}
+
+	emit->endnode(etarget, NULL);
+}
+
+static void emit_local_fixups_node(struct node *tree, struct emitter *emit,
+				   void *etarget, struct data 
*strbuf,
+				   struct version_info *vi)
+{
+	struct fixup_entry *fe;
+	struct node *node;
+
+	/* do nothing if no local fixups */
+	if (!tree->local_fixups)
+		return;
+
+	/* mark all nodes that need a local fixup generated (and parents) 
*/
+	for_each_local_fixup_entry(tree, fe) {
+		node = fe->node;
+		while (node != NULL && !node->emit_local_fixup_node) {
+			node->emit_local_fixup_node = true;
+			node = node->parent;
+		}
+	}
+
+	/* emit the local fixups node now */
+	emit->beginnode(etarget, NULL);
+	emit->string(etarget, "__local_fixups__", 0);
+	emit->align(etarget, sizeof(cell_t));
+
+	emit_local_fixups(tree, emit, etarget, strbuf, vi, tree);
+
+	emit->endnode(etarget, tree->labels);
+}
+
+static void emit_fixups_node(struct node *tree, struct emitter *emit,
+			     void *etarget, struct data *strbuf,
+			     struct version_info *vi)
+{
+	struct fixup *f;
+	struct fixup_entry *fe;
+	char *name, *s;
+	const char *fullpath;
+	int namesz, nameoff, vallen;
+
+	/* do nothing if no fixups */
+	if (!tree->fixups)
+		return;
+
+	/* emit the external fixups */
+	emit->beginnode(etarget, NULL);
+	emit->string(etarget, "__fixups__", 0);
+	emit->align(etarget, sizeof(cell_t));
+
+	for_each_fixup(tree, f) {
+
+		namesz = 0;
+		for_each_fixup_entry(f, fe) {
+			fullpath = fe->node->fullpath;
+			if (fullpath[0] == '\0')
+				fullpath = "/";
+			namesz += strlen(fullpath) + 1;
+			namesz += strlen(fe->prop->name) + 1;
+			namesz += 32;	/* space for 
:<number> + '\0' */
+		}
+
+		name = xmalloc(namesz);
+
+		s = name;
+		for_each_fixup_entry(f, fe) {
+			fullpath = fe->node->fullpath;
+			if (fullpath[0] == '\0')
+				fullpath = "/";
+			snprintf(s, name + namesz - s, "%s:%s:%d", 
fullpath,
+					fe->prop->name, fe-
>offset);
+			s += strlen(s) + 1;
+		}
+
+		nameoff = stringtable_insert(strbuf, f->ref);
+		vallen = s - name - 1;
+
+		emit->property(etarget, NULL);
+		emit->cell(etarget, vallen + 1);
+		emit->cell(etarget, nameoff);
+
+		if ((vi->flags & FTF_VARALIGN) && vallen >= 8)
+			emit->align(etarget, 8);
+
+		emit->string(etarget, name, vallen);
+		emit->align(etarget, sizeof(cell_t));
+
+		free(name);
+	}
+
+	emit->endnode(etarget, tree->labels);
+}
+
 static void flatten_tree(struct node *tree, struct emitter *emit,
 			 void *etarget, struct data *strbuf,
 			 struct version_info *vi)
@@ -310,6 +508,10 @@ 
 		flatten_tree(child, emit, etarget, strbuf, vi);
 	}
 
+	emit_symbols_node(tree, emit, etarget, strbuf, vi);
+	emit_local_fixups_node(tree, emit, etarget, strbuf, vi);
+	emit_fixups_node(tree, emit, etarget, strbuf, vi);
+
 	emit->endnode(etarget, tree->labels);
 }

--- a/libfdt/fdt_ro.c	2014-11-11 22:29:16.000000000 -0500
+++ b/libfdt/fdt_ro.c	2015-12-01 17:11:42.213219672 -0500
@@ -154,9 +154,9 @@ 
 	return fdt_subnode_offset_namelen(fdt, parentoffset, name, 
strlen(name));
 }
 
-int fdt_path_offset(const void *fdt, const char *path)
+int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen)
 {
-	const char *end = path + strlen(path);
+	const char *end = path + namelen;
 	const char *p = path;
 	int offset = 0;
 
@@ -164,7 +164,7 @@ 
 
 	/* see if we have an alias */
 	if (*path != '/') {
-		const char *q = strchr(path, '/');
+		const char *q = memchr(path, '/', end - p);
 
 		if (!q)
 			q = end;
@@ -177,14 +177,15 @@ 
 		p = q;
 	}
 
-	while (*p) {
+	while (p < end) {
 		const char *q;
 
-		while (*p == '/')
+		while (*p == '/') {
 			p++;
-		if (! *p)
-			return offset;
-		q = strchr(p, '/');
+			if (p == end)
+				return offset;
+		}
+		q = memchr(p, '/', end - p);
 		if (! q)
 			q = end;
 
@@ -198,6 +199,11 @@ 
 	return offset;
 }
 
+int fdt_path_offset(const void *fdt, const char *path)
+{
+	return fdt_path_offset_namelen(fdt, path, strlen(path));
+}
+
 const char *fdt_get_name(const void *fdt, int nodeoffset, int *len)
 {
 	const struct fdt_node_header *nh = _fdt_offset_ptr(fdt, 
nodeoffset);

--- a/libfdt/libfdt.h	2014-11-11 22:29:16.000000000 -0500
+++ b/libfdt/libfdt.h	2015-12-01 17:11:42.213219672 -0500
@@ -323,6 +323,17 @@ 
 int fdt_subnode_offset(const void *fdt, int parentoffset, const char 
*name);
 
 /**
+ * fdt_path_offset_namelen - find a tree node by its full path
+ * @fdt: pointer to the device tree blob
+ * @path: full path of the node to locate
+ * @namelen: number of characters of path to consider
+ *
+ * Identical to fdt_path_offset(), but only consider the first namelen
+ * characters of path as the path name.
+ */
+int fdt_path_offset_namelen(const void *fdt, const char *path, int 
namelen);
+
+/**
  * fdt_path_offset - find a tree node by its full path
  * @fdt: pointer to the device tree blob
  * @path: full path of the node to locate

--- a/libfdt/version.lds	2014-11-11 22:29:16.000000000 -0500
+++ b/libfdt/version.lds	2015-12-01 17:11:42.213219672 -0500
@@ -8,6 +8,7 @@ 
 		fdt_get_mem_rsv;
 		fdt_subnode_offset_namelen;
 		fdt_subnode_offset;
+		fdt_path_offset_namelen;
 		fdt_path_offset;
 		fdt_get_name;
 		fdt_get_property_namelen;
@@ -54,6 +55,8 @@ 
 		fdt_get_property_by_offset;
 		fdt_getprop_by_offset;
 		fdt_next_property_offset;
+		fdt_first_subnode;
+		fdt_next_subnode;
 
 	local:
 		*;


--- a/tests/run_tests.sh	2014-11-11 22:29:16.000000000 -0500
+++ b/tests/run_tests.sh	2015-12-01 17:11:42.213219672 -0500
@@ -42,20 +42,20 @@ 
 
 shorten_echo () {
     limit=32
-    echo -n "$1"
+    printf "$1"
     shift
     for x; do
 	if [ ${#x} -le $limit ]; then
-	    echo -n " $x"
+	    printf " $x"
 	else
 	    short=$(echo "$x" | head -c$limit)
-	    echo -n " \"$short\"...<${#x} bytes>"
+	    printf " \"$short\"...<${#x} bytes>"
 	fi
     done
 }
 
 run_test () {
-    echo -n "$@:	"
+    printf "$*:	"
     if [ -n "$VALGRIND" -a -f $1.supp ]; then
 	VGSUPP="--suppressions=$1.supp"
     fi
@@ -63,7 +63,7 @@ 
 }
 
 run_sh_test () {
-    echo -n "$@:	"
+    printf "$*:	"
     base_run_test sh "$@"
 }
 
@@ -106,12 +106,12 @@ 
 
 run_wrap_error_test () {
     shorten_echo "$@"
-    echo -n " {!= 0}:	"
+    printf " {!= 0}:	"
     base_run_test wrap_error "$@"
 }
 
 run_dtc_test () {
-    echo -n "dtc $@:	"
+    printf "dtc $*:	"
     base_run_test wrap_test $VALGRIND $DTC "$@"
 }
 
@@ -126,7 +126,7 @@ 
 run_fdtget_test () {
     expect="$1"
     shift
-    echo -n "fdtget-runtest.sh "$expect" $@:	"
+    printf "fdtget-runtest.sh %s $*:	" "$(echo $expect)"
     base_run_test sh fdtget-runtest.sh "$expect" "$@"
 }
 
@@ -134,14 +134,14 @@ 
     expect="$1"
     shift
     shorten_echo fdtput-runtest.sh "$expect" "$@"
-    echo -n ":	"
+    printf ":	"
     base_run_test sh fdtput-runtest.sh "$expect" "$@"
 }
 
 run_fdtdump_test() {
     file="$1"
     shorten_echo fdtdump-runtest.sh "$file"
-    echo -n ":	"
+    printf ":	"
     base_run_test sh fdtdump-runtest.sh "$file"
 }
 
@@ -279,6 +279,8 @@ 
     run_dtc_test -I dts -O dtb -o embedded_nul.test.dtb embedded_nul.dts
     run_dtc_test -I dts -O dtb -o embedded_nul_equiv.test.dtb 
embedded_nul_equiv.dts
     run_test dtbs_equal_ordered embedded_nul.test.dtb 
embedded_nul_equiv.test.dtb
+
+    run_dtc_test -I dts -O dtb bad-size-cells.dts
 }