All of lore.kernel.org
 help / color / mirror / Atom feed
From: David Gibson <david@gibson.dropbear.id.au>
To: Douglas Anderson <dianders@chromium.org>
Cc: Devicetree-compiler@vger.kernel.org,
	"Rob Herring" <robh@kernel.org>,
	"Krzysztof Kozlowski" <krzk+dt@kernel.org>,
	"Conor Dooley" <conor+dt@kernel.org>,
	"Konrad Dybcio" <konradybcio@kernel.org>,
	"Brian Norris" <briannorris@chromium.org>,
	"Chen-Yu Tsai" <wenst@chromium.org>,
	"André Draszik" <andre.draszik@linaro.org>,
	Devicetree@vger.kernel.org, "Simon Glass" <sjg@chromium.org>,
	"Tudor Ambarus" <tudor.ambarus@linaro.org>,
	linux-kernel@vger.kernel.org,
	"Julius Werner" <jwerner@chromium.org>,
	"Saravana Kannan" <saravanak@kernel.org>,
	"Peter Griffin" <peter.griffin@linaro.org>
Subject: Re: [PATCH v2 06/10] libfdt: overlay: Update base /__local_fixups__ on apply
Date: Fri, 30 Oct 2026 22:26:51 +1100	[thread overview]
Message-ID: <auR-OXOpim7dVTpH@gractus.seuss> (raw)
In-Reply-To: <20261028215600.1713475-7-dianders@chromium.org>

On Wed, Oct 28, 2026 at 02:46:55PM -0700, Douglas Anderson wrote:
> When a base device tree is compiled with `dtc -L` (`--local-fixups`), it
> contains a root `/__local_fixups__` node recording the byte offsets of
> all phandle references in its properties.
> 
> Previously, `fdt_overlay_apply()` updated the base tree's `/__symbols__`
> via `overlay_symbol_update()`, but left the base tree's
> `/__local_fixups__` untouched. As a result, applying an overlay to a
> base tree with `/__local_fixups__` left `/__local_fixups__` missing the
> phandle references introduced by the overlay (both from the overlay's
> `/__local_fixups__` and from its resolved `/__fixups__`), and left stale
> offsets if the overlay overwrote a base property that previously had
> phandle references.
> 
> Add `overlay_local_fixups_update()` to update the base tree's
> `/__local_fixups__` whenever `/__local_fixups__` is present in the base
> tree:
> 
> - Remove pre-existing `/__local_fixups__` properties for any base
>   properties overwritten by overlay fragments.
> - Merge the overlay's `/__local_fixups__` and `/__fixups__` offsets into
>   the base tree's `/__local_fixups__` in sorted ascending order.
> - Prune any subnodes under `/__local_fixups__` that become empty after
>   overwriting properties.
> 
> When the base tree does not have a `/__local_fixups__` node,
> `overlay_local_fixups_update()` is a no-op.

So, this turned out to be substantially more involved than I realised.
I wonder if it might be better to make --strip-locals the default, and
only add support for preserving the local fixups as a later extension.

> Assisted-by: Gemini:gemini-pro-prerelease
> Signed-off-by: Douglas Anderson <dianders@chromium.org>
> ---
> 
> Changes in v2:
> - New patch updating base /__local_fixups__ in libfdt
>   fdt_overlay_apply() instead of stripping /__local_fixups__ by default
>   (David Gibson)
> 
>  libfdt/fdt_overlay.c | 621 ++++++++++++++++++++++++++++++++++++++++++-
>  tests/run_tests.sh   |  19 ++
>  2 files changed, 636 insertions(+), 4 deletions(-)
> 
> diff --git a/libfdt/fdt_overlay.c b/libfdt/fdt_overlay.c
> index eb87b356145f..47acab15041d 100644
> --- a/libfdt/fdt_overlay.c
> +++ b/libfdt/fdt_overlay.c
> @@ -804,7 +804,7 @@ static int overlay_prevent_phandle_overwrite(void *fdt, void *fdto)
>   *      Negative error code on failure
>   */
>  static int overlay_apply_node(void *fdt, int target,
> -			      void *fdto, int node)
> +			      const void *fdto, int node)

This change is unrelated to the rest of the patch.

