[PATCH 2/4] initial oftree command support

Sascha Hauer s.hauer at pengutronix.de
Tue Oct 18 07:56:57 EDT 2011


This adds basic device tree command support. So far we can
parse a flat device tree (-p), which also stores the tree
in memory, dump it (-d) and free (-f) the internally stored tree.

The chosen node can be updated with barebox bootargs, no other
device tree manipulation is implemented yet.

Signed-off-by: Sascha Hauer <s.hauer at pengutronix.de>
---
 commands/Kconfig     |    6 +
 commands/Makefile    |    1 +
 commands/oftree.c    |  135 ++++++++++++++++++++++
 common/Makefile      |    1 +
 common/oftree.c      |  312 ++++++++++++++++++++++++++++++++++++++++++++++++++
 include/libfdt_env.h |    3 +-
 include/of.h         |   21 ++++
 lib/Kconfig          |    3 +
 8 files changed, 481 insertions(+), 1 deletions(-)
 create mode 100644 commands/oftree.c
 create mode 100644 common/oftree.c
 create mode 100644 include/of.h

diff --git a/commands/Kconfig b/commands/Kconfig
index 22e2c2e..18ab840 100644
--- a/commands/Kconfig
+++ b/commands/Kconfig
@@ -343,6 +343,12 @@ config CMD_GO
 	tristate
 	prompt "go"
 
+config CMD_OFTREE
+	tristate
+	select OFTREE
+	prompt "oftree"
+	select FDT
+
 endmenu
 
 config CMD_TIMEOUT
diff --git a/commands/Makefile b/commands/Makefile
index 40fb26c..5c51916 100644
--- a/commands/Makefile
+++ b/commands/Makefile
@@ -57,3 +57,4 @@ obj-$(CONFIG_CMD_LED)		+= led.o
 obj-$(CONFIG_CMD_LED_TRIGGER)	+= trigger.o
 obj-$(CONFIG_CMD_USB)		+= usb.o
 obj-$(CONFIG_CMD_TIME)		+= time.o
