diff options
Diffstat (limited to 'common/cmd_gpt.c')
| -rw-r--r-- | common/cmd_gpt.c | 333 | 
1 files changed, 333 insertions, 0 deletions
| diff --git a/common/cmd_gpt.c b/common/cmd_gpt.c new file mode 100644 index 000000000..da7705da6 --- /dev/null +++ b/common/cmd_gpt.c @@ -0,0 +1,333 @@ +/* + * cmd_gpt.c -- GPT (GUID Partition Table) handling command + * + * Copyright (C) 2012 Samsung Electronics + * author: Lukasz Majewski <l.majewski@samsung.com> + * author: Piotr Wilczek <p.wilczek@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA + */ + +#include <common.h> +#include <malloc.h> +#include <command.h> +#include <mmc.h> +#include <part_efi.h> +#include <exports.h> +#include <linux/ctype.h> + +#ifndef CONFIG_PARTITION_UUIDS +#error CONFIG_PARTITION_UUIDS must be enabled for CONFIG_CMD_GPT to be enabled +#endif + +/** + * extract_env(): Expand env name from string format '&{env_name}' + *                and return pointer to the env (if the env is set) + * + * @param str - pointer to string + * @param env - pointer to pointer to extracted env + * + * @return - zero on successful expand and env is set + */ +static char extract_env(const char *str, char **env) +{ +	char *e, *s; + +	if (!str || strlen(str) < 4) +		return -1; + +	if ((strncmp(str, "${", 2) == 0) && (str[strlen(str) - 1] == '}')) { +		s = strdup(str); +		if (s == NULL) +			return -1; +		memset(s + strlen(s) - 1, '\0', 1); +		memmove(s, s + 2, strlen(s) - 1); +		e = getenv(s); +		free(s); +		if (e == NULL) { +			printf("Environmental '%s' not set\n", str); +			return -1; /* env not set */ +		} +		*env = e; +		return 0; +	} + +	return -1; +} + +/** + * 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; +} + +/** + * set_gpt_info(): Fill partition information from string + *		function allocates memory, remember to free! + * + * @param dev_desc - pointer block device descriptor + * @param str_part - pointer to string with partition information + * @param str_disk_guid - pointer to pointer to allocated string with disk guid + * @param partitions - pointer to pointer to allocated partitions array + * @param parts_count - number of partitions + * + * @return - zero on success, otherwise error + * + */ +static int set_gpt_info(block_dev_desc_t *dev_desc, +			const char *str_part, +			char **str_disk_guid, +			disk_partition_t **partitions, +			u8 *parts_count) +{ +	char *tok, *str, *s; +	int i; +	char *val, *p; +	int p_count; +	disk_partition_t *parts; +	int errno = 0; + +	debug("%s: MMC lba num: 0x%x %d\n", __func__, +	      (unsigned int)dev_desc->lba, (unsigned int)dev_desc->lba); + +	if (str_part == NULL) +		return -1; + +	str = strdup(str_part); + +	/* extract disk guid */ +	s = str; +	tok = strsep(&s, ";"); +	val = extract_val(tok, "uuid_disk"); +	if (!val) { +		free(str); +		return -2; +	} +	if (extract_env(val, &p)) +		p = val; +	*str_disk_guid = strdup(p); +	free(val); + +	if (strlen(s) == 0) +		return -3; + +	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(disk_partition_t), p_count); + +	/* retrive partions data from string */ +	for (i = 0; i < p_count; i++) { +		tok = strsep(&s, ";"); + +		if (tok == NULL) +			break; + +		/* uuid */ +		val = extract_val(tok, "uuid"); +		if (!val) { /* 'uuid' is mandatory */ +			errno = -4; +			goto err; +		} +		if (extract_env(val, &p)) +			p = val; +		if (strlen(p) >= sizeof(parts[i].uuid)) { +			printf("Wrong uuid format for partition %d\n", i); +			errno = -4; +			goto err; +		} +		strcpy((char *)parts[i].uuid, p); +		free(val); + +		/* name */ +		val = extract_val(tok, "name"); +		if (!val) { /* name is mandatory */ +			errno = -4; +			goto err; +		} +		if (extract_env(val, &p)) +			p = val; +		if (strlen(p) >= sizeof(parts[i].name)) { +			errno = -4; +			goto err; +		} +		strcpy((char *)parts[i].name, p); +		free(val); + +		/* size */ +		val = extract_val(tok, "size"); +		if (!val) { /* 'size' is mandatory */ +			errno = -4; +			goto err; +		} +		if (extract_env(val, &p)) +			p = val; +		parts[i].size = ustrtoul(p, &p, 0); +		parts[i].size /= dev_desc->blksz; +		free(val); + +		/* start address */ +		val = extract_val(tok, "start"); +		if (val) { /* start address is optional */ +			if (extract_env(val, &p)) +				p = val; +			parts[i].start = ustrtoul(p, &p, 0); +			parts[i].start /= dev_desc->blksz; +			free(val); +		} +	} + +	*parts_count = p_count; +	*partitions = parts; +	free(str); + +	return 0; +err: +	free(str); +	free(*str_disk_guid); +	free(parts); + +	return errno; +} + +static int gpt_mmc_default(int dev, const char *str_part) +{ +	int ret; +	char *str_disk_guid; +	u8 part_count = 0; +	disk_partition_t *partitions = NULL; + +	struct mmc *mmc = find_mmc_device(dev); + +	if (mmc == NULL) { +		printf("%s: mmc dev %d NOT available\n", __func__, dev); +		return CMD_RET_FAILURE; +	} + +	if (!str_part) +		return -1; + +	/* fill partitions */ +	ret = set_gpt_info(&mmc->block_dev, str_part, +			&str_disk_guid, &partitions, &part_count); +	if (ret) { +		if (ret == -1) +			printf("No partition list provided\n"); +		if (ret == -2) +			printf("Missing disk guid\n"); +		if ((ret == -3) || (ret == -4)) +			printf("Partition list incomplete\n"); +		return -1; +	} + +	/* save partitions layout to disk */ +	gpt_restore(&mmc->block_dev, str_disk_guid, partitions, part_count); +	free(str_disk_guid); +	free(partitions); + +	return 0; +} + +/** + * do_gpt(): Perform GPT operations + * + * @param cmdtp - command name + * @param flag + * @param argc + * @param argv + * + * @return zero on success; otherwise error + */ +static int do_gpt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ +	int ret = CMD_RET_SUCCESS; +	int dev = 0; +	char *pstr; + +	if (argc < 5) +		return CMD_RET_USAGE; + +	/* command: 'write' */ +	if ((strcmp(argv[1], "write") == 0) && (argc == 5)) { +		/* device: 'mmc' */ +		if (strcmp(argv[2], "mmc") == 0) { +			/* check if 'dev' is a number */ +			for (pstr = argv[3]; *pstr != '\0'; pstr++) +				if (!isdigit(*pstr)) { +					printf("'%s' is not a number\n", +						argv[3]); +					return CMD_RET_USAGE; +				} +			dev = (int)simple_strtoul(argv[3], NULL, 10); +			/* write to mmc */ +			if (gpt_mmc_default(dev, argv[4])) +				return CMD_RET_FAILURE; +		} +	} else { +		return CMD_RET_USAGE; +	} +	return ret; +} + +U_BOOT_CMD(gpt, CONFIG_SYS_MAXARGS, 1, do_gpt, +	"GUID Partition Table", +	"<command> <interface> <dev> <partions_list>\n" +	" - GUID partition table restoration\n" +	" Restore GPT information on a device connected\n" +	" to interface\n" +); |