]> git.dujemihanovic.xyz Git - u-boot.git/commitdiff
cmd: Add MBR partition layout control utility
authorMarek Szyprowski <m.szyprowski@samsung.com>
Wed, 23 Dec 2020 12:55:15 +0000 (13:55 +0100)
committerTom Rini <trini@konsulko.com>
Fri, 15 Jan 2021 21:00:32 +0000 (16:00 -0500)
Add a 'mbr' command to let users create or verify MBR partition layout
based on the provided text description. The partition layout is
alternatively read from the '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 type). 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 an 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
cmd/Makefile
cmd/mbr.c [new file with mode: 0644]
doc/usage/index.rst
doc/usage/mbr.rst [new file with mode: 0644]

index da86a940ce1aa8b42f85e46f9ec010489211cf86..0107c0fa3ec24db9d4862740d2fa8f2c0a5b6bf2 100644 (file)
@@ -1034,6 +1034,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
index 5b3400a8403c17829418e7fb169e8a1aa70fac5a..87aa949e2f221b92bdc5e8185ece41a4a1217e43 100644 (file)
@@ -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 (file)
index 0000000..da2e3a4
--- /dev/null
+++ b/cmd/mbr.c
@@ -0,0 +1,314 @@
+// 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 <command.h>
+#include <malloc.h>
+#include <part.h>
+
+/**
+ * extract_val() - Extract a value from the key=value pair list
+ * @str: pointer to string with key=values pairs
+ * @key: pointer to the key to search for
+ *
+ * The list of parameters is come separated, only a value for
+ * the given key is returend.
+ *
+ * Function allocates memory for the value, remember to free!
+ *
+ * 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() - Search for a key without a value in the parameter list
+ * @str: pointer to string with key
+ * @key: pointer to the key to search for
+ *
+ * The list of parameters is come separated.
+ *
+ * Return: True if key has been found.
+ */
+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_partitions(const char *str_part, int blksz,
+       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 / blksz;
+               }
+               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 / blksz;
+                       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 blksz = dev->blksz;
+       int count;
+
+       if (str_to_partitions(str, blksz, &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 blksz = dev->blksz;
+       int count, i, ret = 1;
+
+       if (str_to_partitions(str, blksz, &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"
+);
index fbb2c0481c34261f059fe151b17b42e4904115b9..5869fba1897a50ac77cc013eece301541cb3f7e8 100644 (file)
@@ -14,4 +14,5 @@ Shell commands
    bootefi
    bootmenu
    button
+   mbr
    pstore
diff --git a/doc/usage/mbr.rst b/doc/usage/mbr.rst
new file mode 100644 (file)
index 0000000..bddf2f6
--- /dev/null
@@ -0,0 +1,94 @@
+.. SPDX-License-Identifier: GPL-2.0+
+
+mbr command
+===========
+
+Synopsis
+--------
+
+::
+
+    mbr verify [interface] [device no] [partition list]
+    mbr write [interface] [device no] [partition list]
+
+Description
+-----------
+
+The mbr command lets users create or verify the MBR (Master Boot Record)
+partition layout based on the provided text description. The partition
+layout is alternatively read from the '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 partition 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)
+* id (MBR partition type)
+
+If one wants to create more than 4 partitions, an 'Extended' primary
+partition (with 0x05 ID) has to be explicitly provided as a one of the
+first 4 entries.
+
+Here is an 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
+
+To check if the layout on the MMC #0 storage device matches the provided
+text description one has to issue following command (assuming that
+mbr_parts environment variable is set):
+
+::
+
+    => mbr verify mmc 0
+
+The verify sub-command is especially useful in the system update scripts:
+
+::
+
+    => if mbr verify mmc 0; then
+         echo MBR layout needs to be updated
+         ...
+       fi
+
+The 'mbr write' command returns 0 on success write or 1 on failure.
+
+The 'mbr verify' returns 0 if the layout matches the one on the storage
+device or 1 if not.
+
+Configuration
+-------------
+
+To use the mbr command you must specify CONFIG_CMD_MBR=y.
+
+Return value
+------------
+
+The variable *$?* takes the following values
+
++---+------------------------------+
+| 0 | mbr write was succesful      |
++---+------------------------------+
+| 1 | mbr write failed             |
++---+------------------------------+
+| 0 | mbr verify was succesful     |
++---+------------------------------+
+| 1 | mbr verify was not succesful |
++---+------------------------------+
+|-1 | invalid arguments            |
++---+------------------------------+