xref: /openbmc/u-boot/cmd/gpt.c (revision 894c3ad2)
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * cmd_gpt.c -- GPT (GUID Partition Table) handling command
4  *
5  * Copyright (C) 2015
6  * Lukasz Majewski <l.majewski@majess.pl>
7  *
8  * Copyright (C) 2012 Samsung Electronics
9  * author: Lukasz Majewski <l.majewski@samsung.com>
10  * author: Piotr Wilczek <p.wilczek@samsung.com>
11  */
12 
13 #include <common.h>
14 #include <malloc.h>
15 #include <command.h>
16 #include <part_efi.h>
17 #include <exports.h>
18 #include <linux/ctype.h>
19 #include <div64.h>
20 #include <memalign.h>
21 #include <linux/compat.h>
22 #include <linux/sizes.h>
23 #include <stdlib.h>
24 
25 static LIST_HEAD(disk_partitions);
26 
27 /**
28  * extract_env(): Expand env name from string format '&{env_name}'
29  *                and return pointer to the env (if the env is set)
30  *
31  * @param str - pointer to string
32  * @param env - pointer to pointer to extracted env
33  *
34  * @return - zero on successful expand and env is set
35  */
36 static int extract_env(const char *str, char **env)
37 {
38 	int ret = -1;
39 	char *e, *s;
40 #ifdef CONFIG_RANDOM_UUID
41 	char uuid_str[UUID_STR_LEN + 1];
42 #endif
43 
44 	if (!str || strlen(str) < 4)
45 		return -1;
46 
47 	if (!((strncmp(str, "${", 2) == 0) && (str[strlen(str) - 1] == '}')))
48 		return -1;
49 
50 	s = strdup(str);
51 	if (s == NULL)
52 		return -1;
53 
54 	memset(s + strlen(s) - 1, '\0', 1);
55 	memmove(s, s + 2, strlen(s) - 1);
56 
57 	e = env_get(s);
58 	if (e == NULL) {
59 #ifdef CONFIG_RANDOM_UUID
60 		debug("%s unset. ", str);
61 		gen_rand_uuid_str(uuid_str, UUID_STR_FORMAT_GUID);
62 		env_set(s, uuid_str);
63 
64 		e = env_get(s);
65 		if (e) {
66 			debug("Set to random.\n");
67 			ret = 0;
68 		} else {
69 			debug("Can't get random UUID.\n");
70 		}
71 #else
72 		debug("%s unset.\n", str);
73 #endif
74 	} else {
75 		debug("%s get from environment.\n", str);
76 		ret = 0;
77 	}
78 
79 	*env = e;
80 	free(s);
81 
82 	return ret;
83 }
84 
85 /**
86  * extract_val(): Extract value from a key=value pair list (comma separated).
87  *                Only value for the given key is returend.
88  *                Function allocates memory for the value, remember to free!
89  *
90  * @param str - pointer to string with key=values pairs
91  * @param key - pointer to the key to search for
92  *
93  * @return - pointer to allocated string with the value
94  */
95 static char *extract_val(const char *str, const char *key)
96 {
97 	char *v, *k;
98 	char *s, *strcopy;
99 	char *new = NULL;
100 
101 	strcopy = strdup(str);
102 	if (strcopy == NULL)
103 		return NULL;
104 
105 	s = strcopy;
106 	while (s) {
107 		v = strsep(&s, ",");
108 		if (!v)
109 			break;
110 		k = strsep(&v, "=");
111 		if (!k)
112 			break;
113 		if  (strcmp(k, key) == 0) {
114 			new = strdup(v);
115 			break;
116 		}
117 	}
118 
119 	free(strcopy);
120 
121 	return new;
122 }
123 
124 /**
125  * found_key(): Found key without value in parameter list (comma separated).
126  *
127  * @param str - pointer to string with key
128  * @param key - pointer to the key to search for
129  *
130  * @return - true on found key
131  */
132 static bool found_key(const char *str, const char *key)
133 {
134 	char *k;
135 	char *s, *strcopy;
136 	bool result = false;
137 
138 	strcopy = strdup(str);
139 	if (!strcopy)
140 		return NULL;
141 
142 	s = strcopy;
143 	while (s) {
144 		k = strsep(&s, ",");
145 		if (!k)
146 			break;
147 		if  (strcmp(k, key) == 0) {
148 			result = true;
149 			break;
150 		}
151 	}
152 
153 	free(strcopy);
154 
155 	return result;
156 }
157 
158 static int calc_parts_list_len(int numparts)
159 {
160 	int partlistlen = UUID_STR_LEN + 1 + strlen("uuid_disk=");
161 	/* for the comma */
162 	partlistlen++;
163 
164 	/* per-partition additions; numparts starts at 1, so this should be correct */
165 	partlistlen += numparts * (strlen("name=,") + PART_NAME_LEN + 1);
166 	/* see part.h for definition of struct disk_partition */
167 	partlistlen += numparts * (strlen("start=MiB,") + sizeof(lbaint_t) + 1);
168 	partlistlen += numparts * (strlen("size=MiB,") + sizeof(lbaint_t) + 1);
169 	partlistlen += numparts * (strlen("uuid=;") + UUID_STR_LEN + 1);
170 	/* for the terminating null */
171 	partlistlen++;
172 	debug("Length of partitions_list is %d for %d partitions\n", partlistlen,
173 	      numparts);
174 	return partlistlen;
175 }
176 
177 #ifdef CONFIG_CMD_GPT_RENAME
178 static void del_gpt_info(void)
179 {
180 	struct list_head *pos = &disk_partitions;
181 	struct disk_part *curr;
182 	while (!list_empty(pos)) {
183 		curr = list_entry(pos->next, struct disk_part, list);
184 		list_del(pos->next);
185 		free(curr);
186 	}
187 }
188 
189 static struct disk_part *allocate_disk_part(disk_partition_t *info, int partnum)
190 {
191 	struct disk_part *newpart;
192 	newpart = calloc(1, sizeof(struct disk_part));
193 	if (!newpart)
194 		return ERR_PTR(-ENOMEM);
195 
196 	newpart->gpt_part_info.start = info->start;
197 	newpart->gpt_part_info.size = info->size;
198 	newpart->gpt_part_info.blksz = info->blksz;
199 	strncpy((char *)newpart->gpt_part_info.name, (const char *)info->name,
200 		PART_NAME_LEN);
201 	newpart->gpt_part_info.name[PART_NAME_LEN - 1] = '\0';
202 	strncpy((char *)newpart->gpt_part_info.type, (const char *)info->type,
203 		PART_TYPE_LEN);
204 	newpart->gpt_part_info.type[PART_TYPE_LEN - 1] = '\0';
205 	newpart->gpt_part_info.bootable = info->bootable;
206 #ifdef CONFIG_PARTITION_UUIDS
207 	strncpy(newpart->gpt_part_info.uuid, (const char *)info->uuid,
208 		UUID_STR_LEN);
209 	/* UUID_STR_LEN is correct, as uuid[]'s length is UUID_STR_LEN+1 chars */
210 	newpart->gpt_part_info.uuid[UUID_STR_LEN] = '\0';
211 #endif
212 	newpart->partnum = partnum;
213 
214 	return newpart;
215 }
216 
217 static void prettyprint_part_size(char *sizestr, lbaint_t partsize,
218 				  lbaint_t blksize)
219 {
220 	unsigned long long partbytes, partmegabytes;
221 
222 	partbytes = partsize * blksize;
223 	partmegabytes = lldiv(partbytes, SZ_1M);
224 	snprintf(sizestr, 16, "%lluMiB", partmegabytes);
225 }
226 
227 static void print_gpt_info(void)
228 {
229 	struct list_head *pos;
230 	struct disk_part *curr;
231 	char partstartstr[16];
232 	char partsizestr[16];
233 
234 	list_for_each(pos, &disk_partitions) {
235 		curr = list_entry(pos, struct disk_part, list);
236 		prettyprint_part_size(partstartstr, curr->gpt_part_info.start,
237 				      curr->gpt_part_info.blksz);
238 		prettyprint_part_size(partsizestr, curr->gpt_part_info.size,
239 				      curr->gpt_part_info.blksz);
240 
241 		printf("Partition %d:\n", curr->partnum);
242 		printf("Start %s, size %s\n", partstartstr, partsizestr);
243 		printf("Block size %lu, name %s\n", curr->gpt_part_info.blksz,
244 		       curr->gpt_part_info.name);
245 		printf("Type %s, bootable %d\n", curr->gpt_part_info.type,
246 		       curr->gpt_part_info.bootable);
247 #ifdef CONFIG_PARTITION_UUIDS
248 		printf("UUID %s\n", curr->gpt_part_info.uuid);
249 #endif
250 		printf("\n");
251 	}
252 }
253 
254 /*
255  * create the string that upstream 'gpt write' command will accept as an
256  * argument
257  *
258  * From doc/README.gpt, Format of partitions layout:
259  *    "uuid_disk=...;name=u-boot,size=60MiB,uuid=...;
260  *	name=kernel,size=60MiB,uuid=...;"
261  * The fields 'name' and 'size' are mandatory for every partition.
262  * The field 'start' is optional. The fields 'uuid' and 'uuid_disk'
263  * are optional if CONFIG_RANDOM_UUID is enabled.
264  */
265 static int create_gpt_partitions_list(int numparts, const char *guid,
266 				      char *partitions_list)
267 {
268 	struct list_head *pos;
269 	struct disk_part *curr;
270 	char partstr[PART_NAME_LEN + 1];
271 
272 	if (!partitions_list)
273 		return -EINVAL;
274 
275 	strcpy(partitions_list, "uuid_disk=");
276 	strncat(partitions_list, guid, UUID_STR_LEN + 1);
277 	strcat(partitions_list, ";");
278 
279 	list_for_each(pos, &disk_partitions) {
280 		curr = list_entry(pos, struct disk_part, list);
281 		strcat(partitions_list, "name=");
282 		strncat(partitions_list, (const char *)curr->gpt_part_info.name,
283 			PART_NAME_LEN + 1);
284 		sprintf(partstr, ",start=0x%llx",
285 			(unsigned long long)curr->gpt_part_info.start *
286 					    curr->gpt_part_info.blksz);
287 		/* one extra byte for NULL */
288 		strncat(partitions_list, partstr, PART_NAME_LEN + 1);
289 		sprintf(partstr, ",size=0x%llx",
290 			(unsigned long long)curr->gpt_part_info.size *
291 					    curr->gpt_part_info.blksz);
292 		strncat(partitions_list, partstr, PART_NAME_LEN + 1);
293 
294 		strcat(partitions_list, ",uuid=");
295 		strncat(partitions_list, curr->gpt_part_info.uuid,
296 			UUID_STR_LEN + 1);
297 		strcat(partitions_list, ";");
298 	}
299 	return 0;
300 }
301 
302 /*
303  * read partition info into disk_partitions list where
304  * it can be printed or modified
305  */
306 static int get_gpt_info(struct blk_desc *dev_desc)
307 {
308 	/* start partition numbering at 1, as U-Boot does */
309 	int valid_parts = 0, p, ret;
310 	disk_partition_t info;
311 	struct disk_part *new_disk_part;
312 
313 	/*
314 	 * Always re-read partition info from device, in case
315 	 * it has changed
316 	 */
317 	INIT_LIST_HEAD(&disk_partitions);
318 
319 	for (p = 1; p <= MAX_SEARCH_PARTITIONS; p++) {
320 		ret = part_get_info(dev_desc, p, &info);
321 		if (ret)
322 			continue;
323 
324 		/* Add 1 here because counter is zero-based but p1 is
325 		   the first partition */
326 		new_disk_part = allocate_disk_part(&info, valid_parts+1);
327 		if (IS_ERR(new_disk_part))
328 			goto out;
329 
330 		list_add_tail(&new_disk_part->list, &disk_partitions);
331 		valid_parts++;
332 	}
333 	if (valid_parts == 0) {
334 		printf("** No valid partitions found **\n");
335 		goto out;
336 	}
337 	return valid_parts;
338  out:
339 	if (valid_parts >= 1)
340 		del_gpt_info();
341 	return -ENODEV;
342 }
343 
344 /* a wrapper to test get_gpt_info */
345 static int do_get_gpt_info(struct blk_desc *dev_desc)
346 {
347 	int ret;
348 
349 	ret = get_gpt_info(dev_desc);
350 	if (ret > 0) {
351 		print_gpt_info();
352 		del_gpt_info();
353 		return 0;
354 	}
355 	return ret;
356 }
357 #endif
358 
359 /**
360  * set_gpt_info(): Fill partition information from string
361  *		function allocates memory, remember to free!
362  *
363  * @param dev_desc - pointer block device descriptor
364  * @param str_part - pointer to string with partition information
365  * @param str_disk_guid - pointer to pointer to allocated string with disk guid
366  * @param partitions - pointer to pointer to allocated partitions array
367  * @param parts_count - number of partitions
368  *
369  * @return - zero on success, otherwise error
370  *
371  */
372 static int set_gpt_info(struct blk_desc *dev_desc,
373 			const char *str_part,
374 			char **str_disk_guid,
375 			disk_partition_t **partitions,
376 			u8 *parts_count)
377 {
378 	char *tok, *str, *s;
379 	int i;
380 	char *val, *p;
381 	int p_count;
382 	disk_partition_t *parts;
383 	int errno = 0;
384 	uint64_t size_ll, start_ll;
385 	lbaint_t offset = 0;
386 	int max_str_part = calc_parts_list_len(MAX_SEARCH_PARTITIONS);
387 
388 	debug("%s:  lba num: 0x%x %d\n", __func__,
389 	      (unsigned int)dev_desc->lba, (unsigned int)dev_desc->lba);
390 
391 	if (str_part == NULL)
392 		return -1;
393 
394 	str = strdup(str_part);
395 	if (str == NULL)
396 		return -ENOMEM;
397 
398 	/* extract disk guid */
399 	s = str;
400 	val = extract_val(str, "uuid_disk");
401 	if (!val) {
402 #ifdef CONFIG_RANDOM_UUID
403 		*str_disk_guid = malloc(UUID_STR_LEN + 1);
404 		if (*str_disk_guid == NULL)
405 			return -ENOMEM;
406 		gen_rand_uuid_str(*str_disk_guid, UUID_STR_FORMAT_STD);
407 #else
408 		free(str);
409 		return -2;
410 #endif
411 	} else {
412 		val = strsep(&val, ";");
413 		if (extract_env(val, &p))
414 			p = val;
415 		*str_disk_guid = strdup(p);
416 		free(val);
417 		/* Move s to first partition */
418 		strsep(&s, ";");
419 	}
420 	if (s == NULL) {
421 		printf("Error: is the partitions string NULL-terminated?\n");
422 		return -EINVAL;
423 	}
424 	if (strnlen(s, max_str_part) == 0)
425 		return -3;
426 
427 	i = strnlen(s, max_str_part) - 1;
428 	if (s[i] == ';')
429 		s[i] = '\0';
430 
431 	/* calculate expected number of partitions */
432 	p_count = 1;
433 	p = s;
434 	while (*p) {
435 		if (*p++ == ';')
436 			p_count++;
437 	}
438 
439 	/* allocate memory for partitions */
440 	parts = calloc(sizeof(disk_partition_t), p_count);
441 	if (parts == NULL)
442 		return -ENOMEM;
443 
444 	/* retrieve partitions data from string */
445 	for (i = 0; i < p_count; i++) {
446 		tok = strsep(&s, ";");
447 
448 		if (tok == NULL)
449 			break;
450 
451 		/* uuid */
452 		val = extract_val(tok, "uuid");
453 		if (!val) {
454 			/* 'uuid' is optional if random uuid's are enabled */
455 #ifdef CONFIG_RANDOM_UUID
456 			gen_rand_uuid_str(parts[i].uuid, UUID_STR_FORMAT_STD);
457 #else
458 			errno = -4;
459 			goto err;
460 #endif
461 		} else {
462 			if (extract_env(val, &p))
463 				p = val;
464 			if (strnlen(p, max_str_part) >= sizeof(parts[i].uuid)) {
465 				printf("Wrong uuid format for partition %d\n", i);
466 				errno = -4;
467 				goto err;
468 			}
469 			strncpy((char *)parts[i].uuid, p, max_str_part);
470 			free(val);
471 		}
472 #ifdef CONFIG_PARTITION_TYPE_GUID
473 		/* guid */
474 		val = extract_val(tok, "type");
475 		if (val) {
476 			/* 'type' is optional */
477 			if (extract_env(val, &p))
478 				p = val;
479 			if (strnlen(p, max_str_part) >= sizeof(parts[i].type_guid)) {
480 				printf("Wrong type guid format for partition %d\n",
481 				       i);
482 				errno = -4;
483 				goto err;
484 			}
485 			strncpy((char *)parts[i].type_guid, p, max_str_part);
486 			free(val);
487 		}
488 #endif
489 		/* name */
490 		val = extract_val(tok, "name");
491 		if (!val) { /* name is mandatory */
492 			errno = -4;
493 			goto err;
494 		}
495 		if (extract_env(val, &p))
496 			p = val;
497 		if (strnlen(p, max_str_part) >= sizeof(parts[i].name)) {
498 			errno = -4;
499 			goto err;
500 		}
501 		strncpy((char *)parts[i].name, p, max_str_part);
502 		free(val);
503 
504 		/* size */
505 		val = extract_val(tok, "size");
506 		if (!val) { /* 'size' is mandatory */
507 			errno = -4;
508 			goto err;
509 		}
510 		if (extract_env(val, &p))
511 			p = val;
512 		if ((strcmp(p, "-") == 0)) {
513 			/* Let part efi module to auto extend the size */
514 			parts[i].size = 0;
515 		} else {
516 			size_ll = ustrtoull(p, &p, 0);
517 			parts[i].size = lldiv(size_ll, dev_desc->blksz);
518 		}
519 
520 		free(val);
521 
522 		/* start address */
523 		val = extract_val(tok, "start");
524 		if (val) { /* start address is optional */
525 			if (extract_env(val, &p))
526 				p = val;
527 			start_ll = ustrtoull(p, &p, 0);
528 			parts[i].start = lldiv(start_ll, dev_desc->blksz);
529 			free(val);
530 		}
531 
532 		offset += parts[i].size + parts[i].start;
533 
534 		/* bootable */
535 		if (found_key(tok, "bootable"))
536 			parts[i].bootable = 1;
537 	}
538 
539 	*parts_count = p_count;
540 	*partitions = parts;
541 	free(str);
542 
543 	return 0;
544 err:
545 	free(str);
546 	free(*str_disk_guid);
547 	free(parts);
548 
549 	return errno;
550 }
551 
552 static int gpt_default(struct blk_desc *blk_dev_desc, const char *str_part)
553 {
554 	int ret;
555 	char *str_disk_guid;
556 	u8 part_count = 0;
557 	disk_partition_t *partitions = NULL;
558 
559 	/* fill partitions */
560 	ret = set_gpt_info(blk_dev_desc, str_part,
561 			&str_disk_guid, &partitions, &part_count);
562 	if (ret) {
563 		if (ret == -1)
564 			printf("No partition list provided\n");
565 		if (ret == -2)
566 			printf("Missing disk guid\n");
567 		if ((ret == -3) || (ret == -4))
568 			printf("Partition list incomplete\n");
569 		return -1;
570 	}
571 
572 	/* save partitions layout to disk */
573 	ret = gpt_restore(blk_dev_desc, str_disk_guid, partitions, part_count);
574 	free(str_disk_guid);
575 	free(partitions);
576 
577 	return ret;
578 }
579 
580 static int gpt_verify(struct blk_desc *blk_dev_desc, const char *str_part)
581 {
582 	ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header, gpt_head, 1,
583 				     blk_dev_desc->blksz);
584 	disk_partition_t *partitions = NULL;
585 	gpt_entry *gpt_pte = NULL;
586 	char *str_disk_guid;
587 	u8 part_count = 0;
588 	int ret = 0;
589 
590 	/* fill partitions */
591 	ret = set_gpt_info(blk_dev_desc, str_part,
592 			&str_disk_guid, &partitions, &part_count);
593 	if (ret) {
594 		if (ret == -1) {
595 			printf("No partition list provided - only basic check\n");
596 			ret = gpt_verify_headers(blk_dev_desc, gpt_head,
597 						 &gpt_pte);
598 			goto out;
599 		}
600 		if (ret == -2)
601 			printf("Missing disk guid\n");
602 		if ((ret == -3) || (ret == -4))
603 			printf("Partition list incomplete\n");
604 		return -1;
605 	}
606 
607 	/* Check partition layout with provided pattern */
608 	ret = gpt_verify_partitions(blk_dev_desc, partitions, part_count,
609 				    gpt_head, &gpt_pte);
610 	free(str_disk_guid);
611 	free(partitions);
612  out:
613 	free(gpt_pte);
614 	return ret;
615 }
616 
617 static int do_disk_guid(struct blk_desc *dev_desc, char * const namestr)
618 {
619 	int ret;
620 	char disk_guid[UUID_STR_LEN + 1];
621 
622 	ret = get_disk_guid(dev_desc, disk_guid);
623 	if (ret < 0)
624 		return CMD_RET_FAILURE;
625 
626 	if (namestr)
627 		env_set(namestr, disk_guid);
628 	else
629 		printf("%s\n", disk_guid);
630 
631 	return ret;
632 }
633 
634 #ifdef CONFIG_CMD_GPT_RENAME
635 /*
636  * There are 3 malloc() calls in set_gpt_info() and there is no info about which
637  * failed.
638  */
639 static void set_gpt_cleanup(char **str_disk_guid,
640 			    disk_partition_t **partitions)
641 {
642 #ifdef CONFIG_RANDOM_UUID
643 	if (str_disk_guid)
644 		free(str_disk_guid);
645 #endif
646 	if (partitions)
647 		free(partitions);
648 }
649 
650 static int do_rename_gpt_parts(struct blk_desc *dev_desc, char *subcomm,
651 			       char *name1, char *name2)
652 {
653 	struct list_head *pos;
654 	struct disk_part *curr;
655 	disk_partition_t *new_partitions = NULL;
656 	char disk_guid[UUID_STR_LEN + 1];
657 	char *partitions_list, *str_disk_guid;
658 	u8 part_count = 0;
659 	int partlistlen, ret, numparts = 0, partnum, i = 1, ctr1 = 0, ctr2 = 0;
660 
661 	if ((subcomm == NULL) || (name1 == NULL) || (name2 == NULL) ||
662 	    (strcmp(subcomm, "swap") && (strcmp(subcomm, "rename"))))
663 		return -EINVAL;
664 
665 	ret = get_disk_guid(dev_desc, disk_guid);
666 	if (ret < 0)
667 		return ret;
668 	/*
669 	 * Allocates disk_partitions, requiring matching call to del_gpt_info()
670 	 * if successful.
671 	 */
672 	numparts = get_gpt_info(dev_desc);
673 	if (numparts <=  0)
674 		return numparts ? numparts : -ENODEV;
675 
676 	partlistlen = calc_parts_list_len(numparts);
677 	partitions_list = malloc(partlistlen);
678 	if (!partitions_list) {
679 		del_gpt_info();
680 		return -ENOMEM;
681 	}
682 	memset(partitions_list, '\0', partlistlen);
683 
684 	ret = create_gpt_partitions_list(numparts, disk_guid, partitions_list);
685 	if (ret < 0) {
686 		free(partitions_list);
687 		return ret;
688 	}
689 	/*
690 	 * Uncomment the following line to print a string that 'gpt write'
691 	 * or 'gpt verify' will accept as input.
692 	 */
693 	debug("OLD partitions_list is %s with %u chars\n", partitions_list,
694 	      (unsigned)strlen(partitions_list));
695 
696 	/* set_gpt_info allocates new_partitions and str_disk_guid */
697 	ret = set_gpt_info(dev_desc, partitions_list, &str_disk_guid,
698 			   &new_partitions, &part_count);
699 	if (ret < 0) {
700 		del_gpt_info();
701 		free(partitions_list);
702 		if (ret == -ENOMEM)
703 			set_gpt_cleanup(&str_disk_guid, &new_partitions);
704 		else
705 			goto out;
706 	}
707 
708 	if (!strcmp(subcomm, "swap")) {
709 		if ((strlen(name1) > PART_NAME_LEN) || (strlen(name2) > PART_NAME_LEN)) {
710 			printf("Names longer than %d characters are truncated.\n", PART_NAME_LEN);
711 			ret = -EINVAL;
712 			goto out;
713 		}
714 		list_for_each(pos, &disk_partitions) {
715 			curr = list_entry(pos, struct disk_part, list);
716 			if (!strcmp((char *)curr->gpt_part_info.name, name1)) {
717 				strcpy((char *)curr->gpt_part_info.name, name2);
718 				ctr1++;
719 			} else if (!strcmp((char *)curr->gpt_part_info.name, name2)) {
720 				strcpy((char *)curr->gpt_part_info.name, name1);
721 				ctr2++;
722 			}
723 		}
724 		if ((ctr1 + ctr2 < 2) || (ctr1 != ctr2)) {
725 			printf("Cannot swap partition names except in pairs.\n");
726 			ret = -EINVAL;
727 			goto out;
728 		}
729 	} else { /* rename */
730 		if (strlen(name2) > PART_NAME_LEN) {
731 			printf("Names longer than %d characters are truncated.\n", PART_NAME_LEN);
732 			ret = -EINVAL;
733 			goto out;
734 		}
735 		partnum = (int)simple_strtol(name1, NULL, 10);
736 		if ((partnum < 0) || (partnum > numparts)) {
737 			printf("Illegal partition number %s\n", name1);
738 			ret = -EINVAL;
739 			goto out;
740 		}
741 		ret = part_get_info(dev_desc, partnum, new_partitions);
742 		if (ret < 0)
743 			goto out;
744 
745 		/* U-Boot partition numbering starts at 1 */
746 		list_for_each(pos, &disk_partitions) {
747 			curr = list_entry(pos, struct disk_part, list);
748 			if (i == partnum) {
749 				strcpy((char *)curr->gpt_part_info.name, name2);
750 				break;
751 			}
752 			i++;
753 		}
754 	}
755 
756 	ret = create_gpt_partitions_list(numparts, disk_guid, partitions_list);
757 	if (ret < 0)
758 		goto out;
759 	debug("NEW partitions_list is %s with %u chars\n", partitions_list,
760 	      (unsigned)strlen(partitions_list));
761 
762 	ret = set_gpt_info(dev_desc, partitions_list, &str_disk_guid,
763 			   &new_partitions, &part_count);
764 	/*
765 	 * Even though valid pointers are here passed into set_gpt_info(),
766 	 * it mallocs again, and there's no way to tell which failed.
767 	 */
768 	if (ret < 0) {
769 		del_gpt_info();
770 		free(partitions_list);
771 		if (ret == -ENOMEM)
772 			set_gpt_cleanup(&str_disk_guid, &new_partitions);
773 		else
774 			goto out;
775 	}
776 
777 	debug("Writing new partition table\n");
778 	ret = gpt_restore(dev_desc, disk_guid, new_partitions, numparts);
779 	if (ret < 0) {
780 		printf("Writing new partition table failed\n");
781 		goto out;
782 	}
783 
784 	debug("Reading back new partition table\n");
785 	/*
786 	 * Empty the existing disk_partitions list, as otherwise the memory in
787 	 * the original list is unreachable.
788 	 */
789 	del_gpt_info();
790 	numparts = get_gpt_info(dev_desc);
791 	if (numparts <=  0) {
792 		ret = numparts ? numparts : -ENODEV;
793 		goto out;
794 	}
795 	printf("new partition table with %d partitions is:\n", numparts);
796 	print_gpt_info();
797 	del_gpt_info();
798  out:
799 	free(new_partitions);
800 	free(str_disk_guid);
801 	free(partitions_list);
802 	return ret;
803 }
804 #endif
805 
806 /**
807  * do_gpt(): Perform GPT operations
808  *
809  * @param cmdtp - command name
810  * @param flag
811  * @param argc
812  * @param argv
813  *
814  * @return zero on success; otherwise error
815  */
816 static int do_gpt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
817 {
818 	int ret = CMD_RET_SUCCESS;
819 	int dev = 0;
820 	char *ep;
821 	struct blk_desc *blk_dev_desc = NULL;
822 
823 #ifndef CONFIG_CMD_GPT_RENAME
824 	if (argc < 4 || argc > 5)
825 #else
826 	if (argc < 4 || argc > 6)
827 #endif
828 		return CMD_RET_USAGE;
829 
830 	dev = (int)simple_strtoul(argv[3], &ep, 10);
831 	if (!ep || ep[0] != '\0') {
832 		printf("'%s' is not a number\n", argv[3]);
833 		return CMD_RET_USAGE;
834 	}
835 	blk_dev_desc = blk_get_dev(argv[2], dev);
836 	if (!blk_dev_desc) {
837 		printf("%s: %s dev %d NOT available\n",
838 		       __func__, argv[2], dev);
839 		return CMD_RET_FAILURE;
840 	}
841 
842 	if ((strcmp(argv[1], "write") == 0) && (argc == 5)) {
843 		printf("Writing GPT: ");
844 		ret = gpt_default(blk_dev_desc, argv[4]);
845 	} else if ((strcmp(argv[1], "verify") == 0)) {
846 		ret = gpt_verify(blk_dev_desc, argv[4]);
847 		printf("Verify GPT: ");
848 	} else if (strcmp(argv[1], "guid") == 0) {
849 		ret = do_disk_guid(blk_dev_desc, argv[4]);
850 #ifdef CONFIG_CMD_GPT_RENAME
851 	} else if (strcmp(argv[1], "read") == 0) {
852 		ret = do_get_gpt_info(blk_dev_desc);
853 	} else if ((strcmp(argv[1], "swap") == 0) ||
854 		   (strcmp(argv[1], "rename") == 0)) {
855 		ret = do_rename_gpt_parts(blk_dev_desc, argv[1], argv[4], argv[5]);
856 #endif
857 	} else {
858 		return CMD_RET_USAGE;
859 	}
860 
861 	if (ret) {
862 		printf("error!\n");
863 		return CMD_RET_FAILURE;
864 	}
865 
866 	printf("success!\n");
867 	return CMD_RET_SUCCESS;
868 }
869 
870 U_BOOT_CMD(gpt, CONFIG_SYS_MAXARGS, 1, do_gpt,
871 	"GUID Partition Table",
872 	"<command> <interface> <dev> <partitions_list>\n"
873 	" - GUID partition table restoration and validity check\n"
874 	" Restore or verify GPT information on a device connected\n"
875 	" to interface\n"
876 	" Example usage:\n"
877 	" gpt write mmc 0 $partitions\n"
878 	" gpt verify mmc 0 $partitions\n"
879 	" read <interface> <dev>\n"
880 	"    - read GPT into a data structure for manipulation\n"
881 	" guid <interface> <dev>\n"
882 	"    - print disk GUID\n"
883 	" guid <interface> <dev> <varname>\n"
884 	"    - set environment variable to disk GUID\n"
885 	" Example usage:\n"
886 	" gpt guid mmc 0\n"
887 	" gpt guid mmc 0 varname\n"
888 #ifdef CONFIG_CMD_GPT_RENAME
889 	"gpt partition renaming commands:\n"
890 	"gpt swap <interface> <dev> <name1> <name2>\n"
891 	"    - change all partitions named name1 to name2\n"
892 	"      and vice-versa\n"
893 	"gpt rename <interface> <dev> <part> <name>\n"
894 	"    - rename the specified partition\n"
895 	" Example usage:\n"
896 	" gpt swap mmc 0 foo bar\n"
897 	" gpt rename mmc 0 3 foo\n"
898 #endif
899 );
900