On Mon, Jan 12, 2026 at 03:19:54PM +0100, Herve Codina wrote: > Referencing a sub-node from an orphan node using a path is needed. > > Indeed, using the following snippet: > --- 8< --- > /addon/; > > &node1 { > subnode { > foo-phandle = <&foo_label>; > }; > }; > > &node2 { > foo_label: foo { > prop = <1>; > }; > }; > --- 8< --- > > Even if node2 is an orphan node, foo is a local node. foo-phandle > references the foo node using a label. Another option would be to eliminate the idea of local references, and require a symbol be attached to things that you want to reference by label. > > Once converted to a dtb, the label is lost. Only the phandle of the foo > node is used in the foo-phandle property and this property is marked > FDT_REF_LOCAL. > > Converting back this dtb to a dts, the marked local phandle should be > translated to a path to the related local node. > > The issue is that this local node is not in a root device tree. We need > to identify the orphan node the foo node belongs to. > > We cannot use a path starting by '/'. This kind of path identify node in > the root tree. > > This new syntax allows to identify the orphan node in a path: > $/ > > This leads to a reference by path in the form &{$/}. > > Using the previous example, those both phandles points to the same node: > foo-phandle1 = <&foo_label>; /* Reference by label */ > foo-phandle2 = <&{$node2/foo}>; /* Reference by path */ > > When the dtb is converted back to a dts, the marked local phandle > involving subnode available from orphan nodes can be translated to a > reference by path thanks to the new syntax. > > Add support for this &{$/} syntax to reference by > path a local node from an orphan node. > > Signed-off-by: Herve Codina > --- > dtc-lexer.l | 7 +++++++ > dtc-parser.y | 19 +++++++++++++++++++ > dtc.c | 22 ++++++++++++++++++++-- > livetree.c | 14 +++++++++++++- > treesource.c | 3 ++- > 5 files changed, 61 insertions(+), 4 deletions(-) > > diff --git a/dtc-lexer.l b/dtc-lexer.l > index cb616f9..540bfdf 100644 > --- a/dtc-lexer.l > +++ b/dtc-lexer.l > @@ -239,6 +239,13 @@ static void PRINTF(1, 2) lexical_error(const char *fmt, ...); > return DT_PATH_REF; > } > > +<*>"&{\$"{LABEL}([/]{PATHCHAR}*)?\} { /* orphan path reference */ > + yytext[yyleng-1] = '\0'; > + DPRINT("Ref orphan path: %s\n", yytext+1); > + yylval.labelref = xstrdup(yytext+2); > + return DT_ORPHAN_PATH_REF; > + } > + > [0-9a-fA-F]{2} { > yylval.byte = strtol(yytext, NULL, 16); > DPRINT("Byte: %02x\n", (int)yylval.byte); > diff --git a/dtc-parser.y b/dtc-parser.y > index 7f8c294..9d619cd 100644 > --- a/dtc-parser.y > +++ b/dtc-parser.y > @@ -111,6 +111,7 @@ static struct node *parser_get_node_by_ref(struct node *dt, struct node *orphanl > %token DT_LABEL > %token DT_LABEL_REF > %token DT_PATH_REF > +%token DT_ORPHAN_PATH_REF > %token DT_INCBIN > > %type propdata > @@ -589,6 +590,24 @@ arrayprefix: > ERROR(&@2, "References are only allowed in " > "arrays with 32-bit elements."); > > + $$.data = data_append_integer($1.data, val, $1.bits); > + } > + | arrayprefix DT_ORPHAN_PATH_REF > + { > + uint64_t val = ~0ULL >> (64 - $1.bits); > + > + if ($1.bits == 32) { > + if (!(last_header_flags & DTSF_ADDON)) > + ERROR(&@2, "Orphan path reference %s supported only in addon", $2); > + > + $1.data = data_add_marker($1.data, > + REF_PHANDLE, > + $2); > + } else { > + ERROR(&@2, "References are only allowed in " > + "arrays with 32-bit elements."); > + } > + > $$.data = data_append_integer($1.data, val, $1.bits); > } > | arrayprefix DT_LABEL > diff --git a/dtc.c b/dtc.c > index 63725bf..72d85e4 100644 > --- a/dtc.c > +++ b/dtc.c > @@ -48,13 +48,31 @@ static void fill_fullpaths(struct node *tree, const char *prefix) > static void dti_fill_fullpaths(struct dt_info *dti) > { > struct node *orphan; > + struct node *child; > > /* Fill fullpaths for the root node */ > if (dti->dt) > fill_fullpaths(dti->dt, ""); > > - for_each_orphan(dti->orphanlist, orphan) > - fill_fullpaths(orphan, "__orphan__/"); > + /* Fill fullpaths for orphan nodes */ > + for_each_orphan(dti->orphanlist, orphan) { > + if (orphan->name[0] == '\0') > + die("orphan node has an empty name\n"); > + > + /* > + * An orphan node name is set with its reference. > + * Its name is in the form "&xxxxxx". > + * For its full path, we use "$xxxxx" to make a clear > + * distinction between a reference (&xxxx) where a resolution > + * could be involved vs a "simple" path where we just need to > + * identified the orphan ($xxxx). > + */ > + xasprintf(&orphan->fullpath, "$%s", orphan->name + 1); > + orphan->basenamelen = strlen(orphan->name); > + > + for_each_child(orphan, child) > + fill_fullpaths(child, orphan->fullpath); > + } > } > > /* Usage related data. */ > diff --git a/livetree.c b/livetree.c > index 59b912d..263da1f 100644 > --- a/livetree.c > +++ b/livetree.c > @@ -804,7 +804,19 @@ static struct node *get_node_by_ref(struct node *tree, const char *ref) > path = slash + 1; > } > > - target = get_node_by_label(tree, label); > + if (label[0] == '$' && tree->name[0] == '&') { > + /* > + * We search for an orphan and the given tree is an > + * orphan. Use the given tree only if it matches the > + * expected orphan. > + */ > + if (streq(label + 1, tree->name + 1)) > + target = tree; > + else > + target = NULL; > + } else { > + target = get_node_by_label(tree, label); > + } > > free(buf); > > diff --git a/treesource.c b/treesource.c > index 71dbd5f..44de0db 100644 > --- a/treesource.c > +++ b/treesource.c > @@ -278,7 +278,8 @@ static void write_propval(FILE *f, struct property *prop) > break; > > if (m_phandle) { > - if (m_phandle->ref[0] == '/') > + if (m_phandle->ref[0] == '/' /* Root node */ || > + m_phandle->ref[0] == '$' /* Orphan node */) > fprintf(f, "&{%s}", m_phandle->ref); > else > fprintf(f, "&%s", m_phandle->ref); > -- > 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