+obj-$(CONFIG_CMD_OFTREE)	+= oftree.o
diff --git a/commands/oftree.c b/commands/oftree.c
new file mode 100644
index 0000000..d8deb36
--- /dev/null
+++ b/commands/oftree.c
@@ -0,0 +1,135 @@
+/*
+ * oftree.c - device tree command support
+ *
+ * Copyright (c) 2011 Sascha Hauer <s.hauer at pengutronix.de>, Pengutronix
+ *
+ * based on U-Boot code by:
+ *
+ * Gerald Van Baren, Custom IDEAS, vanbaren at cideas.com
+ * Pantelis Antoniou <pantelis.antoniou at gmail.com> and
+ * Matthew McClintock <msm at freescale.com>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <common.h>
+#include <environment.h>
+#include <fdt.h>
+#include <of.h>
+#include <command.h>
+#include <fs.h>
+#include <malloc.h>
+#include <libfdt.h>
+#include <linux/ctype.h>
+#include <asm/byteorder.h>
+#include <errno.h>
+#include <getopt.h>
+#include <init.h>
+
+static int do_oftree(struct command *cmdtp, int argc, char *argv[])
+{
+	struct fdt_header *fdt;
+	int size;
+	int opt;
+	char *file = NULL;
+	const char *node = "/";
+	int dump = 0;
+	int parse = 0;
+
+	while ((opt = getopt(argc, argv, "dpfn:")) > 0) {
+		switch (opt) {
+		case 'd':
+			dump = 1;
+			break;
+		case 'p':
+			parse = 1;
+			break;
+		case 'f':
+			free(barebox_fdt);
+			barebox_fdt = NULL;
+			return 0;
+		case 'n':
+			node = optarg;
+			break;
+		}
+	}
+
+	if (optind < argc)
+		file = argv[optind];
+
+	if (!dump && !parse)
+		return COMMAND_ERROR_USAGE;
+
+	if (dump) {
+		if (file) {
+			fdt = read_file(file, &size);
+			if (!fdt) {
+				printf("unable to read %s\n", file);
+				return 1;
+			}
+
+			fdt_print(fdt, node);
+			free(fdt);
+		} else {
+			if (barebox_fdt) {
+				fdt_print(barebox_fdt, node);
+				return 0;
+			} else {
+				return 1;
+			}
+		}
+		return 0;
+	}
+
+	if (parse) {
+		if (!file)
+			return COMMAND_ERROR_USAGE;
+
+		fdt = read_file(file, &size);
+		if (!fdt) {
+			perror("open");
+			return 1;
+		}
+
+		fdt = xrealloc(fdt, size + 0x8000);
+		fdt_open_into(fdt, fdt, size + 0x8000);
+		if (!fdt) {
+			printf("unable to read %s\n", file);
+			return 1;
+		}
+
+		if (barebox_fdt)
+			free(barebox_fdt);
+
+		barebox_fdt = fdt;
+	}
+
+	return 0;
+}
+
+BAREBOX_CMD_HELP_START(oftree)
+BAREBOX_CMD_HELP_USAGE("oftree [OPTIONS]\n")
+BAREBOX_CMD_HELP_OPT  ("-p <FILE>",  "parse and store oftree from <file>\n")
+BAREBOX_CMD_HELP_OPT  ("-d [FILE]",  "dump oftree from [FILE] or the parsed tree if no file is given\n")
+BAREBOX_CMD_HELP_OPT  ("-f",  "free stored oftree\n")
+BAREBOX_CMD_HELP_END
+
+BAREBOX_CMD_START(oftree)
+	.cmd		= do_oftree,
+	.usage		= "handle oftrees",
+	BAREBOX_CMD_HELP(cmd_oftree_help)
+BAREBOX_CMD_END
diff --git a/common/Makefile b/common/Makefile
index 74946e9..7bb8ea4 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_AUTO_COMPLETE)	+= complete.o
 obj-$(CONFIG_POLLER)		+= poller.o
 obj-$(CONFIG_BLOCK)		+= block.o
 obj-$(CONFIG_CMD_LOADS)		+= s_record.o
+obj-$(CONFIG_OFTREE)		+= oftree.o
 
 obj-y += memory.o
 obj-$(CONFIG_MALLOC_DLMALLOC) += dlmalloc.o
diff --git a/common/oftree.c b/common/oftree.c
new file mode 100644
index 0000000..2a2f464
--- /dev/null
+++ b/common/oftree.c
@@ -0,0 +1,312 @@
+#include <common.h>
+#include <environment.h>
+#include <fdt.h>
+#include <of.h>
+#include <command.h>
+#include <fs.h>
+#include <malloc.h>
+#include <libfdt.h>
+#include <linux/ctype.h>
+#include <asm/byteorder.h>
+#include <errno.h>
+#include <getopt.h>
+#include <init.h>
+
+#define MAX_LEVEL	32		/* how deeply nested we will go */
+
+static int is_printable_string(const void *data, int len)
+{
+	const char *s = data;
+
+	/* zero length is not */
+	if (len == 0)
+		return 0;
+
+	/* must terminate with zero */
+	if (s[len - 1] != '\0')
+		return 0;
+
+	/* printable or a null byte (concatenated strings) */
+	while (((*s == '\0') || isprint(*s)) && (len > 0)) {
+		/*
+		 * If we see a null, there are three possibilities:
+		 * 1) If len == 1, it is the end of the string, printable
+		 * 2) Next character also a null, not printable.
+		 * 3) Next character not a null, continue to check.
+		 */
+		if (s[0] == '\0') {
+			if (len == 1)
+				return 1;
+			if (s[1] == '\0')
+				return 0;
+		}
+		s++;
+		len--;
+	}
+
+	/* Not the null termination, or not done yet: not printable */
+	if (*s != '\0' || (len != 0))
+		return 0;
+
+	return 1;
+}
+
+/*
+ * Print the property in the best format, a heuristic guess.  Print as
+ * a string, concatenated strings, a byte, word, double word, or (if all
+ * else fails) it is printed as a stream of bytes.
+ */
+static void print_data(const void *data, int len)
+{
+	int j;
+
+	/* no data, don't print */
+	if (len == 0)
+		return;
+
+	/*
+	 * It is a string, but it may have multiple strings (embedded '\0's).
+	 */
+	if (is_printable_string(data, len)) {
+		puts("\"");
+		j = 0;
+		while (j < len) {
+			if (j > 0)
+				puts("\", \"");
+			puts(data);
+			j    += strlen(data) + 1;
+			data += strlen(data) + 1;
+		}
+		puts("\"");
+		return;
+	}
+
+	if ((len % 4) == 0) {
+		const u32 *p;
+
+		printf("<");
+		for (j = 0, p = data; j < len/4; j ++)
+			printf("0x%x%s", fdt32_to_cpu(p[j]), j < (len/4 - 1) ? " " : "");
+		printf(">");
+	} else { /* anything else... hexdump */
+		const u8 *s;
+
+		printf("[");
+		for (j = 0, s = data; j < len; j++)
+			printf("%02x%s", s[j], j < len - 1 ? " " : "");
+		printf("]");
+	}
+}
+
+static void printf_indent(int level, const char *fmt, ...)
+{
+	va_list args;
+
+	printf("%*s", level * 8, "");
+
+	va_start (args, fmt);
+	vprintf(fmt, args);
+	va_end (args);
+}
+
+int fdt_print(struct fdt_header *working_fdt, const char *pathp)
+{
+	const void *nodep;	/* property node pointer */
+	int  nodeoffset;	/* node offset from libfdt */
+	int  nextoffset;	/* next node offset from libfdt */
+	uint32_t tag;		/* tag */
+	int  len;		/* length of the property */
+	int  level = 0;		/* keep track of nesting level */
+	const struct fdt_property *fdt_prop;
+
+	nodeoffset = fdt_path_offset(working_fdt, pathp);
+	if (nodeoffset < 0) {
+		/*
+		 * Not found or something else bad happened.
+		 */
+		printf("libfdt fdt_path_offset() returned %s\n",
+			fdt_strerror(nodeoffset));
+		return 1;
+	}
+
+	while (level >= 0) {
+		tag = fdt_next_tag(working_fdt, nodeoffset, &nextoffset);
+		switch (tag) {
+		case FDT_BEGIN_NODE:
+			pathp = fdt_get_name(working_fdt, nodeoffset, NULL);
+			if (pathp == NULL)
+				pathp = "/* NULL pointer error */";
+			if (*pathp == '\0')
+				pathp = "/";	/* root is nameless */
+			printf_indent(level, "%s {\n",pathp);
+			level++;
+			if (level >= MAX_LEVEL) {
+				printf("Nested too deep, aborting.\n");
+				return 1;
+			}
+			break;
+		case FDT_END_NODE:
+			level--;
+			printf_indent(level, "};\n");
+			if (level == 0) {
+				level = -1;		/* exit the loop */
+			}
+			break;
+		case FDT_PROP:
+			fdt_prop = fdt_offset_ptr(working_fdt, nodeoffset,
+					sizeof(*fdt_prop));
+			pathp    = fdt_string(working_fdt,
+					fdt32_to_cpu(fdt_prop->nameoff));
+			len      = fdt32_to_cpu(fdt_prop->len);
+			nodep    = fdt_prop->data;
+			if (len < 0) {
+				printf("libfdt fdt_getprop(): %s\n",
+					fdt_strerror(len));
+				return 1;
+			} else if (len == 0) {
+				/* the property has no value */
+				printf_indent(level, "%s;\n", pathp);
+			} else {
+				printf_indent(level, "%s = ", pathp);
+				print_data(nodep, len);
+				printf(";\n");
+			}
+			break;
+		case FDT_NOP:
+			printf_indent(level, "/* NOP */\n");
+			break;
+		case FDT_END:
+			return 1;
+		default:
+			printf("Unknown tag 0x%08X\n", tag);
+			return 1;
+		}
+		nodeoffset = nextoffset;
+	}
+	return 0;
+}
+
+/**
+ * fdt_find_and_setprop: Find a node and set it's property
+ *
+ * @fdt: ptr to device tree
+ * @node: path of node
+ * @prop: property name
+ * @val: ptr to new value
+ * @len: length of new property value
+ * @create: flag to create the property if it doesn't exist
+ *
+ * Convenience function to directly set a property given the path to the node.
+ */
+int fdt_find_and_setprop(struct fdt_header *fdt, const char *node,
+		const char *prop, const void *val, int len, int create)
+{
+	int nodeoff = fdt_path_offset(fdt, node);
+
+	if (nodeoff < 0)
+		return nodeoff;
+
+	if ((!create) && (fdt_get_property(fdt, nodeoff, prop, 0) == NULL))
+		return 0; /* create flag not set; so exit quietly */
+
+	return fdt_setprop(fdt, nodeoff, prop, val, len);
+}
+
+void do_fixup_by_path(struct fdt_header *fdt, const char *path,
+		const char *prop, const void *val, int len, int create)
+{
+	int rc = fdt_find_and_setprop(fdt, path, prop, val, len, create);
+	if (rc)
+		printf("Unable to update property %s:%s, err=%s\n",
+			path, prop, fdt_strerror(rc));
+}
+
+void do_fixup_by_path_u32(struct fdt_header *fdt, const char *path,
+		const char *prop, u32 val, int create)
+{
+	val = cpu_to_fdt32(val);
+	do_fixup_by_path(fdt, path, prop, &val, sizeof(val), create);
+}
+
+int fdt_get_path_or_create(struct fdt_header *fdt, const char *path)
+{
+	int nodeoffset;
+
+	nodeoffset = fdt_path_offset (fdt, path);
+	if (nodeoffset < 0) {
+		nodeoffset = fdt_add_subnode(fdt, 0, path + 1);
+		if (nodeoffset < 0) {
+			printf("WARNING: could not create %s %s.\n",
+					path, fdt_strerror(nodeoffset));
+                        return -EINVAL;
+                }
+        }
+
+	return nodeoffset;
+}
+
+struct fdt_header *barebox_fdt;
+
+static int of_fixup_bootargs(struct fdt_header *fdt)
+{
+	int nodeoffset;
+	const char *str;
+	int err;
+
+	nodeoffset = fdt_get_path_or_create(fdt, "/chosen");
+	if (nodeoffset < 0)
+		return nodeoffset;
+
+	str = getenv("bootargs");
+	if (str) {
+		err = fdt_setprop(fdt, nodeoffset,
+				"bootargs", str, strlen(str)+1);
+		if (err < 0)
+			printf("WARNING: could not set bootargs %s.\n",
+					fdt_strerror(err));
+        }
+
+	return 0;
+}
+
+static int of_register_bootargs_fixup(void)
+{
+	return of_register_fixup(of_fixup_bootargs);
+}
+late_initcall(of_register_bootargs_fixup);
+
+struct of_fixup {
+	int (*fixup)(struct fdt_header *);
+	struct list_head list;
+};
+
+static LIST_HEAD(of_fixup_list);
+
+int of_register_fixup(int (*fixup)(struct fdt_header *))
+{
+	struct of_fixup *of_fixup = xzalloc(sizeof(*of_fixup));
+
+	of_fixup->fixup = fixup;
+
+	list_add_tail(&of_fixup->list, &of_fixup_list);
+
+	return 0;
+}
+
+struct fdt_header *of_get_fixed_tree(void)
+{
+	struct of_fixup *of_fixup;
+	int ret;
+
+	if (!barebox_fdt)
+		return NULL;
+
+	list_for_each_entry(of_fixup, &of_fixup_list, list) {
+		ret = of_fixup->fixup(barebox_fdt);
+		if (ret)
+			return NULL;
+	}
+
+	return barebox_fdt;
+}
+
diff --git a/include/libfdt_env.h b/include/libfdt_env.h
index 449bf60..aa8fb5f 100644
--- a/include/libfdt_env.h
+++ b/include/libfdt_env.h
@@ -2,9 +2,10 @@
 #define _LIBFDT_ENV_H
 
 #include <stddef.h>
