#include <linux/ctype.h>
#include <linux/types.h>
#include <asm/global_data.h>
-#include <fdt.h>
#include <libfdt.h>
#include <fdt_support.h>
#define MAX_LEVEL 32 /* how deeply nested we will go */
#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(void);
-static int fdt_parse_prop(char **newval, int count, char *data, int *len);
+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(void *addr)
+{
+ char buf[17];
+
+ working_fdt = addr;
+
+ sprintf(buf, "%lx", (unsigned long)addr);
+ setenv("fdtaddr", buf);
+}
+
/*
- * Flattened Device Tree command, see the help for parameter definitions.
+ * Get a value from the fdt and format it to be set in the environment
*/
-int do_fdt (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
+static int fdt_value_setenv(const void *nodep, int len, const char *var)
{
- if (argc < 2) {
- printf ("Usage:\n%s\n", cmdtp->usage);
+ 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.
+ */
+static int do_fdt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ /*
* Set the address of the fdt
- ********************************************************************/
+ */
if (argv[1][0] == 'a') {
+ unsigned long addr;
/*
* Set the address [and length] of the fdt.
*/
return 0;
}
- working_fdt = (struct fdt_header *)simple_strtoul(argv[2], NULL, 16);
+ addr = simple_strtoul(argv[2], NULL, 16);
+ set_working_fdt_addr((void *)addr);
if (!fdt_valid()) {
return 1;
}
}
- /********************************************************************
+ return CMD_RET_SUCCESS;
+ }
+
+ if (!working_fdt) {
+ puts(
+ "No FDT memory address configured. Please configure\n"
+ "the FDT address via \"fdt addr <address>\" command.\n"
+ "Aborting!\n");
+ return CMD_RET_FAILURE;
+ }
+
+ /*
* Move the working_fdt
- ********************************************************************/
- } else if (strncmp(argv[1], "mo", 2) == 0) {
+ */
+ if (strncmp(argv[1], "mo", 2) == 0) {
struct fdt_header *newaddr;
int len;
int err;
- if (argc < 4) {
- printf ("Usage:\n%s\n", cmdtp->usage);
- return 1;
- }
+ if (argc < 4)
+ return CMD_RET_USAGE;
/*
* Set the address and length of the fdt.
}
working_fdt = newaddr;
- /********************************************************************
+ /*
* Make a new node
- ********************************************************************/
+ */
} else if (strncmp(argv[1], "mk", 2) == 0) {
char *pathp; /* path */
char *nodep; /* new node to add */
/*
* Parameters: Node path, new node to be appended to the path.
*/
- if (argc < 4) {
- printf ("Usage:\n%s\n", cmdtp->usage);
- return 1;
- }
+ if (argc < 4)
+ return CMD_RET_USAGE;
pathp = argv[2];
nodep = argv[3];
return 1;
}
- /********************************************************************
+ /*
* Set the value of a property in the working_fdt.
- ********************************************************************/
+ */
} else if (argv[1][0] == 's') {
char *pathp; /* path */
char *prop; /* property */
/*
* Parameters: Node path, property, optional value.
*/
- if (argc < 4) {
- printf ("Usage:\n%s\n", cmdtp->usage);
- return 1;
- }
+ if (argc < 4)
+ return CMD_RET_USAGE;
pathp = argv[2];
prop = argv[3];
}
/********************************************************************
- * Print (recursive) / List (single level)
+ * 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 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 */
+ char buf[11];
+
+ sprintf(buf, "%d", curIndex + 1);
+ setenv(var, buf);
+ } 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 */
+ 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 ((argv[1][0] == 'p') || (argv[1][0] == 'l')) {
int depth = MAX_LEVEL; /* how deep to print */
char *pathp; /* path */
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;
}
}
- /********************************************************************
+ /*
* 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));
fdt_num_mem_rsv(working_fdt));
printf("\n");
- /********************************************************************
+ /*
* 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);
- /********************************************************************
+ /*
* memory command
- ********************************************************************/
+ */
} else if (strncmp(argv[1], "me", 2) == 0) {
uint64_t addr, size;
int err;
-#ifdef CFG_64BIT_STRTOUL
- addr = simple_strtoull(argv[2], NULL, 16);
- size = simple_strtoull(argv[3], NULL, 16);
-#else
- addr = simple_strtoul(argv[2], NULL, 16);
- size = simple_strtoul(argv[3], NULL, 16);
-#endif
+ 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;
} else if (argv[2][0] == 'a') {
uint64_t addr, size;
int err;
-#ifdef CFG_64BIT_STRTOUL
addr = simple_strtoull(argv[3], NULL, 16);
size = simple_strtoull(argv[4], NULL, 16);
-#else
- addr = simple_strtoul(argv[3], NULL, 16);
- size = simple_strtoul(argv[4], NULL, 16);
-#endif
err = fdt_add_mem_rsv(working_fdt, addr, size);
if (err < 0) {
}
} else {
/* Unrecognized command */
- printf ("Usage:\n%s\n", cmdtp->usage);
- return 1;
+ return CMD_RET_USAGE;
}
}
#ifdef CONFIG_OF_BOARD_SETUP
else if (argv[1][0] == 'c') {
unsigned long initrd_start = 0, initrd_end = 0;
- if ((argc != 2) && (argc != 4)) {
- printf ("Usage:\n%s\n", cmdtp->usage);
- return 1;
- }
+ 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, initrd_start, initrd_end, 1);
- } else {
+ 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 */
- printf ("Usage:\n%s\n", cmdtp->usage);
- return 1;
+ return CMD_RET_USAGE;
}
return 0;
* data: A bytestream to be placed in the property
* len: The length of the resulting bytestream
*/
-static int fdt_parse_prop(char **newval, int count, char *data, int *len)
+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 */
cp = newp;
tmp = simple_strtoul(cp, &newp, 0);
- *(uint32_t *)data = __cpu_to_be32(tmp);
+ *(__be32 *)data = __cpu_to_be32(tmp);
data += 4;
*len += 4;
* Byte stream. Convert the values.
*/
newp++;
- while ((*newp != ']') && (stridx < count)) {
- tmp = simple_strtoul(newp, &newp, 16);
- *data++ = tmp & 0xFF;
- *len = *len + 1;
+ while ((stridx < count) && (*newp != ']')) {
while (*newp == ' ')
newp++;
- if (*newp != '\0')
+ 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);
}
} else {
/*
- * Assume it is a string. Copy it into our data area for
- * convenience (including the terminating '\0').
+ * Assume it is one or more strings. Copy it into our
+ * data area for convenience (including the
+ * terminating '\0's).
*/
while (stridx < count) {
- *len = strlen(newp) + 1;
+ size_t length = strlen(newp) + 1;
strcpy(data, newp);
+ data += length;
+ *len += length;
newp = newval[++stridx];
}
}
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
}
if ((len %4) == 0) {
- const u32 *p;
-
- printf("<");
- for (j = 0, p = data; j < len/4; j ++)
- printf("0x%x%s", p[j], j < (len/4 - 1) ? " " : "");
- printf(">");
+ 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 */
- const u8 *s;
-
- printf("[");
- for (j = 0, s = data; j < len; j++)
- printf("%02x%s", s[j], j < len - 1 ? " " : "");
- printf("]");
+ 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, 255, 0, do_fdt,
- "fdt - flattened device tree utility commands\n",
- "addr <addr> [<length>] - Set the fdt location to <addr>\n"
+#ifdef CONFIG_SYS_LONGHELP
+static char fdt_help_text[] =
+ "addr <addr> [<length>] - Set the fdt location to <addr>\n"
#ifdef CONFIG_OF_BOARD_SETUP
"fdt boardsetup - Do board-specific set up\n"
#endif
"fdt move <fdt> <newaddr> <length> - Copy the fdt to <addr> and make it active\n"
+ "fdt resize - Resize fdt to size + padding to 4k addr\n"
"fdt print <path> [<prop>] - Recursive print starting at <path>\n"
"fdt list <path> [<prop>] - Print one level starting at <path>\n"
+ "fdt get value <var> <path> <prop> - Get <property> and store in <var>\n"
+ "fdt get name <var> <path> <index> - Get name of node <index> and store in <var>\n"
+ "fdt get addr <var> <path> <prop> - Get start address of <property> and store in <var>\n"
+ "fdt get size <var> <path> [<prop>] - Get size of [<property>] or num nodes and store in <var>\n"
"fdt set <path> <prop> [<val>] - Set <property> [to <val>]\n"
"fdt mknode <path> <node> - Create a new node after <path>\n"
"fdt rm <path> [<prop>] - Delete the node or <property>\n"
"fdt rsvmem delete <index> - Delete a mem reserves\n"
"fdt chosen [<start> <end>] - Add/update the /chosen branch in the tree\n"
" <start>/<end> - initrd start/end addr\n"
- "NOTE: If the path or property you are setting/printing has a '#' character\n"
- " or spaces, you MUST escape it with a \\ character or quote it with \".\n"
+ "NOTE: Dereference aliases by omiting the leading '/', "
+ "e.g. fdt print ethernet0.";
+#endif
+
+U_BOOT_CMD(
+ fdt, 255, 0, do_fdt,
+ "flattened device tree utility commands", fdt_help_text
);