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