[PATCH 0/6] Add MBR partition table creation and verify command

Hi All,
This patchset adds 'mbr' command to let one create or verify MBR (Master Boot Record) partition layout based on the provided text description. This can be used in scripts to help system flashing tools/scripts to ensure proper partition layout. It has been inspired by the 'gpt' command already present in u-boot.
Best regards Marek Szyprowski Samsung R&D Institute Poland
Marek Szyprowski (6): disk: dos: rename write_mbr_partition to write_mbr_sector disk: dos: add some defines for the hardcoded numbers disk: dos: use generic macro for unaligned le32 access disk: dos: make some functions static disk: dos: add code for creating MBR partition layout cmd: Add MBR partition layout control utility
cmd/Kconfig | 8 + cmd/Makefile | 1 + cmd/mbr.c | 308 ++++++++++++++++++++++++++++++++++++++ disk/part_dos.c | 207 +++++++++++++++++++++---- disk/part_dos.h | 5 + drivers/fastboot/fb_mmc.c | 2 +- include/part.h | 9 +- 7 files changed, 512 insertions(+), 28 deletions(-) create mode 100644 cmd/mbr.c

write_mbr_partition() function name is a bit misleading, so rename it to write_mbr_sector(). This is a preparation for adding code for writing a complete MBR partition layout.
Signed-off-by: Marek Szyprowski m.szyprowski@samsung.com --- disk/part_dos.c | 2 +- drivers/fastboot/fb_mmc.c | 2 +- include/part.h | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/disk/part_dos.c b/disk/part_dos.c index 04f53106f7..ef706fb59c 100644 --- a/disk/part_dos.c +++ b/disk/part_dos.c @@ -329,7 +329,7 @@ int is_valid_dos_buf(void *buf) return test_block_type(buf) == DOS_MBR ? 0 : -1; }
-int write_mbr_partition(struct blk_desc *dev_desc, void *buf) +int write_mbr_sector(struct blk_desc *dev_desc, void *buf) { if (is_valid_dos_buf(buf)) return -1; diff --git a/drivers/fastboot/fb_mmc.c b/drivers/fastboot/fb_mmc.c index ae8e8e512f..4e26cef941 100644 --- a/drivers/fastboot/fb_mmc.c +++ b/drivers/fastboot/fb_mmc.c @@ -508,7 +508,7 @@ void fastboot_mmc_flash_write(const char *cmd, void *download_buffer, fastboot_fail("invalid MBR partition", response); return; } - if (write_mbr_partition(dev_desc, download_buffer)) { + if (write_mbr_sector(dev_desc, download_buffer)) { printf("%s: writing MBR partition failed\n", __func__); fastboot_fail("writing MBR partition failed", response); diff --git a/include/part.h b/include/part.h index 55be724d20..67b8b2a5cc 100644 --- a/include/part.h +++ b/include/part.h @@ -465,14 +465,14 @@ int get_disk_guid(struct blk_desc *dev_desc, char *guid); int is_valid_dos_buf(void *buf);
/** - * write_mbr_partition() - write DOS MBR + * write_mbr_sector() - write DOS MBR * * @param dev_desc - block device descriptor * @param buf - buffer which contains the MBR * * @return - '0' on success, otherwise error */ -int write_mbr_partition(struct blk_desc *dev_desc, void *buf); +int write_mbr_sector(struct blk_desc *dev_desc, void *buf);
#endif

Add some handy defines for some hardcoded magic numbers related to extended partition handling.
Signed-off-by: Marek Szyprowski m.szyprowski@samsung.com --- disk/part_dos.c | 6 +++--- disk/part_dos.h | 3 +++ 2 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/disk/part_dos.c b/disk/part_dos.c index ef706fb59c..20d35dc9cd 100644 --- a/disk/part_dos.c +++ b/disk/part_dos.c @@ -42,9 +42,9 @@ static inline unsigned int le32_to_int(unsigned char *le32)
static inline int is_extended(int part_type) { - return (part_type == 0x5 || - part_type == 0xf || - part_type == 0x85); + return (part_type == DOS_PART_TYPE_EXTENDED || + part_type == DOS_PART_TYPE_EXTENDED_LBA || + part_type == DOS_PART_TYPE_EXTENDED_LINUX); }
static int get_bootable(dos_partition_t *p) diff --git a/disk/part_dos.h b/disk/part_dos.h index 434b021ae8..dd909a9317 100644 --- a/disk/part_dos.h +++ b/disk/part_dos.h @@ -15,6 +15,9 @@ #define DOS_PBR_MEDIA_TYPE_OFFSET 0x15 #define DOS_MBR 0 #define DOS_PBR 1 +#define DOS_PART_TYPE_EXTENDED 0x05 +#define DOS_PART_TYPE_EXTENDED_LBA 0x0F +#define DOS_PART_TYPE_EXTENDED_LINUX 0x85
typedef struct dos_partition { unsigned char boot_ind; /* 0x80 - active */

Use a generic helper for reading LE32 integers.
Signed-off-by: Marek Szyprowski m.szyprowski@samsung.com --- disk/part_dos.c | 28 +++++++++------------------- 1 file changed, 9 insertions(+), 19 deletions(-)
diff --git a/disk/part_dos.c b/disk/part_dos.c index 20d35dc9cd..3b79b9b1b8 100644 --- a/disk/part_dos.c +++ b/disk/part_dos.c @@ -18,6 +18,7 @@ #include <command.h> #include <ide.h> #include <memalign.h> +#include <asm/unaligned.h> #include "part_dos.h" #include <part.h>
@@ -29,17 +30,6 @@ * to use large numbers of partitions */ #define MAX_EXT_PARTS 256
-/* Convert char[4] in little endian format to the host format integer - */ -static inline unsigned int le32_to_int(unsigned char *le32) -{ - return ((le32[3] << 24) + - (le32[2] << 16) + - (le32[1] << 8) + - le32[0] - ); -} - static inline int is_extended(int part_type) { return (part_type == DOS_PART_TYPE_EXTENDED || @@ -61,8 +51,8 @@ static int get_bootable(dos_partition_t *p) static void print_one_part(dos_partition_t *p, lbaint_t ext_part_sector, int part_num, unsigned int disksig) { - lbaint_t lba_start = ext_part_sector + le32_to_int (p->start4); - lbaint_t lba_size = le32_to_int (p->size4); + lbaint_t lba_start = ext_part_sector + get_unaligned_le32(p->start4); + lbaint_t lba_size = get_unaligned_le32(p->size4);
printf("%3d\t%-10" LBAFlength "u\t%-10" LBAFlength "u\t%08x-%02x\t%02x%s%s\n", @@ -171,7 +161,7 @@ static void print_partition_extended(struct blk_desc *dev_desc, }
if (!ext_part_sector) - disksig = le32_to_int(&buffer[DOS_PART_DISKSIG_OFFSET]); + disksig = get_unaligned_le32(&buffer[DOS_PART_DISKSIG_OFFSET]);
/* Print all primary/logical partitions */ pt = (dos_partition_t *) (buffer + DOS_PART_TBL_OFFSET); @@ -198,7 +188,7 @@ static void print_partition_extended(struct blk_desc *dev_desc, for (i = 0; i < 4; i++, pt++) { if (is_extended (pt->sys_ind)) { lbaint_t lba_start - = le32_to_int (pt->start4) + relative; + = get_unaligned_le32 (pt->start4) + relative;
print_partition_extended(dev_desc, lba_start, ext_part_sector == 0 ? lba_start : relative, @@ -244,7 +234,7 @@ static int part_get_info_extended(struct blk_desc *dev_desc,
#if CONFIG_IS_ENABLED(PARTITION_UUIDS) if (!ext_part_sector) - disksig = le32_to_int(&buffer[DOS_PART_DISKSIG_OFFSET]); + disksig = get_unaligned_le32(&buffer[DOS_PART_DISKSIG_OFFSET]); #endif
/* Print all primary/logical partitions */ @@ -260,8 +250,8 @@ static int part_get_info_extended(struct blk_desc *dev_desc, (ext_part_sector == 0 || is_extended(pt->sys_ind) == 0)) { info->blksz = DOS_PART_DEFAULT_SECTOR; info->start = (lbaint_t)(ext_part_sector + - le32_to_int(pt->start4)); - info->size = (lbaint_t)le32_to_int(pt->size4); + get_unaligned_le32(pt->start4)); + info->size = (lbaint_t)get_unaligned_le32(pt->size4); part_set_generic_name(dev_desc, part_num, (char *)info->name); /* sprintf(info->type, "%d, pt->sys_ind); */ @@ -286,7 +276,7 @@ static int part_get_info_extended(struct blk_desc *dev_desc, for (i = 0; i < 4; i++, pt++) { if (is_extended (pt->sys_ind)) { lbaint_t lba_start - = le32_to_int (pt->start4) + relative; + = get_unaligned_le32 (pt->start4) + relative;
return part_get_info_extended(dev_desc, lba_start, ext_part_sector == 0 ? lba_start : relative,

Make functions not used outside this file static.
Signed-off-by: Marek Szyprowski m.szyprowski@samsung.com --- disk/part_dos.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/disk/part_dos.c b/disk/part_dos.c index 3b79b9b1b8..2c4ad0b6ba 100644 --- a/disk/part_dos.c +++ b/disk/part_dos.c @@ -302,13 +302,13 @@ static int part_get_info_extended(struct blk_desc *dev_desc, return -1; }
-void part_print_dos(struct blk_desc *dev_desc) +static void part_print_dos(struct blk_desc *dev_desc) { printf("Part\tStart Sector\tNum Sectors\tUUID\t\tType\n"); print_partition_extended(dev_desc, 0, 0, 1, 0); }
-int part_get_info_dos(struct blk_desc *dev_desc, int part, +static int part_get_info_dos(struct blk_desc *dev_desc, int part, struct disk_partition *info) { return part_get_info_extended(dev_desc, 0, 0, 1, part, info, 0);

Add a code for creating and writing MBR partition layout. The code generates similar layout of EBRs (Exteneded Block Records) and logical volumes as Linux's fdisk utility.
Signed-off-by: Marek Szyprowski m.szyprowski@samsung.com --- disk/part_dos.c | 167 ++++++++++++++++++++++++++++++++++++++++++++++++ disk/part_dos.h | 2 + include/part.h | 5 ++ 3 files changed, 174 insertions(+)
diff --git a/disk/part_dos.c b/disk/part_dos.c index 2c4ad0b6ba..f77f927995 100644 --- a/disk/part_dos.c +++ b/disk/part_dos.c @@ -319,6 +319,173 @@ int is_valid_dos_buf(void *buf) return test_block_type(buf) == DOS_MBR ? 0 : -1; }
+#if CONFIG_IS_ENABLED(CMD_MBR) +static void lba_to_chs(lbaint_t lba, unsigned char *rc, unsigned char *rh, + unsigned char *rs) +{ + unsigned int c, h, s; + /* use fixed CHS geometry */ + unsigned int sectpertrack = 63; + unsigned int heads = 255; + + c = (lba + 1) / sectpertrack / heads; + h = (lba + 1) / sectpertrack - c * heads; + s = (lba + 1) - (c * heads + h) * sectpertrack; + + if (c > 1023) { + c = 1023; + h = 254; + s = 63; + } + + *rc = c & 0xff; + *rh = h; + *rs = s + ((c & 0x300) >> 2); +} + +static void mbr_fill_pt_entry(dos_partition_t *pt, lbaint_t start, + lbaint_t relative, lbaint_t size, uchar sys_ind, bool bootable) +{ + pt->boot_ind = bootable ? 0x80 : 0x00; + pt->sys_ind = sys_ind; + lba_to_chs(start, &pt->cyl, &pt->head, &pt->sector); + lba_to_chs(start + size - 1, &pt->end_cyl, &pt->end_head, &pt->end_sector); + put_unaligned_le32(relative, &pt->start4); + put_unaligned_le32(size, &pt->size4); +} + +int write_mbr_partitions(struct blk_desc *dev, + struct disk_partition *p, int count, unsigned int disksig) +{ + ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buffer, dev->blksz); + lbaint_t ext_part_start = 0, ext_part_size = 0, ext_part_sect = 0; + dos_partition_t *pt; + int i; + + memset(buffer, 0, dev->blksz); + buffer[DOS_PART_MAGIC_OFFSET] = 0x55; + buffer[DOS_PART_MAGIC_OFFSET + 1] = 0xaa; + put_unaligned_le32(disksig, &buffer[DOS_PART_DISKSIG_OFFSET]); + pt = (dos_partition_t *) (buffer + DOS_PART_TBL_OFFSET); + + /* create all primary partitions */ + for (i = 0; i < 4 && i < count; i++, pt++) { + mbr_fill_pt_entry(pt, p[i].start, p[i].start, p[i].size, + p[i].sys_ind, p[i].bootable); + if (is_extended(p[i].sys_ind)) { + ext_part_start = p[i].start; + ext_part_size = p[i].size; + ext_part_sect = p[i].start; + } + } + + if (i < count && !ext_part_start) { + printf("%s: extended partition is needed for more than 4 partitions\n", + __func__); + return -1; + } + + /* write MBR */ + if (blk_dwrite(dev, 0, 1, buffer) != 1) { + printf("%s: failed writing 'MBR' (1 blks at 0x0)\n", + __func__); + return -1; + } + + /* create extended volumes */ + for (; i < count; i++) { + lbaint_t next_ebr = 0; + + memset(buffer, 0, dev->blksz); + buffer[DOS_PART_MAGIC_OFFSET] = 0x55; + buffer[DOS_PART_MAGIC_OFFSET + 1] = 0xaa; + pt = (dos_partition_t *) (buffer + DOS_PART_TBL_OFFSET); + + mbr_fill_pt_entry(pt, p[i].start, p[i].start - ext_part_sect, + p[i].size, p[i].sys_ind, p[i].bootable); + + if (i + 1 < count) { + pt++; + next_ebr = p[i].start + p[i].size; + mbr_fill_pt_entry(pt, next_ebr, + next_ebr - ext_part_start, + p[i+1].start + p[i+1].size - next_ebr, + DOS_PART_TYPE_EXTENDED, 0); + } + + /* write EBR */ + if (blk_dwrite(dev, ext_part_sect, 1, buffer) != 1) { + printf("%s: failed writing 'EBR' (1 blks at 0x%lx)\n", + __func__, ext_part_sect); + return -1; + } + ext_part_sect = next_ebr; + } + + return 0; +} + +int layout_mbr_partitions(struct disk_partition *p, int count, + lbaint_t total_sectors) +{ + struct disk_partition *ext = NULL; + int i, j; + lbaint_t ext_vol_start; + + /* calculate primary partitions start and size if needed */ + if (!p[0].start) + p[0].start = DOS_PART_DEFAULT_GAP; + for (i = 0; i < 4 && i < count; i++) { + if (!p[i].start) + p[i].start = p[i - 1].start + p[i - 1].size; + if (!p[i].size) { + lbaint_t end = total_sectors; + lbaint_t allocated = 0; + + for (j = i + 1; j < 4 && j < count; j++) { + if (p[j].start) { + end = p[j].start; + break; + } + allocated += p[j].size; + } + p[i].size = end - allocated - p[i].start; + } + if (p[i].sys_ind == 0x05) + ext = &p[i]; + } + + if (i >= 4 && !ext) { + printf("%s: extended partition is needed for more than 4 partitions\n", + __func__); + return -1; + } + + /* calculate extended volumes start and size if needed */ + ext_vol_start = ext->start; + for (i = 4; i < count; i++) { + if (!p[i].start) + p[i].start = ext_vol_start + DOS_PART_DEFAULT_GAP; + if (!p[i].size) { + lbaint_t end = ext->start + ext->size; + lbaint_t allocated = 0; + + for (j = i + 1; j < count; j++) { + if (p[j].start) { + end = p[j].start - DOS_PART_DEFAULT_GAP; + break; + } + allocated += p[j].size + DOS_PART_DEFAULT_GAP; + } + p[i].size = end - allocated - p[i].start; + } + ext_vol_start = p[i].start + p[i].size; + } + + return 0; +} +#endif + int write_mbr_sector(struct blk_desc *dev_desc, void *buf) { if (is_valid_dos_buf(buf)) diff --git a/disk/part_dos.h b/disk/part_dos.h index dd909a9317..5055822422 100644 --- a/disk/part_dos.h +++ b/disk/part_dos.h @@ -19,6 +19,8 @@ #define DOS_PART_TYPE_EXTENDED_LBA 0x0F #define DOS_PART_TYPE_EXTENDED_LINUX 0x85
+#define DOS_PART_DEFAULT_GAP 2048 + typedef struct dos_partition { unsigned char boot_ind; /* 0x80 - active */ unsigned char head; /* starting head */ diff --git a/include/part.h b/include/part.h index 67b8b2a5cc..fac36364bd 100644 --- a/include/part.h +++ b/include/part.h @@ -474,6 +474,11 @@ int is_valid_dos_buf(void *buf); */ int write_mbr_sector(struct blk_desc *dev_desc, void *buf);
+int write_mbr_partitions(struct blk_desc *dev, + struct disk_partition *p, int count, unsigned int disksig); +int layout_mbr_partitions(struct disk_partition *p, int count, + lbaint_t total_sectors); + #endif

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 + */ +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
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-do...
- */
+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"
+);
participants (2)
-
Heinrich Schuchardt
-
Marek Szyprowski