dtc: introduce label relative path references

Reference via label allows extending nodes with compile-time checking of
whether the node being extended exists. This is useful to catch
renamed/removed nodes after an update of the device trees to be extended.

In absence of labels in the original device trees, new style path
references can be used:

  /* upstream device tree */
  / {
	leds: some-non-standard-led-controller-name {
		led-0 {
			default-state = "off";
		};
	};
  };

  /* downstream device tree */
  &{/some-non-standard-led-controller-name/led-0} {
	default-state = "on";
  };

This is a common theme within the barebox bootloader[0], which extends the
upstream (Linux) device trees in that manner. The downside is that,
especially for deep nodes, these references can get quite long and tend to
break often due to upstream rework (e.g. rename to adhere to bindings).

Often there is a label a level or two higher that could be used. This
patch allows combining both a label and a new style path reference to
get a compile-time-checked reference, which allows rewriting the
previous downstream device tree snippet to:

  &{leds/led-0} {
	default-state = "on";
  };

This won't be broken when /some-non-standard-led-controller-name is
renamed or moved while keeping the label. And if led-0 is renamed, we
will get the expected compile-time error.

Overlay support is skipped for now as they require special support: The
label and relative path parts need to be resolved at overlay apply-time,
not at compile-time.

[0]: https://www.barebox.org/doc/latest/devicetree/index.html

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
This commit is contained in:
Ahmad Fatoum 2020-10-25 12:39:59 +01:00 committed by David Gibson
parent 651410e54c
commit ec7986e682
3 changed files with 44 additions and 4 deletions

View file

@ -200,7 +200,7 @@ static void PRINTF(1, 2) lexical_error(const char *fmt, ...);
return DT_LABEL_REF;
}
<*>"&{/"{PATHCHAR}*\} { /* new-style path reference */
<*>"&{"{PATHCHAR}*\} { /* new-style path reference */
yytext[yyleng-1] = '\0';
DPRINT("Ref: %s\n", yytext+2);
yylval.labelref = xstrdup(yytext+2);

View file

@ -23,6 +23,12 @@ extern void yyerror(char const *s);
extern struct dt_info *parser_output;
extern bool treesource_error;
static bool is_ref_relative(const char *ref)
{
return ref[0] != '/' && strchr(&ref[1], '/');
}
%}
%union {
@ -169,6 +175,8 @@ devicetree:
*/
if (!($<flags>-1 & DTSF_PLUGIN))
ERROR(&@2, "Label or path %s not found", $1);
else if (is_ref_relative($1))
ERROR(&@2, "Label-relative reference %s not supported in plugin", $1);
$$ = add_orphan_node(
name_node(build_node(NULL, NULL, NULL),
""),
@ -178,6 +186,9 @@ devicetree:
{
struct node *target = get_node_by_ref($1, $3);
if (($<flags>-1 & DTSF_PLUGIN) && is_ref_relative($3))
ERROR(&@2, "Label-relative reference %s not supported in plugin", $3);
if (target) {
add_label(&target->labels, $2);
merge_nodes(target, $4);
@ -193,6 +204,8 @@ devicetree:
* so $-1 is what we want (plugindecl)
*/
if ($<flags>-1 & DTSF_PLUGIN) {
if (is_ref_relative($2))
ERROR(&@2, "Label-relative reference %s not supported in plugin", $2);
add_orphan_node($1, $3, $2);
} else {
struct node *target = get_node_by_ref($1, $2);

View file

@ -581,12 +581,39 @@ struct node *get_node_by_phandle(struct node *tree, cell_t phandle)
struct node *get_node_by_ref(struct node *tree, const char *ref)
{
struct node *target = tree;
const char *label = NULL, *path = NULL;
if (streq(ref, "/"))
return tree;
else if (ref[0] == '/')
return get_node_by_path(tree, ref);
if (ref[0] == '/')
path = ref;
else
return get_node_by_label(tree, ref);
label = ref;
if (label) {
const char *slash = strchr(label, '/');
char *buf = NULL;
if (slash) {
buf = xstrndup(label, slash - label);
label = buf;
path = slash + 1;
}
target = get_node_by_label(tree, label);
free(buf);
if (!target)
return NULL;
}
if (path)
target = get_node_by_path(target, path);
return target;
}
cell_t get_node_phandle(struct node *root, struct node *node)