mirror of
				https://github.com/smaeul/u-boot.git
				synced 2025-11-03 21:48:15 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			882 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			882 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
// SPDX-License-Identifier: GPL-2.0+
 | 
						|
/*
 | 
						|
 * cmd_gpt.c -- GPT (GUID Partition Table) handling command
 | 
						|
 *
 | 
						|
 * Copyright (C) 2015
 | 
						|
 * Lukasz Majewski <l.majewski@majess.pl>
 | 
						|
 *
 | 
						|
 * Copyright (C) 2012 Samsung Electronics
 | 
						|
 * author: Lukasz Majewski <l.majewski@samsung.com>
 | 
						|
 * author: Piotr Wilczek <p.wilczek@samsung.com>
 | 
						|
 */
 | 
						|
 | 
						|
#include <common.h>
 | 
						|
#include <blk.h>
 | 
						|
#include <env.h>
 | 
						|
#include <log.h>
 | 
						|
#include <malloc.h>
 | 
						|
#include <command.h>
 | 
						|
#include <part.h>
 | 
						|
#include <part_efi.h>
 | 
						|
#include <exports.h>
 | 
						|
#include <uuid.h>
 | 
						|
#include <linux/ctype.h>
 | 
						|
#include <div64.h>
 | 
						|
#include <memalign.h>
 | 
						|
#include <linux/compat.h>
 | 
						|
#include <linux/err.h>
 | 
						|
#include <linux/sizes.h>
 | 
						|
#include <stdlib.h>
 | 
						|
 | 
						|
static LIST_HEAD(disk_partitions);
 | 
						|
 | 
						|
/**
 | 
						|
 * 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 int extract_env(const char *str, char **env)
 | 
						|
{
 | 
						|
	int ret = -1;
 | 
						|
	char *e, *s;
 | 
						|
#ifdef CONFIG_RANDOM_UUID
 | 
						|
	char uuid_str[UUID_STR_LEN + 1];
 | 
						|
#endif
 | 
						|
 | 
						|
	if (!str || strlen(str) < 4)
 | 
						|
		return -1;
 | 
						|
 | 
						|
	if (!((strncmp(str, "${", 2) == 0) && (str[strlen(str) - 1] == '}')))
 | 
						|
		return -1;
 | 
						|
 | 
						|
	s = strdup(str);
 | 
						|
	if (s == NULL)
 | 
						|
		return -1;
 | 
						|
 | 
						|
	memset(s + strlen(s) - 1, '\0', 1);
 | 
						|
	memmove(s, s + 2, strlen(s) - 1);
 | 
						|
 | 
						|
	e = env_get(s);
 | 
						|
	if (e == NULL) {
 | 
						|
#ifdef CONFIG_RANDOM_UUID
 | 
						|
		debug("%s unset. ", str);
 | 
						|
		gen_rand_uuid_str(uuid_str, UUID_STR_FORMAT_GUID);
 | 
						|
		env_set(s, uuid_str);
 | 
						|
 | 
						|
		e = env_get(s);
 | 
						|
		if (e) {
 | 
						|
			debug("Set to random.\n");
 | 
						|
			ret = 0;
 | 
						|
		} else {
 | 
						|
			debug("Can't get random UUID.\n");
 | 
						|
		}
 | 
						|
#else
 | 
						|
		debug("%s unset.\n", str);
 | 
						|
#endif
 | 
						|
	} else {
 | 
						|
		debug("%s get from environment.\n", str);
 | 
						|
		ret = 0;
 | 
						|
	}
 | 
						|
 | 
						|
	*env = e;
 | 
						|
	free(s);
 | 
						|
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * 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 calc_parts_list_len(int numparts)
 | 
						|
{
 | 
						|
	int partlistlen = UUID_STR_LEN + 1 + strlen("uuid_disk=");
 | 
						|
	/* for the comma */
 | 
						|
	partlistlen++;
 | 
						|
 | 
						|
	/* per-partition additions; numparts starts at 1, so this should be correct */
 | 
						|
	partlistlen += numparts * (strlen("name=,") + PART_NAME_LEN + 1);
 | 
						|
	/* see part.h for definition of struct disk_partition */
 | 
						|
	partlistlen += numparts * (strlen("start=MiB,") + sizeof(lbaint_t) + 1);
 | 
						|
	partlistlen += numparts * (strlen("size=MiB,") + sizeof(lbaint_t) + 1);
 | 
						|
	partlistlen += numparts * (strlen("uuid=;") + UUID_STR_LEN + 1);
 | 
						|
	/* for the terminating null */
 | 
						|
	partlistlen++;
 | 
						|
	debug("Length of partitions_list is %d for %d partitions\n", partlistlen,
 | 
						|
	      numparts);
 | 
						|
	return partlistlen;
 | 
						|
}
 | 
						|
 | 
						|
