diff options
Diffstat (limited to 'cmd')
-rw-r--r-- | cmd/Kconfig | 9 | ||||
-rw-r--r-- | cmd/Makefile | 1 | ||||
-rw-r--r-- | cmd/bootmenu.c | 42 | ||||
-rw-r--r-- | cmd/disk.c | 1 | ||||
-rw-r--r-- | cmd/gpt.c | 161 | ||||
-rw-r--r-- | cmd/mbr.c | 314 |
6 files changed, 520 insertions, 8 deletions
diff --git a/cmd/Kconfig b/cmd/Kconfig index da86a940ce1..1b53e5be5ad 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -122,6 +122,7 @@ config CMD_CONSOLE config CMD_CPU bool "cpu" + depends on CPU help Print information about available CPUs. This normally shows the number of CPUs, type (e.g. manufacturer, architecture, product or @@ -1034,6 +1035,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 5b3400a8403..87aa949e2f2 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/bootmenu.c b/cmd/bootmenu.c index 1ba7b622e50..409ef9a8480 100644 --- a/cmd/bootmenu.c +++ b/cmd/bootmenu.c @@ -45,6 +45,7 @@ enum bootmenu_key { KEY_UP, KEY_DOWN, KEY_SELECT, + KEY_QUIT, }; static char *bootmenu_getoption(unsigned short int n) @@ -109,6 +110,9 @@ static void bootmenu_autoboot_loop(struct bootmenu_data *menu, case '\r': *key = KEY_SELECT; break; + case 0x3: /* ^C */ + *key = KEY_QUIT; + break; default: *key = KEY_NONE; break; @@ -136,13 +140,25 @@ static void bootmenu_loop(struct bootmenu_data *menu, { int c; - while (!tstc()) { - WATCHDOG_RESET(); - mdelay(10); + if (*esc == 1) { + if (tstc()) { + c = getchar(); + } else { + WATCHDOG_RESET(); + mdelay(10); + if (tstc()) + c = getchar(); + else + c = '\e'; + } + } else { + while (!tstc()) { + WATCHDOG_RESET(); + mdelay(10); + } + c = getchar(); } - c = getchar(); - switch (*esc) { case 0: /* First char of ANSI escape sequence '\e' */ @@ -157,7 +173,9 @@ static void bootmenu_loop(struct bootmenu_data *menu, *esc = 2; *key = KEY_NONE; } else { - *esc = 0; + /* Alone ESC key was pressed */ + *key = KEY_QUIT; + *esc = (c == '\e') ? 1 : 0; } break; case 2: @@ -187,6 +205,10 @@ static void bootmenu_loop(struct bootmenu_data *menu, /* enter key was pressed */ if (c == '\r') *key = KEY_SELECT; + + /* ^C was pressed */ + if (c == 0x3) + *key = KEY_QUIT; } static char *bootmenu_choice_entry(void *data) @@ -222,6 +244,12 @@ static char *bootmenu_choice_entry(void *data) for (i = 0; i < menu->active; ++i) iter = iter->next; return iter->key; + case KEY_QUIT: + /* Quit by choosing the last entry - U-Boot console */ + iter = menu->first; + while (iter->next) + iter = iter->next; + return iter->key; default: break; } @@ -389,7 +417,7 @@ static void menu_display_statusline(struct menu *m) printf(ANSI_CURSOR_POSITION, menu->count + 5, 1); puts(ANSI_CLEAR_LINE); printf(ANSI_CURSOR_POSITION, menu->count + 6, 1); - puts(" Press UP/DOWN to move, ENTER to select"); + puts(" Press UP/DOWN to move, ENTER to select, ESC/CTRL+C to quit"); puts(ANSI_CLEAR_LINE_TO_END); printf(ANSI_CURSOR_POSITION, menu->count + 7, 1); puts(ANSI_CLEAR_LINE); diff --git a/cmd/disk.c b/cmd/disk.c index 8060e753ebd..0bc3808dfe2 100644 --- a/cmd/disk.c +++ b/cmd/disk.c @@ -120,7 +120,6 @@ int common_diskboot(struct cmd_tbl *cmdtp, const char *intf, int argc, return 1; } bootstage_mark(BOOTSTAGE_ID_IDE_FIT_READ_OK); - fit_print_contents(fit_hdr); } #endif diff --git a/cmd/gpt.c b/cmd/gpt.c index df759416c88..76a95ade6c2 100644 --- a/cmd/gpt.c +++ b/cmd/gpt.c @@ -18,6 +18,7 @@ #include <command.h> #include <part.h> #include <part_efi.h> +#include <part.h> #include <exports.h> #include <uuid.h> #include <linux/ctype.h> @@ -621,6 +622,152 @@ static int gpt_verify(struct blk_desc *blk_dev_desc, const char *str_part) return ret; } +/** + * gpt_enumerate() - Enumerate partition names into environment variable. + * + * Enumerate partition names. Partition names are stored in gpt_partition_list + * environment variable. Each partition name is delimited by space. + * + * @desc: block device descriptor + * + * @Return: '0' on success and -ve error on failure + */ +static int gpt_enumerate(struct blk_desc *desc) +{ + struct part_driver *first_drv, *part_drv; + int str_len = 0, tmp_len; + char part_list[2048]; + int n_drvs; + char *ptr; + + part_list[0] = 0; + n_drvs = part_driver_get_count(); + if (!n_drvs) { + printf("Failed to get partition driver count\n"); + return -ENOENT; + } + + first_drv = part_driver_get_first(); + for (part_drv = first_drv; part_drv != first_drv + n_drvs; part_drv++) { + struct disk_partition pinfo; + int ret; + int i; + + for (i = 1; i < part_drv->max_entries; i++) { + ret = part_drv->get_info(desc, i, &pinfo); + if (ret) { + /* no more entries in table */ + break; + } + + ptr = &part_list[str_len]; + tmp_len = strlen((const char *)pinfo.name); + str_len += tmp_len; + /* +1 for space */ + str_len++; + if (str_len > sizeof(part_list)) { + printf("Error insufficient memory\n"); + return -ENOMEM; + } + strcpy(ptr, (const char *)pinfo.name); + /* One byte for space(" ") delimiter */ + ptr[tmp_len] = ' '; + } + } + if (*part_list) + part_list[strlen(part_list) - 1] = 0; + debug("setenv gpt_partition_list %s\n", part_list); + + return env_set("gpt_partition_list", part_list); +} + +/** + * gpt_setenv_part_variables() - setup partition environmental variables + * + * Setup the gpt_partition_name, gpt_partition_entry, gpt_partition_addr + * and gpt_partition_size environment variables. + * + * @pinfo: pointer to disk partition + * @i: partition entry + * + * @Return: '0' on success and -ENOENT on failure + */ +static int gpt_setenv_part_variables(struct disk_partition *pinfo, int i) +{ + int ret; + + ret = env_set_hex("gpt_partition_addr", pinfo->start); + if (ret) + goto fail; + + ret = env_set_hex("gpt_partition_size", pinfo->size); + if (ret) + goto fail; + + ret = env_set_ulong("gpt_partition_entry", i); + if (ret) + goto fail; + + ret = env_set("gpt_partition_name", (const char *)pinfo->name); + if (ret) + goto fail; + + return 0; + +fail: + return -ENOENT; +} + +/** + * gpt_setenv() - Dynamically setup environment variables. + * + * Dynamically setup environment variables for name, index, offset and size + * for partition in GPT table after running "gpt setenv" for a partition name. + * + * @desc: block device descriptor + * @name: partition name + * + * @Return: '0' on success and -ve err on failure + */ +static int gpt_setenv(struct blk_desc *desc, const char *name) +{ + struct part_driver *first_drv, *part_drv; + int n_drvs; + int ret = -1; + + n_drvs = part_driver_get_count(); + if (!n_drvs) { + printf("Failed to get partition driver count\n"); + goto fail; + } + + first_drv = part_driver_get_first(); + for (part_drv = first_drv; part_drv != first_drv + n_drvs; part_drv++) { + struct disk_partition pinfo; + int i; + + for (i = 1; i < part_drv->max_entries; i++) { + ret = part_drv->get_info(desc, i, &pinfo); + if (ret) { + /* no more entries in table */ + break; + } + + if (!strcmp(name, (const char *)pinfo.name)) { + /* match found, setup environment variables */ + ret = gpt_setenv_part_variables(&pinfo, i); + if (ret) + goto fail; + + return 0; + } + } + } + +fail: + return ret; +} + static int do_disk_guid(struct blk_desc *dev_desc, char * const namestr) { int ret; @@ -827,6 +974,10 @@ static int do_gpt(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) } else if ((strcmp(argv[1], "verify") == 0)) { ret = gpt_verify(blk_dev_desc, argv[4]); printf("Verify GPT: "); + } else if ((strcmp(argv[1], "setenv") == 0)) { + ret = gpt_setenv(blk_dev_desc, argv[4]); + } else if ((strcmp(argv[1], "enumerate") == 0)) { + ret = gpt_enumerate(blk_dev_desc); } else if (strcmp(argv[1], "guid") == 0) { ret = do_disk_guid(blk_dev_desc, argv[4]); #ifdef CONFIG_CMD_GPT_RENAME @@ -857,7 +1008,17 @@ U_BOOT_CMD(gpt, CONFIG_SYS_MAXARGS, 1, do_gpt, " to interface\n" " Example usage:\n" " gpt write mmc 0 $partitions\n" + " - write the GPT to device\n" " gpt verify mmc 0 $partitions\n" + " - verify the GPT on device against $partitions\n" + " gpt setenv mmc 0 $name\n" + " - setup environment variables for partition $name:\n" + " gpt_partition_addr, gpt_partition_size,\n" + " gpt_partition_name, gpt_partition_entry\n" + " gpt enumerate mmc 0\n" + " - store list of partitions to gpt_partition_list environment variable\n" + " read <interface> <dev>\n" + " - read GPT into a data structure for manipulation\n" " gpt guid <interface> <dev>\n" " - print disk GUID\n" " gpt guid <interface> <dev> <varname>\n" diff --git a/cmd/mbr.c b/cmd/mbr.c new file mode 100644 index 00000000000..da2e3a4722b --- /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" +); |