Home | History | Annotate | Line # | Download | only in dist
fdt_overlay.c revision 1.1.1.1.8.2
      1 /*	$NetBSD: fdt_overlay.c,v 1.1.1.1.8.2 2017/12/03 11:38:02 jdolecek Exp $	*/
      2 
      3 #include "libfdt_env.h"
      4 
      5 #include <fdt.h>
      6 #include <libfdt.h>
      7 
      8 #include "libfdt_internal.h"
      9 
     10 /**
     11  * overlay_get_target_phandle - retrieves the target phandle of a fragment
     12  * @fdto: pointer to the device tree overlay blob
     13  * @fragment: node offset of the fragment in the overlay
     14  *
     15  * overlay_get_target_phandle() retrieves the target phandle of an
     16  * overlay fragment when that fragment uses a phandle (target
     17  * property) instead of a path (target-path property).
     18  *
     19  * returns:
     20  *      the phandle pointed by the target property
     21  *      0, if the phandle was not found
     22  *	-1, if the phandle was malformed
     23  */
     24 static uint32_t overlay_get_target_phandle(const void *fdto, int fragment)
     25 {
     26 	const fdt32_t *val;
     27 	int len;
     28 
     29 	val = fdt_getprop(fdto, fragment, "target", &len);
     30 	if (!val)
     31 		return 0;
     32 
     33 	if ((len != sizeof(*val)) || (fdt32_to_cpu(*val) == (uint32_t)-1))
     34 		return (uint32_t)-1;
     35 
     36 	return fdt32_to_cpu(*val);
     37 }
     38 
     39 /**
     40  * overlay_get_target - retrieves the offset of a fragment's target
     41  * @fdt: Base device tree blob
     42  * @fdto: Device tree overlay blob
     43  * @fragment: node offset of the fragment in the overlay
     44  *
     45  * overlay_get_target() retrieves the target offset in the base
     46  * device tree of a fragment, no matter how the actual targetting is
     47  * done (through a phandle or a path)
     48  *
     49  * returns:
     50  *      the targetted node offset in the base device tree
     51  *      Negative error code on error
     52  */
     53 static int overlay_get_target(const void *fdt, const void *fdto,
     54 			      int fragment)
     55 {
     56 	uint32_t phandle;
     57 	const char *path;
     58 	int path_len;
     59 
     60 	/* Try first to do a phandle based lookup */
     61 	phandle = overlay_get_target_phandle(fdto, fragment);
     62 	if (phandle == (uint32_t)-1)
     63 		return -FDT_ERR_BADPHANDLE;
     64 
     65 	if (phandle)
     66 		return fdt_node_offset_by_phandle(fdt, phandle);
     67 
     68 	/* And then a path based lookup */
     69 	path = fdt_getprop(fdto, fragment, "target-path", &path_len);
     70 	if (!path) {
     71 		/*
     72 		 * If we haven't found either a target or a
     73 		 * target-path property in a node that contains a
     74 		 * __overlay__ subnode (we wouldn't be called
     75 		 * otherwise), consider it a improperly written
     76 		 * overlay
     77 		 */
     78 		if (path_len == -FDT_ERR_NOTFOUND)
     79 			return -FDT_ERR_BADOVERLAY;
     80 
     81 		return path_len;
     82 	}
     83 
     84 	return fdt_path_offset(fdt, path);
     85 }
     86 
     87 /**
     88  * overlay_phandle_add_offset - Increases a phandle by an offset
     89  * @fdt: Base device tree blob
     90  * @node: Device tree overlay blob
     91  * @name: Name of the property to modify (phandle or linux,phandle)
     92  * @delta: offset to apply
     93  *
     94  * overlay_phandle_add_offset() increments a node phandle by a given
     95  * offset.
     96  *
     97  * returns:
     98  *      0 on success.
     99  *      Negative error code on error
    100  */
    101 static int overlay_phandle_add_offset(void *fdt, int node,
    102 				      const char *name, uint32_t delta)
    103 {
    104 	const fdt32_t *val;
    105 	uint32_t adj_val;
    106 	int len;
    107 
    108 	val = fdt_getprop(fdt, node, name, &len);
    109 	if (!val)
    110 		return len;
    111 
    112 	if (len != sizeof(*val))
    113 		return -FDT_ERR_BADPHANDLE;
    114 
    115 	adj_val = fdt32_to_cpu(*val);
    116 	if ((adj_val + delta) < adj_val)
    117 		return -FDT_ERR_NOPHANDLES;
    118 
    119 	adj_val += delta;
    120 	if (adj_val == (uint32_t)-1)
    121 		return -FDT_ERR_NOPHANDLES;
    122 
    123 	return fdt_setprop_inplace_u32(fdt, node, name, adj_val);
    124 }
    125 
    126 /**
    127  * overlay_adjust_node_phandles - Offsets the phandles of a node
    128  * @fdto: Device tree overlay blob
    129  * @node: Offset of the node we want to adjust
    130  * @delta: Offset to shift the phandles of
    131  *
    132  * overlay_adjust_node_phandles() adds a constant to all the phandles
    133  * of a given node. This is mainly use as part of the overlay
    134  * application process, when we want to update all the overlay
    135  * phandles to not conflict with the overlays of the base device tree.
    136  *
    137  * returns:
    138  *      0 on success
    139  *      Negative error code on failure
    140  */
    141 static int overlay_adjust_node_phandles(void *fdto, int node,
    142 					uint32_t delta)
    143 {
    144 	int child;
    145 	int ret;
    146 
    147 	ret = overlay_phandle_add_offset(fdto, node, "phandle", delta);
    148 	if (ret && ret != -FDT_ERR_NOTFOUND)
    149 		return ret;
    150 
    151 	ret = overlay_phandle_add_offset(fdto, node, "linux,phandle", delta);
    152 	if (ret && ret != -FDT_ERR_NOTFOUND)
    153 		return ret;
    154 
    155 	fdt_for_each_subnode(child, fdto, node) {
    156 		ret = overlay_adjust_node_phandles(fdto, child, delta);
    157 		if (ret)
    158 			return ret;
    159 	}
    160 
    161 	return 0;
    162 }
    163 
    164 /**
    165  * overlay_adjust_local_phandles - Adjust the phandles of a whole overlay
    166  * @fdto: Device tree overlay blob
    167  * @delta: Offset to shift the phandles of
    168  *
    169  * overlay_adjust_local_phandles() adds a constant to all the
    170  * phandles of an overlay. This is mainly use as part of the overlay
    171  * application process, when we want to update all the overlay
    172  * phandles to not conflict with the overlays of the base device tree.
    173  *
    174  * returns:
    175  *      0 on success
    176  *      Negative error code on failure
    177  */
    178 static int overlay_adjust_local_phandles(void *fdto, uint32_t delta)
    179 {
    180 	/*
    181 	 * Start adjusting the phandles from the overlay root
    182 	 */
    183 	return overlay_adjust_node_phandles(fdto, 0, delta);
    184 }
    185 
    186 /**
    187  * overlay_update_local_node_references - Adjust the overlay references
    188  * @fdto: Device tree overlay blob
    189  * @tree_node: Node offset of the node to operate on
    190  * @fixup_node: Node offset of the matching local fixups node
    191  * @delta: Offset to shift the phandles of
    192  *
    193  * overlay_update_local_nodes_references() update the phandles
    194  * pointing to a node within the device tree overlay by adding a
    195  * constant delta.
    196  *
    197  * This is mainly used as part of a device tree application process,
    198  * where you want the device tree overlays phandles to not conflict
    199  * with the ones from the base device tree before merging them.
    200  *
    201  * returns:
    202  *      0 on success
    203  *      Negative error code on failure
    204  */
    205 static int overlay_update_local_node_references(void *fdto,
    206 						int tree_node,
    207 						int fixup_node,
    208 						uint32_t delta)
    209 {
    210 	int fixup_prop;
    211 	int fixup_child;
    212 	int ret;
    213 
    214 	fdt_for_each_property_offset(fixup_prop, fdto, fixup_node) {
    215 		const fdt32_t *fixup_val;
    216 		const char *tree_val;
    217 		const char *name;
    218 		int fixup_len;
    219 		int tree_len;
    220 		int i;
    221 
    222 		fixup_val = fdt_getprop_by_offset(fdto, fixup_prop,
    223 						  &name, &fixup_len);
    224 		if (!fixup_val)
    225 			return fixup_len;
    226 
    227 		if (fixup_len % sizeof(uint32_t))
    228 			return -FDT_ERR_BADOVERLAY;
    229 
    230 		tree_val = fdt_getprop(fdto, tree_node, name, &tree_len);
    231 		if (!tree_val) {
    232 			if (tree_len == -FDT_ERR_NOTFOUND)
    233 				return -FDT_ERR_BADOVERLAY;
    234 
    235 			return tree_len;
    236 		}
    237 
    238 		for (i = 0; i < (fixup_len / sizeof(uint32_t)); i++) {
    239 			fdt32_t adj_val;
    240 			uint32_t poffset;
    241 
    242 			poffset = fdt32_to_cpu(fixup_val[i]);
    243 
    244 			/*
    245 			 * phandles to fixup can be unaligned.
    246 			 *
    247 			 * Use a memcpy for the architectures that do
    248 			 * not support unaligned accesses.
    249 			 */
    250 			memcpy(&adj_val, tree_val + poffset, sizeof(adj_val));
    251 
    252 			adj_val = cpu_to_fdt32(fdt32_to_cpu(adj_val) + delta);
    253 
    254 			ret = fdt_setprop_inplace_namelen_partial(fdto,
    255 								  tree_node,
    256 								  name,
    257 								  strlen(name),
    258 								  poffset,
    259 								  &adj_val,
    260 								  sizeof(adj_val));
    261 			if (ret == -FDT_ERR_NOSPACE)
    262 				return -FDT_ERR_BADOVERLAY;
    263 
    264 			if (ret)
    265 				return ret;
    266 		}
    267 	}
    268 
    269 	fdt_for_each_subnode(fixup_child, fdto, fixup_node) {
    270 		const char *fixup_child_name = fdt_get_name(fdto, fixup_child,
    271 							    NULL);
    272 		int tree_child;
    273 
    274 		tree_child = fdt_subnode_offset(fdto, tree_node,
    275 						fixup_child_name);
    276 		if (tree_child == -FDT_ERR_NOTFOUND)
    277 			return -FDT_ERR_BADOVERLAY;
    278 		if (tree_child < 0)
    279 			return tree_child;
    280 
    281 		ret = overlay_update_local_node_references(fdto,
    282 							   tree_child,
    283 							   fixup_child,
    284 							   delta);
    285 		if (ret)
    286 			return ret;
    287 	}
    288 
    289 	return 0;
    290 }
    291 
    292 /**
    293  * overlay_update_local_references - Adjust the overlay references
    294  * @fdto: Device tree overlay blob
    295  * @delta: Offset to shift the phandles of
    296  *
    297  * overlay_update_local_references() update all the phandles pointing
    298  * to a node within the device tree overlay by adding a constant
    299  * delta to not conflict with the base overlay.
    300  *
    301  * This is mainly used as part of a device tree application process,
    302  * where you want the device tree overlays phandles to not conflict
    303  * with the ones from the base device tree before merging them.
    304  *
    305  * returns:
    306  *      0 on success
    307  *      Negative error code on failure
    308  */
    309 static int overlay_update_local_references(void *fdto, uint32_t delta)
    310 {
    311 	int fixups;
    312 
    313 	fixups = fdt_path_offset(fdto, "/__local_fixups__");
    314 	if (fixups < 0) {
    315 		/* There's no local phandles to adjust, bail out */
    316 		if (fixups == -FDT_ERR_NOTFOUND)
    317 			return 0;
    318 
    319 		return fixups;
    320 	}
    321 
    322 	/*
    323 	 * Update our local references from the root of the tree
    324 	 */
    325 	return overlay_update_local_node_references(fdto, 0, fixups,
    326 						    delta);
    327 }
    328 
    329 /**
    330  * overlay_fixup_one_phandle - Set an overlay phandle to the base one
    331  * @fdt: Base Device Tree blob
    332  * @fdto: Device tree overlay blob
    333  * @symbols_off: Node offset of the symbols node in the base device tree
    334  * @path: Path to a node holding a phandle in the overlay
    335  * @path_len: number of path characters to consider
    336  * @name: Name of the property holding the phandle reference in the overlay
    337  * @name_len: number of name characters to consider
    338  * @poffset: Offset within the overlay property where the phandle is stored
    339  * @label: Label of the node referenced by the phandle
    340  *
    341  * overlay_fixup_one_phandle() resolves an overlay phandle pointing to
    342  * a node in the base device tree.
    343  *
    344  * This is part of the device tree overlay application process, when
    345  * you want all the phandles in the overlay to point to the actual
    346  * base dt nodes.
    347  *
    348  * returns:
    349  *      0 on success
    350  *      Negative error code on failure
    351  */
    352 static int overlay_fixup_one_phandle(void *fdt, void *fdto,
    353 				     int symbols_off,
    354 				     const char *path, uint32_t path_len,
    355 				     const char *name, uint32_t name_len,
    356 				     int poffset, const char *label)
    357 {
    358 	const char *symbol_path;
    359 	uint32_t phandle;
    360 	fdt32_t phandle_prop;
    361 	int symbol_off, fixup_off;
    362 	int prop_len;
    363 
    364 	if (symbols_off < 0)
    365 		return symbols_off;
    366 
    367 	symbol_path = fdt_getprop(fdt, symbols_off, label,
    368 				  &prop_len);
    369 	if (!symbol_path)
    370 		return prop_len;
    371 
    372 	symbol_off = fdt_path_offset(fdt, symbol_path);
    373 	if (symbol_off < 0)
    374 		return symbol_off;
    375 
    376 	phandle = fdt_get_phandle(fdt, symbol_off);
    377 	if (!phandle)
    378 		return -FDT_ERR_NOTFOUND;
    379 
    380 	fixup_off = fdt_path_offset_namelen(fdto, path, path_len);
    381 	if (fixup_off == -FDT_ERR_NOTFOUND)
    382 		return -FDT_ERR_BADOVERLAY;
    383 	if (fixup_off < 0)
    384 		return fixup_off;
    385 
    386 	phandle_prop = cpu_to_fdt32(phandle);
    387 	return fdt_setprop_inplace_namelen_partial(fdto, fixup_off,
    388 						   name, name_len, poffset,
    389 						   &phandle_prop,
    390 						   sizeof(phandle_prop));
    391 };
    392 
    393 /**
    394  * overlay_fixup_phandle - Set an overlay phandle to the base one
    395  * @fdt: Base Device Tree blob
    396  * @fdto: Device tree overlay blob
    397  * @symbols_off: Node offset of the symbols node in the base device tree
    398  * @property: Property offset in the overlay holding the list of fixups
    399  *
    400  * overlay_fixup_phandle() resolves all the overlay phandles pointed
    401  * to in a __fixups__ property, and updates them to match the phandles
    402  * in use in the base device tree.
    403  *
    404  * This is part of the device tree overlay application process, when
    405  * you want all the phandles in the overlay to point to the actual
    406  * base dt nodes.
    407  *
    408  * returns:
    409  *      0 on success
    410  *      Negative error code on failure
    411  */
    412 static int overlay_fixup_phandle(void *fdt, void *fdto, int symbols_off,
    413 				 int property)
    414 {
    415 	const char *value;
    416 	const char *label;
    417 	int len;
    418 
    419 	value = fdt_getprop_by_offset(fdto, property,
    420 				      &label, &len);
    421 	if (!value) {
    422 		if (len == -FDT_ERR_NOTFOUND)
    423 			return -FDT_ERR_INTERNAL;
    424 
    425 		return len;
    426 	}
    427 
    428 	do {
    429 		const char *path, *name, *fixup_end;
    430 		const char *fixup_str = value;
    431 		uint32_t path_len, name_len;
    432 		uint32_t fixup_len;
    433 		char *sep, *endptr;
    434 		int poffset, ret;
    435 
    436 		fixup_end = memchr(value, '\0', len);
    437 		if (!fixup_end)
    438 			return -FDT_ERR_BADOVERLAY;
    439 		fixup_len = fixup_end - fixup_str;
    440 
    441 		len -= fixup_len + 1;
    442 		value += fixup_len + 1;
    443 
    444 		path = fixup_str;
    445 		sep = memchr(fixup_str, ':', fixup_len);
    446 		if (!sep || *sep != ':')
    447 			return -FDT_ERR_BADOVERLAY;
    448 
    449 		path_len = sep - path;
    450 		if (path_len == (fixup_len - 1))
    451 			return -FDT_ERR_BADOVERLAY;
    452 
    453 		fixup_len -= path_len + 1;
    454 		name = sep + 1;
    455 		sep = memchr(name, ':', fixup_len);
    456 		if (!sep || *sep != ':')
    457 			return -FDT_ERR_BADOVERLAY;
    458 
    459 		name_len = sep - name;
    460 		if (!name_len)
    461 			return -FDT_ERR_BADOVERLAY;
    462 
    463 		poffset = strtoul(sep + 1, &endptr, 10);
    464 		if ((*endptr != '\0') || (endptr <= (sep + 1)))
    465 			return -FDT_ERR_BADOVERLAY;
    466 
    467 		ret = overlay_fixup_one_phandle(fdt, fdto, symbols_off,
    468 						path, path_len, name, name_len,
    469 						poffset, label);
    470 		if (ret)
    471 			return ret;
    472 	} while (len > 0);
    473 
    474 	return 0;
    475 }
    476 
    477 /**
    478  * overlay_fixup_phandles - Resolve the overlay phandles to the base
    479  *                          device tree
    480  * @fdt: Base Device Tree blob
    481  * @fdto: Device tree overlay blob
    482  *
    483  * overlay_fixup_phandles() resolves all the overlay phandles pointing
    484  * to nodes in the base device tree.
    485  *
    486  * This is one of the steps of the device tree overlay application
    487  * process, when you want all the phandles in the overlay to point to
    488  * the actual base dt nodes.
    489  *
    490  * returns:
    491  *      0 on success
    492  *      Negative error code on failure
    493  */
    494 static int overlay_fixup_phandles(void *fdt, void *fdto)
    495 {
    496 	int fixups_off, symbols_off;
    497 	int property;
    498 
    499 	/* We can have overlays without any fixups */
    500 	fixups_off = fdt_path_offset(fdto, "/__fixups__");
    501 	if (fixups_off == -FDT_ERR_NOTFOUND)
    502 		return 0; /* nothing to do */
    503 	if (fixups_off < 0)
    504 		return fixups_off;
    505 
    506 	/* And base DTs without symbols */
    507 	symbols_off = fdt_path_offset(fdt, "/__symbols__");
    508 	if ((symbols_off < 0 && (symbols_off != -FDT_ERR_NOTFOUND)))
    509 		return symbols_off;
    510 
    511 	fdt_for_each_property_offset(property, fdto, fixups_off) {
    512 		int ret;
    513 
    514 		ret = overlay_fixup_phandle(fdt, fdto, symbols_off, property);
    515 		if (ret)
    516 			return ret;
    517 	}
    518 
    519 	return 0;
    520 }
    521 
    522 /**
    523  * overlay_apply_node - Merges a node into the base device tree
    524  * @fdt: Base Device Tree blob
    525  * @target: Node offset in the base device tree to apply the fragment to
    526  * @fdto: Device tree overlay blob
    527  * @node: Node offset in the overlay holding the changes to merge
    528  *
    529  * overlay_apply_node() merges a node into a target base device tree
    530  * node pointed.
    531  *
    532  * This is part of the final step in the device tree overlay
    533  * application process, when all the phandles have been adjusted and
    534  * resolved and you just have to merge overlay into the base device
    535  * tree.
    536  *
    537  * returns:
    538  *      0 on success
    539  *      Negative error code on failure
    540  */
    541 static int overlay_apply_node(void *fdt, int target,
    542 			      void *fdto, int node)
    543 {
    544 	int property;
    545 	int subnode;
    546 
    547 	fdt_for_each_property_offset(property, fdto, node) {
    548 		const char *name;
    549 		const void *prop;
    550 		int prop_len;
    551 		int ret;
    552 
    553 		prop = fdt_getprop_by_offset(fdto, property, &name,
    554 					     &prop_len);
    555 		if (prop_len == -FDT_ERR_NOTFOUND)
    556 			return -FDT_ERR_INTERNAL;
    557 		if (prop_len < 0)
    558 			return prop_len;
    559 
    560 		ret = fdt_setprop(fdt, target, name, prop, prop_len);
    561 		if (ret)
    562 			return ret;
    563 	}
    564 
    565 	fdt_for_each_subnode(subnode, fdto, node) {
    566 		const char *name = fdt_get_name(fdto, subnode, NULL);
    567 		int nnode;
    568 		int ret;
    569 
    570 		nnode = fdt_add_subnode(fdt, target, name);
    571 		if (nnode == -FDT_ERR_EXISTS) {
    572 			nnode = fdt_subnode_offset(fdt, target, name);
    573 			if (nnode == -FDT_ERR_NOTFOUND)
    574 				return -FDT_ERR_INTERNAL;
    575 		}
    576 
    577 		if (nnode < 0)
    578 			return nnode;
    579 
    580 		ret = overlay_apply_node(fdt, nnode, fdto, subnode);
    581 		if (ret)
    582 			return ret;
    583 	}
    584 
    585 	return 0;
    586 }
    587 
    588 /**
    589  * overlay_merge - Merge an overlay into its base device tree
    590  * @fdt: Base Device Tree blob
    591  * @fdto: Device tree overlay blob
    592  *
    593  * overlay_merge() merges an overlay into its base device tree.
    594  *
    595  * This is the final step in the device tree overlay application
    596  * process, when all the phandles have been adjusted and resolved and
    597  * you just have to merge overlay into the base device tree.
    598  *
    599  * returns:
    600  *      0 on success
    601  *      Negative error code on failure
    602  */
    603 static int overlay_merge(void *fdt, void *fdto)
    604 {
    605 	int fragment;
    606 
    607 	fdt_for_each_subnode(fragment, fdto, 0) {
    608 		int overlay;
    609 		int target;
    610 		int ret;
    611 
    612 		/*
    613 		 * Each fragments will have an __overlay__ node. If
    614 		 * they don't, it's not supposed to be merged
    615 		 */
    616 		overlay = fdt_subnode_offset(fdto, fragment, "__overlay__");
    617 		if (overlay == -FDT_ERR_NOTFOUND)
    618 			continue;
    619 
    620 		if (overlay < 0)
    621 			return overlay;
    622 
    623 		target = overlay_get_target(fdt, fdto, fragment);
    624 		if (target < 0)
    625 			return target;
    626 
    627 		ret = overlay_apply_node(fdt, target, fdto, overlay);
    628 		if (ret)
    629 			return ret;
    630 	}
    631 
    632 	return 0;
    633 }
    634 
    635 int fdt_overlay_apply(void *fdt, void *fdto)
    636 {
    637 	uint32_t delta = fdt_get_max_phandle(fdt);
    638 	int ret;
    639 
    640 	FDT_CHECK_HEADER(fdt);
    641 	FDT_CHECK_HEADER(fdto);
    642 
    643 	ret = overlay_adjust_local_phandles(fdto, delta);
    644 	if (ret)
    645 		goto err;
    646 
    647 	ret = overlay_update_local_references(fdto, delta);
    648 	if (ret)
    649 		goto err;
    650 
    651 	ret = overlay_fixup_phandles(fdt, fdto);
    652 	if (ret)
    653 		goto err;
    654 
    655 	ret = overlay_merge(fdt, fdto);
    656 	if (ret)
    657 		goto err;
    658 
    659 	/*
    660 	 * The overlay has been damaged, erase its magic.
    661 	 */
    662 	fdt_set_magic(fdto, ~0);
    663 
    664 	return 0;
    665 
    666 err:
    667 	/*
    668 	 * The overlay might have been damaged, erase its magic.
    669 	 */
    670 	fdt_set_magic(fdto, ~0);
    671 
    672 	/*
    673 	 * The base device tree might have been damaged, erase its
    674 	 * magic.
    675 	 */
    676 	fdt_set_magic(fdt, ~0);
    677 
    678 	return ret;
    679 }
    680