Extract the core device tree printing logic from fdt_print_path() into a new fdt_print() function that takes a node offset parameter. This allows printing from any node offset rather than requiring a path string. Pass in the fdt, for more flexibility. Co-developed-by: Claude <noreply@anthropic.com> Signed-off-by: Simon Glass <sjg@chromium.org>
245 lines
5.5 KiB
C
245 lines
5.5 KiB
C
// 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);
|
|
}
|