
Hi Maxime,
On May 27, 2016, at 12:13 , Maxime Ripard maxime.ripard@free-electrons.com wrote:
The device tree overlays are a good way to deal with user-modifyable boards or boards with some kind of an expansion mechanism where we can easily plug new board in (like the BBB, the Raspberry Pi or the CHIP).
Add a new function to merge overlays with a base device tree.
Signed-off-by: Maxime Ripard maxime.ripard@free-electrons.com
include/libfdt.h | 30 ++++ lib/libfdt/Makefile | 2 +- lib/libfdt/fdt_overlay.c | 414 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 445 insertions(+), 1 deletion(-) create mode 100644 lib/libfdt/fdt_overlay.c
diff --git a/include/libfdt.h b/include/libfdt.h index 1e01b2c7ebdf..783067e841a1 100644 --- a/include/libfdt.h +++ b/include/libfdt.h @@ -1698,6 +1698,36 @@ int fdt_add_subnode(void *fdt, int parentoffset, const char *name); */ int fdt_del_node(void *fdt, int nodeoffset);
+/**
- fdt_overlay_apply - Applies a DT overlay on a base DT
- @fdt: pointer to the base device tree blob
- @fdto: pointer to the device tree overlay blob
- fdt_overlay_apply() will apply the given device tree overlay on the
- given base device tree.
- Expect the base device tree to be modified, even if the function
- returns an error.
If the base tree has been modified on an error it is unsafe to use it for booting. A valid strategy would be to scribble over the DT magic number so that that blob is invalidated.
What are the other people’s opinion on this?
- returns:
- 0, on success
- -FDT_ERR_NOSPACE, there's not enough space in the base device tree
- -FDT_ERR_NOTFOUND, the overlay points to some inexistant nodes or
properties in the base DT
- -FDT_ERR_BADPHANDLE, the phandles in the overlay do not have the right
magic
- -FDT_ERR_INTERNAL,
- -FDT_ERR_BADLAYOUT,
- -FDT_ERR_BADMAGIC,
- -FDT_ERR_BADOFFSET,
- -FDT_ERR_BADPATH,
- -FDT_ERR_BADVERSION,
- -FDT_ERR_BADSTRUCTURE,
- -FDT_ERR_BADSTATE,
- -FDT_ERR_TRUNCATED, standard meanings
- */
+int fdt_overlay_apply(void *fdt, void *fdto);
/**********************************************************************/ /* Debugging / informational functions */ /**********************************************************************/ diff --git a/lib/libfdt/Makefile b/lib/libfdt/Makefile index 934d6142c3e9..4935bf012a75 100644 --- a/lib/libfdt/Makefile +++ b/lib/libfdt/Makefile @@ -6,4 +6,4 @@ #
obj-y += fdt.o fdt_ro.o fdt_rw.o fdt_strerror.o fdt_sw.o fdt_wip.o \
- fdt_empty_tree.o fdt_addresses.o fdt_region.o
- fdt_empty_tree.o fdt_addresses.o fdt_region.o fdt_overlay.o
diff --git a/lib/libfdt/fdt_overlay.c b/lib/libfdt/fdt_overlay.c new file mode 100644 index 000000000000..1e68e903c734 --- /dev/null +++ b/lib/libfdt/fdt_overlay.c @@ -0,0 +1,414 @@ +#include "libfdt_env.h"
+#include <fdt.h> +#include <libfdt.h>
+#include "libfdt_internal.h"
+static uint32_t fdt_overlay_get_target_phandle(const void *fdto, int node) +{
- const uint32_t *val;
- int len;
- val = fdt_getprop(fdto, node, "target", &len);
- if (!val || (len != sizeof(*val)))
return 0;
- return fdt32_to_cpu(*val);
+}
+static int fdt_overlay_get_target(const void *fdt, const void *fdto,
int fragment)
+{
- uint32_t phandle;
- const char *path;
- /* Try first to do a phandle based lookup */
- phandle = fdt_overlay_get_target_phandle(fdto, fragment);
- if (phandle)
return fdt_node_offset_by_phandle(fdt, phandle);
- /* And then a path based lookup */
- path = fdt_getprop(fdto, fragment, "target-path", NULL);
- if (!path)
return -FDT_ERR_NOTFOUND;
- return fdt_path_offset(fdt, path);
+}
Those targets are fine; beware there are patches with more target options.
+static int fdt_overlay_adjust_node_phandles(void *fdto, int node,
uint32_t delta)
+{
- int property;
- int child;
- fdt_for_each_property(fdto, node, property) {
const uint32_t *val;
const char *name;
uint32_t adj_val;
int len;
int ret;
val = fdt_getprop_by_offset(fdto, property,
&name, &len);
if (!val || (len != sizeof(*val)))
Superfluous parentheses.
continue;
if (strcmp(name, "phandle") && strcmp(name, "linux,phandle"))
continue;
adj_val = fdt32_to_cpu(*val);
adj_val += delta;
ret = fdt_setprop_inplace_u32(fdto, node, name, adj_val);
if (ret)
return ret;
- }
- fdt_for_each_subnode(fdto, child, node)
fdt_overlay_adjust_node_phandles(fdto, child, delta);
- return 0;
+}
+static int fdt_overlay_adjust_local_phandles(void *fdto, uint32_t delta) +{
- int root;
- root = fdt_path_offset(fdto, "/");
- if (root < 0)
return root;
- return fdt_overlay_adjust_node_phandles(fdto, root, delta);
+}
+static int _fdt_overlay_update_local_references(void *fdto,
int tree_node,
int fixup_node,
uint32_t delta)
+{
- int fixup_prop;
- int fixup_child;
- fdt_for_each_property(fdto, fixup_node, fixup_prop) {
const char *fixup_name, *tree_name;
const uint32_t *val = NULL;
uint32_t adj_val;
int fixup_len;
int tree_prop;
int tree_len;
int ret;
fdt_getprop_by_offset(fdto, fixup_prop,
&fixup_name, &fixup_len);
if (!strcmp(fixup_name, "phandle") ||
!strcmp(fixup_name, "linux,phandle"))
continue;
fdt_for_each_property(fdto, tree_node, tree_prop) {
val = fdt_getprop_by_offset(fdto, tree_prop,
&tree_name, &tree_len);
if (!strcmp(tree_name, fixup_name))
break;
}
if (!val || !tree_len)
return -FDT_ERR_NOTFOUND;
if (!tree_name)
return -FDT_ERR_NOTFOUND;
if (tree_len != 4)
return -FDT_ERR_NOTFOUND;
adj_val = fdt32_to_cpu(*val);
adj_val += delta;
ret = fdt_setprop_inplace_u32(fdto, tree_node, fixup_name,
adj_val);
if (ret)
return ret;
- }
- fdt_for_each_subnode(fdto, fixup_child, fixup_node) {
const char *fixup_child_name = fdt_get_name(fdto,
fixup_child, NULL);
int tree_child;
fdt_for_each_subnode(fdto, tree_child, tree_node) {
const char *tree_child_name = fdt_get_name(fdto,
tree_child,
NULL);
if (!strcmp(fixup_child_name, tree_child_name))
break;
}
_fdt_overlay_update_local_references(fdto,
tree_child,
fixup_child,
delta);
- }
- return 0;
+}
+static int fdt_overlay_update_local_references(void *dto, uint32_t delta) +{
- int root, fixups;
- root = fdt_path_offset(dto, "/");
- if (root < 0)
return root;
- fixups = fdt_path_offset(dto, "/__local_fixups__");
- if (root < 0)
return root;
- return _fdt_overlay_update_local_references(dto, root, fixups,
delta);
+}
+static int _fdt_overlay_fixup_phandle(void *fdt, void *fdto,
int symbols_off,
const char *path, const char *name,
int index, const char *label)
+{
- const uint32_t *prop_val;
- const char *symbol_path;
- uint32_t *fixup_val;
- uint32_t phandle;
- int symbol_off, fixup_off;
- int prop_len;
- int ret;
- symbol_path = fdt_getprop(fdt, symbols_off, label,
&prop_len);
- if (!symbol_path)
return -FDT_ERR_NOTFOUND;
- symbol_off = fdt_path_offset(fdt, symbol_path);
- if (symbol_off < 0)
return symbol_off;
- phandle = fdt_get_phandle(fdt, symbol_off);
- if (!phandle)
return -FDT_ERR_NOTFOUND;
- fixup_off = fdt_path_offset(fdto, path);
- if (fixup_off < 0)
return fixup_off;
- prop_val = fdt_getprop(fdto, fixup_off, name,
&prop_len);
- if (!prop_val)
return -FDT_ERR_NOTFOUND;
- fixup_val = malloc(prop_len);
- if (!fixup_val)
return -FDT_ERR_INTERNAL;
- memcpy(fixup_val, prop_val, prop_len);
- if (fdt32_to_cpu(fixup_val[index]) != 0xdeadbeef) {
ret = -FDT_ERR_BADPHANDLE;
goto out;
- }
- fixup_val[index] = cpu_to_fdt32(phandle);
- ret = fdt_setprop_inplace(fdto, fixup_off,
name, fixup_val,
prop_len);
+out:
- free(fixup_val);
- return ret;
+};
+static int fdt_overlay_fixup_phandle(void *fdt, void *fdto, int symbols_off,
int property)
+{
- const char *value, *tmp_value;
- const char *label;
- int tmp_len, len, next;
- int table = 0;
- int i;
- value = fdt_getprop_by_offset(fdto, property,
&label, &len);
- tmp_value = value;
- tmp_len = len;
- do {
next = strlen(tmp_value) + 1;
tmp_len -= next;
tmp_value += next;
table++;
- } while (tmp_len > 0);
- for (i = 0; i < table; i++) {
const char *prop_string = value;
const char *path, *name;
char *prop_cpy, *prop_tmp;
int index, stlen;
char *sep;
stlen = strlen(prop_string);
prop_cpy = malloc(stlen + 1);
if (!prop_cpy)
return -FDT_ERR_INTERNAL;
strncpy(prop_cpy, prop_string, stlen);
prop_cpy[stlen] = '\0';
for (prop_tmp = prop_cpy; (sep = strchr(prop_tmp, ':'));
prop_tmp += ((sep - prop_tmp) + 1))
*sep = '\0';
prop_tmp = prop_cpy;
path = prop_tmp;
prop_tmp += strlen(prop_tmp) + 1;
name = prop_tmp;
prop_tmp += strlen(prop_tmp) + 1;
index = strtol(prop_tmp, NULL, 10);
prop_tmp += strlen(prop_tmp) + 1;
value += stlen + 1;
len -= stlen + 1;
_fdt_overlay_fixup_phandle(fdt, fdto, symbols_off,
path, name, index, label);
free(prop_cpy);
- }
- return 0;
+}
+static int fdt_overlay_fixup_phandles(void *dt, void *dto) +{
- int fixups_off, symbols_off;
- int property;
- symbols_off = fdt_path_offset(dt, "/__symbols__");
- fixups_off = fdt_path_offset(dto, "/__fixups__");
- fdt_for_each_property(dto, fixups_off, property)
fdt_overlay_fixup_phandle(dt, dto, symbols_off, property);
- return 0;
+}
+static int fdt_apply_overlay_node(void *dt, void *dto,
int target, int overlay)
+{
- int property;
- int node;
- fdt_for_each_property(dto, overlay, property) {
const char *name;
const void *prop;
int prop_len;
int ret;
prop = fdt_getprop_by_offset(dto, property, &name,
&prop_len);
if (!prop)
return -FDT_ERR_INTERNAL;
ret = fdt_setprop(dt, target, name, prop, prop_len);
if (ret)
return ret;
- }
- fdt_for_each_subnode(dto, node, overlay) {
const char *name = fdt_get_name(dto, node, NULL);
int nnode;
int ret;
nnode = fdt_add_subnode(dt, target, name);
if (nnode < 0)
return nnode;
ret = fdt_apply_overlay_node(dt, dto, nnode, node);
if (ret)
return ret;
- }
- return 0;
+}
+static int fdt_overlay_merge(void *dt, void *dto) +{
- int root, fragment;
- root = fdt_path_offset(dto, "/");
- if (root < 0)
return root;
- fdt_for_each_subnode(dto, fragment, root) {
const char *name = fdt_get_name(dto, fragment, NULL);
uint32_t target;
int overlay;
int ret;
if (strncmp(name, "fragment", 8))
continue;
This is incorrect. The use of “fragment” is a convention only. The real test whether the node is an overlay fragment is that it contains a target property.
target = fdt_overlay_get_target(dt, dto, fragment);
if (target < 0)
return target;
So you could do ‘if (target < 0) continue;’ or handle a more complex error code.
overlay = fdt_subnode_offset(dto, fragment, "__overlay__");
if (overlay < 0)
return overlay;
ret = fdt_apply_overlay_node(dt, dto, target, overlay);
if (ret)
return ret;
- }
- return 0;
+}
+int fdt_overlay_apply(void *fdt, void *fdto) +{
- uint32_t delta = fdt_get_max_phandle(fdt) + 1;
- void *fdto_copy;
- int ret;
- FDT_CHECK_HEADER(fdt);
- FDT_CHECK_HEADER(fdto);
- /*
* We're going to modify the overlay so that we can apply it.
*
* Make sure sure we don't destroy the original
*/
- fdto_copy = malloc(fdt_totalsize(fdto));
- if (!fdto_copy)
return -FDT_ERR_INTERNAL;
- ret = fdt_move(fdto, fdto_copy, fdt_totalsize(fdto));
- if (ret)
goto out;
- ret = fdt_overlay_adjust_local_phandles(fdto_copy, delta);
- if (ret)
goto out;
- ret = fdt_overlay_update_local_references(fdto_copy, delta);
- if (ret)
goto out;
- ret = fdt_overlay_fixup_phandles(fdt, fdto_copy);
- if (ret)
goto out;
- ret = fdt_overlay_merge(fdt, fdto_copy);
+out:
- free(fdto_copy);
- return ret;
+}
2.8.2
I would caution against the liberal use of malloc in libfdt. We’re possibly running in a constrained environment; a custom extents based (non freeing) allocator should be better.
We need some figures about memory consumption when this is enabled, and a CONFIG option to disable it.
Regards
— Pantelis