mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-24 13:43:28 +00:00
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>
This commit is contained in:
parent
67fb2159fb
commit
62b1db3377
5 changed files with 187 additions and 0 deletions
|
@ -243,6 +243,24 @@ int oftree_new(oftree *treep)
|
||||||
|
|
||||||
#endif /* OFNODE_MULTI_TREE */
|
#endif /* OFNODE_MULTI_TREE */
|
||||||
|
|
||||||
|
int oftree_to_fdt(oftree tree, struct abuf *buf)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (of_live_active()) {
|
||||||
|
ret = of_live_flatten(ofnode_to_np(oftree_root(tree)), buf);
|
||||||
|
if (ret)
|
||||||
|
return log_msg_ret("flt", ret);
|
||||||
|
} else {
|
||||||
|
void *fdt = oftree_lookup_fdt(tree);
|
||||||
|
|
||||||
|
abuf_init(buf);
|
||||||
|
abuf_set(buf, fdt, fdt_totalsize(fdt));
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ofnode_from_tree_offset() - get an ofnode from a tree offset (flat tree)
|
* ofnode_from_tree_offset() - get an ofnode from a tree offset (flat tree)
|
||||||
*
|
*
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
/* Enable checks to protect against invalid calls */
|
/* Enable checks to protect against invalid calls */
|
||||||
#undef OF_CHECKS
|
#undef OF_CHECKS
|
||||||
|
|
||||||
|
struct abuf;
|
||||||
struct resource;
|
struct resource;
|
||||||
|
|
||||||
#include <dm/ofnode_decl.h>
|
#include <dm/ofnode_decl.h>
|
||||||
|
@ -136,6 +137,18 @@ static inline ofnode noffset_to_ofnode(ofnode other_node, int of_offset)
|
||||||
*/
|
*/
|
||||||
int oftree_new(oftree *treep);
|
int oftree_new(oftree *treep);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* oftree_to_fdt() - Convert an oftree to a flat FDT
|
||||||
|
*
|
||||||
|
* @tree: tree to flatten (if livetree) or copy (if not)
|
||||||
|
* @buf: Returns inited buffer containing the newly created flat tree. Note
|
||||||
|
* that for flat tree the buffer is not allocated. In either case the caller
|
||||||
|
* must call abut_uninit() to free any memory used by @buf
|
||||||
|
* Return: 0 on success, -ENOMEM if out of memory, other -ve value for any other
|
||||||
|
* error
|
||||||
|
*/
|
||||||
|
int oftree_to_fdt(oftree tree, struct abuf *buf);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ofnode_to_np() - convert an ofnode to a live DT node pointer
|
* ofnode_to_np() - convert an ofnode to a live DT node pointer
|
||||||
*
|
*
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#ifndef _OF_LIVE_H
|
#ifndef _OF_LIVE_H
|
||||||
#define _OF_LIVE_H
|
#define _OF_LIVE_H
|
||||||
|
|
||||||
|
struct abuf;
|
||||||
struct device_node;
|
struct device_node;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -54,4 +55,13 @@ void of_live_free(struct device_node *root);
|
||||||
*/
|
*/
|
||||||
int of_live_create_empty(struct device_node **rootp);
|
int of_live_create_empty(struct device_node **rootp);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* of_live_flatten() - Create an FDT from a hierarchical tree
|
||||||
|
*
|
||||||
|
* @root: Root node of tree to convert
|
||||||
|
* @buf: Buffer to return the tree (inited by this function)
|
||||||
|
* Return: 0 if OK, -ENOMEM if out of memory
|
||||||
|
*/
|
||||||
|
int of_live_flatten(const struct device_node *root, struct abuf *buf);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
122
lib/of_live.c
122
lib/of_live.c
|
@ -8,13 +8,21 @@
|
||||||
* Copyright (c) 2017 Google, Inc
|
* Copyright (c) 2017 Google, Inc
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#define LOG_CATEGORY LOGC_DT
|
||||||
|
|
||||||
#include <common.h>
|
#include <common.h>
|
||||||
|
#include <abuf.h>
|
||||||
#include <log.h>
|
#include <log.h>
|
||||||
#include <linux/libfdt.h>
|
#include <linux/libfdt.h>
|
||||||
#include <of_live.h>
|
#include <of_live.h>
|
||||||
#include <malloc.h>
|
#include <malloc.h>
|
||||||
#include <dm/of_access.h>
|
#include <dm/of_access.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
|
#include <linux/sizes.h>
|
||||||
|
|
||||||
|
enum {
|
||||||
|
BUF_STEP = SZ_64K,
|
||||||
|
};
|
||||||
|
|
||||||
static void *unflatten_dt_alloc(void **mem, unsigned long size,
|
static void *unflatten_dt_alloc(void **mem, unsigned long size,
|
||||||
unsigned long align)
|
unsigned long align)
|
||||||
|
@ -355,3 +363,117 @@ int of_live_create_empty(struct device_node **rootp)
|
||||||
|
|
||||||
return 0;
|
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;
|
||||||
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <common.h>
|
#include <common.h>
|
||||||
|
#include <abuf.h>
|
||||||
#include <dm.h>
|
#include <dm.h>
|
||||||
#include <log.h>
|
#include <log.h>
|
||||||
#include <of_live.h>
|
#include <of_live.h>
|
||||||
|
@ -26,6 +27,7 @@
|
||||||
#include <dm/root.h>
|
#include <dm/root.h>
|
||||||
#include <dm/test.h>
|
#include <dm/test.h>
|
||||||
#include <dm/uclass-internal.h>
|
#include <dm/uclass-internal.h>
|
||||||
|
#include <linux/sizes.h>
|
||||||
#include <test/test.h>
|
#include <test/test.h>
|
||||||
#include <test/ut.h>
|
#include <test/ut.h>
|
||||||
|
|
||||||
|
@ -1456,3 +1458,25 @@ static int dm_test_ofnode_delete(struct unit_test_state *uts)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
DM_TEST(dm_test_ofnode_delete, UT_TESTF_SCAN_FDT);
|
DM_TEST(dm_test_ofnode_delete, UT_TESTF_SCAN_FDT);
|
||||||
|
|
||||||
|
static int dm_test_oftree_to_fdt(struct unit_test_state *uts)
|
||||||
|
{
|
||||||
|
oftree tree, check;
|
||||||
|
struct abuf buf, buf2;
|
||||||
|
|
||||||
|
tree = oftree_default();
|
||||||
|
ut_assertok(oftree_to_fdt(tree, &buf));
|
||||||
|
ut_assert(abuf_size(&buf) > SZ_16K);
|
||||||
|
|
||||||
|
/* convert it back to a tree and see if it looks OK */
|
||||||
|
check = oftree_from_fdt(abuf_data(&buf));
|
||||||
|
ut_assert(oftree_valid(check));
|
||||||
|
|
||||||
|
ut_assertok(oftree_to_fdt(check, &buf2));
|
||||||
|
ut_assert(abuf_size(&buf2) > SZ_16K);
|
||||||
|
ut_asserteq(abuf_size(&buf), abuf_size(&buf2));
|
||||||
|
ut_asserteq_mem(abuf_data(&buf), abuf_data(&buf2), abuf_size(&buf));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
DM_TEST(dm_test_oftree_to_fdt, UT_TESTF_SCAN_FDT);
|
||||||
|
|
Loading…
Reference in a new issue