diff mbox series

[6/6] cmd: Add MBR partition layout control utility

Message ID 20201217112739.5045-7-m.szyprowski@samsung.com
State New
Headers show
Series Add MBR partition table creation and verify command | expand

Commit Message

Marek Szyprowski Dec. 17, 2020, 11:27 a.m. UTC
Add a 'mbr' command to let user create or verify MBR partition layout
based on the provided text description. The partition layout is
altearnatively read from 'mbr_parts' environment variable. This can be
used in scripts to help system image flashing tools to ensure proper
partition layout.

The syntax of the text description of the partition list is similar to
the one used by the 'gpt' command. Supported parameters are: name
(currently ignored), start (partition start offset in bytes), size (in
bytes or '-' to expand it to the whole free area), bootable (boolean flag)
and id (MBR partition system ID). If one wants to create more than 4
partitions, an 'Extended' primary partition (with 0x05 ID) has to be
explicitely provided as a one of the first 4 entries.

Here is the example how to create a 6 partitions (3 on the 'extended
volume'), some of the predefined sizes:

> setenv mbr_parts 'name=boot,start=4M,size=128M,bootable,id=0x0e;

  name=rootfs,size=3072M,id=0x83;
  name=system-data,size=512M,id=0x83;
  name=[ext],size=-,id=0x05;
  name=user,size=-,id=0x83;
  name=modules,size=100M,id=0x83;
  name=ramdisk,size=8M,id=0x83'
> mbr write mmc 0


Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>

---
 cmd/Kconfig  |   8 ++
 cmd/Makefile |   1 +
 cmd/mbr.c    | 308 +++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 317 insertions(+)
 create mode 100644 cmd/mbr.c

-- 
2.17.1

Comments

Heinrich Schuchardt Dec. 18, 2020, 12:42 a.m. UTC | #1
Am 17. Dezember 2020 12:27:39 MEZ schrieb Marek Szyprowski <m.szyprowski@samsung.com>:
>Add a 'mbr' command to let user create or verify MBR partition layout

>based on the provided text description. The partition layout is

>altearnatively read from 'mbr_parts' environment variable. This can be

>used in scripts to help system image flashing tools to ensure proper

>partition layout.

>

>The syntax of the text description of the partition list is similar to

>the one used by the 'gpt' command. Supported parameters are: name

>(currently ignored), start (partition start offset in bytes), size (in

>bytes or '-' to expand it to the whole free area), bootable (boolean

>flag)

>and id (MBR partition system ID). If one wants to create more than 4

>partitions, an 'Extended' primary partition (with 0x05 ID) has to be

>explicitely provided as a one of the first 4 entries.

>

>Here is the example how to create a 6 partitions (3 on the 'extended

>volume'), some of the predefined sizes:

>

>> setenv mbr_parts 'name=boot,start=4M,size=128M,bootable,id=0x0e;

>  name=rootfs,size=3072M,id=0x83;

>  name=system-data,size=512M,id=0x83;

>  name=[ext],size=-,id=0x05;

>  name=user,size=-,id=0x83;

>  name=modules,size=100M,id=0x83;

>  name=ramdisk,size=8M,id=0x83'

>> mbr write mmc 0


It is good to have this information in the commit message. But we cannot expect a user to look at commit messages.

Please, provide a man-page in doc/usage/. You can use this patch as template:

https://lists.denx.de/pipermail/u-boot/2020-December/435144.html

Use 'make htmldocs' to build and check the documentation.

Once  your patch is merged the documentation will be published at

https://u-boot.readthedocs.io/

Best regards

Heinrich

>

>Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>

>---

> cmd/Kconfig  |   8 ++

> cmd/Makefile |   1 +

> cmd/mbr.c    | 308 +++++++++++++++++++++++++++++++++++++++++++++++++++

> 3 files changed, 317 insertions(+)

> create mode 100644 cmd/mbr.c

>

>diff --git a/cmd/Kconfig b/cmd/Kconfig

>index 1595de999b..2c3358e359 100644

>--- a/cmd/Kconfig

>+++ b/cmd/Kconfig

>@@ -1025,6 +1025,14 @@ config CMD_LSBLK

>	  Print list of available block device drivers, and for each, the list

> 	  of known block devices.

> 

>+config CMD_MBR

>+	bool "MBR (Master Boot Record) command"

>+	select DOS_PARTITION

>+	select HAVE_BLOCK_DEVICE

>+	help

>+	  Enable the 'mbr' command to ready and write MBR (Master Boot

>Record)

>+	  style partition tables.

>+

> config CMD_MISC

> 	bool "misc"

> 	depends on MISC

>diff --git a/cmd/Makefile b/cmd/Makefile

>index dd86675bf2..41379d9a0e 100644

>--- a/cmd/Makefile

>+++ b/cmd/Makefile

>@@ -178,6 +178,7 @@ obj-$(CONFIG_CMD_ZFS) += zfs.o

> 

> obj-$(CONFIG_CMD_DFU) += dfu.o

> obj-$(CONFIG_CMD_GPT) += gpt.o

>+obj-$(CONFIG_CMD_MBR) += mbr.o

> obj-$(CONFIG_CMD_ETHSW) += ethsw.o

> obj-$(CONFIG_CMD_AXI) += axi.o

> obj-$(CONFIG_CMD_PVBLOCK) += pvblock.o

>diff --git a/cmd/mbr.c b/cmd/mbr.c

>new file mode 100644

>index 0000000000..25a3f694d3

>--- /dev/null

>+++ b/cmd/mbr.c

>@@ -0,0 +1,308 @@

>+// SPDX-License-Identifier: GPL-2.0+

>+/*

>+ * cmd_mbr.c -- MBR (Master Boot Record) handling command

>+ *

>+ * Copyright (C) 2020 Samsung Electronics

>+ * author: Marek Szyprowski <m.szyprowski@samsung.com>

>+ *

>+ * based on the gpt command.

>+ */

>+

>+#include <common.h>

>+#include <blk.h>

>+#include <malloc.h>

>+#include <command.h>

>+#include <part.h>

>+

>+/**

>+ * extract_val(): Extract value from a key=value pair list (comma

>separated).

>+ *                Only value for the given key is returend.

>+ *                Function allocates memory for the value, remember to

>free!

>+ *

>+ * @param str - pointer to string with key=values pairs

>+ * @param key - pointer to the key to search for

>+ *

>+ * @return - pointer to allocated string with the value

>+ */

>+static char *extract_val(const char *str, const char *key)

>+{

>+	char *v, *k;

>+	char *s, *strcopy;

>+	char *new = NULL;

>+

>+	strcopy = strdup(str);

>+	if (strcopy == NULL)

>+		return NULL;

>+

>+	s = strcopy;

>+	while (s) {

>+		v = strsep(&s, ",");

>+		if (!v)

>+			break;

>+		k = strsep(&v, "=");

>+		if (!k)

>+			break;

>+		if  (strcmp(k, key) == 0) {

>+			new = strdup(v);

>+			break;

>+		}

>+	}

>+

>+	free(strcopy);

>+

>+	return new;

>+}

>+

>+/**

>+ * found_key(): Found key without value in parameter list (comma

>separated).

>+ *

>+ * @param str - pointer to string with key

>+ * @param key - pointer to the key to search for

>+ *

>+ * @return - true on found key

>+ */

>+static bool found_key(const char *str, const char *key)

>+{

>+	char *k;

>+	char *s, *strcopy;

>+	bool result = false;

>+

>+	strcopy = strdup(str);

>+	if (!strcopy)

>+		return NULL;

>+

>+	s = strcopy;

>+	while (s) {

>+		k = strsep(&s, ",");

>+		if (!k)

>+			break;

>+		if  (strcmp(k, key) == 0) {

>+			result = true;

>+			break;

>+		}

>+	}

>+

>+	free(strcopy);

>+

>+	return result;

>+}

>+

>+static int str_to_partition_info(const char *str_part, unsigned long

>*disk_uuid,

>+			struct disk_partition **partitions, int *parts_count)

>+{

>+	char *tok, *str, *s;

>+	int i;

>+	char *val, *p;

>+	int p_count;

>+	struct disk_partition *parts;

>+	int errno = 0;

>+	uint64_t size_ll, start_ll;

>+

>+	if (str_part == NULL)

>+		return -1;

>+

>+	str = strdup(str_part);

>+	if (str == NULL)

>+		return -ENOMEM;

>+

>+	/* extract disk guid */

>+	s = str;

>+	val = extract_val(str, "uuid_disk");

>+	if (val) {

>+		val = strsep(&val, ";");

>+		p = val;

>+		*disk_uuid = ustrtoull(p, &p, 0);

>+		free(val);

>+		/* Move s to first partition */

>+		strsep(&s, ";");

>+	}

>+	if (s == NULL) {

>+		printf("Error: is the partitions string NULL-terminated?\n");

>+		return -EINVAL;

>+	}

>+

>+	/* remove the optional semicolon at the end of the string */

>+	i = strlen(s) - 1;

>+	if (s[i] == ';')

>+		s[i] = '\0';

>+

>+	/* calculate expected number of partitions */

>+	p_count = 1;

>+	p = s;

>+	while (*p) {

>+		if (*p++ == ';')

>+			p_count++;

>+	}

>+

>+	/* allocate memory for partitions */

>+	parts = calloc(sizeof(struct disk_partition), p_count);

>+	if (parts == NULL)

>+		return -ENOMEM;

>+

>+	/* retrieve partitions data from string */

>+	for (i = 0; i < p_count; i++) {

>+		tok = strsep(&s, ";");

>+

>+		if (tok == NULL)

>+			break;

>+

>+		/* size */

>+		val = extract_val(tok, "size");

>+		if (!val) { /* 'size' is mandatory */

>+			errno = -4;

>+			goto err;

>+		}

>+		p = val;

>+		if ((strcmp(p, "-") == 0)) {

>+			/* auto extend the size */

>+			parts[i].size = 0;

>+		} else {

>+			size_ll = ustrtoull(p, &p, 0);

>+			parts[i].size = size_ll / 512;

>+		}

>+		free(val);

>+

>+		/* start address */

>+		val = extract_val(tok, "start");

>+		if (val) { /* start address is optional */

>+			p = val;

>+			start_ll = ustrtoull(p, &p, 0);

>+			parts[i].start = start_ll / 512;

>+			free(val);

>+		}

>+

>+		/* system id */

>+		val = extract_val(tok, "id");

>+		if (!val) { /* '' is mandatory */

>+			errno = -4;

>+			goto err;

>+		}

>+		p = val;

>+		parts[i].sys_ind = ustrtoul(p, &p, 0);

>+		free(val);

>+

>+		/* bootable */

>+		if (found_key(tok, "bootable"))

>+			parts[i].bootable = PART_BOOTABLE;

>+	}

>+

>+	*parts_count = p_count;

>+	*partitions = parts;

>+	free(str);

>+

>+	return 0;

>+err:

>+	free(str);

>+	free(parts);

>+

>+	return errno;

>+}

>+

>+static int do_write_mbr(struct blk_desc *dev, const char *str)

>+{

>+	unsigned long disk_uuid = 0;

>+	struct disk_partition *partitions;

>+	int count;

>+

>+	if (str_to_partition_info(str, &disk_uuid, &partitions, &count)) {

>+		printf("MBR: failed to setup partitions from \"%s\"\n", str);

>+		return -1;

>+	}

>+

>+	if (layout_mbr_partitions(partitions, count, dev->lba)) {

>+		printf("MBR: failed to layout partitions on the device\n");

>+		free(partitions);

>+		return -1;

>+	}

>+

>+	if (write_mbr_partitions(dev, partitions, count, disk_uuid)) {

>+		printf("MBR: failed to write partitions to the device\n");

>+		free(partitions);

>+		return -1;

>+	}

>+

>+	return 0;

>+}

>+

>+static int do_verify_mbr(struct blk_desc *dev, const char *str)

>+{

>+	unsigned long disk_uuid = 0;

>+	struct disk_partition *partitions;

>+	int count, i, ret = 1;

>+

>+	if (str_to_partition_info(str, &disk_uuid, &partitions, &count)) {

>+		printf("MBR: failed to setup partitions from \"%s\"\n", str);

>+		return -1;

>+	}

>+

>+	for (i = 0; i < count; i++) {

>+		struct disk_partition p;

>+

>+		if (part_get_info(dev, i+1, &p))

>+			goto fail;

>+

>+		if ((partitions[i].size && p.size < partitions[i].size) ||

>+		    (partitions[i].start && p.start < partitions[i].start) ||

>+		    (p.sys_ind != partitions[i].sys_ind))

>+			goto fail;

>+	}

>+	ret = 0;

>+fail:

>+	free(partitions);

>+	return ret;

>+}

>+

>+static int do_mbr(struct cmd_tbl *cmdtp, int flag, int argc, char

>*const argv[])

>+{

>+	const char *parts = NULL;

>+	int ret = CMD_RET_SUCCESS;

>+	int dev = 0;

>+	char *ep;

>+	struct blk_desc *blk_dev_desc = NULL;

>+

>+	if (argc != 4 && argc != 5)

>+		return CMD_RET_USAGE;

>+

>+	dev = (int)simple_strtoul(argv[3], &ep, 10);

>+	if (!ep || ep[0] != '\0') {

>+		printf("'%s' is not a number\n", argv[3]);

>+		return CMD_RET_USAGE;

>+	}

>+	blk_dev_desc = blk_get_dev(argv[2], dev);

>+	if (!blk_dev_desc) {

>+		printf("%s: %s dev %d NOT available\n",

>+		       __func__, argv[2], dev);

>+		return CMD_RET_FAILURE;

>+	}

>+

>+	if ((strcmp(argv[1], "write") == 0)) {

>+		parts = (argc == 5) ? argv[4] : env_get("mbr_parts");

>+		printf("MBR: write ");

>+		ret = do_write_mbr(blk_dev_desc, parts);

>+	} else if ((strcmp(argv[1], "verify") == 0)) {

>+		printf("MBR: verify ");

>+		parts = (argc == 5) ? argv[4] : env_get("mbr_parts");

>+		ret = do_verify_mbr(blk_dev_desc, parts);

>+	} else {

>+		return CMD_RET_USAGE;

>+	}

>+

>+	if (ret) {

>+		printf("error!\n");

>+		return CMD_RET_FAILURE;

>+	}

>+

>+	printf("success!\n");

>+	return CMD_RET_SUCCESS;

>+}

>+

>+U_BOOT_CMD(mbr, CONFIG_SYS_MAXARGS, 1, do_mbr,

>+	"MBR (Master Boot Record)",

>+	"<command> <interface> <dev> <partitions_list>\n"

>+	" - MBR partition table restoration utility\n"

>+	" Restore or check partition information on a device connected\n"

>+	" to the given block interface\n"

>+	" Example usage:\n"

>+	" mbr write mmc 0 [\"${mbr_parts}\"]\n"

>+	" mbr verify mmc 0 [\"${partitions}\"]\n"

>+);
Heinrich Schuchardt Dec. 18, 2020, 1:04 a.m. UTC | #2
Am 17. Dezember 2020 12:27:39 MEZ schrieb Marek Szyprowski <m.szyprowski@samsung.com>:
>Add a 'mbr' command to let user create or verify MBR partition layout

>based on the provided text description. The partition layout is

>altearnatively read from 'mbr_parts' environment variable. This can be

>used in scripts to help system image flashing tools to ensure proper

>partition layout.

>

>The syntax of the text description of the partition list is similar to

>the one used by the 'gpt' command. Supported parameters are: name

>(currently ignored), start (partition start offset in bytes), size (in

>bytes or '-' to expand it to the whole free area), bootable (boolean

>flag)

>and id (MBR partition system ID). If one wants to create more than 4

>partitions, an 'Extended' primary partition (with 0x05 ID) has to be

>explicitely provided as a one of the first 4 entries.

>

>Here is the example how to create a 6 partitions (3 on the 'extended

>volume'), some of the predefined sizes:

>

>> setenv mbr_parts 'name=boot,start=4M,size=128M,bootable,id=0x0e;

>  name=rootfs,size=3072M,id=0x83;

>  name=system-data,size=512M,id=0x83;

>  name=[ext],size=-,id=0x05;

>  name=user,size=-,id=0x83;

>  name=modules,size=100M,id=0x83;

>  name=ramdisk,size=8M,id=0x83'

>> mbr write mmc 0

>

>Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>

>---

> cmd/Kconfig  |   8 ++

> cmd/Makefile |   1 +

> cmd/mbr.c    | 308 +++++++++++++++++++++++++++++++++++++++++++++++++++

> 3 files changed, 317 insertions(+)

> create mode 100644 cmd/mbr.c

>

>diff --git a/cmd/Kconfig b/cmd/Kconfig

>index 1595de999b..2c3358e359 100644

>--- a/cmd/Kconfig

>+++ b/cmd/Kconfig

>@@ -1025,6 +1025,14 @@ config CMD_LSBLK

>	  Print list of available block device drivers, and for each, the list

> 	  of known block devices.

> 

>+config CMD_MBR

>+	bool "MBR (Master Boot Record) command"

>+	select DOS_PARTITION

>+	select HAVE_BLOCK_DEVICE

>+	help

>+	  Enable the 'mbr' command to ready and write MBR (Master Boot

>Record)

>+	  style partition tables.

>+

> config CMD_MISC

> 	bool "misc"

> 	depends on MISC

>diff --git a/cmd/Makefile b/cmd/Makefile

>index dd86675bf2..41379d9a0e 100644

>--- a/cmd/Makefile

>+++ b/cmd/Makefile

>@@ -178,6 +178,7 @@ obj-$(CONFIG_CMD_ZFS) += zfs.o

> 

> obj-$(CONFIG_CMD_DFU) += dfu.o

> obj-$(CONFIG_CMD_GPT) += gpt.o

>+obj-$(CONFIG_CMD_MBR) += mbr.o

> obj-$(CONFIG_CMD_ETHSW) += ethsw.o

> obj-$(CONFIG_CMD_AXI) += axi.o

> obj-$(CONFIG_CMD_PVBLOCK) += pvblock.o

>diff --git a/cmd/mbr.c b/cmd/mbr.c

>new file mode 100644

>index 0000000000..25a3f694d3

>--- /dev/null

>+++ b/cmd/mbr.c

>@@ -0,0 +1,308 @@

>+// SPDX-License-Identifier: GPL-2.0+

>+/*

>+ * cmd_mbr.c -- MBR (Master Boot Record) handling command

>+ *

>+ * Copyright (C) 2020 Samsung Electronics

>+ * author: Marek Szyprowski <m.szyprowski@samsung.com>

>+ *

>+ * based on the gpt command.

>+ */

>+

>+#include <common.h>

>+#include <blk.h>

>+#include <malloc.h>

>+#include <command.h>

>+#include <part.h>

>+

>+/**

>+ * extract_val(): Extract value from a key=value pair list (comma

>separated).

>+ *                Only value for the given key is returend.

>+ *                Function allocates memory for the value, remember to

>free!

>+ *

>+ * @param str - pointer to string with key=values pairs

>+ * @param key - pointer to the key to search for

>+ *

>+ * @return - pointer to allocated string with the value


Please, format your comments as described in

https://www.kernel.org/doc/html/latest/doc-guide/kernel-doc.html#function-documentation






>+ */

>+static char *extract_val(const char *str, const char *key)

>+{

>+	char *v, *k;

>+	char *s, *strcopy;

>+	char *new = NULL;

>+

>+	strcopy = strdup(str);

>+	if (strcopy == NULL)

>+		return NULL;

>+

>+	s = strcopy;

>+	while (s) {

>+		v = strsep(&s, ",");

>+		if (!v)

>+			break;

>+		k = strsep(&v, "=");

>+		if (!k)

>+			break;

>+		if  (strcmp(k, key) == 0) {

>+			new = strdup(v);

>+			break;

>+		}

>+	}

>+

>+	free(strcopy);

>+

>+	return new;

>+}

>+

>+/**

>+ * found_key(): Found key without value in parameter list (comma

>separated).

>+ *

>+ * @param str - pointer to string with key

>+ * @param key - pointer to the key to search for

>+ *

>+ * @return - true on found key

>+ */

>+static bool found_key(const char *str, const char *key)

>+{

>+	char *k;

>+	char *s, *strcopy;

>+	bool result = false;

>+

>+	strcopy = strdup(str);

>+	if (!strcopy)

>+		return NULL;

>+

>+	s = strcopy;

>+	while (s) {

>+		k = strsep(&s, ",");

>+		if (!k)

>+			break;

>+		if  (strcmp(k, key) == 0) {

>+			result = true;

>+			break;

>+		}

>+	}

>+

>+	free(strcopy);

>+

>+	return result;

>+}

>+

>+static int str_to_partition_info(const char *str_part, unsigned long

>*disk_uuid,

>+			struct disk_partition **partitions, int *parts_count)

>+{

>+	char *tok, *str, *s;

>+	int i;

>+	char *val, *p;

>+	int p_count;

>+	struct disk_partition *parts;

>+	int errno = 0;

>+	uint64_t size_ll, start_ll;

>+

>+	if (str_part == NULL)

>+		return -1;

>+

>+	str = strdup(str_part);

>+	if (str == NULL)

>+		return -ENOMEM;

>+

>+	/* extract disk guid */

>+	s = str;

>+	val = extract_val(str, "uuid_disk");

>+	if (val) {

>+		val = strsep(&val, ";");

>+		p = val;

>+		*disk_uuid = ustrtoull(p, &p, 0);

>+		free(val);

>+		/* Move s to first partition */

>+		strsep(&s, ";");

>+	}

>+	if (s == NULL) {

>+		printf("Error: is the partitions string NULL-terminated?\n");

>+		return -EINVAL;

>+	}

>+

>+	/* remove the optional semicolon at the end of the string */

>+	i = strlen(s) - 1;

>+	if (s[i] == ';')

>+		s[i] = '\0';

>+

>+	/* calculate expected number of partitions */

>+	p_count = 1;

>+	p = s;

>+	while (*p) {

>+		if (*p++ == ';')

>+			p_count++;

>+	}

>+

>+	/* allocate memory for partitions */

>+	parts = calloc(sizeof(struct disk_partition), p_count);

>+	if (parts == NULL)

>+		return -ENOMEM;

>+

>+	/* retrieve partitions data from string */

>+	for (i = 0; i < p_count; i++) {

>+		tok = strsep(&s, ";");

>+

>+		if (tok == NULL)

>+			break;

>+

>+		/* size */

>+		val = extract_val(tok, "size");

>+		if (!val) { /* 'size' is mandatory */

>+			errno = -4;

>+			goto err;

>+		}

>+		p = val;

>+		if ((strcmp(p, "-") == 0)) {

>+			/* auto extend the size */

>+			parts[i].size = 0;

>+		} else {

>+			size_ll = ustrtoull(p, &p, 0);

>+			parts[i].size = size_ll / 512;


The sector size may have other values than 512.


>+		}

>+		free(val);

>+

>+		/* start address */

>+		val = extract_val(tok, "start");

>+		if (val) { /* start address is optional */

>+			p = val;

>+			start_ll = ustrtoull(p, &p, 0);

>+			parts[i].start = start_ll / 512;


Same here.

Best regards

Heinrich


>+			free(val);

>+		}

>+

>+		/* system id */

>+		val = extract_val(tok, "id");

>+		if (!val) { /* '' is mandatory */

>+			errno = -4;

>+			goto err;

>+		}

>+		p = val;

>+		parts[i].sys_ind = ustrtoul(p, &p, 0);

>+		free(val);

>+

>+		/* bootable */

>+		if (found_key(tok, "bootable"))

>+			parts[i].bootable = PART_BOOTABLE;

>+	}

>+

>+	*parts_count = p_count;

>+	*partitions = parts;

>+	free(str);

>+

>+	return 0;

>+err:

>+	free(str);

>+	free(parts);

>+

>+	return errno;

>+}

>+

>+static int do_write_mbr(struct blk_desc *dev, const char *str)

>+{

>+	unsigned long disk_uuid = 0;

>+	struct disk_partition *partitions;

>+	int count;

>+

>+	if (str_to_partition_info(str, &disk_uuid, &partitions, &count)) {

>+		printf("MBR: failed to setup partitions from \"%s\"\n", str);

>+		return -1;

>+	}

>+

>+	if (layout_mbr_partitions(partitions, count, dev->lba)) {

>+		printf("MBR: failed to layout partitions on the device\n");

>+		free(partitions);

>+		return -1;

>+	}

>+

>+	if (write_mbr_partitions(dev, partitions, count, disk_uuid)) {

>+		printf("MBR: failed to write partitions to the device\n");

>+		free(partitions);

>+		return -1;

>+	}

>+

>+	return 0;

>+}

>+

>+static int do_verify_mbr(struct blk_desc *dev, const char *str)

>+{

>+	unsigned long disk_uuid = 0;

>+	struct disk_partition *partitions;

>+	int count, i, ret = 1;

>+

>+	if (str_to_partition_info(str, &disk_uuid, &partitions, &count)) {

>+		printf("MBR: failed to setup partitions from \"%s\"\n", str);

>+		return -1;

>+	}

>+

>+	for (i = 0; i < count; i++) {

>+		struct disk_partition p;

>+

>+		if (part_get_info(dev, i+1, &p))

>+			goto fail;

>+

>+		if ((partitions[i].size && p.size < partitions[i].size) ||

>+		    (partitions[i].start && p.start < partitions[i].start) ||

>+		    (p.sys_ind != partitions[i].sys_ind))

>+			goto fail;

>+	}

>+	ret = 0;

>+fail:

>+	free(partitions);

>+	return ret;

>+}

>+

>+static int do_mbr(struct cmd_tbl *cmdtp, int flag, int argc, char

>*const argv[])

>+{

>+	const char *parts = NULL;

>+	int ret = CMD_RET_SUCCESS;

>+	int dev = 0;

>+	char *ep;

>+	struct blk_desc *blk_dev_desc = NULL;

>+

>+	if (argc != 4 && argc != 5)

>+		return CMD_RET_USAGE;

>+

>+	dev = (int)simple_strtoul(argv[3], &ep, 10);

>+	if (!ep || ep[0] != '\0') {

>+		printf("'%s' is not a number\n", argv[3]);

>+		return CMD_RET_USAGE;

>+	}

>+	blk_dev_desc = blk_get_dev(argv[2], dev);

>+	if (!blk_dev_desc) {

>+		printf("%s: %s dev %d NOT available\n",

>+		       __func__, argv[2], dev);

>+		return CMD_RET_FAILURE;

>+	}

>+

>+	if ((strcmp(argv[1], "write") == 0)) {

>+		parts = (argc == 5) ? argv[4] : env_get("mbr_parts");

>+		printf("MBR: write ");

>+		ret = do_write_mbr(blk_dev_desc, parts);

>+	} else if ((strcmp(argv[1], "verify") == 0)) {

>+		printf("MBR: verify ");

>+		parts = (argc == 5) ? argv[4] : env_get("mbr_parts");

>+		ret = do_verify_mbr(blk_dev_desc, parts);

>+	} else {

>+		return CMD_RET_USAGE;

>+	}

>+

>+	if (ret) {

>+		printf("error!\n");

>+		return CMD_RET_FAILURE;

>+	}

>+

>+	printf("success!\n");

>+	return CMD_RET_SUCCESS;

>+}

>+

>+U_BOOT_CMD(mbr, CONFIG_SYS_MAXARGS, 1, do_mbr,

>+	"MBR (Master Boot Record)",

>+	"<command> <interface> <dev> <partitions_list>\n"

>+	" - MBR partition table restoration utility\n"

>+	" Restore or check partition information on a device connected\n"

>+	" to the given block interface\n"

>+	" Example usage:\n"

>+	" mbr write mmc 0 [\"${mbr_parts}\"]\n"

>+	" mbr verify mmc 0 [\"${partitions}\"]\n"

>+);
diff mbox series

Patch

diff --git a/cmd/Kconfig b/cmd/Kconfig
index 1595de999b..2c3358e359 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -1025,6 +1025,14 @@  config CMD_LSBLK
 	  Print list of available block device drivers, and for each, the list
 	  of known block devices.
 
+config CMD_MBR
+	bool "MBR (Master Boot Record) command"
+	select DOS_PARTITION
+	select HAVE_BLOCK_DEVICE
+	help
+	  Enable the 'mbr' command to ready and write MBR (Master Boot Record)
+	  style partition tables.
+
 config CMD_MISC
 	bool "misc"
 	depends on MISC
diff --git a/cmd/Makefile b/cmd/Makefile
index dd86675bf2..41379d9a0e 100644
--- a/cmd/Makefile
+++ b/cmd/Makefile
@@ -178,6 +178,7 @@  obj-$(CONFIG_CMD_ZFS) += zfs.o
 
 obj-$(CONFIG_CMD_DFU) += dfu.o
 obj-$(CONFIG_CMD_GPT) += gpt.o
+obj-$(CONFIG_CMD_MBR) += mbr.o
 obj-$(CONFIG_CMD_ETHSW) += ethsw.o
 obj-$(CONFIG_CMD_AXI) += axi.o
 obj-$(CONFIG_CMD_PVBLOCK) += pvblock.o
diff --git a/cmd/mbr.c b/cmd/mbr.c
new file mode 100644
index 0000000000..25a3f694d3
--- /dev/null
+++ b/cmd/mbr.c
@@ -0,0 +1,308 @@ 
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * cmd_mbr.c -- MBR (Master Boot Record) handling command
+ *
+ * Copyright (C) 2020 Samsung Electronics
+ * author: Marek Szyprowski <m.szyprowski@samsung.com>
+ *
+ * based on the gpt command.
+ */
+
+#include <common.h>
+#include <blk.h>
+#include <malloc.h>
+#include <command.h>
+#include <part.h>
+
+/**
+ * extract_val(): Extract value from a key=value pair list (comma separated).
+ *                Only value for the given key is returend.
+ *                Function allocates memory for the value, remember to free!
+ *
+ * @param str - pointer to string with key=values pairs
+ * @param key - pointer to the key to search for
+ *
+ * @return - pointer to allocated string with the value
+ */
+static char *extract_val(const char *str, const char *key)
+{
+	char *v, *k;
+	char *s, *strcopy;
+	char *new = NULL;
+
+	strcopy = strdup(str);
+	if (strcopy == NULL)
+		return NULL;
+
+	s = strcopy;
+	while (s) {
+		v = strsep(&s, ",");
+		if (!v)
+			break;
+		k = strsep(&v, "=");
+		if (!k)
+			break;
+		if  (strcmp(k, key) == 0) {
+			new = strdup(v);
+			break;
+		}
+	}
+
+	free(strcopy);
+
+	return new;
+}
+
+/**
+ * found_key(): Found key without value in parameter list (comma separated).
+ *
+ * @param str - pointer to string with key
+ * @param key - pointer to the key to search for
+ *
+ * @return - true on found key
+ */
+static bool found_key(const char *str, const char *key)
+{
+	char *k;
+	char *s, *strcopy;
+	bool result = false;
+
+	strcopy = strdup(str);
+	if (!strcopy)
+		return NULL;
+
+	s = strcopy;
+	while (s) {
+		k = strsep(&s, ",");
+		if (!k)
+			break;
+		if  (strcmp(k, key) == 0) {
+			result = true;
+			break;
+		}
+	}
+
+	free(strcopy);
+
+	return result;
+}
+
+static int str_to_partition_info(const char *str_part, unsigned long *disk_uuid,
+			struct disk_partition **partitions, int *parts_count)
+{
+	char *tok, *str, *s;
+	int i;
+	char *val, *p;
+	int p_count;
+	struct disk_partition *parts;
+	int errno = 0;
+	uint64_t size_ll, start_ll;
+
+	if (str_part == NULL)
+		return -1;
+
+	str = strdup(str_part);
+	if (str == NULL)
+		return -ENOMEM;
+
+	/* extract disk guid */
+	s = str;
+	val = extract_val(str, "uuid_disk");
+	if (val) {
+		val = strsep(&val, ";");
+		p = val;
+		*disk_uuid = ustrtoull(p, &p, 0);
+		free(val);
+		/* Move s to first partition */
+		strsep(&s, ";");
+	}
+	if (s == NULL) {
+		printf("Error: is the partitions string NULL-terminated?\n");
+		return -EINVAL;
+	}
+
+	/* remove the optional semicolon at the end of the string */
+	i = strlen(s) - 1;
+	if (s[i] == ';')
+		s[i] = '\0';
+
+	/* calculate expected number of partitions */
+	p_count = 1;
+	p = s;
+	while (*p) {
+		if (*p++ == ';')
+			p_count++;
+	}
+
+	/* allocate memory for partitions */
+	parts = calloc(sizeof(struct disk_partition), p_count);
+	if (parts == NULL)
+		return -ENOMEM;
+
+	/* retrieve partitions data from string */
+	for (i = 0; i < p_count; i++) {
+		tok = strsep(&s, ";");
+
+		if (tok == NULL)
+			break;
+
+		/* size */
+		val = extract_val(tok, "size");
+		if (!val) { /* 'size' is mandatory */
+			errno = -4;
+			goto err;
+		}
+		p = val;
+		if ((strcmp(p, "-") == 0)) {
+			/* auto extend the size */
+			parts[i].size = 0;
+		} else {
+			size_ll = ustrtoull(p, &p, 0);
+			parts[i].size = size_ll / 512;
+		}
+		free(val);
+
+		/* start address */
+		val = extract_val(tok, "start");
+		if (val) { /* start address is optional */
+			p = val;
+			start_ll = ustrtoull(p, &p, 0);
+			parts[i].start = start_ll / 512;
+			free(val);
+		}
+
+		/* system id */
+		val = extract_val(tok, "id");
+		if (!val) { /* '' is mandatory */
+			errno = -4;
+			goto err;
+		}
+		p = val;
+		parts[i].sys_ind = ustrtoul(p, &p, 0);
+		free(val);
+
+		/* bootable */
+		if (found_key(tok, "bootable"))
+			parts[i].bootable = PART_BOOTABLE;
+	}
+
+	*parts_count = p_count;
+	*partitions = parts;
+	free(str);
+
+	return 0;
+err:
+	free(str);
+	free(parts);
+
+	return errno;
+}
+
+static int do_write_mbr(struct blk_desc *dev, const char *str)
+{
+	unsigned long disk_uuid = 0;
+	struct disk_partition *partitions;
+	int count;
+
+	if (str_to_partition_info(str, &disk_uuid, &partitions, &count)) {
+		printf("MBR: failed to setup partitions from \"%s\"\n", str);
+		return -1;
+	}
+
+	if (layout_mbr_partitions(partitions, count, dev->lba)) {
+		printf("MBR: failed to layout partitions on the device\n");
+		free(partitions);
+		return -1;
+	}
+
+	if (write_mbr_partitions(dev, partitions, count, disk_uuid)) {
+		printf("MBR: failed to write partitions to the device\n");
+		free(partitions);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int do_verify_mbr(struct blk_desc *dev, const char *str)
+{
+	unsigned long disk_uuid = 0;
+	struct disk_partition *partitions;
+	int count, i, ret = 1;
+
+	if (str_to_partition_info(str, &disk_uuid, &partitions, &count)) {
+		printf("MBR: failed to setup partitions from \"%s\"\n", str);
+		return -1;
+	}
+
+	for (i = 0; i < count; i++) {
+		struct disk_partition p;
+
+		if (part_get_info(dev, i+1, &p))
+			goto fail;
+
+		if ((partitions[i].size && p.size < partitions[i].size) ||
+		    (partitions[i].start && p.start < partitions[i].start) ||
+		    (p.sys_ind != partitions[i].sys_ind))
+			goto fail;
+	}
+	ret = 0;
+fail:
+	free(partitions);
+	return ret;
+}
+
+static int do_mbr(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+	const char *parts = NULL;
+	int ret = CMD_RET_SUCCESS;
+	int dev = 0;
+	char *ep;
+	struct blk_desc *blk_dev_desc = NULL;
+
+	if (argc != 4 && argc != 5)
+		return CMD_RET_USAGE;
+
+	dev = (int)simple_strtoul(argv[3], &ep, 10);
+	if (!ep || ep[0] != '\0') {
+		printf("'%s' is not a number\n", argv[3]);
+		return CMD_RET_USAGE;
+	}
+	blk_dev_desc = blk_get_dev(argv[2], dev);
+	if (!blk_dev_desc) {
+		printf("%s: %s dev %d NOT available\n",
+		       __func__, argv[2], dev);
+		return CMD_RET_FAILURE;
+	}
+
+	if ((strcmp(argv[1], "write") == 0)) {
+		parts = (argc == 5) ? argv[4] : env_get("mbr_parts");
+		printf("MBR: write ");
+		ret = do_write_mbr(blk_dev_desc, parts);
+	} else if ((strcmp(argv[1], "verify") == 0)) {
+		printf("MBR: verify ");
+		parts = (argc == 5) ? argv[4] : env_get("mbr_parts");
+		ret = do_verify_mbr(blk_dev_desc, parts);
+	} else {
+		return CMD_RET_USAGE;
+	}
+
+	if (ret) {
+		printf("error!\n");
+		return CMD_RET_FAILURE;
+	}
+
+	printf("success!\n");
+	return CMD_RET_SUCCESS;
+}
+
+U_BOOT_CMD(mbr, CONFIG_SYS_MAXARGS, 1, do_mbr,
+	"MBR (Master Boot Record)",
+	"<command> <interface> <dev> <partitions_list>\n"
+	" - MBR partition table restoration utility\n"
+	" Restore or check partition information on a device connected\n"
+	" to the given block interface\n"
+	" Example usage:\n"
+	" mbr write mmc 0 [\"${mbr_parts}\"]\n"
+	" mbr verify mmc 0 [\"${partitions}\"]\n"
+);