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:
Simon Glass
2025-08-27 22:11:12 +00:00
5 changed files with 287 additions and 227 deletions

227
cmd/fdt.c
View File

@@ -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"

View File

@@ -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 */

View File

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

View File

@@ -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> */