Files
u-boot/lib/fdt_print.c
Simon Glass a52a0036bc lib: Add fdt_print() to print a node
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>
2025-08-27 12:46:48 -06:00

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);
}