>  {
>  	int property;
>  	int subnode;
> @@ -1003,9 +1003,8 @@ static int overlay_split_fragment_path(const char *path, int path_len,
>   * overlay_symbol_update() updates the symbols of the base tree with the
>   * symbols of the applied overlay
>   *
> - * This is the last step in the device tree overlay application
> - * process, allowing the reference of overlay symbols by subsequent
> - * overlay operations.
> + * This step in the device tree overlay application process allows
> + * the reference of overlay symbols by subsequent overlay operations.
>   *
>   * returns:
>   *      0 on success
> @@ -1122,6 +1121,616 @@ static int overlay_symbol_update(void *fdt, void *fdto)
>  	return 0;
>  }
>  
> +/**
> + * overlay_create_path - Create a relative path under a parent node
> + * @fdt: Base Device Tree blob
> + * @parent: Node offset of the starting parent node
> + * @path: Relative or slash-prefixed path to traverse
> + * @path_len: Length of @path in bytes
> + *
> + * overlay_create_path() walks the slash-separated path components in @path
> + * starting at @parent, creating any missing intermediate subnodes along the
> + * way.
> + *
> + * returns:
> + *      Node offset of the resolved or created subnode on success
> + *      Negative error code on failure
> + */
> +static int overlay_create_path(void *fdt, int parent, const char *path,
> +			       int path_len)
> +{
> +	const char *sep;
> +	int node = parent;
> +
> +	while (path_len > 0 && *path == '/') {
> +		path++;
> +		path_len--;
> +	}

In Python, I'm pretty happy to have the code to optionally allow extra
slashes to make the functions more widely useful.  In C - especially
since this is code that may be run in limited environments - I'd
prefer to pin down exactly what the expected format is and require
that.  So, I'd suggest this function only take a relative path, and
make sure that's the case from the callers.

> +
> +	while (path_len > 0) {
> +		int comp_len;
> +		int next;
> +
> +		sep = memchr(path, '/', path_len);
> +		comp_len = sep ? (sep - path) : path_len;
> +
> +		if (comp_len > 0) {

Similarly, in C I'd treat comp_len == 0 as an error condition, rather
than trying to handle it.

> +			next = fdt_subnode_offset_namelen(fdt, node, path,
> +							  comp_len);
> +			if (next == -FDT_ERR_NOTFOUND)
> +				next = fdt_add_subnode_namelen(fdt, node, path,
> +							       comp_len);
> +			if (next < 0)
> +				return next;
> +			node = next;
> +		}
> +
> +		path += comp_len;
> +		path_len -= comp_len;
> +		while (path_len > 0 && *path == '/') {
> +			path++;
> +			path_len--;
> +		}

Same thoughts here.

> +	}
> +
> +	return node;
> +}
> +
> +/**
> + * overlay_fragment_local_fixups - Find or create a fragment's target under /__local_fixups__
> + * @fdt: Base Device Tree blob
> + * @fdto: Device tree overlay blob
> + * @fragment: Node offset of the fragment in @fdto
> + * @create: Whether to create missing subnodes under /__local_fixups__
> + *
> + * overlay_fragment_local_fixups() resolves the target of @fragment in @fdt and
> + * locates (or creates, when @create is true) the corresponding node at the
> + * same path under @fdt's /__local_fixups__ subtree.
> + *
> + * returns:
> + *      Node offset of the corresponding /__local_fixups__ node on success
> + *      Negative error code on failure
> + */
> +static int overlay_fragment_local_fixups(void *fdt, const void *fdto,
> +					 int fragment, bool create)
> +{
> +	int root_lf, target, cur_fdt, node;
> +
> +	root_lf = fdt_subnode_offset(fdt, 0, "__local_fixups__");
> +	if (root_lf < 0)
> +		return root_lf;



> +
> +	target = fdt_overlay_target_offset(fdt, fdto, fragment, NULL);
> +	if (target < 0)
> +		return target;
> +
> +	cur_fdt = 0;

"cur_fdt" isn't a great name, it's not really suggesting to me what
it's tracking.

> +	node = root_lf;

Might be better to call this node_lf as a reminder that it's tracking
a subnode of __local_fixups__.

> +	while (cur_fdt != target) {
> +		int subnode, ancestor = -FDT_ERR_INTERNAL;
> +		const char *name;
> +		int namelen, next_lf;
> +
> +		fdt_for_each_subnode(subnode, fdt, cur_fdt) {
> +			if (subnode <= target)
> +				ancestor = subnode;
> +			else
> +				break;

I think
	if (subnode > target)
		break;
	ancestor = subnode;
would be clearer.

> +		}
> +		if (ancestor < 0)
> +			return ancestor;
> +
> +		name = fdt_get_name(fdt, ancestor, &namelen);
> +		if (!name)
> +			return namelen;
> +
> +		next_lf = fdt_subnode_offset_namelen(fdt, node, name, namelen);
> +		if (next_lf == -FDT_ERR_NOTFOUND && create) {
> +			char namebuf[256];

Arbitrary buffer size.

> +			int old_struct_size = fdt_size_dt_struct(fdt);
> +
> +			if ((size_t)namelen >= sizeof(namebuf))
> +				return -FDT_ERR_INTERNAL;
> +			memcpy(namebuf, name, namelen);
> +			next_lf = fdt_add_subnode_namelen(fdt, node, namebuf,
> +							  namelen);
> +			if (next_lf < 0)
> +				return next_lf;
> +			if (next_lf <= ancestor) {
> +				int delta = fdt_size_dt_struct(fdt) -
> +					    old_struct_size;
> +				target += delta;
> +				ancestor += delta;

Subtle, but necessary :/.  Probably worth a comment that this - as
well as the memcpy() above is because the fdt_add_subnode_namelen()
might move 'target', and therefore also its name within the fdt.

> +			}
> +		} else if (next_lf < 0) {
> +			return next_lf;
> +		}
> +
> +		cur_fdt = ancestor;
> +		node = next_lf;
> +	}

What this function is doing is pretty similar to
fdt_supernode_atdepth_offset().  I wonder if it there's a reasonable
way to make a common helper.

> +
> +	return node;
> +}
> +
> +/**
> + * overlay_clear_overwritten_local_fixups - Remove local fixups for overwritten properties
> + * @fdt: Base Device Tree blob
> + * @target_lf: Node offset in @fdt's /__local_fixups__ subtree
> + * @fdto: Device tree overlay blob
> + * @overlay: Node offset in @fdto's __overlay__ subtree

I'd probably suggest s/overlay/node/ and s/target_lf/node_lf/.

> + *
> + * overlay_clear_overwritten_local_fixups() recursively deletes properties under
> + * @target_lf that correspond to properties overwritten by @overlay in @fdto.
> + *
> + * returns:
> + *      0 on success
> + *      Negative error code on failure
> + */
> +static int overlay_clear_overwritten_local_fixups(void *fdt, int target_lf,
> +						  const void *fdto, int overlay)
> +{
> +	int prop, subnode;
> +
> +	fdt_for_each_property_offset(prop, fdto, overlay) {
> +		const char *name;
> +		const void *val;
> +		int len, ret;
> +
> +		val = fdt_getprop_by_offset(fdto, prop, &name, &len);
> +		if (!val)
> +			return len;
> +
> +		ret = fdt_delprop(fdt, target_lf, name);
> +		if (ret && ret != -FDT_ERR_NOTFOUND)
> +			return ret;

Doing this as a post-processing step, reconsulting the properties
fromt the overlay seems a bit odd to me, rather than zapping the
__local_fixups__ entry from within overlay_apply_node().  Is there a
reason for it?

> +	}
> +
> +	fdt_for_each_subnode(subnode, fdto, overlay) {
> +		const char *name;
> +		int namelen, sub_lf, ret;
> +
> +		name = fdt_get_name(fdto, subnode, &namelen);
> +		if (!name)
> +			return namelen;
> +
> +		sub_lf = fdt_subnode_offset_namelen(fdt, target_lf, name,
> +						    namelen);
> +		if (sub_lf == -FDT_ERR_NOTFOUND)
> +			continue;
> +		if (sub_lf < 0)
> +			return sub_lf;
> +
> +		ret = overlay_clear_overwritten_local_fixups(fdt, sub_lf,
> +							     fdto, subnode);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +/**
> + * overlay_add_local_fixup_offset - Insert a sorted u32 offset into a local fixup property
> + * @fdt: Base Device Tree blob
> + * @lf_node: Node offset in @fdt's /__local_fixups__ subtree
> + * @name: Property name to update
> + * @name_len: Length of @name in bytes
> + * @poffset: Byte offset of the phandle reference within the target property
> + *
> + * overlay_add_local_fixup_offset() inserts @poffset in ascending sorted order
> + * (ignoring duplicates) into the u32 array property @name on @lf_node.

Does the spec actually require that these offsets be sorted?  A
reference to that requirement might be useful here.

> + *
> + * returns:
> + *      0 on success
> + *      Negative error code on failure
> + */
> +static int overlay_add_local_fixup_offset(void *fdt, int lf_node,
> +					  const char *name, int name_len,
> +					  uint32_t poffset)
> +{
> +	const fdt32_t *old_val;
> +	fdt32_t *vals;
> +	void *prop_data;
> +	int old_len, count, i, idx, ret;
> +
> +	old_val = fdt_getprop_namelen(fdt, lf_node, name, name_len, &old_len);
> +	if (!old_val && old_len != -FDT_ERR_NOTFOUND)
> +		return old_len;
> +
> +	if (!old_val) {
> +		old_len = 0;
> +		count = 0;
> +		idx = 0;
> +	} else {
> +		if (old_len % sizeof(uint32_t))
> +			return -FDT_ERR_BADSTRUCTURE;

This should probably be BADVALUE rather than BADSTRUCTURE.

> +		count = old_len / sizeof(uint32_t);
> +		idx = count;
> +		for (i = 0; i < count; i++) {
> +			uint32_t cur = fdt32_ld(old_val + i);
> +
> +			if (cur == poffset)
> +				return 0;

I'm trying to think under what circumstances this would occur.  Does
it indicate a bad overlay if we're attempting to add a fixup that
already exists?

> +			if (cur > poffset) {
> +				idx = i;
> +				break;
> +			}
> +		}
> +	}
> +
> +	ret = fdt_setprop_placeholder_namelen(fdt, lf_node, name, name_len,
> +					      old_len + sizeof(uint32_t),
> +					      &prop_data);
> +	if (ret)
> +		return ret;
> +
> +	vals = prop_data;
> +	if (idx < count)
> +		memmove(vals + idx + 1, vals + idx,
> +			(count - idx) * sizeof(uint32_t));
> +	fdt32_st(vals + idx, poffset);
> +
> +	return 0;
> +}
> +
> +/**
> + * overlay_merge_fixup_into_local_fixups - Record one /__fixups__ property for a fragment
> + * @fdt: Base Device Tree blob
> + * @fdto: Device tree overlay blob
> + * @property: Property offset in @fdto's /__fixups__ node
> + * @fragment: Node offset of the current fragment in @fdto
> + * @target_frag_name: Name of @fragment
> + * @target_frag_len: Length of @target_frag_name in bytes

fdt_get_name() is a pretty cheap operation.  Might be simpler to allow
this function to derive the @target_frag_* values from @fragment.

> + * @target_lfp: In/out cached node offset of @fragment under @fdt's /__local_fixups__
> + *
> + * overlay_merge_fixup_into_local_fixups() parses all "path:prop:offset" entries
> + * in a single /__fixups__ property of @fdto and adds the resolved property
> + * byte offsets belonging to @fragment into @fdt's /__local_fixups__ subtree.
> + *
> + * returns:
> + *      0 on success
> + *      Negative error code on failure
> + */
> +static int overlay_merge_fixup_into_local_fixups(void *fdt, const void *fdto,
> +						 int property, int fragment,
> +						 const char *target_frag_name,
> +						 int target_frag_len,
> +						 int *target_lfp)
> +{
> +	const char *value, *label;
> +	int len;
> +
> +	value = fdt_getprop_by_offset(fdto, property, &label, &len);
> +	if (!value)
> +		return len;
> +
> +	do {
> +		const char *path, *name, *frag_name, *rel_path;
> +		uint32_t path_len, name_len, poffset;
> +		int frag_name_len, rel_path_len, lf_node, ret;
> +		bool is_overlay;
> +
> +		ret = overlay_parse_fixup_entry(&value, &len, &path, &path_len,
> +						&name, &name_len, &poffset);
> +		if (ret)
> +			return ret;
> +
> +		ret = overlay_split_fragment_path(path, path_len,
> +						  &frag_name, &frag_name_len,
> +						  &rel_path, &rel_path_len,
> +						  &is_overlay);
> +		if (ret)
> +			return ret;
> +		if (!is_overlay)
> +			continue;
> +
> +		if (frag_name_len != target_frag_len ||
> +		    memcmp(frag_name, target_frag_name, frag_name_len) != 0)
> +			continue;

Why filter by target fragment?  Neither the __fixups__ node you're
working from, nor the __local_fixups__ node you're updating is
naturally divided by fragment.

> +		if (*target_lfp < 0) {
> +			*target_lfp = overlay_fragment_local_fixups(
> +				fdt, fdto, fragment, true);
> +			if (*target_lfp < 0)
> +				return *target_lfp;
> +		}
> +
> +		lf_node = overlay_create_path(fdt, *target_lfp, rel_path,
> +					      rel_path_len);
> +		if (lf_node < 0)
> +			return lf_node;
> +
> +		ret = overlay_add_local_fixup_offset(fdt, lf_node, name,
> +						     name_len, poffset);
> +		if (ret)
> +			return ret;
> +	} while (len > 0);
> +
> +	return 0;
> +}
> +
> +/**
> + * overlay_merge_fixups_into_local_fixups - Record resolved /__fixups__ for a fragment
> + * @fdt: Base Device Tree blob
> + * @fdto: Device tree overlay blob
> + * @fragment: Node offset of the current fragment in @fdto
> + * @frag_name: Name of @fragment
> + * @frag_name_len: Length of @frag_name in bytes
> + * @target_lfp: In/out cached node offset of @fragment under @fdt's /__local_fixups__
> + *
> + * overlay_merge_fixups_into_local_fixups() parses external phandle references
> + * belonging to @fragment in @fdto's /__fixups__ node and records their resolved
> + * property byte offsets in @fdt's /__local_fixups__ subtree.
> + *
> + * returns:
> + *      0 on success
> + *      Negative error code on failure
> + */
> +static int overlay_merge_fixups_into_local_fixups(void *fdt, const void *fdto,
> +						  int fragment,
> +						  const char *frag_name,
> +						  int frag_name_len,
> +						  int *target_lfp)
> +{
> +	int fixups_off, prop;
> +
> +	fixups_off = fdt_subnode_offset(fdto, 0, "__fixups__");
> +	if (fixups_off == -FDT_ERR_NOTFOUND)
> +		return 0;
> +	if (fixups_off < 0)
> +		return fixups_off;
> +
> +	fdt_for_each_property_offset(prop, fdto, fixups_off) {
> +		int ret;
> +
> +		ret = overlay_merge_fixup_into_local_fixups(
> +			fdt, fdto, prop, fragment, frag_name, frag_name_len,
> +			target_lfp);
> +		if (ret)
> +			return ret;
> +	}

Is there a reason to do this as a post-processing pass, rather than
making the __local_fixups__ adjustment at the same time we already
process the fixup in overlay_fixup_phandle()?

> +	return 0;
> +}
> +
> +/**
> + * overlay_prune_empty_local_fixups - Remove empty subnodes from /__local_fixups__
> + * @fdt: Base Device Tree blob
> + * @lf_node: Node offset in @fdt's /__local_fixups__ subtree
> + *
> + * overlay_prune_empty_local_fixups() recursively deletes any child subnodes
> + * under @lf_node that have neither properties nor subnodes remaining.
> + *
> + * returns:
> + *      0 on success
> + *      Negative error code on failure
> + */
> +static int overlay_prune_empty_local_fixups(void *fdt, int lf_node)
> +{
> +	int subnode = fdt_first_subnode(fdt, lf_node);
> +
> +	while (subnode >= 0) {

We should propagate the error if subnode < 0 but is not
-FDT_ERR_NOTFOUND.

> +		int next, ret;
> +
> +		ret = overlay_prune_empty_local_fixups(fdt, subnode);
> +		if (ret < 0)
> +			return ret;
> +
> +		next = fdt_next_subnode(fdt, subnode);
> +		if (fdt_first_property_offset(fdt, subnode) == -FDT_ERR_NOTFOUND &&
> +		    fdt_first_subnode(fdt, subnode) == -FDT_ERR_NOTFOUND) {
> +			int old_struct_size = fdt_size_dt_struct(fdt);
> +
> +			ret = fdt_del_node(fdt, subnode);
> +			if (ret < 0)
> +				return ret;
> +			if (next >= 0)
> +				next += fdt_size_dt_struct(fdt) -
> +					old_struct_size;
> +		}
> +		subnode = next;
> +	}
> +
> +	return 0;
> +}
> +
> +/**
> + * overlay_validate_local_fixups - Validate /__local_fixups__ fragment nodes in overlay
> + * @fdto: Device tree overlay blob
> + * @ov_lf_root: Node offset of /__local_fixups__ in @fdto
> + *
> + * overlay_validate_local_fixups() verifies that every fragment subnode under
> + * @ov_lf_root that contains an __overlay__ subnode corresponds to a valid
> + * fragment node at the root of @fdto.

What's the reason for doing this as a pre-validation pass, rather than
checking this as we go to process each individual local_fixups entry?

> + *
> + * returns:
> + *      0 on success
> + *      Negative error code on failure
> + */
> +static int overlay_validate_local_fixups(const void *fdto, int ov_lf_root)
> +{
> +	int lf_frag;
> +
> +	fdt_for_each_subnode(lf_frag, fdto, ov_lf_root) {
> +		const char *frag_name;
> +		int frag_name_len, lf_overlay, fragment;
> +
> +		frag_name = fdt_get_name(fdto, lf_frag, &frag_name_len);
> +		if (!frag_name)
> +			return frag_name_len;
> +
> +		lf_overlay = fdt_subnode_offset(fdto, lf_frag, "__overlay__");
> +		if (lf_overlay == -FDT_ERR_NOTFOUND)
> +			continue;
> +		if (lf_overlay < 0)
> +			return lf_overlay;
> +
> +		fragment = fdt_subnode_offset_namelen(fdto, 0, frag_name,
> +						      frag_name_len);
> +		if (fragment < 0)
> +			return -FDT_ERR_BADOVERLAY;
> +
> +		if (fdt_subnode_offset(fdto, fragment, "__overlay__") < 0)
> +			return -FDT_ERR_BADOVERLAY;
> +	}
> +
> +	return 0;
> +}
> +
> +/**
> + * overlay_fragment_local_fixups_src - Find a fragment's __overlay__ in overlay /__local_fixups__
> + * @fdto: Device tree overlay blob
> + * @ov_lf_root: Node offset of /__local_fixups__ in @fdto (or -FDT_ERR_NOTFOUND)
> + * @frag_name: Name of the fragment node
> + * @frag_name_len: Length of @frag_name in bytes
> + *
> + * overlay_fragment_local_fixups_src() locates the __overlay__ subnode for
> + * @frag_name under @ov_lf_root in @fdto.
> + *
> + * returns:
> + *      Node offset of /__local_fixups__/<frag_name>/__overlay__ on success
> + *      -FDT_ERR_NOTFOUND if @ov_lf_root, <frag_name>, or __overlay__ is absent
> + *      Negative error code on other failures
> + */
> +static int overlay_fragment_local_fixups_src(const void *fdto, int ov_lf_root,
> +					     const char *frag_name,
> +					     int frag_name_len)
> +{
> +	int lf_frag;
> +
> +	if (ov_lf_root < 0)
> +		return -FDT_ERR_NOTFOUND;
> +
> +	lf_frag = fdt_subnode_offset_namelen(fdto, ov_lf_root, frag_name,
> +					     frag_name_len);
> +	if (lf_frag < 0)
> +		return lf_frag;
> +
> +	return fdt_subnode_offset(fdto, lf_frag, "__overlay__");
> +}
> +
> +/**
> + * overlay_fragment_update_local_fixups - Update base /__local_fixups__ for one fragment
> + * @fdt: Base Device Tree blob
> + * @fdto: Device tree overlay blob
> + * @fragment: Node offset of the fragment in @fdto
> + * @ov_lf_root: Node offset of /__local_fixups__ in @fdto (or -FDT_ERR_NOTFOUND)
> + *
> + * overlay_fragment_update_local_fixups() clears any base /__local_fixups__
> + * entries overwritten by @fragment, then merges phandle offsets for @fragment
> + * from @fdto's /__local_fixups__ and /__fixups__ subtrees.
> + *
> + * returns:
> + *      0 on success
> + *      Negative error code on failure
> + */
> +static int overlay_fragment_update_local_fixups(void *fdt, const void *fdto,
> +						int fragment, int ov_lf_root)
> +{
> +	const char *frag_name;
> +	int frag_name_len, overlay, target_lf, lf_overlay, ret;
> +
> +	overlay = fdt_subnode_offset(fdto, fragment, "__overlay__");
> +	if (overlay == -FDT_ERR_NOTFOUND)
> +		return 0;
> +	if (overlay < 0)
> +		return overlay;
> +
> +	frag_name = fdt_get_name(fdto, fragment, &frag_name_len);
> +	if (!frag_name)
> +		return frag_name_len;
> +
> +	target_lf = overlay_fragment_local_fixups(fdt, fdto, fragment, false);
> +	if (target_lf < 0 && target_lf != -FDT_ERR_NOTFOUND)
> +		return target_lf;
> +
> +	if (target_lf >= 0) {
> +		ret = overlay_clear_overwritten_local_fixups(fdt, target_lf,
> +							     fdto, overlay);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	lf_overlay = overlay_fragment_local_fixups_src(fdto, ov_lf_root,
> +						       frag_name,
> +						       frag_name_len);
> +	if (lf_overlay < 0 && lf_overlay != -FDT_ERR_NOTFOUND)
> +		return lf_overlay;
> +
> +	if (lf_overlay >= 0) {
> +		if (target_lf < 0)
> +			target_lf = overlay_fragment_local_fixups(fdt, fdto,
> +								  fragment,
> +								  true);
> +		if (target_lf < 0)
> +			return target_lf;
> +
> +		ret = overlay_apply_node(fdt, target_lf, fdto, lf_overlay);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return overlay_merge_fixups_into_local_fixups(
> +		fdt, fdto, fragment, frag_name, frag_name_len, &target_lf);
> +}
> +
> +/**
> + * overlay_local_fixups_update - Update base /__local_fixups__ with overlay fixups
> + * @fdt: Base Device Tree blob
> + * @fdto: Device tree overlay blob
> + *
> + * overlay_local_fixups_update() updates the base device tree's
> + * /__local_fixups__ subtree (when present) by walking each overlay fragment in
> + * application order, clearing entries for properties overwritten by that
> + * fragment, and merging phandle reference offsets from @fdto's
> + * /__local_fixups__ and /__fixups__ subtrees.
> + *
> + * This is part of the device tree overlay application process, when you want
> + * the merged device tree to retain complete local phandle fixup metadata.
> + *
> + * returns:
> + *      0 on success
> + *      Negative error code on failure
> + */
> +static int overlay_local_fixups_update(void *fdt, const void *fdto)
> +{
> +	int root_lf, ov_lf_root, fragment, ret;
> +
> +	root_lf = fdt_subnode_offset(fdt, 0, "__local_fixups__");
> +	if (root_lf == -FDT_ERR_NOTFOUND)
> +		return 0;
> +	if (root_lf < 0)
> +		return root_lf;
> +
> +	ov_lf_root = fdt_subnode_offset(fdto, 0, "__local_fixups__");
> +	if (ov_lf_root >= 0) {
> +		ret = overlay_validate_local_fixups(fdto, ov_lf_root);
> +		if (ret)
> +			return ret;
> +	} else if (ov_lf_root != -FDT_ERR_NOTFOUND) {
> +		return ov_lf_root;
> +	}
> +
> +	/*
> +	 * Process fragments in application order so that if a later fragment in
> +	 * fdto overwrites a property modified by an earlier fragment, the
> +	 * earlier fragment's local fixups for that property are cleared first.
> +	 */
> +	fdt_for_each_subnode(fragment, fdto, 0) {
> +		ret = overlay_fragment_update_local_fixups(fdt, fdto, fragment,
> +							   ov_lf_root);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	root_lf = fdt_subnode_offset(fdt, 0, "__local_fixups__");
> +	if (root_lf < 0)
> +		return root_lf;
> +
> +	return overlay_prune_empty_local_fixups(fdt, root_lf);
> +}
> +
>  int fdt_overlay_apply(void *fdt, void *fdto)
>  {
>  	uint32_t delta;
> @@ -1162,6 +1771,10 @@ int fdt_overlay_apply(void *fdt, void *fdto)
>  	if (ret)
>  		goto err;
>  
> +	ret = overlay_local_fixups_update(fdt, fdto);
> +	if (ret)
> +		goto err;
> +
>  	/*
>  	 * The overlay has been damaged, erase its magic.
>  	 */
> diff --git a/tests/run_tests.sh b/tests/run_tests.sh
> index 842b543059bb..e232c86aab37 100755
> --- a/tests/run_tests.sh
> +++ b/tests/run_tests.sh
> @@ -1089,6 +1089,25 @@ fdtoverlay_tests() {
>  
>      shorten_echo "check phandle to noda d wasn't overwritten:	"
>      run_wrap_test test "$bd" = "$pd"
> +
> +    # verify that /__local_fixups__ in a -L base blob is updated when applying an overlay
> +    run_dtc_test -@ -L -I dts -O dtb -o overlay_base_phandle_lf.test.dtb "$SRCDIR/overlay_base_phandle.dts"
> +    run_wrap_test $FDTOVERLAY -i overlay_base_phandle_lf.test.dtb -o overlay_base_phandle_lfO.test.dtb overlay_overlay_phandle.test.dtb
> +    run_fdtget_test 0 overlay_base_phandle_lfO.test.dtb /__local_fixups__/b a
> +    run_fdtget_test 0 overlay_base_phandle_lfO.test.dtb /__local_fixups__/b d
> +    run_fdtget_test 0 overlay_base_phandle_lfO.test.dtb /__local_fixups__/c b
> +    run_fdtget_test 0 overlay_base_phandle_lfO.test.dtb /__local_fixups__/d a
> +    run_fdtget_test 0 overlay_base_phandle_lfO.test.dtb /__local_fixups__/d b
> +    run_fdtget_test 0 overlay_base_phandle_lfO.test.dtb /__local_fixups__/d c
> +
> +    run_dtc_test -@ -L -I dts -O dtb -o overlay_base_lf.test.dtb "$SRCDIR/overlay_base.dts"
> +    run_wrap_test $DTPUT overlay_base_lf.test.dtb -cp /__local_fixups__/test-node/sub-test-node
> +    run_wrap_test $DTPUT overlay_base_lf.test.dtb /__local_fixups__/test-node/sub-test-node new-sub-test-property -tu 0
> +    run_dtc_test -@ -I dts -O dtb -o overlay_overlay.fdtoverlay.test.dtb "$SRCDIR/overlay_overlay.dts"
> +    run_wrap_test $FDTOVERLAY -i overlay_base_lf.test.dtb -o overlay_base_lfO.test.dtb overlay_overlay.fdtoverlay.test.dtb
> +    run_fdtget_test "0 4" overlay_base_lfO.test.dtb /__local_fixups__/test-node test-phandle
> +    run_fdtget_test "0 4" overlay_base_lfO.test.dtb /__local_fixups__/test-node test-several-phandle
> +    run_test check_path overlay_base_lfO.test.dtb not-exists "/__local_fixups__/test-node/sub-test-node"
>  }
>  
>  pylibfdt_tests () {
> -- 
> 2.56.0.313.g2479322e68-goog
> 
> 

-- 
David Gibson (he or they)	| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you, not the other way
				| around.
http://www.ozlabs.org/~dgibson

  reply	other threads:[~2026-10-30 11:26 UTC|newest]

Thread overview: 26+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-28 21:46 [PATCH v2 00/10] fdtfactor: Introduce fdtfactor to auto-factor overlays Douglas Anderson
2026-10-28 21:46 ` [PATCH v2 01/10] pylibfdt: Fix FdtSw.as_fdt() when buffer is nearly full Douglas Anderson
2026-10-30 12:50   ` Simon Glass
2026-10-28 21:46 ` [PATCH v2 02/10] pylibfdt: Add subnode/property iterators and get_mem_rsv_list() Douglas Anderson
2026-10-29  8:38   ` David Gibson
2026-10-30 12:50   ` Simon Glass
2026-10-28 21:46 ` [PATCH v2 03/10] pylibfdt: Add NodeTree live tree representation Douglas Anderson
2026-10-30  4:57   ` David Gibson
2026-10-30 12:50   ` Simon Glass
2026-10-28 21:46 ` [PATCH v2 04/10] libfdt: overlay: Factor out fixup and fragment path parsing helpers Douglas Anderson
2026-10-30  4:04   ` David Gibson
2026-10-30 12:50   ` Simon Glass
2026-10-28 21:46 ` [PATCH v2 05/10] libfdt: overlay: Prepare fixup and fragment path helpers for reuse Douglas Anderson
2026-10-30  4:24   ` David Gibson
2026-10-30 12:50   ` Simon Glass
2026-10-28 21:46 ` [PATCH v2 06/10] libfdt: overlay: Update base /__local_fixups__ on apply Douglas Anderson
2026-10-30 11:26   ` David Gibson [this message]
2026-10-30 12:50   ` Simon Glass
2026-10-28 21:46 ` [PATCH v2 07/10] fdtfactor: Add tool to split DTBs into a base DTB and overlays Douglas Anderson
2026-10-30 12:50   ` Simon Glass
2026-10-28 21:46 ` [PATCH v2 08/10] fdtfactor: Add -S/--strip-local-fixups option Douglas Anderson
2026-10-30 12:50   ` Simon Glass
2026-10-28 21:46 ` [PATCH v2 09/10] fdtfactor: Support compressed DTB inputs and outputs Douglas Anderson
2026-10-30 12:50   ` Simon Glass
2026-10-28 21:46 ` [PATCH v2 10/10] fdtfactor: Add compiled FIT image factoring support Douglas Anderson
2026-10-30 12:50   ` Simon Glass

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=auR-OXOpim7dVTpH@gractus.seuss \
    --to=david@gibson.dropbear.id.au \
    --cc=Devicetree-compiler@vger.kernel.org \
    --cc=Devicetree@vger.kernel.org \
    --cc=andre.draszik@linaro.org \
    --cc=briannorris@chromium.org \
    --cc=conor+dt@kernel.org \
    --cc=dianders@chromium.org \
    --cc=jwerner@chromium.org \
    --cc=konradybcio@kernel.org \
    --cc=krzk+dt@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=peter.griffin@linaro.org \
    --cc=robh@kernel.org \
    --cc=saravanak@kernel.org \
    --cc=sjg@chromium.org \
    --cc=tudor.ambarus@linaro.org \
    --cc=wenst@chromium.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.