Message ID | 20201217112739.5045-7-m.szyprowski@samsung.com |
---|---|
State | New |
Headers | show |
Series | Add MBR partition table creation and verify command | expand |
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" >+);
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 --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" +);