Compare commits

...

9 commits

Author SHA1 Message Date
Uwe Kleine-König
a26ef6400b Restore phandle references from __fixups__ node
Some checks failed
Build test / build-make (ubuntu) (push) Has been cancelled
Build test / build-make (alpine) (push) Has been cancelled
Build test / build-make (archlinux) (push) Has been cancelled
Build test / build-make (fedora) (push) Has been cancelled
Build test / build-meson (alpine) (push) Has been cancelled
Build test / build-meson (archlinux) (push) Has been cancelled
Build test / build-meson (fedora) (push) Has been cancelled
Build test / build-meson (ubuntu) (push) Has been cancelled
Build test / clang64 (push) Has been cancelled
Build test / mingw32 (push) Has been cancelled
Build test / mingw64 (push) Has been cancelled
Build test / ucrt64 (push) Has been cancelled
The __fixups__ node contains information about labels. Parse its
properties to create phandle markers which improve the resulting dts
when decompiling a device tree blob.

Reviewed-by: David Gibson <david@gibson.dropbear.id.au>
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@baylibre.com>
Message-ID: <20250919092912.663304-14-u.kleine-koenig@baylibre.com>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
2026-01-16 13:45:59 +11:00
Uwe Kleine-König
05c524db44 Restore phandle references from __local_fixups__ node
The __local_fixups__ node contains information about phandles. Parse it
to improve the result when decompiling a device tree blob.

Signed-off-by: Uwe Kleine-König <u.kleine-koenig@baylibre.com>
Message-ID: <20250919092912.663304-13-u.kleine-koenig@baylibre.com>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
2026-01-16 13:45:12 +11:00
Uwe Kleine-König
db65a3a3f4 Restore labels from __symbols__ node
If the input has a __symbols__ node, restore the named labels for the
respective nodes.

Reviewed-by: David Gibson <david@gibson.dropbear.id.au>
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@baylibre.com>
Message-ID: <20250919092912.663304-12-u.kleine-koenig@baylibre.com>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
2026-01-16 13:37:35 +11:00
Uwe Kleine-König
64330c682c Improve type guessing when compiling to dts format
In the presence of (non-type) markers guess the type of each chunk
between markers individually instead of only once for the whole
property.

Note that this only gets relevant with the next few commits that restore
labels and phandles. Note further that this rework is necessary with
these further changes, because phandle markers are currently not
considered for type guessing and so a phandle at an offset that isn't a
multiple of 4 triggers an assertion if the property was guessed to have
type TYPE_UINT32.

Now that guess_value_type() is only called for data chunks without
markers, the function can be simplified a bit.

Reviewed-by: David Gibson <david@gibson.dropbear.id.au>
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@baylibre.com>
Message-ID: <20250919092912.663304-11-u.kleine-koenig@baylibre.com>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
2026-01-16 13:33:45 +11:00
Uwe Kleine-König
cbb48690c6 Set DTSF_PLUGIN if needed when compiling from dtb
The need for the plugin flag is determined by the existence of __fixups__
or __local_fixups__.

This is a bit simplifying because if __fixups__ or __local_fixups__
exist but don't have properties, the plugin flag isn't needed. But in
practise the test should be good enough such that this corner case
doesn't matter.

Reviewed-by: David Gibson <david@gibson.dropbear.id.au>
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@baylibre.com>
Message-ID: <20250919092912.663304-10-u.kleine-koenig@baylibre.com>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
2026-01-16 12:02:30 +11:00
Uwe Kleine-König
ef3b1baf63 Emit /plugin/ when compiling to .dts with DTSF_PLUGIN set
This fixes `dtc -I dts -O dts` to make the file a plugin if the source
file is one.

Reviewed-by: David Gibson <david@gibson.dropbear.id.au>
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@baylibre.com>
Message-ID: <20250919092912.663304-9-u.kleine-koenig@baylibre.com>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
2026-01-16 12:02:30 +11:00
Maldus512
7c78c8542d Added empty node name check
The Devicetree specification states that a node name shall be of form
`node-name@unit-address` and that the `node-name` component cannot be
empty.

However, the `dtc` parser considers a node name as a non-empty
sequences of the allowed characters plus the @ character, and
unit-address extraction is processed after parsing.

This has the side effect of considering an empty name plus an address
as a valid node name (e.g. `@0`).  I've added the node_name_not_empty
check, verifying that the `node->basenamelen` is not zero (unless it's
the root node).

