diff options
author | Simon Glass | 2023-09-26 08:14:43 -0600 |
---|---|---|
committer | Tom Rini | 2023-10-06 14:38:13 -0400 |
commit | 62b1db33778611a3023d1e3a98e869b495edc9ca (patch) | |
tree | a02bcbcf8555af6897638135af9662a53ff591af /lib | |
parent | 67fb2159fb3438359fa0fc3f8cb491ffe8d57c0f (diff) |
dm: core: Add a way to convert a devicetree to a dtb
Add a way to flatten a devicetree into binary form. For livetree this
involves generating the devicetree using fdt_property() and other calls.
For flattree it simply involves providing the buffer containing the tree.
Signed-off-by: Simon Glass <sjg@chromium.org>
Diffstat (limited to 'lib')
-rw-r--r-- | lib/of_live.c | 122 |
1 files changed, 122 insertions, 0 deletions
diff --git a/lib/of_live.c b/lib/of_live.c index e4eee385547..812c488f606 100644 --- a/lib/of_live.c +++ b/lib/of_live.c @@ -8,13 +8,21 @@ * Copyright (c) 2017 Google, Inc */ +#define LOG_CATEGORY LOGC_DT + #include <common.h> +#include <abuf.h> #include <log.h> #include <linux/libfdt.h> #include <of_live.h> #include <malloc.h> #include <dm/of_access.h> #include <linux/err.h> +#include <linux/sizes.h> + +enum { + BUF_STEP = SZ_64K, +}; static void *unflatten_dt_alloc(void **mem, unsigned long size, unsigned long align) @@ -355,3 +363,117 @@ int of_live_create_empty(struct device_node **rootp) return 0; } + +static int check_space(int ret, struct abuf *buf) +{ + if (ret == -FDT_ERR_NOSPACE) { + if (!abuf_realloc_inc(buf, BUF_STEP)) + return log_msg_ret("spc", -ENOMEM); + ret = fdt_resize(abuf_data(buf), abuf_data(buf), + abuf_size(buf)); + if (ret) + return log_msg_ret("res", -EFAULT); + + return -EAGAIN; + } + + return 0; +} + +/** + * flatten_node() - Write out the node and its properties into a flat tree + */ +static int flatten_node(struct abuf *buf, const struct device_node *node) +{ + const struct device_node *np; + const struct property *pp; + int ret; + + ret = fdt_begin_node(abuf_data(buf), node->name); + ret = check_space(ret, buf); + if (ret == -EAGAIN) { + ret = fdt_begin_node(abuf_data(buf), node->name); + if (ret) { + log_debug("Internal error a %d\n", ret); + return -EFAULT; + } + } + if (ret) + return log_msg_ret("beg", ret); + + /* First write out the properties */ + for (pp = node->properties; !ret && pp; pp = pp->next) { + ret = fdt_property(abuf_data(buf), pp->name, pp->value, + pp->length); + ret = check_space(ret, buf); + if (ret == -EAGAIN) { + ret = fdt_property(abuf_data(buf), pp->name, pp->value, + pp->length); + } + } + + /* Next write out the subnodes */ + for (np = node->child; np; np = np->sibling) { + ret = flatten_node(buf, np); + if (ret) + return log_msg_ret("sub", ret); + } + + ret = fdt_end_node(abuf_data(buf)); + ret = check_space(ret, buf); + if (ret == -EAGAIN) { + ret = fdt_end_node(abuf_data(buf)); + if (ret) { + log_debug("Internal error b %d\n", ret); + return -EFAULT; + } + } + if (ret) + return log_msg_ret("end", ret); + + return 0; +} + +int of_live_flatten(const struct device_node *root, struct abuf *buf) +{ + int ret; + + abuf_init(buf); + if (!abuf_realloc(buf, BUF_STEP)) + return log_msg_ret("ini", -ENOMEM); + + ret = fdt_create(abuf_data(buf), abuf_size(buf)); + if (!ret) + ret = fdt_finish_reservemap(abuf_data(buf)); + if (ret) { + log_debug("Failed to start FDT (err=%d)\n", ret); + return log_msg_ret("sta", -EINVAL); + } + + ret = flatten_node(buf, root); + if (ret) + return log_msg_ret("flt", ret); + + ret = fdt_finish(abuf_data(buf)); + ret = check_space(ret, buf); + if (ret == -EAGAIN) { + ret = fdt_finish(abuf_data(buf)); + if (ret) { + log_debug("Internal error c %d\n", ret); + return -EFAULT; + } + } + if (ret) + return log_msg_ret("fin", ret); + + ret = fdt_pack(abuf_data(buf)); + if (ret) { + log_debug("Failed to pack (err=%d)\n", ret); + return log_msg_ret("pac", -EFAULT); + } + + if (!abuf_realloc(buf, fdt_totalsize(abuf_data(buf)))) + return log_msg_ret("abu", -EFAULT); + + return 0; +} |