[U-Boot] [PATCH 6/6] fdt: Add a Linux tool for reading values from FDT files
Joe Hershberger
joe.hershberger at ni.com
Fri Aug 17 22:34:40 CEST 2012
Designed to be able to access itb files on a filesystem or an mtd
partition.
Supports print and list (like the fdt command) and also offset for
finding the offset and size of a given property in an FDT file.
This is especially helpful when reading properties from an ITB file.
Signed-off-by: Joe Hershberger <joe.hershberger at ni.com>
---
Makefile | 2 +-
tools/fdtview/Makefile | 52 ++++++
tools/fdtview/fdtview.c | 483 ++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 536 insertions(+), 1 deletion(-)
create mode 100644 tools/fdtview/Makefile
create mode 100644 tools/fdtview/fdtview.c
diff --git a/Makefile b/Makefile
index 5ce5cc3..163fb4c 100644
--- a/Makefile
+++ b/Makefile
@@ -659,7 +659,7 @@ $(TIMESTAMP_FILE):
@LC_ALL=C date +'#define U_BOOT_TIME "%T"' >> $@.tmp
@cmp -s $@ $@.tmp && rm -f $@.tmp || mv -f $@.tmp $@
-easylogo env gdb:
+easylogo env fdtview gdb:
$(MAKE) -C tools/$@ all MTD_VERSION=${MTD_VERSION}
gdbtools: gdb
diff --git a/tools/fdtview/Makefile b/tools/fdtview/Makefile
new file mode 100644
index 0000000..5f1488b
--- /dev/null
+++ b/tools/fdtview/Makefile
@@ -0,0 +1,52 @@
+
+include $(TOPDIR)/config.mk
+
+# Source files which exist outside the tools/fdtview directory
+EXT_SRC_FILES-y += lib/crc32.c
+EXT_SRC_FILES-y += lib/ctype.c
+EXT_SRC_FILES-y += lib/md5.c
+EXT_SRC_FILES-y += lib/sha1.c
+EXT_SRC_FILES-y += common/image.c
+
+# Source files located in the tools/fdtview directory
+SRC_FILES-y += fdtview.c
+
+# Flattened device tree objects
+LIBFDT_SRC_FILES-y += fdt.c
+LIBFDT_SRC_FILES-y += fdt_ro.c
+LIBFDT_SRC_FILES-y += fdt_rw.c
+LIBFDT_SRC_FILES-y += fdt_strerror.c
+LIBFDT_SRC_FILES-y += fdt_wip.c
+
+HOSTSRCS += $(addprefix $(SRCTREE)/,$(EXT_SRC_FILES-y))
+HOSTSRCS += $(addprefix $(SRCTREE)/tools/fdtview/,$(SRC_FILES-y))
+HOSTSRCS += $(addprefix $(SRCTREE)/lib/libfdt/,$(LIBFDT_SRC_FILES-y))
+
+$(warning $(HOSTSRCS))
+#
+# Use native tools and options
+# Define __KERNEL_STRICT_NAMES to prevent typedef overlaps
+#
+HOSTCPPFLAGS = -idirafter $(SRCTREE)/include \
+ -idirafter $(OBJTREE)/include2 \
+ -idirafter $(OBJTREE)/include \
+ -I $(SRCTREE)/libfdt \
+ -I $(SRCTREE)/tools \
+ -DUSE_HOSTCC -D__KERNEL_STRICT_NAMES
+
+all : $(obj)fdtview$(SFX)
+
+$(obj)fdtview$(SFX) : $(HOSTSRCS)
+ $(HOSTCC) $(HOSTCFLAGS_NOPED) $(HOSTLDFLAGS) -o $@ $(HOSTSRCS)
+ $(HOSTSTRIP) $@
+
+clean:
+ rm -rf $(obj)*.o $(obj)fdtview
+
+#########################################################################
+
+include $(TOPDIR)/rules.mk
+
+sinclude $(obj).depend
+
+#########################################################################
diff --git a/tools/fdtview/fdtview.c b/tools/fdtview/fdtview.c
new file mode 100644
index 0000000..70b3243
--- /dev/null
+++ b/tools/fdtview/fdtview.c
@@ -0,0 +1,483 @@
+
+#include "os_support.h"
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <unistd.h>
+#include <sha1.h>
+#include "fdt_host.h"
+#include <image.h>
+#include <linux/ctype.h>
+#include <mtd/mtd-user.h>
+
+#define MAX_LEVEL 32 /* how deeply nested we will go */
+
+static void usage(void);
+
+struct fdtview_params {
+ char *path;
+ char *propname;
+ char *imagefile;
+ char *cmdname;
+ int lflag;
+ int oflag;
+ int pflag;
+} params;
+
+/*
+ * Heuristic to guess if this is a string or concatenated strings.
+ */
+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' && s[len - 1] != '\n')
+ return 0;
+
+ /* printable or a null byte (concatenated strings) */
+ while (((*s == '\0') || isprint(*s) || isspace(*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)) {
+ printf("\"");
+ j = 0;
+ while (j < len) {
+ if (j > 0)
+ printf("\", \"");
+ printf(data);
+ j += strlen(data) + 1;
+ data += strlen(data) + 1;
+ }
+ printf("\"");
+ return;
+ }
+
+ if ((len % 4) == 0) {
+ const unsigned int *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 unsigned char *s;
+
+ printf("[");
+ for (j = 0, s = data; j < len; j++)
+ printf("%02x%s", s[j], j < len - 1 ? " " : "");
+ printf("]");
+ }
+}
+
+/*
+ * Recursively print (a portion of) the working_fdt. The depth parameter
+ * determines how deeply nested the fdt is printed.
+ */
+static int fdt_print(unsigned char *working_fdt, const char *pathp,
+ char *prop, int depth)
+{
+ static char tabs[MAX_LEVEL+1] =
+ "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"
+ "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
+ 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;
+ }
+ /*
+ * The user passed in a property as well as node path.
+ * Print only the given property and then return.
+ */
+ if (prop) {
+ nodep = fdt_getprop(working_fdt, nodeoffset, prop, &len);
+ if (len == 0) {
+ /* no property value */
+ printf("%s %s\n", pathp, prop);
+ return 0;
+ } else if (len > 0) {
+ printf("%s = ", prop);
+ print_data(nodep, len);
+ printf("\n");
+ return 0;
+ } else {
+ printf("libfdt fdt_getprop(): %s\n",
+ fdt_strerror(len));
+ return 1;
+ }
+ }
+
+ /*
+ * The user passed in a node path and no property,
+ * print the node and all subnodes.
+ */
+ 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 (level <= depth) {
+ if (pathp == NULL)
+ pathp = "/* NULL pointer error */";
+ if (*pathp == '\0')
+ pathp = "/"; /* root is nameless */
+ printf("%s%s {\n",
+ &tabs[MAX_LEVEL - level], pathp);
+ }
+ level++;
+ if (level >= MAX_LEVEL) {
+ printf("Nested too deep, aborting.\n");
+ return 1;
+ }
+ break;
+ case FDT_END_NODE:
+ level--;
+ if (level <= depth)
+ printf("%s};\n", &tabs[MAX_LEVEL - level]);
+ 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 */
+ if (level <= depth)
+ printf("%s%s;\n",
+ &tabs[MAX_LEVEL - level],
+ pathp);
+ } else {
+ if (level <= depth) {
+ printf("%s%s = ",
+ &tabs[MAX_LEVEL - level],
+ pathp);
+ print_data(nodep, len);
+ printf(";\n");
+ }
+ }
+ break;
+ case FDT_NOP:
+ printf("%s/* NOP */\n", &tabs[MAX_LEVEL - level]);
+ break;
+ case FDT_END:
+ return 1;
+ default:
+ if (level <= depth)
+ printf("Unknown tag 0x%08X\n", tag);
+ return 1;
+ }
+ nodeoffset = nextoffset;
+ }
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ int ifd = -1;
+ struct stat sbuf;
+ unsigned char *working_fdt;
+ int retval = 0;
+ struct mtd_info_user mtd;
+ off_t file_size = 0;
+ int mmap_failed = 0;
+
+ params.cmdname = *argv;
+ params.lflag = 0;
+ params.oflag = 0;
+ params.pflag = 0;
+
+ while (--argc > 0 && **++argv == '-') {
+ while (*++*argv) {
+ switch (**argv) {
+ case 'l':
+ if (argc <= 1)
+ usage();
+ if (argc >= 3) {
+ params.path = *++argv;
+ --argc;
+ } else {
+ params.path = NULL;
+ }
+ if (argc == 3) {
+ params.propname = *++argv;
+ --argc;
+ } else {
+ params.propname = NULL;
+ }
+ params.lflag = 1;
+ goto NXTARG;
+ case 'o':
+ --argc;
+ if (--argc <= 1)
+ usage();
+ params.path = *++argv;
+ params.propname = *++argv;
+ params.oflag = 1;
+ goto NXTARG;
+ case 'p':
+ if (argc <= 1)
+ usage();
+ if (argc >= 3) {
+ params.path = *++argv;
+ --argc;
+ } else {
+ params.path = NULL;
+ }
+ if (argc == 3) {
+ params.propname = *++argv;
+ --argc;
+ } else {
+ params.propname = NULL;
+ }
+ params.pflag = 1;
+ goto NXTARG;
+ default:
+ usage();
+ }
+ }
+NXTARG:;
+ }
+
+ if (argc != 1)
+ usage();
+
+ if (!params.lflag && !params.oflag && !params.pflag)
+ usage();
+
+ params.imagefile = *argv;
+
+ ifd = open(params.imagefile, O_RDONLY|O_BINARY|O_SYNC);
+
+ if (ifd < 0) {
+ fprintf(stderr, "%s: Can't open %s: %s\n",
+ params.cmdname, params.imagefile,
+ strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ if (fstat(ifd, &sbuf) < 0) {
+ fprintf(stderr, "%s: Can't stat %s: %s\n",
+ params.cmdname, params.imagefile,
+ strerror(errno));
+ exit(EXIT_FAILURE);
+ } else {
+ file_size = sbuf.st_size;
+ }
+
+ if ((sbuf.st_rdev & 0xFF00) == 0x1F00) {
+ fprintf(stderr, "%s: Can't access the block interface to %s\n",
+ params.cmdname, params.imagefile);
+ exit(EXIT_FAILURE);
+ }
+ if ((sbuf.st_rdev & 0xFF00) == 0x5A00) {
+ if (ioctl(ifd, MEMGETINFO, mtd) < 0) {
+ fprintf(stderr, "%s: Can't query mem info for %s: %s\n",
+ params.cmdname, params.imagefile,
+ strerror(errno));
+ exit(EXIT_FAILURE);
+ } else {
+ file_size = mtd.size;
+ }
+ }
+
+ if ((unsigned)file_size < sizeof(image_header_t)) {
+ fprintf(stderr,
+ "%s: Bad size: \"%s\" is not valid image\n",
+ params.cmdname, params.imagefile);
+ exit(EXIT_FAILURE);
+ }
+
+ working_fdt = mmap(0, file_size, PROT_READ, MAP_SHARED, ifd, 0);
+ if (working_fdt == MAP_FAILED) {
+ int readsize;
+ int totalsize;
+ mmap_failed = 1;
+
+ printf("mmap() failed. Falling back to read.");
+ working_fdt = malloc(sizeof(image_header_t));
+ if (working_fdt == NULL) {
+ fprintf(stderr, "%s: malloc of %d"
+ " bytes (header) failed: %s\n",
+ params.cmdname, (int) file_size,
+ strerror(errno));
+ retval = 1;
+ goto error;
+ }
+ readsize = read(ifd, working_fdt, sizeof(image_header_t));
+ if (readsize < 0) {
+ fprintf(stderr, "%s: Can't read header from %s: %s\n",
+ params.cmdname, params.imagefile,
+ strerror(errno));
+ retval = readsize;
+ goto error;
+ }
+ retval = fdt_check_header((void *) working_fdt);
+ if (retval < 0) {
+ fprintf(stderr, "%s: Invalid image %s\n",
+ params.cmdname, params.imagefile);
+ goto error;
+ }
+ totalsize = fdt_totalsize(working_fdt);
+ if (totalsize < 0) {
+ fprintf(stderr, "%s: Invalid image size: %d\n",
+ params.cmdname, totalsize);
+ retval = totalsize;
+ goto error;
+ }
+ if (file_size > totalsize)
+ file_size = totalsize;
+ working_fdt = realloc(working_fdt, file_size);
+ if (working_fdt == NULL) {
+ fprintf(stderr, "%s: malloc of %d bytes failed: %s\n",
+ params.cmdname, (int) file_size,
+ strerror(errno));
+ retval = 1;
+ goto error;
+ }
+ if (read(ifd, working_fdt + readsize,
+ file_size - readsize) < 0) {
+ fprintf(stderr, "%s: Can't read %s: %s\n",
+ params.cmdname, params.imagefile,
+ strerror(errno));
+ retval = readsize;
+ goto error;
+ }
+ }
+
+ retval = fdt_check_header((void *)working_fdt);
+ if (retval < 0) {
+ fprintf(stderr, "%s: Invalid image %s\n",
+ params.cmdname, params.imagefile);
+ goto error;
+ }
+
+ if (params.oflag) {
+ int offset;
+ offset = fdt_path_offset(working_fdt, params.path);
+ if (offset > 0) {
+ int propLength = 0;
+ const unsigned char *propData = (const uint8_t *)
+ fdt_getprop(working_fdt, offset,
+ params.propname, &propLength);
+ if (propLength >= 0)
+ printf("%d %d\n", propData - working_fdt,
+ propLength);
+ } else {
+ retval = offset;
+ goto error;
+ }
+ } else if (params.lflag || params.pflag) {
+ int depth = MAX_LEVEL; /* how deep to print */
+ int ret; /* return value */
+ static char root[2] = "/";
+
+ /*
+ * list is an alias for print, but limited to 1 level
+ */
+ if (params.lflag)
+ depth = 1;
+
+ /*
+ * Get the starting path. The root node is an oddball,
+ * the offset is zero and has no name.
+ */
+ if (params.path == NULL)
+ params.path = root;
+
+ ret = fdt_print(working_fdt, params.path, params.propname,
+ depth);
+ if (ret != 0)
+ return ret;
+ }
+
+error:
+ if (mmap_failed)
+ free(working_fdt);
+ else
+ munmap((void *)working_fdt, file_size);
+
+ close(ifd);
+
+ exit(retval);
+}
+
+
+static void usage()
+{
+ fprintf(stderr, " %s [-l [path [propname]] | -o path propname |"
+ " -p [path [propname]]] image\n"
+ " -l ==> Print one level starting at <path>\n"
+ " -o ==> print the offset and size of a property\n"
+ " -p ==> Recursive print starting at <path>\n",
+ params.cmdname);
+
+ exit(EXIT_FAILURE);
+}
--
1.7.11.5
More information about the U-Boot
mailing list