[PATCH v3 3/4] libfdt: overlay_merge: Update phandles, symbols, fixups & local_fixups
Wasim Nazir <[email protected]>
| Newsgroups | org.kernel.vger.devicetree-compiler |
|---|---|
| Message-ID | <[email protected]> |
From: Srivatsa Vaddagiri <[email protected]> fdt_overlay_merge(), like fdt_overlay_apply(), has to perform similar operations on DT blobs. First operation is going through external symbol references specified in __fixups__ node of overlay DT blob and resolving them in base DT blob (as performed by overlay_fixups_update()). Another operation is going through __local_fixups__ of overlay DT blob and resolving them in base DT blob (as performed by overlay_local_fixups_update()). Unlike overlay case though, in case of merging two overlay blobs, its quite normal that some of the external references specified in __fixups__ node are not found in base blob. Modify overlay_fixup_phandles() to understand this possibility. Similarly while merging two blobs, the __symbol__ path needs to be updated based on the possibility that target is available in base DT or not. Modify overlay_symbol_update() to understand this possibility. Signed-off-by: Srivatsa Vaddagiri <[email protected]> Signed-off-by: Wasim Nazir <[email protected]> --- libfdt/fdt_overlay.c | 582 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 568 insertions(+), 14 deletions(-) diff --git a/libfdt/fdt_overlay.c b/libfdt/fdt_overlay.c index 03c88b05be72..aa85774b6f54 100644 --- a/libfdt/fdt_overlay.c +++ b/libfdt/fdt_overlay.c @@ -14,6 +14,8 @@ #define MAX_BUF_SIZE 256 +static bool find_node_str(const char *path_str, const char *node); + /** * overlay_get_target_phandle - retrieves the target phandle of a fragment * @fdto: pointer to the device tree overlay blob @@ -396,11 +398,11 @@ static int overlay_fixup_phandle(void *fdt, void *fdto, int symbols_off, symbol_path = fdt_getprop(fdt, symbols_off, label, &prop_len); if (!symbol_path) return prop_len; - + 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; @@ -459,6 +461,8 @@ static int overlay_fixup_phandle(void *fdt, void *fdto, int symbols_off, * device tree * @fdt: Base Device Tree blob * @fdto: Device tree overlay blob + * @merge_olay_max_phdl: Pointer to max phandle value for merged blobs, + * Both input blobs are overlay blobs that are being merged * * overlay_fixup_phandles() resolves all the overlay phandles pointing * to nodes in the base device tree. @@ -471,7 +475,8 @@ static int overlay_fixup_phandle(void *fdt, void *fdto, int symbols_off, * 0 on success * Negative error code on failure */ -static int overlay_fixup_phandles(void *fdt, void *fdto) +static int overlay_fixup_phandles(void *fdt, void *fdto, + uint32_t merge_olay_max_phdl) { int fixups_off, symbols_off; int property; @@ -492,7 +497,7 @@ static int overlay_fixup_phandles(void *fdt, void *fdto) int ret; ret = overlay_fixup_phandle(fdt, fdto, symbols_off, property); - if (ret) + if (ret && (!merge_olay_max_phdl || ret != -FDT_ERR_NOTFOUND)) return ret; } @@ -1106,6 +1111,7 @@ static int overlay_merge(void *fdt, void *fdto, uint32_t *merge_olay_max_phdl) return 0; } + static int get_path_len(const void *fdt, int nodeoffset) { int len = 0, namelen; @@ -1138,6 +1144,8 @@ static int get_path_len(const void *fdt, int nodeoffset) * overlay_symbol_update - Update the symbols of base tree after a merge * @fdt: Base Device Tree blob * @fdto: Device tree overlay blob + * @merge_olay_max_phdl: Pointer to max phandle value for merged blobs, + * Both input blobs are overlay blobs that are being merged * * overlay_symbol_update() updates the symbols of the base tree with the * symbols of the applied overlay @@ -1150,15 +1158,16 @@ static int get_path_len(const void *fdt, int nodeoffset) * 0 on success * Negative error code on failure */ -static int overlay_symbol_update(void *fdt, void *fdto) +static int overlay_symbol_update(void *fdt, void *fdto, + uint32_t merge_olay_max_phdl) { - int root_sym, ov_sym, prop, path_len, fragment, target; - int len, frag_name_len, ret, rel_path_len; + int root_sym, ov_sym, prop, next_prop, path_len, fragment, target; + int len, frag_name_len, ret, rel_path_len, rel_path_len_olay = 0; const char *s, *e; const char *path; const char *name; const char *frag_name; - const char *rel_path; + const char *rel_path, *rel_path_olay = NULL; const char *target_path; char *buf; void *p; @@ -1180,7 +1189,11 @@ static int overlay_symbol_update(void *fdt, void *fdto) return root_sym; /* iterate over each overlay symbol */ - fdt_for_each_property_offset(prop, fdto, ov_sym) { + /* Safeguard against property being possibly deleted in this loop */ + prop = fdt_first_property_offset(fdto, ov_sym); + while (prop >= 0) { + next_prop = fdt_next_property_offset(fdto, prop); + path = fdt_getprop_by_offset(fdto, prop, &name, &path_len); if (!path) return path_len; @@ -1212,6 +1225,11 @@ static int overlay_symbol_update(void *fdt, void *fdto) /* /<fragment-name>/__overlay__/<relative-subnode-path> */ rel_path = s + len; rel_path_len = e - rel_path - 1; + + if (merge_olay_max_phdl != 0) { + rel_path_olay = s + 1; + rel_path_len_olay = e - rel_path_olay - 1; + } } else if ((e - s) == len && (memcmp(s, "/__overlay__", len - 1) == 0)) { /* /<fragment-name>/__overlay__ */ @@ -1240,8 +1258,24 @@ static int overlay_symbol_update(void *fdt, void *fdto) ret = fdt_overlay_target_offset(fdt, fdto, fragment, &target_path); if (ret < 0) return ret; + target = ret; + /* Before proceeding further, check if you need to update + * rel_path for dtbo-dtbo merging case + */ + if (rel_path_olay) { + uint32_t phandle = + overlay_get_target_phandle(fdto, fragment); + int base_symbol_found = (phandle < merge_olay_max_phdl); + + /* For new nodes we need the overlay string in path */ + if (!base_symbol_found) { + rel_path = rel_path_olay; + rel_path_len = rel_path_len_olay; + } + } + /* if we have a target path use */ if (!target_path) { ret = get_path_len(fdt, target); @@ -1280,6 +1314,7 @@ static int overlay_symbol_update(void *fdt, void *fdto) buf[len] = '/'; memcpy(buf + len + 1, rel_path, rel_path_len); buf[len + 1 + rel_path_len] = '\0'; + prop = next_prop; } return 0; @@ -1308,7 +1343,7 @@ int fdt_overlay_apply(void *fdt, void *fdto) goto err; /* Update fdto's phandles using symbols from fdt */ - ret = overlay_fixup_phandles(fdt, fdto); + ret = overlay_fixup_phandles(fdt, fdto, 0); if (ret) goto err; @@ -1321,7 +1356,7 @@ int fdt_overlay_apply(void *fdt, void *fdto) if (ret) goto err; - ret = overlay_symbol_update(fdt, fdto); + ret = overlay_symbol_update(fdt, fdto, 0); if (ret) goto err; @@ -1347,6 +1382,514 @@ err: return ret; } +static int find_add_subnode(void *fdt, int parent_off, const char *node_name) +{ + int offset; + + offset = fdt_subnode_offset(fdt, parent_off, node_name); + + if (offset < 0) + offset = fdt_add_subnode(fdt, parent_off, node_name); + + return offset; +} + +static int prop_exists_in_node(void *fdt, const char *path, const char *prop_name) +{ + int offset; + const void *val; + + offset = fdt_path_offset(fdt, path); + if (offset < 0) + return 0; + + val = fdt_getprop(fdt, offset, prop_name, NULL); + + return val != NULL; +} + +static void *get_next_component(const char **p, int *len, char sep) +{ + char *q; + int consumed; + + q = memchr(*p, sep, *len); + if (!q) + return NULL; + + /* 1 for ':' */ + q++; + + consumed = (q - *p); + if (*len <= consumed) + return NULL; + + *len -= consumed; + *p = q; + + return q; +} + +static int lookup_target_path(void *fdt, void *fdto, const char *fragment, + int frag_name_len, char *buf, int buf_len, + int *target_off, int *root_path) +{ + int offset, ret, target, len; + const char *target_path; + static const char fragstr[] = "fragment@"; + int fragstrlen = sizeof(fragstr) - 1; + + memset(buf, 0, buf_len); + + /* Check fdto-fragment has fragment string */ + if (frag_name_len < fragstrlen || memcmp(fragment, fragstr, fragstrlen)) + return -FDT_ERR_BADOVERLAY; + + /* find the fragment index in which the symbol lies */ + ret = fdt_subnode_offset_namelen(fdto, 0, fragment, frag_name_len); + /* not found? */ + if (ret < 0) + return -FDT_ERR_BADOVERLAY; + + offset = ret; + + /* an __overlay__ subnode must exist */ + ret = fdt_subnode_offset(fdto, offset, "__overlay__"); + if (ret < 0) + return -FDT_ERR_BADOVERLAY; + + /* get the target of the fragment */ + ret = fdt_overlay_target_offset(fdt, fdto, offset, &target_path); + if (ret < 0) + return ret; + + target = ret; + if (target_off) + *target_off = 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); + } + if (len >= buf_len) + return -FDT_ERR_INTERNAL; + + 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); + + } + + /* Check fdt-path is having fragments or it part of root path */ + if (root_path && find_node_str(buf, fragstr)) + *root_path = 0; + else + *root_path = 1; + + return 0; +} + +static int fixup_snippet_update(void *fdt, void *fdto, const char *snippet, + int snippet_len, char *buf, int buflen, + int *ignore, int base_symbol_found, + uint32_t merge_olay_max_phdl) +{ + const char *snippet_o = snippet; + const char *path, *fragment, *prop_name, *prop_val, *rel_path; + char *sep; + int snippet_len_o = snippet_len, fragment_len, rel_path_len; + unsigned long int prop_len, path_len; + int rem, ret; + static const char tprop[] = "target"; + static const char frag[] = "/fragment"; + static const char olay[] = "/__overlay__"; + int root_path = 0; + + /* Validate format: + * path_to_node : prop_name : prop_offset + */ + path = snippet; + prop_name = get_next_component(&snippet, &snippet_len, ':'); + if (!prop_name) + return -FDT_ERR_BADOVERLAY; + + prop_val = get_next_component(&snippet, &snippet_len, ':'); + if (!prop_val) + return -FDT_ERR_BADOVERLAY; + + path_len = prop_name - path - 1; /* -1 for ':' */ + prop_len = prop_val - prop_name - 1; /* -1 for ':' */ + + if (path_len < sizeof(frag) - 1 || memcmp(path, frag, sizeof(frag) - 1)) + return -FDT_ERR_BADOVERLAY; + + if (base_symbol_found && prop_len == sizeof(tprop) - 1 + && !memcmp(prop_name, tprop, sizeof(tprop) - 1)) { + *ignore = 1; + return 0; + } + + fragment = path; + /* check if there is a '/' besides the first one in node_path */ + sep = memchr(fragment + 1, '/', path_len - 1); + if (sep) { + fragment_len = sep - fragment; + path_len -= (sep - fragment); + if (path_len < sizeof(olay) - 1 + || memcmp(sep, olay, sizeof(olay) - 1)) + return -FDT_ERR_BADOVERLAY; + + { + int frag_offset; + + frag_offset = + fdt_subnode_offset_namelen(fdto, 0, fragment + 1, + fragment_len - 1); + if (frag_offset < 0) + return -FDT_ERR_BADOVERLAY; + + /* an __overlay__ subnode must exist */ + ret = + fdt_subnode_offset(fdto, frag_offset, + "__overlay__"); + if (ret < 0) + return -FDT_ERR_BADOVERLAY; + } + rel_path = sep; + } else { + rel_path = fragment + path_len; + fragment_len = path_len; + } + rel_path_len = snippet_len_o - (rel_path - snippet_o); + + if (rel_path_len <= 0 || fragment_len >= buflen) + return -FDT_ERR_INTERNAL; + + ret = + lookup_target_path(fdt, fdto, fragment + 1, fragment_len - 1, buf, + buflen, NULL, &root_path); + if (ret) + return ret; + + /* Only single overlay should be present in path */ + if (find_node_str(buf, "__overlay__") + && find_node_str(rel_path, "__overlay__")) { + if (sep) + rel_path = sep + sizeof(olay) - 1; + } + + rem = buflen - strlen(buf); + if (rel_path_len >= rem) + return -FDT_ERR_INTERNAL; + + sep = buf + strlen(buf); + memcpy(sep, rel_path, rel_path_len); + + return 0; +} + +static const char *next_snippet(const char **prop, + int *prop_len, int *snippet_len) +{ + const char *next = *prop; + const char *tmp; + int len; + + if (*prop_len <= 0) + return NULL; + + tmp = memchr(next, '\0', *prop_len); + if (!tmp) + return NULL; + + tmp++; + + len = tmp - next; + *snippet_len = len; + *prop += len; + *prop_len -= len; + + return next; +} + +static int add_to_fixups(void *fdt, char *v, const char *label) +{ + const char *val; + char *p; + int vlen = strlen(v) + 1; /* +1 for NULL */ + int len, ret; + int root_fixup; + + root_fixup = fdt_subnode_offset(fdt, 0, "__fixups__"); + if (root_fixup == -FDT_ERR_NOTFOUND) + root_fixup = fdt_add_subnode(fdt, 0, "__fixups__"); + + if (root_fixup < 0) + return root_fixup; + + val = fdt_getprop(fdt, root_fixup, label, &len); + if (val) + vlen += len; + + ret = fdt_setprop_placeholder(fdt, root_fixup, label, + vlen, (void **)&p); + if (ret) + return ret; + + if (val) { + p += len; + vlen -= len; + } + memcpy(p, v, vlen); + + return 0; +} + +static int fdt_find_add_node(void *fdt, int parent_off, const char *node) +{ + int offset; + + offset = fdt_subnode_offset(fdt, parent_off, node); + if (offset < 0) + offset = fdt_add_subnode(fdt, parent_off, node); + + return offset; +} + +/* path => /abc/def/ghi */ +static const char *next_node(const char **path, int *path_len, int *node_len) +{ + const char *sep = *path, *node; + + if (*sep != '/' || *path_len <= 0) + return NULL; + + *path = *path + 1; + node = *path; + *path_len = *path_len - 1; + + sep = memchr(node, '/', *path_len); + if (sep) + *node_len = sep - node; + else + *node_len = *path_len; + + *path_len -= *node_len; + *path += *node_len; + + return node; +} + +static bool find_node_str(const char *path_str, const char *ip_str) +{ + const char *path, *node_str; + int path_len = 0, node_len = 0, ip_str_len = 0; + + path = path_str; + path_len = strlen(path); + ip_str_len = strlen(ip_str); + + while ((node_str = next_node(&path, &path_len, &node_len))) { + if (node_len >= ip_str_len + && !memcmp(node_str, ip_str, ip_str_len)) { + return true; + } + } + + return false; +} + +static int convert_to_u32(const char *p, uint32_t *val) +{ + char *endptr; + unsigned long prop_val; + + prop_val = strtoul(p, &endptr, 10); + if ((*endptr != '\0') || (endptr <= p)) + return -FDT_ERR_BADOVERLAY; + + *val = prop_val; /* size mis-match? */ + + return 0; +} + +static int add_to_local_fixups(void *fdt, const char *snippet) +{ + const char *path, *prop_name, *prop_val, *node; + unsigned long int prop_len = 0; + int path_len, node_len; + int parent, ret; + int snippet_len = strlen(snippet); + uint32_t val = 0; + char buf[MAX_BUF_SIZE]; + + /* Validate format: + * path_to_node : prop_name : prop_offset + * OR + * path_to_node + */ + path = snippet; + prop_name = get_next_component(&snippet, &snippet_len, ':'); + + if (prop_name) { + prop_val = get_next_component(&snippet, &snippet_len, ':'); + if (!prop_val) + return -FDT_ERR_BADOVERLAY; + + path_len = prop_name - path - 1; /* -1 for ':' */ + prop_len = prop_val - prop_name - 1; /* -1 for ':' */ + + ret = convert_to_u32(prop_val, &val); + if (ret) + return ret; + } else + path_len = strlen(snippet); + + parent = fdt_find_add_node(fdt, 0, "__local_fixups__"); + if (parent < 0) + return parent; + + while ((node = next_node(&path, &path_len, &node_len))) { + int offset; + + offset = + fdt_subnode_offset_namelen(fdt, parent, node, node_len); + if (offset < 0) { + offset = + fdt_add_subnode_namelen(fdt, parent, node, + node_len); + } + if (offset < 0) + return offset; + parent = offset; + } + + if (!prop_name) + return parent; + + if (prop_len >= sizeof(buf)) + return -FDT_ERR_INTERNAL; + memcpy(buf, prop_name, prop_len); + buf[prop_len] = 0; + + return fdt_appendprop_u32(fdt, parent, buf, val); +} + +static int overlay_fixups_update(void *fdt, void *fdto, + uint32_t merge_olay_max_phdl) +{ + int ov_fixup, root_fixup, prop; + + ov_fixup = fdt_subnode_offset(fdto, 0, "__fixups__"); + if (ov_fixup < 0) + return 0; + + root_fixup = find_add_subnode(fdt, 0, "__fixups__"); + if (root_fixup < 0) + return root_fixup; + + fdt_for_each_property_offset(prop, fdto, ov_fixup) { + int snippet_len, prop_len, base_symbol_found; + const char *label, *snippet, *prop_val; + + prop_val = fdt_getprop_by_offset(fdto, prop, &label, &prop_len); + if (prop_val == NULL) + return -FDT_ERR_BADOVERLAY; + + base_symbol_found = + prop_exists_in_node(fdt, "/__symbols__", label); + + while ((snippet = + next_snippet(&prop_val, &prop_len, &snippet_len))) { + char new_val[MAX_BUF_SIZE]; + int ignore = 0, ret; + + ret = + fixup_snippet_update(fdt, fdto, snippet, + snippet_len, new_val, + sizeof(new_val), &ignore, + base_symbol_found, + merge_olay_max_phdl); + if (ret) + return ret; + + if (ignore) + continue; + + if (!base_symbol_found) { + ret = add_to_fixups(fdt, new_val, label); + } else { + ret = add_to_local_fixups(fdt, new_val); + } + if (ret) + return ret; + } + } + + return 0; +} + +static int overlay_local_fixups_update(void *fdt, void *fdto, + uint32_t merge_olay_max_phdl) +{ + int ov_lfixups, root_lfixups, node, ret; + int root_path = 0; + + ov_lfixups = fdt_subnode_offset(fdto, 0, "__local_fixups__"); + if (ov_lfixups == -FDT_ERR_NOTFOUND) + return 0; + + root_lfixups = fdt_subnode_offset(fdt, 0, "__local_fixups__"); + if (root_lfixups == -FDT_ERR_NOTFOUND) + root_lfixups = fdt_add_subnode(fdt, 0, "__local_fixups__"); + + if (root_lfixups < 0) + return root_lfixups; + + fdt_for_each_subnode(node, fdto, ov_lfixups) { + int len, child_node, parent_node; + int skip_fdto_child = 0; + const char *name = fdt_get_name(fdto, node, &len); + char buf[MAX_BUF_SIZE]; + + ret = lookup_target_path(fdt, fdto, name, strlen(name), + buf, sizeof(buf), NULL, &root_path); + if (ret) + return ret; + + /* We want to skip dtbo overlay child in two cases + * i.e When target path doesn't have fragment@ or __overlay__ + * strings + */ + if (root_path || find_node_str(buf, "__overlay__")) + skip_fdto_child = 1; + + parent_node = add_to_local_fixups(fdt, buf); + if (parent_node < 0) + return parent_node; + + child_node = fdt_subnode_offset(fdto, node, "__overlay__"); + if (child_node < 0) + return -FDT_ERR_BADOVERLAY; + + ret = copy_node(fdt, fdto, parent_node, child_node, + NULL, skip_fdto_child); + if (ret) + return ret; + } + + return 0; +} + int fdt_overlay_merge(void *fdt, void *fdto, int *fdto_nospace) { uint32_t delta = fdt_get_max_phandle(fdt); @@ -1377,18 +1920,29 @@ int fdt_overlay_merge(void *fdt, void *fdto, int *fdto_nospace) goto err; } - ret = overlay_fixup_phandles(fdt, fdto); - if (ret) + ret = overlay_fixup_phandles(fdt, fdto, max_phandle); + if (ret && ret != -FDT_ERR_NOTFOUND) goto err; ret = overlay_merge(fdt, fdto, &max_phandle); if (ret) goto err; - ret = overlay_symbol_update(fdt, fdto); + max_phandle = delta + delta0 + 1; + ret = overlay_symbol_update(fdt, fdto, max_phandle); if (ret) goto err; + /* fixups node is optional */ + ret = overlay_fixups_update(fdt, fdto, max_phandle); + if (ret && ret != -FDT_ERR_NOTFOUND) + goto err; + + /* local_fixups node is optional */ + ret = overlay_local_fixups_update(fdt, fdto, max_phandle); + if (ret && ret != -FDT_ERR_NOTFOUND) + goto err; + /* * The overlay has been damaged, erase its magic. */ -- 2.49.0