fdt: Allow stacked overlays phandle references
This patch enables an overlay to refer to a previous overlay's labels by performing a merge of symbol information at application time. In a nutshell it allows an overlay to refer to a symbol that a previous overlay has defined. It requires both the base and all the overlays to be compiled with the -@ command line switch so that symbol information is included. base.dts -------- /dts-v1/; / { foo: foonode { foo-property; }; }; $ dtc -@ -I dts -O dtb -o base.dtb base.dts bar.dts ------- /dts-v1/; /plugin/; / { fragment@1 { target = <&foo>; __overlay__ { overlay-1-property; bar: barnode { bar-property; }; }; }; }; $ dtc -@ -I dts -O dtb -o bar.dtb bar.dts baz.dts ------- /dts-v1/; /plugin/; / { fragment@1 { target = <&bar>; __overlay__ { overlay-2-property; baz: baznode { baz-property; }; }; }; }; $ dtc -@ -I dts -O dtb -o baz.dtb baz.dts Applying the overlays: $ fdtoverlay -i base.dtb -o target.dtb bar.dtb baz.dtb Dumping: $ fdtdump target.dtb / { foonode { overlay-1-property; foo-property; linux,phandle = <0x00000001>; phandle = <0x00000001>; barnode { overlay-2-property; phandle = <0x00000002>; linux,phandle = <0x00000002>; bar-property; baznode { phandle = <0x00000003>; linux,phandle = <0x00000003>; baz-property; }; }; }; __symbols__ { baz = "/foonode/barnode/baznode"; bar = "/foonode/barnode"; foo = "/foonode"; }; }; Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com> Signed-off-by: David Gibson <david@gibson.dropbear.id.au> Acked-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
parent
81ecc5d92b
commit
f00c36a01e
@ -39,6 +39,7 @@ static uint32_t overlay_get_target_phandle(const void *fdto, int fragment)
|
||||
* @fdt: Base device tree blob
|
||||
* @fdto: Device tree overlay blob
|
||||
* @fragment: node offset of the fragment in the overlay
|
||||
* @pathp: pointer which receives the path of the target (or NULL)
|
||||
*
|
||||
* overlay_get_target() retrieves the target offset in the base
|
||||
* device tree of a fragment, no matter how the actual targetting is
|
||||
@ -49,37 +50,47 @@ static uint32_t overlay_get_target_phandle(const void *fdto, int fragment)
|
||||
* Negative error code on error
|
||||
*/
|
||||
static int overlay_get_target(const void *fdt, const void *fdto,
|
||||
int fragment)
|
||||
int fragment, char const **pathp)
|
||||
{
|
||||
uint32_t phandle;
|
||||
const char *path;
|
||||
int path_len;
|
||||
const char *path = NULL;
|
||||
int path_len = 0, ret;
|
||||
|
||||
/* Try first to do a phandle based lookup */
|
||||
phandle = overlay_get_target_phandle(fdto, fragment);
|
||||
if (phandle == (uint32_t)-1)
|
||||
return -FDT_ERR_BADPHANDLE;
|
||||
|
||||
if (phandle)
|
||||
return fdt_node_offset_by_phandle(fdt, phandle);
|
||||
/* no phandle, try path */
|
||||
if (!phandle) {
|
||||
/* And then a path based lookup */
|
||||
path = fdt_getprop(fdto, fragment, "target-path", &path_len);
|
||||
if (path)
|
||||
ret = fdt_path_offset(fdt, path);
|
||||
else
|
||||
ret = path_len;
|
||||
} else
|
||||
ret = fdt_node_offset_by_phandle(fdt, phandle);
|
||||
|
||||
/* And then a path based lookup */
|
||||
path = fdt_getprop(fdto, fragment, "target-path", &path_len);
|
||||
if (!path) {
|
||||
/*
|
||||
* If we haven't found either a target or a
|
||||
* target-path property in a node that contains a
|
||||
* __overlay__ subnode (we wouldn't be called
|
||||
* otherwise), consider it a improperly written
|
||||
* overlay
|
||||
*/
|
||||
if (path_len == -FDT_ERR_NOTFOUND)
|
||||
return -FDT_ERR_BADOVERLAY;
|
||||
/*
|
||||
* If we haven't found either a target or a
|
||||
* target-path property in a node that contains a
|
||||
* __overlay__ subnode (we wouldn't be called
|
||||
* otherwise), consider it a improperly written
|
||||
* overlay
|
||||
*/
|
||||
if (ret < 0 && path_len == -FDT_ERR_NOTFOUND)
|
||||
ret = -FDT_ERR_BADOVERLAY;
|
||||
|
||||
return path_len;
|
||||
}
|
||||
/* return on error */
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return fdt_path_offset(fdt, path);
|
||||
/* return pointer to path (if available) */
|
||||
if (pathp)
|
||||
*pathp = path ? path : NULL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -590,7 +601,7 @@ static int overlay_apply_node(void *fdt, int target,
|
||||
*
|
||||
* overlay_merge() merges an overlay into its base device tree.
|
||||
*
|
||||
* This is the final step in the device tree overlay application
|
||||
* This is the next to last step in the device tree overlay application
|
||||
* process, when all the phandles have been adjusted and resolved and
|
||||
* you just have to merge overlay into the base device tree.
|
||||
*
|
||||
@ -618,7 +629,7 @@ static int overlay_merge(void *fdt, void *fdto)
|
||||
if (overlay < 0)
|
||||
return overlay;
|
||||
|
||||
target = overlay_get_target(fdt, fdto, fragment);
|
||||
target = overlay_get_target(fdt, fdto, fragment, NULL);
|
||||
if (target < 0)
|
||||
return target;
|
||||
|
||||
@ -630,6 +641,175 @@ static int overlay_merge(void *fdt, void *fdto)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_path_len(const void *fdt, int nodeoffset)
|
||||
{
|
||||
int len = 0, namelen;
|
||||
const char *name;
|
||||
|
||||
FDT_CHECK_HEADER(fdt);
|
||||
|
||||
for (;;) {
|
||||
name = fdt_get_name(fdt, nodeoffset, &namelen);
|
||||
if (!name)
|
||||
return namelen;
|
||||
|
||||
/* root? we're done */
|
||||
if (namelen == 0)
|
||||
break;
|
||||
|
||||
nodeoffset = fdt_parent_offset(fdt, nodeoffset);
|
||||
if (nodeoffset < 0)
|
||||
return nodeoffset;
|
||||
len += namelen + 1;
|
||||
}
|
||||
|
||||
/* in case of root pretend it's "/" */
|
||||
if (len == 0)
|
||||
len++;
|
||||
return len;
|
||||
}
|
||||
|
||||
/**
|
||||
* overlay_symbol_update - Update the symbols of base tree after a merge
|
||||
* @fdt: Base Device Tree blob
|
||||
* @fdto: Device tree overlay blob
|
||||
*
|
||||
* overlay_symbol_update() updates the symbols of the base tree with the
|
||||
* symbols of the applied overlay
|
||||
*
|
||||
* This is the last step in the device tree overlay application
|
||||
* process, allowing the reference of overlay symbols by subsequent
|
||||
* overlay operations.
|
||||
*
|
||||
* returns:
|
||||
* 0 on success
|
||||
* Negative error code on failure
|
||||
*/
|
||||
static int overlay_symbol_update(void *fdt, void *fdto)
|
||||
{
|
||||
int root_sym, ov_sym, prop, path_len, fragment, target;
|
||||
int len, frag_name_len, ret, rel_path_len;
|
||||
const char *s, *e;
|
||||
const char *path;
|
||||
const char *name;
|
||||
const char *frag_name;
|
||||
const char *rel_path;
|
||||
const char *target_path;
|
||||
char *buf;
|
||||
void *p;
|
||||
|
||||
ov_sym = fdt_subnode_offset(fdto, 0, "__symbols__");
|
||||
|
||||
/* if no overlay symbols exist no problem */
|
||||
if (ov_sym < 0)
|
||||
return 0;
|
||||
|
||||
root_sym = fdt_subnode_offset(fdt, 0, "__symbols__");
|
||||
|
||||
/* it no root symbols exist we should create them */
|
||||
if (root_sym == -FDT_ERR_NOTFOUND)
|
||||
root_sym = fdt_add_subnode(fdt, 0, "__symbols__");
|
||||
|
||||
/* any error is fatal now */
|
||||
if (root_sym < 0)
|
||||
return root_sym;
|
||||
|
||||
/* iterate over each overlay symbol */
|
||||
fdt_for_each_property_offset(prop, fdto, ov_sym) {
|
||||
path = fdt_getprop_by_offset(fdto, prop, &name, &path_len);
|
||||
if (!path)
|
||||
return path_len;
|
||||
|
||||
/* verify it's a string property (terminated by a single \0) */
|
||||
if (path_len < 1 || memchr(path, '\0', path_len) != &path[path_len - 1])
|
||||
return -FDT_ERR_BADVALUE;
|
||||
|
||||
/* keep end marker to avoid strlen() */
|
||||
e = path + path_len;
|
||||
|
||||
/* format: /<fragment-name>/__overlay__/<relative-subnode-path> */
|
||||
|
||||
if (*path != '/')
|
||||
return -FDT_ERR_BADVALUE;
|
||||
|
||||
/* get fragment name first */
|
||||
s = strchr(path + 1, '/');
|
||||
if (!s)
|
||||
return -FDT_ERR_BADOVERLAY;
|
||||
|
||||
frag_name = path + 1;
|
||||
frag_name_len = s - path - 1;
|
||||
|
||||
/* verify format; safe since "s" lies in \0 terminated prop */
|
||||
len = sizeof("/__overlay__/") - 1;
|
||||
if ((e - s) < len || memcmp(s, "/__overlay__/", len))
|
||||
return -FDT_ERR_BADOVERLAY;
|
||||
|
||||
rel_path = s + len;
|
||||
rel_path_len = e - rel_path;
|
||||
|
||||
/* find the fragment index in which the symbol lies */
|
||||
ret = fdt_subnode_offset_namelen(fdto, 0, frag_name,
|
||||
frag_name_len);
|
||||
/* not found? */
|
||||
if (ret < 0)
|
||||
return -FDT_ERR_BADOVERLAY;
|
||||
fragment = ret;
|
||||
|
||||
/* an __overlay__ subnode must exist */
|
||||
ret = fdt_subnode_offset(fdto, fragment, "__overlay__");
|
||||
if (ret < 0)
|
||||
return -FDT_ERR_BADOVERLAY;
|
||||
|
||||
/* get the target of the fragment */
|
||||
ret = overlay_get_target(fdt, fdto, fragment, &target_path);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
target = ret;
|
||||
|
||||
/* if we have a target path use */
|
||||
if (!target_path) {
|
||||
ret = get_path_len(fdt, target);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
len = ret;
|
||||
} else {
|
||||
len = strlen(target_path);
|
||||
}
|
||||
|
||||
ret = fdt_setprop_placeholder(fdt, root_sym, name,
|
||||
len + (len > 1) + rel_path_len + 1, &p);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (!target_path) {
|
||||
/* again in case setprop_placeholder changed it */
|
||||
ret = overlay_get_target(fdt, fdto, fragment, &target_path);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
target = ret;
|
||||
}
|
||||
|
||||
buf = p;
|
||||
if (len > 1) { /* target is not root */
|
||||
if (!target_path) {
|
||||
ret = fdt_get_path(fdt, target, buf, len + 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else
|
||||
memcpy(buf, target_path, len + 1);
|
||||
|
||||
} else
|
||||
len--;
|
||||
|
||||
buf[len] = '/';
|
||||
memcpy(buf + len + 1, rel_path, rel_path_len);
|
||||
buf[len + 1 + rel_path_len] = '\0';
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fdt_overlay_apply(void *fdt, void *fdto)
|
||||
{
|
||||
uint32_t delta = fdt_get_max_phandle(fdt);
|
||||
@ -654,6 +834,10 @@ int fdt_overlay_apply(void *fdt, void *fdto)
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
ret = overlay_symbol_update(fdt, fdto);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
/*
|
||||
* The overlay has been damaged, erase its magic.
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user