Home | History | Annotate | Download | only in Documentation
      1 Device Tree Dynamic Object format internals
      2 -------------------------------------------
      3 
      4 The Device Tree for most platforms is a static representation of
      5 the hardware capabilities. This is insufficient for platforms
      6 that need to dynamically insert Device Tree fragments into the
      7 live tree.
      8 
      9 This document explains the the Device Tree object format and
     10 modifications made to the Device Tree compiler, which make it possible.
     11 
     12 1. Simplified Problem Definition
     13 --------------------------------
     14 
     15 Assume we have a platform which boots using following simplified Device Tree.
     16 
     17 ---- foo.dts -----------------------------------------------------------------
     18 	/* FOO platform */
     19 	/ {
     20 		compatible = "corp,foo";
     21 
     22 		/* shared resources */
     23 		res: res {
     24 		};
     25 
     26 		/* On chip peripherals */
     27 		ocp: ocp {
     28 			/* peripherals that are always instantiated */
     29 			peripheral1 { ... };
     30 		};
     31 	};
     32 ---- foo.dts -----------------------------------------------------------------
     33 
     34 We have a number of peripherals that after probing (using some undefined method)
     35 should result in different Device Tree configuration.
     36 
     37 We cannot boot with this static tree because due to the configuration of the
     38 foo platform there exist multiple conficting peripherals DT fragments.
     39 
     40 So for the bar peripheral we would have this:
     41 
     42 ---- foo+bar.dts -------------------------------------------------------------
     43 	/* FOO platform + bar peripheral */
     44 	/ {
     45 		compatible = "corp,foo";
     46 
     47 		/* shared resources */
     48 		res: res {
     49 		};
     50 
     51 		/* On chip peripherals */
     52 		ocp: ocp {
     53 			/* peripherals that are always instantiated */
     54 			peripheral1 { ... };
     55 
     56 			/* bar peripheral */
     57 			bar {
     58 				compatible = "corp,bar";
     59 				... /* various properties and child nodes */
     60 			};
     61 		};
     62 	};
     63 ---- foo+bar.dts -------------------------------------------------------------
     64 
     65 While for the baz peripheral we would have this:
     66 
     67 ---- foo+baz.dts -------------------------------------------------------------
     68 	/* FOO platform + baz peripheral */
     69 	/ {
     70 		compatible = "corp,foo";
     71 
     72 		/* shared resources */
     73 		res: res {
     74 			/* baz resources */
     75 			baz_res: res_baz { ... };
     76 		};
     77 
     78 		/* On chip peripherals */
     79 		ocp: ocp {
     80 			/* peripherals that are always instantiated */
     81 			peripheral1 { ... };
     82 
     83 			/* baz peripheral */
     84 			baz {
     85 				compatible = "corp,baz";
     86 				/* reference to another point in the tree */
     87 				ref-to-res = <&baz_res>;
     88 				... /* various properties and child nodes */
     89 			};
     90 		};
     91 	};
     92 ---- foo+baz.dts -------------------------------------------------------------
     93 
     94 We note that the baz case is more complicated, since the baz peripheral needs to
     95 reference another node in the DT tree.
     96 
     97 2. Device Tree Object Format Requirements
     98 -----------------------------------------
     99 
    100 Since the Device Tree is used for booting a number of very different hardware
    101 platforms it is imperative that we tread very carefully.
    102 
    103 2.a) No changes to the Device Tree binary format for the base tree. We cannot
    104 modify the tree format at all and all the information we require should be
    105 encoded using Device Tree itself. We can add nodes that can be safely ignored
    106 by both bootloaders and the kernel. The plugin dtbs are optionally tagged
    107 with a different magic number in the header but otherwise they're simple
    108 blobs.
    109 
    110 2.b) Changes to the DTS source format should be absolutely minimal, and should
    111 only be needed for the DT fragment definitions, and not the base boot DT.
    112 
    113 2.c) An explicit option should be used to instruct DTC to generate the required
    114 information needed for object resolution. Platforms that don't use the
    115 dynamic object format can safely ignore it.
    116 
    117 2.d) Finally, DT syntax changes should be kept to a minimum. It should be
    118 possible to express everything using the existing DT syntax.
    119 
    120 3. Implementation
    121 -----------------
    122 
    123 The basic unit of addressing in Device Tree is the phandle. Turns out it's
    124 relatively simple to extend the way phandles are generated and referenced
    125 so that it's possible to dynamically convert symbolic references (labels)
    126 to phandle values. This is a valid assumption as long as the author uses
    127 reference syntax and does not assign phandle values manually (which might
    128 be a problem with decompiled source files).
    129 
    130 We can roughly divide the operation into two steps.
    131 
    132 3.a) Compilation of the base board DTS file using the '-@' option
    133 generates a valid DT blob with an added __symbols__ node at the root node,
    134 containing a list of all nodes that are marked with a label.
    135 
    136 Using the foo.dts file above the following node will be generated;
    137 
    138 $ dtc -@ -O dtb -o foo.dtb -b 0 foo.dts
    139 $ fdtdump foo.dtb
    140 ...
    141 / {
    142 	...
    143 	res {
    144 		...
    145 		phandle = <0x00000001>;
    146 		...
    147 	};
    148 	ocp {
    149 		...
    150 		phandle = <0x00000002>;
    151 		...
    152 	};
    153 	__symbols__ {
    154 		res="/res";
    155 		ocp="/ocp";
    156 	};
    157 };
    158 
    159 Notice that all the nodes that had a label have been recorded, and that
    160 phandles have been generated for them.
    161 
    162 This blob can be used to boot the board normally, the __symbols__ node will
    163 be safely ignored both by the bootloader and the kernel (the only loss will
    164 be a few bytes of memory and disk space).
    165 
    166 We generate a __symbols__ node to record nodes that had labels in the base
    167 tree (or subsequent loaded overlays) so that they can be matched up with
    168 references made to them in Device Tree objects.
    169 
    170 3.b) The Device Tree fragments must be compiled with the same option but they
    171 must also have a tag (/plugin/) that allows undefined references to nodes
    172 that are not present at compilation time to be recorded so that the runtime
    173 loader can fix them.
    174 
    175 So the bar peripheral's DTS format would be of the form:
    176 
    177 /dts-v1/;
    178 /plugin/;	/* allow undefined references and record them */
    179 / {
    180 	....	/* various properties for loader use; i.e. part id etc. */
    181 	fragment@0 {
    182 		target = <&ocp>;
    183 		__overlay__ {
    184 			/* bar peripheral */
    185 			bar {
    186 				compatible = "corp,bar";
    187 				... /* various properties and child nodes */
    188 			}
    189 		};
    190 	};
    191 };
    192 
    193 Note that there's a target property that specifies the location where the
    194 contents of the overlay node will be placed, and it references the node
    195 in the foo.dts file.
    196 
    197 $ dtc -@ -O dtb -o bar.dtbo -b 0 bar.dts
    198 $ fdtdump bar.dtbo
    199 ...
    200 / {
    201 	... /* properties */
    202 	fragment@0 {
    203 		target = <0xffffffff>;
    204 		__overlay__ {
    205 			bar {
    206 				compatible = "corp,bar";
    207 				... /* various properties and child nodes */
    208 			}
    209 		};
    210 	};
    211 	__fixups__ {
    212 	    ocp = "/fragment@0:target:0";
    213 	};
    214 };
    215 
    216 No __symbols__ node has been generated (no label in bar.dts).
    217 Note that the target's ocp label is undefined, so the phandle
    218 value is filled with the illegal value '0xffffffff', while a __fixups__
    219 node has been generated, which marks the location in the tree where
    220 the label lookup should store the runtime phandle value of the ocp node.
    221 
    222 The format of the __fixups__ node entry is
    223 
    224   <label> = "<local-full-path>:<property-name>:<offset>" 
    225 	    [, "<local-full-path>:<property-name>:<offset>"...];
    226 
    227   <label> 		Is the label we're referring
    228   <local-full-path>	Is the full path of the node the reference is
    229   <property-name>	Is the name of the property containing the
    230 			reference
    231   <offset>		The offset (in bytes) of where the property's
    232 			phandle value is located.
    233 
    234 Doing the same with the baz peripheral's DTS format is a little bit more
    235 involved, since baz contains references to local labels which require
    236 local fixups.
    237 
    238 /dts-v1/;
    239 /plugin/;	/* allow undefined label references and record them */
    240 / {
    241 	....	/* various properties for loader use; i.e. part id etc. */
    242 	fragment@0 {
    243 		target = <&res>;
    244 		__overlay__ {
    245 			/* baz resources */
    246 			baz_res: res_baz { ... };
    247 		};
    248 	};
    249 	fragment@1 {
    250 		target = <&ocp>;
    251 		__overlay__ {
    252 			/* baz peripheral */
    253 			baz {
    254 				compatible = "corp,baz";
    255 				/* reference to another point in the tree */
    256 				ref-to-res = <&baz_res>;
    257 				... /* various properties and child nodes */
    258 			}
    259 		};
    260 	};
    261 };
    262 
    263 Note that &bar_res reference.
    264 
    265 $ dtc -@ -O dtb -o baz.dtbo -b 0 baz.dts
    266 $ fdtdump baz.dtbo
    267 ...
    268 / {
    269 	... /* properties */
    270 	fragment@0 {
    271 		target = <0xffffffff>;
    272 		__overlay__ {
    273 			res_baz {
    274 				....
    275 				phandle = <0x00000001>;
    276 			};
    277 		};
    278 	};
    279 	fragment@1 {
    280 		target = <0xffffffff>;
    281 		__overlay__ {
    282 			baz {
    283 				compatible = "corp,baz";
    284 				... /* various properties and child nodes */
    285 				ref-to-res = <0x00000001>;
    286 			}
    287 		};
    288 	};
    289 	__fixups__ {
    290 		res = "/fragment@0:target:0";
    291 		ocp = "/fragment@1:target:0";
    292 	};
    293 	__local_fixups__ {
    294 		fragment@1 {
    295 			__overlay__ {
    296 				baz {
    297 					ref-to-res = <0>;
    298 				};
    299 			};
    300 		};
    301 	};
    302 };
    303 
    304 This is similar to the bar case, but the reference of a local label by the
    305 baz node generates a __local_fixups__ entry that records the place that the
    306 local reference is being made. No matter how phandles are allocated from dtc
    307 the run time loader must apply an offset to each phandle in every dynamic
    308 DT object loaded. The __local_fixups__ node records the offset relative to the
    309 start of every local reference within that property so that the loader can apply
    310 the offset.
    311 
    312 There is an alternative syntax to the expanded form for overlays with phandle
    313 targets which makes the format similar to the one using in .dtsi include files.
    314 
    315 So for the &ocp target example above one can simply write:
    316 
    317 /dts-v1/;
    318 /plugin/;
    319 &ocp {
    320 	/* bar peripheral */
    321 	bar {
    322 		compatible = "corp,bar";
    323 		... /* various properties and child nodes */
    324 	}
    325 };
    326 
    327 The resulting dtb object is identical.
    328