Re: [PATCH 3/6] libfdt: overlay: rename-fragments

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]



On Fri, Mar 16, 2018 at 06:37:55PM +0530, Srivatsa Vaddagiri wrote:
> When merging two overlay blobs, fragment nodes whose target node can't be found
> in base node would need to be retained as-is (including the fragment names) in
> the combined blob. Such unresolved symbols will also need to be listed in
> __fixups__ section of combined blob. This could lead to name comflicts in
> combined blob (two nodes with same name/path such as /fragment@0).
> 
> To avoid such name conflicts in combined blob, rename all fragment@xyz in
> overlay blob as fragment@xyz+delta, where delta is the maximum count of fragment
> nodes found in base blob
> 
> Signed-off-by: Srivatsa Vaddagiri <vatsa@xxxxxxxxxxxxxx>
> 
> ---
>  libfdt/fdt_overlay.c | 390 +++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 390 insertions(+)
> 
> diff --git a/libfdt/fdt_overlay.c b/libfdt/fdt_overlay.c
> index 81b9feb..556551e 100644
> --- a/libfdt/fdt_overlay.c
> +++ b/libfdt/fdt_overlay.c
> @@ -53,6 +53,8 @@
>  
>  #include <fdt.h>
>  #include <libfdt.h>
> +#include <stdio.h>
> +#include <assert.h>
>  
>  #include "libfdt_internal.h"
>  
> @@ -67,6 +69,114 @@ void fdt_overlay_set_verbose(int val)
>  	verbose_overlay = val;
>  }
>  
> +int base_has_fixups;
> +int base_fixup_search_done;
> +unsigned long max_base_fragments;
> +
> +#define PATH_MAX	512
> +#define ULONG_MAX       (~0UL)
> +
> +#define ALIGN(x)	(((x) + (FDT_TAGSIZE) - 1) & ~((FDT_TAGSIZE) - 1))
> +
> +static inline void *xrealloc(void *p, size_t len)

As noted on 1/6 libfdt does not require an allocator, as a deliberate
design decision.  I especially don't want to rely on an allocator
which supplies realloc().


> +{
> +	void *new = realloc(p, len);
> +
> +	if (!new) {
> +		dprintf("FATAL ERROR: realloc() failed for %lu bytes\n", len);
> +		exit(1);

exit() is also a new dependency which would not make sense in a number
of environments which aren't like regular userspace.

> +	}
> +
> +	return new;
> +}
> +
> +static inline void *xmalloc(size_t len)
> +{
> +	void *new = malloc(len);
> +
> +	if (!new) {
> +		dprintf("FATAL ERROR: malloc() failed for %lu bytes\n", len);
> +		exit(1);
> +	}
> +
> +	return new;
> +}
> +
> +static char *realloc_fdt(char *fdt, int delta)
> +{
> +	int new_sz;
> +
> +	delta = ALIGN(delta);
> +	assert(delta > 0);
> +
> +	new_sz = fdt_totalsize(fdt) + delta;
> +
> +	fdt = xrealloc(fdt, new_sz);
> +	fdt_set_totalsize(fdt, new_sz);
> +
> +	return fdt;
> +}


