[U-Boot] [PATCH v7 07/13] mtd: uclass: add a generic 'mtdparts' parser

Stefan Roese sr at denx.de
Sat Sep 1 08:52:54 UTC 2018


On 31.08.2018 16:57, Miquel Raynal wrote:
> The current parser is very specific to U-Boot mtdparts implementation.
> It does not use MTD structures like mtd_info and mtd_partition. Copy
> and adapt the current parser in drivers/mtd/mtd-uclass.c (to not break
> the current use of mtdparts.c itself) and write some kind of a wrapper
> around the current implementation to allow other commands to benefit
> from this parsing in a user-friendly way.
> 
> This new function will allocate an mtd_partition array for each
> successful call. This array must be freed after use by the caller.
> The given 'mtdparts' buffer pointer will be moved forward to the next
> MTD device (if any, it will point towards a '\0' character otherwise).
> 
> Signed-off-by: Miquel Raynal <miquel.raynal at bootlin.com>
> Acked-by: Jagan Teki <jagan at openedev.com>
> ---
>   drivers/mtd/mtdpart.c          | 187 +++++++++++++++++++++++++++++++++
>   include/linux/mtd/partitions.h |   2 +
>   2 files changed, 189 insertions(+)
> 
> diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c
> index 9ccb1b3361..d9708da2ae 100644
> --- a/drivers/mtd/mtdpart.c
> +++ b/drivers/mtd/mtdpart.c
> @@ -76,6 +76,193 @@ char *kstrdup(const char *s, gfp_t gfp)
>   }
>   #endif
>   
> +#define MTD_SIZE_REMAINING		(~0LLU)
> +#define MTD_OFFSET_NOT_SPECIFIED	(~0LLU)
> +
> +/**
> + * mtd_parse_partition - Parse @mtdparts partition definition, fill @partition
> + *                       with it and update the @mtdparts string pointer.
> + *
> + * The partition name is allocated and must be freed by the caller.
> + *
> + * This function is widely inspired from part_parse (mtdparts.c).
> + *
> + * @mtdparts: String describing the partition with mtdparts command syntax
> + * @partition: MTD partition structure to fill
> + *
> + * @return 0 on success, an error otherwise.
> + */
> +static int mtd_parse_partition(const char **_mtdparts,
> +			       struct mtd_partition *partition)
> +{
> +	const char *mtdparts = *_mtdparts;
> +	const char *name = NULL;
> +	int name_len;
> +	char *buf;
> +
> +	/* Ensure the partition structure is empty */
> +	memset(partition, 0, sizeof(struct mtd_partition));
> +
> +	/* Fetch the partition size */
> +	if (*mtdparts == '-') {
> +		/* Assign all remaining space to this partition */
> +		partition->size = MTD_SIZE_REMAINING;
> +		mtdparts++;
> +	} else {
> +		partition->size = ustrtoull(mtdparts, (char **)&mtdparts, 0);
> +		if (partition->size < SZ_4K) {
> +			printf("Minimum allowed partition size is 4kiB\n");
> +			return -EINVAL;
> +		}
> +	}
> +
> +	/* Check for the offset */
> +	partition->offset = MTD_OFFSET_NOT_SPECIFIED;
> +	if (*mtdparts == '@') {
> +		mtdparts++;
> +		partition->offset = ustrtoull(mtdparts, (char **)&mtdparts, 0);
> +	}
> +
> +	/* Now look for the name */
> +	if (*mtdparts == '(') {
> +		name = ++mtdparts;
> +		if ((mtdparts = strchr(name, ')')) == NULL) {
> +			printf("No closing ')' found in partition name\n");
> +			return -EINVAL;
> +		}
> +		name_len = mtdparts - name + 1;
> +		if ((name_len - 1) == 0) {
> +			printf("Empty partition name\n");
> +			return -EINVAL;
> +		}
> +		mtdparts++;
> +	} else {
> +		/* Name will be of the form size at offset */
> +		name_len = 22;
> +	}
> +
> +	/* Check if the partition is read-only */
> +	if (strncmp(mtdparts, "ro", 2) == 0) {
> +		partition->mask_flags |= MTD_WRITEABLE;
> +		mtdparts += 2;
> +	}
> +
> +	/* Check for a potential next partition definition */
> +	if (*mtdparts == ',') {
> +		if (partition->size == MTD_SIZE_REMAINING) {
> +			printf("No partitions allowed after a fill-up partition\n");
> +			return -EINVAL;
> +		}
> +		++mtdparts;
> +	} else if (*mtdparts == ';') {
> +		++mtdparts;
> +	} else if (*mtdparts == '\0') {
> +		/* NOP */
> +	} else {
> +		printf("Unexpected character '%c' in mtdparts\n", *mtdparts);
> +		return -EINVAL;
> +	}
> +
> +	/*
> +	 * Allocate a buffer for the name and either copy the provided name or
> +	 * auto-generate it with the form 'size at offset'.
> +	 */
> +	buf = malloc(name_len);
> +	if (!buf)
> +		return -ENOMEM;
> +
> +	if (name)
> +		strncpy(buf, name, name_len - 1);
> +	else
> +		snprintf(buf, name_len, "0x%08llx at 0x%08llx",
> +			 partition->size, partition->offset);
> +
> +	buf[name_len - 1] = '\0';
> +	partition->name = buf;
> +
> +	*_mtdparts = mtdparts;
> +
> +	return 0;
> +}
> +
> +/**
> + * mtdparts_parse_part - Create a partition array from an mtdparts definition
> + *
> + * Stateless function that takes a @parent MTD device, a string @_mtdparts
> + * describing the partitions (with the "mtdparts" command syntax) and creates
> + * the corresponding MTD partition structure array @_parts. Both the name and
> + * the structure partition itself must be freed freed by the caller.
> + *
> + * @parent: MTD device which contains the partitions
> + * @_mtdparts: Pointer to a string describing the partitions with "mtdparts"
> + *             command syntax.
> + * @_parts: Allocated array containing the partitions, must be freed by the
> + *          caller.
> + * @_nb_parts: Size of @_parts array.
> + *
> + * @return 0 on success, an error otherwise.
> + */
> +int mtd_parse_partitions(struct mtd_info *parent, const char **_mtdparts,
> +			 struct mtd_partition **_parts, int *_nb_parts)
> +{
> +	struct mtd_partition partition = {}, *parts;
> +	const char *mtdparts = *_mtdparts;
> +	int cur_off = 0, cur_sz = 0;
> +	int nb_parts = 0;
> +	int ret, idx;
> +	u64 sz;
> +
> +	/* First, iterate over the partitions until we know their number */
> +	while (mtdparts[0] != '\0' && mtdparts[0] != ';') {
> +		ret = mtd_parse_partition(&mtdparts, &partition);
> +		if (ret)
> +			return ret;
> +
> +		free((char *)partition.name);
> +		nb_parts++;
> +	}
> +
> +	/* Allocate an array of partitions to give back to the caller */
> +	parts = malloc(sizeof(*parts) * nb_parts);
> +	if (!parts) {
> +		printf("Could not allocate enough space to save partitions meta-data\n");
> +		return -ENOMEM;
> +	}
> +
> +	/* Iterate again over each partition to save the data in our array */
> +	for (idx = 0; idx < nb_parts; idx++) {
> +		ret = mtd_parse_partition(_mtdparts, &parts[idx]);
> +		if (ret)
> +			return ret;
> +
> +		if (parts[idx].size == MTD_SIZE_REMAINING)
> +			parts[idx].size = parent->size - cur_sz;
> +		cur_sz += parts[idx].size;
> +
> +		sz = parts[idx].size;
> +		if (sz < parent->writesize || do_div(sz, parent->writesize)) {
> +			printf("Partition size must be a multiple of %d\n",
> +			       parent->writesize);
> +			return -EINVAL;
> +		}
> +
> +		if (parts[idx].offset == MTD_OFFSET_NOT_SPECIFIED)
> +			parts[idx].offset = cur_off;
> +		cur_off += parts[idx].size;
> +
> +		parts[idx].ecclayout = parent->ecclayout;
> +	}
> +
> +	/* Offset by one mtdparts to point to the next device if any */
> +	if (*_mtdparts[0] == ';')
> +		_mtdparts++;
> +
> +	*_parts = parts;
> +	*_nb_parts = nb_parts;
> +
> +	return 0;
> +}
> +
>   /*
>    * MTD methods which simply translate the effective address and pass through
>    * to the _real_ device.
> diff --git a/include/linux/mtd/partitions.h b/include/linux/mtd/partitions.h
> index ce0e8dbee4..ed4ece5e13 100644
> --- a/include/linux/mtd/partitions.h
> +++ b/include/linux/mtd/partitions.h
> @@ -86,5 +86,7 @@ int mtd_add_partition(struct mtd_info *master, const char *name,
>   		      long long offset, long long length);
>   int mtd_del_partition(struct mtd_info *master, int partno);
>   uint64_t mtd_get_device_size(const struct mtd_info *mtd);
> +int mtd_parse_partitions(struct mtd_info *parent, const char **_mtdparts,
> +			 struct mtd_partition **_parts, int *_nb_parts);
>   
>   #endif
> 

Reviewed-by: Stefan Roese <sr at denx.de>

Thanks,
Stefan


More information about the U-Boot mailing list