* [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees
@ 2026-08-26 9:48 Herve Codina
2026-08-26 9:48 ` [RFC PATCH v2 01/74] dtc-parser.y: Avoid an empty proplist Herve Codina
` (73 more replies)
0 siblings, 74 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:48 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
This big picture series adds support for dtb metadata and addon
device-trees.
It is worth noting that this series needs the support for "structured
tags" available and so this RFC v2 must be applied to top of the
following series:
https://lore.kernel.org/all/20260826083146.304291-1-herve.codina@bootlin.com/
Before giving details about the series content, let me give a little bit
of context.
The use-case is to support additional boards that can be hot-plugged to
a connector available in a base board. This connector is standardized in
terms of resources available on each pin. Any additional boards
compatible with the connector should be able to be connected to base
board and all base boards where this connector is implemented should
support any additional boards.
TLDR: The patch 73 gives an example of dts and dtsa (new addon
device-tree) handling this use-case. It provides an example of a
realistic base board description (dts) and an realistic additional
board description (dtsa).
Each base board is described by its own device-tree and the real
resource connected to the connector depends on each board. For instance
an i2c bus on the connector can come from the i2c1 controller from base
board A and i2c5 controller from base board B. This is obviously the
case for all resources wired to the connector.
On the other hand, the device-tree describing the additional board has
no reason to be changed based on the base board the additional board is
going to be connected. Indeed this device-tree describes the additional
board hardware and this hardware doesn't change if the additional board
is connected to the base board A or the base board B.
In order to extend a device-tree at runtime, a device-tree overlay can
be used. The drawback of current overlay implementation is that an
overlay is tightly coupled with the base device-tree it is applied to.
If a symbol of the base device-tree has to be used by the overlay, all
symbols available in the base device-tree need to be visible by the
overlay and the overlay can use only those symbol without any kind of
name translation.
With current overlay implementation, a overlay depends on the base
board. Indeed, if an overlay wants to add an I2C device on the I2C bus
available on the base board A connector, it needs to reference the i2c1
bus whereas it needs to reference the i2c5 bus if it used with the base
board B.
In order to fix the issue, the 'export symbol' concept has been
proposed. This concept allows some specific node to 'export' symbols in
order to be seen by an overlay applied to this node.
The use-case and the export symbol proposal have been detailed during
a talk at ELCE 2025. Have a look at the slides [1] and/or the video [2]
to have a clear view of the topic
[1] https://bootlin.com/pub/conferences/2025/elce/ceresoli-hotplug-status.pdf
[2] https://www.youtube.com/watch?v=C8dEQ4OzMnc
The export symbol proposal based on an export-symbol node in the
device-tree have been rejected by device-tree and dtc maintainers.
A discussion about the topic has been started on the mailing-list [3].
This discussions led to:
- The addition of meta-data in dtb instead of having __fixup__, __local__fixup__,
an similar nodes in the device-tree used by overlays
- A new kind of device-tree, an addon device-tree, in order to replace the
usage of the overlay device-tree in this 'hot-plugging additional board'
use-case.
[3] https://lore.kernel.org/all/20250902105710.00512c6d@booty/
A first RFC iteration related to the implementation of features
discussed on the mailing-list has been sent [4]. During the review, the
need for "structured tag" format emerged in order to allow a smoother
integration of new tags. A series adding support for those structured
tags has been sent separately and its v3 iteration is available [5].
This current RFC v2 is the implementation of features discussed on the
mailing-list rebased on the top of the series adding support for
"structured tags" [5]. A lot of things are new in dtb format (new tags)
and dts format (new keyword, new kind of references) and not yet
mentioned in the standard.
[4] https://lore.kernel.org/all/20260112142009.1006236-1-herve.codina@bootlin.com/
[5] https://lore.kernel.org/all/20260826083146.304291-1-herve.codina@bootlin.com/
Also this RFC v2 contains some improvements and fixes that were missed
in RFC v1.
The purpose of this big picture RFC is to move forward on this topic
based on code and some concrete dts / dtb example. Indeed, this RFC also
adds tests for each feature introduced. Those tests are performed using
dts files and the content of those dts files can also help in the
discussion.
- Patch 1:
The first patch is just a simple fix and can probably be merged out
of this meta-data and addon discussion.
- Patches 2..12: Introduce new meta-data dtb tags based on markers
Those new tags are FDT_PROPDATA_PHANDLE and FDT_PROPDATA_PHANDLE_REF
(FDT_LOCAL and FDT_REF_PHANDLE in RFC v1).
FDT_PROPDATA_PHANDLE (details in patch 7) is used to tag property
using a phandle and this phandle points to a local node (i.e. a
node existing in the dtb).
FDT_PROPDATA_PHANDLE_REF (details in patch 11) is used to tag a
property using a phandle but this phandle points to a non local node
(i.e. the node doesn't exist in the dtb). The reference to the node
is present with the tag and the phandle value has to be fixed when
the dtb is applied. This tag can only be present in plugins
(overlays) and addons dtb.
- Patches 13..15: Introduce addons device-tree
This part introduce the new /addon/ dts keyword
- Patches 16..26: Introduce /export/ keyword and related dtb tags
This part introduces the new /export/ dts keyword (details in patch
17) and the related FDT_EXPORT_SYM and FDT_EXPORT_SYM_REF dtb tags.
FDT_EXPORT_SYM (details in patch 21) is used when the exported
symbol involved is a local node and FDT_EXPORT_SYM_REF (details in
patch 25) is used when the node involved is a non local node.
- Patches 27..34: Introduce /import/ keyword and related dtb tags
This part introduces the new /import/ dts keyword (details in patch
29) and the related FDT_IMPORT_SYM dtb tag (details in patch 31).
- Patches 35..58: Introduce orphan nodes
Even if the orphan nodes concept was already present in overlays,
the final encoding of those nodes in addon dtbs is different
compared to overlays dtbs.
In overlays, orphan nodes are transformed to a /fragment@n/__overlay__
node. This is not the way used in addons.
Indeed, in addons, orphan nodes are not transformed to fit in
something like /fragment@n/__overlay__. They are encoded in the dtb
using a specific tag.
This part, after some preparation, introduces orphan nodes (details
in patch 44) and the related FDT_BEGIN_NODE_REF_SYM dtb tag (details
in patch 51).
It also adds support for addons dts/dtb without a 'root' (details in
patch 53).
This part ended with the support for merging orphan node described
in dts when relevant (details patch 55).
- Patches 59..60: Reference orphan nodes and its sub-nodes by path
A new syntax is needed to be able to reference an orphan node and
its sub-nodes by path.
This new syntax is '${$<orphan_name>/<path>}' (details in patch 59)
- Patches 61..62: Namespace labels references
Add Namespace labels references with the new syntax '&foo.bar.baz'.
This new syntax, only allowed in addons, allows to 'jump' from node
to node based on exported symbols defined at each node (details in
patch 61).
- Patches 63..64: Support for applying an addon
First, add fdt_addon_apply() in libfdt (details in patch 63) and
then the fdtaddon command line tool (details in patch 64).
- Patches 65..71: fdtaddon test
Several tests related to addon application
- Patch 72: A more Realistic test
A test based on use-case we want to handle.
This patch (and its dts and dtsa files) shows the kind of usage is
expected for addons.
Also it proves that metadata and addons features handles our
use-case.
- Patch 73..74: External resolver
Add the ability to use an external symbol resolver (i.e. a symbol
resolver other than the one provided in the core libfdt) and
implement an external resolver in the fdtaddon tool.
This external resolver performs the resolution based on information
given at the tool command line and prove the capabilities of the
addon feature to handle the case where an addon uses several
connectors of a base board.
I know this series is a huge series but I would like to give the big
picture in this RFC (I hope this was a good idea). Of course, this
series can be split for the upstreaming step and handled by parts by
parts. Let me know.
Tests are provided for each feature. In addition to be used for testing,
tests input source files and expected output files can be used to see
the expected behavior related to each feature.
I hope also that this first RFC will help in moving forward regarding
this 'handling an additional board described by a device-tree' topic.
Best regards,
Hervé
Changes RFC v1 -> RFC v2
RFC v1: https://lore.kernel.org/all/20260112142009.1006236-1-herve.codina@bootlin.com/
Rebase on top of the series adding support for "structured tags"
https://lore.kernel.org/all/20260826083146.304291-1-herve.codina@bootlin.com/
Use "structured tag" for the new tags.
Remove patches proposed in RFC v1 which arae either already applied
upstream or moved to the series adding support for "structured tags".
- Patch 1 (patch 15 in RFC v1)
Reword commit log and move the patch at the beginning of the series.
- Patches 2..12: Introduce new meta-data dtb tags based on markers
- Introduce get_structured_tag_data() in fdtdump and
fdt_get_structured_tag_data() in libfdt to avoid code duplication.
- Remove fdt_next_tag_full() and keep fdt_next_tag() returning all
known tags (no implicit filtering). All tags need to be handled at
caller side. Introduce fdt_next_tag_filter() and a predefined
filter function, fdt_tag_filter_skip_metadata(), to ease the
handling of tags at caller side avoiding also code duplication.
- Handle metadata tags on dtb modification.
- Move fill_fullpaths() into build_dt_info()
- Rename FDT_LOCAL and FDT_REF_PHANDLE to FDT_PROPDATA_PHANDLE and
FDT_PROPDATA_PHANDLE_REF
- Complete REF_PHANDLE markers with complete_references() instead of
creating new marker as suggested in RFC v1. Complete those markers
as soon as possible in build_dt_info() to avoid the existence of
incomplete markers later in the code. build_dt_info() is called at
the end of parsing functions whatever the format (dts, dtb) parsed.
Once a dt_info is available, markers are complete. In other words,
markers are completed as soon as the input file is parsed.
- Fold simple patch (RFC v1 patch 4) into patch that needs it.
- Patches 13..15: Introduce addons device-tree
- Check that the DTC input (dts or dtb) is not flagged plugin
(overlay) and addon. Mutually exclusive flags.
- Patches 16..26: Introduce /export/ keyword and related dtb tags
- Fix patch 16 commit log
- Update fdt_tag_filter_skip_metadata()
- Update complete_references()
- Avoid extra padding in FDT_EXPORT_SYM. Make FDT_EXPORT_SYM_REF
consistent with FDT_EXPORT_SYM format (i.e. symbol name after the
reference)
- Put FDT_EXPORT* tags right after FDT_NODE. Indeed, FDT_EXPORT*
tags are node related structured tags.
- Remove the (ugly) 'placeholder for a phandle' field in
FDT_EXPORT_SYM_REF.
- Patches 27..34: Introduce /import/ keyword and related dtb tags
- Update fdt_tag_filter_skip_metadata()
- Patches 35..58: Introduce orphan nodes
- Change existing function signature instead of introducing new
functions. Add '_' suffix to function with old signature that are
still used internally.
- Introduce node_is_orphan() to test if a given node is an orphan
node.
- Fix fixup_phandle_references() when an orphan node is involved
(missed in RFC v1) and add a related test.
- Do not check node name for orphan nodes. A orphan node doesn't
have a node name but a reference to an external node.
- Rework handling in libfdt due to fdt_next_tag() returning all
known tags.
- Take orphan nodes into account in libfdt fdt_check_full().
- Patches 59..60: Reference orphan nodes and its sub-nodes by path
- Fix fill_fullpaths(), get_node_by_path() and get_node_by_ref_().
- phandle properties are no more created in an orphan node. Fix test
expected values.
- Patches 61..62: Namespace labels references
- No specific changes other expected values updates due to series
changes (tags name and locations)
- Patches 63..64: Support for applying an addon
- Minor modification to avoid signed/unsigned mismatch
- Fix last_prop_offset handling in addon_adjust_local_phandles(),
addon_resolve_phandles() and addon_replace_local_phandles()
- Handle data fields re-ordering in FDT_EXPORT_SYM and
FDT_EXPORT_SYM_REF.
- Handle the 'placeholder for a phandle' field removal in
FDT_EXPORT_SYM_REF.
- Use fdt_get_structured_tag_data() to get data related to new tags.
- Use fdt_root_offset() to get the addon root node instead of using
hard-coded offset 0.
- An addon cannot modify a property present in the base device-tree.
In RFC v1 if the property was present in both side, an error was
reported. Fix that and report the error only if the property is
present on both side but with different values. Exact same values
doesn't imply a property modification.
- Update orphan node handling and remove tag overrides done in RFC
v1 to consider orphan nodes as standard nodes at some specific
parts.
- Update comments, split and renamed some functions, make the code
more consistent.
- Patches 67..71: fdtaddon test
- Update expected values due to series changes (tags name and
locations)
- Add a new test related to references by path to orphan nodes
- Add a new test related properties in addon identical to existing
ones
- Patch 72: A more Realistic test
- No specific changes other expected values updates due to series
changes (tags name and locations)
- Patch 73..74: External resolver
- New patches in RFC v2. Allow to resolve symbols on criteria other
than simple import/export symbol names. Symbol resolution of an
addon applied at some node level and using several connectors
should be handle by this kind of external resolver.
Herve Codina (74):
dtc-parser.y: Avoid an empty proplist
Introduce v20 dtb version
fdtdump: Introduce get_structured_tag_data()
libfdt: Introduce fdt_get_structured_tag_data()
libfdt: Prepare for metadata tag support
dtc: Move fill_fullpaths() into build_dt_info()
Add support for FDT_PROPDATA_PHANDLE dtb tag
livetree: Improve get_node_by_phandle()
dtc: Complete REF_PHANDLE markers with ref strings and is_local flags
tests: Add basic metadata tests
Add support for FDT_PROPDATA_PHANDLE_REF dtb tag
tests: metadata: Add external phandle reference tests
Add support for /addon/ keyword
tests: Add a test related to addon dt_flags header value
tests: metadata: Add a basic addon test
dtc: Introduce export symbols
dtc: Add support for /export/ dts keyword parsing
checks: Handle export symbols in fixup_phandle_references()
dtc: Add export symbols (/export/ keyword) in generated dts file
dtc: Extend complete_references() to handle export symbols
Add support for FDT_EXPORT_SYM dtb tag
tests: metadata: Add export symbols with local references tests
dtc: Add support for export symbols sorting
tests: metadata: Add a test for export symbols sorting
Add support for FDT_EXPORT_SYM_REF dtb tag
tests: metadata: Add export symbols with external references tests
dtc: Introduce import symbols
dtc-parser: Introduce last_header_flags
dtc: Add support for /import/ dts keyword parsing
dtc: Add import symbols (/import/ keyword) in generated dts file
Add support for FDT_IMPORT_SYM dtb tag
tests: metadata: Add import symbols tests
dtc: Add support for import symbols sorting
tests: metadata: Improve sort test to check for import symbols sorting
checks: Get 'chosen' node using get_subnode()
dtc: Change get_node_by_path() signature to take dt_info
dtc: Change get_node_by_label() signature to take dt_info
dtc: Change get_node_by_phandle() signature to take dt_info
dtc: Change get_node_by_ref() signature to take dt_info
dtc: Change get_node_phandle() signature to take dt_info
dtc: Change get_property_by_label() signature to take dt_info
dtc: Change get_marker_label() signature to take dt_info
dtc: Change fill_fullpaths() signature to take dt_info
dtc: Introduce orphan nodes
dtc: Handle orphan nodes in get_xxx_by_yyy()
dtc: Avoid NULL fullpath for nodes in orphan trees
dtc: Handle orphan nodes in complete_references()
checks: Perform checks for orphan nodes
dtc: Rename add_orphan_node() to plugin_add_orphan_node()
dtc: Add basic support for addon orphan nodes in dts
Add support for FDT_BEGIN_NODE_REF dtb tag
tests: metadata: Add basic test for addon orphan nodes
dtc: Add support for missing root node in addon device-tree
tests: metadata: Add a test for addon without root node
dtc: Allow parser_get_node_by_ref() to return an orphan node for
merging purpose
tests: metadata: Add a test related to orphan node merging
dtc: Add support for orphan nodes sorting
tests: metadata: Improve sort test to check for orphan nodes sorting
dtc: Add support for references by path involving orphan nodes
tests: metadata: Add a test for references by path involving orphan
nodes
dtc: Add support for namespace labels references
tests: metadata: Add a test for namespace labels references
libfdt: Add support for applying an addon on a base device-tree blob
Add fdtaddon tool to apply an addon
tests: Add a first basic test for fdtaddon
tests: fdtaddon: Add a basic test for addons using an orphan nodes
tests: fdtaddon: Add a basic test for addons with unresolved phandle
references
tests: fdtaddon: Add a basic test for addons with references by path
to orphan nodes
tests: fdtaddon: Add a test for addons with properties identical to
existing ones
tests: fdtaddon: Add a test for addons using namespace label
references
tests: fdtaddon: Add a test for using 'stacked' addons
tests: fdtaddon: Add a test using more realistic dts and dtsa
libfdt/fdt_addon.c: Add support for an external resolver
fdtaddon: Add a simple external resolver
Makefile | 5 +
Makefile.utils | 6 +
checks.c | 154 +-
data.c | 3 +-
dtc-lexer.l | 37 +
dtc-parser.y | 307 ++-
dtc.c | 40 +-
dtc.h | 66 +-
fdtaddon.c | 415 ++++
fdtdump.c | 121 +-
flattree.c | 470 ++++-
fstree.c | 5 +-
libfdt/Makefile.libfdt | 2 +-
libfdt/fdt.c | 365 +++-
libfdt/fdt.h | 89 +
libfdt/fdt_addon.c | 1805 +++++++++++++++++
libfdt/fdt_check.c | 257 ++-
libfdt/fdt_ro.c | 49 +-
libfdt/fdt_rw.c | 172 +-
libfdt/fdt_wip.c | 23 +-
libfdt/libfdt.h | 137 +-
libfdt/libfdt_internal.h | 29 +
libfdt/meson.build | 1 +
libfdt/version.lds | 3 +
livetree.c | 626 +++++-
meson.build | 2 +-
tests/dtflags_addon.dtb.expect | 1 +
tests/dtflags_addon.dts | 11 +
...n_custom_resolver_node_a-merged.dtb.expect | 39 +
...n_addon_custom_resolver_node_a.dtba.expect | 15 +
...fdtaddon_addon_custom_resolver_node_a.dtsa | 22 +
..._custom_resolver_node_ab-merged.dtb.expect | 42 +
..._addon_custom_resolver_node_ab.dtba.expect | 21 +
...dtaddon_addon_custom_resolver_node_ab.dtsa | 29 +
...ddon_addon_namespace-merged.dtb.dts.expect | 57 +
...fdtaddon_addon_namespace-merged.dtb.expect | 49 +
tests/fdtaddon_addon_namespace.dtba.expect | 31 +
tests/fdtaddon_addon_namespace.dtsa | 34 +
...n_addon_prop_existing_ok-merged.dtb.expect | 20 +
...dtaddon_addon_prop_existing_ok.dtba.expect | 17 +
tests/fdtaddon_addon_prop_existing_ok.dtsa | 25 +
tests/fdtaddon_base.dtb.expect | 24 +
tests/fdtaddon_base.dts | 27 +
.../fdtaddon_base_custom_resolver.dtb.expect | 33 +
tests/fdtaddon_base_custom_resolver.dts | 52 +
tests/fdtaddon_base_namespace.dtb.expect | 29 +
tests/fdtaddon_base_namespace.dts | 33 +
tests/fdtaddon_base_prop_existing.dtb.expect | 17 +
tests/fdtaddon_base_prop_existing.dts | 22 +
tests/fdtaddon_basics1-merged1.dtb.dts.expect | 35 +
tests/fdtaddon_basics1-merged1.dtb.expect | 27 +
tests/fdtaddon_basics1-merged2.dtb.dts.expect | 35 +
tests/fdtaddon_basics1-merged2.dtb.expect | 27 +
tests/fdtaddon_basics1.dtba.expect | 8 +
tests/fdtaddon_basics1.dtsa | 13 +
tests/fdtaddon_basics2-merged1.dtb.dts.expect | 35 +
tests/fdtaddon_basics2-merged1.dtb.expect | 27 +
tests/fdtaddon_basics2-merged2.dtb.dts.expect | 35 +
tests/fdtaddon_basics2-merged2.dtb.expect | 27 +
tests/fdtaddon_basics2.dtba.expect | 9 +
tests/fdtaddon_basics2.dtsa | 15 +
tests/fdtaddon_basics3-merged1.dtb.dts.expect | 36 +
tests/fdtaddon_basics3-merged1.dtb.expect | 29 +
tests/fdtaddon_basics3-merged2.dtb.dts.expect | 36 +
tests/fdtaddon_basics3-merged2.dtb.expect | 29 +
tests/fdtaddon_basics3.dtba.expect | 12 +
tests/fdtaddon_basics3.dtsa | 17 +
tests/fdtaddon_basics4-merged1.dtb.dts.expect | 43 +
tests/fdtaddon_basics4-merged1.dtb.expect | 37 +
tests/fdtaddon_basics4-merged2.dtb.dts.expect | 43 +
tests/fdtaddon_basics4-merged2.dtb.expect | 37 +
tests/fdtaddon_basics4.dtba.expect | 22 +
tests/fdtaddon_basics4.dtsa | 25 +
...ddon_realistic_addon-merged.dtb.dts.expect | 91 +
...fdtaddon_realistic_addon-merged.dtb.expect | 93 +
tests/fdtaddon_realistic_addon.dtba.expect | 32 +
tests/fdtaddon_realistic_addon.dtsa | 50 +
tests/fdtaddon_realistic_base.dtb.expect | 72 +
tests/fdtaddon_realistic_base.dts | 74 +
.../fdtaddon_stack_1st-merged.dtb.dts.expect | 51 +
tests/fdtaddon_stack_1st-merged.dtb.expect | 41 +
tests/fdtaddon_stack_1st.dtba.expect | 24 +
tests/fdtaddon_stack_1st.dtsa | 28 +
.../fdtaddon_stack_2nd-merged.dtb.dts.expect | 70 +
tests/fdtaddon_stack_2nd-merged.dtb.expect | 59 +
tests/fdtaddon_stack_2nd.dtba.expect | 30 +
tests/fdtaddon_stack_2nd.dtsa | 35 +
tests/meson.build | 2 +
tests/metadata_addon_base.dtb.dts.expect | 10 +
tests/metadata_addon_base.dtb.expect | 10 +
tests/metadata_addon_base.dts | 14 +
tests/metadata_addon_base.dts.dts.expect | 10 +
tests/metadata_addon_orphan1.dtb.dts.expect | 16 +
tests/metadata_addon_orphan1.dtb.expect | 13 +
tests/metadata_addon_orphan1.dts | 19 +
tests/metadata_addon_orphan1.dts.dts.expect | 16 +
tests/metadata_addon_orphan2.dtb.dts.expect | 10 +
tests/metadata_addon_orphan2.dtb.expect | 8 +
tests/metadata_addon_orphan2.dts | 13 +
tests/metadata_addon_orphan2.dts.dts.expect | 10 +
tests/metadata_addon_orphan3.dtb.dts.expect | 22 +
tests/metadata_addon_orphan3.dtb.expect | 17 +
tests/metadata_addon_orphan3.dts | 28 +
tests/metadata_addon_orphan3.dts.dts.expect | 22 +
.../metadata_addon_references.dtb.dts.expect | 47 +
tests/metadata_addon_references.dtb.expect | 47 +
tests/metadata_addon_references.dts | 43 +
.../metadata_addon_references.dts.dts.expect | 47 +
...metadata_addon_refnamespace.dtb.dts.expect | 32 +
tests/metadata_addon_refnamespace.dtb.expect | 29 +
tests/metadata_addon_refnamespace.dts | 38 +
...metadata_addon_refnamespace.dts.dts.expect | 32 +
.../metadata_exportsyms_local.dtb.dts.expect | 26 +
tests/metadata_exportsyms_local.dtb.expect | 20 +
tests/metadata_exportsyms_local.dts | 28 +
.../metadata_exportsyms_local.dts.dts.expect | 26 +
tests/metadata_exportsyms_ref.dtb.dts.expect | 25 +
tests/metadata_exportsyms_ref.dtb.expect | 18 +
tests/metadata_exportsyms_ref.dts | 26 +
tests/metadata_exportsyms_ref.dts.dts.expect | 25 +
tests/metadata_importsyms.dtb.dts.expect | 9 +
tests/metadata_importsyms.dtb.expect | 8 +
tests/metadata_importsyms.dts | 14 +
tests/metadata_importsyms.dts.dts.expect | 9 +
...data_propdata_phandle_local.dtb.dts.expect | 23 +
...metadata_propdata_phandle_local.dtb.expect | 20 +
tests/metadata_propdata_phandle_local.dts | 27 +
...data_propdata_phandle_local.dts.dts.expect | 23 +
...tadata_propdata_phandle_ref.dtb.dts.expect | 18 +
.../metadata_propdata_phandle_ref.dtb.expect | 16 +
tests/metadata_propdata_phandle_ref.dts | 11 +
...tadata_propdata_phandle_ref.dts.dts.expect | 18 +
tests/metadata_sort.dtb.dts.expect | 61 +
tests/metadata_sort.dtb.expect | 48 +
tests/metadata_sort.dts | 58 +
tests/pylibfdt_tests.py | 6 +-
tests/run_tests.sh | 178 +-
tests/testutils.sh | 1 +
tests/treegen.c | 2 +-
...own_tags_can_skip.fdtput.test.dtb.1.expect | 2 +-
...nknown_tags_can_skip.wip.test.dtb.1.expect | 2 +-
treesource.c | 46 +-
142 files changed, 8299 insertions(+), 298 deletions(-)
create mode 100644 fdtaddon.c
create mode 100644 libfdt/fdt_addon.c
create mode 100644 tests/dtflags_addon.dtb.expect
create mode 100644 tests/dtflags_addon.dts
create mode 100644 tests/fdtaddon_addon_custom_resolver_node_a-merged.dtb.expect
create mode 100644 tests/fdtaddon_addon_custom_resolver_node_a.dtba.expect
create mode 100644 tests/fdtaddon_addon_custom_resolver_node_a.dtsa
create mode 100644 tests/fdtaddon_addon_custom_resolver_node_ab-merged.dtb.expect
create mode 100644 tests/fdtaddon_addon_custom_resolver_node_ab.dtba.expect
create mode 100644 tests/fdtaddon_addon_custom_resolver_node_ab.dtsa
create mode 100644 tests/fdtaddon_addon_namespace-merged.dtb.dts.expect
create mode 100644 tests/fdtaddon_addon_namespace-merged.dtb.expect
create mode 100644 tests/fdtaddon_addon_namespace.dtba.expect
create mode 100644 tests/fdtaddon_addon_namespace.dtsa
create mode 100644 tests/fdtaddon_addon_prop_existing_ok-merged.dtb.expect
create mode 100644 tests/fdtaddon_addon_prop_existing_ok.dtba.expect
create mode 100644 tests/fdtaddon_addon_prop_existing_ok.dtsa
create mode 100644 tests/fdtaddon_base.dtb.expect
create mode 100644 tests/fdtaddon_base.dts
create mode 100644 tests/fdtaddon_base_custom_resolver.dtb.expect
create mode 100644 tests/fdtaddon_base_custom_resolver.dts
create mode 100644 tests/fdtaddon_base_namespace.dtb.expect
create mode 100644 tests/fdtaddon_base_namespace.dts
create mode 100644 tests/fdtaddon_base_prop_existing.dtb.expect
create mode 100644 tests/fdtaddon_base_prop_existing.dts
create mode 100644 tests/fdtaddon_basics1-merged1.dtb.dts.expect
create mode 100644 tests/fdtaddon_basics1-merged1.dtb.expect
create mode 100644 tests/fdtaddon_basics1-merged2.dtb.dts.expect
create mode 100644 tests/fdtaddon_basics1-merged2.dtb.expect
create mode 100644 tests/fdtaddon_basics1.dtba.expect
create mode 100644 tests/fdtaddon_basics1.dtsa
create mode 100644 tests/fdtaddon_basics2-merged1.dtb.dts.expect
create mode 100644 tests/fdtaddon_basics2-merged1.dtb.expect
create mode 100644 tests/fdtaddon_basics2-merged2.dtb.dts.expect
create mode 100644 tests/fdtaddon_basics2-merged2.dtb.expect
create mode 100644 tests/fdtaddon_basics2.dtba.expect
create mode 100644 tests/fdtaddon_basics2.dtsa
create mode 100644 tests/fdtaddon_basics3-merged1.dtb.dts.expect
create mode 100644 tests/fdtaddon_basics3-merged1.dtb.expect
create mode 100644 tests/fdtaddon_basics3-merged2.dtb.dts.expect
create mode 100644 tests/fdtaddon_basics3-merged2.dtb.expect
create mode 100644 tests/fdtaddon_basics3.dtba.expect
create mode 100644 tests/fdtaddon_basics3.dtsa
create mode 100644 tests/fdtaddon_basics4-merged1.dtb.dts.expect
create mode 100644 tests/fdtaddon_basics4-merged1.dtb.expect
create mode 100644 tests/fdtaddon_basics4-merged2.dtb.dts.expect
create mode 100644 tests/fdtaddon_basics4-merged2.dtb.expect
create mode 100644 tests/fdtaddon_basics4.dtba.expect
create mode 100644 tests/fdtaddon_basics4.dtsa
create mode 100644 tests/fdtaddon_realistic_addon-merged.dtb.dts.expect
create mode 100644 tests/fdtaddon_realistic_addon-merged.dtb.expect
create mode 100644 tests/fdtaddon_realistic_addon.dtba.expect
create mode 100644 tests/fdtaddon_realistic_addon.dtsa
create mode 100644 tests/fdtaddon_realistic_base.dtb.expect
create mode 100644 tests/fdtaddon_realistic_base.dts
create mode 100644 tests/fdtaddon_stack_1st-merged.dtb.dts.expect
create mode 100644 tests/fdtaddon_stack_1st-merged.dtb.expect
create mode 100644 tests/fdtaddon_stack_1st.dtba.expect
create mode 100644 tests/fdtaddon_stack_1st.dtsa
create mode 100644 tests/fdtaddon_stack_2nd-merged.dtb.dts.expect
create mode 100644 tests/fdtaddon_stack_2nd-merged.dtb.expect
create mode 100644 tests/fdtaddon_stack_2nd.dtba.expect
create mode 100644 tests/fdtaddon_stack_2nd.dtsa
create mode 100644 tests/metadata_addon_base.dtb.dts.expect
create mode 100644 tests/metadata_addon_base.dtb.expect
create mode 100644 tests/metadata_addon_base.dts
create mode 100644 tests/metadata_addon_base.dts.dts.expect
create mode 100644 tests/metadata_addon_orphan1.dtb.dts.expect
create mode 100644 tests/metadata_addon_orphan1.dtb.expect
create mode 100644 tests/metadata_addon_orphan1.dts
create mode 100644 tests/metadata_addon_orphan1.dts.dts.expect
create mode 100644 tests/metadata_addon_orphan2.dtb.dts.expect
create mode 100644 tests/metadata_addon_orphan2.dtb.expect
create mode 100644 tests/metadata_addon_orphan2.dts
create mode 100644 tests/metadata_addon_orphan2.dts.dts.expect
create mode 100644 tests/metadata_addon_orphan3.dtb.dts.expect
create mode 100644 tests/metadata_addon_orphan3.dtb.expect
create mode 100644 tests/metadata_addon_orphan3.dts
create mode 100644 tests/metadata_addon_orphan3.dts.dts.expect
create mode 100644 tests/metadata_addon_references.dtb.dts.expect
create mode 100644 tests/metadata_addon_references.dtb.expect
create mode 100644 tests/metadata_addon_references.dts
create mode 100644 tests/metadata_addon_references.dts.dts.expect
create mode 100644 tests/metadata_addon_refnamespace.dtb.dts.expect
create mode 100644 tests/metadata_addon_refnamespace.dtb.expect
create mode 100644 tests/metadata_addon_refnamespace.dts
create mode 100644 tests/metadata_addon_refnamespace.dts.dts.expect
create mode 100644 tests/metadata_exportsyms_local.dtb.dts.expect
create mode 100644 tests/metadata_exportsyms_local.dtb.expect
create mode 100644 tests/metadata_exportsyms_local.dts
create mode 100644 tests/metadata_exportsyms_local.dts.dts.expect
create mode 100644 tests/metadata_exportsyms_ref.dtb.dts.expect
create mode 100644 tests/metadata_exportsyms_ref.dtb.expect
create mode 100644 tests/metadata_exportsyms_ref.dts
create mode 100644 tests/metadata_exportsyms_ref.dts.dts.expect
create mode 100644 tests/metadata_importsyms.dtb.dts.expect
create mode 100644 tests/metadata_importsyms.dtb.expect
create mode 100644 tests/metadata_importsyms.dts
create mode 100644 tests/metadata_importsyms.dts.dts.expect
create mode 100644 tests/metadata_propdata_phandle_local.dtb.dts.expect
create mode 100644 tests/metadata_propdata_phandle_local.dtb.expect
create mode 100644 tests/metadata_propdata_phandle_local.dts
create mode 100644 tests/metadata_propdata_phandle_local.dts.dts.expect
create mode 100644 tests/metadata_propdata_phandle_ref.dtb.dts.expect
create mode 100644 tests/metadata_propdata_phandle_ref.dtb.expect
create mode 100644 tests/metadata_propdata_phandle_ref.dts
create mode 100644 tests/metadata_propdata_phandle_ref.dts.dts.expect
create mode 100644 tests/metadata_sort.dtb.dts.expect
create mode 100644 tests/metadata_sort.dtb.expect
create mode 100644 tests/metadata_sort.dts
--
2.55.0
^ permalink raw reply [flat|nested] 75+ messages in thread
* [RFC PATCH v2 01/74] dtc-parser.y: Avoid an empty proplist
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
@ 2026-08-26 9:48 ` Herve Codina
2026-08-26 9:48 ` [RFC PATCH v2 02/74] Introduce v20 dtb version Herve Codina
` (72 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:48 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
In the dts parser grammar, a nodedef is defined by
'{' proplist subnodes '}' ';'
with both proplist and subnodes that can be empty lists.
Having multiple possible empty list can lead to Bison warnings such as
warning: 4 shift/reduce conflicts [-Wconflicts-sr]
warning: 1 reduce/reduce conflict [-Wconflicts-rr]
This will happen as soon as a new list (which also could be empty) is
added in the nodedef definition.
The simpler way to remove those warning and thus have a robust parsing
is to avoid multiple empty lists.
Update the proplist definition and nodedef definition to avoid those
multiple empty lists. Instead of allowing a proplist to be an empty
list, force the list to be a non empty one and update the nodedef to
handle explicitly the absence of the proplist to support the case where
no properties are available, i.e. the case of a node composed only of
sub-nodes.
This doesn't change the functional behavior.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
dtc-parser.y | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/dtc-parser.y b/dtc-parser.y
index 4d5eece5..e47f80d4 100644
--- a/dtc-parser.y
+++ b/dtc-parser.y
@@ -267,12 +267,16 @@ nodedef:
{
$$ = build_node($2, $3, &@$);
}
+ | '{' subnodes '}' ';'
+ {
+ $$ = build_node(NULL, $2, &@$);
+ }
;
proplist:
- /* empty */
+ propdef
{
- $$ = NULL;
+ $$ = chain_property($1, NULL);
}
| proplist propdef
{
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 02/74] Introduce v20 dtb version
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
2026-08-26 9:48 ` [RFC PATCH v2 01/74] dtc-parser.y: Avoid an empty proplist Herve Codina
@ 2026-08-26 9:48 ` Herve Codina
2026-08-26 9:48 ` [RFC PATCH v2 03/74] fdtdump: Introduce get_structured_tag_data() Herve Codina
` (71 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:48 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
This v20 version will add support for
- metadata in device-tree blobs in order to have a better handling of
phandles and unresolved references.
- Addon device-tree blob (successor of device-tree overlay)
- Import and export symbols feature
- multiple trees in an addon device-tree blob (i.e. root device tree
and orphan node tree)
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
dtc.h | 2 +-
fdtdump.c | 2 +-
flattree.c | 3 +++
libfdt/fdt.h | 1 +
libfdt/fdt_rw.c | 13 +++++++------
libfdt/libfdt.h | 6 +++---
tests/pylibfdt_tests.py | 6 +++---
tests/treegen.c | 2 +-
.../unknown_tags_can_skip.fdtput.test.dtb.1.expect | 2 +-
tests/unknown_tags_can_skip.wip.test.dtb.1.expect | 2 +-
10 files changed, 22 insertions(+), 17 deletions(-)
diff --git a/dtc.h b/dtc.h
index f0c2cde6..ef3f2682 100644
--- a/dtc.h
+++ b/dtc.h
@@ -29,7 +29,7 @@
#define debug(...)
#endif
-#define DEFAULT_FDT_VERSION 18
+#define DEFAULT_FDT_VERSION 20
/*
* Command line options
diff --git a/fdtdump.c b/fdtdump.c
index 5994e1d7..20854752 100644
--- a/fdtdump.c
+++ b/fdtdump.c
@@ -18,7 +18,7 @@
#include "util.h"
#define FDT_MAGIC_SIZE 4
-#define MAX_VERSION 18U
+#define MAX_VERSION 20U
#define ALIGN(x, a) (((x) + ((a) - 1)) & ~((a) - 1))
#define PALIGN(p, a) ((void *)(ALIGN((uintptr_t)(p), (a))))
diff --git a/flattree.c b/flattree.c
index ff57fc5d..dd451f0c 100644
--- a/flattree.c
+++ b/flattree.c
@@ -36,6 +36,9 @@ static struct version_info {
{18, 16, 17, FDT_V18_SIZE,
FTF_BOOTCPUID|FTF_STRTABSIZE|FTF_STRUCTSIZE|FTF_NOPS|FTF_DTFLAGS|
FTF_LCVERSW},
+ {20, 18, 18, FDT_V20_SIZE,
+ FTF_BOOTCPUID|FTF_STRTABSIZE|FTF_STRUCTSIZE|FTF_NOPS|FTF_DTFLAGS|
+ FTF_LCVERSW},
};
struct emitter {
diff --git a/libfdt/fdt.h b/libfdt/fdt.h
index a43b85c1..7c59fed2 100644
--- a/libfdt/fdt.h
+++ b/libfdt/fdt.h
@@ -90,5 +90,6 @@ struct fdt_property {
#define FDT_V16_SIZE FDT_V3_SIZE
#define FDT_V17_SIZE (FDT_V16_SIZE + sizeof(fdt32_t))
#define FDT_V18_SIZE (FDT_V17_SIZE + 2 * sizeof(fdt32_t))
+#define FDT_V20_SIZE FDT_V18_SIZE
#endif /* FDT_H */
diff --git a/libfdt/fdt_rw.c b/libfdt/fdt_rw.c
index 005efde8..1c6ce0a3 100644
--- a/libfdt/fdt_rw.c
+++ b/libfdt/fdt_rw.c
@@ -628,10 +628,11 @@ int fdt_open_into(const void *fdt, void *buf, int bufsize)
err = fdt_move(fdt, buf, bufsize);
if (err)
return err;
- if (can_assume(LATEST) || fdt_version(fdt) < 18) {
- fdt_set_version(buf, 18);
+
+ if (can_assume(LATEST) || fdt_version(fdt) < 20) {
+ fdt_set_version(buf, 20);
fdt_set_dt_flags(buf, 0);
- fdt_set_last_comp_version_w(buf, 17);
+ fdt_set_last_comp_version_w(buf, 18);
}
fdt_set_size_dt_struct(buf, struct_size);
fdt_set_totalsize(buf, bufsize);
@@ -661,10 +662,10 @@ int fdt_open_into(const void *fdt, void *buf, int bufsize)
fdt_set_magic(buf, FDT_MAGIC);
fdt_set_totalsize(buf, bufsize);
- fdt_set_version(buf, 18);
- fdt_set_last_comp_version(buf, 16);
+ fdt_set_version(buf, 20);
+ fdt_set_last_comp_version(buf, 18);
fdt_set_dt_flags(buf, 0);
- fdt_set_last_comp_version_w(buf, 17);
+ fdt_set_last_comp_version_w(buf, 18);
fdt_set_boot_cpuid_phys(buf, fdt_boot_cpuid_phys(fdt));
return 0;
diff --git a/libfdt/libfdt.h b/libfdt/libfdt.h
index 8e99e644..5e1e0d06 100644
--- a/libfdt/libfdt.h
+++ b/libfdt/libfdt.h
@@ -14,9 +14,9 @@ extern "C" {
#endif
#define FDT_FIRST_SUPPORTED_VERSION 0x02
-#define FDT_LAST_COMPATIBLE_VERSION 0x10
-#define FDT_LAST_COMPATIBLE_VERSION_W 0x11
-#define FDT_LAST_SUPPORTED_VERSION 0x12
+#define FDT_LAST_COMPATIBLE_VERSION 0x12
+#define FDT_LAST_COMPATIBLE_VERSION_W 0x12
+#define FDT_LAST_SUPPORTED_VERSION 0x14
/* Error codes: informative error codes */
#define FDT_ERR_NOTFOUND 1
diff --git a/tests/pylibfdt_tests.py b/tests/pylibfdt_tests.py
index c3aaf00f..bae52406 100644
--- a/tests/pylibfdt_tests.py
+++ b/tests/pylibfdt_tests.py
@@ -277,13 +277,13 @@ class PyLibfdtBasicTests(unittest.TestCase):
self.assertEqual(self.fdt.off_dt_struct(), 96)
self.assertEqual(self.fdt.off_dt_strings(), 660)
self.assertEqual(self.fdt.off_mem_rsvmap(), 48)
- self.assertEqual(self.fdt.version(), 18)
- self.assertEqual(self.fdt.last_comp_version(), 16)
+ self.assertEqual(self.fdt.version(), 20)
+ self.assertEqual(self.fdt.last_comp_version(), 18)
self.assertEqual(self.fdt.boot_cpuid_phys(), 0)
self.assertEqual(self.fdt.size_dt_strings(), 105)
self.assertEqual(self.fdt.size_dt_struct(), 564)
self.assertEqual(self.fdt.dt_flags(), 0)
- self.assertEqual(self.fdt.last_comp_version_w(), 17)
+ self.assertEqual(self.fdt.last_comp_version_w(), 18)
def testPack(self):
"""Test that we can pack the tree after deleting something"""
diff --git a/tests/treegen.c b/tests/treegen.c
index b0730ef6..36cb5508 100644
--- a/tests/treegen.c
+++ b/tests/treegen.c
@@ -127,7 +127,7 @@ static size_t emit_fdt_header_vers(struct buf *b, uint32_t version,
static size_t emit_fdt_header(struct buf *b)
{
- return emit_fdt_header_vers(b, 0x12, 0x10, 0x11);
+ return emit_fdt_header_vers(b, 0x14, 0x12, 0x12);
}
static void finish_rsvmap(struct buf *b, size_t hdr, size_t rsvmap)
diff --git a/tests/unknown_tags_can_skip.fdtput.test.dtb.1.expect b/tests/unknown_tags_can_skip.fdtput.test.dtb.1.expect
index 6d750f88..204d3d23 100644
--- a/tests/unknown_tags_can_skip.fdtput.test.dtb.1.expect
+++ b/tests/unknown_tags_can_skip.fdtput.test.dtb.1.expect
@@ -1,5 +1,5 @@
/dts-v1/;
-// version: 18
+// version: 20
// last_comp_version: 16
// last_comp_version_w: 17
diff --git a/tests/unknown_tags_can_skip.wip.test.dtb.1.expect b/tests/unknown_tags_can_skip.wip.test.dtb.1.expect
index afd8a4af..a3f9307f 100644
--- a/tests/unknown_tags_can_skip.wip.test.dtb.1.expect
+++ b/tests/unknown_tags_can_skip.wip.test.dtb.1.expect
@@ -1,5 +1,5 @@
/dts-v1/;
-// version: 18
+// version: 20
// last_comp_version: 16
// last_comp_version_w: 17
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 03/74] fdtdump: Introduce get_structured_tag_data()
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
2026-08-26 9:48 ` [RFC PATCH v2 01/74] dtc-parser.y: Avoid an empty proplist Herve Codina
2026-08-26 9:48 ` [RFC PATCH v2 02/74] Introduce v20 dtb version Herve Codina
@ 2026-08-26 9:48 ` Herve Codina
2026-08-26 9:48 ` [RFC PATCH v2 04/74] libfdt: Introduce fdt_get_structured_tag_data() Herve Codina
` (70 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:48 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
With structured tag, data len can be encoded in the tag itself (no data,
1 cell data, 2 cells) or with a dedicated cell (varlen) added between
the tag and the data.
In multiple places, getting the data related to the tag will be needed.
In order to avoid code duplication, introduce get_structured_tag_data()
which purpose is to get the tag data part and its length based on the
tag value.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
fdtdump.c | 50 ++++++++++++++++++++++++++++++--------------------
1 file changed, 30 insertions(+), 20 deletions(-)
diff --git a/fdtdump.c b/fdtdump.c
index 20854752..f26d3a89 100644
--- a/fdtdump.c
+++ b/fdtdump.c
@@ -44,6 +44,33 @@ static const char *tagname(uint32_t tag)
#define dumpf(fmt, args...) \
do { if (debug) printf("// " fmt, ## args); } while (0)
+static const char *get_structured_tag_data(uint32_t tag, const char *blob,
+ const char **data, int *data_size)
+{
+ int sz = 0;
+
+ switch (tag & FDT_TAG_DATA_MASK) {
+ case FDT_TAG_DATA_NONE:
+ break;
+ case FDT_TAG_DATA_1CELL:
+ sz = FDT_CELLSIZE;
+ break;
+ case FDT_TAG_DATA_2CELLS:
+ sz = 2 * FDT_CELLSIZE;
+ break;
+ case FDT_TAG_DATA_VARLEN:
+ /* Get the length */
+ sz = fdt32_to_cpu(GET_CELL(blob));
+ break;
+ }
+
+ *data_size = sz;
+ *data = sz ? blob : NULL;
+
+ /* Skip the data bytes */
+ return PALIGN(blob + sz, 4);
+}
+
static void dump_blob(void *blob, bool debug, int dump_unknown)
{
uintptr_t blob_off = (uintptr_t)blob;
@@ -58,7 +85,7 @@ static void dump_blob(void *blob, bool debug, int dump_unknown)
uint32_t version = fdt32_to_cpu(bph->version);
uint32_t totalsize = fdt32_to_cpu(bph->totalsize);
uint32_t tag;
- const char *p, *s, *t;
+ const char *p, *d, *s, *t;
int depth, sz, shift;
int i;
uint64_t addr, size;
@@ -153,21 +180,7 @@ static void dump_blob(void *blob, bool debug, int dump_unknown)
}
if ((tag & FDT_TAG_STRUCTURED) && (tag & FDT_TAG_SKIP_SAFE)) {
- sz = 0;
- switch (tag & FDT_TAG_DATA_MASK) {
- case FDT_TAG_DATA_NONE:
- break;
- case FDT_TAG_DATA_1CELL:
- sz = FDT_CELLSIZE;
- break;
- case FDT_TAG_DATA_2CELLS:
- sz = 2 * FDT_CELLSIZE;
- break;
- case FDT_TAG_DATA_VARLEN:
- /* Get the length */
- sz = fdt32_to_cpu(GET_CELL(p));
- break;
- }
+ p = get_structured_tag_data(tag, p, &d, &sz);
if (dump_unknown) {
printf("%*s// Unknown tag ignored: 0x%08"PRIx32", data len %d",
@@ -175,13 +188,10 @@ static void dump_blob(void *blob, bool debug, int dump_unknown)
if (dump_unknown > 1 && sz != 0) {
printf(" ");
for (i = 0; i < sz; i++)
- printf("%02hhx", *(p + i));
+ printf("%02hhx", *(d + i));
}
printf("\n");
}
-
- /* Skip the data bytes */
- p = PALIGN(p + sz, 4);
continue;
}
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 04/74] libfdt: Introduce fdt_get_structured_tag_data()
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (2 preceding siblings ...)
2026-08-26 9:48 ` [RFC PATCH v2 03/74] fdtdump: Introduce get_structured_tag_data() Herve Codina
@ 2026-08-26 9:48 ` Herve Codina
2026-08-26 9:48 ` [RFC PATCH v2 05/74] libfdt: Prepare for metadata tag support Herve Codina
` (69 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:48 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
With structured tag, data len can be encoded in the tag itself (no data,
1 cell data, 2 cells) or with a dedicated cell (varlen) added between
the tag and the data.
In multiple places, getting the data related to the tag will be needed.
To avoid code duplication, introduce fdt_get_structured_tag_data() which
purpose is to get the tag data part and its length based on the tag
value.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
libfdt/fdt.c | 80 ++++++++++++++++++++++++++--------------
libfdt/libfdt_internal.h | 3 ++
2 files changed, 55 insertions(+), 28 deletions(-)
diff --git a/libfdt/fdt.c b/libfdt/fdt.c
index 506e0dd3..7941632f 100644
--- a/libfdt/fdt.c
+++ b/libfdt/fdt.c
@@ -167,11 +167,56 @@ const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int len)
return fdt_offset_ptr_(fdt, offset);
}
+int fdt_get_structured_tag_data(uint32_t tag, const void *fdt, int offset,
+ uint32_t *data_len)
+{
+ int data_offset = offset;
+ uint32_t len = 0, sum;
+ const fdt32_t *lenp;
+
+ if (!(tag & FDT_TAG_STRUCTURED))
+ return -FDT_ERR_INTERNAL;
+
+ switch (tag & FDT_TAG_DATA_MASK) {
+ case FDT_TAG_DATA_NONE:
+ break;
+ case FDT_TAG_DATA_1CELL:
+ len = FDT_CELLSIZE;
+ break;
+ case FDT_TAG_DATA_2CELLS:
+ len = 2 * FDT_CELLSIZE;
+ break;
+ case FDT_TAG_DATA_VARLEN:
+ /* Get the length */
+ lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp));
+ if (!can_assume(VALID_DTB) && !lenp)
+ return -FDT_ERR_BADSTRUCTURE;
+ len = fdt32_to_cpu(*lenp);
+
+ /* Skip the cell encoding the length */
+ data_offset += sizeof(*lenp);
+
+ /* Check for the length value */
+ sum = len + data_offset;
+ if (!can_assume(VALID_DTB) &&
+ (sum >= INT_MAX || sum < (uint32_t) offset))
+ return -FDT_ERR_BADSTRUCTURE;
+
+ break;
+ }
+
+ if (data_len)
+ *data_len = len;
+
+ return data_offset;
+}
+
static uint32_t fdt_next_tag_all(const void *fdt, int startoffset, int *nextoffset)
{
const fdt32_t *tagp, *lenp;
uint32_t tag, len, sum;
int offset = startoffset;
+ int tmp_offset;
const char *p;
*nextoffset = -FDT_ERR_TRUNCATED;
@@ -221,34 +266,13 @@ static uint32_t fdt_next_tag_all(const void *fdt, int startoffset, int *nextoffs
if (!(tag & FDT_TAG_STRUCTURED) || !(tag & FDT_TAG_SKIP_SAFE))
return FDT_END;
- switch (tag & FDT_TAG_DATA_MASK) {
- case FDT_TAG_DATA_NONE:
- break;
- case FDT_TAG_DATA_1CELL:
- offset += FDT_CELLSIZE;
- break;
- case FDT_TAG_DATA_2CELLS:
- offset += 2 * FDT_CELLSIZE;
- break;
- case FDT_TAG_DATA_VARLEN:
- /* Get the length */
- lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp));
- if (!can_assume(VALID_DTB) && !lenp)
- return FDT_END; /* premature end */
- len = fdt32_to_cpu(*lenp);
- /*
- * Skip the cell encoding the length and the
- * following length bytes
- */
- len += sizeof(*lenp);
- sum = len + offset;
- if (!can_assume(VALID_DTB) &&
- (sum >= INT_MAX || sum < (uint32_t) offset))
- return FDT_END; /* premature end */
-
- offset += len;
- break;
- }
+ tmp_offset = fdt_get_structured_tag_data(tag, fdt, offset, &len);
+ if (tmp_offset < 0)
+ return FDT_END; /* premature end */
+
+ /* Skip the whole data bloc */
+ offset = tmp_offset + len;
+ break;
}
if (!fdt_offset_ptr(fdt, startoffset, offset - startoffset))
diff --git a/libfdt/libfdt_internal.h b/libfdt/libfdt_internal.h
index ce128fda..d4154119 100644
--- a/libfdt/libfdt_internal.h
+++ b/libfdt/libfdt_internal.h
@@ -23,6 +23,9 @@ int32_t fdt_ro_probe_(const void *fdt);
uint32_t fdt_next_tag_(const void *fdt, int startoffset, int *nextoffset,
bool *is_unknown);
+int fdt_get_structured_tag_data(uint32_t tag, const void *fdt, int offset,
+ uint32_t *data_len);
+
int fdt_check_node_offset_(const void *fdt, int offset);
int fdt_check_prop_offset_(const void *fdt, int offset);
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 05/74] libfdt: Prepare for metadata tag support
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (3 preceding siblings ...)
2026-08-26 9:48 ` [RFC PATCH v2 04/74] libfdt: Introduce fdt_get_structured_tag_data() Herve Codina
@ 2026-08-26 9:48 ` Herve Codina
2026-08-26 9:48 ` [RFC PATCH v2 06/74] dtc: Move fill_fullpaths() into build_dt_info() Herve Codina
` (68 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:48 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
Metadata tags are tags carrying extra information, either global to
the DTB, specific to a node, or specific to a property.
In several places (e.g. node/property iterators), these tags just need
to be skipped. Introduce fdt_next_tag_filter() which wraps
fdt_next_tag() and uses a user-supplied filter function to decide
whether a tag should be hidden from the caller. Filtered-out tags are
returned as FDT_NOP.
Introduce fdt_tag_filter_skip_metadata() a filter function whose purpose
is to filter metadata tags; actual metadata tag filtering will be added
when metadata tags are defined.
Metadata tags must also be handled when a DTB is modified. They have to
be removed or skipped depending on the modification.
Here also, introduce fdt_tag_is_property_metadata() to provide a central
check for whether a tag is a property metadata tag; actual metadata
check will be added when metadata tags are defined.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
libfdt/fdt.c | 34 +++++++++++++--
libfdt/fdt_ro.c | 3 +-
libfdt/fdt_rw.c | 90 +++++++++++++++++++++++++++++++++-------
libfdt/fdt_wip.c | 23 +++++++++-
libfdt/libfdt_internal.h | 10 +++++
5 files changed, 140 insertions(+), 20 deletions(-)
diff --git a/libfdt/fdt.c b/libfdt/fdt.c
index 7941632f..8c026f26 100644
--- a/libfdt/fdt.c
+++ b/libfdt/fdt.c
@@ -323,6 +323,33 @@ uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset)
return fdt_next_tag_(fdt, startoffset, nextoffset, NULL);
}
+uint32_t fdt_next_tag_filter(const void *fdt, int offset, int *nextoffset,
+ bool (*tag_filter)(void *priv, uint32_t tag),
+ void *priv)
+{
+ int startoffset = offset;
+ uint32_t tag;
+
+ tag = fdt_next_tag_(fdt, startoffset, nextoffset, NULL);
+ if (tag == FDT_END)
+ return tag;
+
+ if (tag_filter(priv, tag))
+ return FDT_NOP;
+
+ return tag;
+}
+
+bool fdt_tag_filter_skip_metadata(void *data, uint32_t tag)
+{
+ return false;
+}
+
+bool fdt_tag_is_property_metadata(uint32_t tag)
+{
+ return false;
+}
+
int fdt_check_node_offset_(const void *fdt, int offset)
{
if (!can_assume(VALID_INPUT)
@@ -355,7 +382,8 @@ int fdt_root_offset(const void *fdt)
do {
offset = nextoffset;
- tag = fdt_next_tag(fdt, offset, &nextoffset);
+ tag = fdt_next_tag_filter(fdt, offset, &nextoffset,
+ fdt_tag_filter_skip_metadata, NULL);
switch (tag) {
case FDT_END_NODE:
case FDT_PROP:
@@ -393,8 +421,8 @@ int fdt_next_node(const void *fdt, int offset, int *depth)
do {
offset = nextoffset;
- tag = fdt_next_tag(fdt, offset, &nextoffset);
-
+ tag = fdt_next_tag_filter(fdt, offset, &nextoffset,
+ fdt_tag_filter_skip_metadata, NULL);
switch (tag) {
case FDT_PROP:
case FDT_NOP:
diff --git a/libfdt/fdt_ro.c b/libfdt/fdt_ro.c
index 50c0a8bc..ea6a540d 100644
--- a/libfdt/fdt_ro.c
+++ b/libfdt/fdt_ro.c
@@ -206,7 +206,8 @@ static int nextprop_(const void *fdt, int offset)
int nextoffset;
do {
- tag = fdt_next_tag(fdt, offset, &nextoffset);
+ tag = fdt_next_tag_filter(fdt, offset, &nextoffset,
+ fdt_tag_filter_skip_metadata, NULL);
switch (tag) {
case FDT_END:
diff --git a/libfdt/fdt_rw.c b/libfdt/fdt_rw.c
index 1c6ce0a3..30f3da40 100644
--- a/libfdt/fdt_rw.c
+++ b/libfdt/fdt_rw.c
@@ -259,6 +259,42 @@ int fdt_prop_remove_unknown_tags(void *fdt, bool force_inplace,
return 0;
}
+int fdt_prop_remove_metadata_tags(void *fdt, bool force_inplace,
+ int prop_offset)
+{
+ int nextoffset, offset;
+ uint32_t tag;
+
+ /*
+ * Only inplace using nopify is supported even if we could use an other
+ * method involving splices if force_inplace is set to false.
+ */
+
+ tag = fdt_next_tag(fdt, prop_offset, &nextoffset);
+ if (tag == FDT_END)
+ return nextoffset;
+
+ /*
+ * Look at all tags related to the current property. I.e. tags after the
+ * current property and before either the next property, a sub-node or
+ * the end of current node
+ */
+ do {
+ offset = nextoffset;
+ tag = fdt_next_tag(fdt, offset, &nextoffset);
+ if (tag == FDT_END)
+ return nextoffset;
+
+ /* Remove metadata tags related to the property */
+ if (fdt_tag_is_property_metadata(tag))
+ fdt_nopify_area(fdt, offset, nextoffset);
+
+ } while ((tag != FDT_PROP) && (tag != FDT_BEGIN_NODE) &&
+ (tag != FDT_END_NODE));
+
+ return 0;
+}
+
static int fdt_resize_property_(void *fdt, int nodeoffset,
const char *name, int namelen,
int len, struct fdt_property **prop)
@@ -273,10 +309,18 @@ static int fdt_resize_property_(void *fdt, int nodeoffset,
return oldlen;
/*
- * The property is resized. Remove possible unknown tags related to the
- * property downgrading the dtb version.
+ * The property is resized and data are going to be modified. Remove
+ * property metadata tags
*/
prop_offset = fdt_ptr_offset_(fdt, *prop);
+ err = fdt_prop_remove_metadata_tags(fdt, false, prop_offset);
+ if (err)
+ return err;
+
+ /*
+ * Remove also possible unknown tags related to the property downgrading
+ * the dtb version.
+ */
err = fdt_prop_remove_unknown_tags(fdt, false, prop_offset, true);
if (err)
return err;
@@ -289,7 +333,7 @@ static int fdt_resize_property_(void *fdt, int nodeoffset,
return 0;
}
-static int fdt_node_skip_unknown_tags(void *fdt, int next)
+static int fdt_node_skip_unknown_and_metadata_tags(void *fdt, int next)
{
int nextoffset = next;
int offset;
@@ -299,6 +343,7 @@ static int fdt_node_skip_unknown_tags(void *fdt, int next)
* Skip all tags related to the current node. I.e. tags after the
* current node and before either the next property, a sub-node or the
* end of current node.
+ * FDT_NOP, unknown tags and potential node metadata tags are skipped.
*/
do {
offset = nextoffset;
@@ -332,10 +377,10 @@ static int fdt_add_property_(void *fdt, int nodeoffset, const char *name,
/*
* nextoffset it at the first tag after the node.
- * Skip possible unknown tags related to the node in order to add the
- * property after those tags.
+ * Skip possible unknown tags and metadata tags related to the node in
+ * order to add the property after those tags.
*/
- nextoffset = fdt_node_skip_unknown_tags(fdt, nextoffset);
+ nextoffset = fdt_node_skip_unknown_and_metadata_tags(fdt, nextoffset);
if (nextoffset < 0)
return nextoffset;
@@ -453,6 +498,12 @@ int fdt_appendprop(void *fdt, int nodeoffset, const char *name,
if (err)
return err;
+ /*
+ * We append data to the property. If known metadata tags are
+ * present, they are related to existing data and not impacted
+ * by data added. No need to remove them.
+ */
+
newlen = len + oldlen;
err = fdt_splice_struct_(fdt, prop->data,
FDT_TAGALIGN(oldlen),
@@ -484,11 +535,16 @@ int fdt_delprop(void *fdt, int nodeoffset, const char *name)
if (!prop)
return len;
+ /* The property is going to be removed. Remove its metadata tags */
+ prop_offset = fdt_ptr_offset_(fdt, prop);
+ err = fdt_prop_remove_metadata_tags(fdt, false, prop_offset);
+ if (err)
+ return err;
+
/*
- * The property is going to be removed. Remove also possible unknown
- * tags related to this property. Keep the dtb version unchanged.
+ * Remove also possible unknown tags related to this property.
+ * Keep the dtb version unchanged.
*/
- prop_offset = fdt_ptr_offset_(fdt, prop);
err = fdt_prop_remove_unknown_tags(fdt, false, prop_offset, false);
if (err)
return err;
@@ -522,10 +578,15 @@ int fdt_add_subnode_namelen(void *fdt, int parentoffset,
return offset;
/*
- * Try to place the new node after the parent's properties and unknown
- * tags related to those properties.
- * Unknown tags are reported as FDT_NOP tags by fdt_next_tag.
- * Skipping FDT_NOP tags will correctly skip unknown tags.
+ * Try to place the new node after:
+ * - the parent's properties
+ * - the metadata tags related to those properties
+ * - unknown tags related to those properties.
+ * Unknown tags are reported as FDT_NOP tags by fdt_next_tag_filter.
+ * metadata tags are also reported as FDT_NOP by fdt_next_tag_filter
+ * thanks to the filter function used.
+ * Skipping FDT_NOP tags will correctly skip unknown tags and metadata
+ * tags.
*/
tag = fdt_next_tag(fdt, parentoffset, &nextoffset);
/* the fdt_subnode_offset_namelen() should ensure this never hits */
@@ -533,7 +594,8 @@ int fdt_add_subnode_namelen(void *fdt, int parentoffset,
return -FDT_ERR_INTERNAL;
do {
offset = nextoffset;
- tag = fdt_next_tag(fdt, offset, &nextoffset);
+ tag = fdt_next_tag_filter(fdt, offset, &nextoffset,
+ fdt_tag_filter_skip_metadata, NULL);
} while ((tag == FDT_PROP) || (tag == FDT_NOP));
nh = fdt_offset_ptr_w_(fdt, offset);
diff --git a/libfdt/fdt_wip.c b/libfdt/fdt_wip.c
index 7ca3ffbc..b2f9f8d1 100644
--- a/libfdt/fdt_wip.c
+++ b/libfdt/fdt_wip.c
@@ -31,6 +31,17 @@ int fdt_setprop_inplace_namelen_partial(void *fdt, int nodeoffset,
if ((unsigned)proplen < (len + idx))
return -FDT_ERR_NOSPACE;
+ if (idx < (unsigned)proplen) {
+ /*
+ * The property existing data are going to be modified.
+ * The property metadata tags are related to the property
+ * data. Remove them.
+ */
+ err = fdt_prop_remove_metadata_tags(fdt, true, prop_offset);
+ if (err)
+ return err;
+ }
+
/*
* Remove unknown tags related to the property downgrading the dtb
* version.
@@ -81,10 +92,18 @@ int fdt_nop_property(void *fdt, int nodeoffset, const char *name)
return len;
/*
- * The property is going to be removed (nopified). Remove unknown tags
- * related to this property. Keep the dtb version unchanged.
+ * The property is going to be removed (nopified). Remove its metadata
+ * tags
*/
prop_offset = fdt_ptr_offset_(fdt, prop);
+ err = fdt_prop_remove_metadata_tags(fdt, true, prop_offset);
+ if (err)
+ return err;
+
+ /*
+ * Remove also possible unknown tags related to this property.
+ * Keep the dtb version unchanged.
+ */
err = fdt_prop_remove_unknown_tags(fdt, true, prop_offset, false);
if (err)
return err;
diff --git a/libfdt/libfdt_internal.h b/libfdt/libfdt_internal.h
index d4154119..47dfc0d4 100644
--- a/libfdt/libfdt_internal.h
+++ b/libfdt/libfdt_internal.h
@@ -23,6 +23,14 @@ int32_t fdt_ro_probe_(const void *fdt);
uint32_t fdt_next_tag_(const void *fdt, int startoffset, int *nextoffset,
bool *is_unknown);
+uint32_t fdt_next_tag_filter(const void *fdt, int offset, int *nextoffset,
+ bool (*tag_filter)(void *priv, uint32_t tag),
+ void *priv);
+
+bool fdt_tag_filter_skip_metadata(void *data, uint32_t tag);
+
+bool fdt_tag_is_property_metadata(uint32_t tag);
+
int fdt_get_structured_tag_data(uint32_t tag, const void *fdt, int offset,
uint32_t *data_len);
@@ -31,6 +39,8 @@ int fdt_check_prop_offset_(const void *fdt, int offset);
int fdt_prop_remove_unknown_tags(void *fdt, bool force_inplace,
int prop_offset, bool downgrade_version);
+int fdt_prop_remove_metadata_tags(void *fdt, bool force_inplace,
+ int prop_offset);
int fdt_getprop_offset_namelen(const void *fdt, int nodeoffset,
const char *name, int namelen);
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 06/74] dtc: Move fill_fullpaths() into build_dt_info()
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (4 preceding siblings ...)
2026-08-26 9:48 ` [RFC PATCH v2 05/74] libfdt: Prepare for metadata tag support Herve Codina
@ 2026-08-26 9:48 ` Herve Codina
2026-08-26 9:48 ` [RFC PATCH v2 07/74] Add support for FDT_PROPDATA_PHANDLE dtb tag Herve Codina
` (67 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:48 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
fill_fullpaths() is called unconditionally after parsing to set the full
path of each node in the parsed device tree.
There is no reason to call it outside build_dt_info().
Furthermore, future metadata support will need post-parsing operations
in build_dt_info() to produce a fully consistent dt_info. Those future
operations will need the full paths filled by fill_fullpaths().
Move the fill_fullpaths() call into build_dt_info() in preparation.
No functional change expected.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
dtc.c | 19 -------------------
livetree.c | 19 +++++++++++++++++++
2 files changed, 19 insertions(+), 19 deletions(-)
diff --git a/dtc.c b/dtc.c
index 6dae60de..e5068375 100644
--- a/dtc.c
+++ b/dtc.c
@@ -28,23 +28,6 @@ static int is_power_of_2(int x)
return (x > 0) && ((x & (x - 1)) == 0);
}
-static void fill_fullpaths(struct node *tree, const char *prefix)
-{
- struct node *child;
- const char *unit;
-
- tree->fullpath = join_path(prefix, tree->name);
-
- unit = strchr(tree->name, '@');
- if (unit)
- tree->basenamelen = unit - tree->name;
- else
- tree->basenamelen = strlen(tree->name);
-
- for_each_child(tree, child)
- fill_fullpaths(child, tree->fullpath);
-}
-
/* Usage related data. */
static const char usage_synopsis[] = "dtc [options] <input file>";
static const char usage_short_opts[] = "qI:O:o:V:d:R:S:p:a:fb:i:H:sW:E:@LAThv";
@@ -326,8 +309,6 @@ int main(int argc, char *argv[])
if (cmdline_boot_cpuid != -1)
dti->boot_cpuid_phys = cmdline_boot_cpuid;
- fill_fullpaths(dti->dt, "");
-
/* on a plugin, generate by default */
if (dti->dtsflags & DTSF_PLUGIN) {
generate_fixups = 1;
diff --git a/livetree.c b/livetree.c
index 8baed1fc..abe73de1 100644
--- a/livetree.c
+++ b/livetree.c
@@ -448,6 +448,23 @@ struct reserve_info *add_reserve_entry(struct reserve_info *list,
return list;
}
+static void fill_fullpaths(struct node *tree, const char *prefix)
+{
+ struct node *child;
+ const char *unit;
+
+ tree->fullpath = join_path(prefix, tree->name);
+
+ unit = strchr(tree->name, '@');
+ if (unit)
+ tree->basenamelen = unit - tree->name;
+ else
+ tree->basenamelen = strlen(tree->name);
+
+ for_each_child(tree, child)
+ fill_fullpaths(child, tree->fullpath);
+}
+
struct dt_info *build_dt_info(unsigned int dtsflags,
struct reserve_info *reservelist,
struct node *tree, uint32_t boot_cpuid_phys)
@@ -460,6 +477,8 @@ struct dt_info *build_dt_info(unsigned int dtsflags,
dti->dt = tree;
dti->boot_cpuid_phys = boot_cpuid_phys;
+ fill_fullpaths(dti->dt, "");
+
return dti;
}
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 07/74] Add support for FDT_PROPDATA_PHANDLE dtb tag
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (5 preceding siblings ...)
2026-08-26 9:48 ` [RFC PATCH v2 06/74] dtc: Move fill_fullpaths() into build_dt_info() Herve Codina
@ 2026-08-26 9:48 ` Herve Codina
2026-08-26 9:48 ` [RFC PATCH v2 08/74] livetree: Improve get_node_by_phandle() Herve Codina
` (66 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:48 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
FDT_PROPDATA_PHANDLE dtb tag is a meta-data tag attached to a property.
It indicates that the property defined before this tag (FDT_PROP) uses a
phandle value and the node related to this phandle value is local (i.e.
the node is present in the device-tree blob).
It is followed by one value:
- offset (32bit):
Offset in the property data where the phandle is available.
Example:
FDT_PROP 0x00000008 xxxxxxxx 0xca 0xfe 0xde 0xca 0x01 0x02 0x03 0x04
FDT_PROPDATA_PHANDLE 0x00000004
This means that at the offset 4 of the property data, the value
(0x01020304) is a phandle and the related node is available in the
dtb.
This is what is encoded in the dtb when the related dts has a property
with the value set to <0xcafedeca &foo> with 'foo' a reference to an
existing node where the phandle value is 0x01020304.
If several local phandles are used in the property data, several
FDT_PROPDATA_PHANDLE are present after the FDT_PROP tag. Each of them
points with its offset value to the position of one phandle.
For instance, if a first property with 8 bytes of data has a phandle
value at offset 4 and a second property with 16 bytes of data has
phandle values at offset 0 and 8, the following tags sequence is
present:
FDT_PROP 0x00000008 xxxxxxxx <data bytes>
FDT_PROPDATA_PHANDLE 0x00000004
FDT_PROP 0x00000010 xxxxxxxx <data bytes>
FDT_PROPDATA_PHANDLE 0x00000000
FDT_PROPDATA_PHANDLE 0x00000008
Add support for this new dtb tag.
Suggested-by: David Gibson <david@gibson.dropbear.id.au>
Link: https://lore.kernel.org/all/aL-2fmYsbexEtpNp@zatzit/
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
data.c | 3 ++-
dtc.h | 2 ++
fdtdump.c | 15 ++++++++++-
flattree.c | 63 ++++++++++++++++++++++++++++++++++++++++++++--
libfdt/fdt.c | 37 ++++++++++++++++++++++++++-
libfdt/fdt.h | 11 ++++++++
libfdt/fdt_check.c | 46 ++++++++++++++++++++++++++++++---
libfdt/libfdt.h | 3 ++-
treesource.c | 1 +
9 files changed, 171 insertions(+), 10 deletions(-)
diff --git a/data.c b/data.c
index 5b25aa06..9bae7047 100644
--- a/data.c
+++ b/data.c
@@ -127,7 +127,7 @@ struct data data_insert_at_marker(struct data d, struct marker *m,
return d;
}
-static struct data data_append_markers(struct data d, struct marker *m)
+struct data data_append_markers(struct data d, struct marker *m)
{
struct marker **mp = &d.markers;
@@ -288,6 +288,7 @@ struct marker *alloc_marker(unsigned int offset, enum markertype type,
m->type = type;
m->ref = ref;
m->next = NULL;
+ m->is_local = 0;
return m;
}
diff --git a/dtc.h b/dtc.h
index ef3f2682..64eaffde 100644
--- a/dtc.h
+++ b/dtc.h
@@ -128,6 +128,7 @@ struct marker {
enum markertype type;
unsigned int offset;
char *ref;
+ bool is_local;
struct marker *next;
};
@@ -187,6 +188,7 @@ struct data data_insert_data(struct data d, struct marker *m, struct data old);
struct marker *alloc_marker(unsigned int offset, enum markertype type,
char *ref);
struct data data_add_marker(struct data d, enum markertype type, char *ref);
+struct data data_append_markers(struct data d, struct marker *m);
bool data_is_one_string(struct data d);
diff --git a/fdtdump.c b/fdtdump.c
index f26d3a89..4ee9acc3 100644
--- a/fdtdump.c
+++ b/fdtdump.c
@@ -84,8 +84,9 @@ static void dump_blob(void *blob, bool debug, int dump_unknown)
const char *p_strings = (const char *)blob + off_str;
uint32_t version = fdt32_to_cpu(bph->version);
uint32_t totalsize = fdt32_to_cpu(bph->totalsize);
- uint32_t tag;
+ uint32_t tag, offset;
const char *p, *d, *s, *t;
+ const char *last_prop_name = NULL;
int depth, sz, shift;
int i;
uint64_t addr, size;
@@ -138,6 +139,7 @@ static void dump_blob(void *blob, bool debug, int dump_unknown)
(uintptr_t)p - blob_off - 4, tag, tagname(tag));
if (tag == FDT_BEGIN_NODE) {
+ last_prop_name = NULL;
s = p;
p = PALIGN(p + strlen(s) + 1, 4);
@@ -151,6 +153,7 @@ static void dump_blob(void *blob, bool debug, int dump_unknown)
}
if (tag == FDT_END_NODE) {
+ last_prop_name = NULL;
depth--;
printf("%*s};\n", depth * shift, "");
@@ -176,6 +179,16 @@ static void dump_blob(void *blob, bool debug, int dump_unknown)
printf("%*s%s", depth * shift, "", s);
utilfdt_print_data(t, sz);
printf(";\n");
+ last_prop_name = s;
+ continue;
+ }
+
+ if (tag == FDT_PROPDATA_PHANDLE) {
+ p = get_structured_tag_data(tag, p, &d, &sz);
+ offset = fdt32_to_cpu(GET_CELL(d));
+
+ printf("%*s// [FDT_PROPDATA_PHANDLE] %s[%"PRIu32"]\n", depth * shift, "",
+ last_prop_name, offset);
continue;
}
diff --git a/flattree.c b/flattree.c
index dd451f0c..92d191d5 100644
--- a/flattree.c
+++ b/flattree.c
@@ -15,6 +15,7 @@
#define FTF_NOPS 0x40
#define FTF_DTFLAGS 0x80
#define FTF_LCVERSW 0x100
+#define FTF_PROPDATA_PHANDLE 0x200
static struct version_info {
int version;
@@ -38,7 +39,7 @@ static struct version_info {
FTF_LCVERSW},
{20, 18, 18, FDT_V20_SIZE,
FTF_BOOTCPUID|FTF_STRTABSIZE|FTF_STRUCTSIZE|FTF_NOPS|FTF_DTFLAGS|
- FTF_LCVERSW},
+ FTF_LCVERSW|FTF_PROPDATA_PHANDLE},
};
struct emitter {
@@ -49,6 +50,7 @@ struct emitter {
void (*beginnode)(void *, struct label *labels);
void (*endnode)(void *, struct label *labels);
void (*property)(void *, struct label *labels);
+ void (*propdata_phandle)(void *);
};
static void bin_emit_cell(void *e, cell_t val)
@@ -98,6 +100,11 @@ static void bin_emit_property(void *e, struct label *labels)
bin_emit_cell(e, FDT_PROP);
}
+static void bin_emit_propdata_phandle(void *e)
+{
+ bin_emit_cell(e, FDT_PROPDATA_PHANDLE);
+}
+
static struct emitter bin_emitter = {
.cell = bin_emit_cell,
.string = bin_emit_string,
@@ -106,6 +113,7 @@ static struct emitter bin_emitter = {
.beginnode = bin_emit_beginnode,
.endnode = bin_emit_endnode,
.property = bin_emit_property,
+ .propdata_phandle = bin_emit_propdata_phandle,
};
static void emit_label(FILE *f, const char *prefix, const char *label)
@@ -217,6 +225,14 @@ static void asm_emit_property(void *e, struct label *labels)
asm_emit_cell(e, FDT_PROP);
}
+static void asm_emit_propdata_phandle(void *e)
+{
+ FILE *f = e;
+
+ fprintf(f, "\t/* FDT_PROPDATA_PHANDLE */\n");
+ asm_emit_cell(e, FDT_PROPDATA_PHANDLE);
+}
+
static struct emitter asm_emitter = {
.cell = asm_emit_cell,
.string = asm_emit_string,
@@ -225,6 +241,7 @@ static struct emitter asm_emitter = {
.beginnode = asm_emit_beginnode,
.endnode = asm_emit_endnode,
.property = asm_emit_property,
+ .propdata_phandle = asm_emit_propdata_phandle,
};
static int stringtable_insert(struct data *d, const char *str)
@@ -249,6 +266,7 @@ static void flatten_tree(struct node *tree, struct emitter *emit,
struct property *prop;
struct node *child;
bool seen_name_prop = false;
+ struct marker *m;
if (tree->deleted)
return;
@@ -279,6 +297,17 @@ static void flatten_tree(struct node *tree, struct emitter *emit,
emit->data(etarget, prop->val);
emit->align(etarget, sizeof(cell_t));
+
+ if (vi->flags & FTF_PROPDATA_PHANDLE) {
+ m = prop->val.markers;
+ for_each_marker_of_type(m, REF_PHANDLE) {
+ if (m->is_local) {
+ emit->propdata_phandle(etarget);
+ emit->cell(etarget, m->offset);
+ continue;
+ }
+ }
+ }
}
if ((vi->flags & FTF_NAMEPROPS) && !seen_name_prop) {
@@ -674,6 +703,7 @@ static uint32_t flat_read_tag(struct inbuf *inb)
case FDT_PROP:
case FDT_NOP:
case FDT_END:
+ case FDT_PROPDATA_PHANDLE:
return tag;
default:
break;
@@ -811,9 +841,11 @@ static struct node *unflatten_tree(struct inbuf *dtbuf,
struct inbuf *strbuf,
const char *parent_flatname, int flags)
{
+ struct property *prop = NULL;
struct node *node;
const char *flatname;
uint32_t val;
+ uint32_t offset;
node = build_node(NULL, NULL, NULL);
@@ -826,8 +858,8 @@ static struct node *unflatten_tree(struct inbuf *dtbuf,
node->name = xstrdup(flatname);
do {
- struct property *prop;
struct node *child;
+ struct marker *m;
val = flat_read_tag(dtbuf);
switch (val) {
@@ -840,11 +872,13 @@ static struct node *unflatten_tree(struct inbuf *dtbuf,
break;
case FDT_BEGIN_NODE:
+ prop = NULL;
child = unflatten_tree(dtbuf,strbuf, flatname, flags);
add_child(node, child);
break;
case FDT_END_NODE:
+ prop = NULL;
break;
case FDT_END:
@@ -859,6 +893,28 @@ static struct node *unflatten_tree(struct inbuf *dtbuf,
/* Ignore */
break;
+ case FDT_PROPDATA_PHANDLE:
+ if (!(flags & FTF_PROPDATA_PHANDLE))
+ die("PROPDATA_PHANDLE tag found in flat tree"
+ " version <20\n");
+
+ if (!prop)
+ die("PROPDATA_PHANDLE tag found without a previous PROP tag");
+
+ offset = flat_read_word(dtbuf);
+
+ /*
+ * A reference to a phandle is present in the property
+ * Mark the whole property as TYPE_UINT32.
+ */
+ property_add_marker(prop, TYPE_UINT32, 0, NULL);
+
+ /* Mark the offset as a local phandle reference */
+ m = alloc_marker(offset, REF_PHANDLE, NULL);
+ m->is_local = true;
+ prop->val = data_append_markers(prop->val, m);
+ break;
+
default:
die("Invalid opcode word %08x in device tree blob\n",
val);
@@ -981,6 +1037,9 @@ struct dt_info *dt_from_blob(const char *fname)
if (version >= 18)
flags |= FTF_DTFLAGS | FTF_LCVERSW;
+ if (version >= 20)
+ flags |= FTF_PROPDATA_PHANDLE;
+
inbuf_init(&memresvbuf,
blob + off_mem_rsvmap, blob + totalsize);
inbuf_init(&dtbuf, blob + off_dt, blob + totalsize);
diff --git a/libfdt/fdt.c b/libfdt/fdt.c
index 8c026f26..3762fbef 100644
--- a/libfdt/fdt.c
+++ b/libfdt/fdt.c
@@ -213,7 +213,7 @@ int fdt_get_structured_tag_data(uint32_t tag, const void *fdt, int offset,
static uint32_t fdt_next_tag_all(const void *fdt, int startoffset, int *nextoffset)
{
- const fdt32_t *tagp, *lenp;
+ const fdt32_t *tagp, *lenp, *tmp32p;
uint32_t tag, len, sum;
int offset = startoffset;
int tmp_offset;
@@ -262,6 +262,25 @@ static uint32_t fdt_next_tag_all(const void *fdt, int startoffset, int *nextoffs
case FDT_NOP:
break;
+ case FDT_PROPDATA_PHANDLE:
+ tmp_offset = fdt_get_structured_tag_data(tag, fdt, offset, &len);
+ if (tmp_offset < 0)
+ return FDT_END; /* premature end */
+
+ sum = tmp_offset + len;
+
+ if (!can_assume(VALID_DTB) && len < FDT_CELLSIZE)
+ return FDT_END; /* premature end */
+
+ /* Check offset value */
+ tmp32p = fdt_offset_ptr(fdt, tmp_offset, sizeof(*tmp32p));
+ if (!can_assume(VALID_DTB) && !tmp32p)
+ return FDT_END; /* premature end */
+
+ /* Skip the whole data bloc */
+ offset = sum;
+ break;
+
default:
if (!(tag & FDT_TAG_STRUCTURED) || !(tag & FDT_TAG_SKIP_SAFE))
return FDT_END;
@@ -290,6 +309,7 @@ static bool fdt_tag_is_unknown(uint32_t tag)
case FDT_PROP:
case FDT_NOP:
case FDT_END:
+ case FDT_PROPDATA_PHANDLE:
return false;
default:
break;
@@ -342,11 +362,26 @@ uint32_t fdt_next_tag_filter(const void *fdt, int offset, int *nextoffset,
bool fdt_tag_filter_skip_metadata(void *data, uint32_t tag)
{
+ switch (tag) {
+ case FDT_PROPDATA_PHANDLE:
+ return true;
+
+ default:
+ break;
+ }
+
return false;
}
bool fdt_tag_is_property_metadata(uint32_t tag)
{
+ switch (tag) {
+ case FDT_PROPDATA_PHANDLE:
+ return true;
+ default:
+ break;
+ }
+
return false;
}
diff --git a/libfdt/fdt.h b/libfdt/fdt.h
index 7c59fed2..2f681088 100644
--- a/libfdt/fdt.h
+++ b/libfdt/fdt.h
@@ -84,6 +84,17 @@ struct fdt_property {
#define FDT_TEST_VARLEN_CAN_SKIP FDT_TAG_CAN_SKIP(FDT_TAG_DATA_VARLEN, 0)
#define FDT_TEST_NONE_NO_SKIP FDT_TAG_NO_SKIP(FDT_TAG_DATA_NONE, 0)
+/*
+ * local phandle tag (meta-data). 1 cell: offset
+ *
+ * It indicates that the property defined before this tag (FDT_PROP) uses a
+ * phandle value and the node related to this phandle value is local (i.e.
+ * the node is present in the device-tree blob).
+ *
+ * offset: Offset in the property data where the phandle is available
+ */
+#define FDT_PROPDATA_PHANDLE FDT_TAG_CAN_SKIP(FDT_TAG_DATA_1CELL, 0x1000)
+
#define FDT_V1_SIZE (7*sizeof(fdt32_t))
#define FDT_V2_SIZE (FDT_V1_SIZE + sizeof(fdt32_t))
#define FDT_V3_SIZE (FDT_V2_SIZE + sizeof(fdt32_t))
diff --git a/libfdt/fdt_check.c b/libfdt/fdt_check.c
index 2fd5b61d..ac297841 100644
--- a/libfdt/fdt_check.c
+++ b/libfdt/fdt_check.c
@@ -14,12 +14,15 @@ int fdt_check_full(const void *fdt, size_t bufsize)
{
int err;
int num_memrsv;
- int offset, nextoffset = 0;
+ int offset, nextoffset = 0, d, tmp;
uint32_t tag;
unsigned int depth = 0;
- const void *prop;
+ const void *prop = NULL;
const char *propname;
+ int proplen;
bool expect_end = false;
+ const fdt32_t *pfdt32;
+ uint32_t val;
if (can_assume(PERFECT))
return 0;
@@ -58,6 +61,7 @@ int fdt_check_full(const void *fdt, size_t bufsize)
return 0;
case FDT_BEGIN_NODE:
+ prop = NULL;
depth++;
if (depth > INT_MAX)
return -FDT_ERR_BADSTRUCTURE;
@@ -77,6 +81,7 @@ int fdt_check_full(const void *fdt, size_t bufsize)
break;
case FDT_END_NODE:
+ prop = NULL;
if (depth == 0)
return -FDT_ERR_BADSTRUCTURE;
depth--;
@@ -86,9 +91,42 @@ int fdt_check_full(const void *fdt, size_t bufsize)
case FDT_PROP:
prop = fdt_getprop_by_offset(fdt, offset, &propname,
- &err);
+ &proplen);
if (!prop)
- return err;
+ return proplen;
+ break;
+
+ case FDT_PROPDATA_PHANDLE:
+ /* Be sure that a property is available before this tag */
+ if (!prop)
+ return -FDT_ERR_BADSTRUCTURE;
+
+ d = fdt_get_structured_tag_data(tag, fdt,
+ offset + FDT_TAGSIZE, NULL);
+ if (d < 0)
+ return d;
+
+ /* Retrieve the offset of the phandle in the property */
+ pfdt32 = fdt_offset_ptr(fdt, d, sizeof(*pfdt32));
+ if (!pfdt32)
+ return -FDT_ERR_BADSTRUCTURE;
+ tmp = fdt32_to_cpu(*pfdt32);
+
+ /* Check this offset */
+ if (tmp % sizeof(fdt32_t))
+ return -FDT_ERR_BADVALUE;
+ if ((uint32_t)proplen < tmp + sizeof(fdt32_t))
+ return -FDT_ERR_BADSTRUCTURE;
+
+ /* Retrieve the phandle value from the property */
+ pfdt32 = prop;
+ pfdt32 += tmp / sizeof(fdt32_t);
+ val = fdt32_ld(pfdt32);
+
+ /* Check the phandle value */
+ tmp = fdt_node_offset_by_phandle(fdt, val);
+ if (tmp < 0)
+ return tmp;
break;
default:
diff --git a/libfdt/libfdt.h b/libfdt/libfdt.h
index 5e1e0d06..ee811266 100644
--- a/libfdt/libfdt.h
+++ b/libfdt/libfdt.h
@@ -150,7 +150,8 @@ static inline void *fdt_offset_ptr_w(void *fdt, int offset, int checklen)
* be set to the offset immediately following the tag.
*
* returns:
- * the tag type (FDT_BEGIN_NODE, FDT_END_NODE, FDT_PROP, FDT_NOP, FDT_END),
+ * the tag type (FDT_BEGIN_NODE, FDT_END_NODE, FDT_PROP, FDT_NOP, FDT_END
+ * or any other known tags),
* FDT_END, if offset is out of bounds
*/
uint32_t fdt_next_tag(const void *fdt, int offset, int *nextoffset);
diff --git a/treesource.c b/treesource.c
index bf648bf6..0e33e3e1 100644
--- a/treesource.c
+++ b/treesource.c
@@ -168,6 +168,7 @@ static struct marker **add_marker(struct marker **mi,
nm->offset = offset;
nm->ref = ref;
nm->next = *mi;
+ nm->is_local = 0;
*mi = nm;
return &nm->next;
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 08/74] livetree: Improve get_node_by_phandle()
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (6 preceding siblings ...)
2026-08-26 9:48 ` [RFC PATCH v2 07/74] Add support for FDT_PROPDATA_PHANDLE dtb tag Herve Codina
@ 2026-08-26 9:48 ` Herve Codina
2026-08-26 9:48 ` [RFC PATCH v2 09/74] dtc: Complete REF_PHANDLE markers with ref strings and is_local flags Herve Codina
` (65 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:48 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
get_node_by_phandle() allows to get a node based on its phandle value.
It checks the phandle value against value available in internal node
structure.
This internal phandle value is updated during process_check() and so,
get_node_by_phandle() cannot give correct results before the
process_check() call.
Improve get_node_by_phandle() to look at node phandle properties when
the internal phandle value is not valid.
This allows to return a correct matching node even if process_check()
was not called yet.
With the recently introduced FDT_PROPDATA_HANDLE dtb tag, this will be
needed to update internal phandle references before the call to
process_check(). Indeed, this tag allows to identify phandles and
internal references need to be updated based on the phandle value before
the process_check() call.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
livetree.c | 21 ++++++++++++++++++++-
1 file changed, 20 insertions(+), 1 deletion(-)
diff --git a/livetree.c b/livetree.c
index abe73de1..a9f8c5d2 100644
--- a/livetree.c
+++ b/livetree.c
@@ -628,16 +628,35 @@ struct node *get_node_by_label(struct node *tree, const char *label)
return NULL;
}
+static cell_t get_node_phandle_existing(struct node *node)
+{
+ struct property *prop;
+
+ if (phandle_is_valid(node->phandle))
+ return node->phandle;
+
+ prop = get_property(node, "phandle");
+ if (!prop) {
+ prop = get_property(node, "linux,phandle");
+ if (!prop)
+ return 0;
+ }
+
+ return propval_cell(prop);
+}
+
struct node *get_node_by_phandle(struct node *tree, cell_t phandle)
{
struct node *child, *node;
+ cell_t tree_phandle;
if (!phandle_is_valid(phandle)) {
assert(generate_fixups);
return NULL;
}
- if (tree->phandle == phandle) {
+ tree_phandle = get_node_phandle_existing(tree);
+ if (phandle_is_valid(tree_phandle) && tree_phandle == phandle) {
if (tree->deleted)
return NULL;
return tree;
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 09/74] dtc: Complete REF_PHANDLE markers with ref strings and is_local flags
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (7 preceding siblings ...)
2026-08-26 9:48 ` [RFC PATCH v2 08/74] livetree: Improve get_node_by_phandle() Herve Codina
@ 2026-08-26 9:48 ` Herve Codina
2026-08-26 9:48 ` [RFC PATCH v2 10/74] tests: Add basic metadata tests Herve Codina
` (64 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:48 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
REF_PHANDLE markers lack either the ref string or the is_local flag
depending on the device-tree input format used.
When the marker is created from a dtb blob (via FDT_PROPDATA_PHANDLE),
only the raw phandle value is available. When created from dts source,
the ref string is present but the is_local flag is not set.
Add complete_references() to fill in the missing information.
Call the new function from build_dt_info() in order ensure that a
dt_info is always fully complete and consistent.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
livetree.c | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 82 insertions(+)
diff --git a/livetree.c b/livetree.c
index a9f8c5d2..2ad7fd09 100644
--- a/livetree.c
+++ b/livetree.c
@@ -465,6 +465,82 @@ static void fill_fullpaths(struct node *tree, const char *prefix)
fill_fullpaths(child, tree->fullpath);
}
+static void complete_ref_and_is_local(struct dt_info *dti, char **ref,
+ bool *is_local, cell_t phandle)
+{
+ struct node *refnode;
+
+ /*
+ * When created from a dts, the reference is available but the is_local
+ * flag is not set.
+ *
+ * When created from a dtb, the is_local flag is set (and a valid
+ * phandle value is available) but not the reference.
+ *
+ * Complete both ref and is_local based on existing values.
+ */
+
+ if (*ref) {
+ refnode = get_node_by_ref(dti->dt, *ref);
+ if (refnode)
+ *is_local = true;
+ else
+ *is_local = false;
+ return;
+ }
+
+ if (*is_local) {
+ refnode = get_node_by_phandle(dti->dt, phandle);
+ if (!refnode)
+ die("Node not found for phandle 0x%"PRIx32"\n", phandle);
+
+ *ref = refnode->fullpath;
+ return;
+ }
+
+ die("Cannot have a non local reference without a reference\n");
+}
+
+static void complete_references_internal(struct dt_info *dti, struct node *node)
+{
+ struct property *prop;
+ struct marker *m;
+ struct node *c;
+ cell_t phandle;
+
+ /*
+ * In order to have a complete state, a REF_PHANDLE marker needs to
+ * have a reference set (either a label or a path) and the is_local
+ * flag must be also correctly set.
+ *
+ * When created from dtb, references are not present in REF_PHANDLE
+ * markers created from the FDT_PROPDATA_PHANDLE tag. Indeed, only a
+ * phandle value is available with this tag.
+ *
+ * When created from dts, references are available for REF_PHANDLE
+ * markers but 'is_local' flag is not set.
+ *
+ * Completing a REF_PHANDLE marker consists in setting those fields.
+ */
+ for_each_property(node, prop) {
+ m = prop->val.markers;
+ for_each_marker_of_type(m, REF_PHANDLE) {
+ phandle = propval_cell_n(prop,
+ m->offset / sizeof(cell_t));
+ complete_ref_and_is_local(dti, &m->ref, &m->is_local,
+ phandle);
+ }
+ }
+
+ for_each_child(node, c)
+ complete_references_internal(dti, c);
+}
+
+static void complete_references(struct dt_info *dti)
+{
+ complete_references_internal(dti, dti->dt);
+}
+
struct dt_info *build_dt_info(unsigned int dtsflags,
struct reserve_info *reservelist,
struct node *tree, uint32_t boot_cpuid_phys)
@@ -479,6 +555,12 @@ struct dt_info *build_dt_info(unsigned int dtsflags,
fill_fullpaths(dti->dt, "");
+ /*
+ * Complete references (REF_PHANDLE).
+ * Set ref strings and set is_local flags
+ */
+ complete_references(dti);
+
return dti;
}
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 10/74] tests: Add basic metadata tests
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (8 preceding siblings ...)
2026-08-26 9:48 ` [RFC PATCH v2 09/74] dtc: Complete REF_PHANDLE markers with ref strings and is_local flags Herve Codina
@ 2026-08-26 9:48 ` Herve Codina
2026-08-26 9:48 ` [RFC PATCH v2 11/74] Add support for FDT_PROPDATA_PHANDLE_REF dtb tag Herve Codina
` (63 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:48 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
This first test is related to local phandle references used in
properties (FDT_PROPDATA_PHANDLE dtb tag).
The test pattern used is
- Generate a dts (xxx.dts.dts) from an input dts
- Check this generated dts against expected contents
- Generate a dtb (xxx.dtb) from the same input dts
- Check this generated dtb against expected contents
- Generate a dts (xxx.dtb.dts) from the generated dtb
- Check this generated dts against expected contents
- Generate a dtb (xxx.dtb.dts.dtb) from the generated dts
- Check this generated dtb, expect the same contents as for xxx.dtb
Even if only one meta-data feature is currently tested in this tests
introduction, use a loop in order to ease future addition consisting in
adding new input dts as soon as new meta-data feature are supported.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
tests/meson.build | 1 +
...data_propdata_phandle_local.dtb.dts.expect | 23 ++++++++++++++++
...metadata_propdata_phandle_local.dtb.expect | 20 ++++++++++++++
tests/metadata_propdata_phandle_local.dts | 27 +++++++++++++++++++
...data_propdata_phandle_local.dts.dts.expect | 23 ++++++++++++++++
tests/run_tests.sh | 25 ++++++++++++++++-
6 files changed, 118 insertions(+), 1 deletion(-)
create mode 100644 tests/metadata_propdata_phandle_local.dtb.dts.expect
create mode 100644 tests/metadata_propdata_phandle_local.dtb.expect
create mode 100644 tests/metadata_propdata_phandle_local.dts
create mode 100644 tests/metadata_propdata_phandle_local.dts.dts.expect
diff --git a/tests/meson.build b/tests/meson.build
index fcc73982..ec24f0af 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -143,6 +143,7 @@ run_test_types = [
'fdtput',
'fdtdump',
'fdtoverlay',
+ 'metadata',
]
run_test_deps = [
dtc_tools, treegen_dtb, tests_exe,
diff --git a/tests/metadata_propdata_phandle_local.dtb.dts.expect b/tests/metadata_propdata_phandle_local.dtb.dts.expect
new file mode 100644
index 00000000..076c17ae
--- /dev/null
+++ b/tests/metadata_propdata_phandle_local.dtb.dts.expect
@@ -0,0 +1,23 @@
+/dts-v1/;
+
+/ {
+
+ node-a {
+
+ subnode-a {
+ phandle = <0x01>;
+ };
+ };
+
+ node-b {
+ phandle = <0x02>;
+ };
+
+ node-c {
+ };
+
+ node-d {
+ ref-subnode-a = <&{/node-a/subnode-a}>;
+ ref-node-b = <0x123 0x456 &{/node-b} 0x789>;
+ };
+};
diff --git a/tests/metadata_propdata_phandle_local.dtb.expect b/tests/metadata_propdata_phandle_local.dtb.expect
new file mode 100644
index 00000000..c923d2e2
--- /dev/null
+++ b/tests/metadata_propdata_phandle_local.dtb.expect
@@ -0,0 +1,20 @@
+/dts-v1/;
+
+/ {
+ node-a {
+ subnode-a {
+ phandle = <0x00000001>;
+ };
+ };
+ node-b {
+ phandle = <0x00000002>;
+ };
+ node-c {
+ };
+ node-d {
+ ref-subnode-a = <0x00000001>;
+ // [FDT_PROPDATA_PHANDLE] ref-subnode-a[0]
+ ref-node-b = <0x00000123 0x00000456 0x00000002 0x00000789>;
+ // [FDT_PROPDATA_PHANDLE] ref-node-b[8]
+ };
+};
diff --git a/tests/metadata_propdata_phandle_local.dts b/tests/metadata_propdata_phandle_local.dts
new file mode 100644
index 00000000..f04d24f9
--- /dev/null
+++ b/tests/metadata_propdata_phandle_local.dts
@@ -0,0 +1,27 @@
+// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
+/*
+ * Copyright (C) 2026 Bootlin
+ */
+
+/dts-v1/;
+
+/ {
+ node-a {
+ subnode_a: subnode-a {
+ };
+ };
+
+ node_b: node-b {
+ };
+
+ node-c {
+ };
+
+ node_d: node-d {
+ ref-subnode-a = <&subnode_a>;
+ };
+};
+
+&node_d {
+ ref-node-b = <0x123 0x456 &node_b 0x789>;
+};
diff --git a/tests/metadata_propdata_phandle_local.dts.dts.expect b/tests/metadata_propdata_phandle_local.dts.dts.expect
new file mode 100644
index 00000000..b72b545f
--- /dev/null
+++ b/tests/metadata_propdata_phandle_local.dts.dts.expect
@@ -0,0 +1,23 @@
+/dts-v1/;
+
+/ {
+
+ node-a {
+
+ subnode_a: subnode-a {
+ phandle = <0x01>;
+ };
+ };
+
+ node_b: node-b {
+ phandle = <0x02>;
+ };
+
+ node-c {
+ };
+
+ node_d: node-d {
+ ref-subnode-a = <&subnode_a>;
+ ref-node-b = <0x123 0x456 &node_b 0x789>;
+ };
+};
diff --git a/tests/run_tests.sh b/tests/run_tests.sh
index 42b74e05..8c27b463 100755
--- a/tests/run_tests.sh
+++ b/tests/run_tests.sh
@@ -1239,6 +1239,26 @@ fdtoverlay_tests() {
run_wrap_test test "$bd" = "$pd"
}
+metadata_tests() {
+ for dt in metadata_propdata_phandle_local; do
+ run_dtc_test -I dts -O dts -o $dt.dts.dts "$SRCDIR/$dt.dts"
+ base_run_test check_diff $dt.dts.dts "$SRCDIR/$dt.dts.dts.expect"
+ run_dtc_test -I dts -O dtb -o $dt.dtb "$SRCDIR/$dt.dts"
+ base_run_test wrap_fdtdump $dt.dtb $dt.dtb.out
+ # Remove unneeded comments
+ sed -i '/^\/\/ /d' $dt.dtb.out
+ base_run_test check_diff $dt.dtb.out "$SRCDIR/$dt.dtb.expect"
+ run_test check_full $dt.dtb
+ run_dtc_test -I dtb -O dts -o $dt.dtb.dts $dt.dtb
+ base_run_test check_diff $dt.dtb.dts "$SRCDIR/$dt.dtb.dts.expect"
+ run_dtc_test -I dts -O dtb -o $dt.dtb.dts.dtb $dt.dtb.dts
+ base_run_test wrap_fdtdump $dt.dtb.dts.dtb $dt.dtb.dts.dtb.out
+ # Remove unneeded comments
+ sed -i '/^\/\/ /d' $dt.dtb.dts.dtb.out
+ base_run_test check_diff $dt.dtb.dts.dtb.out "$SRCDIR/$dt.dtb.expect"
+ done
+}
+
pylibfdt_tests () {
run_dtc_test -I dts -O dtb -o test_props.dtb "$SRCDIR/test_props.dts"
TMP=/tmp/tests.stderr.$$
@@ -1278,7 +1298,7 @@ while getopts "vt:me" ARG ; do
done
if [ -z "$TESTSETS" ]; then
- TESTSETS="libfdt utilfdt dtc dtbs_equal fdtget fdtput fdtdump fdtoverlay"
+ TESTSETS="libfdt utilfdt dtc dtbs_equal fdtget fdtput fdtdump fdtoverlay metadata"
# Test pylibfdt if the libfdt Python module is available.
if ! $no_python; then
@@ -1318,6 +1338,9 @@ for set in $TESTSETS; do
"fdtoverlay")
fdtoverlay_tests
;;
+ "metadata")
+ metadata_tests
+ ;;
esac
done
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 11/74] Add support for FDT_PROPDATA_PHANDLE_REF dtb tag
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (9 preceding siblings ...)
2026-08-26 9:48 ` [RFC PATCH v2 10/74] tests: Add basic metadata tests Herve Codina
@ 2026-08-26 9:48 ` Herve Codina
2026-08-26 9:48 ` [RFC PATCH v2 12/74] tests: metadata: Add external phandle reference tests Herve Codina
` (62 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:48 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
The FDT_PROPDATA_PHANDLE_REF dtb tag is similar to the
FDT_PROPDATA_PHANDLE tag except that it identifies a reference to an
external phandle. The node referenced by the phandle is not present in
the device-tree blob.
The FDT_PROPDATA_PHANDLE_REF dtb tag is a meta-data tag attached to a
property.
It indicates that the property defined before this tag (FDT_PROP) uses a
phandle value and the node related to this phandle value is not local
node (i.e. the node is not present in the device-tree blob). This tag
can be available only in overlay or addon device-tree blobs. The phandle
value used in the property has to be resolved when the device-tree blob
is applied on top of a base device-tree.
The tag is followed by the length of the data part (structured tag), two
values and a possible alignment padding:
- data part length (32bit): 4 + strlen(label) + 1
- offset (32bit):
Offset in the property data where the phandle is available.
- label (string including \0):
The label to use to resolve the phandle value.
- padding:
Padding (0x00) added to have the next tag aligned on 32bit.
Example:
FDT_PROP 0x00000008 xxxxxxxx 0x00 0x01 0x02 0x03 0xff 0xff 0xff 0xff
FDT_PROPDATA_PHANDLE_REF 9 0x00000004 "foo1" 0x00 0x00 0x00
This means that at the offset 4 of the property data, the value
(0xffffffff) is an unresolved phandle value and the related node is
the node referenced by "foo1".
This is what is encoded in the dtb when the related dts has a property
with the value set to <0x00010203 &foo1> with 'foo1' a reference
to an non local node.
If several non local phandles are used in the property data, several
FDT_PROPDATA_PHANDLE_REF are present after the FDT_PROP tag. Each of
them points with its offset value to the position of one phandle.
For instance, if a first property with 8 bytes of data has a
unresolved phandle value at offset 4 referenced by "foo" and a second
property with 16 bytes of data has unresolved phandle values at offset 0
and 8 referenced by "bar" and "baz", the following tags sequence is
present:
FDT_PROP 0x00000008 xxxxxxxx <data bytes>
FDT_PROPDATA_PHANDLE_REF 8 0x00000004 "foo"
FDT_PROP 0x00000010 xxxxxxxx <data bytes>
FDT_PROPDATA_PHANDLE_REF 8 0x00000000 "bar"
FDT_PROPDATA_PHANDLE_REF 8 0x00000008 "baz"
Add support for this new dtb tag.
Suggested-by: David Gibson <david@gibson.dropbear.id.au>
Link: https://lore.kernel.org/all/aL-2fmYsbexEtpNp@zatzit/
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
fdtdump.c | 10 ++++++
flattree.c | 78 +++++++++++++++++++++++++++++++++++++++++++++-
libfdt/fdt.c | 30 ++++++++++++++++++
libfdt/fdt.h | 14 +++++++++
libfdt/fdt_check.c | 34 ++++++++++++++++++++
5 files changed, 165 insertions(+), 1 deletion(-)
diff --git a/fdtdump.c b/fdtdump.c
index 4ee9acc3..2acf3210 100644
--- a/fdtdump.c
+++ b/fdtdump.c
@@ -192,6 +192,16 @@ static void dump_blob(void *blob, bool debug, int dump_unknown)
continue;
}
+ if (tag == FDT_PROPDATA_PHANDLE_REF) {
+ p = get_structured_tag_data(tag, p, &d, &sz);
+ offset = fdt32_to_cpu(GET_CELL(d));
+ s = d;
+
+ printf("%*s// [FDT_PROPDATA_PHANDLE_REF] %s[%"PRIu32"], ref = %s\n",
+ depth * shift, "", last_prop_name, offset, s);
+ continue;
+ }
+
if ((tag & FDT_TAG_STRUCTURED) && (tag & FDT_TAG_SKIP_SAFE)) {
p = get_structured_tag_data(tag, p, &d, &sz);
diff --git a/flattree.c b/flattree.c
index 92d191d5..abc50873 100644
--- a/flattree.c
+++ b/flattree.c
@@ -51,6 +51,7 @@ struct emitter {
void (*endnode)(void *, struct label *labels);
void (*property)(void *, struct label *labels);
void (*propdata_phandle)(void *);
+ void (*propdata_phandle_ref)(void *);
};
static void bin_emit_cell(void *e, cell_t val)
@@ -105,6 +106,11 @@ static void bin_emit_propdata_phandle(void *e)
bin_emit_cell(e, FDT_PROPDATA_PHANDLE);
}
+static void bin_emit_propdata_phandle_ref(void *e)
+{
+ bin_emit_cell(e, FDT_PROPDATA_PHANDLE_REF);
+}
+
static struct emitter bin_emitter = {
.cell = bin_emit_cell,
.string = bin_emit_string,
@@ -114,6 +120,7 @@ static struct emitter bin_emitter = {
.endnode = bin_emit_endnode,
.property = bin_emit_property,
.propdata_phandle = bin_emit_propdata_phandle,
+ .propdata_phandle_ref = bin_emit_propdata_phandle_ref,
};
static void emit_label(FILE *f, const char *prefix, const char *label)
@@ -233,6 +240,14 @@ static void asm_emit_propdata_phandle(void *e)
asm_emit_cell(e, FDT_PROPDATA_PHANDLE);
}
+static void asm_emit_propdata_phandle_ref(void *e)
+{
+ FILE *f = e;
+
+ fprintf(f, "\t/* FDT_PROPDATA_PHANDLE_REF */\n");
+ asm_emit_cell(e, FDT_PROPDATA_PHANDLE_REF);
+}
+
static struct emitter asm_emitter = {
.cell = asm_emit_cell,
.string = asm_emit_string,
@@ -242,6 +257,7 @@ static struct emitter asm_emitter = {
.endnode = asm_emit_endnode,
.property = asm_emit_property,
.propdata_phandle = asm_emit_propdata_phandle,
+ .propdata_phandle_ref = asm_emit_propdata_phandle_ref,
};
static int stringtable_insert(struct data *d, const char *str)
@@ -306,6 +322,16 @@ static void flatten_tree(struct node *tree, struct emitter *emit,
emit->cell(etarget, m->offset);
continue;
}
+
+ if (m->ref[0] == '/')
+ die("Phandle uses a non local reference by path (%s)\n",
+ m->ref);
+
+ emit->propdata_phandle_ref(etarget);
+ emit->cell(etarget, sizeof(cell_t) + strlen(m->ref) + 1);
+ emit->cell(etarget, m->offset);
+ emit->string(etarget, m->ref, 0);
+ emit->align(etarget, sizeof(cell_t));
}
}
}
@@ -626,6 +652,16 @@ static void inbuf_init(struct inbuf *inb, void *base, void *limit)
inb->ptr = inb->base;
}
+static void flat_read_subbuf(struct inbuf *inb, struct inbuf *subbuf, int len)
+{
+ if ((inb->ptr + len) > inb->limit)
+ die("Premature end of data parsing flat device tree\n");
+
+ inbuf_init(subbuf, inb->ptr, inb->ptr + len);
+
+ inb->ptr += len;
+}
+
static void flat_read_chunk(struct inbuf *inb, void *p, int len)
{
if ((inb->ptr + len) > inb->limit)
@@ -704,6 +740,7 @@ static uint32_t flat_read_tag(struct inbuf *inb)
case FDT_NOP:
case FDT_END:
case FDT_PROPDATA_PHANDLE:
+ case FDT_PROPDATA_PHANDLE_REF:
return tag;
default:
break;
@@ -713,7 +750,7 @@ static uint32_t flat_read_tag(struct inbuf *inb)
die("Cannot skip unknown tag 0x%08x\n", tag);
}
-static const char *flat_read_string(struct inbuf *inb)
+static const char *flat_read_string_norealign(struct inbuf *inb)
{
int len = 0;
const char *p = inb->ptr;
@@ -729,6 +766,15 @@ static const char *flat_read_string(struct inbuf *inb)
inb->ptr += len;
+ return str;
+}
+
+static const char *flat_read_string(struct inbuf *inb)
+{
+ const char *str;
+
+ str = flat_read_string_norealign(inb);
+
flat_realign(inb, sizeof(uint32_t));
return str;
@@ -844,8 +890,11 @@ static struct node *unflatten_tree(struct inbuf *dtbuf,
struct property *prop = NULL;
struct node *node;
const char *flatname;
+ struct inbuf subbuf;
uint32_t val;
uint32_t offset;
+ uint32_t len;
+ const char *str;
node = build_node(NULL, NULL, NULL);
@@ -915,6 +964,33 @@ static struct node *unflatten_tree(struct inbuf *dtbuf,
prop->val = data_append_markers(prop->val, m);
break;
+ case FDT_PROPDATA_PHANDLE_REF:
+ if (!(flags & FTF_PROPDATA_PHANDLE))
+ die("PROPDATA_PHANDLE_REF tag found in flat tree"
+ " version <20\n");
+
+ if (!prop)
+ die("PROPDATA_PHANDLE_REF tag found without a previous PROP tag");
+
+ len = flat_read_word(dtbuf);
+ flat_read_subbuf(dtbuf, &subbuf, len);
+ flat_realign(dtbuf, sizeof(uint32_t));
+
+ offset = flat_read_word(&subbuf);
+ str = flat_read_string_norealign(&subbuf);
+
+ /*
+ * A reference to a phandle is present in the property
+ * Mark the whole property as TYPE_UINT32.
+ */
+ property_add_marker(prop, TYPE_UINT32, 0, NULL);
+
+ /* Mark the offset as a external phandle reference */
+ m = alloc_marker(offset, REF_PHANDLE, xstrdup(str));
+ m->is_local = false;
+ prop->val = data_append_markers(prop->val, m);
+ break;
+
default:
die("Invalid opcode word %08x in device tree blob\n",
val);
diff --git a/libfdt/fdt.c b/libfdt/fdt.c
index 3762fbef..d23b886b 100644
--- a/libfdt/fdt.c
+++ b/libfdt/fdt.c
@@ -281,6 +281,33 @@ static uint32_t fdt_next_tag_all(const void *fdt, int startoffset, int *nextoffs
offset = sum;
break;
+ case FDT_PROPDATA_PHANDLE_REF:
+ tmp_offset = fdt_get_structured_tag_data(tag, fdt, offset, &len);
+ if (tmp_offset < 0)
+ return FDT_END; /* premature end */
+
+ sum = tmp_offset + len;
+
+ /* Check offset value */
+ tmp32p = fdt_offset_ptr(fdt, tmp_offset, sizeof(*tmp32p));
+ if (!can_assume(VALID_DTB) && !tmp32p)
+ return FDT_END; /* premature end */
+ tmp_offset += sizeof(fdt32_t);
+
+ /* Check ref string */
+ do {
+ p = fdt_offset_ptr(fdt, tmp_offset++, 1);
+ } while (p && (*p != '\0'));
+ if (!can_assume(VALID_DTB) && !p)
+ return FDT_END; /* premature end */
+
+ if (!can_assume(VALID_INPUT) && (uint32_t)tmp_offset > sum)
+ return FDT_END; /* premature end */
+
+ /* Skip the whole data bloc */
+ offset = sum;
+ break;
+
default:
if (!(tag & FDT_TAG_STRUCTURED) || !(tag & FDT_TAG_SKIP_SAFE))
return FDT_END;
@@ -310,6 +337,7 @@ static bool fdt_tag_is_unknown(uint32_t tag)
case FDT_NOP:
case FDT_END:
case FDT_PROPDATA_PHANDLE:
+ case FDT_PROPDATA_PHANDLE_REF:
return false;
default:
break;
@@ -364,6 +392,7 @@ bool fdt_tag_filter_skip_metadata(void *data, uint32_t tag)
{
switch (tag) {
case FDT_PROPDATA_PHANDLE:
+ case FDT_PROPDATA_PHANDLE_REF:
return true;
default:
@@ -377,6 +406,7 @@ bool fdt_tag_is_property_metadata(uint32_t tag)
{
switch (tag) {
case FDT_PROPDATA_PHANDLE:
+ case FDT_PROPDATA_PHANDLE_REF:
return true;
default:
break;
diff --git a/libfdt/fdt.h b/libfdt/fdt.h
index 2f681088..c151aa2b 100644
--- a/libfdt/fdt.h
+++ b/libfdt/fdt.h
@@ -95,6 +95,20 @@ struct fdt_property {
*/
#define FDT_PROPDATA_PHANDLE FDT_TAG_CAN_SKIP(FDT_TAG_DATA_1CELL, 0x1000)
+/*
+ * external phandle tag (meta-data). varlen: offset, string label
+ *
+ * It indicates that the property defined before this tag (FDT_PROP) uses a
+ * phandle value and the node related to this phandle value is not local
+ * node (i.e. the node is not present in the device-tree blob). The phandle
+ * value used in the property has to be resolved when the device-tree blob
+ * is applied on top of a base device-tree.
+ *
+ * offset: Offset in the property data where the phandle is available
+ * label: The label to use to resolve the phandle value
+ */
+#define FDT_PROPDATA_PHANDLE_REF FDT_TAG_CAN_SKIP(FDT_TAG_DATA_VARLEN, 0x1001)
+
#define FDT_V1_SIZE (7*sizeof(fdt32_t))
#define FDT_V2_SIZE (FDT_V1_SIZE + sizeof(fdt32_t))
#define FDT_V3_SIZE (FDT_V2_SIZE + sizeof(fdt32_t))
diff --git a/libfdt/fdt_check.c b/libfdt/fdt_check.c
index ac297841..96dd7dc0 100644
--- a/libfdt/fdt_check.c
+++ b/libfdt/fdt_check.c
@@ -22,6 +22,7 @@ int fdt_check_full(const void *fdt, size_t bufsize)
int proplen;
bool expect_end = false;
const fdt32_t *pfdt32;
+ const char *str;
uint32_t val;
if (can_assume(PERFECT))
@@ -129,6 +130,39 @@ int fdt_check_full(const void *fdt, size_t bufsize)
return tmp;
break;
+ case FDT_PROPDATA_PHANDLE_REF:
+ /* Be sure that a property is available before this tag */
+ if (!prop)
+ return -FDT_ERR_BADSTRUCTURE;
+
+ d = fdt_get_structured_tag_data(tag, fdt,
+ offset + FDT_TAGSIZE, NULL);
+ if (d < 0)
+ return d;
+
+ /* Retrieve the offset of the phandle in the property */
+ pfdt32 = fdt_offset_ptr(fdt, d, sizeof(*pfdt32));
+ if (!pfdt32)
+ return -FDT_ERR_BADSTRUCTURE;
+ tmp = fdt32_to_cpu(*pfdt32);
+
+ /* Check this offset */
+ if (tmp % sizeof(fdt32_t))
+ return -FDT_ERR_BADVALUE;
+ if ((uint32_t)proplen < tmp + sizeof(fdt32_t))
+ return -FDT_ERR_BADSTRUCTURE;
+
+ /* Retrieve the reference */
+ d += sizeof(fdt32_t);
+ str = fdt_offset_ptr(fdt, d, 1);
+ if (!str)
+ return -FDT_ERR_BADSTRUCTURE;
+
+ /* Check that the reference is not an empty string */
+ if (!strlen(str))
+ return -FDT_ERR_BADVALUE;
+ break;
+
default:
return -FDT_ERR_INTERNAL;
}
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 12/74] tests: metadata: Add external phandle reference tests
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (10 preceding siblings ...)
2026-08-26 9:48 ` [RFC PATCH v2 11/74] Add support for FDT_PROPDATA_PHANDLE_REF dtb tag Herve Codina
@ 2026-08-26 9:48 ` Herve Codina
2026-08-26 9:48 ` [RFC PATCH v2 13/74] Add support for /addon/ keyword Herve Codina
` (61 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:48 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
Add tests related to external phandle references used in
properties (FDT_PROPDATA_PHANDLE_REF dtb tag).
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
...etadata_propdata_phandle_ref.dtb.dts.expect | 18 ++++++++++++++++++
tests/metadata_propdata_phandle_ref.dtb.expect | 16 ++++++++++++++++
tests/metadata_propdata_phandle_ref.dts | 11 +++++++++++
...etadata_propdata_phandle_ref.dts.dts.expect | 18 ++++++++++++++++++
tests/run_tests.sh | 2 +-
5 files changed, 64 insertions(+), 1 deletion(-)
create mode 100644 tests/metadata_propdata_phandle_ref.dtb.dts.expect
create mode 100644 tests/metadata_propdata_phandle_ref.dtb.expect
create mode 100644 tests/metadata_propdata_phandle_ref.dts
create mode 100644 tests/metadata_propdata_phandle_ref.dts.dts.expect
diff --git a/tests/metadata_propdata_phandle_ref.dtb.dts.expect b/tests/metadata_propdata_phandle_ref.dtb.dts.expect
new file mode 100644
index 00000000..61ce943b
--- /dev/null
+++ b/tests/metadata_propdata_phandle_ref.dtb.dts.expect
@@ -0,0 +1,18 @@
+/dts-v1/;
+/plugin/;
+
+/ {
+
+ fragment@0 {
+ target = <&test>;
+
+ __overlay__ {
+ ref = <&node_a>;
+ };
+ };
+
+ __fixups__ {
+ test = "/fragment@0:target:0";
+ node_a = "/fragment@0/__overlay__:ref:0";
+ };
+};
diff --git a/tests/metadata_propdata_phandle_ref.dtb.expect b/tests/metadata_propdata_phandle_ref.dtb.expect
new file mode 100644
index 00000000..f79f542d
--- /dev/null
+++ b/tests/metadata_propdata_phandle_ref.dtb.expect
@@ -0,0 +1,16 @@
+/dts-v1/;
+
+/ {
+ fragment@0 {
+ target = <0xffffffff>;
+ // [FDT_PROPDATA_PHANDLE_REF] target[0], ref = test
+ __overlay__ {
+ ref = <0xffffffff>;
+ // [FDT_PROPDATA_PHANDLE_REF] ref[0], ref = node_a
+ };
+ };
+ __fixups__ {
+ test = "/fragment@0:target:0";
+ node_a = "/fragment@0/__overlay__:ref:0";
+ };
+};
diff --git a/tests/metadata_propdata_phandle_ref.dts b/tests/metadata_propdata_phandle_ref.dts
new file mode 100644
index 00000000..b8aa6509
--- /dev/null
+++ b/tests/metadata_propdata_phandle_ref.dts
@@ -0,0 +1,11 @@
+// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
+/*
+ * Copyright (C) 2026 Bootlin
+ */
+
+/dts-v1/;
+/plugin/;
+
+&test {
+ ref = <&node_a>;
+};
diff --git a/tests/metadata_propdata_phandle_ref.dts.dts.expect b/tests/metadata_propdata_phandle_ref.dts.dts.expect
new file mode 100644
index 00000000..61ce943b
--- /dev/null
+++ b/tests/metadata_propdata_phandle_ref.dts.dts.expect
@@ -0,0 +1,18 @@
+/dts-v1/;
+/plugin/;
+
+/ {
+
+ fragment@0 {
+ target = <&test>;
+
+ __overlay__ {
+ ref = <&node_a>;
+ };
+ };
+
+ __fixups__ {
+ test = "/fragment@0:target:0";
+ node_a = "/fragment@0/__overlay__:ref:0";
+ };
+};
diff --git a/tests/run_tests.sh b/tests/run_tests.sh
index 8c27b463..f5ea02bf 100755
--- a/tests/run_tests.sh
+++ b/tests/run_tests.sh
@@ -1240,7 +1240,7 @@ fdtoverlay_tests() {
}
metadata_tests() {
- for dt in metadata_propdata_phandle_local; do
+ for dt in metadata_propdata_phandle_local metadata_propdata_phandle_ref; do
run_dtc_test -I dts -O dts -o $dt.dts.dts "$SRCDIR/$dt.dts"
base_run_test check_diff $dt.dts.dts "$SRCDIR/$dt.dts.dts.expect"
run_dtc_test -I dts -O dtb -o $dt.dtb "$SRCDIR/$dt.dts"
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 13/74] Add support for /addon/ keyword
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (11 preceding siblings ...)
2026-08-26 9:48 ` [RFC PATCH v2 12/74] tests: metadata: Add external phandle reference tests Herve Codina
@ 2026-08-26 9:48 ` Herve Codina
2026-08-26 9:48 ` [RFC PATCH v2 14/74] tests: Add a test related to addon dt_flags header value Herve Codina
` (60 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:48 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
The dts /addon/ keyword allows to mark a dts as an addon dts.
This is similar to /plugin/ used for overlay dts but specific to addon
dts.
It is also worth noting that a dts tagged with /addon/ will lead to a
dtb with the dt_flags set to FDT_FLAG_ADDON (0x1).
This allows to identify without any ambiguity an addon dts and an addon
dtb.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
checks.c | 36 +++++++++++++++++++++++-------------
dtc-lexer.l | 5 +++++
dtc-parser.y | 5 +++++
dtc.h | 1 +
fdtdump.c | 4 ++++
flattree.c | 15 ++++++++++++---
libfdt/fdt.h | 1 +
treesource.c | 6 ++++++
8 files changed, 57 insertions(+), 16 deletions(-)
diff --git a/checks.c b/checks.c
index a6037ed0..cb3b4b16 100644
--- a/checks.c
+++ b/checks.c
@@ -624,7 +624,7 @@ static void fixup_phandle_references(struct check *c, struct dt_info *dti,
refnode = get_node_by_ref(dt, m->ref);
if (! refnode) {
- if (!(dti->dtsflags & DTSF_PLUGIN))
+ if (!(dti->dtsflags & (DTSF_PLUGIN | DTSF_ADDON)))
FAIL(c, dti, node, "Reference to non-existent node or "
"label \"%s\"\n", m->ref);
else /* mark the entry as unresolved */
@@ -726,8 +726,8 @@ static void check_alias_paths(struct check *c, struct dt_info *dti,
continue;
}
- /* This check does not work for overlays with external paths */
- if (!(dti->dtsflags & DTSF_PLUGIN) &&
+ /* This check does not work for overlays nor addons with external paths */
+ if (!(dti->dtsflags & (DTSF_PLUGIN | DTSF_ADDON)) &&
(!prop->val.val || !get_node_by_path(dti->dt, prop->val.val))) {
FAIL_PROP(c, dti, node, prop, "aliases property is not a valid node (%s)",
prop->val.val);
@@ -1425,8 +1425,8 @@ static void check_property_phandle_args(struct check *c,
* entries when each index position has a specific definition.
*/
if (!phandle_is_valid(phandle)) {
- /* Give up if this is an overlay with external references */
- if (dti->dtsflags & DTSF_PLUGIN)
+ /* Give up if this is an overlay or addon with external references */
+ if (dti->dtsflags & (DTSF_PLUGIN | DTSF_ADDON))
break;
cellsize = 0;
@@ -1659,8 +1659,8 @@ static void check_interrupt_map(struct check *c,
phandle = propval_cell_n(irq_map_prop, cell);
if (!phandle_is_valid(phandle)) {
- /* Give up if this is an overlay with external references */
- if (!(dti->dtsflags & DTSF_PLUGIN))
+ /* Give up if this is an overlay or an addon with external references */
+ if (!(dti->dtsflags & (DTSF_PLUGIN | DTSF_ADDON)))
FAIL_PROP(c, dti, node, irq_map_prop,
"Cell %zu is not a phandle(%d)",
cell, phandle);
@@ -1728,9 +1728,9 @@ static void check_interrupts_property(struct check *c,
if (prop) {
phandle = propval_cell(prop);
if (!phandle_is_valid(phandle)) {
- /* Give up if this is an overlay with
+ /* Give up if this is an overlay with or an addon
* external references */
- if (dti->dtsflags & DTSF_PLUGIN)
+ if (dti->dtsflags & (DTSF_PLUGIN | DTSF_ADDON))
return;
FAIL_PROP(c, dti, parent, prop, "Invalid phandle");
continue;
@@ -1846,8 +1846,8 @@ static void check_graph_port(struct check *c, struct dt_info *dti,
check_graph_reg(c, dti, node);
- /* skip checks below for overlays */
- if (dti->dtsflags & DTSF_PLUGIN)
+ /* skip checks below for overlays or addons */
+ if (dti->dtsflags & (DTSF_PLUGIN | DTSF_ADDON))
return;
if (!strprefixeq(node->name, node->basenamelen, "port"))
@@ -1888,8 +1888,8 @@ static void check_graph_endpoint(struct check *c, struct dt_info *dti,
check_graph_reg(c, dti, node);
- /* skip checks below for overlays */
- if (dti->dtsflags & DTSF_PLUGIN)
+ /* skip checks below for overlays or addons */
+ if (dti->dtsflags & (DTSF_PLUGIN | DTSF_ADDON))
return;
if (!strprefixeq(node->name, node->basenamelen, "endpoint"))
@@ -2062,6 +2062,16 @@ void process_checks(bool force, struct dt_info *dti)
unsigned int i;
int error = 0;
+ if ((dti->dtsflags & (DTSF_ADDON | DTSF_PLUGIN)) == (DTSF_ADDON | DTSF_PLUGIN)) {
+ /*
+ * Addons and overlays are mutually exclusive. The same input
+ * cannot be both.
+ */
+ fprintf(stderr,
+ "ERROR: Input tree cannot be both an addon and an overlay\n");
+ exit(2);
+ }
+
for (i = 0; i < ARRAY_SIZE(check_table); i++) {
struct check *c = check_table[i];
diff --git a/dtc-lexer.l b/dtc-lexer.l
index 1b129b11..7e9b6796 100644
--- a/dtc-lexer.l
+++ b/dtc-lexer.l
@@ -108,6 +108,11 @@ static void PRINTF(1, 2) lexical_error(const char *fmt, ...);
return DT_PLUGIN;
}
+<*>"/addon/" {
+ DPRINT("Keyword: /addon/\n");
+ return DT_ADDON;
+ }
+
<*>"/memreserve/" {
DPRINT("Keyword: /memreserve/\n");
BEGIN_DEFAULT();
diff --git a/dtc-parser.y b/dtc-parser.y
index e47f80d4..2e152b0e 100644
--- a/dtc-parser.y
+++ b/dtc-parser.y
@@ -53,6 +53,7 @@ static bool is_ref_relative(const char *ref)
%token DT_V1
%token DT_PLUGIN
+%token DT_ADDON
%token DT_MEMRESERVE
%token DT_LSHIFT DT_RSHIFT DT_LE DT_GE DT_EQ DT_NE DT_AND DT_OR
%token DT_BITS
@@ -120,6 +121,10 @@ header:
{
$$ = DTSF_V1 | DTSF_PLUGIN;
}
+ | DT_V1 ';' DT_ADDON ';'
+ {
+ $$ = DTSF_V1 | DTSF_ADDON;
+ }
;
headers:
diff --git a/dtc.h b/dtc.h
index 64eaffde..40aab572 100644
--- a/dtc.h
+++ b/dtc.h
@@ -336,6 +336,7 @@ struct dt_info {
/* DTS version flags definitions */
#define DTSF_V1 0x0001 /* /dts-v1/ */
#define DTSF_PLUGIN 0x0002 /* /plugin/ */
+#define DTSF_ADDON 0x0004 /* /addon/ */
struct dt_info *build_dt_info(unsigned int dtsflags,
struct reserve_info *reservelist,
diff --git a/fdtdump.c b/fdtdump.c
index 2acf3210..370db848 100644
--- a/fdtdump.c
+++ b/fdtdump.c
@@ -120,6 +120,10 @@ static void dump_blob(void *blob, bool debug, int dump_unknown)
printf("// last_comp_version_w:\t%"PRIu32"\n",
fdt32_to_cpu(bph->last_comp_version_w));
}
+ if (version >= 18) {
+ if (fdt32_to_cpu(bph->dt_flags) & FDT_FLAG_ADDON)
+ printf("/addon/;\n");
+ }
printf("\n");
for (i = 0; ; i++) {
diff --git a/flattree.c b/flattree.c
index abc50873..004119c4 100644
--- a/flattree.c
+++ b/flattree.c
@@ -420,6 +420,7 @@ void dt_to_blob(FILE *f, struct dt_info *dti, int version)
struct data dtbuf = empty_data;
struct data strbuf = empty_data;
struct fdt_header fdt;
+ uint32_t dt_flags = 0;
int padlen = 0;
for (i = 0; i < ARRAY_SIZE(version_table); i++) {
@@ -429,6 +430,8 @@ void dt_to_blob(FILE *f, struct dt_info *dti, int version)
if (!vi)
die("Unknown device tree blob version %d\n", version);
+ dt_flags |= dti->dtsflags & DTSF_ADDON ? FDT_FLAG_ADDON : 0;
+
flatten_tree(dti->dt, &bin_emitter, &dtbuf, &strbuf, vi);
bin_emit_cell(&dtbuf, FDT_END);
@@ -436,7 +439,7 @@ void dt_to_blob(FILE *f, struct dt_info *dti, int version)
/* Make header */
make_fdt_header(&fdt, vi, reservebuf.len, dtbuf.len, strbuf.len,
- dti->boot_cpuid_phys, 0);
+ dti->boot_cpuid_phys, dt_flags);
/*
* If the user asked for more space than is used, adjust the totalsize.
@@ -519,6 +522,7 @@ void dt_to_asm(FILE *f, struct dt_info *dti, int version)
struct data strbuf = empty_data;
struct reserve_info *re;
const char *symprefix = "dt";
+ uint32_t dt_flags = 0;
for (i = 0; i < ARRAY_SIZE(version_table); i++) {
if (version_table[i].version == version)
@@ -527,6 +531,8 @@ void dt_to_asm(FILE *f, struct dt_info *dti, int version)
if (!vi)
die("Unknown device tree blob version %d\n", version);
+ dt_flags |= dti->dtsflags & DTSF_ADDON ? FDT_FLAG_ADDON : 0;
+
fprintf(f, "/* autogenerated by dtc, do not edit */\n\n");
emit_label(f, symprefix, "blob_start");
@@ -569,7 +575,7 @@ void dt_to_asm(FILE *f, struct dt_info *dti, int version)
if (vi->flags & FTF_DTFLAGS) {
fprintf(f, "\t/* dt_flags */\n");
- asm_emit_cell(f, 0);
+ asm_emit_cell(f, dt_flags);
}
if (vi->flags & FTF_LCVERSW) {
@@ -1113,8 +1119,11 @@ struct dt_info *dt_from_blob(const char *fname)
if (version >= 18)
flags |= FTF_DTFLAGS | FTF_LCVERSW;
- if (version >= 20)
+ if (version >= 20) {
flags |= FTF_PROPDATA_PHANDLE;
+ dtsflags |= fdt32_to_cpu(fdt->dt_flags) & FDT_FLAG_ADDON ?
+ DTSF_ADDON : 0;
+ }
inbuf_init(&memresvbuf,
blob + off_mem_rsvmap, blob + totalsize);
diff --git a/libfdt/fdt.h b/libfdt/fdt.h
index c151aa2b..255e9447 100644
--- a/libfdt/fdt.h
+++ b/libfdt/fdt.h
@@ -52,6 +52,7 @@ struct fdt_property {
#endif /* !__ASSEMBLER__ */
#define FDT_MAGIC 0xd00dfeed /* 4: version, 4: total size */
+#define FDT_FLAG_ADDON 0x1
#define FDT_TAGSIZE sizeof(fdt32_t)
#define FDT_CELLSIZE sizeof(fdt32_t)
diff --git a/treesource.c b/treesource.c
index 0e33e3e1..9f6cf92d 100644
--- a/treesource.c
+++ b/treesource.c
@@ -420,8 +420,14 @@ void dt_to_source(FILE *f, struct dt_info *dti)
struct reserve_info *re;
fprintf(f, "/dts-v1/;\n");
+ /*
+ * DTSF_PLUGIN and DTSF_ADDON are mutually exclusive. This has been
+ * already checked.
+ */
if (dti->dtsflags & DTSF_PLUGIN)
fprintf(f, "/plugin/;\n");
+ if (dti->dtsflags & DTSF_ADDON)
+ fprintf(f, "/addon/;\n");
fprintf(f, "\n");
for (re = dti->reservelist; re; re = re->next) {
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 14/74] tests: Add a test related to addon dt_flags header value
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (12 preceding siblings ...)
2026-08-26 9:48 ` [RFC PATCH v2 13/74] Add support for /addon/ keyword Herve Codina
@ 2026-08-26 9:48 ` Herve Codina
2026-08-26 9:48 ` [RFC PATCH v2 15/74] tests: metadata: Add a basic addon test Herve Codina
` (59 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:48 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
Addon dtbs leads to the dt_flags header field set to FDT_FLAG_ADDON
(0x1).
Add a specific test to check this value.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
tests/dtflags_addon.dtb.expect | 1 +
tests/dtflags_addon.dts | 11 +++++++++++
tests/run_tests.sh | 8 ++++++++
3 files changed, 20 insertions(+)
create mode 100644 tests/dtflags_addon.dtb.expect
create mode 100644 tests/dtflags_addon.dts
diff --git a/tests/dtflags_addon.dtb.expect b/tests/dtflags_addon.dtb.expect
new file mode 100644
index 00000000..00f98f32
--- /dev/null
+++ b/tests/dtflags_addon.dtb.expect
@@ -0,0 +1 @@
+// dt_flags: 0x1
diff --git a/tests/dtflags_addon.dts b/tests/dtflags_addon.dts
new file mode 100644
index 00000000..d2b22951
--- /dev/null
+++ b/tests/dtflags_addon.dts
@@ -0,0 +1,11 @@
+// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
+/*
+ * Copyright (C) 2026 Bootlin
+ */
+
+/dts-v1/;
+/addon/;
+
+/ {
+ prop = <1>;
+};
diff --git a/tests/run_tests.sh b/tests/run_tests.sh
index f5ea02bf..734fd39a 100755
--- a/tests/run_tests.sh
+++ b/tests/run_tests.sh
@@ -926,6 +926,14 @@ dtc_tests () {
sed -i '/dt_flags/!d' dtflags_default.dtb.out
base_run_test check_diff dtflags_default.dtb.out "$SRCDIR/dtflags_default.dtb.expect"
run_test check_full dtflags_default.dtb
+
+ # Tests dt_flags header field (FDT_FLAG_ADDON value)
+ run_dtc_test -I dts -O dtb -o dtflags_addon.dtb "$SRCDIR/dtflags_addon.dts"
+ base_run_test wrap_fdtdump dtflags_addon.dtb dtflags_addon.dtb.out
+ # Keep only lines containing 'dt_flags'
+ sed -i '/dt_flags/!d' dtflags_addon.dtb.out
+ base_run_test check_diff dtflags_addon.dtb.out "$SRCDIR/dtflags_addon.dtb.expect"
+ run_test check_full dtflags_addon.dtb
}
cmp_tests () {
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 15/74] tests: metadata: Add a basic addon test
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (13 preceding siblings ...)
2026-08-26 9:48 ` [RFC PATCH v2 14/74] tests: Add a test related to addon dt_flags header value Herve Codina
@ 2026-08-26 9:48 ` Herve Codina
2026-08-26 9:48 ` [RFC PATCH v2 16/74] dtc: Introduce export symbols Herve Codina
` (58 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:48 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
Support for addon dts (/addon/ keyword) and addon dtb (FDT_FLAG_ADDON
header flag) have been recently introduced.
Even if the FDT_FLAG_ADDON dtb flag itself has its own test already
available, add a basic addon test in the metadata 'dts->dts and
dts->dtb->dts' test patterns to check that addon dts/dtb are
handled correctly.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
tests/metadata_addon_base.dtb.dts.expect | 10 ++++++++++
tests/metadata_addon_base.dtb.expect | 10 ++++++++++
tests/metadata_addon_base.dts | 14 ++++++++++++++
tests/metadata_addon_base.dts.dts.expect | 10 ++++++++++
tests/run_tests.sh | 3 ++-
5 files changed, 46 insertions(+), 1 deletion(-)
create mode 100644 tests/metadata_addon_base.dtb.dts.expect
create mode 100644 tests/metadata_addon_base.dtb.expect
create mode 100644 tests/metadata_addon_base.dts
create mode 100644 tests/metadata_addon_base.dts.dts.expect
diff --git a/tests/metadata_addon_base.dtb.dts.expect b/tests/metadata_addon_base.dtb.dts.expect
new file mode 100644
index 00000000..739f9db3
--- /dev/null
+++ b/tests/metadata_addon_base.dtb.dts.expect
@@ -0,0 +1,10 @@
+/dts-v1/;
+/addon/;
+
+/ {
+
+ node-a {
+ prop = <0x01>;
+ ref-base = <&base>;
+ };
+};
diff --git a/tests/metadata_addon_base.dtb.expect b/tests/metadata_addon_base.dtb.expect
new file mode 100644
index 00000000..27378475
--- /dev/null
+++ b/tests/metadata_addon_base.dtb.expect
@@ -0,0 +1,10 @@
+/dts-v1/;
+/addon/;
+
+/ {
+ node-a {
+ prop = <0x00000001>;
+ ref-base = <0xffffffff>;
+ // [FDT_PROPDATA_PHANDLE_REF] ref-base[0], ref = base
+ };
+};
diff --git a/tests/metadata_addon_base.dts b/tests/metadata_addon_base.dts
new file mode 100644
index 00000000..5e29cef9
--- /dev/null
+++ b/tests/metadata_addon_base.dts
@@ -0,0 +1,14 @@
+// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
+/*
+ * Copyright (C) 2026 Bootlin
+ */
+
+/dts-v1/;
+/addon/;
+
+/ {
+ node-a {
+ prop = <1>;
+ ref-base = <&base>;
+ };
+};
diff --git a/tests/metadata_addon_base.dts.dts.expect b/tests/metadata_addon_base.dts.dts.expect
new file mode 100644
index 00000000..739f9db3
--- /dev/null
+++ b/tests/metadata_addon_base.dts.dts.expect
@@ -0,0 +1,10 @@
+/dts-v1/;
+/addon/;
+
+/ {
+
+ node-a {
+ prop = <0x01>;
+ ref-base = <&base>;
+ };
+};
diff --git a/tests/run_tests.sh b/tests/run_tests.sh
index 734fd39a..f119acc0 100755
--- a/tests/run_tests.sh
+++ b/tests/run_tests.sh
@@ -1248,7 +1248,8 @@ fdtoverlay_tests() {
}
metadata_tests() {
- for dt in metadata_propdata_phandle_local metadata_propdata_phandle_ref; do
+ for dt in metadata_propdata_phandle_local metadata_propdata_phandle_ref \
+ metadata_addon_base; do
run_dtc_test -I dts -O dts -o $dt.dts.dts "$SRCDIR/$dt.dts"
base_run_test check_diff $dt.dts.dts "$SRCDIR/$dt.dts.dts.expect"
run_dtc_test -I dts -O dtb -o $dt.dtb "$SRCDIR/$dt.dts"
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 16/74] dtc: Introduce export symbols
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (14 preceding siblings ...)
2026-08-26 9:48 ` [RFC PATCH v2 15/74] tests: metadata: Add a basic addon test Herve Codina
@ 2026-08-26 9:48 ` Herve Codina
2026-08-26 9:48 ` [RFC PATCH v2 17/74] dtc: Add support for /export/ dts keyword parsing Herve Codina
` (57 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:48 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
Export symbols allow to define a list of symbols exported at a given
node level. Those exported symbols can be used by an addon when the
addon is applied on the node exporting the symbols. In order to perform
its symbol resolution. Any unresolved phandle value will be resolved
using those exported symbols.
The feature is similar to __symbols__ involved with overlay but while
all symbols are visible with __symbols__, only specific symbols
(exported symbols) are visible with export symbols.
Also an exported symbol has a specific name and this name has to
used for symbol resolution.
Having both specific exported symbol names and exported symbols at
specific node level allow to:
- Have several nodes providing the same exported symbols
name but each of them pointing to different nodes.
Without looking at the detail of the syntax, node-a and node-b
export the same symbol foo but pointing to different node.
node-a {
/* export foo -> /path/foo1 */
};
node-b {
/* export foo -> /path/foo2 */
};
This allow to have the exact same addon referencing 'foo' applied
either on node-a or node-b.
- Have several board describing a well defined node even if resources
needed for exported symbols are not the same.
On board A, the 'ctrl' exported symbols points to some ctrl device
available on the SoC:
node {
/* export 'ctrl' -> /soc/ctrl@1000
};
On board B, the ctrl device used is on a i2c bus
node {
/* export 'ctrl' -> /soc/i2c@5000/ctrl@10
};
The addon can be used on board A and board B without any
modification. It uses 'ctrl' exported by the node it is applied
to.
Introduce the 'symbol' internal data structure and the export symbol
list related to a node.
No functional change yet but preparation for the future support
for export symbol parsing.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
dtc-parser.y | 6 +++---
dtc.h | 13 ++++++++++++-
flattree.c | 2 +-
fstree.c | 2 +-
livetree.c | 7 ++++---
5 files changed, 21 insertions(+), 9 deletions(-)
diff --git a/dtc-parser.y b/dtc-parser.y
index 2e152b0e..9c936736 100644
--- a/dtc-parser.y
+++ b/dtc-parser.y
@@ -183,7 +183,7 @@ devicetree:
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),
+ name_node(build_node(NULL, NULL, NULL, NULL),
""),
$2, $1);
}
@@ -270,11 +270,11 @@ devicetree:
nodedef:
'{' proplist subnodes '}' ';'
{
- $$ = build_node($2, $3, &@$);
+ $$ = build_node($2, $3, NULL, &@$);
}
| '{' subnodes '}' ';'
{
- $$ = build_node(NULL, $2, &@$);
+ $$ = build_node(NULL, $2, NULL, &@$);
}
;
diff --git a/dtc.h b/dtc.h
index 40aab572..af69b193 100644
--- a/dtc.h
+++ b/dtc.h
@@ -204,6 +204,16 @@ struct label {
struct label *next;
};
+struct symbol {
+ bool is_local;
+ char *name;
+ char *ref;
+ cell_t phandle;
+ char *fullpath;
+ struct symbol *next;
+ struct srcpos *srcpos;
+};
+
struct bus_type {
const char *name;
};
@@ -224,6 +234,7 @@ struct node {
char *name;
struct property *proplist;
struct node *children;
+ struct symbol *exportsymlist;
struct node *parent;
struct node *next_sibling;
@@ -272,7 +283,7 @@ struct property *chain_property(struct property *first, struct property *list);
struct property *reverse_properties(struct property *first);
struct node *build_node(struct property *proplist, struct node *children,
- struct srcpos *srcpos);
+ struct symbol *exportsymlist, struct srcpos *srcpos);
struct node *build_node_delete(struct srcpos *srcpos);
struct node *name_node(struct node *node, const char *name);
struct node *omit_node_if_unused(struct node *node);
diff --git a/flattree.c b/flattree.c
index 004119c4..ac6bcd8f 100644
--- a/flattree.c
+++ b/flattree.c
@@ -902,7 +902,7 @@ static struct node *unflatten_tree(struct inbuf *dtbuf,
uint32_t len;
const char *str;
- node = build_node(NULL, NULL, NULL);
+ node = build_node(NULL, NULL, NULL, NULL);
flatname = flat_read_string(dtbuf);
diff --git a/fstree.c b/fstree.c
index 0f9a534b..445ae53a 100644
--- a/fstree.c
+++ b/fstree.c
@@ -19,7 +19,7 @@ static struct node *read_fstree(const char *dirname)
if (!d)
die("Couldn't opendir() \"%s\": %s\n", dirname, strerror(errno));
- tree = build_node(NULL, NULL, NULL);
+ tree = build_node(NULL, NULL, NULL, NULL);
while ((de = readdir(d)) != NULL) {
char *tmpname;
diff --git a/livetree.c b/livetree.c
index 2ad7fd09..a7f81a90 100644
--- a/livetree.c
+++ b/livetree.c
@@ -86,7 +86,7 @@ struct property *reverse_properties(struct property *first)
}
struct node *build_node(struct property *proplist, struct node *children,
- struct srcpos *srcpos)
+ struct symbol *exportsymlist, struct srcpos *srcpos)
{
struct node *new = xmalloc(sizeof(*new));
struct node *child;
@@ -95,6 +95,7 @@ struct node *build_node(struct property *proplist, struct node *children,
new->proplist = reverse_properties(proplist);
new->children = children;
+ new->exportsymlist = exportsymlist;
new->srcpos = srcpos_copy(srcpos);
for_each_child(new, child) {
@@ -248,7 +249,7 @@ struct node * add_orphan_node(struct node *dt, struct node *new_node, char *ref)
xasprintf(&name, "fragment@%u",
next_orphan_fragment++);
name_node(new_node, "__overlay__");
- node = build_node(p, new_node, NULL);
+ node = build_node(p, new_node, NULL, NULL);
name_node(node, name);
free(name);
@@ -993,7 +994,7 @@ static struct node *build_and_name_child_node(struct node *parent, const char *n
{
struct node *node;
- node = build_node(NULL, NULL, NULL);
+ node = build_node(NULL, NULL, NULL, NULL);
name_node(node, name);
add_child(parent, node);
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 17/74] dtc: Add support for /export/ dts keyword parsing
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (15 preceding siblings ...)
2026-08-26 9:48 ` [RFC PATCH v2 16/74] dtc: Introduce export symbols Herve Codina
@ 2026-08-26 9:48 ` Herve Codina
2026-08-26 9:48 ` [RFC PATCH v2 18/74] checks: Handle export symbols in fixup_phandle_references() Herve Codina
` (56 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:48 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
The /export/ dts keyword is the keyword used to define an exported
symbol at a given node level.
This keyword can be present in a node definition (after properties and
before subnodes) to export a symbol. If several symbols need to be
exported, several /export/ keywords are present.
The syntax used is the following:
/export/ name: reference;
with:
name: The name of the exported symbol
reference: The reference of a node the symbol is pointing to.
For instance:
- Reference by label:
/export/ foo: &foo1;
The exported symbol foo references the node identified by
the label foo1.
- Reference by path:
/export/ foo: &{/path/to/foo@100};
The exported symbol foo references the node at /path/to/foo@100.
Add support for /export/ dts keyword.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
dtc-lexer.l | 6 ++++
dtc-parser.y | 53 ++++++++++++++++++++++++++++++++++
dtc.h | 8 ++++++
livetree.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 147 insertions(+)
diff --git a/dtc-lexer.l b/dtc-lexer.l
index 7e9b6796..3c3f3413 100644
--- a/dtc-lexer.l
+++ b/dtc-lexer.l
@@ -146,6 +146,12 @@ static void PRINTF(1, 2) lexical_error(const char *fmt, ...);
return DT_OMIT_NO_REF;
}
+<*>"/export/" {
+ DPRINT("Keyword: /export/\n");
+ BEGIN_DEFAULT();
+ return DT_EXPORT;
+ }
+
<*>{LABEL}: {
DPRINT("Label: %s\n", yytext);
yylval.labelref = xstrdup(yytext);
diff --git a/dtc-parser.y b/dtc-parser.y
index 9c936736..a0d0aef9 100644
--- a/dtc-parser.y
+++ b/dtc-parser.y
@@ -46,6 +46,8 @@ static bool is_ref_relative(const char *ref)
struct property *proplist;
struct node *node;
struct node *nodelist;
+ struct symbol *symbol;
+ struct symbol *exportlist;
struct reserve_info *re;
uint64_t integer;
unsigned int flags;
@@ -60,6 +62,7 @@ static bool is_ref_relative(const char *ref)
%token DT_DEL_PROP
%token DT_DEL_NODE
%token DT_OMIT_NO_REF
+%token DT_EXPORT
%token <propnodename> DT_PROPNODENAME
%token <integer> DT_LITERAL
%token <integer> DT_CHAR_LITERAL
@@ -80,6 +83,8 @@ static bool is_ref_relative(const char *ref)
%type <data> bytestring
%type <prop> propdef
%type <proplist> proplist
+%type <symbol> exportdef
+%type <exportlist> exportlist
%type <labelref> dt_ref
%type <node> devicetree
@@ -276,6 +281,49 @@ nodedef:
{
$$ = build_node(NULL, $2, NULL, &@$);
}
+ | '{' proplist exportlist subnodes '}' ';'
+ {
+ /*
+ * exportlist is created with chain_symbol() and so it
+ * is created in reverse order. Reverse it now to have
+ * it in correct order
+ */
+ $$ = build_node($2, $4, reverse_symbol($3), &@$);
+ }
+ | '{' exportlist subnodes '}' ';'
+ {
+ /*
+ * exportlist is created with chain_symbol() and so it
+ * is created in reverse order. Reverse it now to have
+ * it in correct order
+ */
+ $$ = build_node(NULL, $3, reverse_symbol($2), &@$);
+ }
+ ;
+
+exportlist:
+ exportdef
+ {
+ $$ = chain_symbol($1, NULL);
+ }
+ | exportlist exportdef
+ {
+ $$ = chain_symbol($2, $1);
+ }
+ | exportlist propdef
+ {
+ ERROR(&@2, "Properties must precede exports");
+ YYERROR;
+ }
+ ;
+
+exportdef:
+ DT_EXPORT DT_LABEL dt_ref ';'
+ {
+ $$ = build_exportsym($2, $3, 0, &@$);
+ free($2);
+ free($3);
+ }
;
proplist:
@@ -576,6 +624,11 @@ subnodes:
ERROR(&@2, "Properties must precede subnodes");
YYERROR;
}
+ | subnode exportdef
+ {
+ ERROR(&@2, "Exports must precede subnodes");
+ YYERROR;
+ }
;
subnode:
diff --git a/dtc.h b/dtc.h
index af69b193..90341aa9 100644
--- a/dtc.h
+++ b/dtc.h
@@ -273,6 +273,9 @@ struct node {
for_each_child_withdel(n, c) \
if (!(c)->deleted)
+#define for_each_symbol(s0, s) \
+ for ((s) = (s0); (s); (s) = (s)->next)
+
void add_label(struct label **labels, char *label);
void delete_labels(struct label **labels);
@@ -282,6 +285,11 @@ struct property *build_property_delete(const char *name);
struct property *chain_property(struct property *first, struct property *list);
struct property *reverse_properties(struct property *first);
+struct symbol *build_exportsym(const char *name, const char *ref, cell_t phandle,
+ struct srcpos *srcpos);
+struct symbol *chain_symbol(struct symbol *first, struct symbol *list);
+struct symbol *reverse_symbol(struct symbol *list);
+
struct node *build_node(struct property *proplist, struct node *children,
struct symbol *exportsymlist, struct srcpos *srcpos);
struct node *build_node_delete(struct srcpos *srcpos);
diff --git a/livetree.c b/livetree.c
index a7f81a90..111c651b 100644
--- a/livetree.c
+++ b/livetree.c
@@ -36,6 +36,57 @@ void delete_labels(struct label **labels)
label->deleted = 1;
}
+struct symbol *build_exportsym(const char *name, const char *ref, cell_t phandle,
+ struct srcpos *srcpos)
+{
+ struct symbol *new = xmalloc(sizeof(*new));
+
+ memset(new, 0, sizeof(*new));
+
+ new->name = xstrdup(name);
+ new->ref = ref ? xstrdup(ref) : NULL;
+ new->phandle = phandle;
+ new->srcpos = srcpos_copy(srcpos);
+
+ return new;
+}
+
+struct symbol *chain_symbol(struct symbol *first, struct symbol *list)
+{
+ assert(first->next == NULL);
+
+ first->next = list;
+ return first;
+}
+
+struct symbol *reverse_symbol(struct symbol *list)
+{
+ struct symbol *p = list;
+ struct symbol *head = NULL;
+ struct symbol *next;
+
+ while (p) {
+ next = p->next;
+ p->next = head;
+ head = p;
+ p = next;
+ }
+ return head;
+}
+
+static void add_symbol(struct symbol **list, struct symbol *new)
+{
+ struct symbol **s;
+
+ new->next = NULL;
+
+ s = list;
+ while (*s)
+ s = &((*s)->next);
+
+ *s = new;
+}
+
struct property *build_property(const char *name, struct data val,
struct srcpos *srcpos)
{
@@ -144,6 +195,7 @@ struct node *merge_nodes(struct node *old_node, struct node *new_node)
{
struct property *new_prop, *old_prop;
struct node *new_child, *old_child;
+ struct symbol *new_sym, *old_sym;
struct label *l;
old_node->deleted = 0;
@@ -217,6 +269,34 @@ struct node *merge_nodes(struct node *old_node, struct node *new_node)
add_child(old_node, new_child);
}
+ /* Merge exported symbols. If there is a collision, keep the new one */
+ while (new_node->exportsymlist) {
+ /* Pop the symbol off the list */
+ new_sym = new_node->exportsymlist;
+ new_node->exportsymlist = new_sym->next;
+ new_sym->next = NULL;
+
+ /* Look for a collision, set new value if there is */
+ for_each_symbol(old_node->exportsymlist, old_sym) {
+ if (streq(old_sym->name, new_sym->name)) {
+ old_sym->is_local = new_sym->is_local;
+ free(old_sym->ref);
+ old_sym->ref = new_sym->ref;
+ old_sym->phandle = new_sym->phandle;
+ old_sym->fullpath = new_sym->fullpath;
+ srcpos_free(old_sym->srcpos);
+ old_sym->srcpos = new_sym->srcpos;
+ free(new_sym);
+ new_sym = NULL;
+ break;
+ }
+ }
+
+ /* if no collision occurred, add symbol to the old node list. */
+ if (new_sym)
+ add_symbol(&old_node->exportsymlist, new_sym);
+ }
+
old_node->srcpos = srcpos_extend(old_node->srcpos, new_node->srcpos);
/* The new node contents are now merged into the old node. Free
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 18/74] checks: Handle export symbols in fixup_phandle_references()
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (16 preceding siblings ...)
2026-08-26 9:48 ` [RFC PATCH v2 17/74] dtc: Add support for /export/ dts keyword parsing Herve Codina
@ 2026-08-26 9:48 ` Herve Codina
2026-08-26 9:48 ` [RFC PATCH v2 19/74] dtc: Add export symbols (/export/ keyword) in generated dts file Herve Codina
` (55 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:48 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
Export symbols use phandle references.
Referenced nodes must have a phandle property set in order to have those
references working.
fixup_phandle_references() does the needed operation for properties
that use a phandle.
Extend fixup_phandle_references() to perform exact same operation for
export symbols.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
checks.c | 24 ++++++++++++++++++++++--
1 file changed, 22 insertions(+), 2 deletions(-)
diff --git a/checks.c b/checks.c
index cb3b4b16..13e8b121 100644
--- a/checks.c
+++ b/checks.c
@@ -612,12 +612,13 @@ static void fixup_phandle_references(struct check *c, struct dt_info *dti,
struct node *node)
{
struct node *dt = dti->dt;
+ struct symbol *exportsym;
struct property *prop;
+ struct node *refnode;
+ cell_t phandle;
for_each_property(node, prop) {
struct marker *m = prop->val.markers;
- struct node *refnode;
- cell_t phandle;
for_each_marker_of_type(m, REF_PHANDLE) {
assert(m->offset + sizeof(cell_t) <= prop->val.len);
@@ -639,6 +640,25 @@ static void fixup_phandle_references(struct check *c, struct dt_info *dti,
reference_node(refnode);
}
}
+
+ for_each_symbol(node->exportsymlist, exportsym) {
+ refnode = get_node_by_ref(dt, exportsym->ref);
+ if (!refnode) {
+ if (!(dti->dtsflags & (DTSF_PLUGIN | DTSF_ADDON))) {
+ FAIL(c, dti, node,
+ "Export \"%s\" references to non-existent node or label \"%s\"\n",
+ exportsym->name, exportsym->ref);
+ }
+ exportsym->phandle = cpu_to_fdt32(0xffffffff);
+ continue;
+ }
+
+ /* Create the phandle property for this referenced node */
+ phandle = get_node_phandle(dt, refnode);
+ exportsym->phandle = phandle;
+
+ reference_node(refnode);
+ }
}
ERROR(phandle_references, fixup_phandle_references, NULL,
&duplicate_node_names, &explicit_phandles);
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 19/74] dtc: Add export symbols (/export/ keyword) in generated dts file
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (17 preceding siblings ...)
2026-08-26 9:48 ` [RFC PATCH v2 18/74] checks: Handle export symbols in fixup_phandle_references() Herve Codina
@ 2026-08-26 9:48 ` Herve Codina
2026-08-26 9:48 ` [RFC PATCH v2 20/74] dtc: Extend complete_references() to handle export symbols Herve Codina
` (54 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:48 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
The export symbols (/export/ keyword) parsing from a dts file is
supported.
Add the support for this keyword in the dts file generation.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
treesource.c | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/treesource.c b/treesource.c
index 9f6cf92d..a7228f60 100644
--- a/treesource.c
+++ b/treesource.c
@@ -370,6 +370,7 @@ static void write_propval(FILE *f, struct property *prop)
static void write_tree_source_node(FILE *f, struct node *tree, int level)
{
+ struct symbol *exportsym;
struct property *prop;
struct node *child;
struct label *l;
@@ -399,6 +400,18 @@ static void write_tree_source_node(FILE *f, struct node *tree, int level)
fprintf(f, "%s", prop->name);
write_propval(f, prop);
}
+
+ if (tree->exportsymlist)
+ fprintf(f, "\n");
+ for_each_symbol(tree->exportsymlist, exportsym) {
+ write_prefix(f, level+1);
+ fprintf(f, "/export/ %s: ", exportsym->name);
+ if (exportsym->ref[0] == '/')
+ fprintf(f, "&{%s};\n", exportsym->ref);
+ else
+ fprintf(f, "&%s;\n", exportsym->ref);
+ }
+
for_each_child(tree, child) {
fprintf(f, "\n");
write_tree_source_node(f, child, level+1);
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 20/74] dtc: Extend complete_references() to handle export symbols
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (18 preceding siblings ...)
2026-08-26 9:48 ` [RFC PATCH v2 19/74] dtc: Add export symbols (/export/ keyword) in generated dts file Herve Codina
@ 2026-08-26 9:48 ` Herve Codina
2026-08-26 9:48 ` [RFC PATCH v2 21/74] Add support for FDT_EXPORT_SYM dtb tag Herve Codina
` (53 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:48 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
Export symbols parsed from a .dts or .dtb file suffer from the same
incompleteness as REF_PHANDLE markers. They may lack either the
ref string or the is_local flag depending on the device-tree input
format used.
Apply the same completion operation as the one done for REF_PHANDLE
markers.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
livetree.c | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/livetree.c b/livetree.c
index 111c651b..8c94e81f 100644
--- a/livetree.c
+++ b/livetree.c
@@ -584,6 +584,7 @@ static void complete_ref_and_is_local(struct dt_info *dti, char **ref,
static void complete_references_internal(struct dt_info *dti, struct node *node)
{
+ struct symbol *exportsym;
struct property *prop;
struct marker *m;
struct node *c;
@@ -613,6 +614,16 @@ static void complete_references_internal(struct dt_info *dti, struct node *node)
}
}
+ /*
+ * The exact same behavior exists for export symbols when a dts or a dtb
+ * is parsed. Perform the exact same completion for export symbols
+ */
+ for_each_symbol(node->exportsymlist, exportsym) {
+ complete_ref_and_is_local(dti, &exportsym->ref,
+ &exportsym->is_local,
+ exportsym->phandle);
+ }
+
for_each_child(node, c)
complete_references_internal(dti, c);
}
@@ -637,7 +648,7 @@ struct dt_info *build_dt_info(unsigned int dtsflags,
fill_fullpaths(dti->dt, "");
/*
- * Complete references (REF_PHANDLE).
+ * Complete references (REF_PHANDLE and export symbols).
* Set ref strings and set is_local flags
*/
complete_references(dti);
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 21/74] Add support for FDT_EXPORT_SYM dtb tag
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (19 preceding siblings ...)
2026-08-26 9:48 ` [RFC PATCH v2 20/74] dtc: Extend complete_references() to handle export symbols Herve Codina
@ 2026-08-26 9:48 ` Herve Codina
2026-08-26 9:48 ` [RFC PATCH v2 22/74] tests: metadata: Add export symbols with local references tests Herve Codina
` (52 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:48 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
The FDT_EXPORT_SYM dtb tag is a meta-data tag defining an exported
symbol. It can be present in a node bloc meaning that a symbol is
exported at this node level. The node pointed to by this symbol is a
local node and identified by a phandle value.
The tag is followed by the length of the data part (structured tag), two
values and a possible alignment padding:
- data part length (32bit): 4 + strlen(name) + 1
- phandle (32bit)
The phandle value identifying the node referenced by this symbol.
- name (string including \0)
The export symbol name. I.e. the name used to reference this
exported symbol.
- padding:
Padding (0x00) added to have the next value aligned on 32bit.
Example:
FDT_EXPORT_SYM 9 0x00000004 'foo1' 0x00 0x00 0x00
This means that 'foo1' is an exported symbol and the node referenced
by this symbol is the node with the phandle value equals 4.
This is what is encoded in the dtb when the related dts has the
following exported symbol defined:
/export/ foo1: &foo;
with 'foo' a reference to an existing node where the phandle value is
0x0000004.
If several symbols are exported at a given node level, several
FDT_EXPORT_SYM are present. Each of them defining one symbol.
For instance, exporting 'foo' pointing to phandle 4 and 'bar' pointing
to phandle 8 leads to the following sequence:
FDT_EXPORT_SYM 8 0x00000004 'foo'
FDT_EXPORT_SYM 8 0x00000008 'bar'
Add support for this new dtb tag.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
dtc.h | 1 +
fdtdump.c | 11 ++++++++-
flattree.c | 53 ++++++++++++++++++++++++++++++++++++++--
libfdt/fdt.c | 29 ++++++++++++++++++++++
libfdt/fdt.h | 13 ++++++++++
libfdt/fdt_check.c | 40 ++++++++++++++++++++++++++++++
libfdt/libfdt_internal.h | 1 +
livetree.c | 2 +-
8 files changed, 146 insertions(+), 4 deletions(-)
diff --git a/dtc.h b/dtc.h
index 90341aa9..8a20d6da 100644
--- a/dtc.h
+++ b/dtc.h
@@ -289,6 +289,7 @@ struct symbol *build_exportsym(const char *name, const char *ref, cell_t phandle
struct srcpos *srcpos);
struct symbol *chain_symbol(struct symbol *first, struct symbol *list);
struct symbol *reverse_symbol(struct symbol *list);
+void add_symbol(struct symbol **list, struct symbol *new);
struct node *build_node(struct property *proplist, struct node *children,
struct symbol *exportsymlist, struct srcpos *srcpos);
diff --git a/fdtdump.c b/fdtdump.c
index 370db848..f39f78c0 100644
--- a/fdtdump.c
+++ b/fdtdump.c
@@ -84,7 +84,7 @@ static void dump_blob(void *blob, bool debug, int dump_unknown)
const char *p_strings = (const char *)blob + off_str;
uint32_t version = fdt32_to_cpu(bph->version);
uint32_t totalsize = fdt32_to_cpu(bph->totalsize);
- uint32_t tag, offset;
+ uint32_t tag, offset, val32;
const char *p, *d, *s, *t;
const char *last_prop_name = NULL;
int depth, sz, shift;
@@ -206,6 +206,15 @@ static void dump_blob(void *blob, bool debug, int dump_unknown)
continue;
}
+ if (tag == FDT_EXPORT_SYM) {
+ p = get_structured_tag_data(tag, p, &d, &sz);
+ val32 = fdt32_to_cpu(GET_CELL(d));
+ s = d;
+ printf("%*s// [FDT_EXPORT_SYM] '%s' -> phandle 0x%08"PRIx32"\n",
+ depth * shift, "", s, val32);
+ continue;
+ }
+
if ((tag & FDT_TAG_STRUCTURED) && (tag & FDT_TAG_SKIP_SAFE)) {
p = get_structured_tag_data(tag, p, &d, &sz);
diff --git a/flattree.c b/flattree.c
index ac6bcd8f..84acbb35 100644
--- a/flattree.c
+++ b/flattree.c
@@ -16,6 +16,7 @@
#define FTF_DTFLAGS 0x80
#define FTF_LCVERSW 0x100
#define FTF_PROPDATA_PHANDLE 0x200
+#define FTF_EXPORT_IMPORT_SYM 0x400
static struct version_info {
int version;
@@ -39,7 +40,7 @@ static struct version_info {
FTF_LCVERSW},
{20, 18, 18, FDT_V20_SIZE,
FTF_BOOTCPUID|FTF_STRTABSIZE|FTF_STRUCTSIZE|FTF_NOPS|FTF_DTFLAGS|
- FTF_LCVERSW|FTF_PROPDATA_PHANDLE},
+ FTF_LCVERSW|FTF_PROPDATA_PHANDLE|FTF_EXPORT_IMPORT_SYM},
};
struct emitter {
@@ -52,6 +53,7 @@ struct emitter {
void (*property)(void *, struct label *labels);
void (*propdata_phandle)(void *);
void (*propdata_phandle_ref)(void *);
+ void (*export_sym)(void *);
};
static void bin_emit_cell(void *e, cell_t val)
@@ -111,6 +113,11 @@ static void bin_emit_propdata_phandle_ref(void *e)
bin_emit_cell(e, FDT_PROPDATA_PHANDLE_REF);
}
+static void bin_emit_export_sym(void *e)
+{
+ bin_emit_cell(e, FDT_EXPORT_SYM);
+}
+
static struct emitter bin_emitter = {
.cell = bin_emit_cell,
.string = bin_emit_string,
@@ -121,6 +128,7 @@ static struct emitter bin_emitter = {
.property = bin_emit_property,
.propdata_phandle = bin_emit_propdata_phandle,
.propdata_phandle_ref = bin_emit_propdata_phandle_ref,
+ .export_sym = bin_emit_export_sym,
};
static void emit_label(FILE *f, const char *prefix, const char *label)
@@ -248,6 +256,14 @@ static void asm_emit_propdata_phandle_ref(void *e)
asm_emit_cell(e, FDT_PROPDATA_PHANDLE_REF);
}
+static void asm_emit_export_sym(void *e)
+{
+ FILE *f = e;
+
+ fprintf(f, "\t/* FDT_EXPORT_SYM */\n");
+ asm_emit_cell(e, FDT_EXPORT_SYM);
+}
+
static struct emitter asm_emitter = {
.cell = asm_emit_cell,
.string = asm_emit_string,
@@ -258,6 +274,7 @@ static struct emitter asm_emitter = {
.property = asm_emit_property,
.propdata_phandle = asm_emit_propdata_phandle,
.propdata_phandle_ref = asm_emit_propdata_phandle_ref,
+ .export_sym = asm_emit_export_sym,
};
static int stringtable_insert(struct data *d, const char *str)
@@ -279,6 +296,7 @@ static void flatten_tree(struct node *tree, struct emitter *emit,
void *etarget, struct data *strbuf,
struct version_info *vi)
{
+ struct symbol *exportsym;
struct property *prop;
struct node *child;
bool seen_name_prop = false;
@@ -296,6 +314,19 @@ static void flatten_tree(struct node *tree, struct emitter *emit,
emit->align(etarget, sizeof(cell_t));
+ if (vi->flags & FTF_EXPORT_IMPORT_SYM) {
+ for_each_symbol(tree->exportsymlist, exportsym) {
+ if (exportsym->is_local) {
+ emit->export_sym(etarget);
+ emit->cell(etarget, sizeof(cell_t) +
+ strlen(exportsym->name) + 1);
+ emit->cell(etarget, exportsym->phandle);
+ emit->string(etarget, exportsym->name, 0);
+ emit->align(etarget, sizeof(cell_t));
+ }
+ }
+ }
+
for_each_property(tree, prop) {
int nameoff;
@@ -747,6 +778,7 @@ static uint32_t flat_read_tag(struct inbuf *inb)
case FDT_END:
case FDT_PROPDATA_PHANDLE:
case FDT_PROPDATA_PHANDLE_REF:
+ case FDT_EXPORT_SYM:
return tag;
default:
break;
@@ -913,8 +945,10 @@ static struct node *unflatten_tree(struct inbuf *dtbuf,
node->name = xstrdup(flatname);
do {
+ struct symbol *exportsym;
struct node *child;
struct marker *m;
+ cell_t phandle;
val = flat_read_tag(dtbuf);
switch (val) {
@@ -997,6 +1031,21 @@ static struct node *unflatten_tree(struct inbuf *dtbuf,
prop->val = data_append_markers(prop->val, m);
break;
+ case FDT_EXPORT_SYM:
+ if (!(flags & FTF_EXPORT_IMPORT_SYM))
+ die("FDT_EXPORT_SYM tag found in flat tree"
+ " version <20\n");
+ len = flat_read_word(dtbuf);
+ flat_read_subbuf(dtbuf, &subbuf, len);
+ flat_realign(dtbuf, sizeof(uint32_t));
+
+ phandle = flat_read_word(&subbuf);
+ str = flat_read_string_norealign(&subbuf);
+ exportsym = build_exportsym(str, NULL, phandle, NULL);
+ exportsym->is_local = true;
+ add_symbol(&node->exportsymlist, exportsym);
+ break;
+
default:
die("Invalid opcode word %08x in device tree blob\n",
val);
@@ -1120,7 +1169,7 @@ struct dt_info *dt_from_blob(const char *fname)
flags |= FTF_DTFLAGS | FTF_LCVERSW;
if (version >= 20) {
- flags |= FTF_PROPDATA_PHANDLE;
+ flags |= FTF_PROPDATA_PHANDLE | FTF_EXPORT_IMPORT_SYM;
dtsflags |= fdt32_to_cpu(fdt->dt_flags) & FDT_FLAG_ADDON ?
DTSF_ADDON : 0;
}
diff --git a/libfdt/fdt.c b/libfdt/fdt.c
index d23b886b..92732caf 100644
--- a/libfdt/fdt.c
+++ b/libfdt/fdt.c
@@ -308,6 +308,33 @@ static uint32_t fdt_next_tag_all(const void *fdt, int startoffset, int *nextoffs
offset = sum;
break;
+ case FDT_EXPORT_SYM:
+ tmp_offset = fdt_get_structured_tag_data(tag, fdt, offset, &len);
+ if (tmp_offset < 0)
+ return FDT_END; /* premature end */
+
+ sum = tmp_offset + len;
+
+ /* Check phandle */
+ tmp32p = fdt_offset_ptr(fdt, tmp_offset, sizeof(*tmp32p));
+ if (!can_assume(VALID_DTB) && !tmp32p)
+ return FDT_END; /* premature end */
+ tmp_offset += sizeof(fdt32_t);
+
+ /* Check name */
+ do {
+ p = fdt_offset_ptr(fdt, tmp_offset++, 1);
+ } while (p && (*p != '\0'));
+ if (!can_assume(VALID_DTB) && !p)
+ return FDT_END; /* premature end */
+
+ if (!can_assume(VALID_INPUT) && (uint32_t)tmp_offset > sum)
+ return FDT_END; /* premature end */
+
+ /* Skip the whole data bloc */
+ offset = sum;
+ break;
+
default:
if (!(tag & FDT_TAG_STRUCTURED) || !(tag & FDT_TAG_SKIP_SAFE))
return FDT_END;
@@ -338,6 +365,7 @@ static bool fdt_tag_is_unknown(uint32_t tag)
case FDT_END:
case FDT_PROPDATA_PHANDLE:
case FDT_PROPDATA_PHANDLE_REF:
+ case FDT_EXPORT_SYM:
return false;
default:
break;
@@ -393,6 +421,7 @@ bool fdt_tag_filter_skip_metadata(void *data, uint32_t tag)
switch (tag) {
case FDT_PROPDATA_PHANDLE:
case FDT_PROPDATA_PHANDLE_REF:
+ case FDT_EXPORT_SYM:
return true;
default:
diff --git a/libfdt/fdt.h b/libfdt/fdt.h
index 255e9447..c6a5b3a1 100644
--- a/libfdt/fdt.h
+++ b/libfdt/fdt.h
@@ -110,6 +110,19 @@ struct fdt_property {
*/
#define FDT_PROPDATA_PHANDLE_REF FDT_TAG_CAN_SKIP(FDT_TAG_DATA_VARLEN, 0x1001)
+/*
+ * Export symbol tag (meta-data). varlen: name, phandle
+ *
+ * It defines an exported symbol. It can be present in a node bloc meaning that
+ * a symbol is exported at this node level. The node pointed to by this symbol
+ * is a local node and identified by a phandle value.
+ *
+ * phandle: phandle value identifying the node referenced by this symbol
+ * name: export symbol name, i.e. the name used to reference this exported
+ * symbol.
+ */
+#define FDT_EXPORT_SYM FDT_TAG_CAN_SKIP(FDT_TAG_DATA_VARLEN, 0x2000)
+
#define FDT_V1_SIZE (7*sizeof(fdt32_t))
#define FDT_V2_SIZE (FDT_V1_SIZE + sizeof(fdt32_t))
#define FDT_V3_SIZE (FDT_V2_SIZE + sizeof(fdt32_t))
diff --git a/libfdt/fdt_check.c b/libfdt/fdt_check.c
index 96dd7dc0..101efd73 100644
--- a/libfdt/fdt_check.c
+++ b/libfdt/fdt_check.c
@@ -21,6 +21,7 @@ int fdt_check_full(const void *fdt, size_t bufsize)
const char *propname;
int proplen;
bool expect_end = false;
+ bool subnode_found = false;
const fdt32_t *pfdt32;
const char *str;
uint32_t val;
@@ -63,6 +64,7 @@ int fdt_check_full(const void *fdt, size_t bufsize)
case FDT_BEGIN_NODE:
prop = NULL;
+ subnode_found = false;
depth++;
if (depth > INT_MAX)
return -FDT_ERR_BADSTRUCTURE;
@@ -83,6 +85,7 @@ int fdt_check_full(const void *fdt, size_t bufsize)
case FDT_END_NODE:
prop = NULL;
+ subnode_found = true;
if (depth == 0)
return -FDT_ERR_BADSTRUCTURE;
depth--;
@@ -163,6 +166,43 @@ int fdt_check_full(const void *fdt, size_t bufsize)
return -FDT_ERR_BADVALUE;
break;
+ case FDT_EXPORT_SYM:
+ /* Export symbols must be in a node */
+ if (depth == 0)
+ return -FDT_ERR_BADSTRUCTURE;
+
+ /* and before any properties or subnodes */
+ if (prop || subnode_found)
+ return -FDT_ERR_BADSTRUCTURE;
+
+ d = fdt_get_structured_tag_data(tag, fdt,
+ offset + FDT_TAGSIZE, NULL);
+ if (d < 0)
+ return d;
+
+ /* Retrieve the phandle value */
+ pfdt32 = fdt_offset_ptr(fdt, d, sizeof(*pfdt32));
+ if (!pfdt32)
+ return -FDT_ERR_BADSTRUCTURE;
+ val = fdt32_ld(pfdt32);
+
+ /* Check the phandle value */
+ tmp = fdt_node_offset_by_phandle(fdt, val);
+ if (tmp < 0)
+ return tmp;
+
+ /* Retrieve the export symbol name */
+ d += sizeof(fdt32_t);
+ str = fdt_offset_ptr(fdt, d, 1);
+ if (!str)
+ return -FDT_ERR_BADSTRUCTURE;
+
+ /* Check that the name is not an empty string */
+ if (!strlen(str))
+ return -FDT_ERR_BADVALUE;
+
+ break;
+
default:
return -FDT_ERR_INTERNAL;
}
diff --git a/libfdt/libfdt_internal.h b/libfdt/libfdt_internal.h
index 47dfc0d4..7ef1d995 100644
--- a/libfdt/libfdt_internal.h
+++ b/libfdt/libfdt_internal.h
@@ -9,6 +9,7 @@
#define FDT_ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1))
#define FDT_TAGALIGN(x) (FDT_ALIGN((x), FDT_TAGSIZE))
+#define FDT_CELLALIGN(x) (FDT_ALIGN((x), FDT_CELLSIZE))
int32_t fdt_ro_probe_(const void *fdt);
#define FDT_RO_PROBE(fdt) \
diff --git a/livetree.c b/livetree.c
index 8c94e81f..35cf1de5 100644
--- a/livetree.c
+++ b/livetree.c
@@ -74,7 +74,7 @@ struct symbol *reverse_symbol(struct symbol *list)
return head;
}
-static void add_symbol(struct symbol **list, struct symbol *new)
+void add_symbol(struct symbol **list, struct symbol *new)
{
struct symbol **s;
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 22/74] tests: metadata: Add export symbols with local references tests
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (20 preceding siblings ...)
2026-08-26 9:48 ` [RFC PATCH v2 21/74] Add support for FDT_EXPORT_SYM dtb tag Herve Codina
@ 2026-08-26 9:48 ` Herve Codina
2026-08-26 9:48 ` [RFC PATCH v2 23/74] dtc: Add support for export symbols sorting Herve Codina
` (51 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:48 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
Add tests related to export symbols using local phandle references
(FDT_EXPORT_SYM dtb tag).
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
.../metadata_exportsyms_local.dtb.dts.expect | 26 +++++++++++++++++
tests/metadata_exportsyms_local.dtb.expect | 20 +++++++++++++
tests/metadata_exportsyms_local.dts | 28 +++++++++++++++++++
.../metadata_exportsyms_local.dts.dts.expect | 26 +++++++++++++++++
tests/run_tests.sh | 2 +-
5 files changed, 101 insertions(+), 1 deletion(-)
create mode 100644 tests/metadata_exportsyms_local.dtb.dts.expect
create mode 100644 tests/metadata_exportsyms_local.dtb.expect
create mode 100644 tests/metadata_exportsyms_local.dts
create mode 100644 tests/metadata_exportsyms_local.dts.dts.expect
diff --git a/tests/metadata_exportsyms_local.dtb.dts.expect b/tests/metadata_exportsyms_local.dtb.dts.expect
new file mode 100644
index 00000000..ac3d5b3a
--- /dev/null
+++ b/tests/metadata_exportsyms_local.dtb.dts.expect
@@ -0,0 +1,26 @@
+/dts-v1/;
+
+/ {
+
+ node-a {
+ };
+
+ node-b {
+ phandle = <0x03>;
+ };
+
+ node-c {
+
+ sub-node {
+ phandle = <0x02>;
+ };
+ };
+
+ node-d {
+ phandle = <0x01>;
+
+ /export/ node_d: &{/node-d};
+ /export/ subnode_c: &{/node-c/sub-node};
+ /export/ node_b: &{/node-b};
+ };
+};
diff --git a/tests/metadata_exportsyms_local.dtb.expect b/tests/metadata_exportsyms_local.dtb.expect
new file mode 100644
index 00000000..e09437f8
--- /dev/null
+++ b/tests/metadata_exportsyms_local.dtb.expect
@@ -0,0 +1,20 @@
+/dts-v1/;
+
+/ {
+ node-a {
+ };
+ node-b {
+ phandle = <0x00000003>;
+ };
+ node-c {
+ sub-node {
+ phandle = <0x00000002>;
+ };
+ };
+ node-d {
+ // [FDT_EXPORT_SYM] 'node_d' -> phandle 0x00000001
+ // [FDT_EXPORT_SYM] 'subnode_c' -> phandle 0x00000002
+ // [FDT_EXPORT_SYM] 'node_b' -> phandle 0x00000003
+ phandle = <0x00000001>;
+ };
+};
diff --git a/tests/metadata_exportsyms_local.dts b/tests/metadata_exportsyms_local.dts
new file mode 100644
index 00000000..10e6e47a
--- /dev/null
+++ b/tests/metadata_exportsyms_local.dts
@@ -0,0 +1,28 @@
+// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
+/*
+ * Copyright (C) 2026 Bootlin
+ */
+
+/dts-v1/;
+
+/ {
+ node-a {
+ };
+
+ node_b: node-b {
+ };
+
+ node-c {
+ sub_node: sub-node {
+ };
+ };
+
+ node_d: node-d {
+ /export/ node_d: &node_d;
+ /export/ subnode_c: &sub_node;
+ };
+};
+
+&node_d {
+ /export/ node_b: &node_b;
+};
diff --git a/tests/metadata_exportsyms_local.dts.dts.expect b/tests/metadata_exportsyms_local.dts.dts.expect
new file mode 100644
index 00000000..4723e7e4
--- /dev/null
+++ b/tests/metadata_exportsyms_local.dts.dts.expect
@@ -0,0 +1,26 @@
+/dts-v1/;
+
+/ {
+
+ node-a {
+ };
+
+ node_b: node-b {
+ phandle = <0x03>;
+ };
+
+ node-c {
+
+ sub_node: sub-node {
+ phandle = <0x02>;
+ };
+ };
+
+ node_d: node-d {
+ phandle = <0x01>;
+
+ /export/ node_d: &node_d;
+ /export/ subnode_c: &sub_node;
+ /export/ node_b: &node_b;
+ };
+};
diff --git a/tests/run_tests.sh b/tests/run_tests.sh
index f119acc0..06c27b71 100755
--- a/tests/run_tests.sh
+++ b/tests/run_tests.sh
@@ -1249,7 +1249,7 @@ fdtoverlay_tests() {
metadata_tests() {
for dt in metadata_propdata_phandle_local metadata_propdata_phandle_ref \
- metadata_addon_base; do
+ metadata_addon_base metadata_exportsyms_local; do
run_dtc_test -I dts -O dts -o $dt.dts.dts "$SRCDIR/$dt.dts"
base_run_test check_diff $dt.dts.dts "$SRCDIR/$dt.dts.dts.expect"
run_dtc_test -I dts -O dtb -o $dt.dtb "$SRCDIR/$dt.dts"
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 23/74] dtc: Add support for export symbols sorting
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (21 preceding siblings ...)
2026-08-26 9:48 ` [RFC PATCH v2 22/74] tests: metadata: Add export symbols with local references tests Herve Codina
@ 2026-08-26 9:48 ` Herve Codina
2026-08-26 9:48 ` [RFC PATCH v2 24/74] tests: metadata: Add a test " Herve Codina
` (50 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:48 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
dtc can sort items when the command line --sort option is set.
Add support for export symbols sorting when this option is used.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
livetree.c | 37 +++++++++++++++++++++++++++++++++++++
1 file changed, 37 insertions(+)
diff --git a/livetree.c b/livetree.c
index 35cf1de5..fe0f300b 100644
--- a/livetree.c
+++ b/livetree.c
@@ -1028,6 +1028,42 @@ static void sort_properties(struct node *node)
free(tbl);
}
+static int cmp_symbol(const void *ax, const void *bx)
+{
+ const struct symbol *a, *b;
+
+ a = *((const struct symbol * const *)ax);
+ b = *((const struct symbol * const *)bx);
+
+ return strcmp(a->name, b->name);
+}
+
+static void sort_exportsyms(struct node *node)
+{
+ int n = 0, i = 0;
+ struct symbol *symbol, **tbl;
+
+ for_each_symbol(node->exportsymlist, symbol)
+ n++;
+
+ if (n == 0)
+ return;
+
+ tbl = xmalloc(n * sizeof(*tbl));
+
+ for_each_symbol(node->exportsymlist, symbol)
+ tbl[i++] = symbol;
+
+ qsort(tbl, n, sizeof(*tbl), cmp_symbol);
+
+ node->exportsymlist = tbl[0];
+ for (i = 0; i < (n-1); i++)
+ tbl[i]->next = tbl[i+1];
+ tbl[n-1]->next = NULL;
+
+ free(tbl);
+}
+
static int cmp_subnode(const void *ax, const void *bx)
{
const struct node *a, *b;
@@ -1069,6 +1105,7 @@ static void sort_node(struct node *node)
struct node *c;
sort_properties(node);
+ sort_exportsyms(node);
sort_subnodes(node);
for_each_child_withdel(node, c)
sort_node(c);
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 24/74] tests: metadata: Add a test for export symbols sorting
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (22 preceding siblings ...)
2026-08-26 9:48 ` [RFC PATCH v2 23/74] dtc: Add support for export symbols sorting Herve Codina
@ 2026-08-26 9:48 ` Herve Codina
2026-08-26 9:48 ` [RFC PATCH v2 25/74] Add support for FDT_EXPORT_SYM_REF dtb tag Herve Codina
` (49 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:48 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
dtc is able to sort the export symbols list when the --sort option is
used.
Add a test for this feature.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
tests/metadata_sort.dtb.dts.expect | 18 ++++++++++++++++++
tests/metadata_sort.dtb.expect | 15 +++++++++++++++
tests/metadata_sort.dts | 21 +++++++++++++++++++++
tests/run_tests.sh | 9 +++++++++
4 files changed, 63 insertions(+)
create mode 100644 tests/metadata_sort.dtb.dts.expect
create mode 100644 tests/metadata_sort.dtb.expect
create mode 100644 tests/metadata_sort.dts
diff --git a/tests/metadata_sort.dtb.dts.expect b/tests/metadata_sort.dtb.dts.expect
new file mode 100644
index 00000000..f07a42b2
--- /dev/null
+++ b/tests/metadata_sort.dtb.dts.expect
@@ -0,0 +1,18 @@
+/dts-v1/;
+/addon/;
+
+/ {
+
+ node-a {
+ prop_a = <0x1c8>;
+ prop_b = <0x7b>;
+
+ /export/ a: &{/node-b};
+ /export/ ba: &{/node-b};
+ /export/ bb: &{/node-b};
+ };
+
+ node-b {
+ phandle = <0x01>;
+ };
+};
diff --git a/tests/metadata_sort.dtb.expect b/tests/metadata_sort.dtb.expect
new file mode 100644
index 00000000..ed10b59a
--- /dev/null
+++ b/tests/metadata_sort.dtb.expect
@@ -0,0 +1,15 @@
+/dts-v1/;
+/addon/;
+
+/ {
+ node-a {
+ // [FDT_EXPORT_SYM] 'a' -> phandle 0x00000001
+ // [FDT_EXPORT_SYM] 'ba' -> phandle 0x00000001
+ // [FDT_EXPORT_SYM] 'bb' -> phandle 0x00000001
+ prop_a = <0x000001c8>;
+ prop_b = <0x0000007b>;
+ };
+ node-b {
+ phandle = <0x00000001>;
+ };
+};
diff --git a/tests/metadata_sort.dts b/tests/metadata_sort.dts
new file mode 100644
index 00000000..7d1cca7e
--- /dev/null
+++ b/tests/metadata_sort.dts
@@ -0,0 +1,21 @@
+// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
+/*
+ * Copyright (C) 2026 Bootlin
+ */
+
+/dts-v1/;
+/addon/;
+
+/ {
+ node_b: node-b {
+ };
+
+ node-a {
+ prop_b = <123>;
+ prop_a = <456>;
+
+ /export/ ba: &node_b;
+ /export/ bb: &node_b;
+ /export/ a: &node_b;
+ };
+};
diff --git a/tests/run_tests.sh b/tests/run_tests.sh
index 06c27b71..0c5f2849 100755
--- a/tests/run_tests.sh
+++ b/tests/run_tests.sh
@@ -1266,6 +1266,15 @@ metadata_tests() {
sed -i '/^\/\/ /d' $dt.dtb.dts.dtb.out
base_run_test check_diff $dt.dtb.dts.dtb.out "$SRCDIR/$dt.dtb.expect"
done
+
+ run_dtc_test -I dts -O dtb -s -o metadata_sort.dtb "$SRCDIR/metadata_sort.dts"
+ base_run_test wrap_fdtdump metadata_sort.dtb metadata_sort.dtb.out
+ # Remove unneeded comments
+ sed -i '/^\/\/ /d' metadata_sort.dtb.out
+ base_run_test check_diff metadata_sort.dtb.out "$SRCDIR/metadata_sort.dtb.expect"
+ run_dtc_test -I dtb -O dts -o metadata_sort.dtb.dts metadata_sort.dtb
+ base_run_test check_diff metadata_sort.dtb.dts "$SRCDIR/metadata_sort.dtb.dts.expect"
+ run_test check_full metadata_sort.dtb
}
pylibfdt_tests () {
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 25/74] Add support for FDT_EXPORT_SYM_REF dtb tag
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (23 preceding siblings ...)
2026-08-26 9:48 ` [RFC PATCH v2 24/74] tests: metadata: Add a test " Herve Codina
@ 2026-08-26 9:48 ` Herve Codina
2026-08-26 9:48 ` [RFC PATCH v2 26/74] tests: metadata: Add export symbols with external references tests Herve Codina
` (48 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:48 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
The FDT_EXPORT_SYM_REF dtb tag is similar to the FDT_EXPORT_SYM tag
except that it identifies a reference to an external phandle. The node
referenced by the phandle is not present in the device-tree blob.
The FDT_EXPORT_SYM_REF dtb tag is a meta-data tag defining an exported
symbol. It can be present in a node bloc meaning that a symbol is
exported at this node level. The node pointed to by this symbol is not a
local node (i.e. the node is not present in the device-tree blob.). This
tag can be available only in overlay or addon device-tree blobs. The
symbol has to be resolved when the device-tree blob is applied on top of
a base device-tree.
The tag is followed by the length of the data part (structured tag), two
values and a possible alignment padding:
- data part length (32bit): strlen(label) + 1 + strlen(name) + 1
- label (string including \0):
The label to use to resolve this symbol. This label is the
reference to the external phandle.
- name (string including \0)
The export symbol name. I.e. the name used to reference this
exported symbol.
- padding:
Padding (0x00) added to have the next value aligned on 32bit.
Example:
FDT_EXPORT_SYM_REF 11 'foo_a' 'foo1' 0x00
This means that 'foo1' is an exported symbol and the node referenced
by this symbol is external to the dtb (unresolved symbol). This
external node is referenced by the "foo_a" label.
This is what is encoded in the dtb when the related dts has the
following exported symbol defined:
/export/ foo1: &foo_a;
with 'foo_a' a reference to a non local node.
If several non local symbols are exported at a given node level, several
FDT_EXPORT_SYM_REF are present. Each of them defining one symbol.
For instance, exporting 'foo1' pointing the node referenced by 'foo_a'
and exporting 'bar' pointing to the node referenced by 'bar_b' leads to
the following sequence:
FDT_EXPORT_SYM_REF 11 'foo_a' 'foo1' 0x00
FDT_EXPORT_SYM_REF 10 'bar_b' 'bar' 0x00 0x00
Add support for this new dtb tag.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
fdtdump.c | 9 +++++++++
flattree.c | 44 ++++++++++++++++++++++++++++++++++++++++++++
libfdt/fdt.c | 30 ++++++++++++++++++++++++++++++
libfdt/fdt.h | 17 +++++++++++++++++
libfdt/fdt_check.c | 38 +++++++++++++++++++++++++++++++++++++-
5 files changed, 137 insertions(+), 1 deletion(-)
diff --git a/fdtdump.c b/fdtdump.c
index f39f78c0..db5b53dc 100644
--- a/fdtdump.c
+++ b/fdtdump.c
@@ -215,6 +215,15 @@ static void dump_blob(void *blob, bool debug, int dump_unknown)
continue;
}
+ if (tag == FDT_EXPORT_SYM_REF) {
+ p = get_structured_tag_data(tag, p, &d, &sz);
+ t = d;
+ s = d + strlen(t) + 1;
+ printf("%*s// [FDT_EXPORT_SYM_REF] '%s' -> '%s'\n", depth * shift, "",
+ s, t);
+ continue;
+ }
+
if ((tag & FDT_TAG_STRUCTURED) && (tag & FDT_TAG_SKIP_SAFE)) {
p = get_structured_tag_data(tag, p, &d, &sz);
diff --git a/flattree.c b/flattree.c
index 84acbb35..4e4727e9 100644
--- a/flattree.c
+++ b/flattree.c
@@ -54,6 +54,7 @@ struct emitter {
void (*propdata_phandle)(void *);
void (*propdata_phandle_ref)(void *);
void (*export_sym)(void *);
+ void (*export_sym_ref)(void *);
};
static void bin_emit_cell(void *e, cell_t val)
@@ -118,6 +119,11 @@ static void bin_emit_export_sym(void *e)
bin_emit_cell(e, FDT_EXPORT_SYM);
}
+static void bin_emit_export_sym_ref(void *e)
+{
+ bin_emit_cell(e, FDT_EXPORT_SYM_REF);
+}
+
static struct emitter bin_emitter = {
.cell = bin_emit_cell,
.string = bin_emit_string,
@@ -129,6 +135,7 @@ static struct emitter bin_emitter = {
.propdata_phandle = bin_emit_propdata_phandle,
.propdata_phandle_ref = bin_emit_propdata_phandle_ref,
.export_sym = bin_emit_export_sym,
+ .export_sym_ref = bin_emit_export_sym_ref,
};
static void emit_label(FILE *f, const char *prefix, const char *label)
@@ -264,6 +271,14 @@ static void asm_emit_export_sym(void *e)
asm_emit_cell(e, FDT_EXPORT_SYM);
}
+static void asm_emit_export_sym_ref(void *e)
+{
+ FILE *f = e;
+
+ fprintf(f, "\t/* FDT_EXPORT_SYM_REF */\n");
+ asm_emit_cell(e, FDT_EXPORT_SYM_REF);
+}
+
static struct emitter asm_emitter = {
.cell = asm_emit_cell,
.string = asm_emit_string,
@@ -275,6 +290,7 @@ static struct emitter asm_emitter = {
.propdata_phandle = asm_emit_propdata_phandle,
.propdata_phandle_ref = asm_emit_propdata_phandle_ref,
.export_sym = asm_emit_export_sym,
+ .export_sym_ref = asm_emit_export_sym_ref,
};
static int stringtable_insert(struct data *d, const char *str)
@@ -323,7 +339,19 @@ static void flatten_tree(struct node *tree, struct emitter *emit,
emit->cell(etarget, exportsym->phandle);
emit->string(etarget, exportsym->name, 0);
emit->align(etarget, sizeof(cell_t));
+ continue;
}
+
+ if (exportsym->ref[0] == '/')
+ die("Export symbol uses a non local reference by path (%s)\n",
+ exportsym->ref);
+
+ emit->export_sym_ref(etarget);
+ emit->cell(etarget, strlen(exportsym->ref) + 1 +
+ strlen(exportsym->name) + 1);
+ emit->string(etarget, exportsym->ref, 0);
+ emit->string(etarget, exportsym->name, 0);
+ emit->align(etarget, sizeof(cell_t));
}
}
@@ -779,6 +807,7 @@ static uint32_t flat_read_tag(struct inbuf *inb)
case FDT_PROPDATA_PHANDLE:
case FDT_PROPDATA_PHANDLE_REF:
case FDT_EXPORT_SYM:
+ case FDT_EXPORT_SYM_REF:
return tag;
default:
break;
@@ -933,6 +962,7 @@ static struct node *unflatten_tree(struct inbuf *dtbuf,
uint32_t offset;
uint32_t len;
const char *str;
+ const char *str2;
node = build_node(NULL, NULL, NULL, NULL);
@@ -1046,6 +1076,20 @@ static struct node *unflatten_tree(struct inbuf *dtbuf,
add_symbol(&node->exportsymlist, exportsym);
break;
+ case FDT_EXPORT_SYM_REF:
+ if (!(flags & FTF_EXPORT_IMPORT_SYM))
+ die("FDT_EXPORT_SYM_REF tag found in flat tree"
+ " version <20\n");
+ len = flat_read_word(dtbuf);
+ flat_read_subbuf(dtbuf, &subbuf, len);
+ flat_realign(dtbuf, sizeof(uint32_t));
+
+ str = flat_read_string_norealign(&subbuf); /* Ref */
+ str2 = flat_read_string_norealign(&subbuf); /* Name */
+ exportsym = build_exportsym(str2, str, 0, NULL);
+ add_symbol(&node->exportsymlist, exportsym);
+ break;
+
default:
die("Invalid opcode word %08x in device tree blob\n",
val);
diff --git a/libfdt/fdt.c b/libfdt/fdt.c
index 92732caf..9bab9149 100644
--- a/libfdt/fdt.c
+++ b/libfdt/fdt.c
@@ -335,6 +335,34 @@ static uint32_t fdt_next_tag_all(const void *fdt, int startoffset, int *nextoffs
offset = sum;
break;
+ case FDT_EXPORT_SYM_REF:
+ tmp_offset = fdt_get_structured_tag_data(tag, fdt, offset, &len);
+ if (tmp_offset < 0)
+ return FDT_END; /* premature end */
+
+ sum = tmp_offset + len;
+
+ /* Check ref */
+ do {
+ p = fdt_offset_ptr(fdt, tmp_offset++, 1);
+ } while (p && (*p != '\0'));
+ if (!can_assume(VALID_DTB) && !p)
+ return FDT_END; /* premature end */
+
+ /* Check name */
+ do {
+ p = fdt_offset_ptr(fdt, tmp_offset++, 1);
+ } while (p && (*p != '\0'));
+ if (!can_assume(VALID_DTB) && !p)
+ return FDT_END; /* premature end */
+
+ if (!can_assume(VALID_INPUT) && (uint32_t)tmp_offset > sum)
+ return FDT_END; /* premature end */
+
+ /* Skip the whole data bloc */
+ offset = sum;
+ break;
+
default:
if (!(tag & FDT_TAG_STRUCTURED) || !(tag & FDT_TAG_SKIP_SAFE))
return FDT_END;
@@ -366,6 +394,7 @@ static bool fdt_tag_is_unknown(uint32_t tag)
case FDT_PROPDATA_PHANDLE:
case FDT_PROPDATA_PHANDLE_REF:
case FDT_EXPORT_SYM:
+ case FDT_EXPORT_SYM_REF:
return false;
default:
break;
@@ -422,6 +451,7 @@ bool fdt_tag_filter_skip_metadata(void *data, uint32_t tag)
case FDT_PROPDATA_PHANDLE:
case FDT_PROPDATA_PHANDLE_REF:
case FDT_EXPORT_SYM:
+ case FDT_EXPORT_SYM_REF:
return true;
default:
diff --git a/libfdt/fdt.h b/libfdt/fdt.h
index c6a5b3a1..624491a0 100644
--- a/libfdt/fdt.h
+++ b/libfdt/fdt.h
@@ -123,6 +123,23 @@ struct fdt_property {
*/
#define FDT_EXPORT_SYM FDT_TAG_CAN_SKIP(FDT_TAG_DATA_VARLEN, 0x2000)
+/*
+ * Export external symbol tag (meta-data). varlen: label, name
+ *
+ * It defines an exported symbol. It can be present in a node bloc meaning that
+ * a symbol is exported at this node level. The node pointed to by this symbol
+ * is not a local node (i.e. the node is not present in the device-tree blob.).
+ * This tag can be available only in overlay or addon device-tree blobs. The
+ * symbol has to be resolved when the device-tree blob is applied on top of a
+ * base device-tree.
+ *
+ * label: Label to use to resolve this symbol. This label is the reference to
+ * the external phandle.
+ * name: export symbol name, i.e. the name used to reference this exported
+ * symbol.
+ */
+#define FDT_EXPORT_SYM_REF FDT_TAG_CAN_SKIP(FDT_TAG_DATA_VARLEN, 0x2001)
+
#define FDT_V1_SIZE (7*sizeof(fdt32_t))
#define FDT_V2_SIZE (FDT_V1_SIZE + sizeof(fdt32_t))
#define FDT_V3_SIZE (FDT_V2_SIZE + sizeof(fdt32_t))
diff --git a/libfdt/fdt_check.c b/libfdt/fdt_check.c
index 101efd73..19b35c8b 100644
--- a/libfdt/fdt_check.c
+++ b/libfdt/fdt_check.c
@@ -25,6 +25,7 @@ int fdt_check_full(const void *fdt, size_t bufsize)
const fdt32_t *pfdt32;
const char *str;
uint32_t val;
+ int len;
if (can_assume(PERFECT))
return 0;
@@ -72,7 +73,6 @@ int fdt_check_full(const void *fdt, size_t bufsize)
/* The root node must have an empty name */
if (depth == 1) {
const char *name;
- int len;
name = fdt_get_name(fdt, offset, &len);
if (!name)
@@ -203,6 +203,42 @@ int fdt_check_full(const void *fdt, size_t bufsize)
break;
+ case FDT_EXPORT_SYM_REF:
+ /* Export symbols must be in a node */
+ if (depth == 0)
+ return -FDT_ERR_BADSTRUCTURE;
+
+ /* and before any properties or subnodes */
+ if (prop || subnode_found)
+ return -FDT_ERR_BADSTRUCTURE;
+
+ d = fdt_get_structured_tag_data(tag, fdt,
+ offset + FDT_TAGSIZE, NULL);
+ if (d < 0)
+ return d;
+
+ /* Retrieve the reference */
+ str = fdt_offset_ptr(fdt, d, 1);
+ if (!str)
+ return -FDT_ERR_BADSTRUCTURE;
+
+ /* Check that the reference is not an empty string */
+ len = strlen(str);
+ if (!len)
+ return -FDT_ERR_BADVALUE;
+
+ /* Retrieve the export symbol name */
+ d += len + 1;
+ str = fdt_offset_ptr(fdt, d, 1);
+ if (!str)
+ return -FDT_ERR_BADSTRUCTURE;
+
+ /* Check that the name is not an empty string */
+ if (!strlen(str))
+ return -FDT_ERR_BADVALUE;
+
+ break;
+
default:
return -FDT_ERR_INTERNAL;
}
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 26/74] tests: metadata: Add export symbols with external references tests
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (24 preceding siblings ...)
2026-08-26 9:48 ` [RFC PATCH v2 25/74] Add support for FDT_EXPORT_SYM_REF dtb tag Herve Codina
@ 2026-08-26 9:48 ` Herve Codina
2026-08-26 9:48 ` [RFC PATCH v2 27/74] dtc: Introduce import symbols Herve Codina
` (47 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:48 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
Add tests related to export symbols using external references
(FDT_EXPORT_SYM_REF dtb tag).
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
tests/metadata_exportsyms_ref.dtb.dts.expect | 25 +++++++++++++++++++
tests/metadata_exportsyms_ref.dtb.expect | 18 ++++++++++++++
tests/metadata_exportsyms_ref.dts | 26 ++++++++++++++++++++
tests/metadata_exportsyms_ref.dts.dts.expect | 25 +++++++++++++++++++
tests/run_tests.sh | 3 ++-
5 files changed, 96 insertions(+), 1 deletion(-)
create mode 100644 tests/metadata_exportsyms_ref.dtb.dts.expect
create mode 100644 tests/metadata_exportsyms_ref.dtb.expect
create mode 100644 tests/metadata_exportsyms_ref.dts
create mode 100644 tests/metadata_exportsyms_ref.dts.dts.expect
diff --git a/tests/metadata_exportsyms_ref.dtb.dts.expect b/tests/metadata_exportsyms_ref.dtb.dts.expect
new file mode 100644
index 00000000..d2c33203
--- /dev/null
+++ b/tests/metadata_exportsyms_ref.dtb.dts.expect
@@ -0,0 +1,25 @@
+/dts-v1/;
+/addon/;
+
+/ {
+
+ node-a {
+ };
+
+ node-b {
+ };
+
+ node-c {
+
+ sub-node {
+
+ /export/ ref_base_node_a: &base_node_a;
+ /export/ ref_base_node_b: &base_node_b;
+ };
+ };
+
+ node-d {
+
+ /export/ ref_base_node_c: &base_node_c;
+ };
+};
diff --git a/tests/metadata_exportsyms_ref.dtb.expect b/tests/metadata_exportsyms_ref.dtb.expect
new file mode 100644
index 00000000..395861bc
--- /dev/null
+++ b/tests/metadata_exportsyms_ref.dtb.expect
@@ -0,0 +1,18 @@
+/dts-v1/;
+/addon/;
+
+/ {
+ node-a {
+ };
+ node-b {
+ };
+ node-c {
+ sub-node {
+ // [FDT_EXPORT_SYM_REF] 'ref_base_node_a' -> 'base_node_a'
+ // [FDT_EXPORT_SYM_REF] 'ref_base_node_b' -> 'base_node_b'
+ };
+ };
+ node-d {
+ // [FDT_EXPORT_SYM_REF] 'ref_base_node_c' -> 'base_node_c'
+ };
+};
diff --git a/tests/metadata_exportsyms_ref.dts b/tests/metadata_exportsyms_ref.dts
new file mode 100644
index 00000000..c094be1e
--- /dev/null
+++ b/tests/metadata_exportsyms_ref.dts
@@ -0,0 +1,26 @@
+// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
+/*
+ * Copyright (C) 2026 Bootlin
+ */
+
+/dts-v1/;
+/addon/;
+
+/ {
+ node-a {
+ };
+
+ node-b {
+ };
+
+ node-c {
+ sub_node: sub-node {
+ /export/ ref_base_node_a: &base_node_a;
+ /export/ ref_base_node_b: &base_node_b;
+ };
+ };
+
+ node-d {
+ /export/ ref_base_node_c: &base_node_c;
+ };
+};
diff --git a/tests/metadata_exportsyms_ref.dts.dts.expect b/tests/metadata_exportsyms_ref.dts.dts.expect
new file mode 100644
index 00000000..2654df12
--- /dev/null
+++ b/tests/metadata_exportsyms_ref.dts.dts.expect
@@ -0,0 +1,25 @@
+/dts-v1/;
+/addon/;
+
+/ {
+
+ node-a {
+ };
+
+ node-b {
+ };
+
+ node-c {
+
+ sub_node: sub-node {
+
+ /export/ ref_base_node_a: &base_node_a;
+ /export/ ref_base_node_b: &base_node_b;
+ };
+ };
+
+ node-d {
+
+ /export/ ref_base_node_c: &base_node_c;
+ };
+};
diff --git a/tests/run_tests.sh b/tests/run_tests.sh
index 0c5f2849..f61f3e91 100755
--- a/tests/run_tests.sh
+++ b/tests/run_tests.sh
@@ -1249,7 +1249,8 @@ fdtoverlay_tests() {
metadata_tests() {
for dt in metadata_propdata_phandle_local metadata_propdata_phandle_ref \
- metadata_addon_base metadata_exportsyms_local; do
+ metadata_addon_base metadata_exportsyms_local \
+ metadata_exportsyms_ref; do
run_dtc_test -I dts -O dts -o $dt.dts.dts "$SRCDIR/$dt.dts"
base_run_test check_diff $dt.dts.dts "$SRCDIR/$dt.dts.dts.expect"
run_dtc_test -I dts -O dtb -o $dt.dtb "$SRCDIR/$dt.dts"
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 27/74] dtc: Introduce import symbols
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (25 preceding siblings ...)
2026-08-26 9:48 ` [RFC PATCH v2 26/74] tests: metadata: Add export symbols with external references tests Herve Codina
@ 2026-08-26 9:48 ` Herve Codina
2026-08-26 9:48 ` [RFC PATCH v2 28/74] dtc-parser: Introduce last_header_flags Herve Codina
` (46 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:48 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
Import symbols allow to define a list of symbols imported and used by an
addon.
Those symbols have to be resolved when the addon is applied on a base
device-tree and will involved exported symbols provided at the node the
addon is applied to.
Import symbols are similar in term of feature to __fixups__ available
in device-tree overlays.
The goal is to identify symbols which are used by an addon but not
defined by the addon itself. Those specific symbols are the imported
symbols.
Introduce the import symbols list related to an addon. No functional
change yet but preparation for the future support for import symbol
parsing.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
dtc-parser.y | 3 ++-
dtc.h | 4 +++-
flattree.c | 2 +-
fstree.c | 2 +-
livetree.c | 4 +++-
5 files changed, 10 insertions(+), 5 deletions(-)
diff --git a/dtc-parser.y b/dtc-parser.y
index a0d0aef9..4e46e9d1 100644
--- a/dtc-parser.y
+++ b/dtc-parser.y
@@ -113,7 +113,8 @@ sourcefile:
headers memreserves devicetree
{
parser_output = build_dt_info($1, $2, $3,
- guess_boot_cpuid($3));
+ guess_boot_cpuid($3),
+ NULL);
}
;
diff --git a/dtc.h b/dtc.h
index 8a20d6da..63fe4dc1 100644
--- a/dtc.h
+++ b/dtc.h
@@ -350,6 +350,7 @@ struct dt_info {
struct reserve_info *reservelist;
uint32_t boot_cpuid_phys;
struct node *dt; /* the device tree */
+ struct symbol *importsymlist; /* Import symbol list */
const char *outname; /* filename being written to, "-" for stdout */
};
@@ -360,7 +361,8 @@ struct dt_info {
struct dt_info *build_dt_info(unsigned int dtsflags,
struct reserve_info *reservelist,
- struct node *tree, uint32_t boot_cpuid_phys);
+ struct node *tree, uint32_t boot_cpuid_phys,
+ struct symbol *importsymlist);
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);
diff --git a/flattree.c b/flattree.c
index 4e4727e9..45dc4ed4 100644
--- a/flattree.c
+++ b/flattree.c
@@ -1241,5 +1241,5 @@ struct dt_info *dt_from_blob(const char *fname)
if (get_subnode(tree, "__fixups__") || get_subnode(tree, "__local_fixups__"))
dtsflags |= DTSF_PLUGIN;
- return build_dt_info(dtsflags, reservelist, tree, boot_cpuid_phys);
+ return build_dt_info(dtsflags, reservelist, tree, boot_cpuid_phys, NULL);
}
diff --git a/fstree.c b/fstree.c
index 445ae53a..a6aaf1e8 100644
--- a/fstree.c
+++ b/fstree.c
@@ -72,5 +72,5 @@ struct dt_info *dt_from_fs(const char *dirname)
tree = read_fstree(dirname);
tree = name_node(tree, "");
- return build_dt_info(DTSF_V1, NULL, tree, guess_boot_cpuid(tree));
+ return build_dt_info(DTSF_V1, NULL, tree, guess_boot_cpuid(tree), NULL);
}
diff --git a/livetree.c b/livetree.c
index fe0f300b..a6c40642 100644
--- a/livetree.c
+++ b/livetree.c
@@ -635,7 +635,8 @@ static void complete_references(struct dt_info *dti)
struct dt_info *build_dt_info(unsigned int dtsflags,
struct reserve_info *reservelist,
- struct node *tree, uint32_t boot_cpuid_phys)
+ struct node *tree, uint32_t boot_cpuid_phys,
+ struct symbol *importsymlist)
{
struct dt_info *dti;
@@ -644,6 +645,7 @@ struct dt_info *build_dt_info(unsigned int dtsflags,
dti->reservelist = reservelist;
dti->dt = tree;
dti->boot_cpuid_phys = boot_cpuid_phys;
+ dti->importsymlist = importsymlist;
fill_fullpaths(dti->dt, "");
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 28/74] dtc-parser: Introduce last_header_flags
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (26 preceding siblings ...)
2026-08-26 9:48 ` [RFC PATCH v2 27/74] dtc: Introduce import symbols Herve Codina
@ 2026-08-26 9:48 ` Herve Codina
2026-08-26 9:48 ` [RFC PATCH v2 29/74] dtc: Add support for /import/ dts keyword parsing Herve Codina
` (45 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:48 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
The parser needs to get header flags value in different places.
It relies on the fact that the rule used to parse the dts file is always
headers memreserves devicetree
With that only rule to parse the file, it uses '$<flags>-1' construct to
get the flags value.
With the future introduction of import symbols parsing, this rule will
change and the parser couldn't rely anymore on '$<flags>-1' to get flags
value. Indeed, import symbols parsing will add a new optional symbol in
this rule leading to two possible rules (with and without the new
symbol) to parse the source file.
Introduce the last_header_flags variable to explicitly keep track of
flags while also being agnostic of the rule structure and use this new
variable instead of '$<flags>-1'.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
dtc-parser.y | 28 +++++++++-------------------
1 file changed, 9 insertions(+), 19 deletions(-)
diff --git a/dtc-parser.y b/dtc-parser.y
index 4e46e9d1..48c40e81 100644
--- a/dtc-parser.y
+++ b/dtc-parser.y
@@ -24,6 +24,8 @@ extern void yyerror(char const *s);
extern struct dt_info *parser_output;
extern bool treesource_error;
+unsigned int last_header_flags;
+
static bool is_ref_relative(const char *ref)
{
return ref[0] != '/' && strchr(&ref[1], '/');
@@ -122,14 +124,17 @@ header:
DT_V1 ';'
{
$$ = DTSF_V1;
+ last_header_flags = $$;
}
| DT_V1 ';' DT_PLUGIN ';'
{
$$ = DTSF_V1 | DTSF_PLUGIN;
+ last_header_flags = $$;
}
| DT_V1 ';' DT_ADDON ';'
{
$$ = DTSF_V1 | DTSF_ADDON;
+ last_header_flags = $$;
}
;
@@ -179,12 +184,7 @@ devicetree:
}
| dt_ref nodedef
{
- /*
- * We rely on the rule being always:
- * versioninfo plugindecl memreserves devicetree
- * so $-1 is what we want (plugindecl)
- */
- if (!($<flags>-1 & DTSF_PLUGIN))
+ if (!(last_header_flags & 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);
@@ -197,7 +197,7 @@ devicetree:
{
struct node *target = get_node_by_ref($1, $3);
- if (($<flags>-1 & DTSF_PLUGIN) && is_ref_relative($3))
+ if ((last_header_flags & DTSF_PLUGIN) && is_ref_relative($3))
ERROR(&@2, "Label-relative reference %s not supported in plugin", $3);
if (target) {
@@ -209,12 +209,7 @@ devicetree:
}
| devicetree DT_PATH_REF nodedef
{
- /*
- * We rely on the rule being always:
- * versioninfo plugindecl memreserves devicetree
- * so $-1 is what we want (plugindecl)
- */
- if ($<flags>-1 & DTSF_PLUGIN) {
+ if (last_header_flags & DTSF_PLUGIN) {
if (is_ref_relative($2))
ERROR(&@2, "Label-relative reference %s not supported in plugin", $2);
add_orphan_node($1, $3, $2);
@@ -235,12 +230,7 @@ devicetree:
if (target) {
merge_nodes(target, $3);
} else {
- /*
- * We rely on the rule being always:
- * versioninfo plugindecl memreserves devicetree
- * so $-1 is what we want (plugindecl)
- */
- if ($<flags>-1 & DTSF_PLUGIN)
+ if (last_header_flags & DTSF_PLUGIN)
add_orphan_node($1, $3, $2);
else
ERROR(&@2, "Label or path %s not found", $2);
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 29/74] dtc: Add support for /import/ dts keyword parsing
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (27 preceding siblings ...)
2026-08-26 9:48 ` [RFC PATCH v2 28/74] dtc-parser: Introduce last_header_flags Herve Codina
@ 2026-08-26 9:48 ` Herve Codina
2026-08-26 9:48 ` [RFC PATCH v2 30/74] dtc: Add import symbols (/import/ keyword) in generated dts file Herve Codina
` (44 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:48 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
The /import/ dts keyword is the keyword used to define an imported
symbol used by an addon device-tree.
An imported symbol is not related to a specific node and it is global to
a whole addon. It can be present after the header definition (and the
possible memreserves definition) and before the devicetree nodes
definition.
If several symbols are imported, several /import/ keyword are present.
The syntax used is the following:
/import/ name: compatible;
with:
name: The name of the imported symbol
compatible: a string indicating the kind of symbol is expected. This
compatible string can be used when the addon is applied in
order to help the symbol resolution.
For instance:
/import/ foo: "bar, foo";
The foo symbol is expected by the addon and should be "bar, foo"
compatible.
The addon uses this symbol (i.e. it references it) but it doesn't
define the symbol itself.
From the addon point of view, foo is an unresolved symbol and it will
be resolved when the addon is applied.
Add support for /import/ dts keyword.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
dtc-lexer.l | 6 ++++++
dtc-parser.y | 45 +++++++++++++++++++++++++++++++++++++++++++++
dtc.h | 3 +++
livetree.c | 13 +++++++++++++
4 files changed, 67 insertions(+)
diff --git a/dtc-lexer.l b/dtc-lexer.l
index 3c3f3413..f21ae00d 100644
--- a/dtc-lexer.l
+++ b/dtc-lexer.l
@@ -152,6 +152,12 @@ static void PRINTF(1, 2) lexical_error(const char *fmt, ...);
return DT_EXPORT;
}
+<*>"/import/" {
+ DPRINT("Keyword: /import/\n");
+ BEGIN_DEFAULT();
+ return DT_IMPORT;
+ }
+
<*>{LABEL}: {
DPRINT("Label: %s\n", yytext);
yylval.labelref = xstrdup(yytext);
diff --git a/dtc-parser.y b/dtc-parser.y
index 48c40e81..61ebde2d 100644
--- a/dtc-parser.y
+++ b/dtc-parser.y
@@ -50,6 +50,7 @@ static bool is_ref_relative(const char *ref)
struct node *nodelist;
struct symbol *symbol;
struct symbol *exportlist;
+ struct symbol *importlist;
struct reserve_info *re;
uint64_t integer;
unsigned int flags;
@@ -65,6 +66,7 @@ static bool is_ref_relative(const char *ref)
%token DT_DEL_NODE
%token DT_OMIT_NO_REF
%token DT_EXPORT
+%token DT_IMPORT
%token <propnodename> DT_PROPNODENAME
%token <integer> DT_LITERAL
%token <integer> DT_CHAR_LITERAL
@@ -87,6 +89,8 @@ static bool is_ref_relative(const char *ref)
%type <proplist> proplist
%type <symbol> exportdef
%type <exportlist> exportlist
+%type <symbol> importdef
+%type <exportlist> importlist
%type <labelref> dt_ref
%type <node> devicetree
@@ -118,6 +122,17 @@ sourcefile:
guess_boot_cpuid($3),
NULL);
}
+ | headers memreserves importlist devicetree
+ {
+ /*
+ * importlist is created with chain_symbol() and so it
+ * is created in reverse order. Reverse it now to have
+ * it in correct order
+ */
+ parser_output = build_dt_info($1, $2, $4,
+ $4 ? guess_boot_cpuid($4) : 0,
+ reverse_symbol($3));
+ }
;
header:
@@ -171,6 +186,36 @@ memreserve:
}
;
+importlist:
+ importdef
+ {
+ $$ = chain_symbol($1, NULL);
+ }
+ | importlist importdef
+ {
+ $$ = chain_symbol($2, $1);
+ }
+ | importlist memreserve
+ {
+ ERROR(&@2, "Mem reserve must precede imports");
+ YYERROR;
+ }
+ ;
+
+importdef:
+ DT_IMPORT DT_LABEL DT_STRING ';'
+ {
+ struct data d = $3;
+
+ if (!(last_header_flags & DTSF_ADDON))
+ ERROR(&@2, "Import symbols supported only in addon");
+
+ $$ = build_importsym($2, d.val, &@$);
+ free($2);
+ data_free($3);
+ }
+ ;
+
dt_ref: DT_LABEL_REF | DT_PATH_REF;
devicetree:
diff --git a/dtc.h b/dtc.h
index 63fe4dc1..61521329 100644
--- a/dtc.h
+++ b/dtc.h
@@ -207,6 +207,7 @@ struct label {
struct symbol {
bool is_local;
char *name;
+ char *compatible;
char *ref;
cell_t phandle;
char *fullpath;
@@ -287,6 +288,8 @@ struct property *reverse_properties(struct property *first);
struct symbol *build_exportsym(const char *name, const char *ref, cell_t phandle,
struct srcpos *srcpos);
+struct symbol *build_importsym(const char *name, const char *compatible,
+ struct srcpos *srcpos);
struct symbol *chain_symbol(struct symbol *first, struct symbol *list);
struct symbol *reverse_symbol(struct symbol *list);
void add_symbol(struct symbol **list, struct symbol *new);
diff --git a/livetree.c b/livetree.c
index a6c40642..30344fb8 100644
--- a/livetree.c
+++ b/livetree.c
@@ -51,6 +51,19 @@ struct symbol *build_exportsym(const char *name, const char *ref, cell_t phandle
return new;
}
+struct symbol *build_importsym(const char *name, const char *compatible, struct srcpos *srcpos)
+{
+ struct symbol *new = xmalloc(sizeof(*new));
+
+ memset(new, 0, sizeof(*new));
+
+ new->name = xstrdup(name);
+ new->compatible = xstrdup(compatible);
+ new->srcpos = srcpos_copy(srcpos);
+
+ return new;
+}
+
struct symbol *chain_symbol(struct symbol *first, struct symbol *list)
{
assert(first->next == NULL);
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 30/74] dtc: Add import symbols (/import/ keyword) in generated dts file
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (28 preceding siblings ...)
2026-08-26 9:48 ` [RFC PATCH v2 29/74] dtc: Add support for /import/ dts keyword parsing Herve Codina
@ 2026-08-26 9:48 ` Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 31/74] Add support for FDT_IMPORT_SYM dtb tag Herve Codina
` (43 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:48 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
The import symbols (/import/ keyword) parsing from a dts file is
supported.
Add the support for this keyword in the dts file generation.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
treesource.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/treesource.c b/treesource.c
index a7228f60..d22f1d32 100644
--- a/treesource.c
+++ b/treesource.c
@@ -431,6 +431,7 @@ static void write_tree_source_node(FILE *f, struct node *tree, int level)
void dt_to_source(FILE *f, struct dt_info *dti)
{
struct reserve_info *re;
+ struct symbol *importsym;
fprintf(f, "/dts-v1/;\n");
/*
@@ -453,5 +454,14 @@ void dt_to_source(FILE *f, struct dt_info *dti)
(unsigned long long)re->size);
}
+ if (dti->importsymlist) {
+ for_each_symbol(dti->importsymlist, importsym) {
+ fprintf(f, "/import/ %s: \"%s\";\n",
+ importsym->name,
+ importsym->compatible);
+ }
+ fprintf(f, "\n");
+ }
+
write_tree_source_node(f, dti->dt, 0);
}
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 31/74] Add support for FDT_IMPORT_SYM dtb tag
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (29 preceding siblings ...)
2026-08-26 9:48 ` [RFC PATCH v2 30/74] dtc: Add import symbols (/import/ keyword) in generated dts file Herve Codina
@ 2026-08-26 9:49 ` Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 32/74] tests: metadata: Add import symbols tests Herve Codina
` (42 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:49 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
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 the length of the data part (structured tag), two
values and possible alignment paddings:
- data part length (32bit): strlen(name) + 1 + strlen(compatible) + 1
- name (string including \0)
The import symbol name. I.e. the name used to reference this
imported symbol.
- 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 13 'foo1' 'bar,foo' 0x00 0x00 0x00
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 'baz' ("bar,baz" compatible) leads
to the following sequence:
FDT_IMPORT_SYM 13 'foo1' 'bar,foo' 0x00 0x00 0x00
FDT_IMPORT_SYM 12 'baz' '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.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
fdtdump.c | 10 ++++++
flattree.c | 76 +++++++++++++++++++++++++++++++++++++++++++++-
libfdt/fdt.c | 30 ++++++++++++++++++
libfdt/fdt.h | 14 +++++++++
libfdt/fdt_check.c | 56 +++++++++++++++++++++++++++++-----
5 files changed, 177 insertions(+), 9 deletions(-)
diff --git a/fdtdump.c b/fdtdump.c
index db5b53dc..7e4c244d 100644
--- a/fdtdump.c
+++ b/fdtdump.c
@@ -224,6 +224,16 @@ static void dump_blob(void *blob, bool debug, int dump_unknown)
continue;
}
+ if (tag == FDT_IMPORT_SYM) {
+ p = get_structured_tag_data(tag, p, &d, &sz);
+ s = d;
+ d = d + strlen(s) + 1;
+ t = d;
+ printf("%*s// [FDT_IMPORT_SYM] '%s' (%s)\n", depth * shift, "",
+ s, t);
+ continue;
+ }
+
if ((tag & FDT_TAG_STRUCTURED) && (tag & FDT_TAG_SKIP_SAFE)) {
p = get_structured_tag_data(tag, p, &d, &sz);
diff --git a/flattree.c b/flattree.c
index 45dc4ed4..4f4a185a 100644
--- a/flattree.c
+++ b/flattree.c
@@ -55,6 +55,7 @@ struct emitter {
void (*propdata_phandle_ref)(void *);
void (*export_sym)(void *);
void (*export_sym_ref)(void *);
+ void (*import_sym)(void *);
};
static void bin_emit_cell(void *e, cell_t val)
@@ -124,6 +125,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,
@@ -136,6 +142,7 @@ static struct emitter bin_emitter = {
.propdata_phandle_ref = bin_emit_propdata_phandle_ref,
.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)
@@ -279,6 +286,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,
@@ -291,6 +309,7 @@ static struct emitter asm_emitter = {
.propdata_phandle_ref = asm_emit_propdata_phandle_ref,
.export_sym = asm_emit_export_sym,
.export_sym_ref = asm_emit_export_sym_ref,
+ .import_sym = asm_emit_import_sym,
};
static int stringtable_insert(struct data *d, const char *str)
@@ -470,6 +489,27 @@ static void make_fdt_header(struct fdt_header *fdt,
fdt->last_comp_version_w = cpu_to_fdt32(vi->last_comp_version_w);
}
+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->cell(etarget, strlen(importsym->name) + 1 +
+ strlen(importsym->compatible) + 1);
+ emit->string(etarget, importsym->name, 0);
+ 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;
@@ -492,6 +532,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);
@@ -583,6 +624,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];
@@ -808,6 +852,7 @@ static uint32_t flat_read_tag(struct inbuf *inb)
case FDT_PROPDATA_PHANDLE_REF:
case FDT_EXPORT_SYM:
case FDT_EXPORT_SYM_REF:
+ case FDT_IMPORT_SYM:
return tag;
default:
break;
@@ -903,6 +948,22 @@ static struct property *flat_read_property(struct inbuf *dtbuf,
return build_property(name, val, NULL);
}
+static struct symbol *flat_read_importsym(struct inbuf *inb)
+{
+ struct inbuf subbuf;
+ uint32_t len;
+ const char *name;
+ const char *compatible;
+
+ len = flat_read_word(inb);
+ flat_read_subbuf(inb, &subbuf, len);
+ flat_realign(inb, sizeof(uint32_t));
+
+ name = flat_read_string_norealign(&subbuf);
+ compatible = flat_read_string_norealign(&subbuf);
+
+ return build_importsym(name, compatible, NULL);
+}
static struct reserve_info *flat_read_mem_reserve(struct inbuf *inb)
{
@@ -1115,6 +1176,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 = DTSF_V1;
@@ -1231,6 +1294,17 @@ struct dt_info *dt_from_blob(const char *fname)
tree = unflatten_tree(&dtbuf, &strbuf, "", flags);
val = flat_read_tag(&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_tag(&dtbuf);
+ }
+ }
+ }
+
if (val != FDT_END)
die("Device tree blob doesn't end with FDT_END\n");
@@ -1241,5 +1315,5 @@ struct dt_info *dt_from_blob(const char *fname)
if (get_subnode(tree, "__fixups__") || get_subnode(tree, "__local_fixups__"))
dtsflags |= DTSF_PLUGIN;
- return build_dt_info(dtsflags, reservelist, tree, boot_cpuid_phys, NULL);
+ return build_dt_info(dtsflags, reservelist, tree, boot_cpuid_phys, importsymlist);
}
diff --git a/libfdt/fdt.c b/libfdt/fdt.c
index 9bab9149..b48c3248 100644
--- a/libfdt/fdt.c
+++ b/libfdt/fdt.c
@@ -363,6 +363,34 @@ static uint32_t fdt_next_tag_all(const void *fdt, int startoffset, int *nextoffs
offset = sum;
break;
+ case FDT_IMPORT_SYM:
+ tmp_offset = fdt_get_structured_tag_data(tag, fdt, offset, &len);
+ if (tmp_offset < 0)
+ return FDT_END; /* premature end */
+
+ sum = tmp_offset + len;
+
+ /* Check name */
+ do {
+ p = fdt_offset_ptr(fdt, tmp_offset++, 1);
+ } while (p && (*p != '\0'));
+ if (!can_assume(VALID_DTB) && !p)
+ return FDT_END; /* premature end */
+
+ /* Check compatible */
+ do {
+ p = fdt_offset_ptr(fdt, tmp_offset++, 1);
+ } while (p && (*p != '\0'));
+ if (!can_assume(VALID_DTB) && !p)
+ return FDT_END; /* premature end */
+
+ if (!can_assume(VALID_INPUT) && (uint32_t)tmp_offset > sum)
+ return FDT_END; /* premature end */
+
+ /* Skip the whole data bloc */
+ offset = sum;
+ break;
+
default:
if (!(tag & FDT_TAG_STRUCTURED) || !(tag & FDT_TAG_SKIP_SAFE))
return FDT_END;
@@ -395,6 +423,7 @@ static bool fdt_tag_is_unknown(uint32_t tag)
case FDT_PROPDATA_PHANDLE_REF:
case FDT_EXPORT_SYM:
case FDT_EXPORT_SYM_REF:
+ case FDT_IMPORT_SYM:
return false;
default:
break;
@@ -452,6 +481,7 @@ bool fdt_tag_filter_skip_metadata(void *data, uint32_t tag)
case FDT_PROPDATA_PHANDLE_REF:
case FDT_EXPORT_SYM:
case FDT_EXPORT_SYM_REF:
+ case FDT_IMPORT_SYM:
return true;
default:
diff --git a/libfdt/fdt.h b/libfdt/fdt.h
index 624491a0..04f47784 100644
--- a/libfdt/fdt.h
+++ b/libfdt/fdt.h
@@ -140,6 +140,20 @@ struct fdt_property {
*/
#define FDT_EXPORT_SYM_REF FDT_TAG_CAN_SKIP(FDT_TAG_DATA_VARLEN, 0x2001)
+/*
+ * Import symbol tag (meta-data). varlen: name, compatible
+ *
+ * It defines 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.
+ *
+ * name: import symbol name, i.e. the name used to reference this imported
+ * symbol.
+ * compatible: compatible string that can be used for symbol resolution.
+ * This string can be an empty string if it is not relevant.
+ */
+#define FDT_IMPORT_SYM FDT_TAG_CAN_SKIP(FDT_TAG_DATA_VARLEN, 0x2100)
+
#define FDT_V1_SIZE (7*sizeof(fdt32_t))
#define FDT_V2_SIZE (FDT_V1_SIZE + sizeof(fdt32_t))
#define FDT_V3_SIZE (FDT_V2_SIZE + sizeof(fdt32_t))
diff --git a/libfdt/fdt_check.c b/libfdt/fdt_check.c
index 19b35c8b..7ce3fdd2 100644
--- a/libfdt/fdt_check.c
+++ b/libfdt/fdt_check.c
@@ -20,8 +20,9 @@ int fdt_check_full(const void *fdt, size_t bufsize)
const void *prop = NULL;
const char *propname;
int proplen;
- bool expect_end = false;
+ bool root_node_found = false;
bool subnode_found = false;
+ bool import_sym_found = false;
const fdt32_t *pfdt32;
const char *str;
uint32_t val;
@@ -50,10 +51,6 @@ int fdt_check_full(const void *fdt, size_t bufsize)
if (nextoffset < 0)
return nextoffset;
- /* If we see two root nodes, something is wrong */
- if (expect_end && tag != FDT_END && tag != FDT_NOP)
- return -FDT_ERR_BADSTRUCTURE;
-
switch (tag) {
case FDT_NOP:
break;
@@ -70,10 +67,20 @@ int fdt_check_full(const void *fdt, size_t bufsize)
if (depth > INT_MAX)
return -FDT_ERR_BADSTRUCTURE;
- /* The root node must have an empty name */
+ /* Root node specific checks */
if (depth == 1) {
const char *name;
+ /* If we see two root nodes, something is wrong */
+ if (root_node_found)
+ return -FDT_ERR_BADSTRUCTURE;
+ root_node_found = true;
+
+ /* Import symbols must be after the root node */
+ if (import_sym_found)
+ return -FDT_ERR_BADSTRUCTURE;
+
+ /* The root node must have an empty name */
name = fdt_get_name(fdt, offset, &len);
if (!name)
return len;
@@ -89,8 +96,6 @@ int fdt_check_full(const void *fdt, size_t bufsize)
if (depth == 0)
return -FDT_ERR_BADSTRUCTURE;
depth--;
- if (depth == 0)
- expect_end = true;
break;
case FDT_PROP:
@@ -239,6 +244,41 @@ int fdt_check_full(const void *fdt, size_t bufsize)
break;
+ case FDT_IMPORT_SYM:
+ /* Import symbols are supported only in addons */
+ if (!(fdt_dt_flags(fdt) & FDT_FLAG_ADDON))
+ return -FDT_ERR_BADVALUE;
+
+ import_sym_found = true;
+
+ d = fdt_get_structured_tag_data(tag, fdt,
+ offset + FDT_TAGSIZE, NULL);
+ if (d < 0)
+ return d;
+
+ /* Retrieve the import symbol name */
+ str = fdt_offset_ptr(fdt, d, 1);
+ if (!str)
+ return -FDT_ERR_BADSTRUCTURE;
+
+ /* Check that the name is not an empty string */
+ len = strlen(str);
+ if (!len)
+ return -FDT_ERR_BADVALUE;
+
+ /* Retrieve the compatible */
+ d += len + 1;
+ str = fdt_offset_ptr(fdt, d, 1);
+ if (!str)
+ return -FDT_ERR_BADSTRUCTURE;
+
+ /*
+ * The compatible string can be an empty string
+ * -> No additional check
+ */
+
+ break;
+
default:
return -FDT_ERR_INTERNAL;
}
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 32/74] tests: metadata: Add import symbols tests
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (30 preceding siblings ...)
2026-08-26 9:49 ` [RFC PATCH v2 31/74] Add support for FDT_IMPORT_SYM dtb tag Herve Codina
@ 2026-08-26 9:49 ` Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 33/74] dtc: Add support for import symbols sorting Herve Codina
` (41 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:49 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
Add tests related to import symbols (FDT_IMPORT_SYM dtb tag).
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
tests/metadata_importsyms.dtb.dts.expect | 9 +++++++++
tests/metadata_importsyms.dtb.expect | 8 ++++++++
tests/metadata_importsyms.dts | 14 ++++++++++++++
tests/metadata_importsyms.dts.dts.expect | 9 +++++++++
tests/run_tests.sh | 10 +++++-----
5 files changed, 45 insertions(+), 5 deletions(-)
create mode 100644 tests/metadata_importsyms.dtb.dts.expect
create mode 100644 tests/metadata_importsyms.dtb.expect
create mode 100644 tests/metadata_importsyms.dts
create mode 100644 tests/metadata_importsyms.dts.dts.expect
diff --git a/tests/metadata_importsyms.dtb.dts.expect b/tests/metadata_importsyms.dtb.dts.expect
new file mode 100644
index 00000000..eabf8c70
--- /dev/null
+++ b/tests/metadata_importsyms.dtb.dts.expect
@@ -0,0 +1,9 @@
+/dts-v1/;
+/addon/;
+
+/import/ base_a: "foo,bar";
+/import/ base_b: "foo,baz";
+
+/ {
+ prop = <0x01>;
+};
diff --git a/tests/metadata_importsyms.dtb.expect b/tests/metadata_importsyms.dtb.expect
new file mode 100644
index 00000000..21770262
--- /dev/null
+++ b/tests/metadata_importsyms.dtb.expect
@@ -0,0 +1,8 @@
+/dts-v1/;
+/addon/;
+
+/ {
+ prop = <0x00000001>;
+};
+// [FDT_IMPORT_SYM] 'base_a' (foo,bar)
+// [FDT_IMPORT_SYM] 'base_b' (foo,baz)
diff --git a/tests/metadata_importsyms.dts b/tests/metadata_importsyms.dts
new file mode 100644
index 00000000..671586db
--- /dev/null
+++ b/tests/metadata_importsyms.dts
@@ -0,0 +1,14 @@
+// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
+/*
+ * Copyright (C) 2026 Bootlin
+ */
+
+/dts-v1/;
+/addon/;
+
+/import/ base_a: "foo,bar";
+/import/ base_b: "foo,baz";
+
+/ {
+ prop = <1>;
+};
diff --git a/tests/metadata_importsyms.dts.dts.expect b/tests/metadata_importsyms.dts.dts.expect
new file mode 100644
index 00000000..eabf8c70
--- /dev/null
+++ b/tests/metadata_importsyms.dts.dts.expect
@@ -0,0 +1,9 @@
+/dts-v1/;
+/addon/;
+
+/import/ base_a: "foo,bar";
+/import/ base_b: "foo,baz";
+
+/ {
+ prop = <0x01>;
+};
diff --git a/tests/run_tests.sh b/tests/run_tests.sh
index f61f3e91..7a041aae 100755
--- a/tests/run_tests.sh
+++ b/tests/run_tests.sh
@@ -1250,21 +1250,21 @@ fdtoverlay_tests() {
metadata_tests() {
for dt in metadata_propdata_phandle_local metadata_propdata_phandle_ref \
metadata_addon_base metadata_exportsyms_local \
- metadata_exportsyms_ref; do
+ metadata_exportsyms_ref metadata_importsyms; do
run_dtc_test -I dts -O dts -o $dt.dts.dts "$SRCDIR/$dt.dts"
base_run_test check_diff $dt.dts.dts "$SRCDIR/$dt.dts.dts.expect"
run_dtc_test -I dts -O dtb -o $dt.dtb "$SRCDIR/$dt.dts"
base_run_test wrap_fdtdump $dt.dtb $dt.dtb.out
- # Remove unneeded comments
- sed -i '/^\/\/ /d' $dt.dtb.out
+ # Remove unneeded comments, keep comments in the form '// [xxxx'
+ sed -i '/^\/\/ [^\[]/d' $dt.dtb.out
base_run_test check_diff $dt.dtb.out "$SRCDIR/$dt.dtb.expect"
run_test check_full $dt.dtb
run_dtc_test -I dtb -O dts -o $dt.dtb.dts $dt.dtb
base_run_test check_diff $dt.dtb.dts "$SRCDIR/$dt.dtb.dts.expect"
run_dtc_test -I dts -O dtb -o $dt.dtb.dts.dtb $dt.dtb.dts
base_run_test wrap_fdtdump $dt.dtb.dts.dtb $dt.dtb.dts.dtb.out
- # Remove unneeded comments
- sed -i '/^\/\/ /d' $dt.dtb.dts.dtb.out
+ # Remove unneeded comments, keep comments in the form '// [xxxx'
+ sed -i '/^\/\/ [^\[]/d' $dt.dtb.dts.dtb.out
base_run_test check_diff $dt.dtb.dts.dtb.out "$SRCDIR/$dt.dtb.expect"
done
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 33/74] dtc: Add support for import symbols sorting
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (31 preceding siblings ...)
2026-08-26 9:49 ` [RFC PATCH v2 32/74] tests: metadata: Add import symbols tests Herve Codina
@ 2026-08-26 9:49 ` Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 34/74] tests: metadata: Improve sort test to check " Herve Codina
` (40 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:49 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
dtc can sort items when the command line --sort option is set.
Add support for import symbols sorting when this option is used.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
livetree.c | 27 +++++++++++++++++++++++++++
1 file changed, 27 insertions(+)
diff --git a/livetree.c b/livetree.c
index 30344fb8..130ddf25 100644
--- a/livetree.c
+++ b/livetree.c
@@ -1126,9 +1126,36 @@ static void sort_node(struct node *node)
sort_node(c);
}
+static void sort_importsyms(struct dt_info *dti)
+{
+ int n = 0, i = 0;
+ struct symbol *symbol, **tbl;
+
+ for_each_symbol(dti->importsymlist, symbol)
+ n++;
+
+ if (n == 0)
+ return;
+
+ tbl = xmalloc(n * sizeof(*tbl));
+
+ for_each_symbol(dti->importsymlist, symbol)
+ tbl[i++] = symbol;
+
+ qsort(tbl, n, sizeof(*tbl), cmp_symbol);
+
+ dti->importsymlist = tbl[0];
+ for (i = 0; i < (n-1); i++)
+ tbl[i]->next = tbl[i+1];
+ tbl[n-1]->next = NULL;
+
+ free(tbl);
+}
+
void sort_tree(struct dt_info *dti)
{
sort_reserve_entries(dti);
+ sort_importsyms(dti);
sort_node(dti->dt);
}
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 34/74] tests: metadata: Improve sort test to check for import symbols sorting
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (32 preceding siblings ...)
2026-08-26 9:49 ` [RFC PATCH v2 33/74] dtc: Add support for import symbols sorting Herve Codina
@ 2026-08-26 9:49 ` Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 35/74] checks: Get 'chosen' node using get_subnode() Herve Codina
` (39 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:49 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
dtc is able to sort the import symbols list when the --sort option is
used.
Improve the metadata sort test to perform a check for this feature.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
tests/metadata_sort.dtb.dts.expect | 4 ++++
tests/metadata_sort.dtb.expect | 3 +++
tests/metadata_sort.dts | 4 ++++
tests/run_tests.sh | 4 ++--
4 files changed, 13 insertions(+), 2 deletions(-)
diff --git a/tests/metadata_sort.dtb.dts.expect b/tests/metadata_sort.dtb.dts.expect
index f07a42b2..270c06db 100644
--- a/tests/metadata_sort.dtb.dts.expect
+++ b/tests/metadata_sort.dtb.dts.expect
@@ -1,6 +1,10 @@
/dts-v1/;
/addon/;
+/import/ abc: "foo,bar";
+/import/ de1: "foo,bar";
+/import/ de2: "foo,bar";
+
/ {
node-a {
diff --git a/tests/metadata_sort.dtb.expect b/tests/metadata_sort.dtb.expect
index ed10b59a..d5e22674 100644
--- a/tests/metadata_sort.dtb.expect
+++ b/tests/metadata_sort.dtb.expect
@@ -13,3 +13,6 @@
phandle = <0x00000001>;
};
};
+// [FDT_IMPORT_SYM] 'abc' (foo,bar)
+// [FDT_IMPORT_SYM] 'de1' (foo,bar)
+// [FDT_IMPORT_SYM] 'de2' (foo,bar)
diff --git a/tests/metadata_sort.dts b/tests/metadata_sort.dts
index 7d1cca7e..e523e209 100644
--- a/tests/metadata_sort.dts
+++ b/tests/metadata_sort.dts
@@ -6,6 +6,10 @@
/dts-v1/;
/addon/;
+/import/ de1: "foo,bar";
+/import/ abc: "foo,bar";
+/import/ de2: "foo,bar";
+
/ {
node_b: node-b {
};
diff --git a/tests/run_tests.sh b/tests/run_tests.sh
index 7a041aae..7c45835e 100755
--- a/tests/run_tests.sh
+++ b/tests/run_tests.sh
@@ -1270,8 +1270,8 @@ metadata_tests() {
run_dtc_test -I dts -O dtb -s -o metadata_sort.dtb "$SRCDIR/metadata_sort.dts"
base_run_test wrap_fdtdump metadata_sort.dtb metadata_sort.dtb.out
- # Remove unneeded comments
- sed -i '/^\/\/ /d' metadata_sort.dtb.out
+ # Remove unneeded comments, keep comments in the form '// [xxxx'
+ sed -i '/^\/\/ [^\[]/d' metadata_sort.dtb.out
base_run_test check_diff metadata_sort.dtb.out "$SRCDIR/metadata_sort.dtb.expect"
run_dtc_test -I dtb -O dts -o metadata_sort.dtb.dts metadata_sort.dtb
base_run_test check_diff metadata_sort.dtb.dts "$SRCDIR/metadata_sort.dtb.dts.expect"
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 35/74] checks: Get 'chosen' node using get_subnode()
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (33 preceding siblings ...)
2026-08-26 9:49 ` [RFC PATCH v2 34/74] tests: metadata: Improve sort test to check " Herve Codina
@ 2026-08-26 9:49 ` Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 36/74] dtc: Change get_node_by_path() signature to take dt_info Herve Codina
` (38 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:49 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
get_node_by_path() is going to be updated in order to handle orphan node
support for addon device-tree.
The 'chosen' node is a subnode of the root node and can perfectly be
retrieved using get_subnode().
In preparation of the future change related to get_node_by_path(),
replace its usage to get the 'chosen' node by a call to get_subnode().
The modification doesn't introduce any functional changes.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
checks.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/checks.c b/checks.c
index 13e8b121..1332acc4 100644
--- a/checks.c
+++ b/checks.c
@@ -1349,8 +1349,7 @@ static void check_obsolete_chosen_interrupt_controller(struct check *c,
if (node != dt)
return;
-
- chosen = get_node_by_path(dt, "/chosen");
+ chosen = get_subnode(dt, "chosen");
if (!chosen)
return;
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 36/74] dtc: Change get_node_by_path() signature to take dt_info
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (34 preceding siblings ...)
2026-08-26 9:49 ` [RFC PATCH v2 35/74] checks: Get 'chosen' node using get_subnode() Herve Codina
@ 2026-08-26 9:49 ` Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 37/74] dtc: Change get_node_by_label() " Herve Codina
` (37 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:49 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
The future introduction of orphan nodes for addons device-tree will lead
to more than one tree in the addons data. Those trees will be:
- the classical root tree starting at the root node
- trees related to orphan nodes
Also, an addon device-tree can have only trees based on orphan nodes. In
other words an addon device-tree is valid without having the classical
'root' tree.
To prepare orphan nodes introduction, move get_node_by_path() parameter
one level up and use dt_info struct instead of the 'root' tree (i.e.
dti->dt).
This change doesn't lead to any functional changes.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
checks.c | 2 +-
dtc.h | 3 ++-
livetree.c | 20 ++++++++++++++------
3 files changed, 17 insertions(+), 8 deletions(-)
diff --git a/checks.c b/checks.c
index 1332acc4..3b852a9d 100644
--- a/checks.c
+++ b/checks.c
@@ -748,7 +748,7 @@ static void check_alias_paths(struct check *c, struct dt_info *dti,
/* This check does not work for overlays nor addons with external paths */
if (!(dti->dtsflags & (DTSF_PLUGIN | DTSF_ADDON)) &&
- (!prop->val.val || !get_node_by_path(dti->dt, prop->val.val))) {
+ (!prop->val.val || !get_node_by_path(dti, prop->val.val))) {
FAIL_PROP(c, dti, node, prop, "aliases property is not a valid node (%s)",
prop->val.val);
continue;
diff --git a/dtc.h b/dtc.h
index 61521329..a07df2c8 100644
--- a/dtc.h
+++ b/dtc.h
@@ -323,7 +323,6 @@ struct property *get_property_by_label(struct node *tree, const char *label,
struct marker *get_marker_label(struct node *tree, const char *label,
struct node **node, struct property **prop);
struct node *get_subnode(struct node *node, const char *nodename);
-struct node *get_node_by_path(struct node *tree, const char *path);
struct node *get_node_by_label(struct node *tree, const char *label);
struct node *get_node_by_phandle(struct node *tree, cell_t phandle);
struct node *get_node_by_ref(struct node *tree, const char *ref);
@@ -357,6 +356,8 @@ struct dt_info {
const char *outname; /* filename being written to, "-" for stdout */
};
+struct node *get_node_by_path(struct dt_info *dti, const char *path);
+
/* DTS version flags definitions */
#define DTSF_V1 0x0001 /* /dts-v1/ */
#define DTSF_PLUGIN 0x0002 /* /plugin/ */
diff --git a/livetree.c b/livetree.c
index 130ddf25..0120efd2 100644
--- a/livetree.c
+++ b/livetree.c
@@ -771,7 +771,7 @@ struct node *get_subnode(struct node *node, const char *nodename)
return NULL;
}
-struct node *get_node_by_path(struct node *tree, const char *path)
+static struct node *get_node_by_path_(struct node *tree, const char *path)
{
const char *p;
struct node *child;
@@ -789,7 +789,7 @@ struct node *get_node_by_path(struct node *tree, const char *path)
for_each_child(tree, child) {
if (p && strprefixeq(path, (size_t)(p - path), child->name))
- return get_node_by_path(child, p+1);
+ return get_node_by_path_(child, p+1);
else if (!p && streq(path, child->name))
return child;
}
@@ -892,11 +892,19 @@ struct node *get_node_by_ref(struct node *tree, const char *ref)
}
if (path)
- target = get_node_by_path(target, path);
+ target = get_node_by_path_(target, path);
return target;
}
+struct node *get_node_by_path(struct dt_info *dti, const char *path)
+{
+ if (dti->dt)
+ return get_node_by_path_(dti->dt, path);
+
+ return NULL;
+}
+
static void add_phandle_property(struct node *node,
const char *name, int format)
{
@@ -940,7 +948,7 @@ uint32_t guess_boot_cpuid(struct node *tree)
struct node *cpus, *bootcpu;
struct property *reg;
- cpus = get_node_by_path(tree, "/cpus");
+ cpus = get_node_by_path_(tree, "/cpus");
if (!cpus)
return 0;
@@ -1415,7 +1423,7 @@ void generate_labels_from_tree(struct dt_info *dti, const char *name)
for_each_property(an, p) {
struct node *labeled_node;
- labeled_node = get_node_by_path(dti->dt, p->val.val);
+ 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)
@@ -1497,7 +1505,7 @@ void fixup_phandles(struct dt_info *dti, const char *name)
*/
*(propname - 1) = '\0';
- n = get_node_by_path(dti->dt, fv);
+ 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);
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 37/74] dtc: Change get_node_by_label() signature to take dt_info
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (35 preceding siblings ...)
2026-08-26 9:49 ` [RFC PATCH v2 36/74] dtc: Change get_node_by_path() signature to take dt_info Herve Codina
@ 2026-08-26 9:49 ` Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 38/74] dtc: Change get_node_by_phandle() " Herve Codina
` (36 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:49 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
The future introduction of orphan nodes for addons device-tree will lead
to more than one tree in the addons data. Those trees will be:
- the classical root tree starting at the root node
- trees related to orphan nodes
Also, an addon device-tree can have only trees based on orphan nodes. In
other words an addon device-tree is valid without having the classical
'root' tree.
To prepare orphan nodes introduction, move get_node_by_label() parameter
one level up and use dt_info struct instead of the 'root' tree (i.e.
dti->dt).
This change doesn't lead to any functional changes.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
checks.c | 2 +-
dtc.h | 2 +-
livetree.c | 19 ++++++++++++++++---
3 files changed, 18 insertions(+), 5 deletions(-)
diff --git a/checks.c b/checks.c
index 3b852a9d..f4ae776b 100644
--- a/checks.c
+++ b/checks.c
@@ -452,7 +452,7 @@ static void check_duplicate_label(struct check *c, struct dt_info *dti,
struct property *otherprop = NULL;
struct marker *othermark = NULL;
- othernode = get_node_by_label(dt, label);
+ othernode = get_node_by_label(dti, label);
if (!othernode)
otherprop = get_property_by_label(dt, label, &othernode);
diff --git a/dtc.h b/dtc.h
index a07df2c8..bf8f0ce4 100644
--- a/dtc.h
+++ b/dtc.h
@@ -323,7 +323,6 @@ struct property *get_property_by_label(struct node *tree, const char *label,
struct marker *get_marker_label(struct node *tree, const char *label,
struct node **node, struct property **prop);
struct node *get_subnode(struct node *node, const char *nodename);
-struct node *get_node_by_label(struct node *tree, const char *label);
struct node *get_node_by_phandle(struct node *tree, cell_t phandle);
struct node *get_node_by_ref(struct node *tree, const char *ref);
cell_t get_node_phandle(struct node *root, struct node *node);
@@ -357,6 +356,7 @@ struct dt_info {
};
struct node *get_node_by_path(struct dt_info *dti, const char *path);
+struct node *get_node_by_label(struct dt_info *dti, const char *label);
/* DTS version flags definitions */
#define DTSF_V1 0x0001 /* /dts-v1/ */
diff --git a/livetree.c b/livetree.c
index 0120efd2..aec1e160 100644
--- a/livetree.c
+++ b/livetree.c
@@ -797,7 +797,7 @@ static struct node *get_node_by_path_(struct node *tree, const char *path)
return NULL;
}
-struct node *get_node_by_label(struct node *tree, const char *label)
+static struct node *get_node_by_label_(struct node *tree, const char *label)
{
struct node *child, *node;
struct label *l;
@@ -809,7 +809,7 @@ struct node *get_node_by_label(struct node *tree, const char *label)
return tree;
for_each_child(tree, child) {
- node = get_node_by_label(child, label);
+ node = get_node_by_label_(child, label);
if (node)
return node;
}
@@ -883,7 +883,7 @@ struct node *get_node_by_ref(struct node *tree, const char *ref)
path = slash + 1;
}
- target = get_node_by_label(tree, label);
+ target = get_node_by_label_(tree, label);
free(buf);
@@ -905,6 +905,19 @@ struct node *get_node_by_path(struct dt_info *dti, const char *path)
return NULL;
}
+struct node *get_node_by_label(struct dt_info *dti, const char *label)
+{
+ struct node *node;
+
+ if (dti->dt) {
+ node = get_node_by_label_(dti->dt, label);
+ if (node)
+ return node;
+ }
+
+ return NULL;
+}
+
static void add_phandle_property(struct node *node,
const char *name, int format)
{
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 38/74] dtc: Change get_node_by_phandle() signature to take dt_info
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (36 preceding siblings ...)
2026-08-26 9:49 ` [RFC PATCH v2 37/74] dtc: Change get_node_by_label() " Herve Codina
@ 2026-08-26 9:49 ` Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 39/74] dtc: Change get_node_by_ref() " Herve Codina
` (35 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:49 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
The future introduction of orphan nodes for addons device-tree will lead
to more than one tree in the addons data. Those trees will be:
- the classical root tree starting at the root node
- trees related to orphan nodes
Also, an addon device-tree can have only trees based on orphan nodes. In
other words an addon device-tree is valid without having the classical
'root' tree.
To prepare orphan nodes introduction, move get_node_by_phandle()
parameter one level up and use dt_info struct instead of the 'root' tree
(i.e. dti->dt).
This change doesn't lead to any functional changes.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
checks.c | 14 +++++---------
dtc.h | 2 +-
livetree.c | 21 +++++++++++++++++----
treesource.c | 2 +-
4 files changed, 24 insertions(+), 15 deletions(-)
diff --git a/checks.c b/checks.c
index f4ae776b..6b25c204 100644
--- a/checks.c
+++ b/checks.c
@@ -541,7 +541,6 @@ static cell_t check_phandle_prop(struct check *c, struct dt_info *dti,
static void check_explicit_phandles(struct check *c, struct dt_info *dti,
struct node *node)
{
- struct node *root = dti->dt;
struct node *other;
cell_t phandle, linux_phandle;
@@ -563,7 +562,7 @@ static void check_explicit_phandles(struct check *c, struct dt_info *dti,
if (linux_phandle && !phandle)
phandle = linux_phandle;
- other = get_node_by_phandle(root, phandle);
+ other = get_node_by_phandle(dti, phandle);
if (other && (other != node)) {
FAIL(c, dti, node, "duplicated phandle 0x%x (seen before at %s)",
phandle, other->fullpath);
@@ -1422,7 +1421,6 @@ static void check_property_phandle_args(struct check *c,
struct property *prop,
const struct provider *provider)
{
- struct node *root = dti->dt;
unsigned int cell, cellsize = 0;
if (!is_multiple_of(prop->val.len, sizeof(cell_t))) {
@@ -1465,7 +1463,7 @@ static void check_property_phandle_args(struct check *c,
cell);
}
- provider_node = get_node_by_phandle(root, phandle);
+ provider_node = get_node_by_phandle(dti, phandle);
if (!provider_node) {
FAIL_PROP(c, dti, node, prop,
"Could not get phandle node for (cell %d)",
@@ -1632,7 +1630,6 @@ static void check_interrupt_map(struct check *c,
struct dt_info *dti,
struct node *node)
{
- struct node *root = dti->dt;
struct property *prop, *irq_map_prop;
size_t cellsize, cell, map_cells;
@@ -1686,7 +1683,7 @@ static void check_interrupt_map(struct check *c,
break;
}
- provider_node = get_node_by_phandle(root, phandle);
+ provider_node = get_node_by_phandle(dti, phandle);
if (!provider_node) {
FAIL_PROP(c, dti, node, irq_map_prop,
"Could not get phandle(%d) node for (cell %zu)",
@@ -1724,7 +1721,6 @@ static void check_interrupts_property(struct check *c,
struct dt_info *dti,
struct node *node)
{
- struct node *root = dti->dt;
struct node *irq_node = NULL, *parent = node;
struct property *irq_prop, *prop = NULL;
cell_t irq_cells, phandle;
@@ -1755,7 +1751,7 @@ static void check_interrupts_property(struct check *c,
continue;
}
- irq_node = get_node_by_phandle(root, phandle);
+ irq_node = get_node_by_phandle(dti, phandle);
if (!irq_node) {
FAIL_PROP(c, dti, parent, prop, "Bad phandle");
return;
@@ -1890,7 +1886,7 @@ static struct node *get_remote_endpoint(struct check *c, struct dt_info *dti,
if (!phandle_is_valid(phandle))
return NULL;
- node = get_node_by_phandle(dti->dt, phandle);
+ node = get_node_by_phandle(dti, phandle);
if (!node)
FAIL_PROP(c, dti, endpoint, prop, "graph phandle is not valid");
diff --git a/dtc.h b/dtc.h
index bf8f0ce4..27f49bc6 100644
--- a/dtc.h
+++ b/dtc.h
@@ -323,7 +323,6 @@ struct property *get_property_by_label(struct node *tree, const char *label,
struct marker *get_marker_label(struct node *tree, const char *label,
struct node **node, struct property **prop);
struct node *get_subnode(struct node *node, const char *nodename);
-struct node *get_node_by_phandle(struct node *tree, cell_t phandle);
struct node *get_node_by_ref(struct node *tree, const char *ref);
cell_t get_node_phandle(struct node *root, struct node *node);
@@ -357,6 +356,7 @@ struct dt_info {
struct node *get_node_by_path(struct dt_info *dti, const char *path);
struct node *get_node_by_label(struct dt_info *dti, const char *label);
+struct node *get_node_by_phandle(struct dt_info *dti, cell_t phandle);
/* DTS version flags definitions */
#define DTSF_V1 0x0001 /* /dts-v1/ */
diff --git a/livetree.c b/livetree.c
index aec1e160..5bd65b1b 100644
--- a/livetree.c
+++ b/livetree.c
@@ -584,7 +584,7 @@ static void complete_ref_and_is_local(struct dt_info *dti, char **ref,
}
if (*is_local) {
- refnode = get_node_by_phandle(dti->dt, phandle);
+ refnode = get_node_by_phandle(dti, phandle);
if (!refnode)
die("Node not found for phandle 0x%"PRIx32"\n", phandle);
@@ -834,7 +834,7 @@ static cell_t get_node_phandle_existing(struct node *node)
return propval_cell(prop);
}
-struct node *get_node_by_phandle(struct node *tree, cell_t phandle)
+static struct node *get_node_by_phandle_(struct node *tree, cell_t phandle)
{
struct node *child, *node;
cell_t tree_phandle;
@@ -852,7 +852,7 @@ struct node *get_node_by_phandle(struct node *tree, cell_t phandle)
}
for_each_child(tree, child) {
- node = get_node_by_phandle(child, phandle);
+ node = get_node_by_phandle_(child, phandle);
if (node)
return node;
}
@@ -918,6 +918,19 @@ struct node *get_node_by_label(struct dt_info *dti, const char *label)
return NULL;
}
+struct node *get_node_by_phandle(struct dt_info *dti, cell_t phandle)
+{
+ struct node *node;
+
+ if (dti->dt) {
+ node = get_node_by_phandle_(dti->dt, phandle);
+ if (node)
+ return node;
+ }
+
+ return NULL;
+}
+
static void add_phandle_property(struct node *node,
const char *name, int format)
{
@@ -941,7 +954,7 @@ cell_t get_node_phandle(struct node *root, struct node *node)
if (phandle_is_valid(node->phandle))
return node->phandle;
- while (get_node_by_phandle(root, phandle))
+ while (get_node_by_phandle_(root, phandle))
phandle++;
node->phandle = phandle;
diff --git a/treesource.c b/treesource.c
index d22f1d32..705fe9a6 100644
--- a/treesource.c
+++ b/treesource.c
@@ -205,7 +205,7 @@ void add_phandle_marker(struct dt_info *dti, struct property *prop, unsigned int
}
phandle = dtb_ld32(prop->val.val + offset);
- refn = get_node_by_phandle(dti->dt, phandle);
+ refn = get_node_by_phandle(dti, phandle);
if (!refn) {
if (quiet < 1)
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 39/74] dtc: Change get_node_by_ref() signature to take dt_info
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (37 preceding siblings ...)
2026-08-26 9:49 ` [RFC PATCH v2 38/74] dtc: Change get_node_by_phandle() " Herve Codina
@ 2026-08-26 9:49 ` Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 40/74] dtc: Change get_node_phandle() " Herve Codina
` (34 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:49 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
The future introduction of orphan nodes for addons device-tree will lead
to more than one tree in the addons data. Those trees will be:
- the classical root tree starting at the root node
- trees related to orphan nodes
Also, an addon device-tree can have only trees based on orphan nodes. In
other words an addon device-tree is valid without having the classical
'root' tree.
To prepare orphan nodes introduction, move get_node_by_ref() parameter
one level up and use dt_info struct instead of the 'root' tree (i.e.
dti->dt).
This change doesn't lead to any functional changes.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
checks.c | 10 ++++------
dtc-parser.y | 22 +++++++++++++++++-----
dtc.h | 2 +-
livetree.c | 25 +++++++++++++++++++------
4 files changed, 41 insertions(+), 18 deletions(-)
diff --git a/checks.c b/checks.c
index 6b25c204..b22b616c 100644
--- a/checks.c
+++ b/checks.c
@@ -494,7 +494,6 @@ ERROR(duplicate_label, check_duplicate_label_node, NULL);
static cell_t check_phandle_prop(struct check *c, struct dt_info *dti,
struct node *node, const char *propname)
{
- struct node *root = dti->dt;
struct property *prop;
struct marker *m;
cell_t phandle;
@@ -512,7 +511,7 @@ static cell_t check_phandle_prop(struct check *c, struct dt_info *dti,
m = prop->val.markers;
for_each_marker_of_type(m, REF_PHANDLE) {
assert(m->offset == 0);
- if (node != get_node_by_ref(root, m->ref))
+ if (node != get_node_by_ref(dti, m->ref))
/* "Set this node's phandle equal to some
* other node's phandle". That's nonsensical
* by construction. */ {
@@ -622,7 +621,7 @@ static void fixup_phandle_references(struct check *c, struct dt_info *dti,
for_each_marker_of_type(m, REF_PHANDLE) {
assert(m->offset + sizeof(cell_t) <= prop->val.len);
- refnode = get_node_by_ref(dt, m->ref);
+ refnode = get_node_by_ref(dti, m->ref);
if (! refnode) {
if (!(dti->dtsflags & (DTSF_PLUGIN | DTSF_ADDON)))
FAIL(c, dti, node, "Reference to non-existent node or "
@@ -641,7 +640,7 @@ static void fixup_phandle_references(struct check *c, struct dt_info *dti,
}
for_each_symbol(node->exportsymlist, exportsym) {
- refnode = get_node_by_ref(dt, exportsym->ref);
+ refnode = get_node_by_ref(dti, exportsym->ref);
if (!refnode) {
if (!(dti->dtsflags & (DTSF_PLUGIN | DTSF_ADDON))) {
FAIL(c, dti, node,
@@ -665,7 +664,6 @@ ERROR(phandle_references, fixup_phandle_references, NULL,
static void fixup_path_references(struct check *c, struct dt_info *dti,
struct node *node)
{
- struct node *dt = dti->dt;
struct property *prop;
for_each_property(node, prop) {
@@ -676,7 +674,7 @@ static void fixup_path_references(struct check *c, struct dt_info *dti,
for_each_marker_of_type(m, REF_PATH) {
assert(m->offset <= prop->val.len);
- refnode = get_node_by_ref(dt, m->ref);
+ refnode = get_node_by_ref(dti, m->ref);
if (!refnode) {
FAIL(c, dti, node, "Reference to non-existent node or label \"%s\"\n",
m->ref);
diff --git a/dtc-parser.y b/dtc-parser.y
index 61ebde2d..dd346fb4 100644
--- a/dtc-parser.y
+++ b/dtc-parser.y
@@ -31,6 +31,18 @@ static bool is_ref_relative(const char *ref)
return ref[0] != '/' && strchr(&ref[1], '/');
}
+static struct node *parser_get_node_by_ref(struct node *dt, const char *ref)
+{
+ /*
+ * Use a temporary dt_info in order to use dti_get_node_by_ref()
+ */
+ struct dt_info dti = {};
+
+ dti.dt = dt;
+
+ return get_node_by_ref(&dti, ref);
+}
+
%}
%union {
@@ -240,7 +252,7 @@ devicetree:
}
| devicetree DT_LABEL dt_ref nodedef
{
- struct node *target = get_node_by_ref($1, $3);
+ struct node *target = parser_get_node_by_ref($1, $3);
if ((last_header_flags & DTSF_PLUGIN) && is_ref_relative($3))
ERROR(&@2, "Label-relative reference %s not supported in plugin", $3);
@@ -259,7 +271,7 @@ devicetree:
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);
+ struct node *target = parser_get_node_by_ref($1, $2);
if (target)
merge_nodes(target, $3);
@@ -270,7 +282,7 @@ devicetree:
}
| devicetree DT_LABEL_REF nodedef
{
- struct node *target = get_node_by_ref($1, $2);
+ struct node *target = parser_get_node_by_ref($1, $2);
if (target) {
merge_nodes(target, $3);
@@ -284,7 +296,7 @@ devicetree:
}
| devicetree DT_DEL_NODE dt_ref ';'
{
- struct node *target = get_node_by_ref($1, $3);
+ struct node *target = parser_get_node_by_ref($1, $3);
if (target)
delete_node(target);
@@ -296,7 +308,7 @@ devicetree:
}
| devicetree DT_OMIT_NO_REF dt_ref ';'
{
- struct node *target = get_node_by_ref($1, $3);
+ struct node *target = parser_get_node_by_ref($1, $3);
if (target)
omit_node_if_unused(target);
diff --git a/dtc.h b/dtc.h
index 27f49bc6..d76ba6b7 100644
--- a/dtc.h
+++ b/dtc.h
@@ -323,7 +323,6 @@ struct property *get_property_by_label(struct node *tree, const char *label,
struct marker *get_marker_label(struct node *tree, const char *label,
struct node **node, struct property **prop);
struct node *get_subnode(struct node *node, const char *nodename);
-struct node *get_node_by_ref(struct node *tree, const char *ref);
cell_t get_node_phandle(struct node *root, struct node *node);
uint32_t guess_boot_cpuid(struct node *tree);
@@ -357,6 +356,7 @@ struct dt_info {
struct node *get_node_by_path(struct dt_info *dti, const char *path);
struct node *get_node_by_label(struct dt_info *dti, const char *label);
struct node *get_node_by_phandle(struct dt_info *dti, cell_t phandle);
+struct node *get_node_by_ref(struct dt_info *dti, const char *ref);
/* DTS version flags definitions */
#define DTSF_V1 0x0001 /* /dts-v1/ */
diff --git a/livetree.c b/livetree.c
index 5bd65b1b..40ca873a 100644
--- a/livetree.c
+++ b/livetree.c
@@ -575,7 +575,7 @@ static void complete_ref_and_is_local(struct dt_info *dti, char **ref,
*/
if (*ref) {
- refnode = get_node_by_ref(dti->dt, *ref);
+ refnode = get_node_by_ref(dti, *ref);
if (refnode)
*is_local = true;
else
@@ -860,7 +860,7 @@ static struct node *get_node_by_phandle_(struct node *tree, cell_t phandle)
return NULL;
}
-struct node *get_node_by_ref(struct node *tree, const char *ref)
+static struct node *get_node_by_ref_(struct node *tree, const char *ref)
{
struct node *target = tree;
const char *label = NULL, *path = NULL;
@@ -931,6 +931,19 @@ struct node *get_node_by_phandle(struct dt_info *dti, cell_t phandle)
return NULL;
}
+struct node *get_node_by_ref(struct dt_info *dti, const char *ref)
+{
+ struct node *node;
+
+ if (dti->dt) {
+ node = get_node_by_ref_(dti->dt, ref);
+ if (node)
+ return node;
+ }
+
+ return NULL;
+}
+
static void add_phandle_property(struct node *node,
const char *name, int format)
{
@@ -1283,7 +1296,7 @@ static bool any_fixup_tree(struct dt_info *dti, struct node *node)
for_each_property(node, prop) {
m = prop->val.markers;
for_each_marker_of_type(m, REF_PHANDLE) {
- if (!get_node_by_ref(dti->dt, m->ref))
+ if (!get_node_by_ref_(dti->dt, m->ref))
return true;
}
}
@@ -1339,7 +1352,7 @@ static int generate_fixups_tree_internal(struct dt_info *dti,
for_each_property(node, prop) {
m = prop->val.markers;
for_each_marker_of_type(m, REF_PHANDLE) {
- refnode = get_node_by_ref(dt, m->ref);
+ refnode = get_node_by_ref_(dt, m->ref);
if (!refnode)
if (add_fixup_entry(dti, fn, node, prop, m))
ret = -1;
@@ -1362,7 +1375,7 @@ static bool any_local_fixup_tree(struct dt_info *dti, struct node *node)
for_each_property(node, prop) {
m = prop->val.markers;
for_each_marker_of_type(m, REF_PHANDLE) {
- if (get_node_by_ref(dti->dt, m->ref))
+ if (get_node_by_ref_(dti->dt, m->ref))
return true;
}
}
@@ -1423,7 +1436,7 @@ static int generate_local_fixups_tree_internal(struct dt_info *dti,
for_each_property(node, prop) {
m = prop->val.markers;
for_each_marker_of_type(m, REF_PHANDLE) {
- refnode = get_node_by_ref(dt, m->ref);
+ refnode = get_node_by_ref_(dt, m->ref);
if (refnode)
if (add_local_fixup_entry(dti, lfn, node, prop, m, refnode))
ret = -1;
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 40/74] dtc: Change get_node_phandle() signature to take dt_info
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (38 preceding siblings ...)
2026-08-26 9:49 ` [RFC PATCH v2 39/74] dtc: Change get_node_by_ref() " Herve Codina
@ 2026-08-26 9:49 ` Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 41/74] dtc: Change get_property_by_label() " Herve Codina
` (33 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:49 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
The future introduction of orphan nodes for addons device-tree will lead
to more than one tree in the addons data. Those trees will be:
- the classical root tree starting at the root node
- trees related to orphan nodes
Also, an addon device-tree can have only trees based on orphan nodes. In
other words an addon device-tree is valid without having the classical
'root' tree.
To prepare orphan nodes introduction, move get_node_phandle() parameter
one level up and use dt_info struct instead of the 'root' tree (i.e.
dti->dt).
This change doesn't lead to any functional changes.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
checks.c | 5 ++---
dtc.h | 2 +-
livetree.c | 7 +++----
3 files changed, 6 insertions(+), 8 deletions(-)
diff --git a/checks.c b/checks.c
index b22b616c..f90a8eed 100644
--- a/checks.c
+++ b/checks.c
@@ -609,7 +609,6 @@ ERROR(name_properties, check_name_properties, NULL, &name_is_string);
static void fixup_phandle_references(struct check *c, struct dt_info *dti,
struct node *node)
{
- struct node *dt = dti->dt;
struct symbol *exportsym;
struct property *prop;
struct node *refnode;
@@ -632,7 +631,7 @@ static void fixup_phandle_references(struct check *c, struct dt_info *dti,
continue;
}
- phandle = get_node_phandle(dt, refnode);
+ phandle = get_node_phandle(dti, refnode);
*((fdt32_t *)(prop->val.val + m->offset)) = cpu_to_fdt32(phandle);
reference_node(refnode);
@@ -652,7 +651,7 @@ static void fixup_phandle_references(struct check *c, struct dt_info *dti,
}
/* Create the phandle property for this referenced node */
- phandle = get_node_phandle(dt, refnode);
+ phandle = get_node_phandle(dti, refnode);
exportsym->phandle = phandle;
reference_node(refnode);
diff --git a/dtc.h b/dtc.h
index d76ba6b7..5d39d31f 100644
--- a/dtc.h
+++ b/dtc.h
@@ -323,7 +323,6 @@ struct property *get_property_by_label(struct node *tree, const char *label,
struct marker *get_marker_label(struct node *tree, const char *label,
struct node **node, struct property **prop);
struct node *get_subnode(struct node *node, const char *nodename);
-cell_t get_node_phandle(struct node *root, struct node *node);
uint32_t guess_boot_cpuid(struct node *tree);
@@ -357,6 +356,7 @@ struct node *get_node_by_path(struct dt_info *dti, const char *path);
struct node *get_node_by_label(struct dt_info *dti, const char *label);
struct node *get_node_by_phandle(struct dt_info *dti, cell_t phandle);
struct node *get_node_by_ref(struct dt_info *dti, const char *ref);
+cell_t get_node_phandle(struct dt_info *dti, struct node *node);
/* DTS version flags definitions */
#define DTSF_V1 0x0001 /* /dts-v1/ */
diff --git a/livetree.c b/livetree.c
index 40ca873a..c42e9a97 100644
--- a/livetree.c
+++ b/livetree.c
@@ -960,14 +960,14 @@ static void add_phandle_property(struct node *node,
add_property(node, build_property(name, d, NULL));
}
-cell_t get_node_phandle(struct node *root, struct node *node)
+cell_t get_node_phandle(struct dt_info *dti, struct node *node)
{
static cell_t phandle = 1; /* FIXME: ick, static local */
if (phandle_is_valid(node->phandle))
return node->phandle;
- while (get_node_by_phandle_(root, phandle))
+ while (get_node_by_phandle(dti, phandle))
phandle++;
node->phandle = phandle;
@@ -1250,7 +1250,6 @@ static void generate_label_tree_internal(struct dt_info *dti,
struct node *an, struct node *node,
bool allocph)
{
- struct node *dt = dti->dt;
struct node *c;
struct property *p;
struct label *l;
@@ -1280,7 +1279,7 @@ static void generate_label_tree_internal(struct dt_info *dti,
/* force allocation of a phandle for this node */
if (allocph)
- (void)get_node_phandle(dt, node);
+ (void)get_node_phandle(dti, node);
}
for_each_child(node, c)
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 41/74] dtc: Change get_property_by_label() signature to take dt_info
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (39 preceding siblings ...)
2026-08-26 9:49 ` [RFC PATCH v2 40/74] dtc: Change get_node_phandle() " Herve Codina
@ 2026-08-26 9:49 ` Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 42/74] dtc: Change get_marker_label() " Herve Codina
` (32 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:49 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
The future introduction of orphan nodes for addons device-tree will lead
to more than one tree in the addons data. Those trees will be:
- the classical root tree starting at the root node
- trees related to orphan nodes
Also, an addon device-tree can have only trees based on orphan nodes. In
other words an addon device-tree is valid without having the classical
'root' tree.
To prepare orphan nodes introduction, move get_property_by_label()
parameter one level up and use dt_info struct instead of the 'root' tree
(i.e. dti->dt).
This change doesn't lead to any functional changes.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
checks.c | 2 +-
dtc.h | 4 ++--
livetree.c | 22 +++++++++++++++++++---
3 files changed, 22 insertions(+), 6 deletions(-)
diff --git a/checks.c b/checks.c
index f90a8eed..7b52f37c 100644
--- a/checks.c
+++ b/checks.c
@@ -455,7 +455,7 @@ static void check_duplicate_label(struct check *c, struct dt_info *dti,
othernode = get_node_by_label(dti, label);
if (!othernode)
- otherprop = get_property_by_label(dt, label, &othernode);
+ otherprop = get_property_by_label(dti, label, &othernode);
if (!othernode)
othermark = get_marker_label(dt, label, &othernode,
&otherprop);
diff --git a/dtc.h b/dtc.h
index 5d39d31f..b596cdd0 100644
--- a/dtc.h
+++ b/dtc.h
@@ -318,8 +318,6 @@ const char *get_unitname(struct node *node);
struct property *get_property(struct node *node, const char *propname);
cell_t propval_cell(struct property *prop);
cell_t propval_cell_n(struct property *prop, unsigned int n);
-struct property *get_property_by_label(struct node *tree, const char *label,
- struct node **node);
struct marker *get_marker_label(struct node *tree, const char *label,
struct node **node, struct property **prop);
struct node *get_subnode(struct node *node, const char *nodename);
@@ -357,6 +355,8 @@ struct node *get_node_by_label(struct dt_info *dti, const char *label);
struct node *get_node_by_phandle(struct dt_info *dti, cell_t phandle);
struct node *get_node_by_ref(struct dt_info *dti, const char *ref);
cell_t get_node_phandle(struct dt_info *dti, struct node *node);
+struct property *get_property_by_label(struct dt_info *dti, const char *label,
+ struct node **node);
/* DTS version flags definitions */
#define DTSF_V1 0x0001 /* /dts-v1/ */
diff --git a/livetree.c b/livetree.c
index c42e9a97..332f2196 100644
--- a/livetree.c
+++ b/livetree.c
@@ -706,8 +706,9 @@ cell_t propval_cell_n(struct property *prop, unsigned int n)
return fdt32_to_cpu(*((fdt32_t *)prop->val.val + n));
}
-struct property *get_property_by_label(struct node *tree, const char *label,
- struct node **node)
+static struct property *get_property_by_label_(struct node *tree,
+ const char *label,
+ struct node **node)
{
struct property *prop;
struct node *c;
@@ -723,7 +724,7 @@ struct property *get_property_by_label(struct node *tree, const char *label,
}
for_each_child(tree, c) {
- prop = get_property_by_label(c, label, node);
+ prop = get_property_by_label_(c, label, node);
if (prop)
return prop;
}
@@ -944,6 +945,21 @@ struct node *get_node_by_ref(struct dt_info *dti, const char *ref)
return NULL;
}
+struct property *get_property_by_label(struct dt_info *dti, const char *label,
+ struct node **node)
+{
+ struct property *property;
+
+ if (dti->dt) {
+ property = get_property_by_label_(dti->dt, label, node);
+ if (property)
+ return property;
+ }
+
+ *node = NULL;
+ return NULL;
+}
+
static void add_phandle_property(struct node *node,
const char *name, int format)
{
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 42/74] dtc: Change get_marker_label() signature to take dt_info
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (40 preceding siblings ...)
2026-08-26 9:49 ` [RFC PATCH v2 41/74] dtc: Change get_property_by_label() " Herve Codina
@ 2026-08-26 9:49 ` Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 43/74] dtc: Change fill_fullpaths() " Herve Codina
` (31 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:49 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
The future introduction of orphan nodes for addons device-tree will lead
to more than one tree in the addons data. Those trees will be:
- the classical root tree starting at the root node
- trees related to orphan nodes
Also, an addon device-tree can have only trees based on orphan nodes. In
other words an addon device-tree is valid without having the classical
'root' tree.
To prepare orphan nodes introduction, move get_marker_label() parameter
one level up and use dt_info struct instead of the 'root' tree (i.e.
dti->dt).
This change doesn't lead to any functional changes.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
checks.c | 4 +---
dtc.h | 4 ++--
livetree.c | 22 +++++++++++++++++++---
3 files changed, 22 insertions(+), 8 deletions(-)
diff --git a/checks.c b/checks.c
index 7b52f37c..2b87e212 100644
--- a/checks.c
+++ b/checks.c
@@ -447,7 +447,6 @@ static void check_duplicate_label(struct check *c, struct dt_info *dti,
const char *label, struct node *node,
struct property *prop, struct marker *mark)
{
- struct node *dt = dti->dt;
struct node *othernode = NULL;
struct property *otherprop = NULL;
struct marker *othermark = NULL;
@@ -457,8 +456,7 @@ static void check_duplicate_label(struct check *c, struct dt_info *dti,
if (!othernode)
otherprop = get_property_by_label(dti, label, &othernode);
if (!othernode)
- othermark = get_marker_label(dt, label, &othernode,
- &otherprop);
+ othermark = get_marker_label(dti, label, &othernode, &otherprop);
if (!othernode)
return;
diff --git a/dtc.h b/dtc.h
index b596cdd0..e2d90f63 100644
--- a/dtc.h
+++ b/dtc.h
@@ -318,8 +318,6 @@ const char *get_unitname(struct node *node);
struct property *get_property(struct node *node, const char *propname);
cell_t propval_cell(struct property *prop);
cell_t propval_cell_n(struct property *prop, unsigned int n);
-struct marker *get_marker_label(struct node *tree, const char *label,
- struct node **node, struct property **prop);
struct node *get_subnode(struct node *node, const char *nodename);
uint32_t guess_boot_cpuid(struct node *tree);
@@ -357,6 +355,8 @@ struct node *get_node_by_ref(struct dt_info *dti, const char *ref);
cell_t get_node_phandle(struct dt_info *dti, struct node *node);
struct property *get_property_by_label(struct dt_info *dti, const char *label,
struct node **node);
+struct marker *get_marker_label(struct dt_info *dti, const char *label,
+ struct node **node, struct property **prop);
/* DTS version flags definitions */
#define DTSF_V1 0x0001 /* /dts-v1/ */
diff --git a/livetree.c b/livetree.c
index 332f2196..f8abc5d4 100644
--- a/livetree.c
+++ b/livetree.c
@@ -733,8 +733,8 @@ static struct property *get_property_by_label_(struct node *tree,
return NULL;
}
-struct marker *get_marker_label(struct node *tree, const char *label,
- struct node **node, struct property **prop)
+static struct marker *get_marker_label_(struct node *tree, const char *label,
+ struct node **node, struct property **prop)
{
struct marker *m;
struct property *p;
@@ -751,7 +751,7 @@ struct marker *get_marker_label(struct node *tree, const char *label,
}
for_each_child(tree, c) {
- m = get_marker_label(c, label, node, prop);
+ m = get_marker_label_(c, label, node, prop);
if (m)
return m;
}
@@ -960,6 +960,22 @@ struct property *get_property_by_label(struct dt_info *dti, const char *label,
return NULL;
}
+struct marker *get_marker_label(struct dt_info *dti, const char *label,
+ struct node **node, struct property **prop)
+{
+ struct marker *marker;
+
+ if (dti->dt) {
+ marker = get_marker_label_(dti->dt, label, node, prop);
+ if (marker)
+ return marker;
+ }
+
+ *prop = NULL;
+ *node = NULL;
+ return NULL;
+}
+
static void add_phandle_property(struct node *node,
const char *name, int format)
{
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 43/74] dtc: Change fill_fullpaths() signature to take dt_info
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (41 preceding siblings ...)
2026-08-26 9:49 ` [RFC PATCH v2 42/74] dtc: Change get_marker_label() " Herve Codina
@ 2026-08-26 9:49 ` Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 44/74] dtc: Introduce orphan nodes Herve Codina
` (30 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:49 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
The future introduction of orphan nodes for addons device-tree will lead
to more than one tree in the addons data. Those trees will be:
- the classical root tree starting at the root node
- trees related to orphan nodes
Also, an addon device-tree can have only trees based on orphan nodes. In
other words an addon device-tree is valid without having the classical
'root' tree.
To prepare orphan nodes introduction, move fill_fullpaths() parameter
one level up and use dt_info struct instead of the 'root' tree (i.e.
dti->dt).
This change doesn't lead to any functional changes.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
livetree.c | 13 ++++++++++---
1 file changed, 10 insertions(+), 3 deletions(-)
diff --git a/livetree.c b/livetree.c
index f8abc5d4..a8c7c852 100644
--- a/livetree.c
+++ b/livetree.c
@@ -542,7 +542,7 @@ struct reserve_info *add_reserve_entry(struct reserve_info *list,
return list;
}
-static void fill_fullpaths(struct node *tree, const char *prefix)
+static void fill_fullpaths_(struct node *tree, const char *prefix)
{
struct node *child;
const char *unit;
@@ -556,7 +556,14 @@ static void fill_fullpaths(struct node *tree, const char *prefix)
tree->basenamelen = strlen(tree->name);
for_each_child(tree, child)
- fill_fullpaths(child, tree->fullpath);
+ fill_fullpaths_(child, tree->fullpath);
+}
+
+static void fill_fullpaths(struct dt_info *dti)
+{
+ /* Fill fullpaths for the root node */
+ if (dti->dt)
+ fill_fullpaths_(dti->dt, "");
}
static void complete_ref_and_is_local(struct dt_info *dti, char **ref,
@@ -660,7 +667,7 @@ struct dt_info *build_dt_info(unsigned int dtsflags,
dti->boot_cpuid_phys = boot_cpuid_phys;
dti->importsymlist = importsymlist;
- fill_fullpaths(dti->dt, "");
+ fill_fullpaths(dti);
/*
* Complete references (REF_PHANDLE and export symbols).
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 44/74] dtc: Introduce orphan nodes
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (42 preceding siblings ...)
2026-08-26 9:49 ` [RFC PATCH v2 43/74] dtc: Change fill_fullpaths() " Herve Codina
@ 2026-08-26 9:49 ` Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 45/74] dtc: Handle orphan nodes in get_xxx_by_yyy() Herve Codina
` (29 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:49 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
Orphans nodes can be present in plugin and addon device-trees.
Those nodes are nodes without a parent. The root device-tree node is a
particular orphan node and is not part of those 'orphan' nodes group.
The orphan nodes group is related to nodes identified by a reference in
addons or plugins device-trees. For instance, the following node can be
described in addons and plugins (overlay) device-trees:
--- 8< ---
&foo {
subnode {
prop = <1>;
};
};
--- 8< ---
The foo referenced node described here has no parent. Indeed, it is not
a child of the root node.
This kind of node is legit in plugins and addons device-tree in order to
perform modification on existing node when the plugin or addon dtb is
applied. The foo referenced node is an existing node in the base tree an
the subnode node is added to the foo referenced node chen the dtb is
applied. Even if foo itself doesn't exists in the plugin or addon
device-tree, it needs to be referenced and modifications expected need
to be described (new sub-node for instance).
In plugin dts, when a orphan node is parsed, the dts structure is
changed and the orphan node is merged in the /fragment@n/__overlay__
node. The foo referenced node mentioned in the previous snippet is
transformed to:
--- 8< ---
/ {
fragment@0 {
target = <&foo>;
__overlay__ {
subnode {
prop = <0x01>;
};
};
};
__fixups__ {
foo = "/fragment@0:target:0";
};
};
--- 8< ---
With this mechanism, the orphan node itself doesn't exist anymore in
resulting plugin dtb but the resulting device-tree contains meta-data
mixed with pure 'device-tree' data.
Addons have been introduced to avoid this kind of mixing.
For addons, instead of changing the device-tree structure to add
something similar to fragments and __overlay__ present in plugins, the
orphan node will be present as a new dtb block and will be identified as
orphan thanks to meta-data added in dtb.
Modification done in this commit doesn't add functional changes but just
introduce orphan nodes entry points in data structures and functions in
order to prepare future changes related to parsing/generating dts and
dtb files.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
dtc-parser.y | 4 ++--
dtc.h | 10 +++++++++-
flattree.c | 3 ++-
fstree.c | 3 ++-
livetree.c | 16 +++++++++++++++-
5 files changed, 30 insertions(+), 6 deletions(-)
diff --git a/dtc-parser.y b/dtc-parser.y
index dd346fb4..47f644eb 100644
--- a/dtc-parser.y
+++ b/dtc-parser.y
@@ -132,7 +132,7 @@ sourcefile:
{
parser_output = build_dt_info($1, $2, $3,
guess_boot_cpuid($3),
- NULL);
+ NULL, NULL);
}
| headers memreserves importlist devicetree
{
@@ -143,7 +143,7 @@ sourcefile:
*/
parser_output = build_dt_info($1, $2, $4,
$4 ? guess_boot_cpuid($4) : 0,
- reverse_symbol($3));
+ reverse_symbol($3), NULL);
}
;
diff --git a/dtc.h b/dtc.h
index e2d90f63..06dd7db1 100644
--- a/dtc.h
+++ b/dtc.h
@@ -250,6 +250,8 @@ struct node {
const struct bus_type *bus;
struct srcpos *srcpos;
+ char *ref; /* Use only for orphan nodes */
+
bool omit_if_unused, is_referenced;
};
@@ -277,6 +279,9 @@ struct node {
#define for_each_symbol(s0, s) \
for ((s) = (s0); (s); (s) = (s)->next)
+#define for_each_orphan(o0, o) \
+ for ((o) = (o0); (o); (o) = (o)->next_sibling)
+
void add_label(struct label **labels, char *label);
void delete_labels(struct label **labels);
@@ -345,6 +350,7 @@ struct dt_info {
uint32_t boot_cpuid_phys;
struct node *dt; /* the device tree */
struct symbol *importsymlist; /* Import symbol list */
+ struct node *orphanlist; /* orphan nodes list */
const char *outname; /* filename being written to, "-" for stdout */
};
@@ -357,6 +363,7 @@ struct property *get_property_by_label(struct dt_info *dti, const char *label,
struct node **node);
struct marker *get_marker_label(struct dt_info *dti, const char *label,
struct node **node, struct property **prop);
+bool node_is_orphan(struct dt_info *dti, const struct node *node);
/* DTS version flags definitions */
#define DTSF_V1 0x0001 /* /dts-v1/ */
@@ -366,7 +373,8 @@ struct marker *get_marker_label(struct dt_info *dti, const char *label,
struct dt_info *build_dt_info(unsigned int dtsflags,
struct reserve_info *reservelist,
struct node *tree, uint32_t boot_cpuid_phys,
- struct symbol *importsymlist);
+ struct symbol *importsymlist,
+ struct node *orphanlist);
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);
diff --git a/flattree.c b/flattree.c
index 4f4a185a..bd6974b8 100644
--- a/flattree.c
+++ b/flattree.c
@@ -1315,5 +1315,6 @@ struct dt_info *dt_from_blob(const char *fname)
if (get_subnode(tree, "__fixups__") || get_subnode(tree, "__local_fixups__"))
dtsflags |= DTSF_PLUGIN;
- return build_dt_info(dtsflags, reservelist, tree, boot_cpuid_phys, importsymlist);
+ return build_dt_info(dtsflags, reservelist, tree, boot_cpuid_phys,
+ importsymlist, NULL);
}
diff --git a/fstree.c b/fstree.c
index a6aaf1e8..2be1ffaf 100644
--- a/fstree.c
+++ b/fstree.c
@@ -72,5 +72,6 @@ struct dt_info *dt_from_fs(const char *dirname)
tree = read_fstree(dirname);
tree = name_node(tree, "");
- return build_dt_info(DTSF_V1, NULL, tree, guess_boot_cpuid(tree), NULL);
+ return build_dt_info(DTSF_V1, NULL, tree, guess_boot_cpuid(tree), NULL,
+ NULL);
}
diff --git a/livetree.c b/livetree.c
index a8c7c852..fddb4b4f 100644
--- a/livetree.c
+++ b/livetree.c
@@ -656,7 +656,8 @@ static void complete_references(struct dt_info *dti)
struct dt_info *build_dt_info(unsigned int dtsflags,
struct reserve_info *reservelist,
struct node *tree, uint32_t boot_cpuid_phys,
- struct symbol *importsymlist)
+ struct symbol *importsymlist,
+ struct node *orphanlist)
{
struct dt_info *dti;
@@ -666,6 +667,7 @@ struct dt_info *build_dt_info(unsigned int dtsflags,
dti->dt = tree;
dti->boot_cpuid_phys = boot_cpuid_phys;
dti->importsymlist = importsymlist;
+ dti->orphanlist = orphanlist;
fill_fullpaths(dti);
@@ -952,6 +954,18 @@ struct node *get_node_by_ref(struct dt_info *dti, const char *ref)
return NULL;
}
+bool node_is_orphan(struct dt_info *dti, const struct node *node)
+{
+ struct node *orphan;
+
+ for_each_orphan(dti->orphanlist, orphan) {
+ if (orphan == node)
+ return true;
+ }
+
+ return false;
+}
+
struct property *get_property_by_label(struct dt_info *dti, const char *label,
struct node **node)
{
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 45/74] dtc: Handle orphan nodes in get_xxx_by_yyy()
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (43 preceding siblings ...)
2026-08-26 9:49 ` [RFC PATCH v2 44/74] dtc: Introduce orphan nodes Herve Codina
@ 2026-08-26 9:49 ` Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 46/74] dtc: Avoid NULL fullpath for nodes in orphan trees Herve Codina
` (28 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:49 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
Orphan nodes have been introduced recently.
Retrieving a node, a property and/or a marker from an orphan node tree
is perfectly legit.
Those retrievals are performed by the get_xxx_by_yyy() functions family.
Update them to handle orphan nodes.
Note: Retrieving a node by path (get_node_py_path() function) from an
orphan node tree is not yet supported. Indeed, there is no way to define
a path involving an orphan node.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
livetree.c | 35 +++++++++++++++++++++++++++++++++++
1 file changed, 35 insertions(+)
diff --git a/livetree.c b/livetree.c
index fddb4b4f..c61025a5 100644
--- a/livetree.c
+++ b/livetree.c
@@ -917,6 +917,7 @@ struct node *get_node_by_path(struct dt_info *dti, const char *path)
struct node *get_node_by_label(struct dt_info *dti, const char *label)
{
+ struct node *orphan;
struct node *node;
if (dti->dt) {
@@ -925,11 +926,18 @@ struct node *get_node_by_label(struct dt_info *dti, const char *label)
return node;
}
+ for_each_orphan(dti->orphanlist, orphan) {
+ node = get_node_by_label_(orphan, label);
+ if (node)
+ return node;
+ }
+
return NULL;
}
struct node *get_node_by_phandle(struct dt_info *dti, cell_t phandle)
{
+ struct node *orphan;
struct node *node;
if (dti->dt) {
@@ -938,11 +946,18 @@ struct node *get_node_by_phandle(struct dt_info *dti, cell_t phandle)
return node;
}
+ for_each_orphan(dti->orphanlist, orphan) {
+ node = get_node_by_phandle_(orphan, phandle);
+ if (node)
+ return node;
+ }
+
return NULL;
}
struct node *get_node_by_ref(struct dt_info *dti, const char *ref)
{
+ struct node *orphan;
struct node *node;
if (dti->dt) {
@@ -951,6 +966,12 @@ struct node *get_node_by_ref(struct dt_info *dti, const char *ref)
return node;
}
+ for_each_orphan(dti->orphanlist, orphan) {
+ node = get_node_by_ref_(orphan, ref);
+ if (node)
+ return node;
+ }
+
return NULL;
}
@@ -970,6 +991,7 @@ struct property *get_property_by_label(struct dt_info *dti, const char *label,
struct node **node)
{
struct property *property;
+ struct node *orphan;
if (dti->dt) {
property = get_property_by_label_(dti->dt, label, node);
@@ -977,6 +999,12 @@ struct property *get_property_by_label(struct dt_info *dti, const char *label,
return property;
}
+ for_each_orphan(dti->orphanlist, orphan) {
+ property = get_property_by_label_(orphan, label, node);
+ if (property)
+ return property;
+ }
+
*node = NULL;
return NULL;
}
@@ -985,6 +1013,7 @@ struct marker *get_marker_label(struct dt_info *dti, const char *label,
struct node **node, struct property **prop)
{
struct marker *marker;
+ struct node *orphan;
if (dti->dt) {
marker = get_marker_label_(dti->dt, label, node, prop);
@@ -992,6 +1021,12 @@ struct marker *get_marker_label(struct dt_info *dti, const char *label,
return marker;
}
+ for_each_orphan(dti->orphanlist, orphan) {
+ marker = get_marker_label_(orphan, label, node, prop);
+ if (marker)
+ return marker;
+ }
+
*prop = NULL;
*node = NULL;
return NULL;
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 46/74] dtc: Avoid NULL fullpath for nodes in orphan trees
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (44 preceding siblings ...)
2026-08-26 9:49 ` [RFC PATCH v2 45/74] dtc: Handle orphan nodes in get_xxx_by_yyy() Herve Codina
@ 2026-08-26 9:49 ` Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 47/74] dtc: Handle orphan nodes in complete_references() Herve Codina
` (27 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:49 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
Orphan nodes have been introduced recently.
The process_checks() function uses fullpath in several places and
crashes due NULL pointer dereferences if fullpath is NULL.
In order to have process_checks() function running without crashes with
orphan trees (i.e. trees based on orphan nodes), the fullpath of node
available in those orphan trees must not be NULL.
Fullpath values are built by dti_fill_fullpaths(). Update it to handle
fullpath in trees based on orphan nodes.
Use a simple "__orphan__/" prefix to avoid the NULL pointer and to be
distinct from the root node ("/" prefix).
It is worth noting that this "__orphan__/" prefix is a temporary prefix
and it will be change later when support for reference by path involving
nodes in orphan tree is added.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
livetree.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/livetree.c b/livetree.c
index c61025a5..8f0aa868 100644
--- a/livetree.c
+++ b/livetree.c
@@ -561,9 +561,14 @@ static void fill_fullpaths_(struct node *tree, const char *prefix)
static void fill_fullpaths(struct dt_info *dti)
{
+ struct node *orphan;
+
/* Fill fullpaths for the root node */
if (dti->dt)
fill_fullpaths_(dti->dt, "");
+
+ for_each_orphan(dti->orphanlist, orphan)
+ fill_fullpaths_(orphan, "__orphan__/");
}
static void complete_ref_and_is_local(struct dt_info *dti, char **ref,
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 47/74] dtc: Handle orphan nodes in complete_references()
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (45 preceding siblings ...)
2026-08-26 9:49 ` [RFC PATCH v2 46/74] dtc: Avoid NULL fullpath for nodes in orphan trees Herve Codina
@ 2026-08-26 9:49 ` Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 48/74] checks: Perform checks for orphan nodes Herve Codina
` (26 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:49 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
Orphan nodes have been introduced recently.
Completing references involving orphan nodes is needed just like for the
'root' node.
Also, it is worth noting that even if an orphan node can be referenced,
this orphan node is an external node and so the references pointing
to orphan nodes cannot be a local reference.
Update related functions to handle orphan nodes.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
livetree.c | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/livetree.c b/livetree.c
index 8f0aa868..71712649 100644
--- a/livetree.c
+++ b/livetree.c
@@ -584,11 +584,15 @@ static void complete_ref_and_is_local(struct dt_info *dti, char **ref,
* phandle value is available) but not the reference.
*
* Complete both ref and is_local based on existing values.
+ *
+ * Also take care of references pointing to an orphan node. Those
+ * reference are not local references. Indeed, an orphan node is an
+ * external node.
*/
if (*ref) {
refnode = get_node_by_ref(dti, *ref);
- if (refnode)
+ if (refnode && !node_is_orphan(dti, refnode))
*is_local = true;
else
*is_local = false;
@@ -655,7 +659,12 @@ static void complete_references_internal(struct dt_info *dti, struct node *node)
static void complete_references(struct dt_info *dti)
{
+ struct node *orphan;
+
complete_references_internal(dti, dti->dt);
+
+ for_each_orphan(dti->orphanlist, orphan)
+ complete_references_internal(dti, orphan);
}
struct dt_info *build_dt_info(unsigned int dtsflags,
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 48/74] checks: Perform checks for orphan nodes
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (46 preceding siblings ...)
2026-08-26 9:49 ` [RFC PATCH v2 47/74] dtc: Handle orphan nodes in complete_references() Herve Codina
@ 2026-08-26 9:49 ` Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 49/74] dtc: Rename add_orphan_node() to plugin_add_orphan_node() Herve Codina
` (25 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:49 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
Orphan nodes have been introduced recently.
Those nodes and related trees need to be handled by the check process.
Indeed, in addition to performing checks, phandles are allocated and
referenced nodes are marked 'referenced' during the check process.
Take into account orphan nodes in check process.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
checks.c | 27 +++++++++++++++++++++++++--
1 file changed, 25 insertions(+), 2 deletions(-)
diff --git a/checks.c b/checks.c
index 2b87e212..a0024389 100644
--- a/checks.c
+++ b/checks.c
@@ -155,6 +155,7 @@ static bool is_multiple_of(int multiple, int divisor)
static bool run_check(struct check *c, struct dt_info *dti)
{
struct node *dt = dti->dt;
+ struct node *orphan;
bool error = false;
int i;
@@ -180,6 +181,9 @@ static bool run_check(struct check *c, struct dt_info *dti)
check_nodes_props(c, dti, dt);
+ for_each_orphan(dti->orphanlist, orphan)
+ check_nodes_props(c, dti, orphan);
+
if (c->status == UNCHECKED)
c->status = PASSED;
@@ -313,7 +317,12 @@ ERROR(duplicate_property_names, check_duplicate_property_names, NULL);
static void check_node_name_chars(struct check *c, struct dt_info *dti,
struct node *node)
{
- size_t n = strspn(node->name, c->data);
+ size_t n;
+
+ if ((dti->dtsflags & DTSF_ADDON) && node_is_orphan(dti, node))
+ return; /* Checking name doesn't make sense for an orphan node */
+
+ n = strspn(node->name, c->data);
if (n < strlen(node->name))
FAIL(c, dti, node, "Bad character '%c' in node name",
@@ -324,7 +333,12 @@ ERROR(node_name_chars, check_node_name_chars, NODECHARS);
static void check_node_name_chars_strict(struct check *c, struct dt_info *dti,
struct node *node)
{
- size_t n = strspn(node->name, c->data);
+ size_t n;
+
+ if ((dti->dtsflags & DTSF_ADDON) && node_is_orphan(dti, node))
+ return; /* Checking name doesn't make sense for an orphan node */
+
+ n = strspn(node->name, c->data);
if (n < node->basenamelen)
FAIL(c, dti, node, "Character '%c' not recommended in node name",
@@ -628,6 +642,15 @@ static void fixup_phandle_references(struct check *c, struct dt_info *dti,
cpu_to_fdt32(0xffffffff);
continue;
}
+ if (node_is_orphan(dti, refnode)) {
+ if (!(dti->dtsflags & DTSF_ADDON))
+ FAIL(c, dti, node, "Reference to orphan node \"%s\"\n",
+ m->ref);
+ else /* mark the entry as unresolved */
+ *((fdt32_t *)(prop->val.val + m->offset)) =
+ cpu_to_fdt32(0xffffffff);
+ continue;
+ }
phandle = get_node_phandle(dti, refnode);
*((fdt32_t *)(prop->val.val + m->offset)) = cpu_to_fdt32(phandle);
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 49/74] dtc: Rename add_orphan_node() to plugin_add_orphan_node()
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (47 preceding siblings ...)
2026-08-26 9:49 ` [RFC PATCH v2 48/74] checks: Perform checks for orphan nodes Herve Codina
@ 2026-08-26 9:49 ` Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 50/74] dtc: Add basic support for addon orphan nodes in dts Herve Codina
` (24 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:49 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
add_orphan_node() is used when a orphan node is parsed in a plugin
(overlay) device-tree.
This function creates some device-tree nodes (/fragment@n/__overlay__)
in order to merge the parsed orphan node.
This is specific to plugin device-trees and cannot be used for addon
device-trees.
In order to clarify that add_orphan_node() is specific to plugin
device-trees and to prepare the support for orphan node parsing
in the addon context, rename current add_orphan_node() to
plugin_add_orphan_node().
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
dtc-parser.y | 6 +++---
dtc.h | 3 ++-
livetree.c | 3 ++-
3 files changed, 7 insertions(+), 5 deletions(-)
diff --git a/dtc-parser.y b/dtc-parser.y
index 47f644eb..885123af 100644
--- a/dtc-parser.y
+++ b/dtc-parser.y
@@ -245,7 +245,7 @@ devicetree:
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(
+ $$ = plugin_add_orphan_node(
name_node(build_node(NULL, NULL, NULL, NULL),
""),
$2, $1);
@@ -269,7 +269,7 @@ devicetree:
if (last_header_flags & DTSF_PLUGIN) {
if (is_ref_relative($2))
ERROR(&@2, "Label-relative reference %s not supported in plugin", $2);
- add_orphan_node($1, $3, $2);
+ plugin_add_orphan_node($1, $3, $2);
} else {
struct node *target = parser_get_node_by_ref($1, $2);
@@ -288,7 +288,7 @@ devicetree:
merge_nodes(target, $3);
} else {
if (last_header_flags & DTSF_PLUGIN)
- add_orphan_node($1, $3, $2);
+ plugin_add_orphan_node($1, $3, $2);
else
ERROR(&@2, "Label or path %s not found", $2);
}
diff --git a/dtc.h b/dtc.h
index 06dd7db1..cad092c1 100644
--- a/dtc.h
+++ b/dtc.h
@@ -307,7 +307,8 @@ struct node *omit_node_if_unused(struct node *node);
struct node *reference_node(struct node *node);
struct node *chain_node(struct node *first, struct node *list);
struct node *merge_nodes(struct node *old_node, struct node *new_node);
-struct node *add_orphan_node(struct node *old_node, struct node *new_node, char *ref);
+struct node *plugin_add_orphan_node(struct node *old_node, struct node *new_node,
+ char *ref);
void add_property(struct node *node, struct property *prop);
void delete_property_by_name(struct node *node, char *name);
diff --git a/livetree.c b/livetree.c
index 71712649..ad9ae295 100644
--- a/livetree.c
+++ b/livetree.c
@@ -319,7 +319,8 @@ struct node *merge_nodes(struct node *old_node, struct node *new_node)
return old_node;
}
-struct node * add_orphan_node(struct node *dt, struct node *new_node, char *ref)
+struct node *plugin_add_orphan_node(struct node *dt, struct node *new_node,
+ char *ref)
{
static unsigned int next_orphan_fragment = 0;
struct node *node;
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 50/74] dtc: Add basic support for addon orphan nodes in dts
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (48 preceding siblings ...)
2026-08-26 9:49 ` [RFC PATCH v2 49/74] dtc: Rename add_orphan_node() to plugin_add_orphan_node() Herve Codina
@ 2026-08-26 9:49 ` Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 51/74] Add support for FDT_BEGIN_NODE_REF dtb tag Herve Codina
` (23 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:49 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
Orphan nodes are nodes without a parent. The root device-tree node is a
particular orphan node and is not part of those 'orphan' nodes group.
The orphan nodes group is related to nodes identified by a reference in
addons or plugins device-trees. For instance, int the following snippet,
foo is an orphan node:
--- 8< ---
/addon/
&foo {
subnode {
prop = <1>;
};
};
--- 8< ---
The foo referenced node described has no parent. Indeed, it is not
a child of the root node.
This kind of node is legit in addons device-tree in order to perform
modification on existing node when the addon dtb is applied.
Add support for addon orphan node in dts.
Compared to plugin (overlay) orphan node parsing, the orphan node parsed
in the addon context does not lead to any change in the device-tree
structure. '/fragment@0/__overlay__' or similar kind of structure is not
needed for addons.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
dtc-parser.y | 60 +++++++++++++++++++++++++++++++++++-----------------
dtc.h | 2 ++
livetree.c | 30 ++++++++++++++++++++++++++
treesource.c | 8 +++++++
4 files changed, 81 insertions(+), 19 deletions(-)
diff --git a/dtc-parser.y b/dtc-parser.y
index 885123af..197a9533 100644
--- a/dtc-parser.y
+++ b/dtc-parser.y
@@ -23,6 +23,7 @@ extern void yyerror(char const *s);
extern struct dt_info *parser_output;
extern bool treesource_error;
+extern struct node *parser_orphanlist;
unsigned int last_header_flags;
@@ -31,7 +32,8 @@ static bool is_ref_relative(const char *ref)
return ref[0] != '/' && strchr(&ref[1], '/');
}
-static struct node *parser_get_node_by_ref(struct node *dt, const char *ref)
+static struct node *parser_get_node_by_ref(struct node *dt, struct node *orphanlist,
+ const char *ref)
{
/*
* Use a temporary dt_info in order to use dti_get_node_by_ref()
@@ -39,6 +41,7 @@ static struct node *parser_get_node_by_ref(struct node *dt, const char *ref)
struct dt_info dti = {};
dti.dt = dt;
+ dti.orphanlist = orphanlist;
return get_node_by_ref(&dti, ref);
}
@@ -132,7 +135,7 @@ sourcefile:
{
parser_output = build_dt_info($1, $2, $3,
guess_boot_cpuid($3),
- NULL, NULL);
+ NULL, parser_orphanlist);
}
| headers memreserves importlist devicetree
{
@@ -143,7 +146,8 @@ sourcefile:
*/
parser_output = build_dt_info($1, $2, $4,
$4 ? guess_boot_cpuid($4) : 0,
- reverse_symbol($3), NULL);
+ reverse_symbol($3),
+ parser_orphanlist);
}
;
@@ -241,21 +245,28 @@ devicetree:
}
| dt_ref nodedef
{
- if (!(last_header_flags & DTSF_PLUGIN))
+ if (!(last_header_flags & (DTSF_PLUGIN | DTSF_ADDON)))
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);
- $$ = plugin_add_orphan_node(
- name_node(build_node(NULL, NULL, NULL, NULL),
- ""),
- $2, $1);
+ ERROR(&@2, "Label-relative reference %s not supported in plugin nor addon", $1);
+
+ if (last_header_flags & DTSF_PLUGIN) {
+ $$ = plugin_add_orphan_node(
+ name_node(build_node(NULL, NULL, NULL, NULL),
+ ""),
+ $2, $1);
+ } else {
+ ERROR(&@2, "Orphan node %s without a root node not yet supported", $1);
+ YYERROR;
+ }
}
| devicetree DT_LABEL dt_ref nodedef
{
- struct node *target = parser_get_node_by_ref($1, $3);
+ struct node *target = parser_get_node_by_ref(
+ $1, parser_orphanlist, $3);
- if ((last_header_flags & DTSF_PLUGIN) && is_ref_relative($3))
- ERROR(&@2, "Label-relative reference %s not supported in plugin", $3);
+ if ((last_header_flags & (DTSF_PLUGIN | DTSF_ADDON)) && is_ref_relative($3))
+ ERROR(&@2, "Label-relative reference %s not supported in plugin nor addon", $3);
if (target) {
add_label(&target->labels, $2);
@@ -266,12 +277,17 @@ devicetree:
}
| devicetree DT_PATH_REF nodedef
{
- if (last_header_flags & DTSF_PLUGIN) {
+ if (last_header_flags & (DTSF_PLUGIN | DTSF_ADDON)) {
if (is_ref_relative($2))
- ERROR(&@2, "Label-relative reference %s not supported in plugin", $2);
- plugin_add_orphan_node($1, $3, $2);
+ ERROR(&@2, "Label-relative reference %s not supported in plugin nor addon", $2);
+ if(last_header_flags & DTSF_PLUGIN)
+ plugin_add_orphan_node($1, $3, $2);
+ else
+ addon_add_orphan_node(&parser_orphanlist,
+ orphan_node($3, $2));
} else {
- struct node *target = parser_get_node_by_ref($1, $2);
+ struct node *target = parser_get_node_by_ref(
+ $1, parser_orphanlist, $2);
if (target)
merge_nodes(target, $3);
@@ -282,13 +298,17 @@ devicetree:
}
| devicetree DT_LABEL_REF nodedef
{
- struct node *target = parser_get_node_by_ref($1, $2);
+ struct node *target = parser_get_node_by_ref(
+ $1, parser_orphanlist, $2);
if (target) {
merge_nodes(target, $3);
} else {
if (last_header_flags & DTSF_PLUGIN)
plugin_add_orphan_node($1, $3, $2);
+ else if (last_header_flags & DTSF_ADDON)
+ addon_add_orphan_node(&parser_orphanlist,
+ orphan_node($3, $2));
else
ERROR(&@2, "Label or path %s not found", $2);
}
@@ -296,7 +316,8 @@ devicetree:
}
| devicetree DT_DEL_NODE dt_ref ';'
{
- struct node *target = parser_get_node_by_ref($1, $3);
+ struct node *target = parser_get_node_by_ref(
+ $1, parser_orphanlist, $3);
if (target)
delete_node(target);
@@ -308,7 +329,8 @@ devicetree:
}
| devicetree DT_OMIT_NO_REF dt_ref ';'
{
- struct node *target = parser_get_node_by_ref($1, $3);
+ struct node *target = parser_get_node_by_ref(
+ $1, parser_orphanlist, $3);
if (target)
omit_node_if_unused(target);
diff --git a/dtc.h b/dtc.h
index cad092c1..de5fa266 100644
--- a/dtc.h
+++ b/dtc.h
@@ -309,6 +309,8 @@ struct node *chain_node(struct node *first, struct node *list);
struct node *merge_nodes(struct node *old_node, struct node *new_node);
struct node *plugin_add_orphan_node(struct node *old_node, struct node *new_node,
char *ref);
+struct node *orphan_node(struct node *node, const char *ref);
+void addon_add_orphan_node(struct node **dt_orphan, struct node *new_orphan);
void add_property(struct node *node, struct property *prop);
void delete_property_by_name(struct node *node, char *name);
diff --git a/livetree.c b/livetree.c
index ad9ae295..26c3c079 100644
--- a/livetree.c
+++ b/livetree.c
@@ -351,6 +351,36 @@ struct node *plugin_add_orphan_node(struct node *dt, struct node *new_node,
return dt;
}
+struct node *orphan_node(struct node *node, const char *ref)
+{
+ node->ref = xstrdup(ref);
+
+ return node;
+}
+
+void addon_add_orphan_node(struct node **dt_orphan, struct node *new_orphan)
+{
+ struct node **last_orphan;
+ char *name;
+
+ /*
+ * For addon orphan node, the node name is set to '&reference'
+ * This ease the dts file generation and the usage of existing code
+ */
+ xasprintf(&name, "&%s", new_orphan->ref);
+
+ name_node(new_orphan, name);
+ free(name);
+
+ new_orphan->next_sibling = NULL;
+
+ last_orphan = dt_orphan;
+ while (*last_orphan)
+ last_orphan = &((*last_orphan)->next_sibling);
+
+ *last_orphan = new_orphan;
+}
+
struct node *chain_node(struct node *first, struct node *list)
{
assert(first->next_sibling == NULL);
diff --git a/treesource.c b/treesource.c
index 705fe9a6..b50d9f81 100644
--- a/treesource.c
+++ b/treesource.c
@@ -12,11 +12,13 @@ extern YYLTYPE yylloc;
struct dt_info *parser_output;
bool treesource_error;
+struct node *parser_orphanlist;
struct dt_info *dt_from_source(const char *fname)
{
parser_output = NULL;
treesource_error = false;
+ parser_orphanlist = NULL;
srcfile_push(fname);
yyin = current_srcfile->f;
@@ -432,6 +434,7 @@ void dt_to_source(FILE *f, struct dt_info *dti)
{
struct reserve_info *re;
struct symbol *importsym;
+ struct node *orphan;
fprintf(f, "/dts-v1/;\n");
/*
@@ -464,4 +467,9 @@ void dt_to_source(FILE *f, struct dt_info *dti)
}
write_tree_source_node(f, dti->dt, 0);
+
+ for_each_orphan(dti->orphanlist, orphan) {
+ fprintf(f, "\n");
+ write_tree_source_node(f, orphan, 0);
+ }
}
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 51/74] Add support for FDT_BEGIN_NODE_REF dtb tag
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (49 preceding siblings ...)
2026-08-26 9:49 ` [RFC PATCH v2 50/74] dtc: Add basic support for addon orphan nodes in dts Herve Codina
@ 2026-08-26 9:49 ` Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 52/74] tests: metadata: Add basic test for addon orphan nodes Herve Codina
` (22 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:49 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
The FDT_BEGIN_NODE_REF dtb tag is a tag defining a referenced node.
This kind of nodes can be present in addon dtbs and identifies a node
external to the addon dtb but modified by the addon.
For instance, if an addon adds a sub-node to a node existing in the
device-tree the addon is applied to, the existing node needs to be
referenced by the addon dtb. This is the purpose of FDT_BEGIN_NODE_REF.
This tag typically identifies what we call orphan nodes in dts.
The structure of FDT_BEGIN_NODE_REF is identical to the structure of
FDT_BEGIN_NODE except that the node name is not present and it is
replaced by the node symbol reference needed to reference the node in
the base device-tree when the addon device-tree blob is applied.
The FDT_BEGIN_NODE_REF block is terminated by a FDT_END_NODE tag.
The FDT_BEGIN_NODE_REF tag is followed by the length of the data part
(structured tag), the node symbol name, a possible alignment padding and
then the node structure. The FDT_BEGIN_NODE_REF block is terminated by a
FDT_END_NODE tag:
- data part length (32bit): strlen(node symbol name) + 1
- node symbol name (string including \0)
The symbol name used to reference the node in the base device-tree
when the addon is applied.
- padding:
Padding (0x00) added to have the next value aligned on 32bit.
- Node structure (see FDT_BEGIN_NODE):
Items provided by the addon to be merged in the referenced
node when the addon is applied.
- FDT_END_NODE tag
Example:
FDT_BEGIN_NODE_REF 5 'foo1' 0x00 0x00 0x00 ... FDT_END_NODE
This means that the external node referenced by 'foo1' is modified by
the addon. Items to merge when the addon is applied are represented by
the '...' part.
This is what is encoded in the dtb when the related dts has the
following orphan node:
&foo1 {
...
};
If several external nodes are modified by the addon (several orphan
nodes described in the addon), several FDT_BEGIN_NODE_REF are present.
Each of them related to an external node. For instance, having 'foo1'
and 'bar10' as external nodes leads to the following sequence:
FDT_BEGIN_NODE_REF 5 'foo1' 0x00 0x00 0x00 ... FDT_END_NODE
FDT_BEGIN_NODE_REF 6 'bar10' 0x00 0x00 ... FDT_END_NODE
The FDT_BEGIN_NODE_REF can be present only for a top level node.
Subnodes are identified by a FDT_BEGIN_NODE tag.
If FDT_BEGIN_NODE_REF tags are present in the dtb, they are present
after the root node definition (i.e. after the first FDT_BEGIN_NODE
/ FDT_END_NODE block).
Add support for this new dtb tag.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
fdtdump.c | 12 +++++
flattree.c | 114 ++++++++++++++++++++++++++++++++-------
libfdt/fdt.c | 95 +++++++++++++++++++++++++++++++-
libfdt/fdt.h | 18 +++++++
libfdt/fdt_check.c | 47 +++++++++++++++-
libfdt/fdt_ro.c | 46 +++++++++++++++-
libfdt/libfdt_internal.h | 10 ++++
7 files changed, 317 insertions(+), 25 deletions(-)
diff --git a/fdtdump.c b/fdtdump.c
index 7e4c244d..d68c07b9 100644
--- a/fdtdump.c
+++ b/fdtdump.c
@@ -206,6 +206,18 @@ static void dump_blob(void *blob, bool debug, int dump_unknown)
continue;
}
+ if (tag == FDT_BEGIN_NODE_REF) {
+ last_prop_name = NULL;
+
+ p = get_structured_tag_data(tag, p, &d, &sz);
+ s = d;
+
+ printf("%*s&%s {\n", depth * shift, "", s);
+
+ depth++;
+ continue;
+ }
+
if (tag == FDT_EXPORT_SYM) {
p = get_structured_tag_data(tag, p, &d, &sz);
val32 = fdt32_to_cpu(GET_CELL(d));
diff --git a/flattree.c b/flattree.c
index bd6974b8..b5223089 100644
--- a/flattree.c
+++ b/flattree.c
@@ -17,6 +17,7 @@
#define FTF_LCVERSW 0x100
#define FTF_PROPDATA_PHANDLE 0x200
#define FTF_EXPORT_IMPORT_SYM 0x400
+#define FTF_ORPHAN 0x800
static struct version_info {
int version;
@@ -40,7 +41,7 @@ static struct version_info {
FTF_LCVERSW},
{20, 18, 18, FDT_V20_SIZE,
FTF_BOOTCPUID|FTF_STRTABSIZE|FTF_STRUCTSIZE|FTF_NOPS|FTF_DTFLAGS|
- FTF_LCVERSW|FTF_PROPDATA_PHANDLE|FTF_EXPORT_IMPORT_SYM},
+ FTF_LCVERSW|FTF_PROPDATA_PHANDLE|FTF_EXPORT_IMPORT_SYM|FTF_ORPHAN},
};
struct emitter {
@@ -56,6 +57,7 @@ struct emitter {
void (*export_sym)(void *);
void (*export_sym_ref)(void *);
void (*import_sym)(void *);
+ void (*beginnode_ref)(void *, struct label *labels);
};
static void bin_emit_cell(void *e, cell_t val)
@@ -130,6 +132,11 @@ static void bin_emit_import_sym(void *e)
bin_emit_cell(e, FDT_IMPORT_SYM);
}
+static void bin_emit_beginnode_ref(void *e, struct label *labels)
+{
+ bin_emit_cell(e, FDT_BEGIN_NODE_REF);
+}
+
static struct emitter bin_emitter = {
.cell = bin_emit_cell,
.string = bin_emit_string,
@@ -143,6 +150,7 @@ static struct emitter bin_emitter = {
.export_sym = bin_emit_export_sym,
.export_sym_ref = bin_emit_export_sym_ref,
.import_sym = bin_emit_import_sym,
+ .beginnode_ref = bin_emit_beginnode_ref,
};
static void emit_label(FILE *f, const char *prefix, const char *label)
@@ -297,6 +305,18 @@ static void asm_emit_import_sym(void *e)
die("FDT_IMPORT_SYM not supported in asm output\n");
}
+static void asm_emit_beginnode_ref(void *e, struct label *labels)
+{
+ /*
+ * Orphan nodes (FDT_BEGIN_NODE_REF tags) 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_BEGIN_NODE_REF not supported in asm output\n");
+}
+
static struct emitter asm_emitter = {
.cell = asm_emit_cell,
.string = asm_emit_string,
@@ -310,6 +330,7 @@ static struct emitter asm_emitter = {
.export_sym = asm_emit_export_sym,
.export_sym_ref = asm_emit_export_sym_ref,
.import_sym = asm_emit_import_sym,
+ .beginnode_ref = asm_emit_beginnode_ref,
};
static int stringtable_insert(struct data *d, const char *str)
@@ -340,13 +361,18 @@ static void flatten_tree(struct node *tree, struct emitter *emit,
if (tree->deleted)
return;
- emit->beginnode(etarget, tree->labels);
-
- if (vi->flags & FTF_FULLPATH)
- emit->string(etarget, tree->fullpath, 0);
- else
- emit->string(etarget, tree->name, 0);
+ if ((vi->flags & FTF_ORPHAN) && tree->ref) {
+ emit->beginnode_ref(etarget, tree->labels);
+ emit->cell(etarget, strlen(tree->ref) + 1);
+ emit->string(etarget, tree->ref, 0);
+ } else {
+ emit->beginnode(etarget, tree->labels);
+ if (vi->flags & FTF_FULLPATH)
+ emit->string(etarget, tree->fullpath, 0);
+ else
+ emit->string(etarget, tree->name, 0);
+ }
emit->align(etarget, sizeof(cell_t));
if (vi->flags & FTF_EXPORT_IMPORT_SYM) {
@@ -510,6 +536,22 @@ static void flatten_imports(struct symbol *importsymlist, struct emitter *emit,
}
}
+static void flatten_orphans(struct node *orphanlist, struct emitter *emit,
+ void *etarget, struct data *strbuf,
+ struct version_info *vi, uint32_t dt_flags)
+{
+ struct node *orphan;
+
+ if (!(vi->flags & FTF_ORPHAN))
+ return;
+
+ if (!(dt_flags & FDT_FLAG_ADDON) && orphanlist)
+ die("Only addons can have orphan nodes\n");
+
+ for_each_orphan(orphanlist, orphan)
+ flatten_tree(orphan, emit, etarget, strbuf, vi);
+}
+
void dt_to_blob(FILE *f, struct dt_info *dti, int version)
{
struct version_info *vi = NULL;
@@ -533,6 +575,9 @@ void dt_to_blob(FILE *f, struct dt_info *dti, int version)
flatten_tree(dti->dt, &bin_emitter, &dtbuf, &strbuf, vi);
flatten_imports(dti->importsymlist, &bin_emitter, &dtbuf, vi, dt_flags);
+ flatten_orphans(dti->orphanlist, &bin_emitter, &dtbuf, &strbuf, vi,
+ dt_flags);
+
bin_emit_cell(&dtbuf, FDT_END);
reservebuf = flatten_reserve_list(dti->reservelist, vi);
@@ -626,6 +671,9 @@ void dt_to_asm(FILE *f, struct dt_info *dti, int version)
if (dti->importsymlist)
die("Import symbols not supported in asm format\n");
+ if (dti->orphanlist)
+ die("Orphan nodes not supported in asm format\n");
+
for (i = 0; i < ARRAY_SIZE(version_table); i++) {
if (version_table[i].version == version)
@@ -850,6 +898,7 @@ static uint32_t flat_read_tag(struct inbuf *inb)
case FDT_END:
case FDT_PROPDATA_PHANDLE:
case FDT_PROPDATA_PHANDLE_REF:
+ case FDT_BEGIN_NODE_REF:
case FDT_EXPORT_SYM:
case FDT_EXPORT_SYM_REF:
case FDT_IMPORT_SYM:
@@ -1013,7 +1062,8 @@ static const char *nodename_from_path(const char *ppath, const char *cpath)
static struct node *unflatten_tree(struct inbuf *dtbuf,
struct inbuf *strbuf,
- const char *parent_flatname, int flags)
+ const char *parent_flatname, int flags,
+ bool is_orphan_node)
{
struct property *prop = NULL;
struct node *node;
@@ -1026,14 +1076,22 @@ static struct node *unflatten_tree(struct inbuf *dtbuf,
const char *str2;
node = build_node(NULL, NULL, NULL, NULL);
-
- flatname = flat_read_string(dtbuf);
-
- if (flags & FTF_FULLPATH)
- node->name = xstrdup(nodename_from_path(parent_flatname,
- flatname));
- else
- node->name = xstrdup(flatname);
+ if (is_orphan_node) {
+ len = flat_read_word(dtbuf);
+ flat_read_subbuf(dtbuf, &subbuf, len);
+ flat_realign(dtbuf, sizeof(uint32_t));
+
+ str = flat_read_string_norealign(&subbuf);
+ orphan_node(node, str);
+ flatname = "";
+ } else {
+ flatname = flat_read_string(dtbuf);
+ if (flags & FTF_FULLPATH)
+ node->name = xstrdup(nodename_from_path(parent_flatname,
+ flatname));
+ else
+ node->name = xstrdup(flatname);
+ }
do {
struct symbol *exportsym;
@@ -1053,7 +1111,7 @@ static struct node *unflatten_tree(struct inbuf *dtbuf,
case FDT_BEGIN_NODE:
prop = NULL;
- child = unflatten_tree(dtbuf,strbuf, flatname, flags);
+ child = unflatten_tree(dtbuf, strbuf, flatname, flags, false);
add_child(node, child);
break;
@@ -1151,6 +1209,11 @@ static struct node *unflatten_tree(struct inbuf *dtbuf,
add_symbol(&node->exportsymlist, exportsym);
break;
+ case FDT_BEGIN_NODE_REF:
+ /* FDT_BEGIN_NODE_REF can only be at the FDT top level. */
+ die("Unexpected FDT_BEGIN_NODE_REF tag\n");
+ break;
+
default:
die("Invalid opcode word %08x in device tree blob\n",
val);
@@ -1178,6 +1241,8 @@ struct dt_info *dt_from_blob(const char *fname)
struct node *tree;
struct symbol *importsymlist = NULL;
struct symbol *importsym;
+ struct node *orphanlist = NULL;
+ struct node *orphan;
uint32_t val;
int flags = 0;
unsigned int dtsflags = DTSF_V1;
@@ -1276,7 +1341,7 @@ struct dt_info *dt_from_blob(const char *fname)
flags |= FTF_DTFLAGS | FTF_LCVERSW;
if (version >= 20) {
- flags |= FTF_PROPDATA_PHANDLE | FTF_EXPORT_IMPORT_SYM;
+ flags |= FTF_PROPDATA_PHANDLE | FTF_EXPORT_IMPORT_SYM | FTF_ORPHAN;
dtsflags |= fdt32_to_cpu(fdt->dt_flags) & FDT_FLAG_ADDON ?
DTSF_ADDON : 0;
}
@@ -1291,7 +1356,7 @@ struct dt_info *dt_from_blob(const char *fname)
if (val != FDT_BEGIN_NODE)
die("Device tree blob doesn't begin with FDT_BEGIN_NODE (begins with 0x%08x)\n", val);
- tree = unflatten_tree(&dtbuf, &strbuf, "", flags);
+ tree = unflatten_tree(&dtbuf, &strbuf, "", flags, false);
val = flat_read_tag(&dtbuf);
@@ -1303,6 +1368,15 @@ struct dt_info *dt_from_blob(const char *fname)
val = flat_read_tag(&dtbuf);
}
}
+
+ if (flags & FTF_ORPHAN) {
+ while (val == FDT_BEGIN_NODE_REF) {
+ orphan = unflatten_tree(&dtbuf, &strbuf, "", flags,
+ true);
+ addon_add_orphan_node(&orphanlist, orphan);
+ val = flat_read_tag(&dtbuf);
+ }
+ }
}
if (val != FDT_END)
@@ -1316,5 +1390,5 @@ struct dt_info *dt_from_blob(const char *fname)
dtsflags |= DTSF_PLUGIN;
return build_dt_info(dtsflags, reservelist, tree, boot_cpuid_phys,
- importsymlist, NULL);
+ importsymlist, orphanlist);
}
diff --git a/libfdt/fdt.c b/libfdt/fdt.c
index b48c3248..f8dd0862 100644
--- a/libfdt/fdt.c
+++ b/libfdt/fdt.c
@@ -308,6 +308,27 @@ static uint32_t fdt_next_tag_all(const void *fdt, int startoffset, int *nextoffs
offset = sum;
break;
+ case FDT_BEGIN_NODE_REF:
+ tmp_offset = fdt_get_structured_tag_data(tag, fdt, offset, &len);
+ if (tmp_offset < 0)
+ return FDT_END; /* premature end */
+
+ sum = tmp_offset + len;
+
+ /* Check ref */
+ do {
+ p = fdt_offset_ptr(fdt, tmp_offset++, 1);
+ } while (p && (*p != '\0'));
+ if (!can_assume(VALID_DTB) && !p)
+ return FDT_END; /* premature end */
+
+ if (!can_assume(VALID_INPUT) && (uint32_t)tmp_offset > sum)
+ return FDT_END; /* premature end */
+
+ /* Skip the whole data bloc */
+ offset = sum;
+ break;
+
case FDT_EXPORT_SYM:
tmp_offset = fdt_get_structured_tag_data(tag, fdt, offset, &len);
if (tmp_offset < 0)
@@ -424,6 +445,7 @@ static bool fdt_tag_is_unknown(uint32_t tag)
case FDT_EXPORT_SYM:
case FDT_EXPORT_SYM_REF:
case FDT_IMPORT_SYM:
+ case FDT_BEGIN_NODE_REF:
return false;
default:
break;
@@ -484,6 +506,7 @@ bool fdt_tag_filter_skip_metadata(void *data, uint32_t tag)
case FDT_IMPORT_SYM:
return true;
+ case FDT_BEGIN_NODE_REF: /* Not metadata but rather node definition */
default:
break;
}
@@ -506,11 +529,15 @@ bool fdt_tag_is_property_metadata(uint32_t tag)
int fdt_check_node_offset_(const void *fdt, int offset)
{
+ uint32_t tag;
+
if (!can_assume(VALID_INPUT)
&& ((offset < 0) || (offset % FDT_TAGSIZE)))
return -FDT_ERR_BADOFFSET;
- if (fdt_next_tag(fdt, offset, &offset) != FDT_BEGIN_NODE)
+
+ tag = fdt_next_tag(fdt, offset, &offset);
+ if (tag != FDT_BEGIN_NODE && tag != FDT_BEGIN_NODE_REF)
return -FDT_ERR_BADOFFSET;
return offset;
@@ -546,6 +573,10 @@ int fdt_root_offset(const void *fdt)
case FDT_BEGIN_NODE:
return offset;
+ case FDT_BEGIN_NODE_REF:
+ /* An orphan node cannot be considered as a "first" node */
+ return -FDT_ERR_NOTFOUND;
+
default:
break;
}
@@ -587,6 +618,10 @@ int fdt_next_node(const void *fdt, int offset, int *depth)
(*depth)++;
break;
+ case FDT_BEGIN_NODE_REF:
+ /* An orphan node cannot be found using fdt_next_node() */
+ return -FDT_ERR_BADSTRUCTURE;
+
case FDT_END_NODE:
if (depth && ((--(*depth)) < 0))
return nextoffset;
@@ -632,6 +667,64 @@ int fdt_next_subnode(const void *fdt, int offset)
return offset;
}
+int fdt_check_orphan_offset(const void *fdt, int offset, int *data_offset)
+{
+ int nextoffset;
+ uint32_t tag;
+ int d;
+
+ tag = fdt_next_tag(fdt, offset, &nextoffset);
+ switch (tag) {
+ case FDT_BEGIN_NODE_REF:
+ if (data_offset) {
+ d = fdt_get_structured_tag_data(tag, fdt,
+ offset + FDT_TAGSIZE,
+ NULL);
+ if (d < 0)
+ return d;
+ *data_offset = d;
+ }
+ break;
+ default:
+ return -FDT_ERR_BADOFFSET;
+ }
+
+ return nextoffset;
+}
+
+int fdt_next_orphan_offset(const void *fdt, int offset)
+{
+ int nextoffset = 0;
+ uint32_t tag;
+
+ if (offset < 0)
+ return offset;
+
+ if (offset != 0) {
+ nextoffset = fdt_check_orphan_offset(fdt, offset, NULL);
+ if (nextoffset < 0)
+ return nextoffset;
+ }
+
+ do {
+ offset = nextoffset;
+ tag = fdt_next_tag(fdt, offset, &nextoffset);
+
+ switch (tag) {
+ case FDT_BEGIN_NODE_REF:
+ return offset;
+
+ case FDT_END:
+ if (nextoffset < 0)
+ return nextoffset;
+
+ return -FDT_ERR_NOTFOUND;
+ }
+ } while (tag != FDT_BEGIN_NODE_REF);
+
+ return offset;
+}
+
const char *fdt_find_string_len_(const char *strtab, int tabsize, const char *s,
int slen)
{
diff --git a/libfdt/fdt.h b/libfdt/fdt.h
index 04f47784..52f5fd5f 100644
--- a/libfdt/fdt.h
+++ b/libfdt/fdt.h
@@ -154,6 +154,24 @@ struct fdt_property {
*/
#define FDT_IMPORT_SYM FDT_TAG_CAN_SKIP(FDT_TAG_DATA_VARLEN, 0x2100)
+/*
+ * Node reference tag. varlen: node symbol name
+ *
+ * It defines a node external to the dtb but modified by the dtb. It can be
+ * present in an addon dtb.
+ *
+ * For instance, if an addon adds a sub-node to a node existing in the
+ * device-tree the addon is applied to, the existing node needs to be
+ * referenced by the addon dtb. This is the purpose of FDT_BEGIN_NODE_REF.
+ *
+ * node symbol name: The symbol name used to reference the node in the base
+ * device-tree when the addon is applied.
+ *
+ * Following the tag and its related node symbol name, the node structure is
+ * present. After the node structure, the FTD_END_NODE tag is present.
+ */
+#define FDT_BEGIN_NODE_REF FDT_TAG_NO_SKIP(FDT_TAG_DATA_VARLEN, 0x2200)
+
#define FDT_V1_SIZE (7*sizeof(fdt32_t))
#define FDT_V2_SIZE (FDT_V1_SIZE + sizeof(fdt32_t))
#define FDT_V3_SIZE (FDT_V2_SIZE + sizeof(fdt32_t))
diff --git a/libfdt/fdt_check.c b/libfdt/fdt_check.c
index 7ce3fdd2..d2a959c0 100644
--- a/libfdt/fdt_check.c
+++ b/libfdt/fdt_check.c
@@ -23,6 +23,7 @@ int fdt_check_full(const void *fdt, size_t bufsize)
bool root_node_found = false;
bool subnode_found = false;
bool import_sym_found = false;
+ bool orphan_found = false;
const fdt32_t *pfdt32;
const char *str;
uint32_t val;
@@ -80,6 +81,10 @@ int fdt_check_full(const void *fdt, size_t bufsize)
if (import_sym_found)
return -FDT_ERR_BADSTRUCTURE;
+ /* Orphan nodes must be after the root node */
+ if (orphan_found)
+ return -FDT_ERR_BADSTRUCTURE;
+
/* The root node must have an empty name */
name = fdt_get_name(fdt, offset, &len);
if (!name)
@@ -133,7 +138,10 @@ int fdt_check_full(const void *fdt, size_t bufsize)
val = fdt32_ld(pfdt32);
/* Check the phandle value */
- tmp = fdt_node_offset_by_phandle(fdt, val);
+ if (!(fdt_dt_flags(fdt) & FDT_FLAG_ADDON))
+ tmp = fdt_node_offset_by_phandle(fdt, val);
+ else
+ tmp = fdta_node_offset_by_phandle(fdt, val);
if (tmp < 0)
return tmp;
break;
@@ -192,7 +200,10 @@ int fdt_check_full(const void *fdt, size_t bufsize)
val = fdt32_ld(pfdt32);
/* Check the phandle value */
- tmp = fdt_node_offset_by_phandle(fdt, val);
+ if (!(fdt_dt_flags(fdt) & FDT_FLAG_ADDON))
+ tmp = fdt_node_offset_by_phandle(fdt, val);
+ else
+ tmp = fdta_node_offset_by_phandle(fdt, val);
if (tmp < 0)
return tmp;
@@ -249,6 +260,10 @@ int fdt_check_full(const void *fdt, size_t bufsize)
if (!(fdt_dt_flags(fdt) & FDT_FLAG_ADDON))
return -FDT_ERR_BADVALUE;
+ /* Orphans must be after import symbols */
+ if (orphan_found)
+ return -FDT_ERR_BADSTRUCTURE;
+
import_sym_found = true;
d = fdt_get_structured_tag_data(tag, fdt,
@@ -279,6 +294,34 @@ int fdt_check_full(const void *fdt, size_t bufsize)
break;
+ case FDT_BEGIN_NODE_REF:
+ if (!(fdt_dt_flags(fdt) & FDT_FLAG_ADDON))
+ return -FDT_ERR_BADVALUE;
+
+ prop = NULL;
+ depth++;
+
+ /* Orphan nodes can only be present as top level nodes */
+ if (depth != 1)
+ return -FDT_ERR_BADSTRUCTURE;
+
+ orphan_found = true;
+
+ d = fdt_get_structured_tag_data(tag, fdt,
+ offset + FDT_TAGSIZE, NULL);
+ if (d < 0)
+ return d;
+
+ /* Retrieve the reference */
+ str = fdt_offset_ptr(fdt, d, 1);
+ if (!str)
+ return -FDT_ERR_BADSTRUCTURE;
+
+ /* Check that the reference is not an empty string */
+ if (!strlen(str))
+ return -FDT_ERR_BADVALUE;
+ break;
+
default:
return -FDT_ERR_INTERNAL;
}
diff --git a/libfdt/fdt_ro.c b/libfdt/fdt_ro.c
index ea6a540d..917ecc33 100644
--- a/libfdt/fdt_ro.c
+++ b/libfdt/fdt_ro.c
@@ -317,6 +317,8 @@ const char *fdt_get_name(const void *fdt, int nodeoffset, int *len)
{
const struct fdt_node_header *nh;
const char *nameptr;
+ int nextoffset;
+ uint32_t tag;
int err;
if (!nodeoffset) {
@@ -332,6 +334,18 @@ const char *fdt_get_name(const void *fdt, int nodeoffset, int *len)
|| ((err = fdt_check_node_offset_(fdt, nodeoffset)) < 0)))
goto fail;
+ /*
+ * fdt_check_node_offset_() considers FDT_BEGIN_NODE_REF as a node.
+ * A FDT_BEGIN_NODE_REF tag, which represents an orphan node, doesn't
+ * have any 'name' attached but only a reference to the external node
+ * the orphan node refers to. No way to get a name.
+ */
+ tag = fdt_next_tag(fdt, nodeoffset, &nextoffset);
+ if (tag == FDT_BEGIN_NODE_REF) {
+ err = FDT_ERR_BADOFFSET;
+ goto fail;
+ }
+
nh = fdt_offset_ptr_(fdt, nodeoffset);
nameptr = nh->name;
@@ -760,7 +774,7 @@ int fdt_node_offset_by_prop_value(const void *fdt, int startoffset,
return offset; /* error from fdt_next_node() */
}
-int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle)
+static int fdt_node_offset_by_phandle_(const void *fdt, int start_node_offset, uint32_t phandle)
{
int offset;
@@ -775,7 +789,7 @@ int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle)
* we want, we scan over them again making our way to the next
* node. Still it's the easiest to implement approach;
* performance can come later. */
- for (offset = fdt_next_node(fdt, -1, NULL);
+ for (offset = fdt_next_node(fdt, start_node_offset, NULL);
offset >= 0;
offset = fdt_next_node(fdt, offset, NULL)) {
if (fdt_get_phandle(fdt, offset) == phandle)
@@ -785,6 +799,34 @@ int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle)
return offset; /* error from fdt_next_node() */
}
+int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle)
+{
+ return fdt_node_offset_by_phandle_(fdt, -1, phandle);
+}
+
+int fdta_node_offset_by_phandle(const void *fdta, uint32_t phandle)
+{
+ int orphan;
+ int node;
+
+ /* Look at root node tree */
+ node = fdt_node_offset_by_phandle_(fdta, -1, phandle);
+ if (node >= 0)
+ return node;
+
+ /* Look at orphans and their trees */
+ fdt_for_each_orphan(orphan, fdta) {
+ if (fdt_get_phandle(fdta, orphan) == phandle)
+ return orphan;
+
+ node = fdt_node_offset_by_phandle_(fdta, orphan, phandle);
+ if (node >= 0)
+ return node;
+ }
+
+ return -FDT_ERR_NOTFOUND;
+}
+
int fdt_stringlist_contains(const char *strlist, int listlen, const char *str)
{
int len = strlen(str);
diff --git a/libfdt/libfdt_internal.h b/libfdt/libfdt_internal.h
index 7ef1d995..be47639d 100644
--- a/libfdt/libfdt_internal.h
+++ b/libfdt/libfdt_internal.h
@@ -61,6 +61,16 @@ static inline const char *fdt_find_string_(const char *strtab, int tabsize,
int fdt_node_end_offset_(void *fdt, int nodeoffset);
+int fdt_check_orphan_offset(const void *fdt, int offset, int *data_offset);
+int fdt_next_orphan_offset(const void *fdt, int offset);
+
+#define fdt_for_each_orphan(orphan, fdt) \
+ for (orphan = fdt_next_orphan_offset(fdt, 0); \
+ orphan >= 0; \
+ orphan = fdt_next_orphan_offset(fdt, orphan))
+
+int fdta_node_offset_by_phandle(const void *fdta, uint32_t phandle);
+
static inline const void *fdt_offset_ptr_(const void *fdt, int offset)
{
return (const char *)fdt + fdt_off_dt_struct(fdt) + offset;
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 52/74] tests: metadata: Add basic test for addon orphan nodes
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (50 preceding siblings ...)
2026-08-26 9:49 ` [RFC PATCH v2 51/74] Add support for FDT_BEGIN_NODE_REF dtb tag Herve Codina
@ 2026-08-26 9:49 ` Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 53/74] dtc: Add support for missing root node in addon device-tree Herve Codina
` (21 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:49 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
Add tests related to addon orphan nodes (FDT_BEGIN_NODE_REF dtb tag).
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
tests/metadata_addon_orphan1.dtb.dts.expect | 16 ++++++++++++++++
tests/metadata_addon_orphan1.dtb.expect | 13 +++++++++++++
tests/metadata_addon_orphan1.dts | 19 +++++++++++++++++++
tests/metadata_addon_orphan1.dts.dts.expect | 16 ++++++++++++++++
tests/run_tests.sh | 3 ++-
5 files changed, 66 insertions(+), 1 deletion(-)
create mode 100644 tests/metadata_addon_orphan1.dtb.dts.expect
create mode 100644 tests/metadata_addon_orphan1.dtb.expect
create mode 100644 tests/metadata_addon_orphan1.dts
create mode 100644 tests/metadata_addon_orphan1.dts.dts.expect
diff --git a/tests/metadata_addon_orphan1.dtb.dts.expect b/tests/metadata_addon_orphan1.dtb.dts.expect
new file mode 100644
index 00000000..c24a00f1
--- /dev/null
+++ b/tests/metadata_addon_orphan1.dtb.dts.expect
@@ -0,0 +1,16 @@
+/dts-v1/;
+/addon/;
+
+/ {
+
+ node-a {
+ prop = <0x01>;
+ };
+};
+
+&base {
+
+ addon-node {
+ prop = <0x02>;
+ };
+};
diff --git a/tests/metadata_addon_orphan1.dtb.expect b/tests/metadata_addon_orphan1.dtb.expect
new file mode 100644
index 00000000..7058a2e0
--- /dev/null
+++ b/tests/metadata_addon_orphan1.dtb.expect
@@ -0,0 +1,13 @@
+/dts-v1/;
+/addon/;
+
+/ {
+ node-a {
+ prop = <0x00000001>;
+ };
+};
+&base {
+ addon-node {
+ prop = <0x00000002>;
+ };
+};
diff --git a/tests/metadata_addon_orphan1.dts b/tests/metadata_addon_orphan1.dts
new file mode 100644
index 00000000..f20d1791
--- /dev/null
+++ b/tests/metadata_addon_orphan1.dts
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
+/*
+ * Copyright (C) 2026 Bootlin
+ */
+
+/dts-v1/;
+/addon/;
+
+/ {
+ node-a {
+ prop = <1>;
+ };
+};
+
+&base {
+ addon-node {
+ prop = <2>;
+ };
+};
diff --git a/tests/metadata_addon_orphan1.dts.dts.expect b/tests/metadata_addon_orphan1.dts.dts.expect
new file mode 100644
index 00000000..c24a00f1
--- /dev/null
+++ b/tests/metadata_addon_orphan1.dts.dts.expect
@@ -0,0 +1,16 @@
+/dts-v1/;
+/addon/;
+
+/ {
+
+ node-a {
+ prop = <0x01>;
+ };
+};
+
+&base {
+
+ addon-node {
+ prop = <0x02>;
+ };
+};
diff --git a/tests/run_tests.sh b/tests/run_tests.sh
index 7c45835e..0ce60c3f 100755
--- a/tests/run_tests.sh
+++ b/tests/run_tests.sh
@@ -1250,7 +1250,8 @@ fdtoverlay_tests() {
metadata_tests() {
for dt in metadata_propdata_phandle_local metadata_propdata_phandle_ref \
metadata_addon_base metadata_exportsyms_local \
- metadata_exportsyms_ref metadata_importsyms; do
+ metadata_exportsyms_ref metadata_importsyms \
+ metadata_addon_orphan1; do
run_dtc_test -I dts -O dts -o $dt.dts.dts "$SRCDIR/$dt.dts"
base_run_test check_diff $dt.dts.dts "$SRCDIR/$dt.dts.dts.expect"
run_dtc_test -I dts -O dtb -o $dt.dtb "$SRCDIR/$dt.dts"
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 53/74] dtc: Add support for missing root node in addon device-tree
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (51 preceding siblings ...)
2026-08-26 9:49 ` [RFC PATCH v2 52/74] tests: metadata: Add basic test for addon orphan nodes Herve Codina
@ 2026-08-26 9:49 ` Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 54/74] tests: metadata: Add a test for addon without root node Herve Codina
` (20 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:49 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
Addon can have orphan nodes fully described (dts) and encoded (dtb)
without the need for a root device tree.
Contrary to plugin (overlay), addon doesn't need any specific root tree
structure to described the orphans. Indeed, /fragment@n/__overlay__
doesn't exist for addons. Orphans are encoded in dtb using the
FDT_BEGIN_NODE_REF tag without any impact on the root tree.
The following snippet is fully legit for addon:
--- 8< ---
/addon/;
&foo {
subnode {
prop = <1>;
};
}
--- 8< ---
This snippet doesn't contains a root device tree, and the related dtb
doesn't need any root device tree to encode the snippet.
The root device is no more mandatory for addon.
Add support for addon without a root device-tree.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
checks.c | 29 +++++++++++++++++++++++++++--
dtc-parser.y | 18 ++++++++++++++----
dtc.c | 21 ++++++++++++++++-----
flattree.c | 43 ++++++++++++++++++++++++++++++-------------
livetree.c | 13 +++++++++++--
treesource.c | 3 ++-
6 files changed, 100 insertions(+), 27 deletions(-)
diff --git a/checks.c b/checks.c
index a0024389..fee37ce0 100644
--- a/checks.c
+++ b/checks.c
@@ -154,7 +154,6 @@ static bool is_multiple_of(int multiple, int divisor)
static bool run_check(struct check *c, struct dt_info *dti)
{
- struct node *dt = dti->dt;
struct node *orphan;
bool error = false;
int i;
@@ -179,7 +178,8 @@ static bool run_check(struct check *c, struct dt_info *dti)
if (c->status != UNCHECKED)
goto out;
- check_nodes_props(c, dti, dt);
+ if (dti->dt)
+ check_nodes_props(c, dti, dti->dt);
for_each_orphan(dti->orphanlist, orphan)
check_nodes_props(c, dti, orphan);
@@ -2105,6 +2105,31 @@ void process_checks(bool force, struct dt_info *dti)
exit(2);
}
+ if (!dti->dt) {
+ /* No root node is only allowed for addons */
+ if (dti->dtsflags & DTSF_ADDON) {
+ if (!dti->orphanlist) {
+ /*
+ * but addons without a root node and without
+ * orphan nodes is really incorrect
+ */
+ fprintf(stderr,
+ "ERROR: Input tree has no root or orphan nodes, aborting\n");
+ exit(2);
+ }
+ } else {
+ fprintf(stderr, "ERROR: Input tree has no root node, aborting\n");
+ exit(2);
+ }
+ }
+
+ if (!(dti->dtsflags & DTSF_ADDON)) {
+ if (dti->orphanlist) {
+ fprintf(stderr, "ERROR: Input tree has orphan nodes, aborting\n");
+ exit(2);
+ }
+ }
+
for (i = 0; i < ARRAY_SIZE(check_table); i++) {
struct check *c = check_table[i];
diff --git a/dtc-parser.y b/dtc-parser.y
index 197a9533..8d43cc4e 100644
--- a/dtc-parser.y
+++ b/dtc-parser.y
@@ -133,12 +133,18 @@ static struct node *parser_get_node_by_ref(struct node *dt, struct node *orphanl
sourcefile:
headers memreserves devicetree
{
+ if (!($1 & DTSF_ADDON) && !$3)
+ ERROR(&@3, "Only addon support missing root node");
+
parser_output = build_dt_info($1, $2, $3,
- guess_boot_cpuid($3),
+ $3 ? guess_boot_cpuid($3) : 0,
NULL, parser_orphanlist);
}
| headers memreserves importlist devicetree
{
+ if (!($1 & DTSF_ADDON) && !$4)
+ ERROR(&@4, "Only addon support missing root node");
+
/*
* importlist is created with chain_symbol() and so it
* is created in reverse order. Reverse it now to have
@@ -241,7 +247,10 @@ devicetree:
}
| devicetree '/' nodedef
{
- $$ = merge_nodes($1, $3);
+ if ($1)
+ $$ = merge_nodes($1, $3);
+ else
+ $$ = name_node($3, "");
}
| dt_ref nodedef
{
@@ -256,8 +265,9 @@ devicetree:
""),
$2, $1);
} else {
- ERROR(&@2, "Orphan node %s without a root node not yet supported", $1);
- YYERROR;
+ addon_add_orphan_node(&parser_orphanlist,
+ orphan_node($2, $1));
+ $$ = NULL;
}
}
| devicetree DT_LABEL dt_ref nodedef
diff --git a/dtc.c b/dtc.c
index e5068375..d0f8998e 100644
--- a/dtc.c
+++ b/dtc.c
@@ -316,18 +316,29 @@ int main(int argc, char *argv[])
process_checks(force, dti);
- if (auto_label_aliases)
+ if (auto_label_aliases) {
+ if (!dti->dt)
+ die("auto-alias not supported without a root node\n");
generate_label_tree(dti, "aliases", false);
+ }
- generate_labels_from_tree(dti, "__symbols__");
+ if (dti->dt)
+ generate_labels_from_tree(dti, "__symbols__");
- if (generate_symbols)
+ if (generate_symbols) {
+ if (!dti->dt)
+ die("generation of symbols not supported without a root node\n");
generate_label_tree(dti, "__symbols__", true);
+ }
- fixup_phandles(dti, "__fixups__");
- local_fixup_phandles(dti, "__local_fixups__");
+ if (dti->dt) {
+ fixup_phandles(dti, "__fixups__");
+ local_fixup_phandles(dti, "__local_fixups__");
+ }
if (generate_fixups) {
+ if (!dti->dt)
+ die("generation of fixups not supported without a root node\n");
generate_fixups_tree(dti, "__fixups__");
generate_local_fixups_tree(dti, "__local_fixups__");
}
diff --git a/flattree.c b/flattree.c
index b5223089..f680dec1 100644
--- a/flattree.c
+++ b/flattree.c
@@ -573,7 +573,9 @@ 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);
+ if (dti->dt)
+ flatten_tree(dti->dt, &bin_emitter, &dtbuf, &strbuf, vi);
+
flatten_imports(dti->importsymlist, &bin_emitter, &dtbuf, vi, dt_flags);
flatten_orphans(dti->orphanlist, &bin_emitter, &dtbuf, &strbuf, vi,
dt_flags);
@@ -769,7 +771,9 @@ void dt_to_asm(FILE *f, struct dt_info *dti, int version)
fprintf(f, "\t.long\t0, 0\n\t.long\t0, 0\n");
emit_label(f, symprefix, "struct_start");
- flatten_tree(dti->dt, &asm_emitter, f, &strbuf, vi);
+
+ if (dti->dt)
+ flatten_tree(dti->dt, &asm_emitter, f, &strbuf, vi);
fprintf(f, "\t/* FDT_END */\n");
asm_emit_cell(f, FDT_END);
@@ -1238,7 +1242,7 @@ struct dt_info *dt_from_blob(const char *fname)
struct inbuf memresvbuf;
int sizeleft;
struct reserve_info *reservelist;
- struct node *tree;
+ struct node *tree = NULL;
struct symbol *importsymlist = NULL;
struct symbol *importsym;
struct node *orphanlist = NULL;
@@ -1352,15 +1356,24 @@ struct dt_info *dt_from_blob(const char *fname)
reservelist = flat_read_mem_reserve(&memresvbuf);
- val = flat_read_tag(&dtbuf);
- if (val != FDT_BEGIN_NODE)
- die("Device tree blob doesn't begin with FDT_BEGIN_NODE (begins with 0x%08x)\n", val);
+ if (!(dtsflags & DTSF_ADDON)) {
+ val = flat_read_tag(&dtbuf);
+ if (val != FDT_BEGIN_NODE)
+ die("Device tree blob doesn't begin with FDT_BEGIN_NODE (begins with 0x%08x)\n",
+ val);
- tree = unflatten_tree(&dtbuf, &strbuf, "", flags, false);
+ tree = unflatten_tree(&dtbuf, &strbuf, "", flags, false);
- val = flat_read_tag(&dtbuf);
+ val = flat_read_tag(&dtbuf);
+ if (val != FDT_END)
+ die("Device tree blob doesn't end with FDT_END\n");
+ } else {
+ val = flat_read_tag(&dtbuf);
+ if (val == FDT_BEGIN_NODE) {
+ tree = unflatten_tree(&dtbuf, &strbuf, "", flags, false);
+ val = flat_read_tag(&dtbuf);
+ }
- if (dtsflags & DTSF_ADDON) {
if (flags & FTF_EXPORT_IMPORT_SYM) {
while (val == FDT_IMPORT_SYM) {
importsym = flat_read_importsym(&dtbuf);
@@ -1377,16 +1390,20 @@ struct dt_info *dt_from_blob(const char *fname)
val = flat_read_tag(&dtbuf);
}
}
- }
- if (val != FDT_END)
- die("Device tree blob doesn't end with FDT_END\n");
+ if (!tree && !orphanlist)
+ die("Device tree blob has 0x%08x tag instead of FDT_BEGIN_NODE or FDT_BEGIN_NODE_REF\n",
+ val);
+
+ if (val != FDT_END)
+ die("Device tree blob doesn't end with FDT_END\n");
+ }
free(blob);
fclose(f);
- if (get_subnode(tree, "__fixups__") || get_subnode(tree, "__local_fixups__"))
+ if (tree && (get_subnode(tree, "__fixups__") || get_subnode(tree, "__local_fixups__")))
dtsflags |= DTSF_PLUGIN;
return build_dt_info(dtsflags, reservelist, tree, boot_cpuid_phys,
diff --git a/livetree.c b/livetree.c
index 26c3c079..f7c13585 100644
--- a/livetree.c
+++ b/livetree.c
@@ -692,7 +692,8 @@ static void complete_references(struct dt_info *dti)
{
struct node *orphan;
- complete_references_internal(dti, dti->dt);
+ if (dti->dt)
+ complete_references_internal(dti, dti->dt);
for_each_orphan(dti->orphanlist, orphan)
complete_references_internal(dti, orphan);
@@ -1336,7 +1337,9 @@ void sort_tree(struct dt_info *dti)
{
sort_reserve_entries(dti);
sort_importsyms(dti);
- sort_node(dti->dt);
+
+ if (dti->dt)
+ sort_node(dti->dt);
}
/* utility helper to avoid code duplication */
@@ -1605,6 +1608,8 @@ 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)
{
+ assert(dti->dt);
+
if (!any_label_tree(dti, dti->dt))
return;
generate_label_tree_internal(dti, build_root_node(dti->dt, name),
@@ -1613,6 +1618,8 @@ void generate_label_tree(struct dt_info *dti, const char *name, bool allocph)
void generate_fixups_tree(struct dt_info *dti, const char *name)
{
+ assert(dti->dt);
+
if (!any_fixup_tree(dti, dti->dt))
return;
if (generate_fixups_tree_internal(dti, build_root_node(dti->dt, name), dti->dt))
@@ -1719,6 +1726,8 @@ void fixup_phandles(struct dt_info *dti, const char *name)
void generate_local_fixups_tree(struct dt_info *dti, const char *name)
{
+ assert(dti->dt);
+
if (!any_local_fixup_tree(dti, dti->dt))
return;
if (generate_local_fixups_tree_internal(dti, build_root_node(dti->dt, name), dti->dt))
diff --git a/treesource.c b/treesource.c
index b50d9f81..d18025c5 100644
--- a/treesource.c
+++ b/treesource.c
@@ -466,7 +466,8 @@ void dt_to_source(FILE *f, struct dt_info *dti)
fprintf(f, "\n");
}
- write_tree_source_node(f, dti->dt, 0);
+ if (dti->dt)
+ write_tree_source_node(f, dti->dt, 0);
for_each_orphan(dti->orphanlist, orphan) {
fprintf(f, "\n");
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 54/74] tests: metadata: Add a test for addon without root node
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (52 preceding siblings ...)
2026-08-26 9:49 ` [RFC PATCH v2 53/74] dtc: Add support for missing root node in addon device-tree Herve Codina
@ 2026-08-26 9:49 ` Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 55/74] dtc: Allow parser_get_node_by_ref() to return an orphan node for merging purpose Herve Codina
` (19 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:49 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
The root device-tree node is no more mandatory for addons.
Indeed, addons can be composed of only orphan nodes without any needs
for the root device-tree node presence.
Add a test related to addon dts and dtb without root node.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
tests/metadata_addon_orphan2.dtb.dts.expect | 10 ++++++++++
tests/metadata_addon_orphan2.dtb.expect | 8 ++++++++
tests/metadata_addon_orphan2.dts | 13 +++++++++++++
tests/metadata_addon_orphan2.dts.dts.expect | 10 ++++++++++
tests/run_tests.sh | 2 +-
5 files changed, 42 insertions(+), 1 deletion(-)
create mode 100644 tests/metadata_addon_orphan2.dtb.dts.expect
create mode 100644 tests/metadata_addon_orphan2.dtb.expect
create mode 100644 tests/metadata_addon_orphan2.dts
create mode 100644 tests/metadata_addon_orphan2.dts.dts.expect
diff --git a/tests/metadata_addon_orphan2.dtb.dts.expect b/tests/metadata_addon_orphan2.dtb.dts.expect
new file mode 100644
index 00000000..32c1b1ed
--- /dev/null
+++ b/tests/metadata_addon_orphan2.dtb.dts.expect
@@ -0,0 +1,10 @@
+/dts-v1/;
+/addon/;
+
+
+&base {
+
+ addon-node {
+ prop = <0x02>;
+ };
+};
diff --git a/tests/metadata_addon_orphan2.dtb.expect b/tests/metadata_addon_orphan2.dtb.expect
new file mode 100644
index 00000000..46e67d06
--- /dev/null
+++ b/tests/metadata_addon_orphan2.dtb.expect
@@ -0,0 +1,8 @@
+/dts-v1/;
+/addon/;
+
+&base {
+ addon-node {
+ prop = <0x00000002>;
+ };
+};
diff --git a/tests/metadata_addon_orphan2.dts b/tests/metadata_addon_orphan2.dts
new file mode 100644
index 00000000..d4ff42f7
--- /dev/null
+++ b/tests/metadata_addon_orphan2.dts
@@ -0,0 +1,13 @@
+// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
+/*
+ * Copyright (C) 2026 Bootlin
+ */
+
+/dts-v1/;
+/addon/;
+
+&base {
+ addon-node {
+ prop = <2>;
+ };
+};
diff --git a/tests/metadata_addon_orphan2.dts.dts.expect b/tests/metadata_addon_orphan2.dts.dts.expect
new file mode 100644
index 00000000..32c1b1ed
--- /dev/null
+++ b/tests/metadata_addon_orphan2.dts.dts.expect
@@ -0,0 +1,10 @@
+/dts-v1/;
+/addon/;
+
+
+&base {
+
+ addon-node {
+ prop = <0x02>;
+ };
+};
diff --git a/tests/run_tests.sh b/tests/run_tests.sh
index 0ce60c3f..e099bd39 100755
--- a/tests/run_tests.sh
+++ b/tests/run_tests.sh
@@ -1251,7 +1251,7 @@ metadata_tests() {
for dt in metadata_propdata_phandle_local metadata_propdata_phandle_ref \
metadata_addon_base metadata_exportsyms_local \
metadata_exportsyms_ref metadata_importsyms \
- metadata_addon_orphan1; do
+ metadata_addon_orphan1 metadata_addon_orphan2; do
run_dtc_test -I dts -O dts -o $dt.dts.dts "$SRCDIR/$dt.dts"
base_run_test check_diff $dt.dts.dts "$SRCDIR/$dt.dts.dts.expect"
run_dtc_test -I dts -O dtb -o $dt.dtb "$SRCDIR/$dt.dts"
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 55/74] dtc: Allow parser_get_node_by_ref() to return an orphan node for merging purpose
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (53 preceding siblings ...)
2026-08-26 9:49 ` [RFC PATCH v2 54/74] tests: metadata: Add a test for addon without root node Herve Codina
@ 2026-08-26 9:49 ` Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 56/74] tests: metadata: Add a test related to orphan node merging Herve Codina
` (18 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:49 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
When the same orphan node is described multiple times in an addon dts,
those multiple descriptions should be merged.
For instance, in the following snippet:
--- 8< ---
/addon/;
&foo {
prop1 = <1>;
};
&foo {
prop2 = <2>;
};
--- 8< ---
The foo orphan node is described twice and should be merged in only one
instance:
--- 8< ---
&foo {
prop1 = <1>;
prop2 = <2>;
};
--- 8< ---
The current mecanisme used to find a node for merging is based on label.
Indeed, without orphan nodes, '&foo' is a reference to a node with the
label 'foo'. This node labeled foo is the node found for merging.
With addons and orphan nodes, '&foo' is not a reference to an existing
node. Indeed, this node is an orphan one because the node referenced by
foo is an external node (i.e. not available in the addon itself).
The label 'foo' doesn't exist. No node in the addon are defined with a
foo label attached.
Take this orphan nodes specificity into account in order to find a
possible orphan node candidate for merging.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
dtc-parser.y | 23 ++++++++++++++++++++++-
1 file changed, 22 insertions(+), 1 deletion(-)
diff --git a/dtc-parser.y b/dtc-parser.y
index 8d43cc4e..6bf6bb50 100644
--- a/dtc-parser.y
+++ b/dtc-parser.y
@@ -39,11 +39,32 @@ static struct node *parser_get_node_by_ref(struct node *dt, struct node *orphanl
* Use a temporary dt_info in order to use dti_get_node_by_ref()
*/
struct dt_info dti = {};
+ struct node *target;
+ struct node *orphan;
dti.dt = dt;
dti.orphanlist = orphanlist;
- return get_node_by_ref(&dti, ref);
+ target = get_node_by_ref(&dti, ref);
+ if (target)
+ return target;
+
+ /*
+ * No node were found by get_node_by_ref().
+ *
+ * parser_get_node_by_ref() is called by the parser only to get a node
+ * in order to perform a possible merge.
+ *
+ * The referenced used can be a label matching an orphan node. In this
+ * merge context, returning a matching orphan node makes sense even if
+ * no label are defined for the orphan node.
+ */
+ for_each_orphan(orphanlist, orphan) {
+ if (streq(orphan->ref, ref))
+ return orphan;
+ }
+
+ return NULL;
}
%}
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 56/74] tests: metadata: Add a test related to orphan node merging
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (54 preceding siblings ...)
2026-08-26 9:49 ` [RFC PATCH v2 55/74] dtc: Allow parser_get_node_by_ref() to return an orphan node for merging purpose Herve Codina
@ 2026-08-26 9:49 ` Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 57/74] dtc: Add support for orphan nodes sorting Herve Codina
` (17 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:49 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
Merging orphan node is supported.
Add a test for this feature.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
tests/metadata_addon_orphan3.dtb.dts.expect | 22 ++++++++++++++++
tests/metadata_addon_orphan3.dtb.expect | 17 +++++++++++++
tests/metadata_addon_orphan3.dts | 28 +++++++++++++++++++++
tests/metadata_addon_orphan3.dts.dts.expect | 22 ++++++++++++++++
tests/run_tests.sh | 3 ++-
5 files changed, 91 insertions(+), 1 deletion(-)
create mode 100644 tests/metadata_addon_orphan3.dtb.dts.expect
create mode 100644 tests/metadata_addon_orphan3.dtb.expect
create mode 100644 tests/metadata_addon_orphan3.dts
create mode 100644 tests/metadata_addon_orphan3.dts.dts.expect
diff --git a/tests/metadata_addon_orphan3.dtb.dts.expect b/tests/metadata_addon_orphan3.dtb.dts.expect
new file mode 100644
index 00000000..2605e1b6
--- /dev/null
+++ b/tests/metadata_addon_orphan3.dtb.dts.expect
@@ -0,0 +1,22 @@
+/dts-v1/;
+/addon/;
+
+
+&base1 {
+
+ addon-node1 {
+ prop1 = <0x01>;
+ prop2 = <0x02>;
+ };
+
+ addon-node2 {
+ prop3 = <0x03>;
+ };
+};
+
+&base2 {
+
+ addon-node {
+ prop = <0x00>;
+ };
+};
diff --git a/tests/metadata_addon_orphan3.dtb.expect b/tests/metadata_addon_orphan3.dtb.expect
new file mode 100644
index 00000000..5a654831
--- /dev/null
+++ b/tests/metadata_addon_orphan3.dtb.expect
@@ -0,0 +1,17 @@
+/dts-v1/;
+/addon/;
+
+&base1 {
+ addon-node1 {
+ prop1 = <0x00000001>;
+ prop2 = <0x00000002>;
+ };
+ addon-node2 {
+ prop3 = <0x00000003>;
+ };
+};
+&base2 {
+ addon-node {
+ prop = <0x00000000>;
+ };
+};
diff --git a/tests/metadata_addon_orphan3.dts b/tests/metadata_addon_orphan3.dts
new file mode 100644
index 00000000..0fe52ec3
--- /dev/null
+++ b/tests/metadata_addon_orphan3.dts
@@ -0,0 +1,28 @@
+// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
+/*
+ * Copyright (C) 2026 Bootlin
+ */
+
+/dts-v1/;
+/addon/;
+
+&base1 {
+ addon-node1 {
+ prop1 = <1>;
+ };
+};
+
+&base2 {
+ addon-node {
+ prop = <0>;
+ };
+};
+
+&base1 {
+ addon-node1 {
+ prop2 = <2>;
+ };
+ addon-node2 {
+ prop3 = <3>;
+ };
+};
diff --git a/tests/metadata_addon_orphan3.dts.dts.expect b/tests/metadata_addon_orphan3.dts.dts.expect
new file mode 100644
index 00000000..2605e1b6
--- /dev/null
+++ b/tests/metadata_addon_orphan3.dts.dts.expect
@@ -0,0 +1,22 @@
+/dts-v1/;
+/addon/;
+
+
+&base1 {
+
+ addon-node1 {
+ prop1 = <0x01>;
+ prop2 = <0x02>;
+ };
+
+ addon-node2 {
+ prop3 = <0x03>;
+ };
+};
+
+&base2 {
+
+ addon-node {
+ prop = <0x00>;
+ };
+};
diff --git a/tests/run_tests.sh b/tests/run_tests.sh
index e099bd39..ab09d745 100755
--- a/tests/run_tests.sh
+++ b/tests/run_tests.sh
@@ -1251,7 +1251,8 @@ metadata_tests() {
for dt in metadata_propdata_phandle_local metadata_propdata_phandle_ref \
metadata_addon_base metadata_exportsyms_local \
metadata_exportsyms_ref metadata_importsyms \
- metadata_addon_orphan1 metadata_addon_orphan2; do
+ metadata_addon_orphan1 metadata_addon_orphan2 \
+ metadata_addon_orphan3; do
run_dtc_test -I dts -O dts -o $dt.dts.dts "$SRCDIR/$dt.dts"
base_run_test check_diff $dt.dts.dts "$SRCDIR/$dt.dts.dts.expect"
run_dtc_test -I dts -O dtb -o $dt.dtb "$SRCDIR/$dt.dts"
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 57/74] dtc: Add support for orphan nodes sorting
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (55 preceding siblings ...)
2026-08-26 9:49 ` [RFC PATCH v2 56/74] tests: metadata: Add a test related to orphan node merging Herve Codina
@ 2026-08-26 9:49 ` Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 58/74] tests: metadata: Improve sort test to check " Herve Codina
` (16 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:49 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
dtc can sort items when the command line --sort option is set.
Add support for orphan nodes sorting when this option is used.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
livetree.c | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/livetree.c b/livetree.c
index f7c13585..8d7803ab 100644
--- a/livetree.c
+++ b/livetree.c
@@ -1333,6 +1333,34 @@ static void sort_importsyms(struct dt_info *dti)
free(tbl);
}
+static void sort_orphans(struct dt_info *dti)
+{
+ int n = 0, i = 0;
+ struct node *orphan, **tbl;
+
+ for_each_orphan(dti->orphanlist, orphan) {
+ sort_node(orphan);
+ n++;
+ }
+
+ if (n == 0)
+ return;
+
+ tbl = xmalloc(n * sizeof(*tbl));
+
+ for_each_orphan(dti->orphanlist, orphan)
+ tbl[i++] = orphan;
+
+ qsort(tbl, n, sizeof(*tbl), cmp_subnode);
+
+ dti->orphanlist = tbl[0];
+ for (i = 0; i < (n-1); i++)
+ tbl[i]->next_sibling = tbl[i+1];
+ tbl[n-1]->next_sibling = NULL;
+
+ free(tbl);
+}
+
void sort_tree(struct dt_info *dti)
{
sort_reserve_entries(dti);
@@ -1340,6 +1368,8 @@ void sort_tree(struct dt_info *dti)
if (dti->dt)
sort_node(dti->dt);
+
+ sort_orphans(dti);
}
/* utility helper to avoid code duplication */
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 58/74] tests: metadata: Improve sort test to check for orphan nodes sorting
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (56 preceding siblings ...)
2026-08-26 9:49 ` [RFC PATCH v2 57/74] dtc: Add support for orphan nodes sorting Herve Codina
@ 2026-08-26 9:49 ` Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 59/74] dtc: Add support for references by path involving orphan nodes Herve Codina
` (15 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:49 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
dtc is able to sort orphan nodes when the --sort option is used.
Improve the metadata sort test to perform a check for this feature.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
tests/metadata_sort.dtb.dts.expect | 39 ++++++++++++++++++++++++++++++
tests/metadata_sort.dtb.expect | 30 +++++++++++++++++++++++
tests/metadata_sort.dts | 33 +++++++++++++++++++++++++
3 files changed, 102 insertions(+)
diff --git a/tests/metadata_sort.dtb.dts.expect b/tests/metadata_sort.dtb.dts.expect
index 270c06db..df3c02ba 100644
--- a/tests/metadata_sort.dtb.dts.expect
+++ b/tests/metadata_sort.dtb.dts.expect
@@ -20,3 +20,42 @@
phandle = <0x01>;
};
};
+
+&abc {
+ prop-a = "a";
+ prop-b = "b";
+
+ node-a {
+ prop = <0x01>;
+ };
+
+ node-b {
+ prop = <0x02>;
+ };
+
+ node-c {
+ prop = <0x03>;
+ };
+};
+
+&de1 {
+ prop-a = "a";
+ prop-b = "b";
+
+ node-a {
+ prop = <0x01>;
+ };
+
+ node-b {
+ prop = <0x02>;
+ };
+
+ node-c {
+ prop = <0x03>;
+ };
+};
+
+&de2 {
+ prop-a = "a";
+ prop-b = "b";
+};
diff --git a/tests/metadata_sort.dtb.expect b/tests/metadata_sort.dtb.expect
index d5e22674..faca4f67 100644
--- a/tests/metadata_sort.dtb.expect
+++ b/tests/metadata_sort.dtb.expect
@@ -16,3 +16,33 @@
// [FDT_IMPORT_SYM] 'abc' (foo,bar)
// [FDT_IMPORT_SYM] 'de1' (foo,bar)
// [FDT_IMPORT_SYM] 'de2' (foo,bar)
+&abc {
+ prop-a = "a";
+ prop-b = "b";
+ node-a {
+ prop = <0x00000001>;
+ };
+ node-b {
+ prop = <0x00000002>;
+ };
+ node-c {
+ prop = <0x00000003>;
+ };
+};
+&de1 {
+ prop-a = "a";
+ prop-b = "b";
+ node-a {
+ prop = <0x00000001>;
+ };
+ node-b {
+ prop = <0x00000002>;
+ };
+ node-c {
+ prop = <0x00000003>;
+ };
+};
+&de2 {
+ prop-a = "a";
+ prop-b = "b";
+};
diff --git a/tests/metadata_sort.dts b/tests/metadata_sort.dts
index e523e209..3b690a53 100644
--- a/tests/metadata_sort.dts
+++ b/tests/metadata_sort.dts
@@ -23,3 +23,36 @@
/export/ a: &node_b;
};
};
+
+&de2 {
+ prop-b = "b";
+ prop-a = "a";
+};
+
+&abc {
+ prop-b = "b";
+ prop-a = "a";
+ node-b {
+ prop = <2>;
+ };
+ node-a {
+ prop = <1>;
+ };
+ node-c {
+ prop = <3>;
+ };
+};
+
+&de1 {
+ prop-b = "b";
+ prop-a = "a";
+ node-b {
+ prop = <2>;
+ };
+ node-a {
+ prop = <1>;
+ };
+ node-c {
+ prop = <3>;
+ };
+};
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 59/74] dtc: Add support for references by path involving orphan nodes
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (57 preceding siblings ...)
2026-08-26 9:49 ` [RFC PATCH v2 58/74] tests: metadata: Improve sort test to check " Herve Codina
@ 2026-08-26 9:49 ` Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 60/74] tests: metadata: Add a test " Herve Codina
` (14 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:49 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
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.
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_PROPDATA_PHANDLE.
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 the 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:
$<orphan_name>/<path>
This leads to a reference by path in the form &{$<orphan_name>/<path>}.
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 a subnode available from orphan nodes can be translated to a
reference by path thanks to the new syntax.
Add support for this &{$<orphan_name>/<path>} syntax to reference by
path a local node from an orphan node.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
dtc-lexer.l | 7 ++++++
dtc-parser.y | 19 ++++++++++++++++
livetree.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++-----
treesource.c | 3 ++-
4 files changed, 85 insertions(+), 7 deletions(-)
diff --git a/dtc-lexer.l b/dtc-lexer.l
index f21ae00d..9f611f01 100644
--- a/dtc-lexer.l
+++ b/dtc-lexer.l
@@ -236,6 +236,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;
+ }
+
<BYTESTRING>[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 6bf6bb50..c32eb65f 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 <labelref> DT_LABEL
%token <labelref> DT_LABEL_REF
%token <labelref> DT_PATH_REF
+%token <labelref> DT_ORPHAN_PATH_REF
%token DT_INCBIN
%type <data> 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/livetree.c b/livetree.c
index 8d7803ab..49912234 100644
--- a/livetree.c
+++ b/livetree.c
@@ -593,13 +593,31 @@ static void fill_fullpaths_(struct node *tree, const char *prefix)
static void 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->ref || orphan->ref[0] == '\0')
+ die("orphan node has an empty ref\n");
+
+ /*
+ * An orphan node name is set with its reference.
+ * Its name is in the form "&xxxxx" with xxxxx its reference.
+ * For its full path, we use "$xxxxx" to make a clear
+ * distinction between a reference (&xxxxx) where a resolution
+ * could be involved vs a "simple" path where we just need to
+ * identified the orphan ($xxxxx).
+ */
+ xasprintf(&orphan->fullpath, "$%s", orphan->ref);
+ orphan->basenamelen = strlen(orphan->ref) + 1;
+
+ for_each_child(orphan, child)
+ fill_fullpaths_(child, orphan->fullpath);
+ }
}
static void complete_ref_and_is_local(struct dt_info *dti, char **ref,
@@ -619,14 +637,27 @@ static void complete_ref_and_is_local(struct dt_info *dti, char **ref,
* Also take care of references pointing to an orphan node. Those
* reference are not local references. Indeed, an orphan node is an
* external node.
+ *
+ * Even if a path to an orphan node is possible, force to use the
+ * orphan node reference instead of the path to avoid later complexity
+ * mostly in dtb generation.
*/
if (*ref) {
refnode = get_node_by_ref(dti, *ref);
- if (refnode && !node_is_orphan(dti, refnode))
- *is_local = true;
- else
+ if (!refnode) {
*is_local = false;
+ return;
+ }
+
+ if (node_is_orphan(dti, refnode)) {
+ *is_local = false;
+ /* Force to use its reference */
+ *ref = refnode->ref;
+ return;
+ }
+
+ *is_local = true;
return;
}
@@ -939,7 +970,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->ref) {
+ /*
+ * 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->ref))
+ target = tree;
+ else
+ target = NULL;
+ } else {
+ target = get_node_by_label_(tree, label);
+ }
free(buf);
@@ -955,6 +998,14 @@ static struct node *get_node_by_ref_(struct node *tree, const char *ref)
struct node *get_node_by_path(struct dt_info *dti, const char *path)
{
+ if (path && path[0] == '$') {
+ /*
+ * The path starts from an orphan node. Use get node by ref to
+ * handle this path involving an orphan node.
+ */
+ return get_node_by_ref(dti, path);
+ }
+
if (dti->dt)
return get_node_by_path_(dti->dt, path);
diff --git a/treesource.c b/treesource.c
index d18025c5..38932e50 100644
--- a/treesource.c
+++ b/treesource.c
@@ -328,7 +328,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.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 60/74] tests: metadata: Add a test for references by path involving orphan nodes
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (58 preceding siblings ...)
2026-08-26 9:49 ` [RFC PATCH v2 59/74] dtc: Add support for references by path involving orphan nodes Herve Codina
@ 2026-08-26 9:49 ` Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 61/74] dtc: Add support for namespace labels references Herve Codina
` (13 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:49 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
The syntax '&{$<orphan_name>/<path>}' allows to reference by path a node
that belongs to an orphan node.
Add a test related to this kind of references.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
.../metadata_addon_references.dtb.dts.expect | 47 +++++++++++++++++++
tests/metadata_addon_references.dtb.expect | 47 +++++++++++++++++++
tests/metadata_addon_references.dts | 43 +++++++++++++++++
.../metadata_addon_references.dts.dts.expect | 47 +++++++++++++++++++
tests/run_tests.sh | 2 +-
5 files changed, 185 insertions(+), 1 deletion(-)
create mode 100644 tests/metadata_addon_references.dtb.dts.expect
create mode 100644 tests/metadata_addon_references.dtb.expect
create mode 100644 tests/metadata_addon_references.dts
create mode 100644 tests/metadata_addon_references.dts.dts.expect
diff --git a/tests/metadata_addon_references.dtb.dts.expect b/tests/metadata_addon_references.dtb.dts.expect
new file mode 100644
index 00000000..ccd7c849
--- /dev/null
+++ b/tests/metadata_addon_references.dtb.dts.expect
@@ -0,0 +1,47 @@
+/dts-v1/;
+/addon/;
+
+/ {
+
+ sub-node {
+ ref1-base1 = <&base1>;
+ ref2-base1-addon-node = <&{$base1/addon-node}>;
+ ref3-base1-addon-node = <&{$base1/addon-node}>;
+ ref4-base1-addon-node = <&{$base1/addon-node}>;
+ ref5-base3 = <&base3>;
+ ref6-base3 = <&base3>;
+ ref7-base3-subsubnode = <&{$base3/sub-node/sub-subnode}>;
+ phandle = <0x03>;
+ };
+};
+
+&base1 {
+
+ addon-node {
+ prop = <0x02>;
+ phandle = <0x01>;
+ };
+};
+
+&base2 {
+
+ addon-node1 {
+ ref-base1-addon-node = <&{$base1/addon-node}>;
+ };
+
+ addon-node2 {
+ ref-root-sub-node = <&{/sub-node}>;
+ };
+};
+
+&base3 {
+
+ sub-node {
+ prop = <0x03>;
+
+ sub-subnode {
+ prop = <0x04>;
+ phandle = <0x02>;
+ };
+ };
+};
diff --git a/tests/metadata_addon_references.dtb.expect b/tests/metadata_addon_references.dtb.expect
new file mode 100644
index 00000000..c58e3b2d
--- /dev/null
+++ b/tests/metadata_addon_references.dtb.expect
@@ -0,0 +1,47 @@
+/dts-v1/;
+/addon/;
+
+/ {
+ sub-node {
+ ref1-base1 = <0xffffffff>;
+ // [FDT_PROPDATA_PHANDLE_REF] ref1-base1[0], ref = base1
+ ref2-base1-addon-node = <0x00000001>;
+ // [FDT_PROPDATA_PHANDLE] ref2-base1-addon-node[0]
+ ref3-base1-addon-node = <0x00000001>;
+ // [FDT_PROPDATA_PHANDLE] ref3-base1-addon-node[0]
+ ref4-base1-addon-node = <0x00000001>;
+ // [FDT_PROPDATA_PHANDLE] ref4-base1-addon-node[0]
+ ref5-base3 = <0xffffffff>;
+ // [FDT_PROPDATA_PHANDLE_REF] ref5-base3[0], ref = base3
+ ref6-base3 = <0xffffffff>;
+ // [FDT_PROPDATA_PHANDLE_REF] ref6-base3[0], ref = base3
+ ref7-base3-subsubnode = <0x00000002>;
+ // [FDT_PROPDATA_PHANDLE] ref7-base3-subsubnode[0]
+ phandle = <0x00000003>;
+ };
+};
+&base1 {
+ addon-node {
+ prop = <0x00000002>;
+ phandle = <0x00000001>;
+ };
+};
+&base2 {
+ addon-node1 {
+ ref-base1-addon-node = <0x00000001>;
+ // [FDT_PROPDATA_PHANDLE] ref-base1-addon-node[0]
+ };
+ addon-node2 {
+ ref-root-sub-node = <0x00000003>;
+ // [FDT_PROPDATA_PHANDLE] ref-root-sub-node[0]
+ };
+};
+&base3 {
+ sub-node {
+ prop = <0x00000003>;
+ sub-subnode {
+ prop = <0x00000004>;
+ phandle = <0x00000002>;
+ };
+ };
+};
diff --git a/tests/metadata_addon_references.dts b/tests/metadata_addon_references.dts
new file mode 100644
index 00000000..239c1416
--- /dev/null
+++ b/tests/metadata_addon_references.dts
@@ -0,0 +1,43 @@
+// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
+/*
+ * Copyright (C) 2026 Bootlin
+ */
+
+/dts-v1/;
+/addon/;
+
+/ {
+ sub_node: sub-node {
+ ref1-base1 = <&base1>;
+ ref2-base1-addon-node = <&b1_addon>;
+ ref3-base1-addon-node = <&{$base1/addon-node}>;
+ ref4-base1-addon-node = <&{$base1/addon-node/}>;
+ ref5-base3 = <&{$base3}>;
+ ref6-base3 = <&{$base3/}>;
+ ref7-base3-subsubnode = <&{$base3/sub-node/sub-subnode}>;
+ };
+};
+
+&base1 {
+ b1_addon: addon-node {
+ prop = <2>;
+ };
+};
+
+&base2 {
+ addon-node1 {
+ ref-base1-addon-node = <&b1_addon>;
+ };
+ addon-node2 {
+ ref-root-sub-node = <&sub_node>;
+ };
+};
+
+&base3 {
+ sub-node {
+ prop = <3>;
+ sub-subnode {
+ prop = <4>;
+ };
+ };
+};
diff --git a/tests/metadata_addon_references.dts.dts.expect b/tests/metadata_addon_references.dts.dts.expect
new file mode 100644
index 00000000..47c30a1f
--- /dev/null
+++ b/tests/metadata_addon_references.dts.dts.expect
@@ -0,0 +1,47 @@
+/dts-v1/;
+/addon/;
+
+/ {
+
+ sub_node: sub-node {
+ ref1-base1 = <&base1>;
+ ref2-base1-addon-node = <&b1_addon>;
+ ref3-base1-addon-node = <&{$base1/addon-node}>;
+ ref4-base1-addon-node = <&{$base1/addon-node/}>;
+ ref5-base3 = <&base3>;
+ ref6-base3 = <&base3>;
+ ref7-base3-subsubnode = <&{$base3/sub-node/sub-subnode}>;
+ phandle = <0x03>;
+ };
+};
+
+&base1 {
+
+ b1_addon: addon-node {
+ prop = <0x02>;
+ phandle = <0x01>;
+ };
+};
+
+&base2 {
+
+ addon-node1 {
+ ref-base1-addon-node = <&b1_addon>;
+ };
+
+ addon-node2 {
+ ref-root-sub-node = <&sub_node>;
+ };
+};
+
+&base3 {
+
+ sub-node {
+ prop = <0x03>;
+
+ sub-subnode {
+ prop = <0x04>;
+ phandle = <0x02>;
+ };
+ };
+};
diff --git a/tests/run_tests.sh b/tests/run_tests.sh
index ab09d745..61a69fcb 100755
--- a/tests/run_tests.sh
+++ b/tests/run_tests.sh
@@ -1252,7 +1252,7 @@ metadata_tests() {
metadata_addon_base metadata_exportsyms_local \
metadata_exportsyms_ref metadata_importsyms \
metadata_addon_orphan1 metadata_addon_orphan2 \
- metadata_addon_orphan3; do
+ metadata_addon_orphan3 metadata_addon_references; do
run_dtc_test -I dts -O dts -o $dt.dts.dts "$SRCDIR/$dt.dts"
base_run_test check_diff $dt.dts.dts "$SRCDIR/$dt.dts.dts.expect"
run_dtc_test -I dts -O dtb -o $dt.dtb "$SRCDIR/$dt.dts"
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 61/74] dtc: Add support for namespace labels references
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (59 preceding siblings ...)
2026-08-26 9:49 ` [RFC PATCH v2 60/74] tests: metadata: Add a test " Herve Codina
@ 2026-08-26 9:49 ` Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 62/74] tests: metadata: Add a test " Herve Codina
` (12 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:49 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
Namespace labels references can only be present in addons and involves
import/exported symbols.
They allow to 'jump' from node to node based on exported symbols defined
at each node.
When a label reference contains a dot ('.') char, it becomes a namespace
labels reference.
For instance &foo.bar.baz is a namespace label reference. The resolution
of this reference is performed in a recursive way.
- Get the node referenced by foo.
- From this node get the node referenced by bar in exported symbols
from foo.
- From this node get the node referenced by baz in exported symbols
from bar.
- ...
The first item (foo) involves import symbols defined in the addon and
export symbols defined in the node the addon is applied to.
Other items involves only exported symbols in referenced nodes to
perform the resolution chain.
We leave the addon thanks to the first item and import symbols. Other
items allow to perform jumps from node to node in the base device-tree
without leaving it.
The full resolution can be summarized with the following diagram:
(addon DT)
&foo.bar.baz
| | |
| | +--------------------------------------------+
| | |
| +-------------------------------+ |
| | |
| (addon DT) | |
+-> /import/ foo: --+ | |
| | |
| | |
addon applied at | | |
target node in the | | |
base DT | | |
| | | |
| (base DT) | | |
+---> target { v | |
/export/ foo: &n1; | |
| | |
| | |
v (base DT) | |
n1 { v |
/export/ bar: &n2; |
| |
v (base DT) |
n2 { v
/export/ baz: &n3;
|
|
v
n3 { ... };
'&foo.bar.baz' references, in the end, the n3 node from the base DT.
Implement support for namespace references parsing in dtc.
The resolution part is out of dtc. Indeed, dtc has no support for
applying an addon.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
dtc-lexer.l | 13 +++++++++++++
dtc-parser.y | 49 ++++++++++++++++++++++++++++++++++++++++++++++---
2 files changed, 59 insertions(+), 3 deletions(-)
diff --git a/dtc-lexer.l b/dtc-lexer.l
index 9f611f01..a3291ff6 100644
--- a/dtc-lexer.l
+++ b/dtc-lexer.l
@@ -229,6 +229,12 @@ static void PRINTF(1, 2) lexical_error(const char *fmt, ...);
return DT_LABEL_REF;
}
+<*>\&{LABEL}(\.{LABEL})+ { /* namespace label reference */
+ DPRINT("Ref namespace: %s\n", yytext+1);
+ yylval.labelref = xstrdup(yytext+1);
+ return DT_LABEL_NAMESPACE_REF;
+ }
+
<*>"&{"{PATHCHAR}*\} { /* new-style path reference */
yytext[yyleng-1] = '\0';
DPRINT("Ref: %s\n", yytext+2);
@@ -243,6 +249,13 @@ static void PRINTF(1, 2) lexical_error(const char *fmt, ...);
return DT_ORPHAN_PATH_REF;
}
+<*>"&{\$"{LABEL}(\.{LABEL})+([/]{PATHCHAR}*)?\} { /* orphan path reference */
+ yytext[yyleng-1] = '\0';
+ DPRINT("Ref (namespace) orphan path: %s\n", yytext+1);
+ yylval.labelref = xstrdup(yytext+2);
+ return DT_ORPHAN_PATH_REF;
+ }
+
<BYTESTRING>[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 c32eb65f..4139d96b 100644
--- a/dtc-parser.y
+++ b/dtc-parser.y
@@ -32,6 +32,11 @@ static bool is_ref_relative(const char *ref)
return ref[0] != '/' && strchr(&ref[1], '/');
}
+static bool is_ref_namespace(const char *ref)
+{
+ return strchr(ref, '.');
+}
+
static struct node *parser_get_node_by_ref(struct node *dt, struct node *orphanlist,
const char *ref)
{
@@ -110,6 +115,7 @@ static struct node *parser_get_node_by_ref(struct node *dt, struct node *orphanl
%token <data> DT_STRING
%token <labelref> DT_LABEL
%token <labelref> DT_LABEL_REF
+%token <labelref> DT_LABEL_NAMESPACE_REF
%token <labelref> DT_PATH_REF
%token <labelref> DT_ORPHAN_PATH_REF
%token DT_INCBIN
@@ -260,7 +266,7 @@ importdef:
}
;
-dt_ref: DT_LABEL_REF | DT_PATH_REF;
+dt_ref: DT_LABEL_REF | DT_PATH_REF | DT_LABEL_NAMESPACE_REF;
devicetree:
'/' nodedef
@@ -281,6 +287,9 @@ devicetree:
else if (is_ref_relative($1))
ERROR(&@2, "Label-relative reference %s not supported in plugin nor addon", $1);
+ if (!(last_header_flags & DTSF_ADDON) && is_ref_namespace($1))
+ ERROR(&@2, "Namespaced label reference %s supported only in addon", $1);
+
if (last_header_flags & DTSF_PLUGIN) {
$$ = plugin_add_orphan_node(
name_node(build_node(NULL, NULL, NULL, NULL),
@@ -300,6 +309,10 @@ devicetree:
if ((last_header_flags & (DTSF_PLUGIN | DTSF_ADDON)) && is_ref_relative($3))
ERROR(&@2, "Label-relative reference %s not supported in plugin nor addon", $3);
+ if (!(last_header_flags & DTSF_ADDON) && is_ref_namespace($3))
+ ERROR(&@2, "Namespaced label reference %s supported only in addon", $3);
+
+
if (target) {
add_label(&target->labels, $2);
merge_nodes(target, $4);
@@ -346,6 +359,25 @@ devicetree:
}
$$ = $1;
}
+ | devicetree DT_LABEL_NAMESPACE_REF nodedef
+ {
+ struct node *target = parser_get_node_by_ref(
+ $1, parser_orphanlist, $2);
+
+ if (!(last_header_flags & DTSF_ADDON))
+ ERROR(&@2, "Namespaced label reference %s supported only in addon", $2);
+
+ if (target) {
+ merge_nodes(target, $3);
+ } else {
+ if (last_header_flags & DTSF_ADDON)
+ addon_add_orphan_node(&parser_orphanlist,
+ orphan_node($3, $2));
+ else
+ ERROR(&@2, "Label or path %s not found", $2);
+ }
+ $$ = $1;
+ }
| devicetree DT_DEL_NODE dt_ref ';'
{
struct node *target = parser_get_node_by_ref(
@@ -422,6 +454,10 @@ exportlist:
exportdef:
DT_EXPORT DT_LABEL dt_ref ';'
{
+ /* Only an addon can use namespaced labels references */
+ if (!(last_header_flags & DTSF_ADDON) && is_ref_namespace($3))
+ ERROR(&@2, "Namespaced label reference %s supported only in addon", $3);
+
$$ = build_exportsym($2, $3, 0, &@$);
free($2);
free($3);
@@ -477,6 +513,9 @@ propdata:
}
| propdataprefix dt_ref
{
+ if (!(last_header_flags & DTSF_ADDON) && is_ref_namespace($2))
+ ERROR(&@2, "Namespaced label reference %s supported only in addon", $2);
+
$1 = data_add_marker($1, TYPE_STRING, $2);
$$ = data_add_marker($1, REF_PATH, $2);
}
@@ -582,13 +621,17 @@ arrayprefix:
{
uint64_t val = ~0ULL >> (64 - $1.bits);
- if ($1.bits == 32)
+ if ($1.bits == 32) {
+ if (!(last_header_flags & DTSF_ADDON) && is_ref_namespace($2))
+ ERROR(&@2, "Namespaced label reference %s supported only in addon", $2);
+
$1.data = data_add_marker($1.data,
REF_PHANDLE,
$2);
- else
+ } else {
ERROR(&@2, "References are only allowed in "
"arrays with 32-bit elements.");
+ }
$$.data = data_append_integer($1.data, val, $1.bits);
}
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 62/74] tests: metadata: Add a test for namespace labels references
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (60 preceding siblings ...)
2026-08-26 9:49 ` [RFC PATCH v2 61/74] dtc: Add support for namespace labels references Herve Codina
@ 2026-08-26 9:49 ` Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 63/74] libfdt: Add support for applying an addon on a base device-tree blob Herve Codina
` (11 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:49 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
Namespace labels references are references in the form '&foo.bar.baz'.
Support for namespace labels references has been recently added to dtc.
Add a test related to this kind of references.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
...metadata_addon_refnamespace.dtb.dts.expect | 32 ++++++++++++++++
tests/metadata_addon_refnamespace.dtb.expect | 29 ++++++++++++++
tests/metadata_addon_refnamespace.dts | 38 +++++++++++++++++++
...metadata_addon_refnamespace.dts.dts.expect | 32 ++++++++++++++++
tests/run_tests.sh | 3 +-
5 files changed, 133 insertions(+), 1 deletion(-)
create mode 100644 tests/metadata_addon_refnamespace.dtb.dts.expect
create mode 100644 tests/metadata_addon_refnamespace.dtb.expect
create mode 100644 tests/metadata_addon_refnamespace.dts
create mode 100644 tests/metadata_addon_refnamespace.dts.dts.expect
diff --git a/tests/metadata_addon_refnamespace.dtb.dts.expect b/tests/metadata_addon_refnamespace.dtb.dts.expect
new file mode 100644
index 00000000..111a7462
--- /dev/null
+++ b/tests/metadata_addon_refnamespace.dtb.dts.expect
@@ -0,0 +1,32 @@
+/dts-v1/;
+/addon/;
+
+/import/ foo: "abc,foo";
+/import/ bar: "abc,bar";
+
+
+&foo.x {
+
+ addon-node {
+ ref0 = <&foo.x.y>;
+ ref1 = <0x0a &bar.other 0x14>;
+ ref2 = <&{$foo.x.y.z/addon-node}>;
+ ref3 = <&{$foo.x.y.z/addon-node}>;
+ };
+};
+
+&foo.x.y.z {
+
+ addon-node {
+ prop = <0x00>;
+ phandle = <0x01>;
+ };
+};
+
+&foo.x.y {
+
+ node {
+ prop1 = <0x01>;
+ prop2 = <0x02>;
+ };
+};
diff --git a/tests/metadata_addon_refnamespace.dtb.expect b/tests/metadata_addon_refnamespace.dtb.expect
new file mode 100644
index 00000000..8db63a1b
--- /dev/null
+++ b/tests/metadata_addon_refnamespace.dtb.expect
@@ -0,0 +1,29 @@
+/dts-v1/;
+/addon/;
+
+// [FDT_IMPORT_SYM] 'foo' (abc,foo)
+// [FDT_IMPORT_SYM] 'bar' (abc,bar)
+&foo.x {
+ addon-node {
+ ref0 = <0xffffffff>;
+ // [FDT_PROPDATA_PHANDLE_REF] ref0[0], ref = foo.x.y
+ ref1 = <0x0000000a 0xffffffff 0x00000014>;
+ // [FDT_PROPDATA_PHANDLE_REF] ref1[4], ref = bar.other
+ ref2 = <0x00000001>;
+ // [FDT_PROPDATA_PHANDLE] ref2[0]
+ ref3 = <0x00000001>;
+ // [FDT_PROPDATA_PHANDLE] ref3[0]
+ };
+};
+&foo.x.y.z {
+ addon-node {
+ prop = <0x00000000>;
+ phandle = <0x00000001>;
+ };
+};
+&foo.x.y {
+ node {
+ prop1 = <0x00000001>;
+ prop2 = <0x00000002>;
+ };
+};
diff --git a/tests/metadata_addon_refnamespace.dts b/tests/metadata_addon_refnamespace.dts
new file mode 100644
index 00000000..51a17a9a
--- /dev/null
+++ b/tests/metadata_addon_refnamespace.dts
@@ -0,0 +1,38 @@
+// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
+/*
+ * Copyright (C) 2026 Bootlin
+ */
+
+/dts-v1/;
+/addon/;
+
+/import/ foo: "abc,foo";
+/import/ bar: "abc,bar";
+
+
+&foo.x {
+ addon-node {
+ ref0 = <&foo.x.y>;
+ ref1 = <10 &bar.other 20>;
+ ref2 = <&addon_node>;
+ ref3 = <&{$foo.x.y.z/addon-node}>;
+ };
+};
+
+&foo.x.y.z {
+ addon_node: addon-node {
+ prop = <0>;
+ };
+};
+
+&foo.x.y {
+ node {
+ prop1 = <1>;
+ };
+};
+
+&foo.x.y {
+ node {
+ prop2 = <2>;
+ };
+};
diff --git a/tests/metadata_addon_refnamespace.dts.dts.expect b/tests/metadata_addon_refnamespace.dts.dts.expect
new file mode 100644
index 00000000..94861c3b
--- /dev/null
+++ b/tests/metadata_addon_refnamespace.dts.dts.expect
@@ -0,0 +1,32 @@
+/dts-v1/;
+/addon/;
+
+/import/ foo: "abc,foo";
+/import/ bar: "abc,bar";
+
+
+&foo.x {
+
+ addon-node {
+ ref0 = <&foo.x.y>;
+ ref1 = <0x0a &bar.other 0x14>;
+ ref2 = <&addon_node>;
+ ref3 = <&{$foo.x.y.z/addon-node}>;
+ };
+};
+
+&foo.x.y.z {
+
+ addon_node: addon-node {
+ prop = <0x00>;
+ phandle = <0x01>;
+ };
+};
+
+&foo.x.y {
+
+ node {
+ prop1 = <0x01>;
+ prop2 = <0x02>;
+ };
+};
diff --git a/tests/run_tests.sh b/tests/run_tests.sh
index 61a69fcb..7f47b685 100755
--- a/tests/run_tests.sh
+++ b/tests/run_tests.sh
@@ -1252,7 +1252,8 @@ metadata_tests() {
metadata_addon_base metadata_exportsyms_local \
metadata_exportsyms_ref metadata_importsyms \
metadata_addon_orphan1 metadata_addon_orphan2 \
- metadata_addon_orphan3 metadata_addon_references; do
+ metadata_addon_orphan3 metadata_addon_references \
+ metadata_addon_refnamespace; do
run_dtc_test -I dts -O dts -o $dt.dts.dts "$SRCDIR/$dt.dts"
base_run_test check_diff $dt.dts.dts "$SRCDIR/$dt.dts.dts.expect"
run_dtc_test -I dts -O dtb -o $dt.dtb "$SRCDIR/$dt.dts"
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 63/74] libfdt: Add support for applying an addon on a base device-tree blob
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (61 preceding siblings ...)
2026-08-26 9:49 ` [RFC PATCH v2 62/74] tests: metadata: Add a test " Herve Codina
@ 2026-08-26 9:49 ` Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 64/74] Add fdtaddon tool to apply an addon Herve Codina
` (10 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:49 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
Addon dtbs have been recently supported. The missing part is having some
support for addon usage.
The goal of addons is to extend a base device-tree. This feature is
similar to the overlay feature but addons and recent modifications
offer metadata and import/export symbol mechanism.
This import/export symbol mechanism allow a fine tuning of symbols
exported (i.e. symbol seen from the addon).
To extend the base device-tree, an addon needs to be applied to this
base device-tree at a specific node. The specific node, target node of
the addon, is not encoded in the addon dtb.
Indeed, addons do not depends on the exact base device tree structure.
An addon can be applied on top of any base device tree as soon as the
target node matches expectations of the addon. Exported symbols at
this target node has to match imported symbols defined in the addon.
When the addon is applied three macro operations have to be done:
- Avoid phandle conflict
The conflict avoidance consists in fixing local phandle to avoid
conflict with already existing phandle available in the base
device-tree. This operation involved metadata recently added in dtb
format.
- Resolve unresolved symbols
The symbol resolution consists in replacing unresolved symbol
references present in the addon dtb by a reference to an existing
node in the base device tree. This operation involved metadata and
import/export symbols.
- Merge addon nodes into base device-tree
This merging consists in adding additional nodes described in the
addon into the base device-tree.
fdt_addon_apply() allows to apply an addon on a base device-tree and
performs internally all the needed operations.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
libfdt/Makefile.libfdt | 2 +-
libfdt/fdt_addon.c | 1741 ++++++++++++++++++++++++++++++++++++++
libfdt/fdt_rw.c | 69 ++
libfdt/libfdt.h | 32 +
libfdt/libfdt_internal.h | 5 +
libfdt/meson.build | 1 +
libfdt/version.lds | 1 +
7 files changed, 1850 insertions(+), 1 deletion(-)
create mode 100644 libfdt/fdt_addon.c
diff --git a/libfdt/Makefile.libfdt b/libfdt/Makefile.libfdt
index b763b2ec..437d5b54 100644
--- a/libfdt/Makefile.libfdt
+++ b/libfdt/Makefile.libfdt
@@ -10,7 +10,7 @@ LIBFDT_soname = libfdt.$(SHAREDLIB_EXT).1
LIBFDT_INCLUDES = fdt.h libfdt.h libfdt_env.h
LIBFDT_VERSION = version.lds
LIBFDT_SRCS = fdt.c fdt_ro.c fdt_wip.c fdt_sw.c fdt_rw.c fdt_strerror.c fdt_empty_tree.c \
- fdt_addresses.c fdt_overlay.c fdt_check.c
+ fdt_addresses.c fdt_overlay.c fdt_addon.c fdt_check.c
LIBFDT_OBJS = $(LIBFDT_SRCS:%.c=%.o)
LIBFDT_LIB = libfdt.$(SHAREDLIB_EXT).$(DTC_VERSION)
diff --git a/libfdt/fdt_addon.c b/libfdt/fdt_addon.c
new file mode 100644
index 00000000..54e71cbd
--- /dev/null
+++ b/libfdt/fdt_addon.c
@@ -0,0 +1,1741 @@
+// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2025 Bootlin
+ */
+#include "libfdt_env.h"
+
+#include <fdt.h>
+#include <libfdt.h>
+
+#include "libfdt_internal.h"
+
+/**
+ * fdt_skip_string - Skip a string
+ * @fdt: device tree blob
+ * @offset: Offset of the string to skip
+ *
+ * fdt_skip_string() returns the offset of the first byte after the skipped
+ * string.
+ *
+ * returns:
+ * offset of the first byte after the skipped string
+ */
+static int fdt_skip_string(const void *fdt, int offset)
+{
+ const char *p;
+ int next;
+
+ next = offset;
+ do {
+ p = fdt_offset_ptr(fdt, next++, 1);
+ } while (p && (*p != '\0'));
+
+ return next;
+}
+
+/**
+ * addon_phandle_add_offset - Increases a phandle by an offset
+ * @fdta: Addon device tree blob
+ * @node: Device tree addon blob
+ * @name: Name of the property to modify (phandle or linux,phandle)
+ * @delta: offset to apply
+ *
+ * addon_phandle_add_offset() increments a node phandle by a given
+ * offset.
+ *
+ * returns:
+ * 0 on success.
+ * Negative error code on error
+ */
+static int addon_phandle_add_offset(void *fdta, int node, const char *name,
+ uint32_t delta)
+{
+ fdt32_t *valp, val;
+ int len;
+
+ valp = fdt_getprop_w(fdta, node, name, &len);
+ if (!valp)
+ return len;
+ if (len != sizeof(val))
+ return -FDT_ERR_BADPHANDLE;
+
+ val = fdt32_ld(valp);
+ if (val + delta < val || val + delta == (uint32_t)-1)
+ return -FDT_ERR_NOPHANDLES;
+ fdt32_st(valp, val + delta);
+ return 0;
+}
+
+/**
+ * addon_phandle_set_val - Set a phandle value
+ * @fdt: Addon device tree blob
+ * @node: Device tree addon blob
+ * @name: Name of the property to modify (phandle or linux,phandle)
+ * @val: Value to set
+ *
+ * addon_phandle_set_val() set a node phandle to a given offset.
+ *
+ * returns:
+ * 0 on success.
+ * Negative error code on error
+ */
+static int addon_phandle_set_val(void *fdta, int node, const char *name,
+ uint32_t val)
+{
+ fdt32_t *valp;
+ int len;
+
+ valp = fdt_getprop_w(fdta, node, name, &len);
+ if (!valp)
+ return len;
+ if (len != sizeof(val))
+ return -FDT_ERR_BADPHANDLE;
+
+ fdt32_st(valp, val);
+ return 0;
+}
+
+/**
+ * addon_property_add_offset - Increases a property value (a phandle reference)
+ * by an offset
+ * @fdta: Addon device tree blob
+ * @prop_offset: Property to update
+ * @data_offset: Offset in the property data to update
+ * @delta: Offset to apply
+ *
+ * returns:
+ * 0 on success.
+ * Negative error code on error
+ */
+static int addon_property_add_offset(void *fdta, int prop_offset,
+ unsigned int data_offset, uint32_t delta)
+{
+ unsigned int proplen;
+ char *propval;
+ uint32_t val;
+ fdt32_t *p;
+ int tmp;
+
+ propval = fdt_getprop_by_offset_w(fdta, prop_offset, NULL, &tmp);
+ if (!propval)
+ return tmp;
+
+ proplen = tmp;
+ if (proplen < data_offset + sizeof(fdt32_t))
+ return -FDT_ERR_BADSTRUCTURE;
+
+ /* Get the phandle pointer */
+ p = (fdt32_t *)(propval + data_offset);
+
+ /* Update the phandle value */
+ val = fdt32_ld(p);
+ if (val + delta < val || val + delta == (uint32_t)-1)
+ return -FDT_ERR_NOPHANDLES;
+ fdt32_st(p, val + delta);
+ return 0;
+}
+
+/**
+ * addon_property_set_val - Set a property value (a phandle reference)
+ * @fdta: Addon device tree blob
+ * @prop_offset: Property to update
+ * @data_offset: Offset in the property data to update
+ * @value: Value to set
+ *
+ * returns:
+ * 0 on success.
+ * Negative error code on error
+ */
+static int addon_property_set_val(void *fdta, int prop_offset,
+ unsigned int data_offset, uint32_t val)
+{
+ unsigned int proplen;
+ char *propval;
+ fdt32_t *p;
+ int tmp;
+
+ propval = fdt_getprop_by_offset_w(fdta, prop_offset, NULL, &tmp);
+ if (!propval)
+ return tmp;
+
+ proplen = tmp;
+ if (proplen < data_offset + sizeof(fdt32_t))
+ return -FDT_ERR_BADSTRUCTURE;
+
+ /* Get the phandle pointer and update it */
+ p = (fdt32_t *)(propval + data_offset);
+ fdt32_st(p, val);
+ return 0;
+}
+
+/**
+ * addon_property_replace - Replace a property value (a phandle reference) if
+ * the old value matches the current value.
+ * @fdta: Addon device tree blob
+ * @prop_offset: Property to update
+ * @data_offset: Offset in the property data to update
+ * @old_val: Old value
+ * @new_val: New value to set if the @old_val matches
+ *
+ * returns:
+ * 0 on success.
+ * Negative error code on error
+ */
+static int addon_property_replace(void *fdt, int prop_offset,
+ unsigned int data_offset, uint32_t old_val,
+ uint32_t new_val)
+{
+ unsigned int proplen;
+ char *propval;
+ uint32_t val;
+ fdt32_t *p;
+ int tmp;
+
+ propval = fdt_getprop_by_offset_w(fdt, prop_offset, NULL, &tmp);
+ if (!propval)
+ return tmp;
+
+ proplen = tmp;
+ if (proplen < data_offset + sizeof(fdt32_t))
+ return -FDT_ERR_BADSTRUCTURE;
+
+ /* Get the phandle pointer */
+ p = (fdt32_t *)(propval + data_offset);
+
+ val = fdt32_ld(p);
+ if (val == old_val)
+ fdt32_st(p, new_val);
+
+ return 0;
+}
+
+/**
+ * addon_adjust_local_phandles - Adjust the local phandles of a whole addon blob
+ * @fdta: Addon device tree blob
+ * @delta: Offset to shift the phandles of
+ *
+ * addon_adjust_local_phandles() adds a constant to all the local phandles of
+ * an addon. This is mainly use as part of the addon application process, when
+ * we want to update all the local phandles of the addon to not conflict with
+ * phandles of the base device tree.
+ *
+ * returns:
+ * 0 on success
+ * Negative error code on failure
+ */
+static int addon_adjust_local_phandles(void *fdta, uint32_t delta)
+{
+ int offset, next_offset, d;
+ int last_prop_offset = -1;
+ int prop_phandle_offset;
+ const fdt32_t *pfdt32;
+ fdt32_t *pfdt32w;
+ uint32_t tag;
+ uint32_t val;
+ int ret;
+
+ /*
+ * phandles that need to be updated are:
+ * - phandles properties in nodes
+ * - phandles in properties where a FDT_PROPDATA_PHANDLE tag is
+ * available
+ * - phandles in FDT_EXPORT_SYM tags
+ */
+
+ next_offset = 0;
+ do {
+ offset = next_offset;
+ tag = fdt_next_tag(fdta, offset, &next_offset);
+ switch (tag) {
+ case FDT_BEGIN_NODE:
+ last_prop_offset = -1;
+
+ ret = addon_phandle_add_offset(fdta, offset, "phandle", delta);
+ if (ret && ret != -FDT_ERR_NOTFOUND)
+ return ret;
+
+ ret = addon_phandle_add_offset(fdta, offset, "linux,phandle", delta);
+ if (ret && ret != -FDT_ERR_NOTFOUND)
+ return ret;
+ break;
+
+ case FDT_END_NODE:
+ last_prop_offset = -1;
+ break;
+
+ case FDT_PROP:
+ last_prop_offset = offset;
+ break;
+
+ case FDT_NOP:
+ break;
+
+ case FDT_PROPDATA_PHANDLE:
+ d = fdt_get_structured_tag_data(tag, fdta,
+ offset + FDT_TAGSIZE, NULL);
+ if (d < 0)
+ return d;
+ /*
+ * The property references a phandle. Get the offset
+ * of this phandle reference in the property data area.
+ */
+ pfdt32 = fdt_offset_ptr(fdta, d, sizeof(*pfdt32));
+ prop_phandle_offset = fdt32_to_cpu(*pfdt32);
+ if (prop_phandle_offset % sizeof(fdt32_t))
+ return -FDT_ERR_BADVALUE;
+
+ if (last_prop_offset == -1)
+ return -FDT_ERR_BADSTRUCTURE;
+
+ /* Update the phandle reference */
+ ret = addon_property_add_offset(fdta, last_prop_offset,
+ prop_phandle_offset,
+ delta);
+ if (ret)
+ return ret;
+ break;
+
+ case FDT_PROPDATA_PHANDLE_REF:
+ break;
+
+ case FDT_BEGIN_NODE_REF:
+ last_prop_offset = -1;
+ break;
+
+ case FDT_END:
+ ret = (next_offset < 0) ? next_offset : 0;
+ break;
+
+ case FDT_EXPORT_SYM:
+ d = fdt_get_structured_tag_data(tag, fdta,
+ offset + FDT_TAGSIZE, NULL);
+ if (d < 0)
+ return d;
+
+ /* Get phandle pointer */
+ pfdt32w = fdt_offset_ptr_w(fdta, d, sizeof(*pfdt32));
+
+ /* Update the phandle value */
+ val = fdt32_ld(pfdt32w);
+ if (val + delta < val || val + delta == (uint32_t)-1)
+ return -FDT_ERR_NOPHANDLES;
+
+ fdt32_st(pfdt32w, val + delta);
+ break;
+
+ case FDT_EXPORT_SYM_REF:
+ break;
+ case FDT_IMPORT_SYM:
+ break;
+
+ default:
+ return -FDT_ERR_BADSTRUCTURE;
+ }
+ } while (tag != FDT_END);
+
+ return ret;
+}
+
+static int fdt_check_exportsym_offset(const void *fdt, int offset,
+ uint32_t *exportsym_tag, int *data_offset)
+{
+ int nextoffset, d;
+ uint32_t tag;
+
+ tag = fdt_next_tag(fdt, offset, &nextoffset);
+ switch (tag) {
+ case FDT_EXPORT_SYM:
+ case FDT_EXPORT_SYM_REF:
+ if (data_offset) {
+ d = fdt_get_structured_tag_data(tag, fdt,
+ offset + FDT_TAGSIZE,
+ NULL);
+ if (d < 0)
+ return d;
+ *data_offset = d;
+ }
+ break;
+ default:
+ return -FDT_ERR_BADOFFSET;
+ }
+
+ if (exportsym_tag)
+ *exportsym_tag = tag;
+
+ return nextoffset;
+}
+
+static int fdt_next_exportsym_nextoffset_(const void *fdt, int nextoffset)
+{
+ uint32_t tag;
+ int offset;
+
+ do {
+ offset = nextoffset;
+ tag = fdt_next_tag(fdt, offset, &nextoffset);
+ switch (tag) {
+ case FDT_EXPORT_SYM:
+ return offset;
+
+ case FDT_EXPORT_SYM_REF:
+ return offset;
+
+ case FDT_BEGIN_NODE:
+ /* We are going to leave the current node (sub-node) */
+ return -FDT_ERR_NOTFOUND;
+
+ case FDT_BEGIN_NODE_REF:
+ /* We cannot have a node ref inside an other node */
+ return -FDT_ERR_BADSTRUCTURE;
+
+ case FDT_END_NODE:
+ /* We are going to leave the current node (parent node) */
+ return -FDT_ERR_NOTFOUND;
+
+ case FDT_END:
+ if (nextoffset < 0)
+ return nextoffset;
+
+ return -FDT_ERR_NOTFOUND;
+ }
+ } while (tag != FDT_END);
+
+ return offset;
+}
+
+static int fdt_next_exportsym_offset(const void *fdt, int offset)
+{
+ int nextoffset;
+
+ if (offset < 0)
+ return offset;
+
+ nextoffset = fdt_check_exportsym_offset(fdt, offset, NULL, NULL);
+ if (nextoffset < 0)
+ return nextoffset;
+
+ return fdt_next_exportsym_nextoffset_(fdt, nextoffset);
+}
+
+static int fdt_first_exportsym_offset(const void *fdt, int node_offset)
+{
+ int nextoffset;
+
+ if (node_offset < 0)
+ return node_offset;
+
+ nextoffset = fdt_check_node_offset_(fdt, node_offset);
+ if (nextoffset < 0)
+ return nextoffset;
+
+ return fdt_next_exportsym_nextoffset_(fdt, nextoffset);
+}
+
+static bool fdt_exportsym_is_ref(const void *fdt, int offset)
+{
+ uint32_t tag;
+
+ /* A exportsym ref has to be an exportsym ... */
+ if (fdt_check_exportsym_offset(fdt, offset, &tag, NULL) < 0)
+ return false;
+
+ if (tag != FDT_EXPORT_SYM_REF)
+ return false;
+
+ return true;
+}
+
+static const char *fdt_exportsym_get_name(const void *fdt, int offset)
+{
+ int data_offset;
+ uint32_t tag;
+
+ if (fdt_check_exportsym_offset(fdt, offset, &tag, &data_offset) < 0)
+ return NULL;
+
+ switch (tag) {
+ case FDT_EXPORT_SYM:
+ data_offset += FDT_CELLSIZE; /* Skip phandle value */
+ break;
+
+ case FDT_EXPORT_SYM_REF:
+ data_offset = fdt_skip_string(fdt, data_offset); /* Skip ref */
+ break;
+
+ default:
+ return NULL;
+ }
+
+ return fdt_offset_ptr(fdt, data_offset, 1);
+}
+
+static uint32_t fdt_exportsym_get_phandle(const void *fdt, int offset)
+{
+ const fdt32_t *pfdt32;
+ int data_offset;
+ uint32_t tag;
+
+ if (fdt_check_exportsym_offset(fdt, offset, &tag, &data_offset) < 0)
+ return 0;
+
+ /* Only FDT_EXPORT_SYM tag has a phandle in its data */
+ if (tag != FDT_EXPORT_SYM)
+ return 0;
+
+ pfdt32 = fdt_offset_ptr(fdt, data_offset, sizeof(*pfdt32));
+ if (!pfdt32)
+ return 0;
+
+ return fdt32_ld_(pfdt32);
+}
+
+static const char *fdt_exportsym_get_ref(const void *fdt, int offset)
+{
+ int data_offset;
+ uint32_t tag;
+
+ if (fdt_check_exportsym_offset(fdt, offset, &tag, &data_offset) < 0)
+ return NULL;
+
+ /* Only FDT_EXPORT_SYM_REF tag has a ref in its data */
+ if (tag != FDT_EXPORT_SYM_REF)
+ return NULL;
+
+ /* Get the reference */
+ return fdt_offset_ptr(fdt, data_offset, 1);
+}
+
+#define fdt_for_each_exportsym(exportsym, fdt, node) \
+ for (exportsym = fdt_first_exportsym_offset(fdt, node); \
+ exportsym >= 0; \
+ exportsym = fdt_next_exportsym_offset(fdt, exportsym))
+
+/**
+ * fdt_resolve_symbol - Resolve a symbol
+ * @fdt: base device tree blob
+ * @target_node: Target node offset in the base device tree blob containing
+ * the exported symbols used for the resolution.
+ * @sym_name: Symbol name
+ * @sym_namelen: Length of the symbol name
+ *
+ * fdt_resolve_symbol() searches for a matching export symbol exported by the
+ * @target_node. It returns the node offset in @fdt referenced by the matching
+ * export symbol.
+ *
+ * returns:
+ * offset of the node matching the symbol after the resolution on success.
+ * Negative error code on failure
+ */
+static int fdt_resolve_symbol(const void *fdt, int target_node,
+ const char *sym_name, size_t sym_namelen)
+{
+ const char *exportsym_name;
+ uint32_t phandle;
+ int exportsym;
+ int ref_node;
+
+ /* Look at export available at the target node */
+ fdt_for_each_exportsym(exportsym, fdt, target_node) {
+
+ /*
+ * Export symbols at target node cannot be reference ones.
+ * Indeed, those export symbols are all resolved symbols. They
+ * must point to a local node using a valid phandle.
+ */
+ if (fdt_exportsym_is_ref(fdt, exportsym))
+ return -FDT_ERR_BADSTRUCTURE;
+
+ exportsym_name = fdt_exportsym_get_name(fdt, exportsym);
+ if (!exportsym_name)
+ return -FDT_ERR_BADSTRUCTURE;
+
+ phandle = fdt_exportsym_get_phandle(fdt, exportsym);
+ if ((phandle == 0) || (phandle == ~0U))
+ return -FDT_ERR_BADPHANDLE;
+
+ if ((strlen(exportsym_name) != sym_namelen) ||
+ (memcmp(exportsym_name, sym_name, sym_namelen) != 0))
+ continue;
+
+ ref_node = fdt_node_offset_by_phandle(fdt, phandle);
+ if (ref_node < 0)
+ return -FDT_ERR_NOTFOUND;
+
+ return ref_node;
+ }
+
+ return -FDT_ERR_NOTFOUND;
+}
+
+/**
+ * fdt_resolve_import - Resolve an imported symbol
+ * @fdt: base device tree blob
+ * @target_node: Target node offset in the base device tree blob containing
+ * the exported symbols used for the resolution.
+ * @importsym_name: Import symbol name
+ * @importsym_compatible: Import symbol compatible string
+ *
+ * returns:
+ * offset of the node matching the symbol after the resolution on success.
+ * Negative error code on failure.
+ */
+static int fdt_resolve_import(const void *fdt, int target_node,
+ const char *importsym_name,
+ const char *importsym_compatible)
+{
+ /*
+ * Do not check the import symbol compatible string against the found
+ * node. Indeed, the found node can be a node without any compatible
+ * string and this is perfectly legit.
+ *
+ * The purpose of the import symbol compatible string is to give
+ * information related to the symbol imported.
+ *
+ * This can be use by a driver when it wants to dynamically resolve
+ * imported symbols based on some specific criteria. In that case, the
+ * compatible string helps the driver to identify the kind of symbol
+ * expected by the addon.
+ *
+ * Here, in libfdt, no driver are available to perform this dynamic
+ * resolution. Resolve the symbol using only its name.
+ */
+ return fdt_resolve_symbol(fdt, target_node, importsym_name,
+ strlen(importsym_name));
+}
+
+static int fdt_check_importsym_offset(const void *fdt, int offset, int *data_offset)
+{
+ int nextoffset, d;
+ uint32_t tag;
+
+ tag = fdt_next_tag(fdt, offset, &nextoffset);
+ switch (tag) {
+ case FDT_IMPORT_SYM:
+ if (data_offset) {
+ d = fdt_get_structured_tag_data(tag, fdt,
+ offset + FDT_TAGSIZE,
+ NULL);
+ if (d < 0)
+ return d;
+ *data_offset = d;
+ }
+ break;
+ default:
+ return -FDT_ERR_BADOFFSET;
+ }
+
+ return nextoffset;
+}
+
+static int fdt_next_importsym_nextoffset_(const void *fdt, int nextoffset)
+{
+ uint32_t tag;
+ int offset;
+
+ do {
+ offset = nextoffset;
+ tag = fdt_next_tag(fdt, offset, &nextoffset);
+
+ switch (tag) {
+ case FDT_IMPORT_SYM:
+ return offset;
+
+ case FDT_END:
+ if (nextoffset < 0)
+ return nextoffset;
+
+ return -FDT_ERR_NOTFOUND;
+ }
+ } while (tag != FDT_IMPORT_SYM);
+
+ return offset;
+}
+
+static int fdt_next_importsym_offset(const void *fdt, int offset)
+{
+ int nextoffset;
+
+ if (offset < 0)
+ return offset;
+
+ nextoffset = fdt_check_importsym_offset(fdt, offset, NULL);
+ if (nextoffset < 0)
+ return nextoffset;
+
+ return fdt_next_importsym_nextoffset_(fdt, nextoffset);
+}
+
+static int fdt_first_importsym_offset(const void *fdt)
+{
+ return fdt_next_importsym_nextoffset_(fdt, 0);
+}
+
+
+static const char *fdt_importsym_get_name(const void *fdt, int offset)
+{
+ int data_offset;
+
+ if (fdt_check_importsym_offset(fdt, offset, &data_offset) < 0)
+ return NULL;
+
+ return fdt_offset_ptr(fdt, data_offset, 1);
+}
+
+static const char *fdt_importsym_get_compatible(const void *fdt, int offset)
+{
+ int data_offset;
+
+ if (fdt_check_importsym_offset(fdt, offset, &data_offset) < 0)
+ return NULL;
+
+ /* Skip name */
+ data_offset = fdt_skip_string(fdt, data_offset);
+
+ return fdt_offset_ptr(fdt, data_offset, 1);
+}
+
+#define fdt_for_each_importsym(importsym, fdt) \
+ for (importsym = fdt_first_importsym_offset(fdt); \
+ importsym >= 0; \
+ importsym = fdt_next_importsym_offset(fdt, importsym))
+
+/**
+ * addon_resolve_symbol - Resolve a symbol from an addon
+ * @fdta: Addon device tree blob.
+ * @fdt: base device tree blob
+ * @target_node: Target node the addon is applied to. This node, in the base
+ * device tree blob, must have exported symbols needed for the
+ * resolution.
+ * @sym_name: Symbol name
+ * @sym_namelen: Length of the symbol name
+ *
+ * addon_resolve_symbol() resolved a symbol used by an addon using the import
+ * symbol table available in the addon device tree blob.
+ *
+ * returns:
+ * offset (in the fdt device tree blob) of the node matching the symbol
+ * after the resolution on success.
+ * Negative error code on failure.
+ */
+static int addon_resolve_symbol(const void *fdta, const void *fdt, int target_node,
+ const char *sym_name, size_t sym_namelen)
+{
+ const char *importsym_compat;
+ const char *importsym_name;
+ int importsym;
+
+ /* Look at imported symbols */
+ fdt_for_each_importsym(importsym, fdta) {
+ importsym_name = fdt_importsym_get_name(fdta, importsym);
+ if (!importsym_name)
+ return -FDT_ERR_BADSTRUCTURE;
+ importsym_compat = fdt_importsym_get_compatible(fdta, importsym);
+ if (!importsym_compat)
+ return -FDT_ERR_BADSTRUCTURE;
+
+ if ((strlen(importsym_name) != sym_namelen) ||
+ (memcmp(importsym_name, sym_name, sym_namelen) != 0))
+ continue;
+
+ return fdt_resolve_import(fdt, target_node, importsym_name,
+ importsym_compat);
+ }
+ /* Not found ... */
+ return -FDT_ERR_NOTFOUND;
+}
+
+/**
+ * addon_resolve_ref - Resolve a reference from an addon
+ * @fdta: Addon device tree blob.
+ * @fdt: base device tree blob
+ * @target_node: Target node the addon is applied to. This node, in the base
+ * device tree blob, must have exported symbols needed for the
+ * resolution.
+ * @ref: The reference to resolve
+ *
+ * addon_resolve_ref() resolves a reference used by an addon.
+ * A reference can be composed of several symbols separated by a '.' char.
+ * For instance "foo.bar.baz". To resolve this reference, addon_resolve_ref()
+ * resolves the "foo" symbol the based on node retrieve from this resolution,
+ * it resolves "bar" and so on up to the last symbol available on the reference.
+ *
+ * This last symbol resolution leads to the node offset returned by
+ * addon_resolve_ref().
+ *
+ * returns:
+ * offset (in the fdt device tree blob) of the node matching the reference
+ * after the resolution on success.
+ * Negative error code on failure.
+ */
+static int addon_resolve_ref(const void *fdta, const void *fdt, int target_node,
+ const char *ref)
+{
+ const char *end = ref + strlen(ref);
+ const char *r = ref;
+ const char *d;
+ int tmp_node;
+
+ /*
+ * A reference can be "foo.bar.baz" for instance.
+ * In that case, we need to resolve foo from the target_node exported
+ * symbols then bar from exported symbols defined in the node retrieve
+ * from foo resolution and so on to resolve the given reference.
+ *
+ * The first symbol (foo in the previous example reference) need to be
+ * resolved through import symbols to "jump" from the addon to the base
+ * device tree. The other ones are "jumps" in the base device tree
+ * without leaving it and so, import symbols are not involved for them.
+ */
+ tmp_node = target_node;
+ while (r < end) {
+ d = memchr(r, '.', end - r);
+ if (!d)
+ d = end;
+
+ if (r == ref)
+ tmp_node = addon_resolve_symbol(fdta, fdt, tmp_node, r, d-r);
+ else
+ tmp_node = fdt_resolve_symbol(fdt, tmp_node, r, d-r);
+
+ if (tmp_node < 0)
+ return tmp_node;
+
+ r = d + 1;
+ }
+
+ return tmp_node;
+}
+
+/**
+ * addon_resolve_phandles - Resolve addon external references
+ * @fdta: Addon device tree blob
+ * @fdt: Device tree blob the addon is going to be applied to
+ * @target_node: Offset of the node in fdt the addon is going to be applied to
+ *
+ * addon_resolve_phandles() resolved unresolved phandle symbols available in
+ * the addon.
+ *
+ * returns:
+ * 0 on success
+ * Negative error code on failure
+ */
+static int addon_resolve_phandles(void *fdta, const void *fdt, int target_node)
+{
+ int offset, next_offset, d;
+ int last_prop_offset = -1;
+ int prop_phandle_offset;
+ const fdt32_t *pfdt32;
+ uint32_t phandle;
+ const char *ref;
+ uint32_t tag;
+ int ref_node;
+ int ret;
+
+ /*
+ * phandles symbols that need to be resolved are:
+ * - reference in properties where a FDT_PROPDATA_PHANDLE_REF tag is
+ * available
+ * - reference in export symbols
+ */
+
+ next_offset = 0;
+ do {
+ offset = next_offset;
+ tag = fdt_next_tag(fdta, offset, &next_offset);
+ switch (tag) {
+ case FDT_BEGIN_NODE:
+ last_prop_offset = -1;
+ break;
+
+ case FDT_END_NODE:
+ last_prop_offset = -1;
+ break;
+
+ case FDT_PROP:
+ last_prop_offset = offset;
+ break;
+
+ case FDT_NOP:
+ break;
+
+ case FDT_PROPDATA_PHANDLE:
+ break;
+
+ case FDT_PROPDATA_PHANDLE_REF:
+ /*
+ * A property references an unresolved phandle. Get
+ * the offset of this phandle in the property data area
+ * and the reference of the phandle
+ */
+ d = fdt_get_structured_tag_data(tag, fdta,
+ offset + FDT_TAGSIZE, NULL);
+ if (d < 0)
+ return d;
+
+ pfdt32 = fdt_offset_ptr(fdta, d, sizeof(*pfdt32));
+ prop_phandle_offset = fdt32_to_cpu(*pfdt32);
+
+ d += sizeof(*pfdt32);
+ ref = fdt_offset_ptr(fdta, d, 1);
+
+ /* Resolve this phandle */
+ ref_node = addon_resolve_ref(fdta, fdt, target_node, ref);
+ if (ref_node < 0)
+ return ref_node;
+
+ phandle = fdt_get_phandle(fdt, ref_node);
+ if ((phandle == 0) || (phandle == ~0U))
+ return -FDT_ERR_BADPHANDLE;
+
+ if (last_prop_offset == -1)
+ return -FDT_ERR_BADSTRUCTURE;
+
+ /* Update property value */
+ ret = addon_property_set_val(fdta, last_prop_offset,
+ prop_phandle_offset,
+ phandle);
+ if (ret)
+ return ret;
+
+ break;
+
+ case FDT_BEGIN_NODE_REF:
+ last_prop_offset = -1;
+ break;
+
+ case FDT_END:
+ ret = (next_offset < 0) ? next_offset : 0;
+ break;
+
+ case FDT_EXPORT_SYM:
+ break;
+
+ case FDT_EXPORT_SYM_REF:
+ d = fdt_get_structured_tag_data(tag, fdta,
+ offset + FDT_TAGSIZE, NULL);
+ if (d < 0)
+ return d;
+
+ /*
+ * The export symbol references an unresolved phandle.
+ * Check that this reference can be resolved correctly.
+ * Even if there is no simple way to store the resolved
+ * phandle (resolution will be done again later when
+ * those export symbols are merged in the base device
+ * tree), it is consistent to check this resolution now
+ * in this function.
+ */
+
+ /* Get the reference */
+ ref = fdt_offset_ptr(fdta, d, 1);
+
+ /* Resolve the phandle */
+ ref_node = addon_resolve_ref(fdta, fdt, target_node, ref);
+ if (ref_node < 0)
+ return ref_node;
+
+ phandle = fdt_get_phandle(fdt, ref_node);
+ if ((phandle == 0) || (phandle == ~0U))
+ return -FDT_ERR_BADPHANDLE;
+
+ break;
+
+ case FDT_IMPORT_SYM:
+ break;
+
+ default:
+ return -FDT_ERR_BADSTRUCTURE;
+ }
+ } while (tag != FDT_END);
+
+ return ret;
+}
+
+/**
+ * addon_replace_local_phandles - Replace the old value of phandles by a new one
+ * for a whole addon
+ * @fdta: Device tree addon blob
+ * @old_phandle: Old phandle value
+ * @new_phandle: New phandle value
+ *
+ * addon_replace_local_phandles() replaces phandle values only if the
+ * old_phandle_value matches the current phandle value.
+ *
+ * returns:
+ * 0 on success
+ * Negative error code on failure
+ */
+static int addon_replace_local_phandles(void *fdta, uint32_t old_phandle,
+ uint32_t new_phandle)
+{
+ int offset, next_offset, d;
+ int last_prop_offset = -1;
+ int prop_phandle_offset;
+ const fdt32_t *pfdt32;
+ fdt32_t *pfdt32w;
+ uint32_t tag;
+ uint32_t val;
+ int ret;
+
+ /*
+ * phandles that need to be updated are:
+ * - phandles in properties where a FDT_PROPDATA_PHANDLE tag is
+ * available
+ * - phandles in FDT_EXPORT_SYM tags
+ *
+ * phandles properties in nodes are replace by
+ * addon_prevent_phandle_overwrite_node()
+ */
+
+ next_offset = 0;
+ do {
+ offset = next_offset;
+ tag = fdt_next_tag(fdta, offset, &next_offset);
+ switch (tag) {
+ case FDT_BEGIN_NODE:
+ /*
+ * "phandle" and/of "linux,phandle" properties are
+ * already updated by addon_prevent_phandle_overwrite_node()
+ * Nothing more to update here.
+ */
+ last_prop_offset = -1;
+ break;
+
+ case FDT_END_NODE:
+ last_prop_offset = -1;
+ break;
+
+ case FDT_PROP:
+ last_prop_offset = offset;
+ break;
+
+ case FDT_NOP:
+ break;
+
+ case FDT_PROPDATA_PHANDLE:
+ /*
+ * The property references a phandle. Get the offset
+ * of this phandle in the property data area.
+ */
+ d = fdt_get_structured_tag_data(tag, fdta,
+ offset + FDT_TAGSIZE, NULL);
+ if (d < 0)
+ return d;
+
+ pfdt32 = fdt_offset_ptr(fdta, d, sizeof(*pfdt32));
+ prop_phandle_offset = fdt32_to_cpu(*pfdt32);
+
+ if (last_prop_offset == -1)
+ return -FDT_ERR_BADSTRUCTURE;
+
+ /* Update the phandle value */
+ ret = addon_property_replace(fdta, last_prop_offset,
+ prop_phandle_offset,
+ old_phandle, new_phandle);
+ if (ret)
+ return ret;
+ break;
+
+ case FDT_PROPDATA_PHANDLE_REF:
+ break;
+
+ case FDT_BEGIN_NODE_REF:
+ last_prop_offset = -1;
+ break;
+
+ case FDT_END:
+ ret = (next_offset < 0) ? next_offset : 0;
+ break;
+
+ case FDT_EXPORT_SYM:
+ d = fdt_get_structured_tag_data(tag, fdta,
+ offset + FDT_TAGSIZE, NULL);
+ if (d < 0)
+ return d;
+
+ /* Get phandle pointer */
+ pfdt32w = fdt_offset_ptr_w(fdta, d, sizeof(*pfdt32));
+
+ /* Update the phandle value */
+ val = fdt32_ld(pfdt32w);
+ if (val == old_phandle)
+ fdt32_st(pfdt32w, new_phandle);
+ break;
+
+ case FDT_EXPORT_SYM_REF:
+ break;
+ case FDT_IMPORT_SYM:
+ break;
+
+ default:
+ return -FDT_ERR_BADSTRUCTURE;
+ }
+ } while (tag != FDT_END);
+
+ return ret;
+}
+
+/**
+ * addon_prevent_phandle_overwrite_node - Prevents phandle overwrite for a given
+ * addon node.
+ * @fdta: Addon Device tree blob
+ * @fdta_node: Node in fdta
+ * @fdt: Base device tree blob
+ * @fdt_node: Node in fdt that could be overwrite by the fdta_node
+ *
+ * returns:
+ * 0 on success
+ * Negative error code on failure
+ */
+static int addon_prevent_phandle_overwrite_node(void *fdta, int fdta_node,
+ const void *fdt, int fdt_node)
+{
+ uint32_t fdta_phandle, fdt_phandle;
+ int fdta_child, fdt_child;
+ const char *name;
+ int ret;
+
+ fdta_phandle = fdt_get_phandle(fdta, fdta_node);
+ fdt_phandle = fdt_get_phandle(fdt, fdt_node);
+
+ if (fdta_phandle && fdt_phandle) {
+ ret = addon_phandle_set_val(fdta, fdta_node, "phandle", fdt_phandle);
+ if (ret && ret != -FDT_ERR_NOTFOUND)
+ return ret;
+
+ ret = addon_phandle_set_val(fdta, fdta_node, "linux,phandle", fdt_phandle);
+ if (ret && ret != -FDT_ERR_NOTFOUND)
+ return ret;
+
+ ret = addon_replace_local_phandles(fdta, fdta_phandle, fdt_phandle);
+ if (ret)
+ return ret;
+ }
+
+ fdt_for_each_subnode(fdta_child, fdta, fdta_node) {
+ name = fdt_get_name(fdta, fdta_child, NULL);
+ if (!name)
+ return -FDT_ERR_BADSTRUCTURE;
+
+ fdt_child = fdt_subnode_offset(fdt, fdt_node, name);
+ if (fdt_child == -FDT_ERR_NOTFOUND) {
+ /*
+ * No further overwrites possible here as the addon node
+ * is new node.
+ */
+ continue;
+ }
+
+ ret = addon_prevent_phandle_overwrite_node(fdta, fdta_child,
+ fdt, fdt_child);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static const char *fdt_orphan_get_ref(const void *fdt, int offset)
+{
+ int data_offset;
+
+ if (fdt_check_orphan_offset(fdt, offset, &data_offset) < 0)
+ return NULL;
+
+ return fdt_offset_ptr(fdt, data_offset, 1);
+}
+
+/**
+ * addon_prevent_phandles_overwrite - Fixes addon phandles to not overwrite base
+ * phandles
+ * @fdta Addon Device tree blob
+ * @fdt: Base Device Tree blob
+ * @target_node: Target node in the base device tree the addon is going to be
+ * applied to
+ *
+ * Checks recursively if applying fdta overwrites phandle values in the base
+ * fdt. When such a phandle is found, the fdta is changed to use the fdt's
+ * phandle value to not break references in the base.
+ *
+ * returns:
+ * 0 on success
+ * Negative error code on failure
+ */
+static int addon_prevent_phandles_overwrite(void *fdta, const void *fdt,
+ int target_node)
+{
+ int fdt_orphan_target;
+ int addon_root_node;
+ const char *ref;
+ int fdta_orphan;
+ int ret;
+
+ addon_root_node = fdt_root_offset(fdta);
+ if (addon_root_node < 0 && addon_root_node != -FDT_ERR_NOTFOUND)
+ return addon_root_node;
+
+ if (addon_root_node >= 0) {
+ ret = addon_prevent_phandle_overwrite_node(fdta, addon_root_node,
+ fdt, target_node);
+ if (ret < 0)
+ return ret;
+ }
+
+ fdt_for_each_orphan(fdta_orphan, fdta) {
+ ref = fdt_orphan_get_ref(fdta, fdta_orphan);
+ if (!ref)
+ return -FDT_ERR_BADSTRUCTURE;
+
+ fdt_orphan_target = addon_resolve_ref(fdta, fdt, target_node, ref);
+ if (fdt_orphan_target < 0)
+ return fdt_orphan_target;
+
+ ret = addon_prevent_phandle_overwrite_node(fdta, fdta_orphan,
+ fdt, fdt_orphan_target);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int fdt_check_propmarker_offset(const void *fdt, int offset,
+ uint32_t *propmarker_tag, int *data_offset)
+{
+ int nextoffset, d;
+ uint32_t tag;
+
+ tag = fdt_next_tag(fdt, offset, &nextoffset);
+ switch (tag) {
+ case FDT_PROPDATA_PHANDLE:
+ case FDT_PROPDATA_PHANDLE_REF:
+ if (data_offset) {
+ d = fdt_get_structured_tag_data(tag, fdt,
+ offset + FDT_TAGSIZE,
+ NULL);
+ if (d < 0)
+ return d;
+ *data_offset = d;
+ }
+ break;
+ default:
+ return -FDT_ERR_BADOFFSET;
+ }
+
+ if (propmarker_tag)
+ *propmarker_tag = tag;
+
+ return nextoffset;
+}
+
+static int fdt_next_propmarker_nextoffset_(const void *fdt, int nextoffset)
+{
+ uint32_t tag;
+ int offset;
+
+ do {
+ offset = nextoffset;
+ tag = fdt_next_tag(fdt, offset, &nextoffset);
+ switch (tag) {
+ case FDT_EXPORT_SYM:
+ /* We are going to leave the current property (enter an export symbol) */
+ return -FDT_ERR_NOTFOUND;
+
+ case FDT_EXPORT_SYM_REF:
+ /* We are going to leave the current property (enter an export symbol) */
+ return -FDT_ERR_NOTFOUND;
+
+ case FDT_BEGIN_NODE:
+ /* We are going to leave the current property (new sub-node) */
+ return -FDT_ERR_NOTFOUND;
+
+ case FDT_BEGIN_NODE_REF:
+ /* We cannot have a node ref inside an other node */
+ return -FDT_ERR_BADSTRUCTURE;
+
+ case FDT_END_NODE:
+ /* We are going to leave the current node (parent node) */
+ return -FDT_ERR_NOTFOUND;
+
+ case FDT_PROP:
+ /* We are going to leave the current property (enter a new property) */
+ return -FDT_ERR_NOTFOUND;
+
+ case FDT_PROPDATA_PHANDLE:
+ return offset;
+
+ case FDT_PROPDATA_PHANDLE_REF:
+ return offset;
+
+ case FDT_END:
+ if (nextoffset < 0)
+ return nextoffset;
+
+ return -FDT_ERR_NOTFOUND;
+ }
+ } while (tag != FDT_END);
+
+ return offset;
+}
+
+static int fdt_next_propmarker_offset(const void *fdt, int offset)
+{
+ int nextoffset;
+
+ if (offset < 0)
+ return offset;
+
+ nextoffset = fdt_check_propmarker_offset(fdt, offset, NULL, NULL);
+ if (nextoffset < 0)
+ return nextoffset;
+
+ return fdt_next_propmarker_nextoffset_(fdt, nextoffset);
+}
+
+static int fdt_first_propmarker_offset(const void *fdt, int prop_offset)
+{
+ int nextoffset;
+
+ if (prop_offset < 0)
+ return prop_offset;
+
+ nextoffset = fdt_check_prop_offset_(fdt, prop_offset);
+ if (nextoffset < 0)
+ return nextoffset;
+
+ return fdt_next_propmarker_nextoffset_(fdt, nextoffset);
+
+}
+
+static int fdt_propmarker_is_phandle(const void *fdt, int offset)
+{
+ uint32_t tag;
+
+ /*
+ * Only property 'phandle' markers are currently supported but well,
+ * prevent some issue that would happen if some other markers are
+ * added in the future.
+ */
+
+ /* A propmarker phandle has to be propmarker ... */
+ if (fdt_check_propmarker_offset(fdt, offset, &tag, NULL) < 0)
+ return false;
+
+ /* ... and a 'phandle' */
+ if ((tag != FDT_PROPDATA_PHANDLE) && (tag != FDT_PROPDATA_PHANDLE_REF))
+ return false;
+
+ return true;
+}
+
+static uint32_t fdt_propmarker_phandle_get_propoffset(const void *fdt,
+ int offset)
+{
+ const fdt32_t *pfdt32;
+ int data_offset;
+
+ if (fdt_check_propmarker_offset(fdt, offset, NULL, &data_offset) < 0)
+ return ~0U;
+
+ pfdt32 = fdt_offset_ptr(fdt, data_offset, sizeof(*pfdt32));
+ if (!pfdt32)
+ return ~0U;
+
+ return fdt32_ld_(pfdt32);
+}
+
+#define fdt_for_each_propmarker(propmarker, fdt, prop) \
+ for (propmarker = fdt_first_propmarker_offset(fdt, prop); \
+ propmarker >= 0; \
+ propmarker = fdt_next_propmarker_offset(fdt, propmarker))
+
+static int fdt_mark_property_phandle(void *fdt, int fdt_property,
+ uint32_t propoffset)
+{
+ int propmarker;
+ uint32_t tmp;
+
+ fdt_for_each_propmarker(propmarker, fdt, fdt_property) {
+ if (!fdt_propmarker_is_phandle(fdt, propmarker))
+ continue;
+
+ tmp = fdt_propmarker_phandle_get_propoffset(fdt, propmarker);
+ if (tmp == ~0U)
+ return -FDT_ERR_BADSTRUCTURE;
+
+ if (tmp == propoffset)
+ return -FDT_ERR_EXISTS;
+ }
+
+ return fdt_mark_property_phandle_(fdt, fdt_property, propoffset);
+}
+
+static int fdt_add_exportsym(void *fdt, int fdt_node,
+ const char *exportsym_name, uint32_t phandle)
+{
+ const char *tmp;
+ int exportsym;
+
+ fdt_for_each_exportsym(exportsym, fdt, fdt_node) {
+ tmp = fdt_exportsym_get_name(fdt, exportsym);
+ if (!tmp)
+ return -FDT_ERR_BADSTRUCTURE;
+
+ if (!strcmp(tmp, exportsym_name))
+ return -FDT_ERR_EXISTS;
+ }
+
+ return fdt_add_exportsym_(fdt, fdt_node, exportsym_name, phandle);
+}
+
+/**
+ * addon_merge_node_properties - Merges properties available in an addon node
+ * into the base device tree node
+ * @fdta: Addon Device Tree blob
+ * @fdta_node: Addon node whose properties have to be merged
+ * @fdt: Base Device Tree blob
+ * @fdt_node: Base node the addon node properties have to be merged into
+ * @is_existing_node: True if the node was already existing in the base device
+ * tree blob.
+ *
+ * returns:
+ * 0 on success
+ * Negative error code on failure
+ */
+static int addon_merge_node_properties(const void *fdta, int fdta_node,
+ void *fdt, int fdt_node,
+ bool is_existing_node)
+{
+ const void *prop, *prop2;
+ int prop_len, prop2_len;
+ uint32_t propoffset;
+ int fdta_property;
+ int fdt_property;
+ const char *name;
+ int propmarker;
+ int ret;
+
+ fdt_for_each_property_offset(fdta_property, fdta, fdta_node) {
+ prop = fdt_getprop_by_offset(fdta, fdta_property, &name,
+ &prop_len);
+ if (prop_len == -FDT_ERR_NOTFOUND)
+ return -FDT_ERR_INTERNAL;
+ if (prop_len < 0)
+ return prop_len;
+
+ if (is_existing_node) {
+ if (!strcmp(name, "phandle") ||
+ !strcmp(name, "linux,phandle")) {
+ /*
+ * phandles available in both addon and base dtb
+ * have the same values. This is ensured by
+ * previous operation related to phandle
+ * resolution done in addon_prevent_phandles_overwrite().
+ * Just skip the property merge.
+ */
+ continue;
+ }
+
+ /*
+ * It is not allowed to add or modify a property in an
+ * existing node.
+ */
+ prop2 = fdt_getprop(fdt, fdt_node, name, &prop2_len);
+ if (prop2_len == -FDT_ERR_NOTFOUND)
+ return -FDT_ERR_EXISTS; /* Cannot add a property */
+ if (prop2_len < 0)
+ return prop2_len;
+
+ if (prop_len != prop2_len)
+ return -FDT_ERR_EXISTS; /* Cannot modify a property */
+
+ if (memcmp(prop, prop2, prop_len) != 0)
+ return -FDT_ERR_EXISTS; /* Cannot modify a property */
+
+ /*
+ * The property is already present but it has the exact
+ * same value. Ok, just skip the property merge.
+ */
+ continue;
+ }
+
+ ret = fdt_setprop(fdt, fdt_node, name, prop, prop_len);
+ if (ret)
+ return ret;
+
+ /* Retrieve the newly created fdt property offset */
+ fdt_property = fdt_getprop_offset(fdt, fdt_node, name);
+ if (fdt_property < 0)
+ return -FDT_ERR_INTERNAL;
+
+ fdt_for_each_propmarker(propmarker, fdta, fdta_property) {
+ if (!fdt_propmarker_is_phandle(fdta, propmarker))
+ continue;
+
+ propoffset = fdt_propmarker_phandle_get_propoffset(fdta,
+ propmarker);
+ /*
+ * Mark the property as a local phandle reference.
+ * Either it was already a local phandle reference in
+ * the addon or it was an external phandle reference in
+ * the addon and has been resolved.
+ * In all cases it is local in the merged device tree
+ * blob.
+ */
+ ret = fdt_mark_property_phandle(fdt, fdt_property,
+ propoffset);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * addon_merge_node_exports - Merges exported symbols in an addon node
+ * into the base device tree node
+ * @fdta: Addon Device Tree blob
+ * @fdta_node: Addon node whose exported symbols have to be merged
+ * @fdt: Base Device Tree blob
+ * @fdt_node: Base node the addon node exported symbols have to be merged into
+ * @is_existing_node: True if the node was already existing in the base device
+ * tree blob.
+ * @target_path: Path in the base Device Tree to the node where the addon is
+ * applied to. This is the node where export symbols used for symbol
+ * resolution are present.
+ *
+ * returns:
+ * 0 on success
+ * Negative error code on failure
+ */
+static int addon_merge_node_exports(const void *fdta, int fdta_node,
+ void *fdt, int fdt_node,
+ bool is_existing_node, const char *target_path)
+{
+ const char *exportsym_name;
+ const char *exportsym_ref;
+ uint32_t phandle;
+ int target_node;
+ int exportsym;
+ int ref_node;
+ int ret;
+
+ fdt_for_each_exportsym(exportsym, fdta, fdta_node) {
+
+ if (is_existing_node) {
+ /*
+ * It is not allowed to add or modify export symbols in
+ * existing nodes.
+ */
+ return -FDT_ERR_EXISTS;
+ }
+
+ exportsym_name = fdt_exportsym_get_name(fdta, exportsym);
+ if (!exportsym_name)
+ return -FDT_ERR_BADSTRUCTURE;
+
+ if (fdt_exportsym_is_ref(fdta, exportsym)) {
+ /*
+ * The export symbol is a reference one.
+ * Get the phandle from the node referenced by the
+ * export symbol reference.
+ */
+
+ /* Get the reference */
+ exportsym_ref = fdt_exportsym_get_ref(fdta, exportsym);
+ if (!exportsym_ref)
+ return FDT_ERR_BADSTRUCTURE;
+
+ /* Get the target node */
+ target_node = fdt_path_offset(fdt, target_path);
+ if (target_node < 0)
+ return target_node;
+
+ /* Resolve the reference */
+ ref_node = addon_resolve_ref(fdta, fdt, target_node, exportsym_ref);
+ if (ref_node < 0)
+ return ref_node;
+
+ /* Finally get the phandle */
+ phandle = fdt_get_phandle(fdt, ref_node);
+ } else {
+ /*
+ * The export symbol is not a reference one.
+ * Get directly the phandle attached the symbol.
+ */
+ phandle = fdt_exportsym_get_phandle(fdta, exportsym);
+ }
+
+ if ((phandle == 0) || (phandle == ~0U))
+ return -FDT_ERR_BADPHANDLE;
+
+ ret = fdt_add_exportsym(fdt, fdt_node, exportsym_name, phandle);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * addon_merge_node - Merges an addon node and its sub-nodes into the base
+ * device tree
+ * @fdta: Addon Device Tree blob
+ * @fdta_node: Addon node to be merged
+ * @fdt: Base Device Tree blob
+ * @fdt_node: Base node the addon node has to be merged into
+ * @is_existing_node: Flag set to true if the node already exist in the Base
+ * Device Tree blob
+ * @target_path: Path in the base Device Tree to the node where the addon is
+ * applied to. This is the node where export symbols used for symbol
+ * resolution are present.
+ *
+ * returns:
+ * 0 on success
+ * Negative error code on failure
+ */
+static int addon_merge_node(const void *fdta, int fdta_node, void *fdt,
+ int fdt_node, bool is_existing_node,
+ const char *target_path)
+{
+ bool is_existing;
+ int fdta_subnode;
+ const char *name;
+ int fdt_subnode;
+ int ret;
+
+ ret = addon_merge_node_exports(fdta, fdta_node, fdt, fdt_node,
+ is_existing_node, target_path);
+ if (ret)
+ return ret;
+
+ ret = addon_merge_node_properties(fdta, fdta_node, fdt, fdt_node,
+ is_existing_node);
+ if (ret)
+ return ret;
+
+ fdt_for_each_subnode(fdta_subnode, fdta, fdta_node) {
+ name = fdt_get_name(fdta, fdta_subnode, NULL);
+
+ is_existing = false;
+ fdt_subnode = fdt_add_subnode(fdt, fdt_node, name);
+ if (fdt_subnode == -FDT_ERR_EXISTS) {
+ is_existing = true;
+ fdt_subnode = fdt_subnode_offset(fdt, fdt_node, name);
+ if (fdt_subnode == -FDT_ERR_NOTFOUND)
+ return -FDT_ERR_INTERNAL;
+ }
+
+ if (fdt_subnode < 0)
+ return fdt_subnode;
+
+ ret = addon_merge_node(fdta, fdta_subnode, fdt, fdt_subnode,
+ is_existing, target_path);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int addon_merge(void *fdta, void *fdt, const char *target_path)
+{
+ int fdt_orphan_target;
+ int fdta_orphan;
+ const char *ref;
+ int target_node;
+ int ret;
+
+ /* Get the target node from its path */
+ target_node = fdt_path_offset(fdt, target_path);
+ if (target_node < 0)
+ return target_node;
+
+ /* Merge the addon root node into the base target_node */
+ ret = addon_merge_node(fdta, 0, fdt, target_node, true, target_path);
+ if (ret < 0)
+ return ret;
+
+ fdt_for_each_orphan(fdta_orphan, fdta) {
+ ref = fdt_orphan_get_ref(fdta, fdta_orphan);
+ if (!ref)
+ return -FDT_ERR_BADSTRUCTURE;
+
+ fdt_orphan_target = addon_resolve_ref(fdta, fdt, target_node, ref);
+ if (fdt_orphan_target < 0)
+ return fdt_orphan_target;
+
+ ret = addon_merge_node(fdta, fdta_orphan, fdt, fdt_orphan_target,
+ true, target_path);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * An orphan node has been merged. The merged node can be
+ * anywhere in the fdt. Depending on the location of the merged
+ * node in the fdt, the target node offset can be no longer
+ * valid.
+ *
+ * Reload the offset from the target path to have a valid value.
+ */
+ target_node = fdt_path_offset(fdt, target_path);
+ if (target_node < 0)
+ return target_node;
+ }
+
+ return 0;
+}
+
+int fdt_addon_apply(void *fdt, void *fdta, const char *target)
+{
+ int target_node;
+ uint32_t delta;
+ int ret;
+
+ FDT_RO_PROBE(fdt);
+ FDT_RO_PROBE(fdta);
+
+ /* Get the target node */
+ target_node = fdt_path_offset(fdt, target);
+ if (target_node < 0)
+ return target_node;
+
+ ret = fdt_find_max_phandle(fdt, &delta);
+ if (ret)
+ return ret;
+
+ /* Increase all phandles in the fdta by delta */
+ ret = addon_adjust_local_phandles(fdta, delta);
+ if (ret)
+ goto err;
+
+ /* Resolve phandles */
+ ret = addon_resolve_phandles(fdta, fdt, target_node);
+ if (ret < 0)
+ goto err;
+
+ /* Don't overwrite phandles in fdt */
+ ret = addon_prevent_phandles_overwrite(fdta, fdt, target_node);
+ if (ret < 0)
+ goto err;
+
+ ret = addon_merge(fdta, fdt, target);
+ if (ret < 0)
+ goto err;
+
+ /* The addon has been damaged, erase its magic */
+ fdt_set_magic(fdta, ~0);
+
+ return 0;
+
+err:
+ /* The addon might have been damaged, erase its magic */
+ fdt_set_magic(fdta, ~0);
+
+ /* The base device tree might have been damaged, erase its magic */
+ fdt_set_magic(fdt, ~0);
+
+ return ret;
+}
diff --git a/libfdt/fdt_rw.c b/libfdt/fdt_rw.c
index 30f3da40..e0c01af5 100644
--- a/libfdt/fdt_rw.c
+++ b/libfdt/fdt_rw.c
@@ -553,6 +553,75 @@ int fdt_delprop(void *fdt, int nodeoffset, const char *name)
return fdt_splice_struct_(fdt, prop, proplen, 0);
}
+int fdt_mark_property_phandle_(void *fdt, int fdt_property, uint32_t propoffset)
+{
+ fdt32_t *markerp;
+ int nextoffset;
+ int err;
+
+ FDT_RW_PROBE(fdt);
+
+ nextoffset = fdt_check_prop_offset_(fdt, fdt_property);
+ if (nextoffset < 0)
+ return nextoffset;
+
+ /* Insert the marker at next_offset, i.e. right after the property */
+ markerp = fdt_offset_ptr_w_(fdt, nextoffset);
+ err = fdt_splice_struct_(fdt, markerp, 0, FDT_TAGSIZE + FDT_CELLSIZE);
+ if (err)
+ return err;
+
+ *(markerp++) = cpu_to_fdt32(FDT_PROPDATA_PHANDLE);
+ *markerp = cpu_to_fdt32(propoffset);
+
+ return 0;
+}
+
+int fdt_add_exportsym_(void *fdt, int nodeoffset, const char *name,
+ uint32_t phandle)
+{
+ int namelen, exportlen, datalen;
+ int nextoffset;
+ char *p;
+ int err;
+
+ FDT_RW_PROBE(fdt);
+
+ nextoffset = fdt_check_node_offset_(fdt, nodeoffset);
+ if (nextoffset < 0)
+ return nextoffset;
+
+ /*
+ * nextoffset it at the first tag after the node.
+ * Skip possible unknown tags and metadata tags related to the node in
+ * order to add the new export sym tag after those tags.
+ */
+ nextoffset = fdt_node_skip_unknown_and_metadata_tags(fdt, nextoffset);
+ if (nextoffset < 0)
+ return nextoffset;
+
+
+ p = fdt_offset_ptr_w_(fdt, nextoffset);
+ namelen = strlen(name);
+ datalen = FDT_CELLSIZE + namelen + 1;
+ exportlen = FDT_TAGSIZE + FDT_CELLSIZE + FDT_CELLALIGN(datalen);
+
+ err = fdt_splice_struct_(fdt, p, 0, exportlen);
+ if (err)
+ return err;
+
+ *((fdt32_t *)p) = cpu_to_fdt32(FDT_EXPORT_SYM);
+ p += FDT_TAGSIZE;
+ *((fdt32_t *)p) = cpu_to_fdt32(datalen);
+ p += FDT_CELLSIZE;
+ *((fdt32_t *)p) = cpu_to_fdt32(phandle);
+ p += FDT_CELLSIZE;
+ memset(p, 0, FDT_CELLALIGN(namelen+1));
+ memcpy(p, name, namelen);
+
+ return 0;
+}
+
int fdt_add_subnode_namelen(void *fdt, int parentoffset,
const char *name, int namelen)
{
diff --git a/libfdt/libfdt.h b/libfdt/libfdt.h
index ee811266..3ef29beb 100644
--- a/libfdt/libfdt.h
+++ b/libfdt/libfdt.h
@@ -2538,6 +2538,38 @@ int fdt_overlay_apply(void *fdt, void *fdto);
int fdt_overlay_target_offset(const void *fdt, const void *fdto,
int fragment_offset, char const **pathp);
+/**
+ * fdt_addon_apply - Applies a DT addon on a base DT
+ * @fdt: pointer to the base device tree blob
+ * @fdta: pointer to the device tree addon blob
+ * @target: target node path
+ *
+ * fdt_addon_apply() will apply the given device tree addon on the given target
+ * node in base device tree.
+ *
+ * Expect the base device tree to be modified, even if the function
+ * returns an error.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, there's not enough space in the base device tree
+ * -FDT_ERR_NOTFOUND, the overlay points to some nonexistent nodes or
+ * properties in the base DT
+ * -FDT_ERR_BADPHANDLE,
+ * -FDT_ERR_BADOVERLAY,
+ * -FDT_ERR_NOPHANDLES,
+ * -FDT_ERR_INTERNAL,
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADOFFSET,
+ * -FDT_ERR_BADPATH,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_addon_apply(void *fdt, void *fdta, const char *target);
+
/**********************************************************************/
/* Debugging / informational functions */
/**********************************************************************/
diff --git a/libfdt/libfdt_internal.h b/libfdt/libfdt_internal.h
index be47639d..9379a510 100644
--- a/libfdt/libfdt_internal.h
+++ b/libfdt/libfdt_internal.h
@@ -51,6 +51,11 @@ static inline int fdt_getprop_offset(const void *fdt, int nodeoffset,
return fdt_getprop_offset_namelen(fdt, nodeoffset, name, strlen(name));
}
+int fdt_mark_property_phandle_(void *fdt, int fdt_property,
+ uint32_t propoffset);
+int fdt_add_exportsym_(void *fdt, int nodeoffset, const char *name,
+ uint32_t phandle);
+
const char *fdt_find_string_len_(const char *strtab, int tabsize, const char *s,
int s_len);
static inline const char *fdt_find_string_(const char *strtab, int tabsize,
diff --git a/libfdt/meson.build b/libfdt/meson.build
index 68d4c1d1..a6e875f4 100644
--- a/libfdt/meson.build
+++ b/libfdt/meson.build
@@ -9,6 +9,7 @@ sources = files(
'fdt_check.c',
'fdt_empty_tree.c',
'fdt_overlay.c',
+ 'fdt_addon.c',
'fdt_ro.c',
'fdt_rw.c',
'fdt_strerror.c',
diff --git a/libfdt/version.lds b/libfdt/version.lds
index d0b71669..74925ac0 100644
--- a/libfdt/version.lds
+++ b/libfdt/version.lds
@@ -83,6 +83,7 @@ LIBFDT_1.2 {
fdt_overlay_target_offset;
fdt_get_symbol;
fdt_get_symbol_namelen;
+ fdt_addon_apply;
local:
*;
};
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 64/74] Add fdtaddon tool to apply an addon
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (62 preceding siblings ...)
2026-08-26 9:49 ` [RFC PATCH v2 63/74] libfdt: Add support for applying an addon on a base device-tree blob Herve Codina
@ 2026-08-26 9:49 ` Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 65/74] tests: Add a first basic test for fdtaddon Herve Codina
` (9 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:49 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
libfdt has support for applying an addon on top of a base device-tree.
This is provided by the libfdt fdt_addon_apply() function.
The fdtaddon tool is command line tool which allows to apply addon dtb
file to a base device-tree dtb file. It relies on fdt_addon_apply().
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
Makefile | 5 ++
Makefile.utils | 6 ++
fdtaddon.c | 197 +++++++++++++++++++++++++++++++++++++++++++++++++
meson.build | 2 +-
4 files changed, 209 insertions(+), 1 deletion(-)
create mode 100644 fdtaddon.c
diff --git a/Makefile b/Makefile
index 9110d177..175c400b 100644
--- a/Makefile
+++ b/Makefile
@@ -164,6 +164,7 @@ BIN += fdtdump
BIN += fdtget
BIN += fdtput
BIN += fdtoverlay
+BIN += fdtaddon
SCRIPTS = dtdiff
@@ -177,6 +178,7 @@ ifneq ($(MAKECMDGOALS),libfdt)
-include $(FDTGET_OBJS:%.o=%.d)
-include $(FDTPUT_OBJS:%.o=%.d)
-include $(FDTOVERLAY_OBJS:%.o=%.d)
+-include $(FDTADDON_OBJS:%.o=%.d)
endif
endif
@@ -260,6 +262,8 @@ fdtput: $(FDTPUT_OBJS) $(LIBFDT_dep)
fdtoverlay: $(FDTOVERLAY_OBJS) $(LIBFDT_dep)
+fdtaddon: $(FDTADDON_OBJS) $(LIBFDT_dep)
+
tags: FORCE
rm -f tags
find . \( -name tests -type d -prune \) -o \
@@ -277,6 +281,7 @@ TESTS_BIN += fdtput
TESTS_BIN += fdtget
TESTS_BIN += fdtdump
TESTS_BIN += fdtoverlay
+TESTS_BIN += fdtaddon
ifneq ($(MAKECMDGOALS),libfdt)
include tests/Makefile.tests
diff --git a/Makefile.utils b/Makefile.utils
index 9436b34d..eae9222b 100644
--- a/Makefile.utils
+++ b/Makefile.utils
@@ -29,3 +29,9 @@ FDTOVERLAY_SRCS = \
util.c
FDTOVERLAY_OBJS = $(FDTOVERLAY_SRCS:%.c=%.o)
+
+FDTADDON_SRCS = \
+ fdtaddon.c \
+ util.c
+
+FDTADDON_OBJS = $(FDTADDON_SRCS:%.c=%.o)
diff --git a/fdtaddon.c b/fdtaddon.c
new file mode 100644
index 00000000..037851d9
--- /dev/null
+++ b/fdtaddon.c
@@ -0,0 +1,197 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2025 Bootlin. All rights reserved.
+ *
+ * Author:
+ * Herve Codina <herve.codina@bootlin.com>
+ */
+
+#include <assert.h>
+#include <ctype.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include <libfdt.h>
+
+#include "util.h"
+
+/* Usage related data. */
+static const char usage_synopsis[] =
+ "apply an addon to a base blob\n"
+ " fdtaddon <options> <addon.dtba>";
+static const char usage_short_opts[] = "i:o:t:v" USAGE_COMMON_SHORT_OPTS;
+static const struct option usage_long_opts[] = {
+ { "input", required_argument, NULL, 'i' },
+ { "output", required_argument, NULL, 'o' },
+ { "target", required_argument, NULL, 't' },
+ { "verbose", no_argument, NULL, 'v' },
+ USAGE_COMMON_LONG_OPTS,
+};
+static const char *const usage_opts_help[] = { "Input base DT blob",
+ "Output DT blob", "Target node",
+ "Verbose messages",
+ USAGE_COMMON_OPTS_HELP };
+
+static void *do_apply(void *base, const void *addon, const char *target)
+{
+ void *tmp_merged;
+ void *tmp_addon;
+ size_t max_merged_size;
+ int ret;
+
+ /*
+ * We take copies first, because a failed apply can trash
+ * both the base blob and the overlay.
+ */
+
+ /*
+ * The merged size should not be greater than the sum of the size of
+ * individual items.
+ */
+ max_merged_size = fdt_totalsize(base) + fdt_totalsize(addon);
+
+ tmp_merged = xmalloc(max_merged_size);
+ ret = fdt_open_into(base, tmp_merged, max_merged_size);
+ if (ret) {
+ fprintf(stderr,
+ "\nFailed to make temporary copy: %s\n",
+ fdt_strerror(ret));
+ free(tmp_merged);
+ return NULL;
+ }
+
+ tmp_addon = xmalloc(fdt_totalsize(addon));
+ memcpy(tmp_addon, addon, fdt_totalsize(addon));
+
+ if (!(fdt_dt_flags(tmp_addon) & FDT_FLAG_ADDON)) {
+ fprintf(stderr,
+ "\nAddon dtb is not an 'addon'\n");
+ goto fail;
+ }
+
+ ret = fdt_addon_apply(tmp_merged, tmp_addon, target);
+ if (ret) {
+ fprintf(stderr, "\nFailed to apply %s\n", fdt_strerror(ret));
+ goto fail;
+ }
+
+ free(tmp_addon);
+ return tmp_merged;
+
+fail:
+ free(tmp_merged);
+ free(tmp_addon);
+ return NULL;
+}
+
+static int do_fdtaddon(const char *input_filename, const char *output_filename,
+ const char *addon_filename, const char *target)
+{
+ void *base_blob = NULL;
+ void *addon_blob = NULL;
+ void *merged_blob = NULL;
+ size_t base_buflen;
+ size_t addon_buflen;
+ int ret = -1;
+
+ base_blob = utilfdt_read(input_filename, &base_buflen);
+ if (!base_blob) {
+ fprintf(stderr, "\nFailed to read '%s'\n", input_filename);
+ goto out_err;
+ }
+ if (fdt_totalsize(base_blob) > base_buflen) {
+ fprintf(stderr,
+ "\nBase blob is incomplete (%zu / %"PRIu32" bytes read)\n",
+ base_buflen, fdt_totalsize(base_blob));
+ goto out_err;
+ }
+
+ addon_blob = utilfdt_read(addon_filename, &addon_buflen);
+ if (!addon_blob) {
+ fprintf(stderr, "\nFailed to read '%s'\n", addon_filename);
+ goto out_err;
+ }
+ if (fdt_totalsize(addon_blob) > addon_buflen) {
+ fprintf(stderr,
+ "\nAddon blob is incomplete (%zu / %"PRIu32" bytes read)\n",
+ addon_buflen, fdt_totalsize(addon_blob));
+ goto out_err;
+ }
+
+ /* apply the addon */
+ merged_blob = do_apply(base_blob, addon_blob, target);
+ if (!merged_blob)
+ goto out_err;
+
+ fdt_pack(merged_blob);
+ ret = utilfdt_write(output_filename, merged_blob);
+ if (ret)
+ fprintf(stderr, "\nFailed to write '%s'\n", output_filename);
+
+out_err:
+ free(merged_blob);
+ free(addon_blob);
+ free(base_blob);
+
+ return ret;
+}
+
+int main(int argc, char *argv[])
+{
+ char *input_filename = NULL;
+ char *output_filename = NULL;
+ char *addon_filename = NULL;
+ const char *target = NULL;
+ int verbose;
+ int opt;
+
+ while ((opt = util_getopt_long()) != EOF) {
+ switch (opt) {
+ case_USAGE_COMMON_FLAGS
+
+ case 'i':
+ input_filename = optarg;
+ break;
+ case 'o':
+ output_filename = optarg;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case 't':
+ target = optarg;
+ break;
+ }
+ }
+
+ if (!input_filename)
+ usage("missing input file");
+
+ if (!output_filename)
+ usage("missing output file");
+
+ if (!target)
+ usage("missing target");
+
+ argv += optind;
+ argc -= optind;
+
+ if (argc != 1)
+ usage("missing addon file");
+
+ addon_filename = argv[0];
+
+ if (verbose) {
+ printf("input = %s\n", input_filename);
+ printf("output = %s\n", output_filename);
+ printf("addon = %s\n", addon_filename);
+ }
+
+ if (do_fdtaddon(input_filename, output_filename, addon_filename, target))
+ return 1;
+
+ return 0;
+}
diff --git a/meson.build b/meson.build
index 501b706c..7f7e83b4 100644
--- a/meson.build
+++ b/meson.build
@@ -108,7 +108,7 @@ if get_option('tools') and not wheel_only
install: true,
)
- foreach e: ['fdtdump', 'fdtget', 'fdtput', 'fdtoverlay']
+ foreach e: ['fdtdump', 'fdtget', 'fdtput', 'fdtoverlay', 'fdtaddon']
dtc_tools += executable(e, files(e + '.c'), dependencies: util_dep, install: true)
endforeach
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 65/74] tests: Add a first basic test for fdtaddon
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (63 preceding siblings ...)
2026-08-26 9:49 ` [RFC PATCH v2 64/74] Add fdtaddon tool to apply an addon Herve Codina
@ 2026-08-26 9:49 ` Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 66/74] tests: fdtaddon: Add a basic test for addons using an orphan nodes Herve Codina
` (8 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:49 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
The fdtaddon tests family goal is to test features related the
application of an addon to a base device tree.
This first basic test added in fdtaddon tests is related to the
application of a simple addon.
The terminology used in the test is the following:
- dts: A 'full' device tree source.
This can be either a base device tree or a merged device tree (i.e. a
base device tree where an addon has been applied to).
- dtb: A 'full' device tree blob
This can be either a dtb generated by dtc from a dts or the output of
the fdtaddon command.
- dtsa: An addon device tree source
A dts with the '/addon/' keyword.
- dtba: An addon device tree blob
This a device tree blob generated from a dtsa.
With this terminology in mind, the test pattern is the following:
- Generate the base tree dtb (fdtaddon_base.dtb)
- Check this generated dtb against expected contents
- Generate a dtba (xxx.dtba) from an input dtsa
- Check this generated dtba against expected contents
- Apply the dtba to the base tree dtb at node-a1 (xxx-merged1.dtb)
- Check this generated dtb against expected contents
- Generate a dts (xxx-merged1.dtb.dts) from the generated xxx-merged1.dtb
- Check this generated dts against expected contents
- Apply the dtba to the base tree dtb at node-a2 (xxx-merged2.dtb)
- Check this generated dtb against expected contents
- Generate a dts (xxx-merged2.dtb.dts) from the generated xxx-merged2.dtb
- Check this generated dts against expected contents
Even if only one basic addon dsta is currently provided in this tests
introduction, use a loop in order to ease future addition consisting in
testing other features based on more complex addon dtsa.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
tests/fdtaddon_base.dtb.expect | 24 ++++++++++
tests/fdtaddon_base.dts | 27 +++++++++++
tests/fdtaddon_basics1-merged1.dtb.dts.expect | 35 ++++++++++++++
tests/fdtaddon_basics1-merged1.dtb.expect | 27 +++++++++++
tests/fdtaddon_basics1-merged2.dtb.dts.expect | 35 ++++++++++++++
tests/fdtaddon_basics1-merged2.dtb.expect | 27 +++++++++++
tests/fdtaddon_basics1.dtba.expect | 8 ++++
tests/fdtaddon_basics1.dtsa | 13 +++++
tests/meson.build | 1 +
tests/run_tests.sh | 47 ++++++++++++++++++-
tests/testutils.sh | 1 +
11 files changed, 244 insertions(+), 1 deletion(-)
create mode 100644 tests/fdtaddon_base.dtb.expect
create mode 100644 tests/fdtaddon_base.dts
create mode 100644 tests/fdtaddon_basics1-merged1.dtb.dts.expect
create mode 100644 tests/fdtaddon_basics1-merged1.dtb.expect
create mode 100644 tests/fdtaddon_basics1-merged2.dtb.dts.expect
create mode 100644 tests/fdtaddon_basics1-merged2.dtb.expect
create mode 100644 tests/fdtaddon_basics1.dtba.expect
create mode 100644 tests/fdtaddon_basics1.dtsa
diff --git a/tests/fdtaddon_base.dtb.expect b/tests/fdtaddon_base.dtb.expect
new file mode 100644
index 00000000..2da5be4d
--- /dev/null
+++ b/tests/fdtaddon_base.dtb.expect
@@ -0,0 +1,24 @@
+/dts-v1/;
+
+/ {
+ base-node {
+ sub-node {
+ prop = <0x00000000>;
+ phandle = <0x00000002>;
+ };
+ };
+ somewhere {
+ node-a1 {
+ // [FDT_EXPORT_SYM] 'node_a' -> phandle 0x00000001
+ // [FDT_EXPORT_SYM] 'other' -> phandle 0x00000002
+ compatible = "abc,aaa";
+ phandle = <0x00000001>;
+ };
+ node-a2 {
+ // [FDT_EXPORT_SYM] 'node_a' -> phandle 0x00000003
+ // [FDT_EXPORT_SYM] 'other' -> phandle 0x00000002
+ compatible = "abc,aaa";
+ phandle = <0x00000003>;
+ };
+ };
+};
diff --git a/tests/fdtaddon_base.dts b/tests/fdtaddon_base.dts
new file mode 100644
index 00000000..3b001461
--- /dev/null
+++ b/tests/fdtaddon_base.dts
@@ -0,0 +1,27 @@
+// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
+/*
+ * Copyright (C) 2026 Bootlin
+ */
+
+/dts-v1/;
+
+/ {
+ base-node {
+ other_a: sub-node {
+ prop = <0>;
+ };
+ };
+
+ somewhere {
+ node_a1: node-a1 {
+ compatible = "abc,aaa";
+ /export/ node_a: &node_a1;
+ /export/ other: &other_a;
+ };
+ node_a2: node-a2 {
+ compatible = "abc,aaa";
+ /export/ node_a: &node_a2;
+ /export/ other: &other_a;
+ };
+ };
+};
diff --git a/tests/fdtaddon_basics1-merged1.dtb.dts.expect b/tests/fdtaddon_basics1-merged1.dtb.dts.expect
new file mode 100644
index 00000000..927733c1
--- /dev/null
+++ b/tests/fdtaddon_basics1-merged1.dtb.dts.expect
@@ -0,0 +1,35 @@
+/dts-v1/;
+
+/ {
+
+ base-node {
+
+ sub-node {
+ prop = <0x00>;
+ phandle = <0x02>;
+ };
+ };
+
+ somewhere {
+
+ node-a1 {
+ compatible = "abc,aaa";
+ phandle = <0x01>;
+
+ /export/ node_a: &{/somewhere/node-a1};
+ /export/ other: &{/base-node/sub-node};
+
+ addon-node {
+ prop = <0x00>;
+ };
+ };
+
+ node-a2 {
+ compatible = "abc,aaa";
+ phandle = <0x03>;
+
+ /export/ node_a: &{/somewhere/node-a2};
+ /export/ other: &{/base-node/sub-node};
+ };
+ };
+};
diff --git a/tests/fdtaddon_basics1-merged1.dtb.expect b/tests/fdtaddon_basics1-merged1.dtb.expect
new file mode 100644
index 00000000..966a43df
--- /dev/null
+++ b/tests/fdtaddon_basics1-merged1.dtb.expect
@@ -0,0 +1,27 @@
+/dts-v1/;
+
+/ {
+ base-node {
+ sub-node {
+ prop = <0x00000000>;
+ phandle = <0x00000002>;
+ };
+ };
+ somewhere {
+ node-a1 {
+ // [FDT_EXPORT_SYM] 'node_a' -> phandle 0x00000001
+ // [FDT_EXPORT_SYM] 'other' -> phandle 0x00000002
+ compatible = "abc,aaa";
+ phandle = <0x00000001>;
+ addon-node {
+ prop = <0x00000000>;
+ };
+ };
+ node-a2 {
+ // [FDT_EXPORT_SYM] 'node_a' -> phandle 0x00000003
+ // [FDT_EXPORT_SYM] 'other' -> phandle 0x00000002
+ compatible = "abc,aaa";
+ phandle = <0x00000003>;
+ };
+ };
+};
diff --git a/tests/fdtaddon_basics1-merged2.dtb.dts.expect b/tests/fdtaddon_basics1-merged2.dtb.dts.expect
new file mode 100644
index 00000000..d85567b7
--- /dev/null
+++ b/tests/fdtaddon_basics1-merged2.dtb.dts.expect
@@ -0,0 +1,35 @@
+/dts-v1/;
+
+/ {
+
+ base-node {
+
+ sub-node {
+ prop = <0x00>;
+ phandle = <0x02>;
+ };
+ };
+
+ somewhere {
+
+ node-a1 {
+ compatible = "abc,aaa";
+ phandle = <0x01>;
+
+ /export/ node_a: &{/somewhere/node-a1};
+ /export/ other: &{/base-node/sub-node};
+ };
+
+ node-a2 {
+ compatible = "abc,aaa";
+ phandle = <0x03>;
+
+ /export/ node_a: &{/somewhere/node-a2};
+ /export/ other: &{/base-node/sub-node};
+
+ addon-node {
+ prop = <0x00>;
+ };
+ };
+ };
+};
diff --git a/tests/fdtaddon_basics1-merged2.dtb.expect b/tests/fdtaddon_basics1-merged2.dtb.expect
new file mode 100644
index 00000000..ca9c7ba0
--- /dev/null
+++ b/tests/fdtaddon_basics1-merged2.dtb.expect
@@ -0,0 +1,27 @@
+/dts-v1/;
+
+/ {
+ base-node {
+ sub-node {
+ prop = <0x00000000>;
+ phandle = <0x00000002>;
+ };
+ };
+ somewhere {
+ node-a1 {
+ // [FDT_EXPORT_SYM] 'node_a' -> phandle 0x00000001
+ // [FDT_EXPORT_SYM] 'other' -> phandle 0x00000002
+ compatible = "abc,aaa";
+ phandle = <0x00000001>;
+ };
+ node-a2 {
+ // [FDT_EXPORT_SYM] 'node_a' -> phandle 0x00000003
+ // [FDT_EXPORT_SYM] 'other' -> phandle 0x00000002
+ compatible = "abc,aaa";
+ phandle = <0x00000003>;
+ addon-node {
+ prop = <0x00000000>;
+ };
+ };
+ };
+};
diff --git a/tests/fdtaddon_basics1.dtba.expect b/tests/fdtaddon_basics1.dtba.expect
new file mode 100644
index 00000000..1102923a
--- /dev/null
+++ b/tests/fdtaddon_basics1.dtba.expect
@@ -0,0 +1,8 @@
+/dts-v1/;
+/addon/;
+
+/ {
+ addon-node {
+ prop = <0x00000000>;
+ };
+};
diff --git a/tests/fdtaddon_basics1.dtsa b/tests/fdtaddon_basics1.dtsa
new file mode 100644
index 00000000..84621c97
--- /dev/null
+++ b/tests/fdtaddon_basics1.dtsa
@@ -0,0 +1,13 @@
+// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
+/*
+ * Copyright (C) 2026 Bootlin
+ */
+
+/dts-v1/;
+/addon/;
+
+/ {
+ addon-node {
+ prop = <0>;
+ };
+};
diff --git a/tests/meson.build b/tests/meson.build
index ec24f0af..e1a2454e 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -144,6 +144,7 @@ run_test_types = [
'fdtdump',
'fdtoverlay',
'metadata',
+ 'fdtaddon',
]
run_test_deps = [
dtc_tools, treegen_dtb, tests_exe,
diff --git a/tests/run_tests.sh b/tests/run_tests.sh
index 7f47b685..3f80b183 100755
--- a/tests/run_tests.sh
+++ b/tests/run_tests.sh
@@ -1281,6 +1281,48 @@ metadata_tests() {
run_test check_full metadata_sort.dtb
}
+run_fdtaddon_test () {
+ printf "fdtaddon $*: "
+ base_run_test wrap_test $VALGRIND $FDTADDON "$@"
+}
+
+check_dtb () {
+ local dtb="$1"
+ base_run_test wrap_fdtdump "$dtb" "$dtb.out"
+ sed -i '/^\/\/ [^\[]/d' "$dtb.out"
+ base_run_test check_diff "$dtb.out" "$SRCDIR/$dtb.expect"
+ run_test check_full "$dtb"
+}
+
+check_dts () {
+ local dts="$1"
+ base_run_test check_diff "$dts" "$SRCDIR/$dts.expect"
+}
+
+fdtaddon_tests() {
+ run_dtc_test -I dts -O dtb -o fdtaddon_base.dtb "$SRCDIR/fdtaddon_base.dts"
+ check_dtb fdtaddon_base.dtb
+
+ for dt in fdtaddon_basics1; do
+ run_dtc_test -I dts -O dtb -o $dt.dtba "$SRCDIR/$dt.dtsa"
+ check_dtb $dt.dtba
+
+ run_fdtaddon_test -i fdtaddon_base.dtb -o $dt-merged1.dtb \
+ -t "/somewhere/node-a1" $dt.dtba
+ check_dtb $dt-merged1.dtb
+
+ run_dtc_test -I dtb -O dts -o $dt-merged1.dtb.dts $dt-merged1.dtb
+ check_dts $dt-merged1.dtb.dts
+
+ run_fdtaddon_test -i fdtaddon_base.dtb -o $dt-merged2.dtb \
+ -t "/somewhere/node-a2" $dt.dtba
+ check_dtb $dt-merged2.dtb
+
+ run_dtc_test -I dtb -O dts -o $dt-merged2.dtb.dts $dt-merged2.dtb
+ check_dts $dt-merged2.dtb.dts
+ done
+}
+
pylibfdt_tests () {
run_dtc_test -I dts -O dtb -o test_props.dtb "$SRCDIR/test_props.dts"
TMP=/tmp/tests.stderr.$$
@@ -1320,7 +1362,7 @@ while getopts "vt:me" ARG ; do
done
if [ -z "$TESTSETS" ]; then
- TESTSETS="libfdt utilfdt dtc dtbs_equal fdtget fdtput fdtdump fdtoverlay metadata"
+ TESTSETS="libfdt utilfdt dtc dtbs_equal fdtget fdtput fdtdump fdtoverlay metadata fdtaddon"
# Test pylibfdt if the libfdt Python module is available.
if ! $no_python; then
@@ -1363,6 +1405,9 @@ for set in $TESTSETS; do
"metadata")
metadata_tests
;;
+ "fdtaddon")
+ fdtaddon_tests
+ ;;
esac
done
diff --git a/tests/testutils.sh b/tests/testutils.sh
index 6b2f0d14..b5e121e7 100644
--- a/tests/testutils.sh
+++ b/tests/testutils.sh
@@ -27,6 +27,7 @@ DTGET=${TEST_BINDIR}/fdtget
DTPUT=${TEST_BINDIR}/fdtput
FDTDUMP=${TEST_BINDIR}/fdtdump
FDTOVERLAY=${TEST_BINDIR}/fdtoverlay
+FDTADDON=${TEST_BINDIR}/fdtaddon
verbose_run () {
if [ -z "$QUIET_TEST" ]; then
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 66/74] tests: fdtaddon: Add a basic test for addons using an orphan nodes
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (64 preceding siblings ...)
2026-08-26 9:49 ` [RFC PATCH v2 65/74] tests: Add a first basic test for fdtaddon Herve Codina
@ 2026-08-26 9:49 ` Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 67/74] tests: fdtaddon: Add a basic test for addons with unresolved phandle references Herve Codina
` (7 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:49 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
Orphan nodes are available in addons.
Add a test checking the application of an addon when orphan nodes are
involved.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
tests/fdtaddon_basics2-merged1.dtb.dts.expect | 35 +++++++++++++++++++
tests/fdtaddon_basics2-merged1.dtb.expect | 27 ++++++++++++++
tests/fdtaddon_basics2-merged2.dtb.dts.expect | 35 +++++++++++++++++++
tests/fdtaddon_basics2-merged2.dtb.expect | 27 ++++++++++++++
tests/fdtaddon_basics2.dtba.expect | 9 +++++
tests/fdtaddon_basics2.dtsa | 15 ++++++++
tests/run_tests.sh | 2 +-
7 files changed, 149 insertions(+), 1 deletion(-)
create mode 100644 tests/fdtaddon_basics2-merged1.dtb.dts.expect
create mode 100644 tests/fdtaddon_basics2-merged1.dtb.expect
create mode 100644 tests/fdtaddon_basics2-merged2.dtb.dts.expect
create mode 100644 tests/fdtaddon_basics2-merged2.dtb.expect
create mode 100644 tests/fdtaddon_basics2.dtba.expect
create mode 100644 tests/fdtaddon_basics2.dtsa
diff --git a/tests/fdtaddon_basics2-merged1.dtb.dts.expect b/tests/fdtaddon_basics2-merged1.dtb.dts.expect
new file mode 100644
index 00000000..927733c1
--- /dev/null
+++ b/tests/fdtaddon_basics2-merged1.dtb.dts.expect
@@ -0,0 +1,35 @@
+/dts-v1/;
+
+/ {
+
+ base-node {
+
+ sub-node {
+ prop = <0x00>;
+ phandle = <0x02>;
+ };
+ };
+
+ somewhere {
+
+ node-a1 {
+ compatible = "abc,aaa";
+ phandle = <0x01>;
+
+ /export/ node_a: &{/somewhere/node-a1};
+ /export/ other: &{/base-node/sub-node};
+
+ addon-node {
+ prop = <0x00>;
+ };
+ };
+
+ node-a2 {
+ compatible = "abc,aaa";
+ phandle = <0x03>;
+
+ /export/ node_a: &{/somewhere/node-a2};
+ /export/ other: &{/base-node/sub-node};
+ };
+ };
+};
diff --git a/tests/fdtaddon_basics2-merged1.dtb.expect b/tests/fdtaddon_basics2-merged1.dtb.expect
new file mode 100644
index 00000000..966a43df
--- /dev/null
+++ b/tests/fdtaddon_basics2-merged1.dtb.expect
@@ -0,0 +1,27 @@
+/dts-v1/;
+
+/ {
+ base-node {
+ sub-node {
+ prop = <0x00000000>;
+ phandle = <0x00000002>;
+ };
+ };
+ somewhere {
+ node-a1 {
+ // [FDT_EXPORT_SYM] 'node_a' -> phandle 0x00000001
+ // [FDT_EXPORT_SYM] 'other' -> phandle 0x00000002
+ compatible = "abc,aaa";
+ phandle = <0x00000001>;
+ addon-node {
+ prop = <0x00000000>;
+ };
+ };
+ node-a2 {
+ // [FDT_EXPORT_SYM] 'node_a' -> phandle 0x00000003
+ // [FDT_EXPORT_SYM] 'other' -> phandle 0x00000002
+ compatible = "abc,aaa";
+ phandle = <0x00000003>;
+ };
+ };
+};
diff --git a/tests/fdtaddon_basics2-merged2.dtb.dts.expect b/tests/fdtaddon_basics2-merged2.dtb.dts.expect
new file mode 100644
index 00000000..d85567b7
--- /dev/null
+++ b/tests/fdtaddon_basics2-merged2.dtb.dts.expect
@@ -0,0 +1,35 @@
+/dts-v1/;
+
+/ {
+
+ base-node {
+
+ sub-node {
+ prop = <0x00>;
+ phandle = <0x02>;
+ };
+ };
+
+ somewhere {
+
+ node-a1 {
+ compatible = "abc,aaa";
+ phandle = <0x01>;
+
+ /export/ node_a: &{/somewhere/node-a1};
+ /export/ other: &{/base-node/sub-node};
+ };
+
+ node-a2 {
+ compatible = "abc,aaa";
+ phandle = <0x03>;
+
+ /export/ node_a: &{/somewhere/node-a2};
+ /export/ other: &{/base-node/sub-node};
+
+ addon-node {
+ prop = <0x00>;
+ };
+ };
+ };
+};
diff --git a/tests/fdtaddon_basics2-merged2.dtb.expect b/tests/fdtaddon_basics2-merged2.dtb.expect
new file mode 100644
index 00000000..ca9c7ba0
--- /dev/null
+++ b/tests/fdtaddon_basics2-merged2.dtb.expect
@@ -0,0 +1,27 @@
+/dts-v1/;
+
+/ {
+ base-node {
+ sub-node {
+ prop = <0x00000000>;
+ phandle = <0x00000002>;
+ };
+ };
+ somewhere {
+ node-a1 {
+ // [FDT_EXPORT_SYM] 'node_a' -> phandle 0x00000001
+ // [FDT_EXPORT_SYM] 'other' -> phandle 0x00000002
+ compatible = "abc,aaa";
+ phandle = <0x00000001>;
+ };
+ node-a2 {
+ // [FDT_EXPORT_SYM] 'node_a' -> phandle 0x00000003
+ // [FDT_EXPORT_SYM] 'other' -> phandle 0x00000002
+ compatible = "abc,aaa";
+ phandle = <0x00000003>;
+ addon-node {
+ prop = <0x00000000>;
+ };
+ };
+ };
+};
diff --git a/tests/fdtaddon_basics2.dtba.expect b/tests/fdtaddon_basics2.dtba.expect
new file mode 100644
index 00000000..ec1582e6
--- /dev/null
+++ b/tests/fdtaddon_basics2.dtba.expect
@@ -0,0 +1,9 @@
+/dts-v1/;
+/addon/;
+
+// [FDT_IMPORT_SYM] 'node_a' (abc,aaa)
+&node_a {
+ addon-node {
+ prop = <0x00000000>;
+ };
+};
diff --git a/tests/fdtaddon_basics2.dtsa b/tests/fdtaddon_basics2.dtsa
new file mode 100644
index 00000000..0dc70af9
--- /dev/null
+++ b/tests/fdtaddon_basics2.dtsa
@@ -0,0 +1,15 @@
+// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
+/*
+ * Copyright (C) 2026 Bootlin
+ */
+
+/dts-v1/;
+/addon/;
+
+/import/ node_a: "abc,aaa";
+
+&node_a {
+ addon-node {
+ prop = <0>;
+ };
+};
diff --git a/tests/run_tests.sh b/tests/run_tests.sh
index 3f80b183..558f4bd1 100755
--- a/tests/run_tests.sh
+++ b/tests/run_tests.sh
@@ -1303,7 +1303,7 @@ fdtaddon_tests() {
run_dtc_test -I dts -O dtb -o fdtaddon_base.dtb "$SRCDIR/fdtaddon_base.dts"
check_dtb fdtaddon_base.dtb
- for dt in fdtaddon_basics1; do
+ for dt in fdtaddon_basics1 fdtaddon_basics2; do
run_dtc_test -I dts -O dtb -o $dt.dtba "$SRCDIR/$dt.dtsa"
check_dtb $dt.dtba
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 67/74] tests: fdtaddon: Add a basic test for addons with unresolved phandle references
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (65 preceding siblings ...)
2026-08-26 9:49 ` [RFC PATCH v2 66/74] tests: fdtaddon: Add a basic test for addons using an orphan nodes Herve Codina
@ 2026-08-26 9:49 ` Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 68/74] tests: fdtaddon: Add a basic test for addons with references by path to orphan nodes Herve Codina
` (6 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:49 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
Addons can have unresolved phandle references. Those references are
resolved when the addon is applied.
Add a basic test for this feature.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
tests/fdtaddon_basics3-merged1.dtb.dts.expect | 36 +++++++++++++++++++
tests/fdtaddon_basics3-merged1.dtb.expect | 29 +++++++++++++++
tests/fdtaddon_basics3-merged2.dtb.dts.expect | 36 +++++++++++++++++++
tests/fdtaddon_basics3-merged2.dtb.expect | 29 +++++++++++++++
tests/fdtaddon_basics3.dtba.expect | 12 +++++++
tests/fdtaddon_basics3.dtsa | 17 +++++++++
tests/run_tests.sh | 2 +-
7 files changed, 160 insertions(+), 1 deletion(-)
create mode 100644 tests/fdtaddon_basics3-merged1.dtb.dts.expect
create mode 100644 tests/fdtaddon_basics3-merged1.dtb.expect
create mode 100644 tests/fdtaddon_basics3-merged2.dtb.dts.expect
create mode 100644 tests/fdtaddon_basics3-merged2.dtb.expect
create mode 100644 tests/fdtaddon_basics3.dtba.expect
create mode 100644 tests/fdtaddon_basics3.dtsa
diff --git a/tests/fdtaddon_basics3-merged1.dtb.dts.expect b/tests/fdtaddon_basics3-merged1.dtb.dts.expect
new file mode 100644
index 00000000..c1bf3cc2
--- /dev/null
+++ b/tests/fdtaddon_basics3-merged1.dtb.dts.expect
@@ -0,0 +1,36 @@
+/dts-v1/;
+
+/ {
+
+ base-node {
+
+ sub-node {
+ prop = <0x00>;
+ phandle = <0x02>;
+ };
+ };
+
+ somewhere {
+
+ node-a1 {
+ compatible = "abc,aaa";
+ phandle = <0x01>;
+
+ /export/ node_a: &{/somewhere/node-a1};
+ /export/ other: &{/base-node/sub-node};
+
+ addon-node {
+ ref-other = <&{/base-node/sub-node} 0x0a>;
+ prop = <0x00>;
+ };
+ };
+
+ node-a2 {
+ compatible = "abc,aaa";
+ phandle = <0x03>;
+
+ /export/ node_a: &{/somewhere/node-a2};
+ /export/ other: &{/base-node/sub-node};
+ };
+ };
+};
diff --git a/tests/fdtaddon_basics3-merged1.dtb.expect b/tests/fdtaddon_basics3-merged1.dtb.expect
new file mode 100644
index 00000000..8dccf052
--- /dev/null
+++ b/tests/fdtaddon_basics3-merged1.dtb.expect
@@ -0,0 +1,29 @@
+/dts-v1/;
+
+/ {
+ base-node {
+ sub-node {
+ prop = <0x00000000>;
+ phandle = <0x00000002>;
+ };
+ };
+ somewhere {
+ node-a1 {
+ // [FDT_EXPORT_SYM] 'node_a' -> phandle 0x00000001
+ // [FDT_EXPORT_SYM] 'other' -> phandle 0x00000002
+ compatible = "abc,aaa";
+ phandle = <0x00000001>;
+ addon-node {
+ ref-other = <0x00000002 0x0000000a>;
+ // [FDT_PROPDATA_PHANDLE] ref-other[0]
+ prop = <0x00000000>;
+ };
+ };
+ node-a2 {
+ // [FDT_EXPORT_SYM] 'node_a' -> phandle 0x00000003
+ // [FDT_EXPORT_SYM] 'other' -> phandle 0x00000002
+ compatible = "abc,aaa";
+ phandle = <0x00000003>;
+ };
+ };
+};
diff --git a/tests/fdtaddon_basics3-merged2.dtb.dts.expect b/tests/fdtaddon_basics3-merged2.dtb.dts.expect
new file mode 100644
index 00000000..8dc9fd25
--- /dev/null
+++ b/tests/fdtaddon_basics3-merged2.dtb.dts.expect
@@ -0,0 +1,36 @@
+/dts-v1/;
+
+/ {
+
+ base-node {
+
+ sub-node {
+ prop = <0x00>;
+ phandle = <0x02>;
+ };
+ };
+
+ somewhere {
+
+ node-a1 {
+ compatible = "abc,aaa";
+ phandle = <0x01>;
+
+ /export/ node_a: &{/somewhere/node-a1};
+ /export/ other: &{/base-node/sub-node};
+ };
+
+ node-a2 {
+ compatible = "abc,aaa";
+ phandle = <0x03>;
+
+ /export/ node_a: &{/somewhere/node-a2};
+ /export/ other: &{/base-node/sub-node};
+
+ addon-node {
+ ref-other = <&{/base-node/sub-node} 0x0a>;
+ prop = <0x00>;
+ };
+ };
+ };
+};
diff --git a/tests/fdtaddon_basics3-merged2.dtb.expect b/tests/fdtaddon_basics3-merged2.dtb.expect
new file mode 100644
index 00000000..4761a8e5
--- /dev/null
+++ b/tests/fdtaddon_basics3-merged2.dtb.expect
@@ -0,0 +1,29 @@
+/dts-v1/;
+
+/ {
+ base-node {
+ sub-node {
+ prop = <0x00000000>;
+ phandle = <0x00000002>;
+ };
+ };
+ somewhere {
+ node-a1 {
+ // [FDT_EXPORT_SYM] 'node_a' -> phandle 0x00000001
+ // [FDT_EXPORT_SYM] 'other' -> phandle 0x00000002
+ compatible = "abc,aaa";
+ phandle = <0x00000001>;
+ };
+ node-a2 {
+ // [FDT_EXPORT_SYM] 'node_a' -> phandle 0x00000003
+ // [FDT_EXPORT_SYM] 'other' -> phandle 0x00000002
+ compatible = "abc,aaa";
+ phandle = <0x00000003>;
+ addon-node {
+ ref-other = <0x00000002 0x0000000a>;
+ // [FDT_PROPDATA_PHANDLE] ref-other[0]
+ prop = <0x00000000>;
+ };
+ };
+ };
+};
diff --git a/tests/fdtaddon_basics3.dtba.expect b/tests/fdtaddon_basics3.dtba.expect
new file mode 100644
index 00000000..20a87a19
--- /dev/null
+++ b/tests/fdtaddon_basics3.dtba.expect
@@ -0,0 +1,12 @@
+/dts-v1/;
+/addon/;
+
+// [FDT_IMPORT_SYM] 'node_a' (abc,aaa)
+// [FDT_IMPORT_SYM] 'other' ()
+&node_a {
+ addon-node {
+ prop = <0x00000000>;
+ ref-other = <0xffffffff 0x0000000a>;
+ // [FDT_PROPDATA_PHANDLE_REF] ref-other[0], ref = other
+ };
+};
diff --git a/tests/fdtaddon_basics3.dtsa b/tests/fdtaddon_basics3.dtsa
new file mode 100644
index 00000000..8658b45e
--- /dev/null
+++ b/tests/fdtaddon_basics3.dtsa
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
+/*
+ * Copyright (C) 2026 Bootlin
+ */
+
+/dts-v1/;
+/addon/;
+
+/import/ node_a: "abc,aaa";
+/import/ other: "";
+
+&node_a {
+ addon-node {
+ prop = <0>;
+ ref-other = <&other 10>;
+ };
+};
diff --git a/tests/run_tests.sh b/tests/run_tests.sh
index 558f4bd1..de3d31a3 100755
--- a/tests/run_tests.sh
+++ b/tests/run_tests.sh
@@ -1303,7 +1303,7 @@ fdtaddon_tests() {
run_dtc_test -I dts -O dtb -o fdtaddon_base.dtb "$SRCDIR/fdtaddon_base.dts"
check_dtb fdtaddon_base.dtb
- for dt in fdtaddon_basics1 fdtaddon_basics2; do
+ for dt in fdtaddon_basics1 fdtaddon_basics2 fdtaddon_basics3; do
run_dtc_test -I dts -O dtb -o $dt.dtba "$SRCDIR/$dt.dtsa"
check_dtb $dt.dtba
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 68/74] tests: fdtaddon: Add a basic test for addons with references by path to orphan nodes
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (66 preceding siblings ...)
2026-08-26 9:49 ` [RFC PATCH v2 67/74] tests: fdtaddon: Add a basic test for addons with unresolved phandle references Herve Codina
@ 2026-08-26 9:49 ` Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 69/74] tests: fdtaddon: Add a test for addons with properties identical to existing ones Herve Codina
` (5 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:49 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
Addons can use references by path to reference by path to reference an
orphan node or sub-nodes of an orphan node.
Those references are converted to either local or external references
and have to be handled when the addon is applied.
Add a basic test for this feature.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
tests/fdtaddon_basics4-merged1.dtb.dts.expect | 43 +++++++++++++++++++
tests/fdtaddon_basics4-merged1.dtb.expect | 37 ++++++++++++++++
tests/fdtaddon_basics4-merged2.dtb.dts.expect | 43 +++++++++++++++++++
tests/fdtaddon_basics4-merged2.dtb.expect | 37 ++++++++++++++++
tests/fdtaddon_basics4.dtba.expect | 22 ++++++++++
tests/fdtaddon_basics4.dtsa | 25 +++++++++++
tests/run_tests.sh | 3 +-
7 files changed, 209 insertions(+), 1 deletion(-)
create mode 100644 tests/fdtaddon_basics4-merged1.dtb.dts.expect
create mode 100644 tests/fdtaddon_basics4-merged1.dtb.expect
create mode 100644 tests/fdtaddon_basics4-merged2.dtb.dts.expect
create mode 100644 tests/fdtaddon_basics4-merged2.dtb.expect
create mode 100644 tests/fdtaddon_basics4.dtba.expect
create mode 100644 tests/fdtaddon_basics4.dtsa
diff --git a/tests/fdtaddon_basics4-merged1.dtb.dts.expect b/tests/fdtaddon_basics4-merged1.dtb.dts.expect
new file mode 100644
index 00000000..9e6bbf18
--- /dev/null
+++ b/tests/fdtaddon_basics4-merged1.dtb.dts.expect
@@ -0,0 +1,43 @@
+/dts-v1/;
+
+/ {
+
+ base-node {
+
+ sub-node {
+ prop = <0x00>;
+ phandle = <0x02>;
+
+ sub-node {
+ phandle = <0x04>;
+ prop = <0x01>;
+ };
+ };
+ };
+
+ somewhere {
+
+ node-a1 {
+ compatible = "abc,aaa";
+ phandle = <0x01>;
+
+ /export/ node_a: &{/somewhere/node-a1};
+ /export/ other: &{/base-node/sub-node};
+
+ addon-node {
+ ref-other-subnode = <&{/base-node/sub-node/sub-node}>;
+ ref-other2 = <&{/base-node/sub-node}>;
+ ref-other1 = <&{/base-node/sub-node} 0x0a>;
+ prop = <0x00>;
+ };
+ };
+
+ node-a2 {
+ compatible = "abc,aaa";
+ phandle = <0x03>;
+
+ /export/ node_a: &{/somewhere/node-a2};
+ /export/ other: &{/base-node/sub-node};
+ };
+ };
+};
diff --git a/tests/fdtaddon_basics4-merged1.dtb.expect b/tests/fdtaddon_basics4-merged1.dtb.expect
new file mode 100644
index 00000000..f339430a
--- /dev/null
+++ b/tests/fdtaddon_basics4-merged1.dtb.expect
@@ -0,0 +1,37 @@
+/dts-v1/;
+
+/ {
+ base-node {
+ sub-node {
+ prop = <0x00000000>;
+ phandle = <0x00000002>;
+ sub-node {
+ phandle = <0x00000004>;
+ prop = <0x00000001>;
+ };
+ };
+ };
+ somewhere {
+ node-a1 {
+ // [FDT_EXPORT_SYM] 'node_a' -> phandle 0x00000001
+ // [FDT_EXPORT_SYM] 'other' -> phandle 0x00000002
+ compatible = "abc,aaa";
+ phandle = <0x00000001>;
+ addon-node {
+ ref-other-subnode = <0x00000004>;
+ // [FDT_PROPDATA_PHANDLE] ref-other-subnode[0]
+ ref-other2 = <0x00000002>;
+ // [FDT_PROPDATA_PHANDLE] ref-other2[0]
+ ref-other1 = <0x00000002 0x0000000a>;
+ // [FDT_PROPDATA_PHANDLE] ref-other1[0]
+ prop = <0x00000000>;
+ };
+ };
+ node-a2 {
+ // [FDT_EXPORT_SYM] 'node_a' -> phandle 0x00000003
+ // [FDT_EXPORT_SYM] 'other' -> phandle 0x00000002
+ compatible = "abc,aaa";
+ phandle = <0x00000003>;
+ };
+ };
+};
diff --git a/tests/fdtaddon_basics4-merged2.dtb.dts.expect b/tests/fdtaddon_basics4-merged2.dtb.dts.expect
new file mode 100644
index 00000000..0c513c93
--- /dev/null
+++ b/tests/fdtaddon_basics4-merged2.dtb.dts.expect
@@ -0,0 +1,43 @@
+/dts-v1/;
+
+/ {
+
+ base-node {
+
+ sub-node {
+ prop = <0x00>;
+ phandle = <0x02>;
+
+ sub-node {
+ phandle = <0x04>;
+ prop = <0x01>;
+ };
+ };
+ };
+
+ somewhere {
+
+ node-a1 {
+ compatible = "abc,aaa";
+ phandle = <0x01>;
+
+ /export/ node_a: &{/somewhere/node-a1};
+ /export/ other: &{/base-node/sub-node};
+ };
+
+ node-a2 {
+ compatible = "abc,aaa";
+ phandle = <0x03>;
+
+ /export/ node_a: &{/somewhere/node-a2};
+ /export/ other: &{/base-node/sub-node};
+
+ addon-node {
+ ref-other-subnode = <&{/base-node/sub-node/sub-node}>;
+ ref-other2 = <&{/base-node/sub-node}>;
+ ref-other1 = <&{/base-node/sub-node} 0x0a>;
+ prop = <0x00>;
+ };
+ };
+ };
+};
diff --git a/tests/fdtaddon_basics4-merged2.dtb.expect b/tests/fdtaddon_basics4-merged2.dtb.expect
new file mode 100644
index 00000000..f4072f16
--- /dev/null
+++ b/tests/fdtaddon_basics4-merged2.dtb.expect
@@ -0,0 +1,37 @@
+/dts-v1/;
+
+/ {
+ base-node {
+ sub-node {
+ prop = <0x00000000>;
+ phandle = <0x00000002>;
+ sub-node {
+ phandle = <0x00000004>;
+ prop = <0x00000001>;
+ };
+ };
+ };
+ somewhere {
+ node-a1 {
+ // [FDT_EXPORT_SYM] 'node_a' -> phandle 0x00000001
+ // [FDT_EXPORT_SYM] 'other' -> phandle 0x00000002
+ compatible = "abc,aaa";
+ phandle = <0x00000001>;
+ };
+ node-a2 {
+ // [FDT_EXPORT_SYM] 'node_a' -> phandle 0x00000003
+ // [FDT_EXPORT_SYM] 'other' -> phandle 0x00000002
+ compatible = "abc,aaa";
+ phandle = <0x00000003>;
+ addon-node {
+ ref-other-subnode = <0x00000004>;
+ // [FDT_PROPDATA_PHANDLE] ref-other-subnode[0]
+ ref-other2 = <0x00000002>;
+ // [FDT_PROPDATA_PHANDLE] ref-other2[0]
+ ref-other1 = <0x00000002 0x0000000a>;
+ // [FDT_PROPDATA_PHANDLE] ref-other1[0]
+ prop = <0x00000000>;
+ };
+ };
+ };
+};
diff --git a/tests/fdtaddon_basics4.dtba.expect b/tests/fdtaddon_basics4.dtba.expect
new file mode 100644
index 00000000..14450cb9
--- /dev/null
+++ b/tests/fdtaddon_basics4.dtba.expect
@@ -0,0 +1,22 @@
+/dts-v1/;
+/addon/;
+
+// [FDT_IMPORT_SYM] 'node_a' (abc,aaa)
+// [FDT_IMPORT_SYM] 'other' ()
+&node_a {
+ addon-node {
+ prop = <0x00000000>;
+ ref-other1 = <0xffffffff 0x0000000a>;
+ // [FDT_PROPDATA_PHANDLE_REF] ref-other1[0], ref = other
+ ref-other2 = <0xffffffff>;
+ // [FDT_PROPDATA_PHANDLE_REF] ref-other2[0], ref = other
+ ref-other-subnode = <0x00000001>;
+ // [FDT_PROPDATA_PHANDLE] ref-other-subnode[0]
+ };
+};
+&other {
+ sub-node {
+ prop = <0x00000001>;
+ phandle = <0x00000001>;
+ };
+};
diff --git a/tests/fdtaddon_basics4.dtsa b/tests/fdtaddon_basics4.dtsa
new file mode 100644
index 00000000..5ca19ce9
--- /dev/null
+++ b/tests/fdtaddon_basics4.dtsa
@@ -0,0 +1,25 @@
+// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
+/*
+ * Copyright (C) 2026 Bootlin
+ */
+
+/dts-v1/;
+/addon/;
+
+/import/ node_a: "abc,aaa";
+/import/ other: "";
+
+&node_a {
+ addon-node {
+ prop = <0>;
+ ref-other1 = <&other 10>;
+ ref-other2 = <&{$other}>;
+ ref-other-subnode = <&{$other/sub-node}>;
+ };
+};
+
+&other {
+ sub-node {
+ prop = <1>;
+ };
+};
diff --git a/tests/run_tests.sh b/tests/run_tests.sh
index de3d31a3..5f09bf47 100755
--- a/tests/run_tests.sh
+++ b/tests/run_tests.sh
@@ -1303,7 +1303,8 @@ fdtaddon_tests() {
run_dtc_test -I dts -O dtb -o fdtaddon_base.dtb "$SRCDIR/fdtaddon_base.dts"
check_dtb fdtaddon_base.dtb
- for dt in fdtaddon_basics1 fdtaddon_basics2 fdtaddon_basics3; do
+ for dt in fdtaddon_basics1 fdtaddon_basics2 fdtaddon_basics3 \
+ fdtaddon_basics4; do
run_dtc_test -I dts -O dtb -o $dt.dtba "$SRCDIR/$dt.dtsa"
check_dtb $dt.dtba
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 69/74] tests: fdtaddon: Add a test for addons with properties identical to existing ones
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (67 preceding siblings ...)
2026-08-26 9:49 ` [RFC PATCH v2 68/74] tests: fdtaddon: Add a basic test for addons with references by path to orphan nodes Herve Codina
@ 2026-08-26 9:49 ` Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 70/74] tests: fdtaddon: Add a test for addons using namespace label references Herve Codina
` (4 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:49 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
An addon cannot modify or add a property in a existing node.
If an addon has a property with the exact same value as an existing one,
applying the addon doesn't lead to a property modification.
Indeed, because both properties (the addon one and the existing one) are
equal, the existing one is not modified.
Add a test for this feature.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
...n_addon_prop_existing_ok-merged.dtb.expect | 20 +++++++++++++++
...dtaddon_addon_prop_existing_ok.dtba.expect | 17 +++++++++++++
tests/fdtaddon_addon_prop_existing_ok.dtsa | 25 +++++++++++++++++++
tests/fdtaddon_base_prop_existing.dtb.expect | 17 +++++++++++++
tests/fdtaddon_base_prop_existing.dts | 22 ++++++++++++++++
tests/run_tests.sh | 12 +++++++++
6 files changed, 113 insertions(+)
create mode 100644 tests/fdtaddon_addon_prop_existing_ok-merged.dtb.expect
create mode 100644 tests/fdtaddon_addon_prop_existing_ok.dtba.expect
create mode 100644 tests/fdtaddon_addon_prop_existing_ok.dtsa
create mode 100644 tests/fdtaddon_base_prop_existing.dtb.expect
create mode 100644 tests/fdtaddon_base_prop_existing.dts
diff --git a/tests/fdtaddon_addon_prop_existing_ok-merged.dtb.expect b/tests/fdtaddon_addon_prop_existing_ok-merged.dtb.expect
new file mode 100644
index 00000000..a49c3f36
--- /dev/null
+++ b/tests/fdtaddon_addon_prop_existing_ok-merged.dtb.expect
@@ -0,0 +1,20 @@
+/dts-v1/;
+
+/ {
+ base-node {
+ // [FDT_EXPORT_SYM] 'base' -> phandle 0x00000001
+ compatible = "abc,base";
+ exist_bool;
+ exist_int = <0x0000007b>;
+ exist_string = "abcdef";
+ phandle = <0x00000001>;
+ sub-node {
+ sub_exist_bool;
+ sub_exist_int = <0x000001c8>;
+ sub_exist_string = "ghi";
+ addon-node {
+ prop;
+ };
+ };
+ };
+};
diff --git a/tests/fdtaddon_addon_prop_existing_ok.dtba.expect b/tests/fdtaddon_addon_prop_existing_ok.dtba.expect
new file mode 100644
index 00000000..c16c38c7
--- /dev/null
+++ b/tests/fdtaddon_addon_prop_existing_ok.dtba.expect
@@ -0,0 +1,17 @@
+/dts-v1/;
+/addon/;
+
+// [FDT_IMPORT_SYM] 'base' (abc,base)
+&base {
+ exist_bool;
+ exist_int = <0x0000007b>;
+ exist_string = "abcdef";
+ sub-node {
+ sub_exist_bool;
+ sub_exist_int = <0x000001c8>;
+ sub_exist_string = "ghi";
+ addon-node {
+ prop;
+ };
+ };
+};
diff --git a/tests/fdtaddon_addon_prop_existing_ok.dtsa b/tests/fdtaddon_addon_prop_existing_ok.dtsa
new file mode 100644
index 00000000..16e525c1
--- /dev/null
+++ b/tests/fdtaddon_addon_prop_existing_ok.dtsa
@@ -0,0 +1,25 @@
+// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
+/*
+ * Copyright (C) 2026 Bootlin
+ */
+
+/dts-v1/;
+/addon/;
+
+/import/ base: "abc,base";
+
+&base {
+ exist_bool;
+ exist_int = <123>;
+ exist_string = "abcdef";
+
+ sub-node {
+ sub_exist_bool;
+ sub_exist_int = <456>;
+ sub_exist_string = "ghi";
+
+ addon-node {
+ prop;
+ };
+ };
+};
diff --git a/tests/fdtaddon_base_prop_existing.dtb.expect b/tests/fdtaddon_base_prop_existing.dtb.expect
new file mode 100644
index 00000000..2b9d3ac0
--- /dev/null
+++ b/tests/fdtaddon_base_prop_existing.dtb.expect
@@ -0,0 +1,17 @@
+/dts-v1/;
+
+/ {
+ base-node {
+ // [FDT_EXPORT_SYM] 'base' -> phandle 0x00000001
+ compatible = "abc,base";
+ exist_bool;
+ exist_int = <0x0000007b>;
+ exist_string = "abcdef";
+ phandle = <0x00000001>;
+ sub-node {
+ sub_exist_bool;
+ sub_exist_int = <0x000001c8>;
+ sub_exist_string = "ghi";
+ };
+ };
+};
diff --git a/tests/fdtaddon_base_prop_existing.dts b/tests/fdtaddon_base_prop_existing.dts
new file mode 100644
index 00000000..7b0f7102
--- /dev/null
+++ b/tests/fdtaddon_base_prop_existing.dts
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
+/*
+ * Copyright (C) 2026 Bootlin
+ */
+
+/dts-v1/;
+
+/ {
+ base_node: base-node {
+ compatible = "abc,base";
+ exist_bool;
+ exist_int = <123>;
+ exist_string = "abcdef";
+ /export/ base: &base_node;
+
+ sub-node {
+ sub_exist_bool;
+ sub_exist_int = <456>;
+ sub_exist_string = "ghi";
+ };
+ };
+};
diff --git a/tests/run_tests.sh b/tests/run_tests.sh
index 5f09bf47..0182dffc 100755
--- a/tests/run_tests.sh
+++ b/tests/run_tests.sh
@@ -1322,6 +1322,18 @@ fdtaddon_tests() {
run_dtc_test -I dtb -O dts -o $dt-merged2.dtb.dts $dt-merged2.dtb
check_dts $dt-merged2.dtb.dts
done
+
+ # Test addon with exact same properties as ones available in the base tree
+ run_dtc_test -I dts -O dtb -o fdtaddon_base_prop_existing.dtb "$SRCDIR/fdtaddon_base_prop_existing.dts"
+ check_dtb fdtaddon_base_prop_existing.dtb
+
+ run_dtc_test -I dts -O dtb -o fdtaddon_addon_prop_existing_ok.dtba "$SRCDIR/fdtaddon_addon_prop_existing_ok.dtsa"
+ check_dtb fdtaddon_addon_prop_existing_ok.dtba
+
+ run_fdtaddon_test -i fdtaddon_base_prop_existing.dtb -o fdtaddon_addon_prop_existing_ok-merged.dtb \
+ -t "/base-node" fdtaddon_addon_prop_existing_ok.dtba
+ check_dtb fdtaddon_addon_prop_existing_ok-merged.dtb
+
}
pylibfdt_tests () {
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 70/74] tests: fdtaddon: Add a test for addons using namespace label references
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (68 preceding siblings ...)
2026-08-26 9:49 ` [RFC PATCH v2 69/74] tests: fdtaddon: Add a test for addons with properties identical to existing ones Herve Codina
@ 2026-08-26 9:49 ` Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 71/74] tests: fdtaddon: Add a test for using 'stacked' addons Herve Codina
` (3 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:49 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
Namespace label references are labels in the form &foo.bar.baz
Those kind of labels allow to 'jump' from node to node based on
exported symbols defined at each nodes.
Add a test for this feature.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
...ddon_addon_namespace-merged.dtb.dts.expect | 57 +++++++++++++++++++
...fdtaddon_addon_namespace-merged.dtb.expect | 49 ++++++++++++++++
tests/fdtaddon_addon_namespace.dtba.expect | 31 ++++++++++
tests/fdtaddon_addon_namespace.dtsa | 34 +++++++++++
tests/fdtaddon_base_namespace.dtb.expect | 29 ++++++++++
tests/fdtaddon_base_namespace.dts | 33 +++++++++++
tests/run_tests.sh | 13 +++++
7 files changed, 246 insertions(+)
create mode 100644 tests/fdtaddon_addon_namespace-merged.dtb.dts.expect
create mode 100644 tests/fdtaddon_addon_namespace-merged.dtb.expect
create mode 100644 tests/fdtaddon_addon_namespace.dtba.expect
create mode 100644 tests/fdtaddon_addon_namespace.dtsa
create mode 100644 tests/fdtaddon_base_namespace.dtb.expect
create mode 100644 tests/fdtaddon_base_namespace.dts
diff --git a/tests/fdtaddon_addon_namespace-merged.dtb.dts.expect b/tests/fdtaddon_addon_namespace-merged.dtb.dts.expect
new file mode 100644
index 00000000..ff0e5fff
--- /dev/null
+++ b/tests/fdtaddon_addon_namespace-merged.dtb.dts.expect
@@ -0,0 +1,57 @@
+/dts-v1/;
+
+/ {
+
+ other_n1 {
+ prop = <0x00>;
+ phandle = <0x05>;
+
+ /export/ a: &{/other-a};
+ /export/ b: &{/other-b};
+ };
+
+ other-a {
+ prop = <0x00>;
+ phandle = <0x01>;
+
+ addon-node-1a {
+ phandle = <0x06>;
+ prop = <0x1a>;
+ };
+ };
+
+ other-b {
+ prop = <0x00>;
+ phandle = <0x02>;
+
+ /export/ x: &{/other-x};
+ };
+
+ other-x {
+ prop = <0x00>;
+ phandle = <0x03>;
+
+ addon-node-1bx {
+ phandle = <0x07>;
+ prop = <0x1b>;
+ };
+ };
+
+ node {
+ compatible = "abc,aaa";
+ phandle = <0x04>;
+
+ /export/ node: &{/node};
+ /export/ n1: &{/other_n1};
+
+ addon-node2 {
+ ref-addon-n1bx = <&{/other-x/addon-node-1bx}>;
+ ref-n1bx = <&{/other-x}>;
+ };
+
+ addon-node1 {
+ ref-addon-n1a = <&{/other-a/addon-node-1a}>;
+ ref-n1a = <&{/other-a}>;
+ };
+ };
+};
diff --git a/tests/fdtaddon_addon_namespace-merged.dtb.expect b/tests/fdtaddon_addon_namespace-merged.dtb.expect
new file mode 100644
index 00000000..71edbba8
--- /dev/null
+++ b/tests/fdtaddon_addon_namespace-merged.dtb.expect
@@ -0,0 +1,49 @@
+/dts-v1/;
+
+/ {
+ other_n1 {
+ // [FDT_EXPORT_SYM] 'a' -> phandle 0x00000001
+ // [FDT_EXPORT_SYM] 'b' -> phandle 0x00000002
+ prop = <0x00000000>;
+ phandle = <0x00000005>;
+ };
+ other-a {
+ prop = <0x00000000>;
+ phandle = <0x00000001>;
+ addon-node-1a {
+ phandle = <0x00000006>;
+ prop = <0x0000001a>;
+ };
+ };
+ other-b {
+ // [FDT_EXPORT_SYM] 'x' -> phandle 0x00000003
+ prop = <0x00000000>;
+ phandle = <0x00000002>;
+ };
+ other-x {
+ prop = <0x00000000>;
+ phandle = <0x00000003>;
+ addon-node-1bx {
+ phandle = <0x00000007>;
+ prop = <0x0000001b>;
+ };
+ };
+ node {
+ // [FDT_EXPORT_SYM] 'node' -> phandle 0x00000004
+ // [FDT_EXPORT_SYM] 'n1' -> phandle 0x00000005
+ compatible = "abc,aaa";
+ phandle = <0x00000004>;
+ addon-node2 {
+ ref-addon-n1bx = <0x00000007>;
+ // [FDT_PROPDATA_PHANDLE] ref-addon-n1bx[0]
+ ref-n1bx = <0x00000003>;
+ // [FDT_PROPDATA_PHANDLE] ref-n1bx[0]
+ };
+ addon-node1 {
+ ref-addon-n1a = <0x00000006>;
+ // [FDT_PROPDATA_PHANDLE] ref-addon-n1a[0]
+ ref-n1a = <0x00000001>;
+ // [FDT_PROPDATA_PHANDLE] ref-n1a[0]
+ };
+ };
+};
diff --git a/tests/fdtaddon_addon_namespace.dtba.expect b/tests/fdtaddon_addon_namespace.dtba.expect
new file mode 100644
index 00000000..ec1090b5
--- /dev/null
+++ b/tests/fdtaddon_addon_namespace.dtba.expect
@@ -0,0 +1,31 @@
+/dts-v1/;
+/addon/;
+
+// [FDT_IMPORT_SYM] 'node' (abc,aaa)
+// [FDT_IMPORT_SYM] 'n1' ()
+&node {
+ addon-node1 {
+ ref-n1a = <0xffffffff>;
+ // [FDT_PROPDATA_PHANDLE_REF] ref-n1a[0], ref = n1.a
+ ref-addon-n1a = <0x00000001>;
+ // [FDT_PROPDATA_PHANDLE] ref-addon-n1a[0]
+ };
+ addon-node2 {
+ ref-n1bx = <0xffffffff>;
+ // [FDT_PROPDATA_PHANDLE_REF] ref-n1bx[0], ref = n1.b.x
+ ref-addon-n1bx = <0x00000002>;
+ // [FDT_PROPDATA_PHANDLE] ref-addon-n1bx[0]
+ };
+};
+&n1.a {
+ addon-node-1a {
+ prop = <0x0000001a>;
+ phandle = <0x00000001>;
+ };
+};
+&n1.b.x {
+ addon-node-1bx {
+ prop = <0x0000001b>;
+ phandle = <0x00000002>;
+ };
+};
diff --git a/tests/fdtaddon_addon_namespace.dtsa b/tests/fdtaddon_addon_namespace.dtsa
new file mode 100644
index 00000000..8c25d3d4
--- /dev/null
+++ b/tests/fdtaddon_addon_namespace.dtsa
@@ -0,0 +1,34 @@
+// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
+/*
+ * Copyright (C) 2026 Bootlin
+ */
+
+/dts-v1/;
+/addon/;
+
+/import/ node: "abc,aaa";
+/import/ n1: "";
+
+&node {
+ addon-node1 {
+ ref-n1a = <&n1.a>;
+ ref-addon-n1a = <&addon_n1a>;
+ };
+
+ addon-node2 {
+ ref-n1bx = <&n1.b.x>;
+ ref-addon-n1bx = <&addon_n1bx>;
+ };
+};
+
+&n1.a {
+ addon_n1a: addon-node-1a {
+ prop = <0x1a>;
+ };
+};
+
+&n1.b.x {
+ addon_n1bx: addon-node-1bx {
+ prop = <0x1b>;
+ };
+};
diff --git a/tests/fdtaddon_base_namespace.dtb.expect b/tests/fdtaddon_base_namespace.dtb.expect
new file mode 100644
index 00000000..879c4550
--- /dev/null
+++ b/tests/fdtaddon_base_namespace.dtb.expect
@@ -0,0 +1,29 @@
+/dts-v1/;
+
+/ {
+ other_n1 {
+ // [FDT_EXPORT_SYM] 'a' -> phandle 0x00000001
+ // [FDT_EXPORT_SYM] 'b' -> phandle 0x00000002
+ prop = <0x00000000>;
+ phandle = <0x00000005>;
+ };
+ other-a {
+ prop = <0x00000000>;
+ phandle = <0x00000001>;
+ };
+ other-b {
+ // [FDT_EXPORT_SYM] 'x' -> phandle 0x00000003
+ prop = <0x00000000>;
+ phandle = <0x00000002>;
+ };
+ other-x {
+ prop = <0x00000000>;
+ phandle = <0x00000003>;
+ };
+ node {
+ // [FDT_EXPORT_SYM] 'node' -> phandle 0x00000004
+ // [FDT_EXPORT_SYM] 'n1' -> phandle 0x00000005
+ compatible = "abc,aaa";
+ phandle = <0x00000004>;
+ };
+};
diff --git a/tests/fdtaddon_base_namespace.dts b/tests/fdtaddon_base_namespace.dts
new file mode 100644
index 00000000..e2bc4f04
--- /dev/null
+++ b/tests/fdtaddon_base_namespace.dts
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
+/*
+ * Copyright (C) 2026 Bootlin
+ */
+
+/dts-v1/;
+
+/ {
+ n1: other_n1 {
+ prop = <0>;
+ /export/ a: &other_a;
+ /export/ b: &other_b;
+ };
+
+ other_a: other-a {
+ prop = <0>;
+ };
+
+ other_b: other-b {
+ prop = <0>;
+ /export/ x: &other_x;
+ };
+
+ other_x: other-x {
+ prop = <0>;
+ };
+
+ node: node {
+ compatible = "abc,aaa";
+ /export/ node: &node;
+ /export/ n1: &n1;
+ };
+};
diff --git a/tests/run_tests.sh b/tests/run_tests.sh
index 0182dffc..8f8db608 100755
--- a/tests/run_tests.sh
+++ b/tests/run_tests.sh
@@ -1334,6 +1334,19 @@ fdtaddon_tests() {
-t "/base-node" fdtaddon_addon_prop_existing_ok.dtba
check_dtb fdtaddon_addon_prop_existing_ok-merged.dtb
+ # Test namespace label reference
+ run_dtc_test -I dts -O dtb -o fdtaddon_base_namespace.dtb "$SRCDIR/fdtaddon_base_namespace.dts"
+ check_dtb fdtaddon_base_namespace.dtb
+
+ run_dtc_test -I dts -O dtb -o fdtaddon_addon_namespace.dtba "$SRCDIR/fdtaddon_addon_namespace.dtsa"
+ check_dtb fdtaddon_addon_namespace.dtba
+
+ run_fdtaddon_test -i fdtaddon_base_namespace.dtb -o fdtaddon_addon_namespace-merged.dtb \
+ -t "/node" fdtaddon_addon_namespace.dtba
+ check_dtb fdtaddon_addon_namespace-merged.dtb
+
+ run_dtc_test -I dtb -O dts -o fdtaddon_addon_namespace-merged.dtb.dts fdtaddon_addon_namespace-merged.dtb
+ check_dts fdtaddon_addon_namespace-merged.dtb.dts
}
pylibfdt_tests () {
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 71/74] tests: fdtaddon: Add a test for using 'stacked' addons
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (69 preceding siblings ...)
2026-08-26 9:49 ` [RFC PATCH v2 70/74] tests: fdtaddon: Add a test for addons using namespace label references Herve Codina
@ 2026-08-26 9:49 ` Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 72/74] tests: fdtaddon: Add a test using more realistic dts and dtsa Herve Codina
` (2 subsequent siblings)
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:49 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
Addon can be stacked on top of each other.
A first addon can be applied. This first addon exports some symbols and
those exported symbols can be used by a second addon.
Add a test for this feature.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
.../fdtaddon_stack_1st-merged.dtb.dts.expect | 51 ++++++++++++++
tests/fdtaddon_stack_1st-merged.dtb.expect | 41 +++++++++++
tests/fdtaddon_stack_1st.dtba.expect | 24 +++++++
tests/fdtaddon_stack_1st.dtsa | 28 ++++++++
.../fdtaddon_stack_2nd-merged.dtb.dts.expect | 70 +++++++++++++++++++
tests/fdtaddon_stack_2nd-merged.dtb.expect | 59 ++++++++++++++++
tests/fdtaddon_stack_2nd.dtba.expect | 30 ++++++++
tests/fdtaddon_stack_2nd.dtsa | 35 ++++++++++
tests/run_tests.sh | 21 ++++++
9 files changed, 359 insertions(+)
create mode 100644 tests/fdtaddon_stack_1st-merged.dtb.dts.expect
create mode 100644 tests/fdtaddon_stack_1st-merged.dtb.expect
create mode 100644 tests/fdtaddon_stack_1st.dtba.expect
create mode 100644 tests/fdtaddon_stack_1st.dtsa
create mode 100644 tests/fdtaddon_stack_2nd-merged.dtb.dts.expect
create mode 100644 tests/fdtaddon_stack_2nd-merged.dtb.expect
create mode 100644 tests/fdtaddon_stack_2nd.dtba.expect
create mode 100644 tests/fdtaddon_stack_2nd.dtsa
diff --git a/tests/fdtaddon_stack_1st-merged.dtb.dts.expect b/tests/fdtaddon_stack_1st-merged.dtb.dts.expect
new file mode 100644
index 00000000..766f3c94
--- /dev/null
+++ b/tests/fdtaddon_stack_1st-merged.dtb.dts.expect
@@ -0,0 +1,51 @@
+/dts-v1/;
+
+/ {
+
+ base-node {
+
+ sub-node {
+ prop = <0x00>;
+ phandle = <0x02>;
+ };
+ };
+
+ somewhere {
+
+ node-a1 {
+ compatible = "abc,aaa";
+ phandle = <0x01>;
+
+ /export/ node_a: &{/somewhere/node-a1};
+ /export/ other: &{/base-node/sub-node};
+
+ addon1-node {
+ ref-other = <&{/base-node/sub-node} 0x0a>;
+ prop = <0x00>;
+
+ sub-node-other {
+ phandle = <0x05>;
+ prop = <0x01>;
+ };
+
+ sub-node-stack1 {
+ phandle = <0x04>;
+ prop = <0x00>;
+ compatible = "abc,bbb";
+
+ /export/ stack: &{/somewhere/node-a1/addon1-node/sub-node-stack1};
+ /export/ base_other: &{/base-node/sub-node};
+ /export/ other: &{/somewhere/node-a1/addon1-node/sub-node-other};
+ };
+ };
+ };
+
+ node-a2 {
+ compatible = "abc,aaa";
+ phandle = <0x03>;
+
+ /export/ node_a: &{/somewhere/node-a2};
+ /export/ other: &{/base-node/sub-node};
+ };
+ };
+};
diff --git a/tests/fdtaddon_stack_1st-merged.dtb.expect b/tests/fdtaddon_stack_1st-merged.dtb.expect
new file mode 100644
index 00000000..367695a0
--- /dev/null
+++ b/tests/fdtaddon_stack_1st-merged.dtb.expect
@@ -0,0 +1,41 @@
+/dts-v1/;
+
+/ {
+ base-node {
+ sub-node {
+ prop = <0x00000000>;
+ phandle = <0x00000002>;
+ };
+ };
+ somewhere {
+ node-a1 {
+ // [FDT_EXPORT_SYM] 'node_a' -> phandle 0x00000001
+ // [FDT_EXPORT_SYM] 'other' -> phandle 0x00000002
+ compatible = "abc,aaa";
+ phandle = <0x00000001>;
+ addon1-node {
+ ref-other = <0x00000002 0x0000000a>;
+ // [FDT_PROPDATA_PHANDLE] ref-other[0]
+ prop = <0x00000000>;
+ sub-node-other {
+ phandle = <0x00000005>;
+ prop = <0x00000001>;
+ };
+ sub-node-stack1 {
+ // [FDT_EXPORT_SYM] 'stack' -> phandle 0x00000004
+ // [FDT_EXPORT_SYM] 'base_other' -> phandle 0x00000002
+ // [FDT_EXPORT_SYM] 'other' -> phandle 0x00000005
+ phandle = <0x00000004>;
+ prop = <0x00000000>;
+ compatible = "abc,bbb";
+ };
+ };
+ };
+ node-a2 {
+ // [FDT_EXPORT_SYM] 'node_a' -> phandle 0x00000003
+ // [FDT_EXPORT_SYM] 'other' -> phandle 0x00000002
+ compatible = "abc,aaa";
+ phandle = <0x00000003>;
+ };
+ };
+};
diff --git a/tests/fdtaddon_stack_1st.dtba.expect b/tests/fdtaddon_stack_1st.dtba.expect
new file mode 100644
index 00000000..cd0d89a0
--- /dev/null
+++ b/tests/fdtaddon_stack_1st.dtba.expect
@@ -0,0 +1,24 @@
+/dts-v1/;
+/addon/;
+
+// [FDT_IMPORT_SYM] 'node_a' (abc,aaa)
+// [FDT_IMPORT_SYM] 'other' ()
+&node_a {
+ addon1-node {
+ prop = <0x00000000>;
+ ref-other = <0xffffffff 0x0000000a>;
+ // [FDT_PROPDATA_PHANDLE_REF] ref-other[0], ref = other
+ sub-node-stack1 {
+ // [FDT_EXPORT_SYM] 'stack' -> phandle 0x00000001
+ // [FDT_EXPORT_SYM_REF] 'base_other' -> 'other'
+ // [FDT_EXPORT_SYM] 'other' -> phandle 0x00000002
+ compatible = "abc,bbb";
+ prop = <0x00000000>;
+ phandle = <0x00000001>;
+ };
+ sub-node-other {
+ prop = <0x00000001>;
+ phandle = <0x00000002>;
+ };
+ };
+};
diff --git a/tests/fdtaddon_stack_1st.dtsa b/tests/fdtaddon_stack_1st.dtsa
new file mode 100644
index 00000000..c4c86ff9
--- /dev/null
+++ b/tests/fdtaddon_stack_1st.dtsa
@@ -0,0 +1,28 @@
+// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
+/*
+ * Copyright (C) 2026 Bootlin
+ */
+
+/dts-v1/;
+/addon/;
+
+/import/ node_a: "abc,aaa";
+/import/ other: "";
+
+&node_a {
+ addon1-node {
+ prop = <0>;
+ ref-other = <&other 10>;
+ stack1: sub-node-stack1 {
+ compatible = "abc,bbb";
+ prop = <0>;
+ /export/ stack: &stack1;
+ /export/ base_other: &other;
+ /export/ other: &new_other;
+
+ };
+ new_other: sub-node-other {
+ prop = <1>;
+ };
+ };
+};
diff --git a/tests/fdtaddon_stack_2nd-merged.dtb.dts.expect b/tests/fdtaddon_stack_2nd-merged.dtb.dts.expect
new file mode 100644
index 00000000..fd38dc8c
--- /dev/null
+++ b/tests/fdtaddon_stack_2nd-merged.dtb.dts.expect
@@ -0,0 +1,70 @@
+/dts-v1/;
+
+/ {
+
+ base-node {
+
+ sub-node {
+ prop = <0x00>;
+ phandle = <0x02>;
+
+ addon2-node-base-other {
+ ref = <&{/somewhere/node-a1/addon1-node/sub-node-stack1/addon2-node}>;
+ prop = <0x01>;
+ };
+ };
+ };
+
+ somewhere {
+
+ node-a1 {
+ compatible = "abc,aaa";
+ phandle = <0x01>;
+
+ /export/ node_a: &{/somewhere/node-a1};
+ /export/ other: &{/base-node/sub-node};
+
+ addon1-node {
+ ref-other = <&{/base-node/sub-node} 0x0a>;
+ prop = <0x00>;
+
+ sub-node-other {
+ phandle = <0x05>;
+ prop = <0x01>;
+
+ addon2-node-other {
+ prop = <&{/base-node/sub-node}>;
+
+ addon2-subnode {
+ prop = <0x02>;
+ };
+ };
+ };
+
+ sub-node-stack1 {
+ phandle = <0x04>;
+ prop = <0x00>;
+ compatible = "abc,bbb";
+
+ /export/ stack: &{/somewhere/node-a1/addon1-node/sub-node-stack1};
+ /export/ base_other: &{/base-node/sub-node};
+ /export/ other: &{/somewhere/node-a1/addon1-node/sub-node-other};
+
+ addon2-node {
+ phandle = <0x06>;
+ ref-other = <&{/somewhere/node-a1/addon1-node/sub-node-other} 0x0a>;
+ prop = <0x00>;
+ };
+ };
+ };
+ };
+
+ node-a2 {
+ compatible = "abc,aaa";
+ phandle = <0x03>;
+
+ /export/ node_a: &{/somewhere/node-a2};
+ /export/ other: &{/base-node/sub-node};
+ };
+ };
+};
diff --git a/tests/fdtaddon_stack_2nd-merged.dtb.expect b/tests/fdtaddon_stack_2nd-merged.dtb.expect
new file mode 100644
index 00000000..1d45dd23
--- /dev/null
+++ b/tests/fdtaddon_stack_2nd-merged.dtb.expect
@@ -0,0 +1,59 @@
+/dts-v1/;
+
+/ {
+ base-node {
+ sub-node {
+ prop = <0x00000000>;
+ phandle = <0x00000002>;
+ addon2-node-base-other {
+ ref = <0x00000006>;
+ // [FDT_PROPDATA_PHANDLE] ref[0]
+ prop = <0x00000001>;
+ };
+ };
+ };
+ somewhere {
+ node-a1 {
+ // [FDT_EXPORT_SYM] 'node_a' -> phandle 0x00000001
+ // [FDT_EXPORT_SYM] 'other' -> phandle 0x00000002
+ compatible = "abc,aaa";
+ phandle = <0x00000001>;
+ addon1-node {
+ ref-other = <0x00000002 0x0000000a>;
+ // [FDT_PROPDATA_PHANDLE] ref-other[0]
+ prop = <0x00000000>;
+ sub-node-other {
+ phandle = <0x00000005>;
+ prop = <0x00000001>;
+ addon2-node-other {
+ prop = <0x00000002>;
+ // [FDT_PROPDATA_PHANDLE] prop[0]
+ addon2-subnode {
+ prop = <0x00000002>;
+ };
+ };
+ };
+ sub-node-stack1 {
+ // [FDT_EXPORT_SYM] 'stack' -> phandle 0x00000004
+ // [FDT_EXPORT_SYM] 'base_other' -> phandle 0x00000002
+ // [FDT_EXPORT_SYM] 'other' -> phandle 0x00000005
+ phandle = <0x00000004>;
+ prop = <0x00000000>;
+ compatible = "abc,bbb";
+ addon2-node {
+ phandle = <0x00000006>;
+ ref-other = <0x00000005 0x0000000a>;
+ // [FDT_PROPDATA_PHANDLE] ref-other[0]
+ prop = <0x00000000>;
+ };
+ };
+ };
+ };
+ node-a2 {
+ // [FDT_EXPORT_SYM] 'node_a' -> phandle 0x00000003
+ // [FDT_EXPORT_SYM] 'other' -> phandle 0x00000002
+ compatible = "abc,aaa";
+ phandle = <0x00000003>;
+ };
+ };
+};
diff --git a/tests/fdtaddon_stack_2nd.dtba.expect b/tests/fdtaddon_stack_2nd.dtba.expect
new file mode 100644
index 00000000..2e0f948a
--- /dev/null
+++ b/tests/fdtaddon_stack_2nd.dtba.expect
@@ -0,0 +1,30 @@
+/dts-v1/;
+/addon/;
+
+// [FDT_IMPORT_SYM] 'stack' (abc,bbb)
+// [FDT_IMPORT_SYM] 'other' ()
+// [FDT_IMPORT_SYM] 'base_other' ()
+&stack {
+ addon2-node {
+ prop = <0x00000000>;
+ ref-other = <0xffffffff 0x0000000a>;
+ // [FDT_PROPDATA_PHANDLE_REF] ref-other[0], ref = other
+ phandle = <0x00000001>;
+ };
+};
+&other {
+ addon2-node-other {
+ prop = <0xffffffff>;
+ // [FDT_PROPDATA_PHANDLE_REF] prop[0], ref = base_other
+ addon2-subnode {
+ prop = <0x00000002>;
+ };
+ };
+};
+&base_other {
+ addon2-node-base-other {
+ prop = <0x00000001>;
+ ref = <0x00000001>;
+ // [FDT_PROPDATA_PHANDLE] ref[0]
+ };
+};
diff --git a/tests/fdtaddon_stack_2nd.dtsa b/tests/fdtaddon_stack_2nd.dtsa
new file mode 100644
index 00000000..21d80d2a
--- /dev/null
+++ b/tests/fdtaddon_stack_2nd.dtsa
@@ -0,0 +1,35 @@
+// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
+/*
+ * Copyright (C) 2026 Bootlin
+ */
+
+/dts-v1/;
+/addon/;
+
+/import/ stack: "abc,bbb";
+/import/ other: "";
+/import/ base_other: "";
+
+&stack {
+ addon2_node: addon2-node {
+ prop = <0>;
+ ref-other = <&other 10>;
+ };
+};
+
+&other {
+ addon2-node-other {
+ prop = <&base_other>;
+
+ addon2-subnode {
+ prop = <2>;
+ };
+ };
+};
+
+&base_other {
+ addon2-node-base-other {
+ prop = <1>;
+ ref = <&addon2_node>;
+ };
+};
diff --git a/tests/run_tests.sh b/tests/run_tests.sh
index 8f8db608..d634274d 100755
--- a/tests/run_tests.sh
+++ b/tests/run_tests.sh
@@ -1347,6 +1347,27 @@ fdtaddon_tests() {
run_dtc_test -I dtb -O dts -o fdtaddon_addon_namespace-merged.dtb.dts fdtaddon_addon_namespace-merged.dtb
check_dts fdtaddon_addon_namespace-merged.dtb.dts
+
+ # test stacked addons
+ run_dtc_test -I dts -O dtb -o fdtaddon_stack_1st.dtba "$SRCDIR/fdtaddon_stack_1st.dtsa"
+ check_dtb fdtaddon_stack_1st.dtba
+
+ run_fdtaddon_test -i fdtaddon_base.dtb -o fdtaddon_stack_1st-merged.dtb \
+ -t "/somewhere/node-a1" fdtaddon_stack_1st.dtba
+ check_dtb fdtaddon_stack_1st-merged.dtb
+
+ run_dtc_test -I dtb -O dts -o fdtaddon_stack_1st-merged.dtb.dts fdtaddon_stack_1st-merged.dtb
+ check_dts fdtaddon_stack_1st-merged.dtb.dts
+
+ run_dtc_test -I dts -O dtb -o fdtaddon_stack_2nd.dtba "$SRCDIR/fdtaddon_stack_2nd.dtsa"
+ check_dtb fdtaddon_stack_2nd.dtba
+
+ run_fdtaddon_test -i fdtaddon_stack_1st-merged.dtb -o fdtaddon_stack_2nd-merged.dtb \
+ -t "/somewhere/node-a1/addon1-node/sub-node-stack1" fdtaddon_stack_2nd.dtba
+ check_dtb fdtaddon_stack_2nd-merged.dtb
+
+ run_dtc_test -I dtb -O dts -o fdtaddon_stack_2nd-merged.dtb.dts fdtaddon_stack_2nd-merged.dtb
+ check_dts fdtaddon_stack_2nd-merged.dtb.dts
}
pylibfdt_tests () {
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 72/74] tests: fdtaddon: Add a test using more realistic dts and dtsa
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (70 preceding siblings ...)
2026-08-26 9:49 ` [RFC PATCH v2 71/74] tests: fdtaddon: Add a test for using 'stacked' addons Herve Codina
@ 2026-08-26 9:49 ` Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 73/74] libfdt/fdt_addon.c: Add support for an external resolver Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 74/74] fdtaddon: Add a simple " Herve Codina
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:49 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
The dts and dtsa files used in the fdtaddon tests were centered on
specific addon features in order to test those specific features.
In term of structure or naming, they are not representative of real
use-cases.
This new test uses realistic dts and dtsa files with naming and
structures tradidionally found in real cases.
The dts describes the hardware of the base board.
The hardware structure of the base board is the following:
+--------------+
| 5V regulator +-------+
+--------------+ |
|
+----------------------+ | +----------------+
| SOC | | | Connector A |
| | | | |
| +-----------------+ | +-------+ 5v |
| + i2c1 controller +---------------+ i2c bus |
| +-----------------+ | +-------+ gpio 0 / irq 0 |
| | | +---+ gpio 1 / irq 1 |
| +------------------+ | | | +----------------+
| | gpio1 controller | | | |
| | gpio #10 +------+ |
| +------------------+ | |
| | |
| +---------------- -+ | |
| | gpio2 controller | | |
| | gpio #3 +----------+
| +------------------+ |
+----------------------+
The connector 'Connector A' is the connector where the
pluggable board can be connected to. It is described in the dts and
related export symbols are set in order to be usable by the dtsa
describing the pluggable board.
The hardware structure of the pluggable board is the following:
+---------------+
| EEPROM |
| i2c addr 0x51 |
| +
+-------+ i2c |
| +---------------+
|
| +---------------+
| | Expander IO |
+----------------+ | | i2c addr 0x23 |
| Connector | | | |
| | | +---+ Vcc1 |
| 5v +---|---+---+ Vcc2 |
| i2c bus +---+-------+ i2c |
| gpio 0 / irq 0 +-----------+ irq |
| gpio 1 / irq 1 +-----------+ reset |
+----------------+ +---------------+
This structure is described in the addon dtsa. This addon dtsa is
expected to be applied to the base device tree node describing the
connector the pluggable board is connected to.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
...ddon_realistic_addon-merged.dtb.dts.expect | 91 ++++++++++++++++++
...fdtaddon_realistic_addon-merged.dtb.expect | 93 +++++++++++++++++++
tests/fdtaddon_realistic_addon.dtba.expect | 32 +++++++
tests/fdtaddon_realistic_addon.dtsa | 50 ++++++++++
tests/fdtaddon_realistic_base.dtb.expect | 72 ++++++++++++++
tests/fdtaddon_realistic_base.dts | 74 +++++++++++++++
tests/run_tests.sh | 14 +++
7 files changed, 426 insertions(+)
create mode 100644 tests/fdtaddon_realistic_addon-merged.dtb.dts.expect
create mode 100644 tests/fdtaddon_realistic_addon-merged.dtb.expect
create mode 100644 tests/fdtaddon_realistic_addon.dtba.expect
create mode 100644 tests/fdtaddon_realistic_addon.dtsa
create mode 100644 tests/fdtaddon_realistic_base.dtb.expect
create mode 100644 tests/fdtaddon_realistic_base.dts
diff --git a/tests/fdtaddon_realistic_addon-merged.dtb.dts.expect b/tests/fdtaddon_realistic_addon-merged.dtb.dts.expect
new file mode 100644
index 00000000..49aa9500
--- /dev/null
+++ b/tests/fdtaddon_realistic_addon-merged.dtb.dts.expect
@@ -0,0 +1,91 @@
+/dts-v1/;
+
+/ {
+
+ regulator {
+ compatible = "regulator-fixed";
+ regulator-min-microvolt = <0x4c4b40>;
+ regulator-max-microvolt = <0x4c4b40>;
+ gpios = <&{/soc/gpio@2000} 0x05 0x00>;
+ phandle = <0x05>;
+ };
+
+ soc {
+ compatible = "simple-bus";
+ #address-cells = <0x01>;
+ #size-cells = <0x01>;
+
+ i2c@1000 {
+ compatible = "xyz,i2c-controller";
+ reg = <0x1000 0x100>;
+ #address-cells = <0x01>;
+ #size-cells = <0x00>;
+ phandle = <0x06>;
+ };
+
+ gpio@2000 {
+ compatible = "xyz,gpio-controller";
+ reg = <0x2000 0x100>;
+ gpio-controller;
+ #gpio-cells = <0x02>;
+ interrupt-controller;
+ #interrupt-cells = <0x02>;
+ #address-cells = <0x00>;
+ phandle = <0x01>;
+ };
+
+ gpio@3000 {
+ compatible = "xyz,gpio-controller";
+ reg = <0x3000 0x100>;
+ gpio-controller;
+ #gpio-cells = <0x02>;
+ interrupt-controller;
+ #interrupt-cells = <0x02>;
+ #address-cells = <0x00>;
+ phandle = <0x02>;
+ };
+ };
+
+ connector-a {
+ compatible = "abc,foo-connector";
+ #address-cells = <0x00>;
+ #interrupt-cells = <0x02>;
+ interrupt-map = <0x00 0x00 &{/soc/gpio@2000} 0x0a 0x00 0x00 0x100 &{/soc/gpio@2000} 0x0a 0x100 0x01 0x00 &{/soc/gpio@3000} 0x03 0x00 0x01 0x100 &{/soc/gpio@3000} 0x03 0x100>;
+ #gpio-cells = <0x02>;
+ gpio-map-mask = <0x0f 0x00>;
+ gpio-map-pass-thru = <0x00 0x0f>;
+ gpio-map = <0x00 0x00 &{/soc/gpio@2000} 0x0a 0x00 0x01 0x00 &{/soc/gpio@3000} 0x03 0x00>;
+ phandle = <0x03>;
+
+ /export/ connector: &{/connector-a};
+ /export/ conn_i2c: &{/connector-a/conn-i2c};
+ /export/ conn_5v: &{/regulator};
+
+ conn-i2c {
+ compatible = "i2c-bus-extension";
+ i2c-parent = <&{/soc/i2c@1000}>;
+ #address-cells = <0x01>;
+ #size-cells = <0x00>;
+ phandle = <0x04>;
+
+ eeprom@51 {
+ reg = <0x51>;
+ compatible = "xxx,eeprom";
+ };
+
+ gpio@23 {
+ reset-gpios = <&{/connector-a} 0x01 0x00>;
+ vcc2-supply = <&{/regulator}>;
+ vcc1-supply = <&{/regulator}>;
+ #interrupt-cells = <0x02>;
+ interrupt-controller;
+ interrupts = <0x00 0x100>;
+ interrupt-parent = <&{/connector-a}>;
+ #gpio-cells = <0x02>;
+ gpio-controller;
+ reg = <0x23>;
+ compatible = "xxx,expander-io";
+ };
+ };
+ };
+};
diff --git a/tests/fdtaddon_realistic_addon-merged.dtb.expect b/tests/fdtaddon_realistic_addon-merged.dtb.expect
new file mode 100644
index 00000000..54f47a1c
--- /dev/null
+++ b/tests/fdtaddon_realistic_addon-merged.dtb.expect
@@ -0,0 +1,93 @@
+/dts-v1/;
+
+/ {
+ regulator {
+ compatible = "regulator-fixed";
+ regulator-min-microvolt = <0x004c4b40>;
+ regulator-max-microvolt = <0x004c4b40>;
+ gpios = <0x00000001 0x00000005 0x00000000>;
+ // [FDT_PROPDATA_PHANDLE] gpios[0]
+ phandle = <0x00000005>;
+ };
+ soc {
+ compatible = "simple-bus";
+ #address-cells = <0x00000001>;
+ #size-cells = <0x00000001>;
+ i2c@1000 {
+ compatible = "xyz,i2c-controller";
+ reg = <0x00001000 0x00000100>;
+ #address-cells = <0x00000001>;
+ #size-cells = <0x00000000>;
+ phandle = <0x00000006>;
+ };
+ gpio@2000 {
+ compatible = "xyz,gpio-controller";
+ reg = <0x00002000 0x00000100>;
+ gpio-controller;
+ #gpio-cells = <0x00000002>;
+ interrupt-controller;
+ #interrupt-cells = <0x00000002>;
+ #address-cells = <0x00000000>;
+ phandle = <0x00000001>;
+ };
+ gpio@3000 {
+ compatible = "xyz,gpio-controller";
+ reg = <0x00003000 0x00000100>;
+ gpio-controller;
+ #gpio-cells = <0x00000002>;
+ interrupt-controller;
+ #interrupt-cells = <0x00000002>;
+ #address-cells = <0x00000000>;
+ phandle = <0x00000002>;
+ };
+ };
+ connector-a {
+ // [FDT_EXPORT_SYM] 'connector' -> phandle 0x00000003
+ // [FDT_EXPORT_SYM] 'conn_i2c' -> phandle 0x00000004
+ // [FDT_EXPORT_SYM] 'conn_5v' -> phandle 0x00000005
+ compatible = "abc,foo-connector";
+ #address-cells = <0x00000000>;
+ #interrupt-cells = <0x00000002>;
+ interrupt-map = <0x00000000 0x00000000 0x00000001 0x0000000a 0x00000000 0x00000000 0x00000100 0x00000001 0x0000000a 0x00000100 0x00000001 0x00000000 0x00000002 0x00000003 0x00000000 0x00000001 0x00000100 0x00000002 0x00000003 0x00000100>;
+ // [FDT_PROPDATA_PHANDLE] interrupt-map[8]
+ // [FDT_PROPDATA_PHANDLE] interrupt-map[28]
+ // [FDT_PROPDATA_PHANDLE] interrupt-map[48]
+ // [FDT_PROPDATA_PHANDLE] interrupt-map[68]
+ #gpio-cells = <0x00000002>;
+ gpio-map-mask = <0x0000000f 0x00000000>;
+ gpio-map-pass-thru = <0x00000000 0x0000000f>;
+ gpio-map = <0x00000000 0x00000000 0x00000001 0x0000000a 0x00000000 0x00000001 0x00000000 0x00000002 0x00000003 0x00000000>;
+ // [FDT_PROPDATA_PHANDLE] gpio-map[8]
+ // [FDT_PROPDATA_PHANDLE] gpio-map[28]
+ phandle = <0x00000003>;
+ conn-i2c {
+ compatible = "i2c-bus-extension";
+ i2c-parent = <0x00000006>;
+ // [FDT_PROPDATA_PHANDLE] i2c-parent[0]
+ #address-cells = <0x00000001>;
+ #size-cells = <0x00000000>;
+ phandle = <0x00000004>;
+ eeprom@51 {
+ reg = <0x00000051>;
+ compatible = "xxx,eeprom";
+ };
+ gpio@23 {
+ reset-gpios = <0x00000003 0x00000001 0x00000000>;
+ // [FDT_PROPDATA_PHANDLE] reset-gpios[0]
+ vcc2-supply = <0x00000005>;
+ // [FDT_PROPDATA_PHANDLE] vcc2-supply[0]
+ vcc1-supply = <0x00000005>;
+ // [FDT_PROPDATA_PHANDLE] vcc1-supply[0]
+ #interrupt-cells = <0x00000002>;
+ interrupt-controller;
+ interrupts = <0x00000000 0x00000100>;
+ interrupt-parent = <0x00000003>;
+ // [FDT_PROPDATA_PHANDLE] interrupt-parent[0]
+ #gpio-cells = <0x00000002>;
+ gpio-controller;
+ reg = <0x00000023>;
+ compatible = "xxx,expander-io";
+ };
+ };
+ };
+};
diff --git a/tests/fdtaddon_realistic_addon.dtba.expect b/tests/fdtaddon_realistic_addon.dtba.expect
new file mode 100644
index 00000000..eeef4d43
--- /dev/null
+++ b/tests/fdtaddon_realistic_addon.dtba.expect
@@ -0,0 +1,32 @@
+/dts-v1/;
+/addon/;
+
+// [FDT_IMPORT_SYM] 'connector' (abc,foo-connector)
+// [FDT_IMPORT_SYM] 'conn_5v' ()
+&connector {
+ conn-i2c {
+ gpio@23 {
+ compatible = "xxx,expander-io";
+ reg = <0x00000023>;
+ gpio-controller;
+ #gpio-cells = <0x00000002>;
+ interrupt-parent = <0xffffffff>;
+ // [FDT_PROPDATA_PHANDLE_REF] interrupt-parent[0], ref = connector
+ interrupts = <0x00000000 0x00000100>;
+ interrupt-controller;
+ #interrupt-cells = <0x00000002>;
+ vcc1-supply = <0xffffffff>;
+ // [FDT_PROPDATA_PHANDLE_REF] vcc1-supply[0], ref = conn_5v
+ vcc2-supply = <0xffffffff>;
+ // [FDT_PROPDATA_PHANDLE_REF] vcc2-supply[0], ref = connector.conn_5v
+ reset-gpios = <0xffffffff 0x00000001 0x00000000>;
+ // [FDT_PROPDATA_PHANDLE_REF] reset-gpios[0], ref = connector
+ };
+ };
+};
+&connector.conn_i2c {
+ eeprom@51 {
+ compatible = "xxx,eeprom";
+ reg = <0x00000051>;
+ };
+};
diff --git a/tests/fdtaddon_realistic_addon.dtsa b/tests/fdtaddon_realistic_addon.dtsa
new file mode 100644
index 00000000..255a1ffc
--- /dev/null
+++ b/tests/fdtaddon_realistic_addon.dtsa
@@ -0,0 +1,50 @@
+// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
+/*
+ * Copyright (C) 2026 Bootlin
+ */
+
+/dts-v1/;
+/addon/;
+
+/import/ connector: "abc,foo-connector";
+/import/ conn_5v: "";
+
+&connector {
+ /*
+ * Add a gpio expander at the conn-i2c node (i2c extension bus)
+ * available at the connector node
+ */
+ conn-i2c {
+ gpio@23 {
+ compatible = "xxx,expander-io";
+ reg = <0x23>;
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ /* Use the connector interrupt number 0 */
+ interrupt-parent = <&connector>;
+ interrupts = <0 0x100>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+
+ /* Use 5V power-supply wired to the connector */
+ vcc1-supply = <&conn_5v>; /* Need to be an imported symbol */
+ vcc2-supply = <&connector.conn_5v>; /* Only connector need to be imported */
+
+ /* Use the connector gpio number 1 */
+ reset-gpios = <&connector 1 0>;
+ };
+ };
+};
+
+/*
+ * Use namespace reference to add an EEPROM at i2c bus connected
+ * to the connector. It can be an i2c extension or the i2c controller itself.
+ * We don't know and we don't have to know.
+ */
+&connector.conn_i2c {
+ eeprom@51 {
+ compatible = "xxx,eeprom";
+ reg = <0x51>;
+ };
+};
diff --git a/tests/fdtaddon_realistic_base.dtb.expect b/tests/fdtaddon_realistic_base.dtb.expect
new file mode 100644
index 00000000..9addbbd8
--- /dev/null
+++ b/tests/fdtaddon_realistic_base.dtb.expect
@@ -0,0 +1,72 @@
+/dts-v1/;
+
+/ {
+ regulator {
+ compatible = "regulator-fixed";
+ regulator-min-microvolt = <0x004c4b40>;
+ regulator-max-microvolt = <0x004c4b40>;
+ gpios = <0x00000001 0x00000005 0x00000000>;
+ // [FDT_PROPDATA_PHANDLE] gpios[0]
+ phandle = <0x00000005>;
+ };
+ soc {
+ compatible = "simple-bus";
+ #address-cells = <0x00000001>;
+ #size-cells = <0x00000001>;
+ i2c@1000 {
+ compatible = "xyz,i2c-controller";
+ reg = <0x00001000 0x00000100>;
+ #address-cells = <0x00000001>;
+ #size-cells = <0x00000000>;
+ phandle = <0x00000006>;
+ };
+ gpio@2000 {
+ compatible = "xyz,gpio-controller";
+ reg = <0x00002000 0x00000100>;
+ gpio-controller;
+ #gpio-cells = <0x00000002>;
+ interrupt-controller;
+ #interrupt-cells = <0x00000002>;
+ #address-cells = <0x00000000>;
+ phandle = <0x00000001>;
+ };
+ gpio@3000 {
+ compatible = "xyz,gpio-controller";
+ reg = <0x00003000 0x00000100>;
+ gpio-controller;
+ #gpio-cells = <0x00000002>;
+ interrupt-controller;
+ #interrupt-cells = <0x00000002>;
+ #address-cells = <0x00000000>;
+ phandle = <0x00000002>;
+ };
+ };
+ connector-a {
+ // [FDT_EXPORT_SYM] 'connector' -> phandle 0x00000003
+ // [FDT_EXPORT_SYM] 'conn_i2c' -> phandle 0x00000004
+ // [FDT_EXPORT_SYM] 'conn_5v' -> phandle 0x00000005
+ compatible = "abc,foo-connector";
+ #address-cells = <0x00000000>;
+ #interrupt-cells = <0x00000002>;
+ interrupt-map = <0x00000000 0x00000000 0x00000001 0x0000000a 0x00000000 0x00000000 0x00000100 0x00000001 0x0000000a 0x00000100 0x00000001 0x00000000 0x00000002 0x00000003 0x00000000 0x00000001 0x00000100 0x00000002 0x00000003 0x00000100>;
+ // [FDT_PROPDATA_PHANDLE] interrupt-map[8]
+ // [FDT_PROPDATA_PHANDLE] interrupt-map[28]
+ // [FDT_PROPDATA_PHANDLE] interrupt-map[48]
+ // [FDT_PROPDATA_PHANDLE] interrupt-map[68]
+ #gpio-cells = <0x00000002>;
+ gpio-map-mask = <0x0000000f 0x00000000>;
+ gpio-map-pass-thru = <0x00000000 0x0000000f>;
+ gpio-map = <0x00000000 0x00000000 0x00000001 0x0000000a 0x00000000 0x00000001 0x00000000 0x00000002 0x00000003 0x00000000>;
+ // [FDT_PROPDATA_PHANDLE] gpio-map[8]
+ // [FDT_PROPDATA_PHANDLE] gpio-map[28]
+ phandle = <0x00000003>;
+ conn-i2c {
+ compatible = "i2c-bus-extension";
+ i2c-parent = <0x00000006>;
+ // [FDT_PROPDATA_PHANDLE] i2c-parent[0]
+ #address-cells = <0x00000001>;
+ #size-cells = <0x00000000>;
+ phandle = <0x00000004>;
+ };
+ };
+};
diff --git a/tests/fdtaddon_realistic_base.dts b/tests/fdtaddon_realistic_base.dts
new file mode 100644
index 00000000..0c13f1af
--- /dev/null
+++ b/tests/fdtaddon_realistic_base.dts
@@ -0,0 +1,74 @@
+// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
+/*
+ * Copyright (C) 2026 Bootlin
+ */
+
+/dts-v1/;
+
+/ {
+ conn_a_5v: regulator {
+ compatible = "regulator-fixed";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ gpios = <&gpio1 5 0>;
+ };
+
+ soc {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ i2c1: i2c@1000 {
+ compatible = "xyz,i2c-controller";
+ reg = <0x1000 0x100>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
+ gpio1: gpio@2000 {
+ compatible = "xyz,gpio-controller";
+ reg = <0x2000 0x100>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ #address-cells = <0>; /* interrupt-map usage */
+ };
+
+ gpio2: gpio@3000 {
+ compatible = "xyz,gpio-controller";
+ reg = <0x3000 0x100>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ #address-cells = <0>; /* interrupt-map usage */
+ };
+ };
+
+ conn_a: connector-a {
+ compatible = "abc,foo-connector";
+ #address-cells = <0>;
+ #interrupt-cells = <2>;
+ interrupt-map = <0 0 &gpio1 10 0>,
+ <0 0x100 &gpio1 10 0x100>,
+ <1 0 &gpio2 3 0>,
+ <1 0x100 &gpio2 3 0x100>;
+ #gpio-cells = <2>;
+ gpio-map-mask = <0xf 0x0>;
+ gpio-map-pass-thru = <0x0 0xf>;
+ gpio-map = <0 0 &gpio1 10 0>,
+ <1 0 &gpio2 3 0>;
+
+ /export/ connector: &conn_a;
+ /export/ conn_i2c: &conn_a_i2c;
+ /export/ conn_5v: &conn_a_5v;
+
+ conn_a_i2c: conn-i2c {
+ compatible = "i2c-bus-extension";
+ i2c-parent = <&i2c1>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+ };
+};
diff --git a/tests/run_tests.sh b/tests/run_tests.sh
index d634274d..664ebf5d 100755
--- a/tests/run_tests.sh
+++ b/tests/run_tests.sh
@@ -1368,6 +1368,20 @@ fdtaddon_tests() {
run_dtc_test -I dtb -O dts -o fdtaddon_stack_2nd-merged.dtb.dts fdtaddon_stack_2nd-merged.dtb
check_dts fdtaddon_stack_2nd-merged.dtb.dts
+
+ # More realistic dts and dtsa input files
+ run_dtc_test -I dts -O dtb -o fdtaddon_realistic_base.dtb "$SRCDIR/fdtaddon_realistic_base.dts"
+ check_dtb fdtaddon_realistic_base.dtb
+
+ run_dtc_test -I dts -O dtb -o fdtaddon_realistic_addon.dtba "$SRCDIR/fdtaddon_realistic_addon.dtsa"
+ check_dtb fdtaddon_realistic_addon.dtba
+
+ run_fdtaddon_test -i fdtaddon_realistic_base.dtb -o fdtaddon_realistic_addon-merged.dtb \
+ -t "/connector-a" fdtaddon_realistic_addon.dtba
+ check_dtb fdtaddon_realistic_addon-merged.dtb
+
+ run_dtc_test -I dtb -O dts -o fdtaddon_realistic_addon-merged.dtb.dts fdtaddon_realistic_addon-merged.dtb
+ check_dts fdtaddon_realistic_addon-merged.dtb.dts
}
pylibfdt_tests () {
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 73/74] libfdt/fdt_addon.c: Add support for an external resolver
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (71 preceding siblings ...)
2026-08-26 9:49 ` [RFC PATCH v2 72/74] tests: fdtaddon: Add a test using more realistic dts and dtsa Herve Codina
@ 2026-08-26 9:49 ` Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 74/74] fdtaddon: Add a simple " Herve Codina
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:49 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
fdt_addon.c uses its own default resolver to resolve import symbols.
It expects a matching between the name of the import symbol to resolve
and the name of export symbols available in the addon target node (node
in the base dtb).
In some cases, it makes sense to have a resolver that could choose the
right export symbol to match based on some other criteria.
The external resolver allows the user of libfdt to provide its own
resolver and so match an import symbol based on its own criteria.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
libfdt/fdt_addon.c | 138 +++++++++++++++++++++++++++++++++------------
libfdt/libfdt.h | 98 +++++++++++++++++++++++++++++++-
libfdt/version.lds | 2 +
3 files changed, 200 insertions(+), 38 deletions(-)
diff --git a/libfdt/fdt_addon.c b/libfdt/fdt_addon.c
index 54e71cbd..99d9c10a 100644
--- a/libfdt/fdt_addon.c
+++ b/libfdt/fdt_addon.c
@@ -10,6 +10,11 @@
#include "libfdt_internal.h"
+struct fdt_addon_resolver {
+ const struct fdt_addon_resolver_ops *ops;
+ void *priv;
+};
+
/**
* fdt_skip_string - Skip a string
* @fdt: device tree blob
@@ -568,21 +573,9 @@ static int fdt_resolve_symbol(const void *fdt, int target_node,
return -FDT_ERR_NOTFOUND;
}
-/**
- * fdt_resolve_import - Resolve an imported symbol
- * @fdt: base device tree blob
- * @target_node: Target node offset in the base device tree blob containing
- * the exported symbols used for the resolution.
- * @importsym_name: Import symbol name
- * @importsym_compatible: Import symbol compatible string
- *
- * returns:
- * offset of the node matching the symbol after the resolution on success.
- * Negative error code on failure.
- */
-static int fdt_resolve_import(const void *fdt, int target_node,
- const char *importsym_name,
- const char *importsym_compatible)
+int fdt_addon_resolve_default(const void *fdt, int target_node,
+ const char *importsym_name,
+ const char *importsym_compatible)
{
/*
* Do not check the import symbol compatible string against the found
@@ -604,6 +597,33 @@ static int fdt_resolve_import(const void *fdt, int target_node,
strlen(importsym_name));
}
+/**
+ * fdt_resolve_import - Resolve an imported symbol
+ * @fdt: base device tree blob
+ * @target_node: Target node offset in the base device tree blob containing
+ * the exported symbols used for the resolution.
+ * @importsym_name: Import symbol name
+ * @importsym_compatible: Import symbol compatible string
+ * @resolver: Custom resolver. If NULL, the default resolver is used.
+ *
+ * returns:
+ * offset of the node matching the symbol after the resolution on success.
+ * Negative error code on failure.
+ */
+static int fdt_resolve_import(const void *fdt, int target_node,
+ const char *importsym_name,
+ const char *importsym_compatible,
+ const struct fdt_addon_resolver *resolver)
+{
+ if (resolver)
+ return resolver->ops->resolve(resolver->priv, fdt, target_node,
+ importsym_name,
+ importsym_compatible);
+
+ return fdt_addon_resolve_default(fdt, target_node, importsym_name,
+ importsym_compatible);
+}
+
static int fdt_check_importsym_offset(const void *fdt, int offset, int *data_offset)
{
int nextoffset, d;
@@ -709,6 +729,7 @@ static const char *fdt_importsym_get_compatible(const void *fdt, int offset)
* resolution.
* @sym_name: Symbol name
* @sym_namelen: Length of the symbol name
+ * @resolver: Custom resolver, NULL to use the default resolver.
*
* addon_resolve_symbol() resolved a symbol used by an addon using the import
* symbol table available in the addon device tree blob.
@@ -719,7 +740,8 @@ static const char *fdt_importsym_get_compatible(const void *fdt, int offset)
* Negative error code on failure.
*/
static int addon_resolve_symbol(const void *fdta, const void *fdt, int target_node,
- const char *sym_name, size_t sym_namelen)
+ const char *sym_name, size_t sym_namelen,
+ const struct fdt_addon_resolver *resolver)
{
const char *importsym_compat;
const char *importsym_name;
@@ -739,7 +761,7 @@ static int addon_resolve_symbol(const void *fdta, const void *fdt, int target_no
continue;
return fdt_resolve_import(fdt, target_node, importsym_name,
- importsym_compat);
+ importsym_compat, resolver);
}
/* Not found ... */
return -FDT_ERR_NOTFOUND;
@@ -753,6 +775,7 @@ static int addon_resolve_symbol(const void *fdta, const void *fdt, int target_no
* device tree blob, must have exported symbols needed for the
* resolution.
* @ref: The reference to resolve
+ * @resolver: Custom resolver, NULL to use the default resolver
*
* addon_resolve_ref() resolves a reference used by an addon.
* A reference can be composed of several symbols separated by a '.' char.
@@ -769,7 +792,8 @@ static int addon_resolve_symbol(const void *fdta, const void *fdt, int target_no
* Negative error code on failure.
*/
static int addon_resolve_ref(const void *fdta, const void *fdt, int target_node,
- const char *ref)
+ const char *ref,
+ const struct fdt_addon_resolver *resolver)
{
const char *end = ref + strlen(ref);
const char *r = ref;
@@ -794,7 +818,8 @@ static int addon_resolve_ref(const void *fdta, const void *fdt, int target_node,
d = end;
if (r == ref)
- tmp_node = addon_resolve_symbol(fdta, fdt, tmp_node, r, d-r);
+ tmp_node = addon_resolve_symbol(fdta, fdt, tmp_node, r, d-r,
+ resolver);
else
tmp_node = fdt_resolve_symbol(fdt, tmp_node, r, d-r);
@@ -812,6 +837,7 @@ static int addon_resolve_ref(const void *fdta, const void *fdt, int target_node,
* @fdta: Addon device tree blob
* @fdt: Device tree blob the addon is going to be applied to
* @target_node: Offset of the node in fdt the addon is going to be applied to
+ * @resolver: Custom resolver. NULL to use the default resolver
*
* addon_resolve_phandles() resolved unresolved phandle symbols available in
* the addon.
@@ -820,7 +846,8 @@ static int addon_resolve_ref(const void *fdta, const void *fdt, int target_node,
* 0 on success
* Negative error code on failure
*/
-static int addon_resolve_phandles(void *fdta, const void *fdt, int target_node)
+static int addon_resolve_phandles(void *fdta, const void *fdt, int target_node,
+ const struct fdt_addon_resolver *resolver)
{
int offset, next_offset, d;
int last_prop_offset = -1;
@@ -880,7 +907,8 @@ static int addon_resolve_phandles(void *fdta, const void *fdt, int target_node)
ref = fdt_offset_ptr(fdta, d, 1);
/* Resolve this phandle */
- ref_node = addon_resolve_ref(fdta, fdt, target_node, ref);
+ ref_node = addon_resolve_ref(fdta, fdt, target_node, ref,
+ resolver);
if (ref_node < 0)
return ref_node;
@@ -931,7 +959,8 @@ static int addon_resolve_phandles(void *fdta, const void *fdt, int target_node)
ref = fdt_offset_ptr(fdta, d, 1);
/* Resolve the phandle */
- ref_node = addon_resolve_ref(fdta, fdt, target_node, ref);
+ ref_node = addon_resolve_ref(fdta, fdt, target_node,
+ ref, resolver);
if (ref_node < 0)
return ref_node;
@@ -1153,6 +1182,7 @@ static const char *fdt_orphan_get_ref(const void *fdt, int offset)
* @fdt: Base Device Tree blob
* @target_node: Target node in the base device tree the addon is going to be
* applied to
+ * @resolver: Custom resolver, NULL to use the default resolver
*
* Checks recursively if applying fdta overwrites phandle values in the base
* fdt. When such a phandle is found, the fdta is changed to use the fdt's
@@ -1163,7 +1193,8 @@ static const char *fdt_orphan_get_ref(const void *fdt, int offset)
* Negative error code on failure
*/
static int addon_prevent_phandles_overwrite(void *fdta, const void *fdt,
- int target_node)
+ int target_node,
+ const struct fdt_addon_resolver *resolver)
{
int fdt_orphan_target;
int addon_root_node;
@@ -1187,7 +1218,8 @@ static int addon_prevent_phandles_overwrite(void *fdta, const void *fdt,
if (!ref)
return -FDT_ERR_BADSTRUCTURE;
- fdt_orphan_target = addon_resolve_ref(fdta, fdt, target_node, ref);
+ fdt_orphan_target = addon_resolve_ref(fdta, fdt, target_node,
+ ref, resolver);
if (fdt_orphan_target < 0)
return fdt_orphan_target;
@@ -1505,6 +1537,7 @@ static int addon_merge_node_properties(const void *fdta, int fdta_node,
* @target_path: Path in the base Device Tree to the node where the addon is
* applied to. This is the node where export symbols used for symbol
* resolution are present.
+ * @resolver: Custom resolver. NULL to use the default resolver.
*
* returns:
* 0 on success
@@ -1512,7 +1545,8 @@ static int addon_merge_node_properties(const void *fdta, int fdta_node,
*/
static int addon_merge_node_exports(const void *fdta, int fdta_node,
void *fdt, int fdt_node,
- bool is_existing_node, const char *target_path)
+ bool is_existing_node, const char *target_path,
+ const struct fdt_addon_resolver *resolver)
{
const char *exportsym_name;
const char *exportsym_ref;
@@ -1554,7 +1588,8 @@ static int addon_merge_node_exports(const void *fdta, int fdta_node,
return target_node;
/* Resolve the reference */
- ref_node = addon_resolve_ref(fdta, fdt, target_node, exportsym_ref);
+ ref_node = addon_resolve_ref(fdta, fdt, target_node,
+ exportsym_ref, resolver);
if (ref_node < 0)
return ref_node;
@@ -1591,6 +1626,7 @@ static int addon_merge_node_exports(const void *fdta, int fdta_node,
* @target_path: Path in the base Device Tree to the node where the addon is
* applied to. This is the node where export symbols used for symbol
* resolution are present.
+ * @resolver: Custom resolver, NULL to use the default resolver.
*
* returns:
* 0 on success
@@ -1598,7 +1634,8 @@ static int addon_merge_node_exports(const void *fdta, int fdta_node,
*/
static int addon_merge_node(const void *fdta, int fdta_node, void *fdt,
int fdt_node, bool is_existing_node,
- const char *target_path)
+ const char *target_path,
+ const struct fdt_addon_resolver *resolver)
{
bool is_existing;
int fdta_subnode;
@@ -1607,7 +1644,8 @@ static int addon_merge_node(const void *fdta, int fdta_node, void *fdt,
int ret;
ret = addon_merge_node_exports(fdta, fdta_node, fdt, fdt_node,
- is_existing_node, target_path);
+ is_existing_node, target_path,
+ resolver);
if (ret)
return ret;
@@ -1632,7 +1670,7 @@ static int addon_merge_node(const void *fdta, int fdta_node, void *fdt,
return fdt_subnode;
ret = addon_merge_node(fdta, fdta_subnode, fdt, fdt_subnode,
- is_existing, target_path);
+ is_existing, target_path, resolver);
if (ret)
return ret;
}
@@ -1640,7 +1678,8 @@ static int addon_merge_node(const void *fdta, int fdta_node, void *fdt,
return 0;
}
-static int addon_merge(void *fdta, void *fdt, const char *target_path)
+static int addon_merge(void *fdta, void *fdt, const char *target_path,
+ const struct fdt_addon_resolver *resolver)
{
int fdt_orphan_target;
int fdta_orphan;
@@ -1654,7 +1693,8 @@ static int addon_merge(void *fdta, void *fdt, const char *target_path)
return target_node;
/* Merge the addon root node into the base target_node */
- ret = addon_merge_node(fdta, 0, fdt, target_node, true, target_path);
+ ret = addon_merge_node(fdta, 0, fdt, target_node, true, target_path,
+ resolver);
if (ret < 0)
return ret;
@@ -1663,12 +1703,13 @@ static int addon_merge(void *fdta, void *fdt, const char *target_path)
if (!ref)
return -FDT_ERR_BADSTRUCTURE;
- fdt_orphan_target = addon_resolve_ref(fdta, fdt, target_node, ref);
+ fdt_orphan_target = addon_resolve_ref(fdta, fdt, target_node,
+ ref, resolver);
if (fdt_orphan_target < 0)
return fdt_orphan_target;
ret = addon_merge_node(fdta, fdta_orphan, fdt, fdt_orphan_target,
- true, target_path);
+ true, target_path, resolver);
if (ret < 0)
return ret;
@@ -1688,8 +1729,12 @@ static int addon_merge(void *fdta, void *fdt, const char *target_path)
return 0;
}
-int fdt_addon_apply(void *fdt, void *fdta, const char *target)
+int fdt_addon_apply_resolver(void *fdt, void *fdta, const char *target,
+ const struct fdt_addon_resolver_ops *resolver_ops,
+ void *resolver_priv)
{
+ struct fdt_addon_resolver *resolver = NULL;
+ struct fdt_addon_resolver custom_resolver;
int target_node;
uint32_t delta;
int ret;
@@ -1697,11 +1742,25 @@ int fdt_addon_apply(void *fdt, void *fdta, const char *target)
FDT_RO_PROBE(fdt);
FDT_RO_PROBE(fdta);
+ if (resolver_ops) {
+ /* Initialize the custom resolver */
+ custom_resolver.ops = resolver_ops;
+ custom_resolver.priv = resolver_priv;
+ /* This custom resolver is the resolver used */
+ resolver = &custom_resolver;
+ }
+
/* Get the target node */
target_node = fdt_path_offset(fdt, target);
if (target_node < 0)
return target_node;
+ if (resolver && resolver->ops->validate) {
+ ret = resolver->ops->validate(resolver->priv, fdt, target_node);
+ if (ret < 0)
+ return ret;
+ }
+
ret = fdt_find_max_phandle(fdt, &delta);
if (ret)
return ret;
@@ -1712,16 +1771,16 @@ int fdt_addon_apply(void *fdt, void *fdta, const char *target)
goto err;
/* Resolve phandles */
- ret = addon_resolve_phandles(fdta, fdt, target_node);
+ ret = addon_resolve_phandles(fdta, fdt, target_node, resolver);
if (ret < 0)
goto err;
/* Don't overwrite phandles in fdt */
- ret = addon_prevent_phandles_overwrite(fdta, fdt, target_node);
+ ret = addon_prevent_phandles_overwrite(fdta, fdt, target_node, resolver);
if (ret < 0)
goto err;
- ret = addon_merge(fdta, fdt, target);
+ ret = addon_merge(fdta, fdt, target, resolver);
if (ret < 0)
goto err;
@@ -1739,3 +1798,8 @@ err:
return ret;
}
+
+int fdt_addon_apply(void *fdt, void *fdta, const char *target)
+{
+ return fdt_addon_apply_resolver(fdt, fdta, target, NULL, NULL);
+}
diff --git a/libfdt/libfdt.h b/libfdt/libfdt.h
index 3ef29beb..5df115b7 100644
--- a/libfdt/libfdt.h
+++ b/libfdt/libfdt.h
@@ -2545,7 +2545,8 @@ int fdt_overlay_target_offset(const void *fdt, const void *fdto,
* @target: target node path
*
* fdt_addon_apply() will apply the given device tree addon on the given target
- * node in base device tree.
+ * node in base device tree. fdt_addon_apply() uses the default resolver to
+ * resolve symbols.
*
* Expect the base device tree to be modified, even if the function
* returns an error.
@@ -2570,6 +2571,101 @@ int fdt_overlay_target_offset(const void *fdt, const void *fdto,
*/
int fdt_addon_apply(void *fdt, void *fdta, const char *target);
+#ifndef SWIG /* Not available in Python */
+struct fdt_addon_resolver_ops {
+ /**
+ * Validate function (optional)
+ * @priv: Private data
+ * @fdt: fdt blob to look for validation
+ * @target_node: Target node offset the addon is applied to
+ *
+ * If non NULL, this function is called soon when an addon is applied
+ * and allows the resolver to perform some internal checks. If NULL,
+ * no resolver validation takes place and the applying process continues
+ * its operations.
+ *
+ * Return 0 if the validation of the resolver succeed. Negative error
+ * code on failure. On failure, the addon applying process is aborted.
+ * and the negative error code returned by this function is returned from
+ * the function applying the addon.
+ */
+ int (*validate)(void *priv, const void *fdt, int target_node);
+
+ /**
+ * Resolve function (mandatory)
+ * @priv: Private data
+ * @fdt: fdt blob to look for importsym matching node
+ * @target_node: Target node offset the addon is applied to
+ * @importsym_name: Import symbol name
+ * @importsym_compatible: Import symbol compatible string
+ *
+ * Return the node offset in fdt blob of the node matching the import
+ * symbol (identified by its name and compatible string) after the
+ * resolution on success. Negative error code on failure.
+ */
+ int (*resolve)(void *priv, const void *fdt, int target_node,
+ const char *importsym_name,
+ const char *importsym_compatible);
+};
+
+/**
+ * fdt_resolve_default - Default libfdt resolver.
+ * @fdt: base device tree blob
+ * @target_node: Target node offset in the base device tree blob containing
+ * the exported symbols used for the resolution.
+ * @importsym_name: Import symbol name
+ * @importsym_compatible: Import symbol compatible string
+ *
+ * The default libfdt resolver search for a matching node in export symbols
+ * available at the @target_node. The @importsym_compatible string is not used.
+ *
+ * returns:
+ * offset of the node matching the symbol after the resolution on success.
+ * Negative error code on failure.
+ */
+int fdt_addon_resolve_default(const void *fdt, int target_node,
+ const char *importsym_name,
+ const char *importsym_compatible);
+
+
+/**
+ * fdt_addon_apply_resolver - Applies a DT addon on a base DT using the provided
+ * revolver.
+ * @fdt: pointer to the base device tree blob
+ * @fdta: pointer to the device tree addon blob
+ * @target: target node path
+ * @resolver_ops: Resolver operations to use, NULL to use the default resolver.
+ * @resolver_priv: Private data passed to function provided by resolver_ops
+ *
+ * fdt_addon_apply() will apply the given device tree addon on the given target
+ * node in base device tree.
+ *
+ * Expect the base device tree to be modified, even if the function
+ * returns an error.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, there's not enough space in the base device tree
+ * -FDT_ERR_NOTFOUND, the overlay points to some nonexistent nodes or
+ * properties in the base DT
+ * -FDT_ERR_BADPHANDLE,
+ * -FDT_ERR_BADOVERLAY,
+ * -FDT_ERR_NOPHANDLES,
+ * -FDT_ERR_INTERNAL,
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADOFFSET,
+ * -FDT_ERR_BADPATH,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_addon_apply_resolver(void *fdt, void *fdta, const char *target,
+ const struct fdt_addon_resolver_ops *resolver_ops,
+ void *resolver_priv);
+#endif /* SWIG */
+
/**********************************************************************/
/* Debugging / informational functions */
/**********************************************************************/
diff --git a/libfdt/version.lds b/libfdt/version.lds
index 74925ac0..0fa3490c 100644
--- a/libfdt/version.lds
+++ b/libfdt/version.lds
@@ -84,6 +84,8 @@ LIBFDT_1.2 {
fdt_get_symbol;
fdt_get_symbol_namelen;
fdt_addon_apply;
+ fdt_addon_apply_resolver;
+ fdt_addon_resolve_default;
local:
*;
};
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
* [RFC PATCH v2 74/74] fdtaddon: Add a simple external resolver
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
` (72 preceding siblings ...)
2026-08-26 9:49 ` [RFC PATCH v2 73/74] libfdt/fdt_addon.c: Add support for an external resolver Herve Codina
@ 2026-08-26 9:49 ` Herve Codina
73 siblings, 0 replies; 75+ messages in thread
From: Herve Codina @ 2026-08-26 9:49 UTC (permalink / raw)
To: David Gibson, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Laurent Pinchart, David Lechner, Ayush Singh, Geert Uytterhoeven,
devicetree-compiler, devicetree, linux-kernel, devicetree-spec,
Hui Pu, Ian Ray, Luca Ceresoli, Thomas Petazzoni, Herve Codina
libfdt allows to use an external resolver to resolve import symbols when
an addon is applied.
Add a simple external resolver in fdtaddon based on export symbols given
on the tool command line (-e or --export). Those export symbols are used
to mach import symbols (match by names) in order to resolved them.
Two syntaxes are available for the -e or --export option:
- Export by symbol (--export '<name>=&<symbol>')
The export symbol <name> is created an reference the node identified
by <symbol>. This <symbol> has to be an exported symbol available at
the node the addon is applied to. For instance, assuming this base
device-tree fragment in the base device-tree:
target {
/export/ bar: &{/somewhere/bar-node};
}
The '--target "/target" --export "foo=&bar"' will export foo as an
exported symbol pointing to /somewhere/bar-node. An addon can import
the foo symbol an it will be resolved to /somewhere/bar-node.
- Export by path (--export '<name>=&{<path>}')
The export symbol <name> is created and reference the node available
at the given <path>. This path must be an absolute path.
The '--target "/target" --export "foo=&{/somewhere/baz-node}"'
export the symbol 'foo' referencing the node at /somewhere/baz-node.
Here again, an addon importing the foo symbol will see this foo symbol
resolved to /somewhere/baz-node.
It is worth noting that /somewhere/baz-node may not be referenced in
the base device-tree. Indeed, /export/ could not be present to
reference this node. If the node is not referenced, dtc will not
create its phandle value and the resolution will failed. For this
reason having a /export/ keyword at the target node pointing to the
/somewhere/baz-node node is still recommended. This will
ensure that dtc will create the needed phandle.
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
fdtaddon.c | 232 +++++++++++++++++-
...n_custom_resolver_node_a-merged.dtb.expect | 39 +++
...n_addon_custom_resolver_node_a.dtba.expect | 15 ++
...fdtaddon_addon_custom_resolver_node_a.dtsa | 22 ++
..._custom_resolver_node_ab-merged.dtb.expect | 42 ++++
..._addon_custom_resolver_node_ab.dtba.expect | 21 ++
...dtaddon_addon_custom_resolver_node_ab.dtsa | 29 +++
.../fdtaddon_base_custom_resolver.dtb.expect | 33 +++
tests/fdtaddon_base_custom_resolver.dts | 52 ++++
tests/run_tests.sh | 25 ++
10 files changed, 503 insertions(+), 7 deletions(-)
create mode 100644 tests/fdtaddon_addon_custom_resolver_node_a-merged.dtb.expect
create mode 100644 tests/fdtaddon_addon_custom_resolver_node_a.dtba.expect
create mode 100644 tests/fdtaddon_addon_custom_resolver_node_a.dtsa
create mode 100644 tests/fdtaddon_addon_custom_resolver_node_ab-merged.dtb.expect
create mode 100644 tests/fdtaddon_addon_custom_resolver_node_ab.dtba.expect
create mode 100644 tests/fdtaddon_addon_custom_resolver_node_ab.dtsa
create mode 100644 tests/fdtaddon_base_custom_resolver.dtb.expect
create mode 100644 tests/fdtaddon_base_custom_resolver.dts
diff --git a/fdtaddon.c b/fdtaddon.c
index 037851d9..80073860 100644
--- a/fdtaddon.c
+++ b/fdtaddon.c
@@ -22,20 +22,215 @@
static const char usage_synopsis[] =
"apply an addon to a base blob\n"
" fdtaddon <options> <addon.dtba>";
-static const char usage_short_opts[] = "i:o:t:v" USAGE_COMMON_SHORT_OPTS;
+static const char usage_short_opts[] = "i:o:t:e:v" USAGE_COMMON_SHORT_OPTS;
static const struct option usage_long_opts[] = {
{ "input", required_argument, NULL, 'i' },
{ "output", required_argument, NULL, 'o' },
{ "target", required_argument, NULL, 't' },
+ { "export", required_argument, NULL, 'e' },
{ "verbose", no_argument, NULL, 'v' },
USAGE_COMMON_LONG_OPTS,
};
static const char *const usage_opts_help[] = { "Input base DT blob",
- "Output DT blob", "Target node",
+ "Output DT blob",
+ "Target node",
+ "Custom export symbol"
+ "\n\tMultiple --export or -e can be present in order to export multiple symbols."
+ "\n\tThe following syntaxes are supported:"
+ "\n\t - Defines a export symbol pointing to a node using its symbol"
+ "\n\t --export '<export_name>=&<symbol>'"
+ "\n\t - Defines a export symbol pointing to a node by path"
+ "\n\t --export '<export_name>=&{path_to_node}'",
"Verbose messages",
USAGE_COMMON_OPTS_HELP };
-static void *do_apply(void *base, const void *addon, const char *target)
+struct export_symbol {
+ char *name;
+ char *ref;
+ struct export_symbol *next;
+};
+
+struct resolver {
+ struct export_symbol *export_list;
+ int verbose;
+};
+
+static void resolver_init(struct resolver *resolver)
+{
+ memset(resolver, 0, sizeof(*resolver));
+}
+
+static void resolver_exit(struct resolver *resolver)
+{
+ struct export_symbol *e, *next;
+
+ e = resolver->export_list;
+ while (e) {
+ free(e->name);
+ free(e->ref);
+ next = e->next;
+ free(e);
+ e = next;
+ }
+}
+
+static char *str_strip_spaces(char *str)
+{
+ char *strip, *tmp;
+
+ tmp = str;
+ strip = str;
+ while (*tmp != '\0') {
+ if (*tmp != ' ' && *tmp != '\t')
+ *(strip++) = *tmp;
+ tmp++;
+ }
+ *strip = *tmp;
+ return str;
+}
+
+static char *str_split(char *str, char token, char **remaining)
+{
+ char *tmp;
+
+ tmp = strchr(str, token);
+ if (!tmp)
+ return NULL;
+ *(tmp++) = '\0'; /* Split the string at token char */
+
+ *remaining = tmp;
+ return str;
+}
+
+static int resolver_parse_export(struct resolver *resolver, const char *arg)
+{
+ struct export_symbol *export_symbol;
+ const char *name, *ref;
+ char *tmp, *strip;
+
+ strip = xstrdup(arg);
+ strip = str_strip_spaces(strip);
+
+ tmp = strip;
+
+ name = str_split(tmp, '=', &tmp);
+ if (!name || !strlen(name))
+ goto fail;
+
+ if (*(tmp++) != '&')
+ goto fail;
+ if (*tmp == '\0')
+ goto fail;
+
+ if (*tmp == '{') {
+ /* ref by path : &{/xxxx/yyyy} */
+ tmp++;
+ if (*(tmp) != '/')
+ goto fail;
+
+ ref = str_split(tmp, '}', &tmp);
+ if (!ref || !strlen(ref))
+ goto fail;
+ if (*tmp != '\0')
+ goto fail;
+ } else {
+ /* ref by symbol : &xxxx */
+ ref = tmp;
+ if (!strlen(ref))
+ goto fail;
+ }
+
+ export_symbol = xmalloc(sizeof(*export_symbol));
+ export_symbol->name = xstrdup(name);
+ export_symbol->ref = xstrdup(ref);
+ free(strip);
+
+ export_symbol->next = resolver->export_list;
+ resolver->export_list = export_symbol;
+ return 0;
+fail:
+ free(strip);
+ return -1;
+}
+
+static int resolver_resolve(void *priv, const void *fdt, int target_node,
+ const char *importsym_name,
+ const char *importsym_compatible)
+{
+ struct resolver *resolver = priv;
+ int node = -FDT_ERR_NOTFOUND;
+ struct export_symbol *e;
+
+ e = resolver->export_list;
+ while (e) {
+ if (strcmp(importsym_name, e->name)) {
+ e = e->next;
+ continue;
+ }
+
+ if (e->ref[0] == '/')
+ node = fdt_path_offset(fdt, e->ref);
+ else
+ node = fdt_addon_resolve_default(fdt, target_node, e->ref,
+ NULL);
+
+ return node;
+ }
+ return -FDT_ERR_NOTFOUND;
+}
+
+static int resolver_validate(void *priv, const void *fdt, int target_node)
+{
+ struct resolver *resolver = priv;
+ struct export_symbol *e;
+ uint32_t phandle;
+ int node;
+
+ /*
+ * Check the export_symbol list. The related node must exist in the fdt
+ * blob and must have a valid phandle value.
+ *
+ * Simply revolve all export symbols available in the list to validate
+ * that they can be resolved.
+ */
+
+ e = resolver->export_list;
+ while (e) {
+ node = resolver_resolve(resolver, fdt, target_node, e->name,
+ NULL);
+ if (node < 0) {
+ fprintf(stderr,
+ "\nExport symbol '%s': Cannot find reference '%s'\n",
+ e->name, e->ref);
+ return node;
+ }
+
+ phandle = fdt_get_phandle(fdt, node);
+ if ((phandle == 0) || (phandle == ~0U)) {
+ fprintf(stderr,
+ "\nExport symbol '%s', reference '%s': Invalid phandle\n",
+ e->name, e->ref);
+ return -FDT_ERR_BADPHANDLE;
+ }
+
+ if (resolver->verbose) {
+ printf("Export symbol '%s', ref '%s' -> node at 0x%x, phandle 0x%"PRIx32"\n",
+ e->name, e->ref, node, phandle);
+ }
+
+ e = e->next;
+ }
+ return 0;
+}
+
+static const struct fdt_addon_resolver_ops resolver_ops = {
+ .validate = resolver_validate,
+ .resolve = resolver_resolve,
+};
+
+
+static void *do_apply(void *base, const void *addon, const char *target,
+ struct resolver *resolver)
{
void *tmp_merged;
void *tmp_addon;
@@ -72,7 +267,12 @@ static void *do_apply(void *base, const void *addon, const char *target)
goto fail;
}
- ret = fdt_addon_apply(tmp_merged, tmp_addon, target);
+ if (resolver)
+ ret = fdt_addon_apply_resolver(tmp_merged, tmp_addon, target,
+ &resolver_ops, resolver);
+ else
+ ret = fdt_addon_apply(tmp_merged, tmp_addon, target);
+
if (ret) {
fprintf(stderr, "\nFailed to apply %s\n", fdt_strerror(ret));
goto fail;
@@ -88,7 +288,8 @@ fail:
}
static int do_fdtaddon(const char *input_filename, const char *output_filename,
- const char *addon_filename, const char *target)
+ const char *addon_filename, const char *target,
+ struct resolver *resolver)
{
void *base_blob = NULL;
void *addon_blob = NULL;
@@ -122,7 +323,7 @@ static int do_fdtaddon(const char *input_filename, const char *output_filename,
}
/* apply the addon */
- merged_blob = do_apply(base_blob, addon_blob, target);
+ merged_blob = do_apply(base_blob, addon_blob, target, resolver);
if (!merged_blob)
goto out_err;
@@ -146,7 +347,12 @@ int main(int argc, char *argv[])
char *addon_filename = NULL;
const char *target = NULL;
int verbose;
+ bool use_resolver = false;
+ struct resolver resolver;
int opt;
+ int ret;
+
+ resolver_init(&resolver);
while ((opt = util_getopt_long()) != EOF) {
switch (opt) {
@@ -164,6 +370,13 @@ int main(int argc, char *argv[])
case 't':
target = optarg;
break;
+ case 'e':
+ if (resolver_parse_export(&resolver, optarg) < 0) {
+ resolver_exit(&resolver);
+ return 1;
+ }
+ use_resolver = true;
+ break;
}
}
@@ -190,7 +403,12 @@ int main(int argc, char *argv[])
printf("addon = %s\n", addon_filename);
}
- if (do_fdtaddon(input_filename, output_filename, addon_filename, target))
+ resolver.verbose = verbose;
+
+ ret = do_fdtaddon(input_filename, output_filename, addon_filename, target,
+ use_resolver ? &resolver : NULL);
+ resolver_exit(&resolver);
+ if (ret)
return 1;
return 0;
diff --git a/tests/fdtaddon_addon_custom_resolver_node_a-merged.dtb.expect b/tests/fdtaddon_addon_custom_resolver_node_a-merged.dtb.expect
new file mode 100644
index 00000000..637387a7
--- /dev/null
+++ b/tests/fdtaddon_addon_custom_resolver_node_a-merged.dtb.expect
@@ -0,0 +1,39 @@
+/dts-v1/;
+
+/ {
+ base-node {
+ sub-node {
+ prop = <0x00000000>;
+ phandle = <0x00000003>;
+ addon-node {
+ prop = "other";
+ };
+ };
+ };
+ somewhere {
+ // [FDT_EXPORT_SYM] 'node_a1' -> phandle 0x00000001
+ // [FDT_EXPORT_SYM] 'node_a2' -> phandle 0x00000002
+ // [FDT_EXPORT_SYM] 'sub_node' -> phandle 0x00000003
+ // [FDT_EXPORT_SYM] 'node_b1' -> phandle 0x00000004
+ // [FDT_EXPORT_SYM] 'node_b2' -> phandle 0x00000005
+ node-a1 {
+ compatible = "abc,aaa";
+ phandle = <0x00000001>;
+ addon-node {
+ prop = "node_a";
+ };
+ };
+ node-a2 {
+ compatible = "abc,aaa";
+ phandle = <0x00000002>;
+ };
+ node-b1 {
+ compatible = "abc,bbb";
+ phandle = <0x00000004>;
+ };
+ node-b2 {
+ compatible = "abc,bbb";
+ phandle = <0x00000005>;
+ };
+ };
+};
diff --git a/tests/fdtaddon_addon_custom_resolver_node_a.dtba.expect b/tests/fdtaddon_addon_custom_resolver_node_a.dtba.expect
new file mode 100644
index 00000000..60104190
--- /dev/null
+++ b/tests/fdtaddon_addon_custom_resolver_node_a.dtba.expect
@@ -0,0 +1,15 @@
+/dts-v1/;
+/addon/;
+
+// [FDT_IMPORT_SYM] 'node_a' (abc,aaa)
+// [FDT_IMPORT_SYM] 'other' ()
+&node_a {
+ addon-node {
+ prop = "node_a";
+ };
+};
+&other {
+ addon-node {
+ prop = "other";
+ };
+};
diff --git a/tests/fdtaddon_addon_custom_resolver_node_a.dtsa b/tests/fdtaddon_addon_custom_resolver_node_a.dtsa
new file mode 100644
index 00000000..d400c4f2
--- /dev/null
+++ b/tests/fdtaddon_addon_custom_resolver_node_a.dtsa
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
+/*
+ * Copyright (C) 2026 Bootlin
+ */
+
+/dts-v1/;
+/addon/;
+
+/import/ node_a: "abc,aaa";
+/import/ other: "";
+
+&node_a {
+ addon-node {
+ prop = "node_a";
+ };
+};
+
+&other {
+ addon-node {
+ prop = "other";
+ };
+};
diff --git a/tests/fdtaddon_addon_custom_resolver_node_ab-merged.dtb.expect b/tests/fdtaddon_addon_custom_resolver_node_ab-merged.dtb.expect
new file mode 100644
index 00000000..40678266
--- /dev/null
+++ b/tests/fdtaddon_addon_custom_resolver_node_ab-merged.dtb.expect
@@ -0,0 +1,42 @@
+/dts-v1/;
+
+/ {
+ base-node {
+ sub-node {
+ prop = <0x00000000>;
+ phandle = <0x00000003>;
+ addon-node {
+ prop = "other";
+ };
+ };
+ };
+ somewhere {
+ // [FDT_EXPORT_SYM] 'node_a1' -> phandle 0x00000001
+ // [FDT_EXPORT_SYM] 'node_a2' -> phandle 0x00000002
+ // [FDT_EXPORT_SYM] 'sub_node' -> phandle 0x00000003
+ // [FDT_EXPORT_SYM] 'node_b1' -> phandle 0x00000004
+ // [FDT_EXPORT_SYM] 'node_b2' -> phandle 0x00000005
+ node-a1 {
+ compatible = "abc,aaa";
+ phandle = <0x00000001>;
+ };
+ node-a2 {
+ compatible = "abc,aaa";
+ phandle = <0x00000002>;
+ addon-node {
+ prop = "node_a";
+ };
+ };
+ node-b1 {
+ compatible = "abc,bbb";
+ phandle = <0x00000004>;
+ addon-node {
+ prop = "node_b";
+ };
+ };
+ node-b2 {
+ compatible = "abc,bbb";
+ phandle = <0x00000005>;
+ };
+ };
+};
diff --git a/tests/fdtaddon_addon_custom_resolver_node_ab.dtba.expect b/tests/fdtaddon_addon_custom_resolver_node_ab.dtba.expect
new file mode 100644
index 00000000..04116f47
--- /dev/null
+++ b/tests/fdtaddon_addon_custom_resolver_node_ab.dtba.expect
@@ -0,0 +1,21 @@
+/dts-v1/;
+/addon/;
+
+// [FDT_IMPORT_SYM] 'node_a' (abc,aaa)
+// [FDT_IMPORT_SYM] 'node_b' (abc,bbb)
+// [FDT_IMPORT_SYM] 'other' ()
+&node_a {
+ addon-node {
+ prop = "node_a";
+ };
+};
+&node_b {
+ addon-node {
+ prop = "node_b";
+ };
+};
+&other {
+ addon-node {
+ prop = "other";
+ };
+};
diff --git a/tests/fdtaddon_addon_custom_resolver_node_ab.dtsa b/tests/fdtaddon_addon_custom_resolver_node_ab.dtsa
new file mode 100644
index 00000000..74a9cab3
--- /dev/null
+++ b/tests/fdtaddon_addon_custom_resolver_node_ab.dtsa
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
+/*
+ * Copyright (C) 2026 Bootlin
+ */
+
+/dts-v1/;
+/addon/;
+
+/import/ node_a: "abc,aaa";
+/import/ node_b: "abc,bbb";
+/import/ other: "";
+
+&node_a {
+ addon-node {
+ prop = "node_a";
+ };
+};
+
+&node_b {
+ addon-node {
+ prop = "node_b";
+ };
+};
+
+&other {
+ addon-node {
+ prop = "other";
+ };
+};
diff --git a/tests/fdtaddon_base_custom_resolver.dtb.expect b/tests/fdtaddon_base_custom_resolver.dtb.expect
new file mode 100644
index 00000000..4a76672f
--- /dev/null
+++ b/tests/fdtaddon_base_custom_resolver.dtb.expect
@@ -0,0 +1,33 @@
+/dts-v1/;
+
+/ {
+ base-node {
+ sub-node {
+ prop = <0x00000000>;
+ phandle = <0x00000003>;
+ };
+ };
+ somewhere {
+ // [FDT_EXPORT_SYM] 'node_a1' -> phandle 0x00000001
+ // [FDT_EXPORT_SYM] 'node_a2' -> phandle 0x00000002
+ // [FDT_EXPORT_SYM] 'sub_node' -> phandle 0x00000003
+ // [FDT_EXPORT_SYM] 'node_b1' -> phandle 0x00000004
+ // [FDT_EXPORT_SYM] 'node_b2' -> phandle 0x00000005
+ node-a1 {
+ compatible = "abc,aaa";
+ phandle = <0x00000001>;
+ };
+ node-a2 {
+ compatible = "abc,aaa";
+ phandle = <0x00000002>;
+ };
+ node-b1 {
+ compatible = "abc,bbb";
+ phandle = <0x00000004>;
+ };
+ node-b2 {
+ compatible = "abc,bbb";
+ phandle = <0x00000005>;
+ };
+ };
+};
diff --git a/tests/fdtaddon_base_custom_resolver.dts b/tests/fdtaddon_base_custom_resolver.dts
new file mode 100644
index 00000000..b92475b2
--- /dev/null
+++ b/tests/fdtaddon_base_custom_resolver.dts
@@ -0,0 +1,52 @@
+// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
+/*
+ * Copyright (C) 2026 Bootlin
+ */
+
+/dts-v1/;
+
+/ {
+ base-node {
+ sub_node: sub-node {
+ prop = <0>;
+ };
+ };
+
+ somewhere {
+ /*
+ * Nodes are referenced, either by path or by symbol, by the
+ * custom resolver available in the fdt_addon tool.
+ *
+ * Nodes referenced by symbols need to have the symbol exported
+ * here (i.e. in the node where the addon is applied to).
+ *
+ * For nodes referenced by path by the custom resolver, this is
+ * not strictly necessary but we need to ensure that DTC will
+ * create the phandle property for those nodes.
+ *
+ * DTC creates phandles when a node is referenced in the dts
+ * compiled by DTC. Using the /export/ keyword is a good way
+ * to ensure this reference and so to have the phandle created.
+ * Even if the exported symbol is not used (path instead of
+ * symbol), this ensures the phandle creation by DTC.
+ */
+ /export/ node_a1: &node_a1;
+ /export/ node_a2: &node_a2;
+ /export/ sub_node: &sub_node;
+ /export/ node_b1: &node_b1;
+ /export/ node_b2: &node_b2;
+
+ node_a1: node-a1 {
+ compatible = "abc,aaa";
+ };
+ node_a2: node-a2 {
+ compatible = "abc,aaa";
+ };
+ node_b1: node-b1 {
+ compatible = "abc,bbb";
+ };
+ node_b2: node-b2 {
+ compatible = "abc,bbb";
+ };
+ };
+};
diff --git a/tests/run_tests.sh b/tests/run_tests.sh
index 664ebf5d..6f4f8386 100755
--- a/tests/run_tests.sh
+++ b/tests/run_tests.sh
@@ -1369,6 +1369,31 @@ fdtaddon_tests() {
run_dtc_test -I dtb -O dts -o fdtaddon_stack_2nd-merged.dtb.dts fdtaddon_stack_2nd-merged.dtb
check_dts fdtaddon_stack_2nd-merged.dtb.dts
+ # Custom resolver
+ run_dtc_test -I dts -O dtb -o fdtaddon_base_custom_resolver.dtb "$SRCDIR/fdtaddon_base_custom_resolver.dts"
+ check_dtb fdtaddon_base_custom_resolver.dtb
+
+ run_dtc_test -I dts -O dtb -o fdtaddon_addon_custom_resolver_node_a.dtba "$SRCDIR/fdtaddon_addon_custom_resolver_node_a.dtsa"
+ check_dtb fdtaddon_addon_custom_resolver_node_a.dtba
+
+ run_fdtaddon_test -i fdtaddon_base_custom_resolver.dtb -o fdtaddon_addon_custom_resolver_node_a-merged.dtb \
+ -t "/somewhere" \
+ -e "node_a=&node_a1" \
+ -e "other=&{/base-node/sub-node}" \
+ fdtaddon_addon_custom_resolver_node_a.dtba
+ check_dtb fdtaddon_addon_custom_resolver_node_a-merged.dtb
+
+ run_dtc_test -I dts -O dtb -o fdtaddon_addon_custom_resolver_node_ab.dtba "$SRCDIR/fdtaddon_addon_custom_resolver_node_ab.dtsa"
+ check_dtb fdtaddon_addon_custom_resolver_node_ab.dtba
+
+ run_fdtaddon_test -i fdtaddon_base_custom_resolver.dtb -o fdtaddon_addon_custom_resolver_node_ab-merged.dtb \
+ -t "/somewhere" \
+ -e "node_a=&{/somewhere/node-a2}" \
+ -e "node_b=&node_b1" \
+ -e "other=&sub_node" \
+ fdtaddon_addon_custom_resolver_node_ab.dtba
+ check_dtb fdtaddon_addon_custom_resolver_node_ab-merged.dtb
+
# More realistic dts and dtsa input files
run_dtc_test -I dts -O dtb -o fdtaddon_realistic_base.dtb "$SRCDIR/fdtaddon_realistic_base.dts"
check_dtb fdtaddon_realistic_base.dtb
--
2.55.0
^ permalink raw reply related [flat|nested] 75+ messages in thread
end of thread, other threads:[~2026-08-26 9:54 UTC | newest]
Thread overview: 75+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-26 9:48 [RFC PATCH v2 00/74] Add support for dtb metadata and addon device-trees Herve Codina
2026-08-26 9:48 ` [RFC PATCH v2 01/74] dtc-parser.y: Avoid an empty proplist Herve Codina
2026-08-26 9:48 ` [RFC PATCH v2 02/74] Introduce v20 dtb version Herve Codina
2026-08-26 9:48 ` [RFC PATCH v2 03/74] fdtdump: Introduce get_structured_tag_data() Herve Codina
2026-08-26 9:48 ` [RFC PATCH v2 04/74] libfdt: Introduce fdt_get_structured_tag_data() Herve Codina
2026-08-26 9:48 ` [RFC PATCH v2 05/74] libfdt: Prepare for metadata tag support Herve Codina
2026-08-26 9:48 ` [RFC PATCH v2 06/74] dtc: Move fill_fullpaths() into build_dt_info() Herve Codina
2026-08-26 9:48 ` [RFC PATCH v2 07/74] Add support for FDT_PROPDATA_PHANDLE dtb tag Herve Codina
2026-08-26 9:48 ` [RFC PATCH v2 08/74] livetree: Improve get_node_by_phandle() Herve Codina
2026-08-26 9:48 ` [RFC PATCH v2 09/74] dtc: Complete REF_PHANDLE markers with ref strings and is_local flags Herve Codina
2026-08-26 9:48 ` [RFC PATCH v2 10/74] tests: Add basic metadata tests Herve Codina
2026-08-26 9:48 ` [RFC PATCH v2 11/74] Add support for FDT_PROPDATA_PHANDLE_REF dtb tag Herve Codina
2026-08-26 9:48 ` [RFC PATCH v2 12/74] tests: metadata: Add external phandle reference tests Herve Codina
2026-08-26 9:48 ` [RFC PATCH v2 13/74] Add support for /addon/ keyword Herve Codina
2026-08-26 9:48 ` [RFC PATCH v2 14/74] tests: Add a test related to addon dt_flags header value Herve Codina
2026-08-26 9:48 ` [RFC PATCH v2 15/74] tests: metadata: Add a basic addon test Herve Codina
2026-08-26 9:48 ` [RFC PATCH v2 16/74] dtc: Introduce export symbols Herve Codina
2026-08-26 9:48 ` [RFC PATCH v2 17/74] dtc: Add support for /export/ dts keyword parsing Herve Codina
2026-08-26 9:48 ` [RFC PATCH v2 18/74] checks: Handle export symbols in fixup_phandle_references() Herve Codina
2026-08-26 9:48 ` [RFC PATCH v2 19/74] dtc: Add export symbols (/export/ keyword) in generated dts file Herve Codina
2026-08-26 9:48 ` [RFC PATCH v2 20/74] dtc: Extend complete_references() to handle export symbols Herve Codina
2026-08-26 9:48 ` [RFC PATCH v2 21/74] Add support for FDT_EXPORT_SYM dtb tag Herve Codina
2026-08-26 9:48 ` [RFC PATCH v2 22/74] tests: metadata: Add export symbols with local references tests Herve Codina
2026-08-26 9:48 ` [RFC PATCH v2 23/74] dtc: Add support for export symbols sorting Herve Codina
2026-08-26 9:48 ` [RFC PATCH v2 24/74] tests: metadata: Add a test " Herve Codina
2026-08-26 9:48 ` [RFC PATCH v2 25/74] Add support for FDT_EXPORT_SYM_REF dtb tag Herve Codina
2026-08-26 9:48 ` [RFC PATCH v2 26/74] tests: metadata: Add export symbols with external references tests Herve Codina
2026-08-26 9:48 ` [RFC PATCH v2 27/74] dtc: Introduce import symbols Herve Codina
2026-08-26 9:48 ` [RFC PATCH v2 28/74] dtc-parser: Introduce last_header_flags Herve Codina
2026-08-26 9:48 ` [RFC PATCH v2 29/74] dtc: Add support for /import/ dts keyword parsing Herve Codina
2026-08-26 9:48 ` [RFC PATCH v2 30/74] dtc: Add import symbols (/import/ keyword) in generated dts file Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 31/74] Add support for FDT_IMPORT_SYM dtb tag Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 32/74] tests: metadata: Add import symbols tests Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 33/74] dtc: Add support for import symbols sorting Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 34/74] tests: metadata: Improve sort test to check " Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 35/74] checks: Get 'chosen' node using get_subnode() Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 36/74] dtc: Change get_node_by_path() signature to take dt_info Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 37/74] dtc: Change get_node_by_label() " Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 38/74] dtc: Change get_node_by_phandle() " Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 39/74] dtc: Change get_node_by_ref() " Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 40/74] dtc: Change get_node_phandle() " Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 41/74] dtc: Change get_property_by_label() " Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 42/74] dtc: Change get_marker_label() " Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 43/74] dtc: Change fill_fullpaths() " Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 44/74] dtc: Introduce orphan nodes Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 45/74] dtc: Handle orphan nodes in get_xxx_by_yyy() Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 46/74] dtc: Avoid NULL fullpath for nodes in orphan trees Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 47/74] dtc: Handle orphan nodes in complete_references() Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 48/74] checks: Perform checks for orphan nodes Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 49/74] dtc: Rename add_orphan_node() to plugin_add_orphan_node() Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 50/74] dtc: Add basic support for addon orphan nodes in dts Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 51/74] Add support for FDT_BEGIN_NODE_REF dtb tag Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 52/74] tests: metadata: Add basic test for addon orphan nodes Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 53/74] dtc: Add support for missing root node in addon device-tree Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 54/74] tests: metadata: Add a test for addon without root node Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 55/74] dtc: Allow parser_get_node_by_ref() to return an orphan node for merging purpose Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 56/74] tests: metadata: Add a test related to orphan node merging Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 57/74] dtc: Add support for orphan nodes sorting Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 58/74] tests: metadata: Improve sort test to check " Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 59/74] dtc: Add support for references by path involving orphan nodes Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 60/74] tests: metadata: Add a test " Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 61/74] dtc: Add support for namespace labels references Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 62/74] tests: metadata: Add a test " Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 63/74] libfdt: Add support for applying an addon on a base device-tree blob Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 64/74] Add fdtaddon tool to apply an addon Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 65/74] tests: Add a first basic test for fdtaddon Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 66/74] tests: fdtaddon: Add a basic test for addons using an orphan nodes Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 67/74] tests: fdtaddon: Add a basic test for addons with unresolved phandle references Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 68/74] tests: fdtaddon: Add a basic test for addons with references by path to orphan nodes Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 69/74] tests: fdtaddon: Add a test for addons with properties identical to existing ones Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 70/74] tests: fdtaddon: Add a test for addons using namespace label references Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 71/74] tests: fdtaddon: Add a test for using 'stacked' addons Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 72/74] tests: fdtaddon: Add a test using more realistic dts and dtsa Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 73/74] libfdt/fdt_addon.c: Add support for an external resolver Herve Codina
2026-08-26 9:49 ` [RFC PATCH v2 74/74] fdtaddon: Add a simple " Herve Codina
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox