Merge branch 'apa' into 'master'
Allow printing fdt nodes from outside the 'fdt' command See merge request u-boot/u-boot!164
This commit is contained in:
227
cmd/fdt.c
227
cmd/fdt.c
@@ -27,8 +27,6 @@
|
||||
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.
|
||||
@@ -56,7 +54,7 @@ void set_working_fdt_addr(ulong addr)
|
||||
static int fdt_value_env_set(const void *nodep, int len,
|
||||
const char *var, int index)
|
||||
{
|
||||
if (is_printable_string(nodep, len)) {
|
||||
if (fdt_printable_str(nodep, len)) {
|
||||
const char *nodec = (const char *)nodep;
|
||||
int i;
|
||||
|
||||
@@ -526,7 +524,7 @@ static int do_fdt(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
|
||||
else
|
||||
prop = NULL;
|
||||
|
||||
ret = fdt_print(pathp, prop, depth);
|
||||
ret = fdt_print_path(pathp, prop, depth);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
@@ -890,227 +888,6 @@ static int fdt_parse_prop(char * const *newval, int count, char *data, int *len)
|
||||
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;
|
||||
const char *ss, *se;
|
||||
|
||||
/* zero length is not */
|
||||
if (len == 0)
|
||||
return 0;
|
||||
|
||||
/* must terminate with zero */
|
||||
if (s[len - 1] != '\0')
|
||||
return 0;
|
||||
|
||||
se = s + len;
|
||||
|
||||
while (s < se) {
|
||||
ss = s;
|
||||
while (s < se && *s && isprint((unsigned char)*s))
|
||||
s++;
|
||||
|
||||
/* not zero, or not done yet */
|
||||
if (*s != '\0' || s == ss)
|
||||
return 0;
|
||||
|
||||
s++;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/********************************************************************/
|
||||
U_BOOT_LONGHELP(fdt,
|
||||
"addr [-c] [-q] <addr> [<size>] - Set the [control] fdt location to <addr>\n"
|
||||
|
||||
@@ -490,4 +490,43 @@ void fdt_fixup_pstore(void *blob);
|
||||
*/
|
||||
int fdt_kaslrseed(void *blob, bool overwrite);
|
||||
|
||||
/**
|
||||
* fdt_printable_str() - checks if a property value appears to be a string
|
||||
*
|
||||
* Check a property of a given length to see if it is all printable and
|
||||
* has a valid terminator. The property can contain either a single string,
|
||||
* or multiple strings each of non-zero length.
|
||||
*
|
||||
* @data: data to check
|
||||
* @len: data length including terminator
|
||||
* Return: true if a valid printable string, false if not
|
||||
*/
|
||||
bool fdt_printable_str(const void *data, int len);
|
||||
|
||||
/*
|
||||
* fdt_print() - Print a portion of the device tree starting from a node
|
||||
*
|
||||
* Recursively prints the device tree starting from the given node offset.
|
||||
* The depth parameter controls how deeply nested nodes are printed.
|
||||
*
|
||||
* @fdt: Pointer to the device tree blob
|
||||
* @nodeoffset: Node offset to start printing from
|
||||
* @depth: Maximum depth to print
|
||||
* Return: 0 on success, 1 on error
|
||||
*/
|
||||
int fdt_print(const void *fdt, int nodeoffset, int depth);
|
||||
|
||||
/**
|
||||
* fdt_print_path() - Print a portion of the device tree
|
||||
*
|
||||
* Recursively prints the working device tree starting from the given path.
|
||||
* The depth parameter controls how deeply nested nodes are printed.
|
||||
*
|
||||
* @pathp: Path to the starting node
|
||||
* @prop: Property to print (if NULL, prints all properties)
|
||||
* @depth: Maximum depth to print
|
||||
* Return: 0 on success, 1 on error
|
||||
*/
|
||||
int fdt_print_path(const char *pathp, char *prop, int depth);
|
||||
|
||||
#endif /* ifndef __FDT_SUPPORT_H */
|
||||
|
||||
@@ -124,7 +124,7 @@ obj-$(CONFIG_$(PHASE_)CRC32) += crc32.o
|
||||
obj-$(CONFIG_CRC32C) += crc32c.o
|
||||
obj-y += ctype.o
|
||||
obj-y += div64.o
|
||||
obj-$(CONFIG_$(PHASE_)OF_LIBFDT) += fdtdec.o fdtdec_common.o
|
||||
obj-$(CONFIG_$(PHASE_)OF_LIBFDT) += fdtdec.o fdtdec_common.o fdt_print.o
|
||||
obj-y += hang.o
|
||||
obj-y += linux_compat.o
|
||||
obj-y += linux_string.o
|
||||
|
||||
244
lib/fdt_print.c
Normal file
244
lib/fdt_print.c
Normal file
@@ -0,0 +1,244 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2007
|
||||
* Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com
|
||||
* Based on code written by:
|
||||
* Pantelis Antoniou <pantelis.antoniou@gmail.com> and
|
||||
* Matthew McClintock <msm@freescale.com>
|
||||
*/
|
||||
|
||||
#include <env.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/libfdt.h>
|
||||
#include <fdt_support.h>
|
||||
|
||||
#define MAX_LEVEL 32 /* how deeply nested we will go */
|
||||
|
||||
bool fdt_printable_str(const void *data, int len)
|
||||
{
|
||||
const char *s = data;
|
||||
const char *ss, *se;
|
||||
|
||||
/* zero length is not */
|
||||
if (!len)
|
||||
return false;
|
||||
|
||||
/* must terminate with zero */
|
||||
if (s[len - 1])
|
||||
return false;
|
||||
|
||||
se = s + len;
|
||||
|
||||
while (s < se) {
|
||||
ss = s;
|
||||
while (s < se && *s && isprint((unsigned char)*s))
|
||||
s++;
|
||||
|
||||
/* not zero, or not done yet */
|
||||
if (*s || s == ss)
|
||||
return false;
|
||||
|
||||
s++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 (fdt_printable_str(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) an fdt starting from a node.
|
||||
* The depth parameter determines how deeply nested the fdt is printed.
|
||||
*/
|
||||
int fdt_print(const void *fdt, int nodeoffset, 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 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;
|
||||
const char *pathp;
|
||||
|
||||
/*
|
||||
* Print the node and all subnodes.
|
||||
*/
|
||||
while(level >= 0) {
|
||||
tag = fdt_next_tag(fdt, nodeoffset, &nextoffset);
|
||||
switch(tag) {
|
||||
case FDT_BEGIN_NODE:
|
||||
pathp = fdt_get_name(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(fdt, nodeoffset,
|
||||
sizeof(*fdt_prop));
|
||||
pathp = fdt_string(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;
|
||||
}
|
||||
|
||||
/*
|
||||
* Print a portion of the working_fdt starting from a path.
|
||||
* The depth parameter determines how deeply nested the fdt is printed.
|
||||
*/
|
||||
int fdt_print_path(const char *pathp, char *prop, int depth)
|
||||
{
|
||||
const void *nodep; /* property node pointer */
|
||||
int nodeoffset; /* node offset from libfdt */
|
||||
int len; /* length of the property */
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/* Print the node and all subnodes using fdt_print() */
|
||||
return fdt_print(working_fdt, nodeoffset, depth);
|
||||
}
|
||||
@@ -136,7 +136,7 @@ static int make_fuller_fdt(struct unit_test_state *uts, void *fdt, int size,
|
||||
/*
|
||||
* <u32>
|
||||
* This value is deliberate as it used to break cmd/fdt.c
|
||||
* is_printable_string() implementation.
|
||||
* fdt_printable_str() implementation.
|
||||
*/
|
||||
ut_assertok(fdt_property_u32(fdt, "clock-frequency", 16640000));
|
||||
/* <prop-encoded-array> */
|
||||
|
||||
Reference in New Issue
Block a user