fdt_overlay.c revision 328459
1328459Skevans#include "libfdt_env.h" 2328459Skevans 3328459Skevans#include <fdt.h> 4328459Skevans#include <libfdt.h> 5328459Skevans 6328459Skevans#include "libfdt_internal.h" 7328459Skevans 8328459Skevans/** 9328459Skevans * overlay_get_target_phandle - retrieves the target phandle of a fragment 10328459Skevans * @fdto: pointer to the device tree overlay blob 11328459Skevans * @fragment: node offset of the fragment in the overlay 12328459Skevans * 13328459Skevans * overlay_get_target_phandle() retrieves the target phandle of an 14328459Skevans * overlay fragment when that fragment uses a phandle (target 15328459Skevans * property) instead of a path (target-path property). 16328459Skevans * 17328459Skevans * returns: 18328459Skevans * the phandle pointed by the target property 19328459Skevans * 0, if the phandle was not found 20328459Skevans * -1, if the phandle was malformed 21328459Skevans */ 22328459Skevansstatic uint32_t overlay_get_target_phandle(const void *fdto, int fragment) 23328459Skevans{ 24328459Skevans const fdt32_t *val; 25328459Skevans int len; 26328459Skevans 27328459Skevans val = fdt_getprop(fdto, fragment, "target", &len); 28328459Skevans if (!val) 29328459Skevans return 0; 30328459Skevans 31328459Skevans if ((len != sizeof(*val)) || (fdt32_to_cpu(*val) == (uint32_t)-1)) 32328459Skevans return (uint32_t)-1; 33328459Skevans 34328459Skevans return fdt32_to_cpu(*val); 35328459Skevans} 36328459Skevans 37328459Skevans/** 38328459Skevans * overlay_get_target - retrieves the offset of a fragment's target 39328459Skevans * @fdt: Base device tree blob 40328459Skevans * @fdto: Device tree overlay blob 41328459Skevans * @fragment: node offset of the fragment in the overlay 42328459Skevans * @pathp: pointer which receives the path of the target (or NULL) 43328459Skevans * 44328459Skevans * overlay_get_target() retrieves the target offset in the base 45328459Skevans * device tree of a fragment, no matter how the actual targetting is 46328459Skevans * done (through a phandle or a path) 47328459Skevans * 48328459Skevans * returns: 49328459Skevans * the targetted node offset in the base device tree 50328459Skevans * Negative error code on error 51328459Skevans */ 52328459Skevansstatic int overlay_get_target(const void *fdt, const void *fdto, 53328459Skevans int fragment, char const **pathp) 54328459Skevans{ 55328459Skevans uint32_t phandle; 56328459Skevans const char *path = NULL; 57328459Skevans int path_len = 0, ret; 58328459Skevans 59328459Skevans /* Try first to do a phandle based lookup */ 60328459Skevans phandle = overlay_get_target_phandle(fdto, fragment); 61328459Skevans if (phandle == (uint32_t)-1) 62328459Skevans return -FDT_ERR_BADPHANDLE; 63328459Skevans 64328459Skevans /* no phandle, try path */ 65328459Skevans if (!phandle) { 66328459Skevans /* And then a path based lookup */ 67328459Skevans path = fdt_getprop(fdto, fragment, "target-path", &path_len); 68328459Skevans if (path) 69328459Skevans ret = fdt_path_offset(fdt, path); 70328459Skevans else 71328459Skevans ret = path_len; 72328459Skevans } else 73328459Skevans ret = fdt_node_offset_by_phandle(fdt, phandle); 74328459Skevans 75328459Skevans /* 76328459Skevans * If we haven't found either a target or a 77328459Skevans * target-path property in a node that contains a 78328459Skevans * __overlay__ subnode (we wouldn't be called 79328459Skevans * otherwise), consider it a improperly written 80328459Skevans * overlay 81328459Skevans */ 82328459Skevans if (ret < 0 && path_len == -FDT_ERR_NOTFOUND) 83328459Skevans ret = -FDT_ERR_BADOVERLAY; 84328459Skevans 85328459Skevans /* return on error */ 86328459Skevans if (ret < 0) 87328459Skevans return ret; 88328459Skevans 89328459Skevans /* return pointer to path (if available) */ 90328459Skevans if (pathp) 91328459Skevans *pathp = path ? path : NULL; 92328459Skevans 93328459Skevans return ret; 94328459Skevans} 95328459Skevans 96328459Skevans/** 97328459Skevans * overlay_phandle_add_offset - Increases a phandle by an offset 98328459Skevans * @fdt: Base device tree blob 99328459Skevans * @node: Device tree overlay blob 100328459Skevans * @name: Name of the property to modify (phandle or linux,phandle) 101328459Skevans * @delta: offset to apply 102328459Skevans * 103328459Skevans * overlay_phandle_add_offset() increments a node phandle by a given 104328459Skevans * offset. 105328459Skevans * 106328459Skevans * returns: 107328459Skevans * 0 on success. 108328459Skevans * Negative error code on error 109328459Skevans */ 110328459Skevansstatic int overlay_phandle_add_offset(void *fdt, int node, 111328459Skevans const char *name, uint32_t delta) 112328459Skevans{ 113328459Skevans const fdt32_t *val; 114328459Skevans uint32_t adj_val; 115328459Skevans int len; 116328459Skevans 117328459Skevans val = fdt_getprop(fdt, node, name, &len); 118328459Skevans if (!val) 119328459Skevans return len; 120328459Skevans 121328459Skevans if (len != sizeof(*val)) 122328459Skevans return -FDT_ERR_BADPHANDLE; 123328459Skevans 124328459Skevans adj_val = fdt32_to_cpu(*val); 125328459Skevans if ((adj_val + delta) < adj_val) 126328459Skevans return -FDT_ERR_NOPHANDLES; 127328459Skevans 128328459Skevans adj_val += delta; 129328459Skevans if (adj_val == (uint32_t)-1) 130328459Skevans return -FDT_ERR_NOPHANDLES; 131328459Skevans 132328459Skevans return fdt_setprop_inplace_u32(fdt, node, name, adj_val); 133328459Skevans} 134328459Skevans 135328459Skevans/** 136328459Skevans * overlay_adjust_node_phandles - Offsets the phandles of a node 137328459Skevans * @fdto: Device tree overlay blob 138328459Skevans * @node: Offset of the node we want to adjust 139328459Skevans * @delta: Offset to shift the phandles of 140328459Skevans * 141328459Skevans * overlay_adjust_node_phandles() adds a constant to all the phandles 142328459Skevans * of a given node. This is mainly use as part of the overlay 143328459Skevans * application process, when we want to update all the overlay 144328459Skevans * phandles to not conflict with the overlays of the base device tree. 145328459Skevans * 146328459Skevans * returns: 147328459Skevans * 0 on success 148328459Skevans * Negative error code on failure 149328459Skevans */ 150328459Skevansstatic int overlay_adjust_node_phandles(void *fdto, int node, 151328459Skevans uint32_t delta) 152328459Skevans{ 153328459Skevans int child; 154328459Skevans int ret; 155328459Skevans 156328459Skevans ret = overlay_phandle_add_offset(fdto, node, "phandle", delta); 157328459Skevans if (ret && ret != -FDT_ERR_NOTFOUND) 158328459Skevans return ret; 159328459Skevans 160328459Skevans ret = overlay_phandle_add_offset(fdto, node, "linux,phandle", delta); 161328459Skevans if (ret && ret != -FDT_ERR_NOTFOUND) 162328459Skevans return ret; 163328459Skevans 164328459Skevans fdt_for_each_subnode(child, fdto, node) { 165328459Skevans ret = overlay_adjust_node_phandles(fdto, child, delta); 166328459Skevans if (ret) 167328459Skevans return ret; 168328459Skevans } 169328459Skevans 170328459Skevans return 0; 171328459Skevans} 172328459Skevans 173328459Skevans/** 174328459Skevans * overlay_adjust_local_phandles - Adjust the phandles of a whole overlay 175328459Skevans * @fdto: Device tree overlay blob 176328459Skevans * @delta: Offset to shift the phandles of 177328459Skevans * 178328459Skevans * overlay_adjust_local_phandles() adds a constant to all the 179328459Skevans * phandles of an overlay. This is mainly use as part of the overlay 180328459Skevans * application process, when we want to update all the overlay 181328459Skevans * phandles to not conflict with the overlays of the base device tree. 182328459Skevans * 183328459Skevans * returns: 184328459Skevans * 0 on success 185328459Skevans * Negative error code on failure 186328459Skevans */ 187328459Skevansstatic int overlay_adjust_local_phandles(void *fdto, uint32_t delta) 188328459Skevans{ 189328459Skevans /* 190328459Skevans * Start adjusting the phandles from the overlay root 191328459Skevans */ 192328459Skevans return overlay_adjust_node_phandles(fdto, 0, delta); 193328459Skevans} 194328459Skevans 195328459Skevans/** 196328459Skevans * overlay_update_local_node_references - Adjust the overlay references 197328459Skevans * @fdto: Device tree overlay blob 198328459Skevans * @tree_node: Node offset of the node to operate on 199328459Skevans * @fixup_node: Node offset of the matching local fixups node 200328459Skevans * @delta: Offset to shift the phandles of 201328459Skevans * 202328459Skevans * overlay_update_local_nodes_references() update the phandles 203328459Skevans * pointing to a node within the device tree overlay by adding a 204328459Skevans * constant delta. 205328459Skevans * 206328459Skevans * This is mainly used as part of a device tree application process, 207328459Skevans * where you want the device tree overlays phandles to not conflict 208328459Skevans * with the ones from the base device tree before merging them. 209328459Skevans * 210328459Skevans * returns: 211328459Skevans * 0 on success 212328459Skevans * Negative error code on failure 213328459Skevans */ 214328459Skevansstatic int overlay_update_local_node_references(void *fdto, 215328459Skevans int tree_node, 216328459Skevans int fixup_node, 217328459Skevans uint32_t delta) 218328459Skevans{ 219328459Skevans int fixup_prop; 220328459Skevans int fixup_child; 221328459Skevans int ret; 222328459Skevans 223328459Skevans fdt_for_each_property_offset(fixup_prop, fdto, fixup_node) { 224328459Skevans const fdt32_t *fixup_val; 225328459Skevans const char *tree_val; 226328459Skevans const char *name; 227328459Skevans int fixup_len; 228328459Skevans int tree_len; 229328459Skevans int i; 230328459Skevans 231328459Skevans fixup_val = fdt_getprop_by_offset(fdto, fixup_prop, 232328459Skevans &name, &fixup_len); 233328459Skevans if (!fixup_val) 234328459Skevans return fixup_len; 235328459Skevans 236328459Skevans if (fixup_len % sizeof(uint32_t)) 237328459Skevans return -FDT_ERR_BADOVERLAY; 238328459Skevans 239328459Skevans tree_val = fdt_getprop(fdto, tree_node, name, &tree_len); 240328459Skevans if (!tree_val) { 241328459Skevans if (tree_len == -FDT_ERR_NOTFOUND) 242328459Skevans return -FDT_ERR_BADOVERLAY; 243328459Skevans 244328459Skevans return tree_len; 245328459Skevans } 246328459Skevans 247328459Skevans for (i = 0; i < (fixup_len / sizeof(uint32_t)); i++) { 248328459Skevans fdt32_t adj_val; 249328459Skevans uint32_t poffset; 250328459Skevans 251328459Skevans poffset = fdt32_to_cpu(fixup_val[i]); 252328459Skevans 253328459Skevans /* 254328459Skevans * phandles to fixup can be unaligned. 255328459Skevans * 256328459Skevans * Use a memcpy for the architectures that do 257328459Skevans * not support unaligned accesses. 258328459Skevans */ 259328459Skevans memcpy(&adj_val, tree_val + poffset, sizeof(adj_val)); 260328459Skevans 261328459Skevans adj_val = cpu_to_fdt32(fdt32_to_cpu(adj_val) + delta); 262328459Skevans 263328459Skevans ret = fdt_setprop_inplace_namelen_partial(fdto, 264328459Skevans tree_node, 265328459Skevans name, 266328459Skevans strlen(name), 267328459Skevans poffset, 268328459Skevans &adj_val, 269328459Skevans sizeof(adj_val)); 270328459Skevans if (ret == -FDT_ERR_NOSPACE) 271328459Skevans return -FDT_ERR_BADOVERLAY; 272328459Skevans 273328459Skevans if (ret) 274328459Skevans return ret; 275328459Skevans } 276328459Skevans } 277328459Skevans 278328459Skevans fdt_for_each_subnode(fixup_child, fdto, fixup_node) { 279328459Skevans const char *fixup_child_name = fdt_get_name(fdto, fixup_child, 280328459Skevans NULL); 281328459Skevans int tree_child; 282328459Skevans 283328459Skevans tree_child = fdt_subnode_offset(fdto, tree_node, 284328459Skevans fixup_child_name); 285328459Skevans if (tree_child == -FDT_ERR_NOTFOUND) 286328459Skevans return -FDT_ERR_BADOVERLAY; 287328459Skevans if (tree_child < 0) 288328459Skevans return tree_child; 289328459Skevans 290328459Skevans ret = overlay_update_local_node_references(fdto, 291328459Skevans tree_child, 292328459Skevans fixup_child, 293328459Skevans delta); 294328459Skevans if (ret) 295328459Skevans return ret; 296328459Skevans } 297328459Skevans 298328459Skevans return 0; 299328459Skevans} 300328459Skevans 301328459Skevans/** 302328459Skevans * overlay_update_local_references - Adjust the overlay references 303328459Skevans * @fdto: Device tree overlay blob 304328459Skevans * @delta: Offset to shift the phandles of 305328459Skevans * 306328459Skevans * overlay_update_local_references() update all the phandles pointing 307328459Skevans * to a node within the device tree overlay by adding a constant 308328459Skevans * delta to not conflict with the base overlay. 309328459Skevans * 310328459Skevans * This is mainly used as part of a device tree application process, 311328459Skevans * where you want the device tree overlays phandles to not conflict 312328459Skevans * with the ones from the base device tree before merging them. 313328459Skevans * 314328459Skevans * returns: 315328459Skevans * 0 on success 316328459Skevans * Negative error code on failure 317328459Skevans */ 318328459Skevansstatic int overlay_update_local_references(void *fdto, uint32_t delta) 319328459Skevans{ 320328459Skevans int fixups; 321328459Skevans 322328459Skevans fixups = fdt_path_offset(fdto, "/__local_fixups__"); 323328459Skevans if (fixups < 0) { 324328459Skevans /* There's no local phandles to adjust, bail out */ 325328459Skevans if (fixups == -FDT_ERR_NOTFOUND) 326328459Skevans return 0; 327328459Skevans 328328459Skevans return fixups; 329328459Skevans } 330328459Skevans 331328459Skevans /* 332328459Skevans * Update our local references from the root of the tree 333328459Skevans */ 334328459Skevans return overlay_update_local_node_references(fdto, 0, fixups, 335328459Skevans delta); 336328459Skevans} 337328459Skevans 338328459Skevans/** 339328459Skevans * overlay_fixup_one_phandle - Set an overlay phandle to the base one 340328459Skevans * @fdt: Base Device Tree blob 341328459Skevans * @fdto: Device tree overlay blob 342328459Skevans * @symbols_off: Node offset of the symbols node in the base device tree 343328459Skevans * @path: Path to a node holding a phandle in the overlay 344328459Skevans * @path_len: number of path characters to consider 345328459Skevans * @name: Name of the property holding the phandle reference in the overlay 346328459Skevans * @name_len: number of name characters to consider 347328459Skevans * @poffset: Offset within the overlay property where the phandle is stored 348328459Skevans * @label: Label of the node referenced by the phandle 349328459Skevans * 350328459Skevans * overlay_fixup_one_phandle() resolves an overlay phandle pointing to 351328459Skevans * a node in the base device tree. 352328459Skevans * 353328459Skevans * This is part of the device tree overlay application process, when 354328459Skevans * you want all the phandles in the overlay to point to the actual 355328459Skevans * base dt nodes. 356328459Skevans * 357328459Skevans * returns: 358328459Skevans * 0 on success 359328459Skevans * Negative error code on failure 360328459Skevans */ 361328459Skevansstatic int overlay_fixup_one_phandle(void *fdt, void *fdto, 362328459Skevans int symbols_off, 363328459Skevans const char *path, uint32_t path_len, 364328459Skevans const char *name, uint32_t name_len, 365328459Skevans int poffset, const char *label) 366328459Skevans{ 367328459Skevans const char *symbol_path; 368328459Skevans uint32_t phandle; 369328459Skevans fdt32_t phandle_prop; 370328459Skevans int symbol_off, fixup_off; 371328459Skevans int prop_len; 372328459Skevans 373328459Skevans if (symbols_off < 0) 374328459Skevans return symbols_off; 375328459Skevans 376328459Skevans symbol_path = fdt_getprop(fdt, symbols_off, label, 377328459Skevans &prop_len); 378328459Skevans if (!symbol_path) 379328459Skevans return prop_len; 380328459Skevans 381328459Skevans symbol_off = fdt_path_offset(fdt, symbol_path); 382328459Skevans if (symbol_off < 0) 383328459Skevans return symbol_off; 384328459Skevans 385328459Skevans phandle = fdt_get_phandle(fdt, symbol_off); 386328459Skevans if (!phandle) 387328459Skevans return -FDT_ERR_NOTFOUND; 388328459Skevans 389328459Skevans fixup_off = fdt_path_offset_namelen(fdto, path, path_len); 390328459Skevans if (fixup_off == -FDT_ERR_NOTFOUND) 391328459Skevans return -FDT_ERR_BADOVERLAY; 392328459Skevans if (fixup_off < 0) 393328459Skevans return fixup_off; 394328459Skevans 395328459Skevans phandle_prop = cpu_to_fdt32(phandle); 396328459Skevans return fdt_setprop_inplace_namelen_partial(fdto, fixup_off, 397328459Skevans name, name_len, poffset, 398328459Skevans &phandle_prop, 399328459Skevans sizeof(phandle_prop)); 400328459Skevans}; 401328459Skevans 402328459Skevans/** 403328459Skevans * overlay_fixup_phandle - Set an overlay phandle to the base one 404328459Skevans * @fdt: Base Device Tree blob 405328459Skevans * @fdto: Device tree overlay blob 406328459Skevans * @symbols_off: Node offset of the symbols node in the base device tree 407328459Skevans * @property: Property offset in the overlay holding the list of fixups 408328459Skevans * 409328459Skevans * overlay_fixup_phandle() resolves all the overlay phandles pointed 410328459Skevans * to in a __fixups__ property, and updates them to match the phandles 411328459Skevans * in use in the base device tree. 412328459Skevans * 413328459Skevans * This is part of the device tree overlay application process, when 414328459Skevans * you want all the phandles in the overlay to point to the actual 415328459Skevans * base dt nodes. 416328459Skevans * 417328459Skevans * returns: 418328459Skevans * 0 on success 419328459Skevans * Negative error code on failure 420328459Skevans */ 421328459Skevansstatic int overlay_fixup_phandle(void *fdt, void *fdto, int symbols_off, 422328459Skevans int property) 423328459Skevans{ 424328459Skevans const char *value; 425328459Skevans const char *label; 426328459Skevans int len; 427328459Skevans 428328459Skevans value = fdt_getprop_by_offset(fdto, property, 429328459Skevans &label, &len); 430328459Skevans if (!value) { 431328459Skevans if (len == -FDT_ERR_NOTFOUND) 432328459Skevans return -FDT_ERR_INTERNAL; 433328459Skevans 434328459Skevans return len; 435328459Skevans } 436328459Skevans 437328459Skevans do { 438328459Skevans const char *path, *name, *fixup_end; 439328459Skevans const char *fixup_str = value; 440328459Skevans uint32_t path_len, name_len; 441328459Skevans uint32_t fixup_len; 442328459Skevans char *sep, *endptr; 443328459Skevans int poffset, ret; 444328459Skevans 445328459Skevans fixup_end = memchr(value, '\0', len); 446328459Skevans if (!fixup_end) 447328459Skevans return -FDT_ERR_BADOVERLAY; 448328459Skevans fixup_len = fixup_end - fixup_str; 449328459Skevans 450328459Skevans len -= fixup_len + 1; 451328459Skevans value += fixup_len + 1; 452328459Skevans 453328459Skevans path = fixup_str; 454328459Skevans sep = memchr(fixup_str, ':', fixup_len); 455328459Skevans if (!sep || *sep != ':') 456328459Skevans return -FDT_ERR_BADOVERLAY; 457328459Skevans 458328459Skevans path_len = sep - path; 459328459Skevans if (path_len == (fixup_len - 1)) 460328459Skevans return -FDT_ERR_BADOVERLAY; 461328459Skevans 462328459Skevans fixup_len -= path_len + 1; 463328459Skevans name = sep + 1; 464328459Skevans sep = memchr(name, ':', fixup_len); 465328459Skevans if (!sep || *sep != ':') 466328459Skevans return -FDT_ERR_BADOVERLAY; 467328459Skevans 468328459Skevans name_len = sep - name; 469328459Skevans if (!name_len) 470328459Skevans return -FDT_ERR_BADOVERLAY; 471328459Skevans 472328459Skevans poffset = strtoul(sep + 1, &endptr, 10); 473328459Skevans if ((*endptr != '\0') || (endptr <= (sep + 1))) 474328459Skevans return -FDT_ERR_BADOVERLAY; 475328459Skevans 476328459Skevans ret = overlay_fixup_one_phandle(fdt, fdto, symbols_off, 477328459Skevans path, path_len, name, name_len, 478328459Skevans poffset, label); 479328459Skevans if (ret) 480328459Skevans return ret; 481328459Skevans } while (len > 0); 482328459Skevans 483328459Skevans return 0; 484328459Skevans} 485328459Skevans 486328459Skevans/** 487328459Skevans * overlay_fixup_phandles - Resolve the overlay phandles to the base 488328459Skevans * device tree 489328459Skevans * @fdt: Base Device Tree blob 490328459Skevans * @fdto: Device tree overlay blob 491328459Skevans * 492328459Skevans * overlay_fixup_phandles() resolves all the overlay phandles pointing 493328459Skevans * to nodes in the base device tree. 494328459Skevans * 495328459Skevans * This is one of the steps of the device tree overlay application 496328459Skevans * process, when you want all the phandles in the overlay to point to 497328459Skevans * the actual base dt nodes. 498328459Skevans * 499328459Skevans * returns: 500328459Skevans * 0 on success 501328459Skevans * Negative error code on failure 502328459Skevans */ 503328459Skevansstatic int overlay_fixup_phandles(void *fdt, void *fdto) 504328459Skevans{ 505328459Skevans int fixups_off, symbols_off; 506328459Skevans int property; 507328459Skevans 508328459Skevans /* We can have overlays without any fixups */ 509328459Skevans fixups_off = fdt_path_offset(fdto, "/__fixups__"); 510328459Skevans if (fixups_off == -FDT_ERR_NOTFOUND) 511328459Skevans return 0; /* nothing to do */ 512328459Skevans if (fixups_off < 0) 513328459Skevans return fixups_off; 514328459Skevans 515328459Skevans /* And base DTs without symbols */ 516328459Skevans symbols_off = fdt_path_offset(fdt, "/__symbols__"); 517328459Skevans if ((symbols_off < 0 && (symbols_off != -FDT_ERR_NOTFOUND))) 518328459Skevans return symbols_off; 519328459Skevans 520328459Skevans fdt_for_each_property_offset(property, fdto, fixups_off) { 521328459Skevans int ret; 522328459Skevans 523328459Skevans ret = overlay_fixup_phandle(fdt, fdto, symbols_off, property); 524328459Skevans if (ret) 525328459Skevans return ret; 526328459Skevans } 527328459Skevans 528328459Skevans return 0; 529328459Skevans} 530328459Skevans 531328459Skevans/** 532328459Skevans * overlay_apply_node - Merges a node into the base device tree 533328459Skevans * @fdt: Base Device Tree blob 534328459Skevans * @target: Node offset in the base device tree to apply the fragment to 535328459Skevans * @fdto: Device tree overlay blob 536328459Skevans * @node: Node offset in the overlay holding the changes to merge 537328459Skevans * 538328459Skevans * overlay_apply_node() merges a node into a target base device tree 539328459Skevans * node pointed. 540328459Skevans * 541328459Skevans * This is part of the final step in the device tree overlay 542328459Skevans * application process, when all the phandles have been adjusted and 543328459Skevans * resolved and you just have to merge overlay into the base device 544328459Skevans * tree. 545328459Skevans * 546328459Skevans * returns: 547328459Skevans * 0 on success 548328459Skevans * Negative error code on failure 549328459Skevans */ 550328459Skevansstatic int overlay_apply_node(void *fdt, int target, 551328459Skevans void *fdto, int node) 552328459Skevans{ 553328459Skevans int property; 554328459Skevans int subnode; 555328459Skevans 556328459Skevans fdt_for_each_property_offset(property, fdto, node) { 557328459Skevans const char *name; 558328459Skevans const void *prop; 559328459Skevans int prop_len; 560328459Skevans int ret; 561328459Skevans 562328459Skevans prop = fdt_getprop_by_offset(fdto, property, &name, 563328459Skevans &prop_len); 564328459Skevans if (prop_len == -FDT_ERR_NOTFOUND) 565328459Skevans return -FDT_ERR_INTERNAL; 566328459Skevans if (prop_len < 0) 567328459Skevans return prop_len; 568328459Skevans 569328459Skevans ret = fdt_setprop(fdt, target, name, prop, prop_len); 570328459Skevans if (ret) 571328459Skevans return ret; 572328459Skevans } 573328459Skevans 574328459Skevans fdt_for_each_subnode(subnode, fdto, node) { 575328459Skevans const char *name = fdt_get_name(fdto, subnode, NULL); 576328459Skevans int nnode; 577328459Skevans int ret; 578328459Skevans 579328459Skevans nnode = fdt_add_subnode(fdt, target, name); 580328459Skevans if (nnode == -FDT_ERR_EXISTS) { 581328459Skevans nnode = fdt_subnode_offset(fdt, target, name); 582328459Skevans if (nnode == -FDT_ERR_NOTFOUND) 583328459Skevans return -FDT_ERR_INTERNAL; 584328459Skevans } 585328459Skevans 586328459Skevans if (nnode < 0) 587328459Skevans return nnode; 588328459Skevans 589328459Skevans ret = overlay_apply_node(fdt, nnode, fdto, subnode); 590328459Skevans if (ret) 591328459Skevans return ret; 592328459Skevans } 593328459Skevans 594328459Skevans return 0; 595328459Skevans} 596328459Skevans 597328459Skevans/** 598328459Skevans * overlay_merge - Merge an overlay into its base device tree 599328459Skevans * @fdt: Base Device Tree blob 600328459Skevans * @fdto: Device tree overlay blob 601328459Skevans * 602328459Skevans * overlay_merge() merges an overlay into its base device tree. 603328459Skevans * 604328459Skevans * This is the next to last step in the device tree overlay application 605328459Skevans * process, when all the phandles have been adjusted and resolved and 606328459Skevans * you just have to merge overlay into the base device tree. 607328459Skevans * 608328459Skevans * returns: 609328459Skevans * 0 on success 610328459Skevans * Negative error code on failure 611328459Skevans */ 612328459Skevansstatic int overlay_merge(void *fdt, void *fdto) 613328459Skevans{ 614328459Skevans int fragment; 615328459Skevans 616328459Skevans fdt_for_each_subnode(fragment, fdto, 0) { 617328459Skevans int overlay; 618328459Skevans int target; 619328459Skevans int ret; 620328459Skevans 621328459Skevans /* 622328459Skevans * Each fragments will have an __overlay__ node. If 623328459Skevans * they don't, it's not supposed to be merged 624328459Skevans */ 625328459Skevans overlay = fdt_subnode_offset(fdto, fragment, "__overlay__"); 626328459Skevans if (overlay == -FDT_ERR_NOTFOUND) 627328459Skevans continue; 628328459Skevans 629328459Skevans if (overlay < 0) 630328459Skevans return overlay; 631328459Skevans 632328459Skevans target = overlay_get_target(fdt, fdto, fragment, NULL); 633328459Skevans if (target < 0) 634328459Skevans return target; 635328459Skevans 636328459Skevans ret = overlay_apply_node(fdt, target, fdto, overlay); 637328459Skevans if (ret) 638328459Skevans return ret; 639328459Skevans } 640328459Skevans 641328459Skevans return 0; 642328459Skevans} 643328459Skevans 644328459Skevansstatic int get_path_len(const void *fdt, int nodeoffset) 645328459Skevans{ 646328459Skevans int len = 0, namelen; 647328459Skevans const char *name; 648328459Skevans 649328459Skevans FDT_CHECK_HEADER(fdt); 650328459Skevans 651328459Skevans for (;;) { 652328459Skevans name = fdt_get_name(fdt, nodeoffset, &namelen); 653328459Skevans if (!name) 654328459Skevans return namelen; 655328459Skevans 656328459Skevans /* root? we're done */ 657328459Skevans if (namelen == 0) 658328459Skevans break; 659328459Skevans 660328459Skevans nodeoffset = fdt_parent_offset(fdt, nodeoffset); 661328459Skevans if (nodeoffset < 0) 662328459Skevans return nodeoffset; 663328459Skevans len += namelen + 1; 664328459Skevans } 665328459Skevans 666328459Skevans /* in case of root pretend it's "/" */ 667328459Skevans if (len == 0) 668328459Skevans len++; 669328459Skevans return len; 670328459Skevans} 671328459Skevans 672328459Skevans/** 673328459Skevans * overlay_symbol_update - Update the symbols of base tree after a merge 674328459Skevans * @fdt: Base Device Tree blob 675328459Skevans * @fdto: Device tree overlay blob 676328459Skevans * 677328459Skevans * overlay_symbol_update() updates the symbols of the base tree with the 678328459Skevans * symbols of the applied overlay 679328459Skevans * 680328459Skevans * This is the last step in the device tree overlay application 681328459Skevans * process, allowing the reference of overlay symbols by subsequent 682328459Skevans * overlay operations. 683328459Skevans * 684328459Skevans * returns: 685328459Skevans * 0 on success 686328459Skevans * Negative error code on failure 687328459Skevans */ 688328459Skevansstatic int overlay_symbol_update(void *fdt, void *fdto) 689328459Skevans{ 690328459Skevans int root_sym, ov_sym, prop, path_len, fragment, target; 691328459Skevans int len, frag_name_len, ret, rel_path_len; 692328459Skevans const char *s, *e; 693328459Skevans const char *path; 694328459Skevans const char *name; 695328459Skevans const char *frag_name; 696328459Skevans const char *rel_path; 697328459Skevans const char *target_path; 698328459Skevans char *buf; 699328459Skevans void *p; 700328459Skevans 701328459Skevans ov_sym = fdt_subnode_offset(fdto, 0, "__symbols__"); 702328459Skevans 703328459Skevans /* if no overlay symbols exist no problem */ 704328459Skevans if (ov_sym < 0) 705328459Skevans return 0; 706328459Skevans 707328459Skevans root_sym = fdt_subnode_offset(fdt, 0, "__symbols__"); 708328459Skevans 709328459Skevans /* it no root symbols exist we should create them */ 710328459Skevans if (root_sym == -FDT_ERR_NOTFOUND) 711328459Skevans root_sym = fdt_add_subnode(fdt, 0, "__symbols__"); 712328459Skevans 713328459Skevans /* any error is fatal now */ 714328459Skevans if (root_sym < 0) 715328459Skevans return root_sym; 716328459Skevans 717328459Skevans /* iterate over each overlay symbol */ 718328459Skevans fdt_for_each_property_offset(prop, fdto, ov_sym) { 719328459Skevans path = fdt_getprop_by_offset(fdto, prop, &name, &path_len); 720328459Skevans if (!path) 721328459Skevans return path_len; 722328459Skevans 723328459Skevans /* verify it's a string property (terminated by a single \0) */ 724328459Skevans if (path_len < 1 || memchr(path, '\0', path_len) != &path[path_len - 1]) 725328459Skevans return -FDT_ERR_BADVALUE; 726328459Skevans 727328459Skevans /* keep end marker to avoid strlen() */ 728328459Skevans e = path + path_len; 729328459Skevans 730328459Skevans /* format: /<fragment-name>/__overlay__/<relative-subnode-path> */ 731328459Skevans 732328459Skevans if (*path != '/') 733328459Skevans return -FDT_ERR_BADVALUE; 734328459Skevans 735328459Skevans /* get fragment name first */ 736328459Skevans s = strchr(path + 1, '/'); 737328459Skevans if (!s) 738328459Skevans return -FDT_ERR_BADOVERLAY; 739328459Skevans 740328459Skevans frag_name = path + 1; 741328459Skevans frag_name_len = s - path - 1; 742328459Skevans 743328459Skevans /* verify format; safe since "s" lies in \0 terminated prop */ 744328459Skevans len = sizeof("/__overlay__/") - 1; 745328459Skevans if ((e - s) < len || memcmp(s, "/__overlay__/", len)) 746328459Skevans return -FDT_ERR_BADOVERLAY; 747328459Skevans 748328459Skevans rel_path = s + len; 749328459Skevans rel_path_len = e - rel_path; 750328459Skevans 751328459Skevans /* find the fragment index in which the symbol lies */ 752328459Skevans ret = fdt_subnode_offset_namelen(fdto, 0, frag_name, 753328459Skevans frag_name_len); 754328459Skevans /* not found? */ 755328459Skevans if (ret < 0) 756328459Skevans return -FDT_ERR_BADOVERLAY; 757328459Skevans fragment = ret; 758328459Skevans 759328459Skevans /* an __overlay__ subnode must exist */ 760328459Skevans ret = fdt_subnode_offset(fdto, fragment, "__overlay__"); 761328459Skevans if (ret < 0) 762328459Skevans return -FDT_ERR_BADOVERLAY; 763328459Skevans 764328459Skevans /* get the target of the fragment */ 765328459Skevans ret = overlay_get_target(fdt, fdto, fragment, &target_path); 766328459Skevans if (ret < 0) 767328459Skevans return ret; 768328459Skevans target = ret; 769328459Skevans 770328459Skevans /* if we have a target path use */ 771328459Skevans if (!target_path) { 772328459Skevans ret = get_path_len(fdt, target); 773328459Skevans if (ret < 0) 774328459Skevans return ret; 775328459Skevans len = ret; 776328459Skevans } else { 777328459Skevans len = strlen(target_path); 778328459Skevans } 779328459Skevans 780328459Skevans ret = fdt_setprop_placeholder(fdt, root_sym, name, 781328459Skevans len + (len > 1) + rel_path_len + 1, &p); 782328459Skevans if (ret < 0) 783328459Skevans return ret; 784328459Skevans 785328459Skevans if (!target_path) { 786328459Skevans /* again in case setprop_placeholder changed it */ 787328459Skevans ret = overlay_get_target(fdt, fdto, fragment, &target_path); 788328459Skevans if (ret < 0) 789328459Skevans return ret; 790328459Skevans target = ret; 791328459Skevans } 792328459Skevans 793328459Skevans buf = p; 794328459Skevans if (len > 1) { /* target is not root */ 795328459Skevans if (!target_path) { 796328459Skevans ret = fdt_get_path(fdt, target, buf, len + 1); 797328459Skevans if (ret < 0) 798328459Skevans return ret; 799328459Skevans } else 800328459Skevans memcpy(buf, target_path, len + 1); 801328459Skevans 802328459Skevans } else 803328459Skevans len--; 804328459Skevans 805328459Skevans buf[len] = '/'; 806328459Skevans memcpy(buf + len + 1, rel_path, rel_path_len); 807328459Skevans buf[len + 1 + rel_path_len] = '\0'; 808328459Skevans } 809328459Skevans 810328459Skevans return 0; 811328459Skevans} 812328459Skevans 813328459Skevansint fdt_overlay_apply(void *fdt, void *fdto) 814328459Skevans{ 815328459Skevans uint32_t delta = fdt_get_max_phandle(fdt); 816328459Skevans int ret; 817328459Skevans 818328459Skevans FDT_CHECK_HEADER(fdt); 819328459Skevans FDT_CHECK_HEADER(fdto); 820328459Skevans 821328459Skevans ret = overlay_adjust_local_phandles(fdto, delta); 822328459Skevans if (ret) 823328459Skevans goto err; 824328459Skevans 825328459Skevans ret = overlay_update_local_references(fdto, delta); 826328459Skevans if (ret) 827328459Skevans goto err; 828328459Skevans 829328459Skevans ret = overlay_fixup_phandles(fdt, fdto); 830328459Skevans if (ret) 831328459Skevans goto err; 832328459Skevans 833328459Skevans ret = overlay_merge(fdt, fdto); 834328459Skevans if (ret) 835328459Skevans goto err; 836328459Skevans 837328459Skevans ret = overlay_symbol_update(fdt, fdto); 838328459Skevans if (ret) 839328459Skevans goto err; 840328459Skevans 841328459Skevans /* 842328459Skevans * The overlay has been damaged, erase its magic. 843328459Skevans */ 844328459Skevans fdt_set_magic(fdto, ~0); 845328459Skevans 846328459Skevans return 0; 847328459Skevans 848328459Skevanserr: 849328459Skevans /* 850328459Skevans * The overlay might have been damaged, erase its magic. 851328459Skevans */ 852328459Skevans fdt_set_magic(fdto, ~0); 853328459Skevans 854328459Skevans /* 855328459Skevans * The base device tree might have been damaged, erase its 856328459Skevans * magic. 857328459Skevans */ 858328459Skevans fdt_set_magic(fdt, ~0); 859328459Skevans 860328459Skevans return ret; 861328459Skevans} 862