[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