-#include <stdint.h>
 #include <string.h>
 
+typedef unsigned long int     uintptr_t;
+
 #define _B(n)	((unsigned long long)((uint8_t *)&x)[n])
 static inline uint32_t fdt32_to_cpu(uint32_t x)
 {
diff --git a/include/of.h b/include/of.h
new file mode 100644
index 0000000..c2661ef
--- /dev/null
+++ b/include/of.h
@@ -0,0 +1,21 @@
+#ifndef __OF_H
+#define __OF_H
+
+#include <fdt.h>
+
+extern struct fdt_header *barebox_fdt;
+
+int fdt_print(struct fdt_header *working_fdt, const char *pathp);
+
+struct fdt_header *of_get_fixed_tree(void);
+int of_register_fixup(int (*fixup)(struct fdt_header *));
+
+int fdt_find_and_setprop(struct fdt_header *fdt, const char *node, const char *prop,
+			 const void *val, int len, int create);
+void do_fixup_by_path(struct fdt_header *fdt, const char *path, const char *prop,
+		      const void *val, int len, int create);
+void do_fixup_by_path_u32(struct fdt_header *fdt, const char *path, const char *prop,
+			  u32 val, int create);
+int fdt_get_path_or_create(struct fdt_header *fdt, const char *path);
+
+#endif /* __OF_H */
diff --git a/lib/Kconfig b/lib/Kconfig
index c7dc161..531398f 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -15,3 +15,6 @@ source lib/lzo/Kconfig
 config FDT
 	bool
 
+config OFTREE
+	select FDT
+	bool
-- 
1.7.7




More information about the barebox mailing list