mtd: mtdpart: add a generic mtdparts-like parser

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@bootlin.com>
Acked-by: Jagan Teki <jagan@openedev.com>
Reviewed-by: Stefan Roese <sr@denx.de>
Reviewed-by: Boris Brezillon <boris.brezillon@bootlin.com>
lime2-spi
Miquel Raynal 6 years ago committed by Jagan Teki
parent e9f62db64b
commit 21cc1fb5af
  1. 210
      drivers/mtd/mtdpart.c
  2. 21
      include/linux/mtd/partitions.h

@ -26,6 +26,7 @@
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/err.h>
#include <linux/sizes.h>
#include "mtdcore.h"
@ -76,6 +77,215 @@ 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 partition size 4kiB, %lldB requested\n",
partition->size);
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;
mtdparts = strchr(name, ')');
if (!mtdparts) {
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@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\n");
return -EINVAL;
}
++mtdparts;
} else if ((*mtdparts == ';') || (*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@offset'.
*/
buf = malloc(name_len);
if (!buf)
return -ENOMEM;
if (name)
strncpy(buf, name, name_len - 1);
else
snprintf(buf, name_len, "0x%08llx@0x%08llx",
partition->size, partition->offset);
buf[name_len - 1] = '\0';
partition->name = buf;
*_mtdparts = mtdparts;
return 0;
}
/**
* mtd_parse_partitions - 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, the caller may use
* @mtd_free_parsed_partitions() for this purpose.
*
* @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.
* @_nparts: 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 *_nparts)
{
struct mtd_partition partition = {}, *parts;
const char *mtdparts = *_mtdparts;
int cur_off = 0, cur_sz = 0;
int nparts = 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);
nparts++;
}
/* Allocate an array of partitions to give back to the caller */
parts = malloc(sizeof(*parts) * nparts);
if (!parts) {
printf("Not 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 < nparts; 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;
*_nparts = nparts;
return 0;
}
/**
* mtd_free_parsed_partitions - Free dynamically allocated partitions
*
* Each successful call to @mtd_parse_partitions must be followed by a call to
* @mtd_free_parsed_partitions to free any allocated array during the parsing
* process.
*
* @parts: Array containing the partitions that will be freed.
* @nparts: Size of @parts array.
*/
void mtd_free_parsed_partitions(struct mtd_partition *parts,
unsigned int nparts)
{
int i;
for (i = 0; i < nparts; i++)
free((char *)parts[i].name);
free(parts);
}
/*
* MTD methods which simply translate the effective address and pass through
* to the _real_ device.

@ -87,4 +87,25 @@ int mtd_add_partition(struct mtd_info *master, const char *name,
int mtd_del_partition(struct mtd_info *master, int partno);
uint64_t mtd_get_device_size(const struct mtd_info *mtd);
#if defined(CONFIG_MTD_PARTITIONS)
int mtd_parse_partitions(struct mtd_info *parent, const char **_mtdparts,
struct mtd_partition **_parts, int *_nparts);
void mtd_free_parsed_partitions(struct mtd_partition *parts,
unsigned int nparts);
#else
static inline int
mtd_parse_partitions(struct mtd_info *parent, const char **_mtdparts,
struct mtd_partition **_parts, int *_nparts)
{
*_nparts = 0;
return 0;
}
static inline void
mtd_free_parsed_partitions(struct mtd_partition *parts, unsigned int nparts)
{
return;
}
#endif /* defined(MTD_PARTITIONS) */
#endif

Loading…
Cancel
Save