Jetpack/u-boot/arch/arm/mach-tegra/dt-edit.c

424 lines
9.3 KiB
C

/*
* Copyright (C) 2010-2019 NVIDIA CORPORATION.
*
* SPDX-License-Identifier: GPL-2.0
*/
#include <stdlib.h>
#include <common.h>
#include <fdt_support.h>
#include <fdtdec.h>
#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);
}