> +/**
> + * fdt_set_name_memcheck - Change name of a node, increasing the size of the
> + * blob in the process if required
> + *
> + * @fdt      - Pointer to pointer that points to device-tree blob
> + * @offset   - Offset to the node
> + * @orig_len - Length of node's current name
> + * @name     - new name of the node
> + */
> +static int fdt_set_name_memcheck(void **fdt, int offset,
> +				int orig_len, char *name)
> +{
> +	int err, lendiff;
> +
> +	err = fdt_set_name(*fdt, offset, name);
> +	if (!err)
> +		return 0;
> +
> +	if (err != -FDT_ERR_NOSPACE)
> +		return err;
> +
> +	lendiff = strlen(name) + 1 - orig_len;
> +
> +	*fdt = realloc_fdt(*fdt, lendiff);
> +
> +	return fdt_set_name(*fdt, offset, name);
> +}
> +
> +/**
> + * fdt_setprop_placeholder_memcheck - Set property value
> + *
> + * This is a wrapper around fdt_setprop_placeholder(). It increases the size of
> + * DT blob in case fdt_setprop_placeholder() fails because of lack of space
> + *
> + * @fdt - pointer to pointer that point to a device-tree blob
> + * @offset - Offset of property whose length needs to be increased to @new_len
> + * @name   - name of the property
> + * @orig_len - Current length of the property value
> + * @new_len  - New length of the property value
> + * @prop_data - Used to store pointer to property value of length @new_len
> + */
> +static int fdt_setprop_placeholder_memcheck(void **fdt, int offset,
> +				const char *name, int orig_len, int new_len, void **prop_data)
> +{
> +	int err;
> +
> +	assert(new_len >= orig_len);
> +
> +	err = fdt_setprop_placeholder(*fdt, offset, name, new_len, prop_data);
> +	if (!err)
> +		return 0;
> +
> +	if (err != -FDT_ERR_NOSPACE)
> +		return err;
> +
> +	*fdt = realloc_fdt(*fdt, new_len - orig_len);
> +
> +	return fdt_setprop_placeholder(*fdt, offset, name, new_len, prop_data);
> +}
> +
>  /**
>   * overlay_get_target_phandle - retrieves the target phandle of a fragment
>   * @fdto: pointer to the device tree overlay blob
> @@ -874,15 +984,295 @@ static int overlay_symbol_update(void *fdt, void *fdto)
>  	return 0;
>  }
>  
> +/* Return maximum count of nodes that are named fragment@xyz */
> +static unsigned long count_fragments(void *fdt)
> +{
> +	int offset = -1;
> +	const char *name, *idx;
> +	char *stop;
> +	unsigned long index, max = 0;
> +	int len;
> +
> +	do {
> +		offset = fdt_next_node(fdt, offset, NULL);
> +		if (offset < 0)
> +			break;
> +
> +		name = fdt_get_name(fdt, offset, &len);
> +		if (len > 9 && !strncmp(name, "fragment@", 9)) {
> +			idx = name + 9;
> +			index = strtoul(idx, &stop, 10);
> +			if (index > max)
> +				max = index;
> +		}

This is wrong on multiple counts.  1) You should only check top level
nodes, not all of them, 2) fragments aren't necessarily named
"fragment@XX", that's just a convention - several people have
suggested using "fragment-XX" to avoid some other problems, for
example.  The correct way to identify fragments is to look for the
__overlay__ subnode.  3) You have no error handling if the unit
address doesn't parse as a decimal integer.

Come to that, I'm not sure it's ever been properly specified whether
the number on a fragment is in decimal or not.  Usually unit names are
in hex, but fragments are weird in a number of ways.