Signed-off-by: Mattia Maldini <mattia512maldini@gmail.com>
[dwg: Re-wrap commit message]
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
2026-01-16 11:51:53 +11:00
Herve Codina
14dd76b967 fdtdump: Change FDT_PROP prob handling to ease future addition
In order to ease future tags addition, perform operation related to
FDT_PROP when the tag is explicitly FDT_PROP instead of relying to a
kind of default value case.

Handle the FDT_PROP tag exactly in the same way as it is done for
other tags.

No functional modification.

Signed-off-by: Herve Codina <herve.codina@bootlin.com>
Message-ID: <20260112142009.1006236-6-herve.codina@bootlin.com>
Reviewed-by: Ayush Singh <ayush@beagleboard.org>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
2026-01-15 11:26:20 +11:00
Stephen Gallagher
9a1c801a1a Fix discarded const qualifiers
It's unsafe to implicitly discard the const qualifier on a pointer. In
overlay_fixup_phandle(), this was probably just an oversight, and making
the "sep" variable a const char * is sufficient to fix it.

In create_node(), however, the "p" variable is directly modifying the
buffer pointed to by "const char* node_name". To fix this, we need to
actually make a duplicate of the buffer and operate on that instead.

This introduces a malloc()/free()  and an unbounded strdup() into the
operation, but fdtput isn't a long-running service and the node_name
argument comes directly from argv, so this shouldn't introduce a
significant performance impact.

Signed-off-by: Stephen Gallagher <sgallagh@redhat.com>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
2026-01-10 18:55:38 +11:00
10 changed files with 307 additions and 52 deletions

View file

@ -340,6 +340,14 @@ static void check_node_name_format(struct check *c, struct dt_info *dti,
}
ERROR(node_name_format, check_node_name_format, NULL, &node_name_chars);
static void check_node_name_not_empty(struct check *c, struct dt_info *dti,
struct node *node)
{
if (node->basenamelen == 0 && node->parent != NULL)
FAIL(c, dti, node, "Empty node name");
}
ERROR(node_name_not_empty, check_node_name_not_empty, NULL, &node_name_chars);
static void check_node_name_vs_property_name(struct check *c,
struct dt_info *dti,
struct node *node)
@ -1899,7 +1907,7 @@ WARNING(graph_endpoint, check_graph_endpoint, NULL, &graph_nodes);
static struct check *check_table[] = {
&duplicate_node_names, &duplicate_property_names,
&node_name_chars, &node_name_format, &property_name_chars,
&node_name_chars, &node_name_format, &node_name_not_empty, &property_name_chars,
&name_is_string, &name_properties, &node_name_vs_property_name,
&duplicate_label,

5
dtc.c
View file

@ -338,9 +338,14 @@ int main(int argc, char *argv[])
if (auto_label_aliases)
generate_label_tree(dti, "aliases", false);
generate_labels_from_tree(dti, "__symbols__");
if (generate_symbols)
generate_label_tree(dti, "__symbols__", true);
fixup_phandles(dti, "__fixups__");
local_fixup_phandles(dti, "__local_fixups__");
if (generate_fixups) {
generate_fixups_tree(dti, "__fixups__");
generate_local_fixups_tree(dti, "__local_fixups__");

6
dtc.h
View file

@ -339,9 +339,12 @@ struct dt_info *build_dt_info(unsigned int dtsflags,
struct reserve_info *reservelist,
struct node *tree, uint32_t boot_cpuid_phys);
void sort_tree(struct dt_info *dti);
void generate_labels_from_tree(struct dt_info *dti, const char *name);
void generate_label_tree(struct dt_info *dti, const char *name, bool allocph);
void generate_fixups_tree(struct dt_info *dti, const char *name);
void fixup_phandles(struct dt_info *dti, const char *name);
void generate_local_fixups_tree(struct dt_info *dti, const char *name);
void local_fixup_phandles(struct dt_info *dti, const char *name);
/* Checks */
@ -357,6 +360,9 @@ struct dt_info *dt_from_blob(const char *fname);
/* Tree source */
void property_add_marker(struct property *prop,
enum markertype type, unsigned int offset, char *ref);
void add_phandle_marker(struct dt_info *dti, struct property *prop, unsigned int offset);
void dt_to_source(FILE *f, struct dt_info *dti);
struct dt_info *dt_from_source(const char *f);

View file

@ -129,23 +129,25 @@ static void dump_blob(void *blob, bool debug)
continue;
}
if (tag != FDT_PROP) {
fprintf(stderr, "%*s ** Unknown tag 0x%08"PRIx32"\n", depth * shift, "", tag);
break;
if (tag == FDT_PROP) {
sz = fdt32_to_cpu(GET_CELL(p));
s = p_strings + fdt32_to_cpu(GET_CELL(p));
if (version < 16 && sz >= 8)
p = PALIGN(p, 8);
t = p;
p = PALIGN(p + sz, 4);
dumpf("%04"PRIxPTR": string: %s\n", (uintptr_t)s - blob_off, s);
dumpf("%04"PRIxPTR": value\n", (uintptr_t)t - blob_off);
printf("%*s%s", depth * shift, "", s);
utilfdt_print_data(t, sz);
printf(";\n");
continue;
}
sz = fdt32_to_cpu(GET_CELL(p));
s = p_strings + fdt32_to_cpu(GET_CELL(p));
if (version < 16 && sz >= 8)
p = PALIGN(p, 8);
t = p;
p = PALIGN(p + sz, 4);
dumpf("%04"PRIxPTR": string: %s\n", (uintptr_t)s - blob_off, s);
dumpf("%04"PRIxPTR": value\n", (uintptr_t)t - blob_off);
printf("%*s%s", depth * shift, "", s);
utilfdt_print_data(t, sz);
printf(";\n");
fprintf(stderr, "%*s ** Unknown tag 0x%08"PRIx32"\n", depth * shift, "", tag);
break;
}
}

