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