#ifdef CONFIG_CMD_GPT_RENAME
 | 
						|
static void del_gpt_info(void)
 | 
						|
{
 | 
						|
	struct list_head *pos = &disk_partitions;
 | 
						|
	struct disk_part *curr;
 | 
						|
	while (!list_empty(pos)) {
 | 
						|
		curr = list_entry(pos->next, struct disk_part, list);
 | 
						|
		list_del(pos->next);
 | 
						|
		free(curr);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static struct disk_part *allocate_disk_part(struct disk_partition *info,
 | 
						|
					    int partnum)
 | 
						|
{
 | 
						|
	struct disk_part *newpart;
 | 
						|
	newpart = calloc(1, sizeof(struct disk_part));
 | 
						|
	if (!newpart)
 | 
						|
		return ERR_PTR(-ENOMEM);
 | 
						|
 | 
						|
	newpart->gpt_part_info.start = info->start;
 | 
						|
	newpart->gpt_part_info.size = info->size;
 | 
						|
	newpart->gpt_part_info.blksz = info->blksz;
 | 
						|
	strncpy((char *)newpart->gpt_part_info.name, (const char *)info->name,
 | 
						|
		PART_NAME_LEN);
 | 
						|
	newpart->gpt_part_info.name[PART_NAME_LEN - 1] = '\0';
 | 
						|
	strncpy((char *)newpart->gpt_part_info.type, (const char *)info->type,
 | 
						|
		PART_TYPE_LEN);
 | 
						|
	newpart->gpt_part_info.type[PART_TYPE_LEN - 1] = '\0';
 | 
						|
	newpart->gpt_part_info.bootable = info->bootable;
 | 
						|
#ifdef CONFIG_PARTITION_UUIDS
 | 
						|
	strncpy(newpart->gpt_part_info.uuid, (const char *)info->uuid,
 | 
						|
		UUID_STR_LEN);
 | 
						|
	/* UUID_STR_LEN is correct, as uuid[]'s length is UUID_STR_LEN+1 chars */
 | 
						|
	newpart->gpt_part_info.uuid[UUID_STR_LEN] = '\0';
 | 
						|
#endif
 | 
						|
	newpart->partnum = partnum;
 | 
						|
 | 
						|
	return newpart;
 | 
						|
}
 | 
						|
 | 
						|
static void prettyprint_part_size(char *sizestr, lbaint_t partsize,
 | 
						|
				  lbaint_t blksize)
 | 
						|
{
 | 
						|
	unsigned long long partbytes, partmegabytes;
 | 
						|
 | 
						|
	partbytes = partsize * blksize;
 | 
						|
	partmegabytes = lldiv(partbytes, SZ_1M);
 | 
						|
	snprintf(sizestr, 16, "%lluMiB", partmegabytes);
 | 
						|
}
 | 
						|
 | 
						|
static void print_gpt_info(void)
 | 
						|
{
 | 
						|
	struct list_head *pos;
 | 
						|
	struct disk_part *curr;
 | 
						|
	char partstartstr[16];
 | 
						|
	char partsizestr[16];
 | 
						|
 | 
						|
	list_for_each(pos, &disk_partitions) {
 | 
						|
		curr = list_entry(pos, struct disk_part, list);
 | 
						|
		prettyprint_part_size(partstartstr, curr->gpt_part_info.start,
 | 
						|
				      curr->gpt_part_info.blksz);
 | 
						|
		prettyprint_part_size(partsizestr, curr->gpt_part_info.size,
 | 
						|
				      curr->gpt_part_info.blksz);
 | 
						|
 | 
						|
		printf("Partition %d:\n", curr->partnum);
 | 
						|
		printf("Start %s, size %s\n", partstartstr, partsizestr);
 | 
						|
		printf("Block size %lu, name %s\n", curr->gpt_part_info.blksz,
 | 
						|
		       curr->gpt_part_info.name);
 | 
						|
		printf("Type %s, bootable %d\n", curr->gpt_part_info.type,
 | 
						|
		       curr->gpt_part_info.bootable & PART_BOOTABLE);
 | 
						|
#ifdef CONFIG_PARTITION_UUIDS
 | 
						|
		printf("UUID %s\n", curr->gpt_part_info.uuid);
 | 
						|
#endif
 | 
						|
		printf("\n");
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * create the string that upstream 'gpt write' command will accept as an
 | 
						|
 * argument
 | 
						|
 *
 | 
						|
 * From doc/README.gpt, Format of partitions layout:
 | 
						|
 *    "uuid_disk=...;name=u-boot,size=60MiB,uuid=...;
 | 
						|
 *	name=kernel,size=60MiB,uuid=...;"
 | 
						|
 * The fields 'name' and 'size' are mandatory for every partition.
 | 
						|
 * The field 'start' is optional. The fields 'uuid' and 'uuid_disk'
 | 
						|
 * are optional if CONFIG_RANDOM_UUID is enabled.
 | 
						|
 */
 | 
						|
static int create_gpt_partitions_list(int numparts, const char *guid,
 | 
						|
				      char *partitions_list)
 | 
						|
{
 | 
						|
	struct list_head *pos;
 | 
						|
	struct disk_part *curr;
 | 
						|
	char partstr[PART_NAME_LEN + 1];
 | 
						|
 | 
						|
	if (!partitions_list)
 | 
						|
		return -EINVAL;
 | 
						|
 | 
						|
	strcpy(partitions_list, "uuid_disk=");
 | 
						|
	strncat(partitions_list, guid, UUID_STR_LEN + 1);
 | 
						|
	strcat(partitions_list, ";");
 | 
						|
 | 
						|
	list_for_each(pos, &disk_partitions) {
 | 
						|
		curr = list_entry(pos, struct disk_part, list);
 | 
						|
		strcat(partitions_list, "name=");
 | 
						|
		strncat(partitions_list, (const char *)curr->gpt_part_info.name,
 | 
						|
			PART_NAME_LEN + 1);
 | 
						|
		sprintf(partstr, ",start=0x%llx",
 | 
						|
			(unsigned long long)curr->gpt_part_info.start *
 | 
						|
					    curr->gpt_part_info.blksz);
 | 
						|
		/* one extra byte for NULL */
 | 
						|
		strncat(partitions_list, partstr, PART_NAME_LEN + 1);
 | 
						|
		sprintf(partstr, ",size=0x%llx",
 | 
						|
			(unsigned long long)curr->gpt_part_info.size *
 | 
						|
					    curr->gpt_part_info.blksz);
 | 
						|
		strncat(partitions_list, partstr, PART_NAME_LEN + 1);
 | 
						|
 | 
						|
		strcat(partitions_list, ",uuid=");
 | 
						|
		strncat(partitions_list, curr->gpt_part_info.uuid,
 | 
						|
			UUID_STR_LEN + 1);
 | 
						|
		strcat(partitions_list, ";");
 | 
						|
	}
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * read partition info into disk_partitions list where
 | 
						|
 * it can be printed or modified
 | 
						|
 */
 | 
						|
static int get_gpt_info(struct blk_desc *dev_desc)
 | 
						|
{
 | 
						|
	/* start partition numbering at 1, as U-Boot does */
 | 
						|
	int valid_parts = 0, p, ret;
 | 
						|
	struct disk_partition info;
 | 
						|
	struct disk_part *new_disk_part;
 | 
						|
 | 
						|
	/*
 | 
						|
	 * Always re-read partition info from device, in case
 | 
						|
	 * it has changed
 | 
						|
	 */
 | 
						|
	INIT_LIST_HEAD(&disk_partitions);
 | 
						|
 | 
						|
	for (p = 1; p <= MAX_SEARCH_PARTITIONS; p++) {
 | 
						|
		ret = part_get_info(dev_desc, p, &info);
 | 
						|
		if (ret)
 | 
						|
			continue;
 | 
						|
 | 
						|
		/* Add 1 here because counter is zero-based but p1 is
 | 
						|
		   the first partition */
 | 
						|
		new_disk_part = allocate_disk_part(&info, valid_parts+1);
 | 
						|
		if (IS_ERR(new_disk_part))
 | 
						|
			goto out;
 | 
						|
 | 
						|
		list_add_tail(&new_disk_part->list, &disk_partitions);
 | 
						|
		valid_parts++;
 | 
						|
	}
 | 
						|
	if (valid_parts == 0) {
 | 
						|
		printf("** No valid partitions found **\n");
 | 
						|
		goto out;
 | 
						|
	}
 | 
						|
	return valid_parts;
 | 
						|
 out:
 | 
						|
	if (valid_parts >= 1)
 | 
						|
		del_gpt_info();
 | 
						|
	return -ENODEV;
 | 
						|
}
 | 
						|
 | 
						|
/* a wrapper to test get_gpt_info */
 | 
						|
static int do_get_gpt_info(struct blk_desc *dev_desc)
 | 
						|
{
 | 
						|
	int ret;
 | 
						|
 | 
						|
	ret = get_gpt_info(dev_desc);
 | 
						|
	if (ret > 0) {
 | 
						|
		print_gpt_info();
 | 
						|
		del_gpt_info();
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
/**
 | 
						|
 * 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(struct blk_desc *dev_desc,
 | 
						|
			const char *str_part,
 | 
						|
			char **str_disk_guid,
 | 
						|
			struct disk_partition **partitions,
 | 
						|
			u8 *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;
 | 
						|
	lbaint_t offset = 0;
 | 
						|
	int max_str_part = calc_parts_list_len(MAX_SEARCH_PARTITIONS);
 | 
						|
 | 
						|
	debug("%s:  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);
 | 
						|
	if (str == NULL)
 | 
						|
		return -ENOMEM;
 | 
						|
 | 
						|
	/* extract disk guid */
 | 
						|
	s = str;
 | 
						|
	val = extract_val(str, "uuid_disk");
 | 
						|
	if (!val) {
 | 
						|
#ifdef CONFIG_RANDOM_UUID
 | 
						|
		*str_disk_guid = malloc(UUID_STR_LEN + 1);
 | 
						|
		if (*str_disk_guid == NULL)
 | 
						|
			return -ENOMEM;
 | 
						|
		gen_rand_uuid_str(*str_disk_guid, UUID_STR_FORMAT_STD);
 | 
						|
#else
 | 
						|
		free(str);
 | 
						|
		return -2;
 | 
						|
#endif
 | 
						|
	} else {
 | 
						|
		val = strsep(&val, ";");
 | 
						|
		if (extract_env(val, &p))
 | 
						|
			p = val;
 | 
						|
		*str_disk_guid = strdup(p);
 | 
						|
		free(val);
 | 
						|
		/* Move s to first partition */
 | 
						|
		strsep(&s, ";");
 | 
						|
	}
 | 
						|
	if (s == NULL) {
 | 
						|
		printf("Error: is the partitions string NULL-terminated?\n");
 | 
						|
		return -EINVAL;
 | 
						|
	}
 | 
						|
	if (strnlen(s, max_str_part) == 0)
 | 
						|
		return -3;
 | 
						|
 | 
						|
	i = strnlen(s, max_str_part) - 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;
 | 
						|
 | 
						|
		/* uuid */
 | 
						|
		val = extract_val(tok, "uuid");
 | 
						|
		if (!val) {
 | 
						|
			/* 'uuid' is optional if random uuid's are enabled */
 | 
						|
#ifdef CONFIG_RANDOM_UUID
 | 
						|
			gen_rand_uuid_str(parts[i].uuid, UUID_STR_FORMAT_STD);
 | 
						|
#else
 | 
						|
			errno = -4;
 | 
						|
			goto err;
 | 
						|
#endif
 | 
						|
		} else {
 | 
						|
			if (extract_env(val, &p))
 | 
						|
				p = val;
 | 
						|
			if (strnlen(p, max_str_part) >= sizeof(parts[i].uuid)) {
 | 
						|
				printf("Wrong uuid format for partition %d\n", i);
 | 
						|
				errno = -4;
 | 
						|
				goto err;
 | 
						|
			}
 | 
						|
			strncpy((char *)parts[i].uuid, p, max_str_part);
 | 
						|
			free(val);
 | 
						|
		}
 | 
						|
#ifdef CONFIG_PARTITION_TYPE_GUID
 | 
						|
		/* guid */
 | 
						|
		val = extract_val(tok, "type");
 | 
						|
		if (val) {
 | 
						|
			/* 'type' is optional */
 | 
						|
			if (extract_env(val, &p))
 | 
						|
				p = val;
 | 
						|
			if (strnlen(p, max_str_part) >= sizeof(parts[i].type_guid)) {
 | 
						|
				printf("Wrong type guid format for partition %d\n",
 | 
						|
				       i);
 | 
						|
				errno = -4;
 | 
						|
				goto err;
 | 
						|
			}
 | 
						|
			strncpy((char *)parts[i].type_guid, p, max_str_part);
 | 
						|
			free(val);
 | 
						|
		}
 | 
						|
#endif
 | 
						|
		/* name */
 | 
						|
		val = extract_val(tok, "name");
 | 
						|
		if (!val) { /* name is mandatory */
 | 
						|
			errno = -4;
 | 
						|
			goto err;
 | 
						|
		}
 | 
						|
		if (extract_env(val, &p))
 | 
						|
			p = val;
 | 
						|
		if (strnlen(p, max_str_part) >= sizeof(parts[i].name)) {
 | 
						|
			errno = -4;
 | 
						|
			goto err;
 | 
						|
		}
 | 
						|
		strncpy((char *)parts[i].name, p, max_str_part);
 | 
						|
		free(val);
 | 
						|
 | 
						|
		/* size */
 | 
						|
		val = extract_val(tok, "size");
 | 
						|
		if (!val) { /* 'size' is mandatory */
 | 
						|
			errno = -4;
 | 
						|
			goto err;
 | 
						|
		}
 | 
						|
		if (extract_env(val, &p))
 | 
						|
			p = val;
 | 
						|
		if ((strcmp(p, "-") == 0)) {
 | 
						|
			/* Let part efi module to auto extend the size */
 | 
						|
			parts[i].size = 0;
 | 
						|
		} else {
 | 
						|
			size_ll = ustrtoull(p, &p, 0);
 | 
						|
			parts[i].size = lldiv(size_ll, 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;
 | 
						|
			start_ll = ustrtoull(p, &p, 0);
 | 
						|
			parts[i].start = lldiv(start_ll, dev_desc->blksz);
 | 
						|
			free(val);
 | 
						|
		}
 | 
						|
 | 
						|
		offset += parts[i].size + parts[i].start;
 | 
						|
 | 
						|
		/* 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(*str_disk_guid);
 | 
						|
	free(parts);
 | 
						|
 | 
						|
	return errno;
 | 
						|
}
 | 
						|
 | 
						|
static int gpt_default(struct blk_desc *blk_dev_desc, const char *str_part)
 | 
						|
{
 | 
						|
	int ret;
 | 
						|
	char *str_disk_guid;
 | 
						|
	u8 part_count = 0;
 | 
						|
	struct disk_partition *partitions = NULL;
 | 
						|
 | 
						|
	/* fill partitions */
 | 
						|
	ret = set_gpt_info(blk_dev_desc, 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 */
 | 
						|
	ret = gpt_restore(blk_dev_desc, str_disk_guid, partitions, part_count);
 | 
						|
	free(str_disk_guid);
 | 
						|
	free(partitions);
 | 
						|
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
static int gpt_verify(struct blk_desc *blk_dev_desc, const char *str_part)
 | 
						|
{
 | 
						|
	ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header, gpt_head, 1,
 | 
						|
				     blk_dev_desc->blksz);
 | 
						|
	struct disk_partition *partitions = NULL;
 | 
						|
	gpt_entry *gpt_pte = NULL;
 | 
						|
	char *str_disk_guid;
 | 
						|
	u8 part_count = 0;
 | 
						|
	int ret = 0;
 | 
						|
 | 
						|
	/* fill partitions */
 | 
						|
	ret = set_gpt_info(blk_dev_desc, str_part,
 | 
						|
			&str_disk_guid, &partitions, &part_count);
 | 
						|
	if (ret) {
 | 
						|
		if (ret == -1) {
 | 
						|
			printf("No partition list provided - only basic check\n");
 | 
						|
			ret = gpt_verify_headers(blk_dev_desc, gpt_head,
 | 
						|
						 &gpt_pte);
 | 
						|
			goto out;
 | 
						|
		}
 | 
						|
		if (ret == -2)
 | 
						|
			printf("Missing disk guid\n");
 | 
						|
		if ((ret == -3) || (ret == -4))
 | 
						|
			printf("Partition list incomplete\n");
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	/* Check partition layout with provided pattern */
 | 
						|
	ret = gpt_verify_partitions(blk_dev_desc, partitions, part_count,
 | 
						|
				    gpt_head, &gpt_pte);
 | 
						|
	free(str_disk_guid);
 | 
						|
	free(partitions);
 | 
						|
 out:
 | 
						|
	free(gpt_pte);
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
static int do_disk_guid(struct blk_desc *dev_desc, char * const namestr)
 | 
						|
{
 | 
						|
	int ret;
 | 
						|
	char disk_guid[UUID_STR_LEN + 1];
 | 
						|
 | 
						|
	ret = get_disk_guid(dev_desc, disk_guid);
 | 
						|
	if (ret < 0)
 | 
						|
		return CMD_RET_FAILURE;
 | 
						|
 | 
						|
	if (namestr)
 | 
						|
		env_set(namestr, disk_guid);
 | 
						|
	else
 | 
						|
		printf("%s\n", disk_guid);
 | 
						|
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
#ifdef CONFIG_CMD_GPT_RENAME
 | 
						|
static int do_rename_gpt_parts(struct blk_desc *dev_desc, char *subcomm,
 | 
						|
			       char *name1, char *name2)
 | 
						|
{
 | 
						|
	struct list_head *pos;
 | 
						|
	struct disk_part *curr;
 | 
						|
	struct disk_partition *new_partitions = NULL;
 | 
						|
	char disk_guid[UUID_STR_LEN + 1];
 | 
						|
	char *partitions_list, *str_disk_guid = NULL;
 | 
						|
	u8 part_count = 0;
 | 
						|
	int partlistlen, ret, numparts = 0, partnum, i = 1, ctr1 = 0, ctr2 = 0;
 | 
						|
 | 
						|
	if ((subcomm == NULL) || (name1 == NULL) || (name2 == NULL) ||
 | 
						|
	    (strcmp(subcomm, "swap") && (strcmp(subcomm, "rename"))))
 | 
						|
		return -EINVAL;
 | 
						|
 | 
						|
	ret = get_disk_guid(dev_desc, disk_guid);
 | 
						|
	if (ret < 0)
 | 
						|
		return ret;
 | 
						|
	/*
 | 
						|
	 * Allocates disk_partitions, requiring matching call to del_gpt_info()
 | 
						|
	 * if successful.
 | 
						|
	 */
 | 
						|
	numparts = get_gpt_info(dev_desc);
 | 
						|
	if (numparts <=  0)
 | 
						|
		return numparts ? numparts : -ENODEV;
 | 
						|
 | 
						|
	partlistlen = calc_parts_list_len(numparts);
 | 
						|
	partitions_list = malloc(partlistlen);
 | 
						|
	if (!partitions_list) {
 | 
						|
		del_gpt_info();
 | 
						|
		return -ENOMEM;
 | 
						|
	}
 | 
						|
	memset(partitions_list, '\0', partlistlen);
 | 
						|
 | 
						|
	ret = create_gpt_partitions_list(numparts, disk_guid, partitions_list);
 | 
						|
	if (ret < 0) {
 | 
						|
		free(partitions_list);
 | 
						|
		return ret;
 | 
						|
	}
 | 
						|
	/*
 | 
						|
	 * Uncomment the following line to print a string that 'gpt write'
 | 
						|
	 * or 'gpt verify' will accept as input.
 | 
						|
	 */
 | 
						|
	debug("OLD partitions_list is %s with %u chars\n", partitions_list,
 | 
						|
	      (unsigned)strlen(partitions_list));
 | 
						|
 | 
						|
	/* set_gpt_info allocates new_partitions and str_disk_guid */
 | 
						|
	ret = set_gpt_info(dev_desc, partitions_list, &str_disk_guid,
 | 
						|
			   &new_partitions, &part_count);
 | 
						|
	if (ret < 0)
 | 
						|
		goto out;
 | 
						|
 | 
						|
	if (!strcmp(subcomm, "swap")) {
 | 
						|
		if ((strlen(name1) > PART_NAME_LEN) || (strlen(name2) > PART_NAME_LEN)) {
 | 
						|
			printf("Names longer than %d characters are truncated.\n", PART_NAME_LEN);
 | 
						|
			ret = -EINVAL;
 | 
						|
			goto out;
 | 
						|
		}
 | 
						|
		list_for_each(pos, &disk_partitions) {
 | 
						|
			curr = list_entry(pos, struct disk_part, list);
 | 
						|
			if (!strcmp((char *)curr->gpt_part_info.name, name1)) {
 | 
						|
				strcpy((char *)curr->gpt_part_info.name, name2);
 | 
						|
				ctr1++;
 | 
						|
			} else if (!strcmp((char *)curr->gpt_part_info.name, name2)) {
 | 
						|
				strcpy((char *)curr->gpt_part_info.name, name1);
 | 
						|
				ctr2++;
 | 
						|
			}
 | 
						|
		}
 | 
						|
		if ((ctr1 + ctr2 < 2) || (ctr1 != ctr2)) {
 | 
						|
			printf("Cannot swap partition names except in pairs.\n");
 | 
						|
			ret = -EINVAL;
 | 
						|
			goto out;
 | 
						|
		}
 | 
						|
	} else { /* rename */
 | 
						|
		if (strlen(name2) > PART_NAME_LEN) {
 | 
						|
			printf("Names longer than %d characters are truncated.\n", PART_NAME_LEN);
 | 
						|
			ret = -EINVAL;
 | 
						|
			goto out;
 | 
						|
		}
 | 
						|
		partnum = (int)simple_strtol(name1, NULL, 10);
 | 
						|
		if ((partnum < 0) || (partnum > numparts)) {
 | 
						|
			printf("Illegal partition number %s\n", name1);
 | 
						|
			ret = -EINVAL;
 | 
						|
			goto out;
 | 
						|
		}
 | 
						|
		ret = part_get_info(dev_desc, partnum, new_partitions);
 | 
						|
		if (ret < 0)
 | 
						|
			goto out;
 | 
						|
 | 
						|
		/* U-Boot partition numbering starts at 1 */
 | 
						|
		list_for_each(pos, &disk_partitions) {
 | 
						|
			curr = list_entry(pos, struct disk_part, list);
 | 
						|
			if (i == partnum) {
 | 
						|
				strcpy((char *)curr->gpt_part_info.name, name2);
 | 
						|
				break;
 | 
						|
			}
 | 
						|
			i++;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	ret = create_gpt_partitions_list(numparts, disk_guid, partitions_list);
 | 
						|
	if (ret < 0)
 | 
						|
		goto out;
 | 
						|
	debug("NEW partitions_list is %s with %u chars\n", partitions_list,
 | 
						|
	      (unsigned)strlen(partitions_list));
 | 
						|
 | 
						|
	ret = set_gpt_info(dev_desc, partitions_list, &str_disk_guid,
 | 
						|
			   &new_partitions, &part_count);
 | 
						|
	/*
 | 
						|
	 * Even though valid pointers are here passed into set_gpt_info(),
 | 
						|
	 * it mallocs again, and there's no way to tell which failed.
 | 
						|
	 */
 | 
						|
	if (ret < 0)
 | 
						|
		goto out;
 | 
						|
 | 
						|
	debug("Writing new partition table\n");
 | 
						|
	ret = gpt_restore(dev_desc, disk_guid, new_partitions, numparts);
 | 
						|
	if (ret < 0) {
 | 
						|
		printf("Writing new partition table failed\n");
 | 
						|
		goto out;
 | 
						|
	}
 | 
						|
 | 
						|
	debug("Reading back new partition table\n");
 | 
						|
	/*
 | 
						|
	 * Empty the existing disk_partitions list, as otherwise the memory in
 | 
						|
	 * the original list is unreachable.
 | 
						|
	 */
 | 
						|
	del_gpt_info();
 | 
						|
	numparts = get_gpt_info(dev_desc);
 | 
						|
	if (numparts <=  0) {
 | 
						|
		ret = numparts ? numparts : -ENODEV;
 | 
						|
		goto out;
 | 
						|
	}
 | 
						|
	printf("new partition table with %d partitions is:\n", numparts);
 | 
						|
	print_gpt_info();
 | 
						|
 out:
 | 
						|
	del_gpt_info();
 | 
						|
#ifdef CONFIG_RANDOM_UUID
 | 
						|
	free(str_disk_guid);
 | 
						|
#endif
 | 
						|
	free(new_partitions);
 | 
						|
	free(partitions_list);
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
/**
 | 
						|
 * 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(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
 | 
						|
{
 | 
						|
	int ret = CMD_RET_SUCCESS;
 | 
						|
	int dev = 0;
 | 
						|
	char *ep;
 | 
						|
	struct blk_desc *blk_dev_desc = NULL;
 | 
						|
 | 
						|
#ifndef CONFIG_CMD_GPT_RENAME
 | 
						|
	if (argc < 4 || argc > 5)
 | 
						|
#else
 | 
						|
	if (argc < 4 || argc > 6)
 | 
						|
#endif
 | 
						|
		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) && (argc == 5)) {
 | 
						|
		printf("Writing GPT: ");
 | 
						|
		ret = gpt_default(blk_dev_desc, argv[4]);
 | 
						|
	} else if ((strcmp(argv[1], "verify") == 0)) {
 | 
						|
		ret = gpt_verify(blk_dev_desc, argv[4]);
 | 
						|
		printf("Verify GPT: ");
 | 
						|
	} else if (strcmp(argv[1], "guid") == 0) {
 | 
						|
		ret = do_disk_guid(blk_dev_desc, argv[4]);
 | 
						|
#ifdef CONFIG_CMD_GPT_RENAME
 | 
						|
	} else if (strcmp(argv[1], "read") == 0) {
 | 
						|
		ret = do_get_gpt_info(blk_dev_desc);
 | 
						|
	} else if ((strcmp(argv[1], "swap") == 0) ||
 | 
						|
		   (strcmp(argv[1], "rename") == 0)) {
 | 
						|
		ret = do_rename_gpt_parts(blk_dev_desc, argv[1], argv[4], argv[5]);
 | 
						|
#endif
 | 
						|
	} else {
 | 
						|
		return CMD_RET_USAGE;
 | 
						|
	}
 | 
						|
 | 
						|
	if (ret) {
 | 
						|
		printf("error!\n");
 | 
						|
		return CMD_RET_FAILURE;
 | 
						|
	}
 | 
						|
 | 
						|
	printf("success!\n");
 | 
						|
	return CMD_RET_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
U_BOOT_CMD(gpt, CONFIG_SYS_MAXARGS, 1, do_gpt,
 | 
						|
	"GUID Partition Table",
 | 
						|
	"<command> <interface> <dev> <partitions_list>\n"
 | 
						|
	" - GUID partition table restoration and validity check\n"
 | 
						|
	" Restore or verify GPT information on a device connected\n"
 | 
						|
	" to interface\n"
 | 
						|
	" Example usage:\n"
 | 
						|
	" gpt write mmc 0 $partitions\n"
 | 
						|
	" gpt verify mmc 0 $partitions\n"
 | 
						|
	" gpt guid <interface> <dev>\n"
 | 
						|
	"    - print disk GUID\n"
 | 
						|
	" gpt guid <interface> <dev> <varname>\n"
 | 
						|
	"    - set environment variable to disk GUID\n"
 | 
						|
	" Example usage:\n"
 | 
						|
	" gpt guid mmc 0\n"
 | 
						|
	" gpt guid mmc 0 varname\n"
 | 
						|
#ifdef CONFIG_CMD_GPT_RENAME
 | 
						|
	"gpt partition renaming commands:\n"
 | 
						|
	" gpt read <interface> <dev>\n"
 | 
						|
	"    - read GPT into a data structure for manipulation\n"
 | 
						|
	" gpt swap <interface> <dev> <name1> <name2>\n"
 | 
						|
	"    - change all partitions named name1 to name2\n"
 | 
						|
	"      and vice-versa\n"
 | 
						|
	" gpt rename <interface> <dev> <part> <name>\n"
 | 
						|
	"    - rename the specified partition\n"
 | 
						|
	" Example usage:\n"
 | 
						|
	" gpt swap mmc 0 foo bar\n"
 | 
						|
	" gpt rename mmc 0 3 foo\n"
 | 
						|
#endif
 | 
						|
);
 |