> +	} while (1);
> +
> +	return max;
> +}
> +
> +static void copy_buf(const char **start, int len, char **pp,
> +				int *rembufsize, char **bufp, int *bufsize)
> +{
> +	int delta;
> +
> +	if (*rembufsize < len) {
> +		delta = (len - *rembufsize) + 1024;
> +		*bufp = xrealloc(*bufp, *bufsize + delta);
> +		*bufsize += delta;
> +		*rembufsize += delta;
> +	}
> +	memcpy(*pp, *start, len);
> +	*start += len;
> +	*pp += len;
> +	*rembufsize -= len;
> +}
> +
> +/**
> + * rename_fragments_in_section - Rename fragment@xyz instances in a section

What does "section" mean?  It's not a standard term in connection to
fdts.

> + *
> + * @fdto    - pointer to pointer that points to a device-tree blob
> + * @secname - Section in which fragments need to be renamed 
> + * @delta   - Increment to be applied to fragment index
> + */
> +static int rename_fragments_in_section(void **fdto, const char *secname,
> +				unsigned long delta)
> +{
> +	int offset, len, new_len, property;
> +	unsigned long index, new_index;
> +	int bufsize, rembufsize, err;
> +
> +	offset = fdt_path_offset(*fdto, secname);
> +	if (offset < 0) {
> +		dprintf("%s section not found\n", secname);
> +		return -FDT_ERR_NOTFOUND;
> +	}
> +
> +	fdt_for_each_property_offset(property, *fdto, offset) {
> +		const char *value, *end, *sep, *start, *copy_start;
> +		const char *label;
> +		int modified = 0, needed;
> +		char *buf, *p;
> +		void *q;
> +
> +		value = fdt_getprop_by_offset(*fdto, property,
> +					      &label, &len);
> +		if (!value) {
> +			dprintf("Failed to get value of property at offset %d"
> +				" in section %s (err %d)\n", property,
> +								secname, len);
> +			return -1;

That's equivalent to -FDT_ERR_NOTFOUND, which I don't think is what
you want.

> +		}
> +
> +		/*
> +		 * Property value could be in this format
> +		 *	fragment@M ...fragment@N....fragment@O..
> +		 *
> +		 * This need to be converted to
> +		 *	fragment@M+delta...fragment@N+delta....fragment@O+delta
> +		 *
> +		 * which could require more space than what is currently
> +		 * allocated to the property. 'buf' will be used to create the
> +		 * new value for the property, which can then be used to
> +		 * modify property's value
> +		 */
> +
> +		/*
> +		 * 1024 bytes of additional space for holding extra characters
> +		 * if required
> +		 */
> +		buf = xmalloc(len + 1024);
> +		bufsize = len + 1024;
> +		rembufsize = len + 1024;
> +
> +		start = value;
> +		end = value + len;
> +		p = buf;
> +		copy_start = start;
> +
> +		while (start < end) {
> +			char *stop;
> +
> +			sep = memchr(start, '@', (end - start));
> +			if (!sep)
> +				break;
> +
> +			/* Check if string "fragment" exists */
> +			sep -= 8;
> +			if (sep < start || strncmp(sep, "fragment", 8)) {
> +				/* Start scan again after '@' */
> +				start = sep + 9;
> +				continue;
> +			}
> +
> +			sep += 9;
> +
> +			/*
> +			 * copy characters from @copy_start to @sep into @p
> +			 * increasing the size of @buf if available space is
> +			 * less.
> +			 */
> +			copy_buf(&copy_start, sep - copy_start, &p, &rembufsize,
> +								&buf, &bufsize);
> +			stop = NULL;
> +			index = strtoul(sep, &stop, 10);
> +			if (!stop || ULONG_MAX - index < delta) {
> +				free(buf);
> +				return -FDT_ERR_BADSTATE;

BADSTATE has a specific meaning, and this isn't it.

> +			}
> +
> +			new_index = index + delta;
> +			needed = snprintf(NULL, 0, "%lu", new_index);
> +			if (needed > rembufsize) {
> +				long diff = needed - rembufsize;
> +				if (diff < 1024)
> +					diff = 1024;
> +				buf = xrealloc(buf, bufsize + diff);
> +				bufsize += diff;
> +				rembufsize += diff;
> +			}
> +			modified = 1;
> +			p += snprintf(p, rembufsize, "%lu", new_index);
> +			start = stop;
> +			copy_start = start;
> +		}
> +
> +		if (end > copy_start)
> +			copy_buf(&copy_start, end - copy_start, &p,
> +						&rembufsize, &buf, &bufsize);
> +
> +		if (modified) {
> +			new_len = p - buf;
> +			err = fdt_setprop_placeholder_memcheck(fdto, offset,
> +						label, len, new_len, &q);
> +			if (err) {
> +				free(buf);
> +				return err;
> +			}
> +
> +			memcpy(q, buf, new_len);
> +		}
> +		free(buf);
> +	}
> +
> +	return 0;
> +}
> +
> +/**
> + * rename_nodes - Rename all fragement@xyz nodes
> + *
> + * @fdto - pointer to pointer that points to device-tree blob
> + * @delta - increment to be added to fragment number
> + */
> +static int rename_nodes(void **fdto, unsigned long delta)
> +{
> +	int offset = -1, err;
> +	const char *name, *idx;
> +	char *stop = NULL;
> +	int len;
> +	char new_name[PATH_MAX];

PATH_MAX is another new dependency that doesn't make sense for
arbitrary environments.

> +	unsigned long index, new_index;
> +
> +	do {
> +		offset = fdt_next_node(*fdto, offset, NULL);
> +		if (offset < 0)
> +			break;

Again, you're mangling all nodes, not just top-level ones.

> +		name = fdt_get_name(*fdto, offset, &len);
> +		if (len > 9 && !strncmp(name, "fragment@", 9)) {
> +			idx = name + 9;
> +			stop = NULL;
> +			index = strtoul(idx, &stop, 10);
> +			if (ULONG_MAX - delta < index)
> +				return -FDT_ERR_BADSTATE;
> +			new_index = index + delta;
> +			snprintf(new_name, sizeof(new_name),
> +						"fragment@%lu", new_index);
> +
> +			err = fdt_set_name_memcheck(fdto, offset,
> +							len, new_name);
> +			if (err < 0)
> +				return err;
> +		}
> +	} while (1);
> +
> +	return 0;
> +}
> +
> +/*
> + * In case of overlaying onto a base blob which itself has unresolved external
> + * phandle references, we will need to merge overlay fragments found in overlay
> + * blob into root node of base blob, which can lead to naming conflicts for
> + * nodes. To prevent such conflicts, rename all occurence of fragment@xyz in
> + * overlay blob with fragment@abc (abc = xyz + delta)
> + */
> +static int rename_fragments(void **fdto, unsigned long delta)
> +{
> +	int rc;
> +
> +	rc = rename_nodes(fdto, delta);
> +	if (rc < 0) {
> +		dprintf("rename_nodes failed %d\n", rc);
> +		return rc;
> +	}
> +
> +	rc = rename_fragments_in_section(fdto, "/__symbols__", delta);
> +	if (rc < 0 && rc != -FDT_ERR_NOTFOUND) {
> +		dprintf("rename of fragments in __symbols__ section "
> +			"failed %d\n", rc);
> +		return rc;
> +	}
> +
> +	rc = rename_fragments_in_section(fdto, "/__fixups__", delta);
> +	if (rc < 0) {
> +		dprintf("rename of fragments in __fixups__ section "
> +			"failed %d\n", rc);
> +		return rc;
> +	}
> +
> +	/*
> +	 * renaming fragments in __local_fixups__ section should be covered by
> +	 * rename_nodes()
> +	 */

Ugh, I guess it will, but not safely.  It's possible that some other
node further down in the tree could have the name "fragment@" in which
case it will get mangled improperly by this code.

> +
> +	return 0;
> +}
> +
>  int fdt_overlay_apply(void **fdt, void **fdto)
>  {
>  	uint32_t delta = fdt_get_max_phandle(*fdt);
>  	int ret;
> +	unsigned long max_fdto_fragments;
>  
>  	FDT_CHECK_HEADER(*fdt);
>  	FDT_CHECK_HEADER(*fdto);
>  
>  	dprintf("Adjusting local phandles\n");
> +	/* Does base blob have unresolved external references? */
> +	if (!base_fixup_search_done) {
> +		if (fdt_path_offset(*fdt, "/__fixups__") >= 0)
> +			base_has_fixups = 1;
> +		base_fixup_search_done = 1;
> +	}

Yeah, this is gross, just use a different entry point for the two cases.

> +
> +	if (base_has_fixups) {
> +		if (!max_base_fragments)
> +			max_base_fragments = count_fragments(*fdt) + 1;
> +		if ((long)max_base_fragments < 0)
> +			return -FDT_ERR_BADSTATE;

This is not a safe overflow check.

> +		max_fdto_fragments = count_fragments(*fdto) + 1;
> +		if ((long)max_fdto_fragments < 0)
> +			return -FDT_ERR_BADSTATE;
> +		dprintf("max_base fragments %lu, max_overlay fragments %lu\n",
> +					max_base_fragments, max_fdto_fragments);
> +		ret = rename_fragments(fdto, max_base_fragments);
> +		if (ret < 0) {
> +			dprintf("rename_fragments failed %d\n", ret);
> +			return ret;
> +		}
> +		max_base_fragments += max_fdto_fragments;

You adjust this value, then never use it again.

> +	}
> +
>  	ret = overlay_adjust_local_phandles(*fdto, delta);
>  	if (ret)
>  		goto err;

-- 
David Gibson			| 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

Attachment: signature.asc
Description: PGP signature


[Index of Archives]     [Device Tree]     [Device Tree Spec]     [Linux Driver Backports]     [Video for Linux]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]

  Powered by Linux