On Mon, Jan 12, 2026 at 03:19:25PM +0100, Herve Codina wrote: > The FDT_IMPORT_SYM dtb tag is a meta-data tag defining an imported > symbol. It can be present globally in an addon dtb (i.e. outside nodes > definition) meaning that this symbol needs to be resolved when the dtb > is applied. > > The tag is followed by two values and possible alignment paddings: > - name (string including \0) > The import symbol name. I.e. the name used to reference this > imported symbol. > - padding: > Padding (0x00) added to have the next value aligned on 32bit. > - compatible (string including \0) > The compatible string that can be used for symbol resolution. > This string can be an empty string if it is not relevant. > - padding: > Padding (0x00) added to have the next value aligned on 32bit. > > Example: > FDT_IMPORT_SYM 'foo1' 0x00 0x00 0x00 'bar,foo' > > This means that 'foo1' is an imported symbol and it is 'bar,foo' > compatible. > > This is what is encoded in the dtb when the related dts has the > following imported symbol defined: > /import/ foo1: "bar,foo"; > > If several symbols are imported, several FDT_IMPORT_SYM are present. > Each of them defining one imported symbol. For instance, importing > 'foo1' ("bar,foo" compatible) and 'baz1' ("bar,baz" compatible) leads > to the following sequence: > FDT_IMPORT_SYM 'foo1' 0x00 0x00 0x00 'bar,foo' > FDT_IMPORT_SYM 'baz1' 0x00 0x00 0x00 'bar,baz' > > If FDT_IMPORT_SYM tags are present in the dtb, they are present after > the root node definition (i.e. after the FDT_END_NODE related to the > first FDT_BEGIN_NODE). > > Add support for this new dtb tag. Since these are global to the whole tree, would a new block make more sense? > > Signed-off-by: Herve Codina > --- > fdtdump.c | 11 +++++++++ > flattree.c | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++- > libfdt/fdt.c | 28 ++++++++++++++++++++++ > libfdt/fdt.h | 1 + > 4 files changed, 107 insertions(+), 1 deletion(-) > > diff --git a/fdtdump.c b/fdtdump.c > index 8baadc4..04e6e38 100644 > --- a/fdtdump.c > +++ b/fdtdump.c > @@ -198,6 +198,17 @@ static void dump_blob(void *blob, bool debug) > continue; > } > > + if (tag == FDT_IMPORT_SYM) { > + s = p; > + p = PALIGN(p + strlen(s) + 1, 4); > + t = p; > + p = PALIGN(p + strlen(t) + 1, 4); > + > + printf("%*s// [FDT_IMPORT_SYM] '%s' (%s)\n", depth * shift, "", > + s, t); > + continue; > + } > + > fprintf(stderr, "%*s ** Unknown tag 0x%08"PRIx32"\n", depth * shift, "", tag); > break; > } > diff --git a/flattree.c b/flattree.c > index 47a289f..add02f0 100644 > --- a/flattree.c > +++ b/flattree.c > @@ -50,6 +50,7 @@ struct emitter { > void (*ref_phandle)(void *); > void (*export_sym)(void *); > void (*export_sym_ref)(void *); > + void (*import_sym)(void *); > }; > > static void bin_emit_cell(void *e, cell_t val) > @@ -119,6 +120,11 @@ static void bin_emit_export_sym_ref(void *e) > bin_emit_cell(e, FDT_EXPORT_SYM_REF); > } > > +static void bin_emit_import_sym(void *e) > +{ > + bin_emit_cell(e, FDT_IMPORT_SYM); > +} > + > static struct emitter bin_emitter = { > .cell = bin_emit_cell, > .string = bin_emit_string, > @@ -131,6 +137,7 @@ static struct emitter bin_emitter = { > .ref_phandle = bin_emit_ref_phandle, > .export_sym = bin_emit_export_sym, > .export_sym_ref = bin_emit_export_sym_ref, > + .import_sym = bin_emit_import_sym, > }; > > static void emit_label(FILE *f, const char *prefix, const char *label) > @@ -274,6 +281,17 @@ static void asm_emit_export_sym_ref(void *e) > asm_emit_cell(e, FDT_EXPORT_SYM_REF); > } > > +static void asm_emit_import_sym(void *e) > +{ > + /* > + * Import symbols are an feature introduced for addons. > + * Addons device-tree blob have to reason to be in the asm format. > + * > + * Need to be implemented if really needed. > + */ > + die("FDT_IMPORT_SYM not supported in asm output\n"); > +} > + > static struct emitter asm_emitter = { > .cell = asm_emit_cell, > .string = asm_emit_string, > @@ -286,6 +304,7 @@ static struct emitter asm_emitter = { > .ref_phandle = asm_emit_ref_phandle, > .export_sym = asm_emit_export_sym, > .export_sym = asm_emit_export_sym_ref, > + .import_sym = asm_emit_import_sym, > }; > > static int stringtable_insert(struct data *d, const char *str) > @@ -461,6 +480,26 @@ static void make_fdt_header(struct fdt_header *fdt, > fdt->dt_flags = cpu_to_fdt32(dt_flags); > } > > +static void flatten_imports(struct symbol *importsymlist, struct emitter *emit, > + void *etarget, struct version_info *vi, uint32_t dt_flags) > +{ > + struct symbol *importsym; > + > + if (!(vi->flags & FTF_EXPORT_IMPORT_SYM)) > + return; > + > + if (!(dt_flags & FDT_FLAG_ADDON) && importsymlist) > + die("Only addons can have an import list\n"); > + > + for_each_symbol(importsymlist, importsym) { > + emit->import_sym(etarget); > + emit->string(etarget, importsym->name, 0); > + emit->align(etarget, sizeof(cell_t)); > + emit->string(etarget, importsym->compatible, 0); > + emit->align(etarget, sizeof(cell_t)); > + } > +} > + > void dt_to_blob(FILE *f, struct dt_info *dti, int version) > { > struct version_info *vi = NULL; > @@ -483,6 +522,7 @@ void dt_to_blob(FILE *f, struct dt_info *dti, int version) > dt_flags |= dti->dtsflags & DTSF_ADDON ? FDT_FLAG_ADDON : 0; > > flatten_tree(dti->dt, &bin_emitter, &dtbuf, &strbuf, vi); > + flatten_imports(dti->importsymlist, &bin_emitter, &dtbuf, vi, dt_flags); > bin_emit_cell(&dtbuf, FDT_END); > > reservebuf = flatten_reserve_list(dti->reservelist, vi); > @@ -574,6 +614,9 @@ void dt_to_asm(FILE *f, struct dt_info *dti, int version) > const char *symprefix = "dt"; > uint32_t dt_flags = 0; > > + if (dti->importsymlist) > + die("Import symbols not supported in asm format\n"); > + > for (i = 0; i < ARRAY_SIZE(version_table); i++) { > if (version_table[i].version == version) > vi = &version_table[i]; > @@ -810,6 +853,16 @@ static struct property *flat_read_property(struct inbuf *dtbuf, > return build_property(name, val, NULL); > } > > +static struct symbol *flat_read_importsym(struct inbuf *inb) > +{ > + const char *name; > + const char *compatible; > + > + name = flat_read_string(inb); > + compatible = flat_read_string(inb); > + > + return build_importsym(name, compatible, NULL); > +} > > static struct reserve_info *flat_read_mem_reserve(struct inbuf *inb) > { > @@ -984,6 +1037,8 @@ struct dt_info *dt_from_blob(const char *fname) > int sizeleft; > struct reserve_info *reservelist; > struct node *tree; > + struct symbol *importsymlist = NULL; > + struct symbol *importsym; > uint32_t val; > int flags = 0; > unsigned int dtsflags = 0; > @@ -1098,6 +1153,17 @@ struct dt_info *dt_from_blob(const char *fname) > tree = unflatten_tree(&dtbuf, &strbuf, "", flags); > > val = flat_read_word(&dtbuf); > + > + if (dtsflags & DTSF_ADDON) { > + if (flags & FTF_EXPORT_IMPORT_SYM) { > + while (val == FDT_IMPORT_SYM) { > + importsym = flat_read_importsym(&dtbuf); > + add_symbol(&importsymlist, importsym); > + val = flat_read_word(&dtbuf); > + } > + } > + } > + > if (val != FDT_END) > die("Device tree blob doesn't end with FDT_END\n"); > > @@ -1105,5 +1171,5 @@ struct dt_info *dt_from_blob(const char *fname) > > fclose(f); > > - return build_dt_info(DTSF_V1 | dtsflags, reservelist, tree, boot_cpuid_phys, NULL); > + return build_dt_info(DTSF_V1 | dtsflags, reservelist, tree, boot_cpuid_phys, importsymlist); > } > diff --git a/libfdt/fdt.c b/libfdt/fdt.c > index febfa71..c169dd9 100644 > --- a/libfdt/fdt.c > +++ b/libfdt/fdt.c > @@ -271,6 +271,23 @@ uint32_t fdt_next_tag_full(const void *fdt, int startoffset, int *nextoffset) > return FDT_END; /* premature end */ > break; > > + case FDT_IMPORT_SYM: > + /* Skip name */ > + do { > + p = fdt_offset_ptr(fdt, offset++, 1); > + } while (p && (*p != '\0')); > + if (!can_assume(VALID_DTB) && !p) > + return FDT_END; /* premature end */ > + offset = FDT_CELLALIGN(offset); > + > + /* Skip compatible */ > + do { > + p = fdt_offset_ptr(fdt, offset++, 1); > + } while (p && (*p != '\0')); > + if (!can_assume(VALID_DTB) && !p) > + return FDT_END; /* premature end */ > + break; > + > default: > return FDT_END; > } > @@ -286,6 +303,7 @@ uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset) > { > uint32_t tag, tmp_tag; > int tmp_offset, tmp_next; > + int is_skip_to_end = 0; > > /* Retrieve next tag */ > tag = fdt_next_tag_full(fdt, startoffset, nextoffset); > @@ -300,6 +318,9 @@ uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset) > case FDT_END_NODE: > case FDT_PROP: > case FDT_NOP: > + if (is_skip_to_end) > + break; > + > case FDT_END: > /* > * Next tag is not a meta-data tag -> Ok this next tag > @@ -320,6 +341,13 @@ uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset) > */ > break; > > + case FDT_IMPORT_SYM: > + /* Those tags are available at the root level, after the > + * root node -> Skip everything until FDT_END > + */ > + is_skip_to_end = 1; > + break; > + > default: > break; > } > diff --git a/libfdt/fdt.h b/libfdt/fdt.h > index c23723b..b6c23ef 100644 > --- a/libfdt/fdt.h > +++ b/libfdt/fdt.h > @@ -67,6 +67,7 @@ struct fdt_property { > #define FDT_EXPORT_SYM 0xa /* export symbol: name, phandle value */ > #define FDT_EXPORT_SYM_REF 0xb /* export symbol: name, phandle value (maybe > unresolved), external label */ > +#define FDT_IMPORT_SYM 0xc /* import symbol: name, compatible */ > > #define FDT_V1_SIZE (7*sizeof(fdt32_t)) > #define FDT_V2_SIZE (FDT_V1_SIZE + sizeof(fdt32_t)) > -- > 2.52.0 > > -- David Gibson (he or they) | I'll have my music baroque, and my code david AT gibson.dropbear.id.au | minimalist, thank you, not the other way | around. http://www.ozlabs.org/~dgibson