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