// SPDX-License-Identifier: GPL-2.0+ /* * (C) Copyright 2007 * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com * Based on code written by: * Pantelis Antoniou and * Matthew McClintock */ #include #include #include #include #include #include #include #include #include #include #include #define MAX_LEVEL 32 /* how deeply nested we will go */ #define SCRATCHPAD 1024 /* bytes of scratchpad memory */ /* * Global data (for the gd->bd) */ DECLARE_GLOBAL_DATA_PTR; 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); /* * The working_fdt points to our working flattened device tree. */ struct fdt_header *working_fdt; void set_working_fdt_addr(ulong addr) { void *buf; buf = map_sysmem(addr, 0); working_fdt = buf; env_set_hex("fdtaddr", addr); } /* * Get a value from the fdt and format it to be set in the environment */ static int fdt_value_env_set(const void *nodep, int len, const char *var) { if (is_printable_string(nodep, len)) env_set(var, (void *)nodep); else if (len == 4) { char buf[11]; sprintf(buf, "0x%08X", fdt32_to_cpu(*(fdt32_t *)nodep)); env_set(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)); env_set(var, buf); } else { printf("error: unprintable value\n"); return 1; } return 0; } static const char * const fdt_member_table[] = { "magic", "totalsize", "off_dt_struct", "off_dt_strings", "off_mem_rsvmap", "version", "last_comp_version", "boot_cpuid_phys", "size_dt_strings", "size_dt_struct", }; static int fdt_get_header_value(int argc, char *const argv[]) { fdt32_t *fdtp = (fdt32_t *)working_fdt; ulong val; int i; if (argv[2][0] != 'g') return CMD_RET_FAILURE; for (i = 0; i < ARRAY_SIZE(fdt_member_table); i++) { if (strcmp(fdt_member_table[i], argv[4])) continue; val = fdt32_to_cpu(fdtp[i]); env_set_hex(argv[3], val); return CMD_RET_SUCCESS; } return CMD_RET_FAILURE; } /* * Flattened Device Tree command, see the help for parameter definitions. */ static int do_fdt(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { if (argc < 2) return CMD_RET_USAGE; /* fdt addr: Set the address of the fdt */ if (strncmp(argv[1], "ad", 2) == 0) { unsigned long addr; int control = 0; int quiet = 0; struct fdt_header *blob; /* Set the address [and length] of the fdt */ argc -= 2; argv += 2; while (argc > 0 && **argv == '-') { char *arg = *argv; while (*++arg) { switch (*arg) { case 'c': control = 1; break; case 'q': quiet = 1; break; default: return CMD_RET_USAGE; } } argc--; argv++; } if (argc == 0) { if (control) blob = (struct fdt_header *)gd->fdt_blob; else blob = working_fdt; if (!blob || !fdt_valid(&blob)) return 1; printf("%s fdt: %08lx\n", control ? "Control" : "Working", control ? (ulong)map_to_sysmem(blob) : env_get_hex("fdtaddr", 0)); return 0; } addr = hextoul(argv[0], NULL); blob = map_sysmem(addr, 0); if ((quiet && fdt_check_header(blob)) || (!quiet && !fdt_valid(&blob))) return 1; if (control) gd->fdt_blob = blob; else set_working_fdt_addr(addr); if (argc >= 2) { int len; int err; /* Optional new length */ len = hextoul(argv[1], NULL); if (len < fdt_totalsize(blob)) { if (!quiet) 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(blob, blob, len); if (!quiet && err != 0) { printf("libfdt fdt_open_into(): %s\n", fdt_strerror(err)); } } } 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 < 4) return CMD_RET_USAGE; /* * Set the address and length of the fdt. */ working_fdt = (struct fdt_header *)hextoul(argv[2], NULL); if (!fdt_valid(&working_fdt)) return 1; newaddr = (struct fdt_header *)hextoul(argv[3], NULL); /* * If the user specifies a length, use that. Otherwise use the * current length. */ if (argc <= 4) { len = fdt_totalsize(working_fdt); } else { len = hextoul(argv[4], NULL); 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(working_fdt, newaddr, len); if (err != 0) { printf ("libfdt fdt_open_into(): %s\n", fdt_strerror(err)); return 1; } set_working_fdt_addr((ulong)newaddr); #ifdef CONFIG_OF_SYSTEM_SETUP /* Call the board-specific fixup routine */ } else if (strncmp(argv[1], "sys", 3) == 0) { int err = ft_system_setup(working_fdt, gd->bd); if (err) { printf("Failed to add system information to FDT: %s\n", fdt_strerror(err)); return CMD_RET_FAILURE; } #endif /* * Make a new node */ } else if (strncmp(argv[1], "mk", 2) == 0) { char *pathp; /* path */ char *nodep; /* new node to add */ int nodeoffset; /* node offset from libfdt */ int err; /* * Parameters: Node path, new node to be appended to the path. */ 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 (strncmp(argv[1], "se", 2) == 0) { char *pathp; /* path */ char *prop; /* property */ int nodeoffset; /* node offset from libfdt */ static char data[SCRATCHPAD] __aligned(4);/* property storage */ const void *ptmp; 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]; 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; } if (argc == 4) { len = 0; } else { ptmp = fdt_getprop(working_fdt, nodeoffset, prop, &len); if (len > SCRATCHPAD) { printf("prop (%d) doesn't fit in scratchpad!\n", len); return 1; } if (ptmp != NULL) memcpy(data, ptmp, len); ret = fdt_parse_prop(&argv[4], argc - 4, data, &len); if (ret != 0) return ret; } 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) { /* * Not found or something else bad happened. */ printf("libfdt fdt_path_offset() returned %s\n", fdt_strerror(nodeoffset)); return 1; } if (subcmd[0] == 'n' || (subcmd[0] == 's' && argc == 5)) { int req_index = -1; int startDepth = fdt_node_depth( working_fdt, nodeoffset); int curDepth = startDepth; int cur_index = -1; int nextNodeOffset = fdt_next_node( working_fdt, nodeoffset, &curDepth); if (subcmd[0] == 'n') req_index = hextoul(argv[5], NULL); while (curDepth > startDepth) { if (curDepth == startDepth + 1) cur_index++; if (subcmd[0] == 'n' && cur_index == req_index) { const char *node_name; node_name = fdt_get_name(working_fdt, nextNodeOffset, NULL); env_set(var, node_name); 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 */ env_set_ulong(var, cur_index + 1); } else { /* node index not found */ printf("libfdt node not found\n"); return 1; } } else { nodep = fdt_getprop( working_fdt, nodeoffset, prop, &len); if (len == 0) { /* no property value */ env_set(var, ""); return 0; } else if (nodep && len > 0) { if (subcmd[0] == 'v') { int ret; ret = fdt_value_env_set(nodep, len, var); if (ret != 0) return ret; } else if (subcmd[0] == 'a') { /* Get address */ char buf[11]; sprintf(buf, "0x%p", nodep); env_set(var, buf); } else if (subcmd[0] == 's') { /* Get size */ char buf[11]; sprintf(buf, "0x%08X", len); env_set(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 ((argv[1][0] == 'p') || (argv[1][0] == 'l')) { int depth = MAX_LEVEL; /* how deep to print */ char *pathp; /* path */ char *prop; /* property */ int ret; /* return value */ static char root[2] = "/"; /* * list is an alias for print, but limited to 1 level */ if (argv[1][0] == 'l') { depth = 1; } /* * Get the starting path. The root node is an oddball, * the offset is zero and has no name. */ if (argc == 2) pathp = root; else pathp = argv[2]; if (argc > 3) prop = argv[3]; else prop = NULL; ret = fdt_print(pathp, prop, depth); if (ret != 0) return ret; /* * Remove a property/node */ } else if (strncmp(argv[1], "rm", 2) == 0) { int nodeoffset; /* node offset from libfdt */ int err; /* * Get the path. The root node is an oddball, the offset * is zero and has no name. */ 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(working_fdt, nodeoffset, argv[3]); if (err < 0) { printf("libfdt fdt_delprop(): %s\n", fdt_strerror(err)); return err; } } else { err = fdt_del_node(working_fdt, nodeoffset); if (err < 0) { printf("libfdt fdt_del_node(): %s\n", fdt_strerror(err)); return err; } } /* * Display header info */ } else if (argv[1][0] == 'h') { if (argc == 5) return fdt_get_header_value(argc, argv); 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"); /* * Set boot cpu id */ } else if (strncmp(argv[1], "boo", 3) == 0) { unsigned long tmp = hextoul(argv[2], NULL); fdt_set_boot_cpuid_phys(working_fdt, tmp); /* * 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; /* * 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 = hextoul(argv[3], NULL); 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) { int err = ft_board_setup(working_fdt, gd->bd); if (err) { printf("Failed to update board information in FDT: %s\n", fdt_strerror(err)); return CMD_RET_FAILURE; } #ifdef CONFIG_ARCH_KEYSTONE ft_board_setup_ex(working_fdt, gd->bd); #endif } #endif /* Create a chosen node */ else if (strncmp(argv[1], "cho", 3) == 0) { unsigned long initrd_start = 0, initrd_end = 0; if ((argc != 2) && (argc != 4)) return CMD_RET_USAGE; if (argc == 4) { initrd_start = hextoul(argv[2], NULL); initrd_end = initrd_start + hextoul(argv[3], NULL) - 1; } fdt_chosen(working_fdt); fdt_initrd(working_fdt, initrd_start, initrd_end); #if defined(CONFIG_FIT_SIGNATURE) } else if (strncmp(argv[1], "che", 3) == 0) { int cfg_noffset; int ret; unsigned long addr; struct fdt_header *blob; if (!working_fdt) return CMD_RET_FAILURE; if (argc > 2) { addr = hextoul(argv[2], NULL); blob = map_sysmem(addr, 0); } else { blob = (struct fdt_header *)gd->fdt_blob; } if (!fdt_valid(&blob)) return 1; gd->fdt_blob = blob; cfg_noffset = fit_conf_get_node(working_fdt, NULL); if (!cfg_noffset) { printf("Could not find configuration node: %s\n", fdt_strerror(cfg_noffset)); return CMD_RET_FAILURE; } ret = fit_config_verify(working_fdt, cfg_noffset); if (ret == 0) return CMD_RET_SUCCESS; else return CMD_RET_FAILURE; #endif } #ifdef CONFIG_OF_LIBFDT_OVERLAY /* apply an overlay */ else if (strncmp(argv[1], "ap", 2) == 0) { unsigned long addr; struct fdt_header *blob; int ret; if (argc != 3) return CMD_RET_USAGE; if (!working_fdt) return CMD_RET_FAILURE; addr = hextoul(argv[2], NULL); blob = map_sysmem(addr, 0); if (!fdt_valid(&blob)) return CMD_RET_FAILURE; /* apply method prints messages on error */ ret = fdt_overlay_apply_verbose(working_fdt, blob); if (ret) return CMD_RET_FAILURE; } #endif /* resize the fdt */ else if (strncmp(argv[1], "re", 2) == 0) { uint extrasize; if (argc > 2) extrasize = hextoul(argv[2], NULL); else extrasize = 0; fdt_shrink_to_minimum(working_fdt, extrasize); } else { /* Unrecognized command */ return CMD_RET_USAGE; } return 0; } /****************************************************************************/ /* * 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); if (*cp != '?') *(fdt32_t *)data = cpu_to_fdt32(tmp); else newp++; 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 = hextoul(newp, &newp); *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) { const char *s = data; /* zero length is not */ if (len == 0) return 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) || 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; const char *env_max_dump; ulong max_dump = ULONG_MAX; /* no data, don't print */ if (len == 0) return; env_max_dump = env_get("fdt_max_dump"); if (env_max_dump) max_dump = hextoul(env_max_dump, NULL); /* * 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) { if (len > 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 > 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("]"); } } } /****************************************************************************/ /* * 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 (nodep && 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 [-cq] [] - Set the [control] fdt location to \n" #ifdef CONFIG_OF_LIBFDT_OVERLAY "fdt apply - Apply overlay to the DT\n" #endif #ifdef CONFIG_OF_BOARD_SETUP "fdt boardsetup - Do board-specific set up\n" #endif #ifdef CONFIG_OF_SYSTEM_SETUP "fdt systemsetup - Do system-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 + some optional if needed\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 header [get ] - Display header info\n" " get - get header member and store it in \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 addr/size\n" #if defined(CONFIG_FIT_SIGNATURE) "fdt checksign [] - check FIT signature\n" " - addr of key blob\n" " default gd->fdt_blob\n" #endif "NOTE: Dereference aliases by omitting the leading '/', " "e.g. fdt print ethernet0."; #endif U_BOOT_CMD( fdt, 255, 0, do_fdt, "flattened device tree utility commands", fdt_help_text );