/* * Copyright (C) 2010-2019 NVIDIA CORPORATION. * * SPDX-License-Identifier: GPL-2.0 */ #include #include #include #include #include "dt-edit.h" #define fdt_for_each_property(fdt, prop, parent) \ for (prop = fdt_first_property_offset(fdt, parent); \ prop >= 0; \ prop = fdt_next_property_offset(fdt, prop)) typedef int iter_envitem(void *blob_dst, char *item, void *param); static int fdt_copy_node_content(void *blob_src, int ofs_src, void *blob_dst, int ofs_dst, int indent) { int ofs_src_child, ofs_dst_child; int ret; /* * FIXME: This doesn't remove properties or nodes in the destination * that are not present in the source. For the nodes we care about * right now, this is not an issue. */ fdt_for_each_property(blob_src, ofs_src_child, ofs_src) { const void *prop; const char *name; int len; prop = fdt_getprop_by_offset(blob_src, ofs_src_child, &name, &len); debug("%s: %*scopy prop: %s\n", __func__, indent, "", name); add_prop: ret = fdt_setprop(blob_dst, ofs_dst, name, prop, len); if (ret == -FDT_ERR_NOSPACE) { ret = fdt_increase_size(blob_dst, 512); debug("Increased FDT blob size by 512 bytes\n"); if (!ret) goto add_prop; /* retry setprop */ else goto err_ret; /* error out */ } else if (ret < 0) { error("Can't copy DT prop %s: %s\n", name, fdt_strerror(ret)); return ret; } } fdt_for_each_subnode(blob_src, ofs_src_child, ofs_src) { const char *name; name = fdt_get_name(blob_src, ofs_src_child, NULL); debug("%s: %*scopy node: %s\n", __func__, indent, "", name); ofs_dst_child = fdt_subnode_offset(blob_dst, ofs_dst, name); if (ofs_dst_child < 0) { debug("%s: %*s(creating it in dst)\n", __func__, indent, ""); add_node: ofs_dst_child = fdt_add_subnode(blob_dst, ofs_dst, name); if (ofs_dst_child == -FDT_ERR_NOSPACE) { ret = fdt_increase_size(blob_dst, 512); debug("Increased FDT blob size by 512 bytes\n"); if (!ret) goto add_node; /* retry add_subnode */ else goto err_ret; /* error out */ } else if (ofs_dst_child < 0) { error("Can't copy DT node %s: %s\n", name, fdt_strerror(ofs_dst_child)); return ofs_dst_child; } } fdt_copy_node_content(blob_src, ofs_src_child, blob_dst, ofs_dst_child, indent + 2); } return 0; err_ret: printf("Can't increase blob size: %s\n", fdt_strerror(ret)); return ret; } static int fdt_add_path(void *blob, const char *path) { char *pcopy, *tmp, *node; int ofs_parent, ofs_child, ret; if (path[0] != '/') { error("Can't add path %s; missing leading /", path); return -1; } path++; if (!*path) { debug("%s: path points at DT root!", __func__); return 0; } pcopy = strdup(path); if (!pcopy) { error("strdup() failed"); return -1; } tmp = pcopy; ofs_parent = 0; while (true) { node = strsep(&tmp, "/"); if (!node) break; debug("%s: node=%s\n", __func__, node); ofs_child = fdt_subnode_offset(blob, ofs_parent, node); if (ofs_child < 0) ofs_child = fdt_add_subnode(blob, ofs_parent, node); if (ofs_child < 0) { error("Can't create DT node %s\n", node); ret = ofs_child; goto out; } ofs_parent = ofs_child; } ret = ofs_parent; out: free(pcopy); return ret; } static iter_envitem fdt_iter_copy_prop; static int fdt_iter_copy_prop(void *blob_dst, char *prop_path, void *blob_src) { char *prop_name, *node_path; const void *prop; int ofs_src, ofs_dst, len, ret; prop_name = strrchr(prop_path, '/'); if (!prop_name) { error("Can't copy prop %s; missing /", prop_path); return -1; } *prop_name = 0; prop_name++; node_path = prop_path; if (*node_path) { ofs_src = fdt_path_offset(blob_src, node_path); if (ofs_src < 0) { error("DT node %s missing in source; can't copy %s\n", node_path, prop_name); return -1; } ofs_dst = fdt_path_offset(blob_dst, node_path); if (ofs_src < 0) { error("DT node %s missing in dest; can't copy prop %s\n", node_path, prop_name); return -1; } } else { ofs_src = 0; ofs_dst = 0; } prop = fdt_getprop(blob_src, ofs_src, prop_name, &len); if (!prop) { error("DT property %s/%s missing in source; can't copy\n", node_path, prop_name); return -1; } add_prop: ret = fdt_setprop(blob_dst, ofs_dst, prop_name, prop, len); if (ret == -FDT_ERR_NOSPACE) { ret = fdt_increase_size(blob_dst, 512); debug("Increased FDT blob size by 512 bytes\n"); if (!ret) goto add_prop; /* retry setprop */ else goto err_ret; /* error out */ } else if (ret < 0) { error("Can't set DT prop %s/%s: %s\n", node_path, prop_name, fdt_strerror(ret)); return ret; } return 0; err_ret: printf("Can't increase blob size: %s\n", fdt_strerror(ret)); return ret; } static iter_envitem fdt_iter_copy_node; static int fdt_iter_copy_node(void *blob_dst, char *path, void *blob_src) { int ofs_dst, ofs_src; int ret; ofs_dst = fdt_add_path(blob_dst, path); if (ofs_dst < 0) { error("Can't find/create dest DT node %s to copy\n", path); return ofs_dst; } if (!fdtdec_get_is_enabled(blob_dst, ofs_dst)) { debug("%s: DT node %s disabled in dest; skipping copy\n", __func__, path); return 0; } ofs_src = fdt_path_offset(blob_src, path); if (ofs_src < 0) { error("DT node %s missing in source; can't copy\n", path); return 0; } ret = fdt_copy_node_content(blob_src, ofs_src, blob_dst, ofs_dst, 2); if (ret < 0) return ret; return 0; } static iter_envitem fdt_iter_del_node; static int fdt_iter_del_node(void *blob_dst, char *node_path, void *unused_param) { int ofs; ofs = fdt_path_offset(blob_dst, node_path); /* Node doesn't exist -> property can't exist -> it's removed! */ if (ofs == -FDT_ERR_NOTFOUND) return 0; if (ofs < 0) { error("DT node %s lookup failure; can't del node\n", node_path); return ofs; } return fdt_del_node(blob_dst, ofs); } static iter_envitem fdt_iter_del_prop; static int fdt_iter_del_prop(void *blob_dst, char *prop_path, void *unused_param) { char *prop_name, *node_path; int ofs, ret; prop_name = strrchr(prop_path, '/'); if (!prop_name) { error("Can't del prop %s; missing /", prop_path); return -1; } *prop_name = 0; prop_name++; node_path = prop_path; if (*node_path) { ofs = fdt_path_offset(blob_dst, node_path); /* Node doesn't exist -> property can't exist -> it's removed! */ if (ofs == -FDT_ERR_NOTFOUND) return 0; if (ofs < 0) { error("DT node %s lookup failure; can't del prop %s\n", node_path, prop_name); return ofs; } } else { ofs = 0; } ret = fdt_delprop(blob_dst, ofs, prop_name); /* Property doesn't exist -> it's already removed! */ if (ret == -FDT_ERR_NOTFOUND) return 0; return ret; } static int fdt_iter_envlist(iter_envitem *func, void *blob_dst, const char *env_varname, void *param) { char *items, *tmp, *item; int ret; items = getenv(env_varname); if (!items) { debug("%s: No env var %s\n", __func__, env_varname); return 0; } items = strdup(items); if (!items) { error("strdup(%s) failed", env_varname); return -1; } tmp = items; while (true) { item = strsep(&tmp, ":"); if (!item) break; debug("%s: item: %s\n", __func__, item); ret = func(blob_dst, item, param); if (ret < 0) { ret = -1; goto out; } } ret = 0; out: free(items); return ret; } __weak void *fdt_copy_get_blob_src_default(void) { return NULL; } static void *fdt_get_blob_src(void) { char *src_addr_s; src_addr_s = getenv("fdt_copy_src_addr"); if (!src_addr_s) return fdt_copy_get_blob_src_default(); return (void *)simple_strtoul(src_addr_s, NULL, 16); } static void *fdt_get_copy_blob_src(void *blob_dst) { void *blob_src = fdt_get_blob_src(); if (blob_src == blob_dst) return NULL; return blob_src; } int fdt_copy_env_nodelist(void *blob_dst) { void *blob_src; debug("%s:\n", __func__); blob_src = fdt_get_copy_blob_src(blob_dst); if (!blob_src) { debug("%s: No source DT\n", __func__); return 0; } return fdt_iter_envlist(fdt_iter_copy_node, blob_dst, "fdt_copy_node_paths", blob_src); } /* Deletes node list from dst blob, then copies same node list from src->dst */ int fdt_del_then_copy_env_nodelist(void *blob_dst) { void *blob_src; debug("%s:\n", __func__); blob_src = fdt_get_copy_blob_src(blob_dst); if (!blob_src) { debug("%s: No source DT\n", __func__); return 0; } fdt_iter_envlist(fdt_iter_del_node, blob_dst, "fdt_del_copy_node_paths", NULL); return fdt_iter_envlist(fdt_iter_copy_node, blob_dst, "fdt_del_copy_node_paths", blob_src); } int fdt_copy_env_proplist(void *blob_dst) { void *blob_src; debug("%s:\n", __func__); blob_src = fdt_get_copy_blob_src(blob_dst); if (!blob_src) { debug("%s: No source DT\n", __func__); return 0; } return fdt_iter_envlist(fdt_iter_copy_prop, blob_dst, "fdt_copy_prop_paths", blob_src); } int fdt_del_env_nodelist(void) { void *blob_src; debug("%s:\n", __func__); blob_src = fdt_get_blob_src(); if (!blob_src) { error("%s: No source DT\n", __func__); return -ENXIO; } return fdt_iter_envlist(fdt_iter_del_node, blob_src, "fdt_del_node_paths", NULL); } int fdt_del_env_proplist(void) { void *blob_src; debug("%s:\n", __func__); blob_src = fdt_get_blob_src(); if (!blob_src) { error("%s: No source DT\n", __func__); return -ENXIO; } return fdt_iter_envlist(fdt_iter_del_prop, blob_src, "fdt_del_prop_paths", NULL); }