View file

@ -254,19 +254,21 @@ static int create_paths(char **blob, const char *in_path)
static int create_node(char **blob, const char *node_name)
{
int node = 0;
char *p;
const char *p;
char *path = NULL;
p = strrchr(node_name, '/');
if (!p) {
report_error(node_name, -1, -FDT_ERR_BADPATH);
return -1;
}
*p = '\0';
*blob = realloc_node(*blob, p + 1);
if (p > node_name) {
node = fdt_path_offset(*blob, node_name);
path = xstrndup(node_name, (size_t)(p - node_name));
node = fdt_path_offset(*blob, path);
free(path);
if (node < 0) {
report_error(node_name, -1, node);
return -1;

View file

@ -807,6 +807,7 @@ struct dt_info *dt_from_blob(const char *fname)
struct node *tree;
uint32_t val;
int flags = 0;
unsigned int dtsflags = DTSF_V1;
f = srcfile_relative_open(fname, NULL);
@ -919,5 +920,8 @@ struct dt_info *dt_from_blob(const char *fname)
fclose(f);
return build_dt_info(DTSF_V1, reservelist, tree, boot_cpuid_phys);
if (get_subnode(tree, "__fixups__") || get_subnode(tree, "__local_fixups__"))
dtsflags |= DTSF_PLUGIN;
return build_dt_info(dtsflags, reservelist, tree, boot_cpuid_phys);
}

View file

@ -407,7 +407,8 @@ static int overlay_fixup_phandle(void *fdt, void *fdto, int symbols_off,
const char *fixup_str = value;
uint32_t path_len, name_len;
uint32_t fixup_len;
char *sep, *endptr;
const char *sep;
char *endptr;
int poffset, ret;
fixup_end = memchr(value, '\0', len);

View file

@ -1112,6 +1112,27 @@ static int generate_local_fixups_tree_internal(struct dt_info *dti,
return ret;
}
void generate_labels_from_tree(struct dt_info *dti, const char *name)
{
struct node *an;
struct property *p;
an = get_subnode(dti->dt, name);
if (!an)
return;
for_each_property(an, p) {
struct node *labeled_node;
labeled_node = get_node_by_path(dti->dt, p->val.val);
if (labeled_node)
add_label(&labeled_node->labels, p->name);
else if (quiet < 1)
fprintf(stderr, "Warning: Path %s referenced in property %s/%s missing",
p->val.val, name, p->name);
}
}
void generate_label_tree(struct dt_info *dti, const char *name, bool allocph)
{
if (!any_label_tree(dti, dti->dt))
@ -1130,6 +1151,102 @@ void generate_fixups_tree(struct dt_info *dti, const char *name)
name);
}
void fixup_phandles(struct dt_info *dti, const char *name)
{
struct node *an;
struct property *fp;
an = get_subnode(dti->dt, name);
if (!an)
return;
for_each_property(an, fp) {
char *fnext = fp->val.val;
char *fv;
unsigned int fl;
while ((fl = fp->val.len - (fnext - fp->val.val))) {
char *propname, *soffset;
struct node *n;
struct property *p;
long offset;
fv = fnext;
fnext = memchr(fv, 0, fl);
if (!fnext) {
if (quiet < 1)
fprintf(stderr, "Warning: Malformed fixup entry for label %s\n",
fp->name);
break;
}
fnext += 1;
propname = memchr(fv, ':', fnext - 1 - fv);
if (!propname) {
if (quiet < 1)
fprintf(stderr, "Warning: Malformed fixup entry for label %s\n",
fp->name);
continue;
}
propname++;
soffset = memchr(propname, ':', fnext - 1 - propname);
if (!soffset) {
if (quiet < 1)
fprintf(stderr, "Warning: Malformed fixup entry for label %s\n",
fp->name);
continue;
}
soffset++;
/*
* temporarily modify the property to not have to create
* a copy for the node path.
*/
*(propname - 1) = '\0';
n = get_node_by_path(dti->dt, fv);
if (!n && quiet < 1)
fprintf(stderr, "Warning: Label %s references non-existing node %s\n",
fp->name, fv);
*(propname - 1) = ':';
if (!n)
continue;
/*
* temporarily modify the property to not have to create
* a copy for the property name.
*/
*(soffset - 1) = '\0';
p = get_property(n, propname);
if (!p && quiet < 1)
fprintf(stderr, "Warning: Label %s references non-existing property %s in node %s\n",
fp->name, n->fullpath, propname);
*(soffset - 1) = ':';
if (!p)
continue;
offset = strtol(soffset, NULL, 0);
if (offset < 0 || offset + 4 > p->val.len) {
if (quiet < 1)
fprintf(stderr,
"Warning: Label %s contains invalid offset for property %s in node %s\n",
fp->name, p->name, n->fullpath);
continue;
}
property_add_marker(p, REF_PHANDLE, offset, fp->name);
}
}
}
void generate_local_fixups_tree(struct dt_info *dti, const char *name)
{
if (!any_local_fixup_tree(dti, dti->dt))
@ -1139,3 +1256,60 @@ void generate_local_fixups_tree(struct dt_info *dti, const char *name)
"Warning: Preexisting data in %s malformed, some content could not be added.\n",
name);
}
static void local_fixup_phandles_node(struct dt_info *dti, struct node *lf, struct node *n)
{
struct property *lfp;
struct node *lfsubnode;
for_each_property(lf, lfp) {
struct property *p = get_property(n, lfp->name);
fdt32_t *offsets = (fdt32_t *)lfp->val.val;
size_t i;
if (!p) {
if (quiet < 1)
fprintf(stderr, "Warning: Property %s in %s referenced in __local_fixups__ missing\n",
lfp->name, n->fullpath);
continue;
}
/*
* Each property in the __local_fixups__ tree is a concatenation
* of offsets, so it must be a multiple of sizeof(fdt32_t).
*/
if (lfp->val.len % sizeof(fdt32_t)) {
if (quiet < 1)
fprintf(stderr, "Warning: property %s in /__local_fixups__%s malformed\n",
lfp->name, n->fullpath);
continue;
}
for (i = 0; i < lfp->val.len / sizeof(fdt32_t); i++)
add_phandle_marker(dti, p, dtb_ld32(offsets + i));
}
for_each_child(lf, lfsubnode) {
struct node *subnode = get_subnode(n, lfsubnode->name);
if (!subnode) {
if (quiet < 1)
fprintf(stderr, "Warning: node %s/%s referenced in __local_fixups__ missing\n",
lfsubnode->name, n->fullpath);
continue;
}
local_fixup_phandles_node(dti, lfsubnode, subnode);
}
}
void local_fixup_phandles(struct dt_info *dti, const char *name)
{
struct node *an;
an = get_subnode(dti->dt, name);
if (!an)
return;
local_fixup_phandles_node(dti, an, dti->dt);
}

View file

@ -18,6 +18,7 @@ add_project_arguments(
'-Wshadow',
'-Wsuggest-attribute=format',
'-Wwrite-strings',
'-Wdiscarded-qualifiers',
]),
language: 'c'
)

