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.hershber...@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 _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot