Skip to content

Commit

Permalink
block: support embedded device command line partition
Browse files Browse the repository at this point in the history
Read block device partition table from command line.  The partition used
for fixed block device (eMMC) embedded device.  It is no MBR, save
storage space.  Bootloader can be easily accessed by absolute address of
data on the block device.  Users can easily change the partition.

This code reference MTD partition, source "drivers/mtd/cmdlinepart.c"
About the partition verbose reference
"Documentation/block/cmdline-partition.txt"

[[email protected]: fix printk text]
[[email protected]: fix error return code in parse_parts()]
Signed-off-by: Cai Zhiyong <[email protected]>
Cc: Karel Zak <[email protected]>
Cc: "Wanglin (Albert)" <[email protected]>
Cc: Marius Groeger <[email protected]>
Cc: David Woodhouse <[email protected]>
Cc: Jens Axboe <[email protected]>
Cc: Brian Norris <[email protected]>
Cc: Artem Bityutskiy <[email protected]>
Signed-off-by: Wei Yongjun <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
  • Loading branch information
Cai Zhiyong authored and torvalds committed Sep 11, 2013
1 parent ed751e6 commit bab5541
Show file tree
Hide file tree
Showing 10 changed files with 452 additions and 0 deletions.
39 changes: 39 additions & 0 deletions Documentation/block/cmdline-partition.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
Embedded device command line partition
=====================================================================

Read block device partition table from command line.
The partition used for fixed block device (eMMC) embedded device.
It is no MBR, save storage space. Bootloader can be easily accessed
by absolute address of data on the block device.
Users can easily change the partition.

The format for the command line is just like mtdparts:

blkdevparts=<blkdev-def>[;<blkdev-def>]
<blkdev-def> := <blkdev-id>:<partdef>[,<partdef>]
<partdef> := <size>[@<offset>](part-name)

<blkdev-id>
block device disk name, embedded device used fixed block device,
it's disk name also fixed. such as: mmcblk0, mmcblk1, mmcblk0boot0.

<size>
partition size, in bytes, such as: 512, 1m, 1G.

<offset>
partition start address, in bytes.

(part-name)
partition name, kernel send uevent with "PARTNAME". application can create
a link to block device partition with the name "PARTNAME".
user space application can access partition by partition name.

Example:
eMMC disk name is "mmcblk0" and "mmcblk0boot0"

bootargs:
'blkdevparts=mmcblk0:1G(data0),1G(data1),-;mmcblk0boot0:1m(boot),-(kernel)'

dmesg:
mmcblk0: p1(data0) p2(data1) p3()
mmcblk0boot0: p1(boot) p2(kernel)
6 changes: 6 additions & 0 deletions block/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,12 @@ config BLK_DEV_THROTTLING

See Documentation/cgroups/blkio-controller.txt for more information.

config CMDLINE_PARSER
bool "Block device command line partition parser"
default n
---help---
Parsing command line, get the partitions information.

menu "Partition Types"

source "block/partitions/Kconfig"
Expand Down
1 change: 1 addition & 0 deletions block/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ obj-$(CONFIG_IOSCHED_CFQ) += cfq-iosched.o

obj-$(CONFIG_BLOCK_COMPAT) += compat_ioctl.o
obj-$(CONFIG_BLK_DEV_INTEGRITY) += blk-integrity.o
obj-$(CONFIG_CMDLINE_PARSER) += cmdline-parser.o
250 changes: 250 additions & 0 deletions block/cmdline-parser.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
/*
* Parse command line, get partition information
*
* Written by Cai Zhiyong <[email protected]>
*
*/
#include <linux/buffer_head.h>
#include <linux/module.h>
#include <linux/cmdline-parser.h>

static int parse_subpart(struct cmdline_subpart **subpart, char *partdef)
{
int ret = 0;
struct cmdline_subpart *new_subpart;

*subpart = NULL;

new_subpart = kzalloc(sizeof(struct cmdline_subpart), GFP_KERNEL);
if (!new_subpart)
return -ENOMEM;

if (*partdef == '-') {
new_subpart->size = (sector_t)(~0ULL);
partdef++;
} else {
new_subpart->size = (sector_t)memparse(partdef, &partdef);
if (new_subpart->size < (sector_t)PAGE_SIZE) {
pr_warn("cmdline partition size is invalid.");
ret = -EINVAL;
goto fail;
}
}

if (*partdef == '@') {
partdef++;
new_subpart->from = (sector_t)memparse(partdef, &partdef);
} else {
new_subpart->from = (sector_t)(~0ULL);
}

if (*partdef == '(') {
int length;
char *next = strchr(++partdef, ')');

if (!next) {
pr_warn("cmdline partition format is invalid.");
ret = -EINVAL;
goto fail;
}

length = min_t(int, next - partdef,
sizeof(new_subpart->name) - 1);
strncpy(new_subpart->name, partdef, length);
new_subpart->name[length] = '\0';

partdef = ++next;
} else
new_subpart->name[0] = '\0';

new_subpart->flags = 0;

if (!strncmp(partdef, "ro", 2)) {
new_subpart->flags |= PF_RDONLY;
partdef += 2;
}

if (!strncmp(partdef, "lk", 2)) {
new_subpart->flags |= PF_POWERUP_LOCK;
partdef += 2;
}

*subpart = new_subpart;
return 0;
fail:
kfree(new_subpart);
return ret;
}

