aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorSimon Glass2023-09-26 08:14:43 -0600
committerTom Rini2023-10-06 14:38:13 -0400
commit62b1db33778611a3023d1e3a98e869b495edc9ca (patch)
treea02bcbcf8555af6897638135af9662a53ff591af /lib
parent67fb2159fb3438359fa0fc3f8cb491ffe8d57c0f (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.c122
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;
+}