View file

@ -173,23 +173,59 @@ static struct marker **add_marker(struct marker **mi,
return &nm->next;
}
static void add_string_markers(struct property *prop)
void property_add_marker(struct property *prop,
enum markertype type, unsigned int offset, char *ref)
{
int l, len = prop->val.len;
const char *p = prop->val.val;
add_marker(&prop->val.markers, type, offset, ref);
}
static void add_string_markers(struct property *prop, unsigned int offset, int len)
{
int l;
const char *p = prop->val.val + offset;
struct marker **mi = &prop->val.markers;
for (l = strlen(p) + 1; l < len; l += strlen(p + l) + 1)
mi = add_marker(mi, TYPE_STRING, l, NULL);
mi = add_marker(mi, TYPE_STRING, offset + l, NULL);
}
static enum markertype guess_value_type(struct property *prop)
void add_phandle_marker(struct dt_info *dti, struct property *prop, unsigned int offset)
{
int len = prop->val.len;
const char *p = prop->val.val;
struct marker *m = prop->val.markers;
cell_t phandle;
struct node *refn;
char *ref;
if (prop->val.len < offset + 4) {
if (quiet < 1)
fprintf(stderr,
"Warning: property %s too short to contain a phandle at offset %u\n",
prop->name, offset);
return;
}
phandle = dtb_ld32(prop->val.val + offset);
refn = get_node_by_phandle(dti->dt, phandle);
if (!refn) {
if (quiet < 1)
fprintf(stderr,
"Warning: node referenced by phandle 0x%x in property %s not found\n",
phandle, prop->name);
return;
}
if (refn->labels)
ref = refn->labels->label;
else
ref = refn->fullpath;
add_marker(&prop->val.markers, REF_PHANDLE, offset, ref);
}
static enum markertype guess_value_type(struct property *prop, unsigned int offset, int len)
{
const char *p = prop->val.val + offset;
int nnotstring = 0, nnul = 0;
int nnotstringlbl = 0, nnotcelllbl = 0;
int i;
for (i = 0; i < len; i++) {
@ -199,30 +235,49 @@ static enum markertype guess_value_type(struct property *prop)
nnul++;
}
for_each_marker_of_type(m, LABEL) {
if ((m->offset > 0) && (prop->val.val[m->offset - 1] != '\0'))
nnotstringlbl++;
if ((m->offset % sizeof(cell_t)) != 0)
nnotcelllbl++;
}
if ((p[len-1] == '\0') && (nnotstring == 0) && (nnul <= (len-nnul))
&& (nnotstringlbl == 0)) {
if ((p[len-1] == '\0') && (nnotstring == 0) && (nnul <= len - nnul)) {
if (nnul > 1)
add_string_markers(prop);
add_string_markers(prop, offset, len);
return TYPE_STRING;
} else if (((len % sizeof(cell_t)) == 0) && (nnotcelllbl == 0)) {
} else if ((len % sizeof(cell_t)) == 0) {
return TYPE_UINT32;
}
return TYPE_UINT8;
}
static void guess_type_markers(struct property *prop)
{
struct marker **m = &prop->val.markers;
unsigned int offset = 0;
for (m = &prop->val.markers; *m; m = &((*m)->next)) {
if (is_type_marker((*m)->type))
/* assume the whole property is already marked */
return;
if ((*m)->offset > offset) {
m = add_marker(m, guess_value_type(prop, offset, (*m)->offset - offset),
offset, NULL);
offset = (*m)->offset;
}
if ((*m)->type == REF_PHANDLE) {
m = add_marker(m, TYPE_UINT32, offset, NULL);
offset += 4;
}
}
if (offset < prop->val.len)
add_marker(m, guess_value_type(prop, offset, prop->val.len - offset),
offset, NULL);
}
static void write_propval(FILE *f, struct property *prop)
{
size_t len = prop->val.len;
struct marker *m = prop->val.markers;
struct marker dummy_marker;
struct marker *m;
enum markertype emit_type = TYPE_NONE;
char *srcstr;
@ -241,14 +296,8 @@ static void write_propval(FILE *f, struct property *prop)
fprintf(f, " =");
if (!next_type_marker(m)) {
/* data type information missing, need to guess */
dummy_marker.type = guess_value_type(prop);
dummy_marker.next = prop->val.markers;
dummy_marker.offset = 0;
dummy_marker.ref = NULL;
m = &dummy_marker;
}
guess_type_markers(prop);
m = prop->val.markers;
for_each_marker(m) {
size_t chunk_len = (m->next ? m->next->offset : len) - m->offset;
@ -369,7 +418,10 @@ void dt_to_source(FILE *f, struct dt_info *dti)
{
struct reserve_info *re;
fprintf(f, "/dts-v1/;\n\n");
fprintf(f, "/dts-v1/;\n");
if (dti->dtsflags & DTSF_PLUGIN)
fprintf(f, "/plugin/;\n");
fprintf(f, "\n");
for (re = dti->reservelist; re; re = re->next) {
struct label *l;