X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=common%2Fcmd_fdt.c;h=edefd77733177f23068e775af45d9e02c6aef4e2;hb=fb7db41cd46e96621fd97b827622668ee2c6b845;hp=08fe3512d4fe6ff8f3a95c328413c4f352d8f4ae;hpb=6f1d57c567824b7e525ea36f972b662d67382f73;p=oweals%2Fu-boot.git diff --git a/common/cmd_fdt.c b/common/cmd_fdt.c index 08fe3512d4..edefd77733 100644 --- a/common/cmd_fdt.c +++ b/common/cmd_fdt.c @@ -28,267 +28,399 @@ #include #include #include - -#ifdef CONFIG_OF_LIBFDT - #include -#include #include #include +#include #define MAX_LEVEL 32 /* how deeply nested we will go */ -#define SCRATCHPAD 1024 /* bytes of scratchpad memory */ +#define SCRATCHPAD 1024 /* bytes of scratchpad memory */ +#ifndef CONFIG_CMD_FDT_MAX_DUMP +#define CONFIG_CMD_FDT_MAX_DUMP 64 +#endif /* * Global data (for the gd->bd) */ DECLARE_GLOBAL_DATA_PTR; +static int fdt_valid(struct fdt_header **blobp); +static int fdt_parse_prop(char *const*newval, int count, char *data, int *len); +static int fdt_print(const char *pathp, char *prop, int depth); +static int is_printable_string(const void *data, int len); + /* - * Scratchpad memory. + * The working_fdt points to our working flattened device tree. */ -static char data[SCRATCHPAD]; +struct fdt_header *working_fdt; +void set_working_fdt_addr(void *addr) +{ + void *buf; + + buf = map_sysmem((ulong)addr, 0); + working_fdt = buf; + setenv_addr("fdtaddr", addr); +} /* - * Function prototypes/declarations. + * Get a value from the fdt and format it to be set in the environment */ -static int fdt_valid(void); -static void print_data(const void *data, int len); - +static int fdt_value_setenv(const void *nodep, int len, const char *var) +{ + if (is_printable_string(nodep, len)) + setenv(var, (void *)nodep); + else if (len == 4) { + char buf[11]; + + sprintf(buf, "0x%08X", *(uint32_t *)nodep); + setenv(var, buf); + } else if (len%4 == 0 && len <= 20) { + /* Needed to print things like sha1 hashes. */ + char buf[41]; + int i; + + for (i = 0; i < len; i += sizeof(unsigned int)) + sprintf(buf + (i * 2), "%08x", + *(unsigned int *)(nodep + i)); + setenv(var, buf); + } else { + printf("error: unprintable value\n"); + return 1; + } + return 0; +} /* * Flattened Device Tree command, see the help for parameter definitions. */ -int do_fdt (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) +static int do_fdt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { - char op; - - if (argc < 2) { - printf ("Usage:\n%s\n", cmdtp->usage); - return 1; - } + if (argc < 2) + return CMD_RET_USAGE; /* - * Figure out which subcommand was given - */ - op = argv[1][0]; - /******************************************************************** * Set the address of the fdt - ********************************************************************/ - if (op == 'a') { + */ + if (argv[1][0] == 'a') { + unsigned long addr; + int control = 0; + struct fdt_header *blob; /* * Set the address [and length] of the fdt. */ - fdt = (struct fdt_header *)simple_strtoul(argv[2], NULL, 16); + argc -= 2; + argv += 2; +/* Temporary #ifdef - some archs don't have fdt_blob yet */ +#ifdef CONFIG_OF_CONTROL + if (argc && !strcmp(*argv, "-c")) { + control = 1; + argc--; + argv++; + } +#endif + if (argc == 0) { + if (control) + blob = (struct fdt_header *)gd->fdt_blob; + else + blob = working_fdt; + if (!blob || !fdt_valid(&blob)) + return 1; + printf("The address of the fdt is %#08lx\n", + control ? (ulong)blob : + getenv_hex("fdtaddr", 0)); + return 0; + } - if (!fdt_valid()) { + addr = simple_strtoul(argv[0], NULL, 16); + blob = map_sysmem(addr, 0); + if (!fdt_valid(&blob)) return 1; - } + if (control) + gd->fdt_blob = blob; + else + set_working_fdt_addr(blob); - if (argc >= 4) { + if (argc >= 2) { int len; int err; /* * Optional new length */ - len = simple_strtoul(argv[3], NULL, 16); - if (len < fdt_totalsize(fdt)) { - printf ("New length %d < existing length %d, ignoring.\n", - len, fdt_totalsize(fdt)); + len = simple_strtoul(argv[1], NULL, 16); + if (len < fdt_totalsize(blob)) { + printf ("New length %d < existing length %d, " + "ignoring.\n", + len, fdt_totalsize(blob)); } else { /* * Open in place with a new length. */ - err = fdt_open_into(fdt, fdt, len); + err = fdt_open_into(blob, blob, len); if (err != 0) { - printf ("libfdt: %s\n", fdt_strerror(err)); + printf ("libfdt fdt_open_into(): %s\n", + fdt_strerror(err)); } } } - /******************************************************************** - * Move the fdt - ********************************************************************/ - } else if (op == 'm') { + return CMD_RET_SUCCESS; + } + + if (!working_fdt) { + puts( + "No FDT memory address configured. Please configure\n" + "the FDT address via \"fdt addr
\" command.\n" + "Aborting!\n"); + return CMD_RET_FAILURE; + } + + /* + * Move the working_fdt + */ + if (strncmp(argv[1], "mo", 2) == 0) { struct fdt_header *newaddr; int len; int err; - if (argc != 5) { - printf ("Usage:\n%s\n", cmdtp->usage); - return 1; - } + if (argc < 4) + return CMD_RET_USAGE; /* * Set the address and length of the fdt. */ - fdt = (struct fdt_header *)simple_strtoul(argv[2], NULL, 16); - if (!fdt_valid()) { + working_fdt = (struct fdt_header *)simple_strtoul(argv[2], NULL, 16); + if (!fdt_valid(&working_fdt)) return 1; - } - newaddr = (struct fdt_header *)simple_strtoul(argv[3], NULL, 16); - len = simple_strtoul(argv[4], NULL, 16); - if (len < fdt_totalsize(fdt)) { - printf ("New length %d < existing length %d, aborting.\n", - len, fdt_totalsize(fdt)); - return 1; + newaddr = (struct fdt_header *)simple_strtoul(argv[3],NULL,16); + + /* + * If the user specifies a length, use that. Otherwise use the + * current length. + */ + if (argc <= 4) { + len = fdt_totalsize(working_fdt); + } else { + len = simple_strtoul(argv[4], NULL, 16); + if (len < fdt_totalsize(working_fdt)) { + printf ("New length 0x%X < existing length " + "0x%X, aborting.\n", + len, fdt_totalsize(working_fdt)); + return 1; + } } /* * Copy to the new location. */ - err = fdt_open_into(fdt, newaddr, len); + err = fdt_open_into(working_fdt, newaddr, len); if (err != 0) { - printf ("libfdt: %s\n", fdt_strerror(err)); + printf ("libfdt fdt_open_into(): %s\n", + fdt_strerror(err)); return 1; } - fdt = newaddr; + working_fdt = newaddr; - /******************************************************************** - * Set the value of a node in the fdt. - ********************************************************************/ - } else if (op == 's') { + /* + * Make a new node + */ + } else if (strncmp(argv[1], "mk", 2) == 0) { char *pathp; /* path */ - char *prop; /* property */ - struct fdt_property *nodep; /* node struct pointer */ - char *newval; /* value from the user (as a string) */ - char *vp; /* temporary value pointer */ - char *cp; /* temporary char pointer */ + char *nodep; /* new node to add */ int nodeoffset; /* node offset from libfdt */ - int len; /* new length of the property */ - int oldlen; /* original length of the property */ - unsigned long tmp; /* holds converted values */ - int ret; /* return value */ + int err; /* - * Parameters: Node path, property, value. + * Parameters: Node path, new node to be appended to the path. */ - if (argc < 5) { - printf ("Usage:\n%s\n", cmdtp->usage); + if (argc < 4) + return CMD_RET_USAGE; + + pathp = argv[2]; + nodep = argv[3]; + + 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; } + err = fdt_add_subnode(working_fdt, nodeoffset, nodep); + if (err < 0) { + printf ("libfdt fdt_add_subnode(): %s\n", + fdt_strerror(err)); + return 1; + } + + /* + * Set the value of a property in the working_fdt. + */ + } else if (argv[1][0] == 's') { + char *pathp; /* path */ + char *prop; /* property */ + int nodeoffset; /* node offset from libfdt */ + static char data[SCRATCHPAD]; /* storage for the property */ + int len; /* new length of the property */ + int ret; /* return value */ + + /* + * Parameters: Node path, property, optional value. + */ + if (argc < 4) + return CMD_RET_USAGE; pathp = argv[2]; prop = argv[3]; - newval = argv[4]; - - if (strcmp(pathp, "/") == 0) { - nodeoffset = 0; + if (argc == 4) { + len = 0; } else { - nodeoffset = fdt_path_offset (fdt, pathp); - if (nodeoffset < 0) { - /* - * Not found or something else bad happened. - */ - printf ("libfdt: %s\n", fdt_strerror(nodeoffset)); - return 1; - } + ret = fdt_parse_prop(&argv[4], argc - 4, data, &len); + if (ret != 0) + return ret; } - nodep = fdt_getprop (fdt, nodeoffset, prop, &oldlen); - if (oldlen < 0) { - printf ("libfdt %s\n", fdt_strerror(oldlen)); - return 1; - } else if (oldlen == 0) { + + nodeoffset = fdt_path_offset (working_fdt, pathp); + if (nodeoffset < 0) { /* - * The specified property has no value + * Not found or something else bad happened. */ - printf("%s has no value, cannot set one (yet).\n", prop); + printf ("libfdt fdt_path_offset() returned %s\n", + fdt_strerror(nodeoffset)); return 1; - } else { + } + + ret = fdt_setprop(working_fdt, nodeoffset, prop, data, len); + if (ret < 0) { + printf ("libfdt fdt_setprop(): %s\n", fdt_strerror(ret)); + return 1; + } + + /******************************************************************** + * Get the value of a property in the working_fdt. + ********************************************************************/ + } else if (argv[1][0] == 'g') { + char *subcmd; /* sub-command */ + char *pathp; /* path */ + char *prop; /* property */ + char *var; /* variable to store result */ + int nodeoffset; /* node offset from libfdt */ + const void *nodep; /* property node pointer */ + int len = 0; /* new length of the property */ + + /* + * Parameters: Node path, property, optional value. + */ + if (argc < 5) + return CMD_RET_USAGE; + + subcmd = argv[2]; + + if (argc < 6 && subcmd[0] != 's') + return CMD_RET_USAGE; + + var = argv[3]; + pathp = argv[4]; + prop = argv[5]; + + nodeoffset = fdt_path_offset(working_fdt, pathp); + if (nodeoffset < 0) { /* - * Convert the new property + * Not found or something else bad happened. */ - vp = data; - if (*newval == '<') { - /* - * Bigger values than bytes. - */ - len = 0; - newval++; - while ((*newval != '>') && (*newval != '\0')) { - cp = newval; - tmp = simple_strtoul(cp, &newval, 16); - if ((newval - cp) <= 2) { - *vp = tmp & 0xFF; - vp += 1; - len += 1; - } else if ((newval - cp) <= 4) { - *(uint16_t *)vp = __cpu_to_be16(tmp); - vp += 2; - len += 2; - } else if ((newval - cp) <= 8) { - *(uint32_t *)vp = __cpu_to_be32(tmp); - vp += 4; - len += 4; - } else { - printf("Sorry, I could not convert \"%s\"\n", cp); - return 1; - } - while (*newval == ' ') - newval++; - } - if (*newval != '>') { - printf("Unexpected character '%c'\n", *newval); - return 1; - } - } else if (*newval == '[') { - /* - * Byte stream. Convert the values. - */ - len = 0; - newval++; - while ((*newval != ']') && (*newval != '\0')) { - tmp = simple_strtoul(newval, &newval, 16); - *vp++ = tmp & 0xFF; - len++; - while (*newval == ' ') - newval++; - } - if (*newval != ']') { - printf("Unexpected character '%c'\n", *newval); - return 1; + printf("libfdt fdt_path_offset() returned %s\n", + fdt_strerror(nodeoffset)); + return 1; + } + + if (subcmd[0] == 'n' || (subcmd[0] == 's' && argc == 5)) { + int reqIndex = -1; + int startDepth = fdt_node_depth( + working_fdt, nodeoffset); + int curDepth = startDepth; + int curIndex = -1; + int nextNodeOffset = fdt_next_node( + working_fdt, nodeoffset, &curDepth); + + if (subcmd[0] == 'n') + reqIndex = simple_strtoul(argv[5], NULL, 16); + + while (curDepth > startDepth) { + if (curDepth == startDepth + 1) + curIndex++; + if (subcmd[0] == 'n' && curIndex == reqIndex) { + const char *nodeName = fdt_get_name( + working_fdt, nextNodeOffset, NULL); + + setenv(var, (char *)nodeName); + return 0; } + nextNodeOffset = fdt_next_node( + working_fdt, nextNodeOffset, &curDepth); + if (nextNodeOffset < 0) + break; + } + if (subcmd[0] == 's') { + /* get the num nodes at this level */ + setenv_ulong(var, curIndex + 1); } else { - /* - * Assume it is a string. Copy it into our data area for - * convenience (including the terminating '\0'). - */ - len = strlen(newval) + 1; - strcpy(data, newval); + /* node index not found */ + printf("libfdt node not found\n"); + return 1; } - - ret = fdt_setprop(fdt, nodeoffset, prop, data, len); - if (ret < 0) { - printf ("libfdt %s\n", fdt_strerror(ret)); + } else { + nodep = fdt_getprop( + working_fdt, nodeoffset, prop, &len); + if (len == 0) { + /* no property value */ + setenv(var, ""); + return 0; + } else if (len > 0) { + if (subcmd[0] == 'v') { + int ret; + + ret = fdt_value_setenv(nodep, len, var); + if (ret != 0) + return ret; + } else if (subcmd[0] == 'a') { + /* Get address */ + char buf[11]; + + sprintf(buf, "0x%p", nodep); + setenv(var, buf); + } else if (subcmd[0] == 's') { + /* Get size */ + char buf[11]; + + sprintf(buf, "0x%08X", len); + setenv(var, buf); + } else + return CMD_RET_USAGE; + return 0; + } else { + printf("libfdt fdt_getprop(): %s\n", + fdt_strerror(len)); return 1; } } - /******************************************************************** + /* * Print (recursive) / List (single level) - ********************************************************************/ - } else if ((op == 'p') || (op == 'l')) { - /* - * Recursively print (a portion of) the fdt. - */ - static int offstack[MAX_LEVEL]; - 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"; + */ + } else if ((argv[1][0] == 'p') || (argv[1][0] == 'l')) { int depth = MAX_LEVEL; /* how deep to print */ char *pathp; /* path */ - char *prop; /* property */ - 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 */ + char *prop; /* property */ + int ret; /* return value */ + static char root[2] = "/"; /* * list is an alias for print, but limited to 1 level */ - if (op == 'l') { + if (argv[1][0] == 'l') { depth = 1; } @@ -296,105 +428,23 @@ int do_fdt (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) * Get the starting path. The root node is an oddball, * the offset is zero and has no name. */ - pathp = argv[2]; + if (argc == 2) + pathp = root; + else + pathp = argv[2]; if (argc > 3) prop = argv[3]; else prop = NULL; - if (strcmp(pathp, "/") == 0) { - nodeoffset = 0; - printf("/"); - } else { - nodeoffset = fdt_path_offset (fdt, pathp); - if (nodeoffset < 0) { - /* - * Not found or something else bad happened. - */ - printf ("libfdt %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 (fdt, nodeoffset, prop, &len); - if (len == 0) { - printf("%s %s\n", pathp, prop); /* no property value */ - return 0; - } else if (len > 0) { - printf("%s=", prop); - print_data (nodep, len); - printf("\n"); - return 0; - } else { - printf ("libfdt %s\n", fdt_strerror(len)); - return 1; - } - } + ret = fdt_print(pathp, prop, depth); + if (ret != 0) + return ret; - /* - * The user passed in a node path and no property, print the node - * and all subnodes. - */ - offstack[0] = nodeoffset; - - while(level >= 0) { - tag = fdt_next_tag(fdt, nodeoffset, &nextoffset, &pathp); - switch(tag) { - case FDT_BEGIN_NODE: - if(level <= depth) - printf("%s%s {\n", &tabs[MAX_LEVEL - level], pathp); - level++; - offstack[level] = nodeoffset; - if (level >= MAX_LEVEL) { - printf("Aaaiii nested too deep.\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: - nodep = fdt_getprop (fdt, offstack[level], pathp, &len); - if (len < 0) { - printf ("libfdt %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: - break; - case FDT_END: - return 1; - default: - if(level <= depth) - printf("Unknown tag 0x%08X\n", tag); - return 1; - } - nodeoffset = nextoffset; - } - - /******************************************************************** + /* * Remove a property/node - ********************************************************************/ - } else if (op == 'r') { + */ + } else if (strncmp(argv[1], "rm", 2) == 0) { int nodeoffset; /* node offset from libfdt */ int err; @@ -402,111 +452,317 @@ int do_fdt (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) * Get the path. The root node is an oddball, the offset * is zero and has no name. */ - if (strcmp(argv[2], "/") == 0) { - nodeoffset = 0; - } else { - nodeoffset = fdt_path_offset (fdt, argv[2]); - if (nodeoffset < 0) { - /* - * Not found or something else bad happened. - */ - printf ("libfdt %s\n", fdt_strerror(nodeoffset)); - return 1; - } + nodeoffset = fdt_path_offset (working_fdt, argv[2]); + if (nodeoffset < 0) { + /* + * Not found or something else bad happened. + */ + printf ("libfdt fdt_path_offset() returned %s\n", + fdt_strerror(nodeoffset)); + return 1; } /* * Do the delete. A fourth parameter means delete a property, * otherwise delete the node. */ if (argc > 3) { - err = fdt_delprop(fdt, nodeoffset, argv[3]); + err = fdt_delprop(working_fdt, nodeoffset, argv[3]); if (err < 0) { - printf("fdt_delprop libfdt: %s\n", fdt_strerror(err)); + printf("libfdt fdt_delprop(): %s\n", + fdt_strerror(err)); return err; } } else { - err = fdt_del_node(fdt, nodeoffset); + err = fdt_del_node(working_fdt, nodeoffset); if (err < 0) { - printf("fdt_del_node libfdt: %s\n", fdt_strerror(err)); + printf("libfdt fdt_del_node(): %s\n", + fdt_strerror(err)); return err; } } - /******************************************************************** - * Create a chosen node - ********************************************************************/ - } else if (op == 'c') { - fdt_chosen(fdt, 0, 0, 1); + /* + * Display header info + */ + } else if (argv[1][0] == 'h') { + u32 version = fdt_version(working_fdt); + printf("magic:\t\t\t0x%x\n", fdt_magic(working_fdt)); + printf("totalsize:\t\t0x%x (%d)\n", fdt_totalsize(working_fdt), + fdt_totalsize(working_fdt)); + printf("off_dt_struct:\t\t0x%x\n", + fdt_off_dt_struct(working_fdt)); + printf("off_dt_strings:\t\t0x%x\n", + fdt_off_dt_strings(working_fdt)); + printf("off_mem_rsvmap:\t\t0x%x\n", + fdt_off_mem_rsvmap(working_fdt)); + printf("version:\t\t%d\n", version); + printf("last_comp_version:\t%d\n", + fdt_last_comp_version(working_fdt)); + if (version >= 2) + printf("boot_cpuid_phys:\t0x%x\n", + fdt_boot_cpuid_phys(working_fdt)); + if (version >= 3) + printf("size_dt_strings:\t0x%x\n", + fdt_size_dt_strings(working_fdt)); + if (version >= 17) + printf("size_dt_struct:\t\t0x%x\n", + fdt_size_dt_struct(working_fdt)); + printf("number mem_rsv:\t\t0x%x\n", + fdt_num_mem_rsv(working_fdt)); + printf("\n"); - /******************************************************************** - * Create a u-boot-env node - ********************************************************************/ - } else if (op == 'e') { - fdt_env(fdt); + /* + * Set boot cpu id + */ + } else if (strncmp(argv[1], "boo", 3) == 0) { + unsigned long tmp = simple_strtoul(argv[2], NULL, 16); + fdt_set_boot_cpuid_phys(working_fdt, tmp); - /******************************************************************** - * Create a bd_t node - ********************************************************************/ - } else if (op == 'b') { - fdt_bd_t(fdt); + /* + * memory command + */ + } else if (strncmp(argv[1], "me", 2) == 0) { + uint64_t addr, size; + int err; + addr = simple_strtoull(argv[2], NULL, 16); + size = simple_strtoull(argv[3], NULL, 16); + err = fdt_fixup_memory(working_fdt, addr, size); + if (err < 0) + return err; - /******************************************************************** - * Unrecognized command - ********************************************************************/ - } else { - printf ("Usage:\n%s\n", cmdtp->usage); - return 1; + /* + * mem reserve commands + */ + } else if (strncmp(argv[1], "rs", 2) == 0) { + if (argv[2][0] == 'p') { + uint64_t addr, size; + int total = fdt_num_mem_rsv(working_fdt); + int j, err; + printf("index\t\t start\t\t size\n"); + printf("-------------------------------" + "-----------------\n"); + for (j = 0; j < total; j++) { + err = fdt_get_mem_rsv(working_fdt, j, &addr, &size); + if (err < 0) { + printf("libfdt fdt_get_mem_rsv(): %s\n", + fdt_strerror(err)); + return err; + } + printf(" %x\t%08x%08x\t%08x%08x\n", j, + (u32)(addr >> 32), + (u32)(addr & 0xffffffff), + (u32)(size >> 32), + (u32)(size & 0xffffffff)); + } + } else if (argv[2][0] == 'a') { + uint64_t addr, size; + int err; + addr = simple_strtoull(argv[3], NULL, 16); + size = simple_strtoull(argv[4], NULL, 16); + err = fdt_add_mem_rsv(working_fdt, addr, size); + + if (err < 0) { + printf("libfdt fdt_add_mem_rsv(): %s\n", + fdt_strerror(err)); + return err; + } + } else if (argv[2][0] == 'd') { + unsigned long idx = simple_strtoul(argv[3], NULL, 16); + int err = fdt_del_mem_rsv(working_fdt, idx); + + if (err < 0) { + printf("libfdt fdt_del_mem_rsv(): %s\n", + fdt_strerror(err)); + return err; + } + } else { + /* Unrecognized command */ + return CMD_RET_USAGE; + } + } +#ifdef CONFIG_OF_BOARD_SETUP + /* Call the board-specific fixup routine */ + else if (strncmp(argv[1], "boa", 3) == 0) + ft_board_setup(working_fdt, gd->bd); +#endif + /* Create a chosen node */ + else if (argv[1][0] == 'c') { + unsigned long initrd_start = 0, initrd_end = 0; + + if ((argc != 2) && (argc != 4)) + return CMD_RET_USAGE; + + if (argc == 4) { + initrd_start = simple_strtoul(argv[2], NULL, 16); + initrd_end = simple_strtoul(argv[3], NULL, 16); + } + + fdt_chosen(working_fdt, 1); + fdt_initrd(working_fdt, initrd_start, initrd_end, 1); + } + /* resize the fdt */ + else if (strncmp(argv[1], "re", 2) == 0) { + fdt_resize(working_fdt); + } + else { + /* Unrecognized command */ + return CMD_RET_USAGE; } return 0; } -/********************************************************************/ +/****************************************************************************/ -static int fdt_valid(void) +/** + * fdt_valid() - Check if an FDT is valid. If not, change it to NULL + * + * @blobp: Pointer to FDT pointer + * @return 1 if OK, 0 if bad (in which case *blobp is set to NULL) + */ +static int fdt_valid(struct fdt_header **blobp) { - int err; + const void *blob = *blobp; + int err; - if (fdt == NULL) { + if (blob == NULL) { printf ("The address of the fdt is invalid (NULL).\n"); return 0; } - err = fdt_check_header(fdt); + err = fdt_check_header(blob); if (err == 0) return 1; /* valid */ if (err < 0) { - printf("libfdt: %s", fdt_strerror(err)); + printf("libfdt fdt_check_header(): %s", fdt_strerror(err)); /* * Be more informative on bad version. */ if (err == -FDT_ERR_BADVERSION) { - if (fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION) { - printf (" - too old, fdt $d < %d", - fdt_version(fdt), FDT_FIRST_SUPPORTED_VERSION); - fdt = NULL; + if (fdt_version(blob) < + FDT_FIRST_SUPPORTED_VERSION) { + printf (" - too old, fdt %d < %d", + fdt_version(blob), + FDT_FIRST_SUPPORTED_VERSION); } - if (fdt_last_comp_version(fdt) > FDT_LAST_SUPPORTED_VERSION) { - printf (" - too new, fdt $d > %d", - fdt_version(fdt), FDT_LAST_SUPPORTED_VERSION); - fdt = NULL; + if (fdt_last_comp_version(blob) > + FDT_LAST_SUPPORTED_VERSION) { + printf (" - too new, fdt %d > %d", + fdt_version(blob), + FDT_LAST_SUPPORTED_VERSION); } - return 0; } printf("\n"); + *blobp = NULL; return 0; } return 1; } -/********************************************************************/ +/****************************************************************************/ /* - * OF flat tree handling - * Written by: Pantelis Antoniou - * Updated by: Matthew McClintock - * Converted to libfdt by: Gerald Van Baren + * Parse the user's input, partially heuristic. Valid formats: + * <0x00112233 4 05> - an array of cells. Numbers follow standard + * C conventions. + * [00 11 22 .. nn] - byte stream + * "string" - If the the value doesn't start with "<" or "[", it is + * treated as a string. Note that the quotes are + * stripped by the parser before we get the string. + * newval: An array of strings containing the new property as specified + * on the command line + * count: The number of strings in the array + * data: A bytestream to be placed in the property + * len: The length of the resulting bytestream + */ +static int fdt_parse_prop(char * const *newval, int count, char *data, int *len) +{ + char *cp; /* temporary char pointer */ + char *newp; /* temporary newval char pointer */ + unsigned long tmp; /* holds converted values */ + int stridx = 0; + + *len = 0; + newp = newval[0]; + + /* An array of cells */ + if (*newp == '<') { + newp++; + while ((*newp != '>') && (stridx < count)) { + /* + * Keep searching until we find that last ">" + * That way users don't have to escape the spaces + */ + if (*newp == '\0') { + newp = newval[++stridx]; + continue; + } + + cp = newp; + tmp = simple_strtoul(cp, &newp, 0); + *(__be32 *)data = __cpu_to_be32(tmp); + data += 4; + *len += 4; + + /* If the ptr didn't advance, something went wrong */ + if ((newp - cp) <= 0) { + printf("Sorry, I could not convert \"%s\"\n", + cp); + return 1; + } + + while (*newp == ' ') + newp++; + } + + if (*newp != '>') { + printf("Unexpected character '%c'\n", *newp); + return 1; + } + } else if (*newp == '[') { + /* + * Byte stream. Convert the values. + */ + newp++; + while ((stridx < count) && (*newp != ']')) { + while (*newp == ' ') + newp++; + if (*newp == '\0') { + newp = newval[++stridx]; + continue; + } + if (!isxdigit(*newp)) + break; + tmp = simple_strtoul(newp, &newp, 16); + *data++ = tmp & 0xFF; + *len = *len + 1; + } + if (*newp != ']') { + printf("Unexpected character '%c'\n", *newp); + return 1; + } + } else { + /* + * Assume it is one or more strings. Copy it into our + * data area for convenience (including the + * terminating '\0's). + */ + while (stridx < count) { + size_t length = strlen(newp) + 1; + strcpy(data, newp); + data += length; + *len += length; + newp = newval[++stridx]; + } + } + return 0; +} + +/****************************************************************************/ + +/* + * Heuristic to guess if this is a string or concatenated strings. */ static int is_printable_string(const void *data, int len) @@ -517,12 +773,12 @@ static int is_printable_string(const void *data, int len) if (len == 0) return 0; - /* must terminate with zero */ - if (s[len - 1] != '\0') + /* must terminate with zero or '\n' */ + if (s[len - 1] != '\0' && s[len - 1] != '\n') return 0; /* printable or a null byte (concatenated strings) */ - while (((*s == '\0') || isprint(*s)) && (len > 0)) { + 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 @@ -546,10 +802,15 @@ static int is_printable_string(const void *data, int len) 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; - const u8 *s; /* no data, don't print */ if (len == 0) @@ -572,64 +833,186 @@ static void print_data(const void *data, int len) return; } - switch (len) { - case 1: /* byte */ - printf("<%02x>", (*(u8 *) data) & 0xff); - break; - case 2: /* half-word */ - printf("<%04x>", be16_to_cpu(*(u16 *) data) & 0xffff); - break; - case 4: /* word */ - printf("<%08x>", be32_to_cpu(*(u32 *) data) & 0xffffffffU); - break; - case 8: /* double-word */ -#if __WORDSIZE == 64 - printf("<%016llx>", be64_to_cpu(*(uint64_t *) data)); -#else - printf("<%08x ", be32_to_cpu(*(u32 *) data) & 0xffffffffU); - data += 4; - printf("%08x>", be32_to_cpu(*(u32 *) data) & 0xffffffffU); -#endif - break; - default: /* anything else... hexdump */ - printf("["); - for (j = 0, s = data; j < len; j++) - printf("%02x%s", s[j], j < len - 1 ? " " : ""); - printf("]"); - - break; + if ((len %4) == 0) { + if (len > CONFIG_CMD_FDT_MAX_DUMP) + printf("* 0x%p [0x%08x]", data, len); + else { + const __be32 *p; + + printf("<"); + for (j = 0, p = data; j < len/4; j++) + printf("0x%08x%s", fdt32_to_cpu(p[j]), + j < (len/4 - 1) ? " " : ""); + printf(">"); + } + } else { /* anything else... hexdump */ + if (len > CONFIG_CMD_FDT_MAX_DUMP) + printf("* 0x%p [0x%08x]", data, len); + else { + const u8 *s; + + printf("["); + for (j = 0, s = data; j < len; j++) + printf("%02x%s", s[j], j < len - 1 ? " " : ""); + printf("]"); + } } } -/********************************************************************/ +/****************************************************************************/ -U_BOOT_CMD( - fdt, 5, 0, do_fdt, - "fdt - flattened device tree utility commands\n", - "addr [] - Set the fdt location to \n" - "fdt move - Copy the fdt to \n" +/* + * Recursively print (a portion of) the working_fdt. The depth parameter + * determines how deeply nested the fdt is printed. + */ +static int fdt_print(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; +} + +/********************************************************************/ +#ifdef CONFIG_SYS_LONGHELP +static char fdt_help_text[] = + "addr [-c] [] - Set the [control] fdt location to \n" +#ifdef CONFIG_OF_BOARD_SETUP + "fdt boardsetup - Do board-specific set up\n" +#endif + "fdt move - Copy the fdt to and make it active\n" + "fdt resize - Resize fdt to size + padding to 4k addr\n" "fdt print [] - Recursive print starting at \n" "fdt list [] - Print one level starting at \n" + "fdt get value - Get and store in \n" + "fdt get name - Get name of node and store in \n" + "fdt get addr - Get start address of and store in \n" + "fdt get size [] - Get size of [] or num nodes and store in \n" "fdt set [] - Set [to ]\n" "fdt mknode - Create a new node after \n" "fdt rm [] - Delete the node or \n" - "fdt chosen - Add/update the \"/chosen\" branch in the tree\n" -#ifdef CONFIG_OF_HAS_UBOOT_ENV - "fdt env - Add/replace the \"/u-boot-env\" branch in the tree\n" + "fdt header - Display header info\n" + "fdt bootcpu - Set boot cpuid\n" + "fdt memory - Add/Update memory node\n" + "fdt rsvmem print - Show current mem reserves\n" + "fdt rsvmem add - Add a mem reserve\n" + "fdt rsvmem delete - Delete a mem reserves\n" + "fdt chosen [ ] - Add/update the /chosen branch in the tree\n" + " / - initrd start/end addr\n" + "NOTE: Dereference aliases by omiting the leading '/', " + "e.g. fdt print ethernet0."; #endif -#ifdef CONFIG_OF_HAS_BD_T - "fdt bd_t - Add/replace the \"/bd_t\" branch in the tree\n" -#endif - "Hints:\n" - " * Set a larger length with the fdt addr command to add to the blob.\n" - " * If the property you are setting/printing has a '#' character,\n" - " you MUST escape it with a \\ character or quote it with \" or\n" - " it will be ignored as a comment.\n" - " * If the value has spaces in it, you MUST escape the spaces with\n" - " \\ characters or quote it with \"\"\n" - "Examples: fdt print / # print the whole tree\n" - " fdt print /cpus \"#address-cells\"\n" - " fdt set /cpus \"#address-cells\" \"[00 00 00 01]\"\n" -); -#endif /* CONFIG_OF_LIBFDT */ +U_BOOT_CMD( + fdt, 255, 0, do_fdt, + "flattened device tree utility commands", fdt_help_text +);