static void free_subpart(struct cmdline_parts *parts)
{
struct cmdline_subpart *subpart;

while (parts->subpart) {
subpart = parts->subpart;
parts->subpart = subpart->next_subpart;
kfree(subpart);
}
}

static int parse_parts(struct cmdline_parts **parts, const char *bdevdef)
{
int ret = -EINVAL;
char *next;
int length;
struct cmdline_subpart **next_subpart;
struct cmdline_parts *newparts;
char buf[BDEVNAME_SIZE + 32 + 4];

*parts = NULL;

newparts = kzalloc(sizeof(struct cmdline_parts), GFP_KERNEL);
if (!newparts)
return -ENOMEM;

next = strchr(bdevdef, ':');
if (!next) {
pr_warn("cmdline partition has no block device.");
goto fail;
}

length = min_t(int, next - bdevdef, sizeof(newparts->name) - 1);
strncpy(newparts->name, bdevdef, length);
newparts->name[length] = '\0';
newparts->nr_subparts = 0;

next_subpart = &newparts->subpart;

while (next && *(++next)) {
bdevdef = next;
next = strchr(bdevdef, ',');

length = (!next) ? (sizeof(buf) - 1) :
min_t(int, next - bdevdef, sizeof(buf) - 1);

strncpy(buf, bdevdef, length);
buf[length] = '\0';

ret = parse_subpart(next_subpart, buf);
if (ret)
goto fail;

newparts->nr_subparts++;
next_subpart = &(*next_subpart)->next_subpart;
}

if (!newparts->subpart) {
pr_warn("cmdline partition has no valid partition.");
ret = -EINVAL;
goto fail;
}

*parts = newparts;

return 0;
fail:
free_subpart(newparts);
kfree(newparts);
return ret;
}

void cmdline_parts_free(struct cmdline_parts **parts)
{
struct cmdline_parts *next_parts;

while (*parts) {
next_parts = (*parts)->next_parts;
free_subpart(*parts);
kfree(*parts);
*parts = next_parts;
}
}

int cmdline_parts_parse(struct cmdline_parts **parts, const char *cmdline)
{
int ret;
char *buf;
char *pbuf;
char *next;
struct cmdline_parts **next_parts;

*parts = NULL;

next = pbuf = buf = kstrdup(cmdline, GFP_KERNEL);
if (!buf)
return -ENOMEM;

next_parts = parts;

while (next && *pbuf) {
next = strchr(pbuf, ';');
if (next)
*next = '\0';

ret = parse_parts(next_parts, pbuf);
if (ret)
goto fail;

if (next)
pbuf = ++next;

next_parts = &(*next_parts)->next_parts;
}

if (!*parts) {
pr_warn("cmdline partition has no valid partition.");
ret = -EINVAL;
goto fail;
}

ret = 0;
done:
kfree(buf);
return ret;

fail:
cmdline_parts_free(parts);
goto done;
}

struct cmdline_parts *cmdline_parts_find(struct cmdline_parts *parts,
const char *bdev)
{
while (parts && strncmp(bdev, parts->name, sizeof(parts->name)))
parts = parts->next_parts;
return parts;
}

/*
* add_part()
* 0 success.
* 1 can not add so many partitions.
*/
void cmdline_parts_set(struct cmdline_parts *parts, sector_t disk_size,
int slot,
int (*add_part)(int, struct cmdline_subpart *, void *),
void *param)

{
sector_t from = 0;
struct cmdline_subpart *subpart;

for (subpart = parts->subpart; subpart;
subpart = subpart->next_subpart, slot++) {
if (subpart->from == (sector_t)(~0ULL))
subpart->from = from;
else
from = subpart->from;

if (from >= disk_size)
break;

if (subpart->size > (disk_size - from))
subpart->size = disk_size - from;

from += subpart->size;

if (add_part(slot, subpart, param))
break;
}
}
7 changes: 7 additions & 0 deletions block/partitions/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -260,3 +260,10 @@ config SYSV68_PARTITION
partition table format used by Motorola Delta machines (using
sysv68).
Otherwise, say N.

config CMDLINE_PARTITION
bool "Command line partition support" if PARTITION_ADVANCED
select CMDLINE_PARSER
help
Say Y here if you would read the partitions table from bootargs.
The format for the command line is just like mtdparts.
1 change: 1 addition & 0 deletions block/partitions/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ obj-$(CONFIG_ACORN_PARTITION) += acorn.o
obj-$(CONFIG_AMIGA_PARTITION) += amiga.o
obj-$(CONFIG_ATARI_PARTITION) += atari.o
obj-$(CONFIG_AIX_PARTITION) += aix.o
obj-$(CONFIG_CMDLINE_PARTITION) += cmdline.o
obj-$(CONFIG_MAC_PARTITION) += mac.o
obj-$(CONFIG_LDM_PARTITION) += ldm.o
obj-$(CONFIG_MSDOS_PARTITION) += msdos.o
Expand Down
4 changes: 4 additions & 0 deletions block/partitions/check.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include "efi.h"
#include "karma.h"
#include "sysv68.h"
#include "cmdline.h"

int warn_no_part = 1; /*This is ugly: should make genhd removable media aware*/

Expand Down Expand Up @@ -65,6 +66,9 @@ static int (*check_part[])(struct parsed_partitions *) = {
adfspart_check_ADFS,
#endif

#ifdef CONFIG_CMDLINE_PARTITION
cmdline_partition,
#endif
#ifdef CONFIG_EFI_PARTITION
efi_partition, /* this must come before msdos */
#endif
Expand Down
Loading

0 comments on commit bab5541

Please sign in to comment.