]> git.sur5r.net Git - u-boot/blob - cmd/gpt.c
GPT: read partition table from device into a data structure
[u-boot] / cmd / gpt.c
1 /*
2  * cmd_gpt.c -- GPT (GUID Partition Table) handling command
3  *
4  * Copyright (C) 2015
5  * Lukasz Majewski <l.majewski@majess.pl>
6  *
7  * Copyright (C) 2012 Samsung Electronics
8  * author: Lukasz Majewski <l.majewski@samsung.com>
9  * author: Piotr Wilczek <p.wilczek@samsung.com>
10  *
11  * SPDX-License-Identifier:     GPL-2.0+
12  */
13
14 #include <common.h>
15 #include <malloc.h>
16 #include <command.h>
17 #include <part_efi.h>
18 #include <exports.h>
19 #include <linux/ctype.h>
20 #include <div64.h>
21 #include <memalign.h>
22 #include <linux/compat.h>
23
24 static LIST_HEAD(disk_partitions);
25
26 /**
27  * extract_env(): Expand env name from string format '&{env_name}'
28  *                and return pointer to the env (if the env is set)
29  *
30  * @param str - pointer to string
31  * @param env - pointer to pointer to extracted env
32  *
33  * @return - zero on successful expand and env is set
34  */
35 static int extract_env(const char *str, char **env)
36 {
37         int ret = -1;
38         char *e, *s;
39 #ifdef CONFIG_RANDOM_UUID
40         char uuid_str[UUID_STR_LEN + 1];
41 #endif
42
43         if (!str || strlen(str) < 4)
44                 return -1;
45
46         if (!((strncmp(str, "${", 2) == 0) && (str[strlen(str) - 1] == '}')))
47                 return -1;
48
49         s = strdup(str);
50         if (s == NULL)
51                 return -1;
52
53         memset(s + strlen(s) - 1, '\0', 1);
54         memmove(s, s + 2, strlen(s) - 1);
55
56         e = getenv(s);
57         if (e == NULL) {
58 #ifdef CONFIG_RANDOM_UUID
59                 debug("%s unset. ", str);
60                 gen_rand_uuid_str(uuid_str, UUID_STR_FORMAT_GUID);
61                 setenv(s, uuid_str);
62
63                 e = getenv(s);
64                 if (e) {
65                         debug("Set to random.\n");
66                         ret = 0;
67                 } else {
68                         debug("Can't get random UUID.\n");
69                 }
70 #else
71                 debug("%s unset.\n", str);
72 #endif
73         } else {
74                 debug("%s get from environment.\n", str);
75                 ret = 0;
76         }
77
78         *env = e;
79         free(s);
80
81         return ret;
82 }
83
84 /**
85  * extract_val(): Extract value from a key=value pair list (comma separated).
86  *                Only value for the given key is returend.
87  *                Function allocates memory for the value, remember to free!
88  *
89  * @param str - pointer to string with key=values pairs
90  * @param key - pointer to the key to search for
91  *
92  * @return - pointer to allocated string with the value
93  */
94 static char *extract_val(const char *str, const char *key)
95 {
96         char *v, *k;
97         char *s, *strcopy;
98         char *new = NULL;
99
100         strcopy = strdup(str);
101         if (strcopy == NULL)
102                 return NULL;
103
104         s = strcopy;
105         while (s) {
106                 v = strsep(&s, ",");
107                 if (!v)
108                         break;
109                 k = strsep(&v, "=");
110                 if (!k)
111                         break;
112                 if  (strcmp(k, key) == 0) {
113                         new = strdup(v);
114                         break;
115                 }
116         }
117
118         free(strcopy);
119
120         return new;
121 }
122
123 /**
124  * found_key(): Found key without value in parameter list (comma separated).
125  *
126  * @param str - pointer to string with key
127  * @param key - pointer to the key to search for
128  *
129  * @return - true on found key
130  */
131 static bool found_key(const char *str, const char *key)
132 {
133         char *k;
134         char *s, *strcopy;
135         bool result = false;
136
137         strcopy = strdup(str);
138         if (!strcopy)
139                 return NULL;
140
141         s = strcopy;
142         while (s) {
143                 k = strsep(&s, ",");
144                 if (!k)
145                         break;
146                 if  (strcmp(k, key) == 0) {
147                         result = true;
148                         break;
149                 }
150         }
151
152         free(strcopy);
153
154         return result;
155 }
156
157 #ifdef CONFIG_CMD_GPT_RENAME
158 static void del_gpt_info(void)
159 {
160         struct list_head *pos = &disk_partitions;
161         struct disk_part *curr;
162         while (!list_empty(pos)) {
163                 curr = list_entry(pos->next, struct disk_part, list);
164                 list_del(pos->next);
165                 free(curr);
166         }
167 }
168
169 static struct disk_part *allocate_disk_part(disk_partition_t *info, int partnum)
170 {
171         struct disk_part *newpart;
172         newpart = malloc(sizeof(*newpart));
173         if (!newpart)
174                 return ERR_PTR(-ENOMEM);
175         memset(newpart, '\0', sizeof(newpart));
176
177         newpart->gpt_part_info.start = info->start;
178         newpart->gpt_part_info.size = info->size;
179         newpart->gpt_part_info.blksz = info->blksz;
180         strncpy((char *)newpart->gpt_part_info.name, (const char *)info->name,
181                 PART_NAME_LEN);
182         newpart->gpt_part_info.name[PART_NAME_LEN - 1] = '\0';
183         strncpy((char *)newpart->gpt_part_info.type, (const char *)info->type,
184                 PART_TYPE_LEN);
185         newpart->gpt_part_info.type[PART_TYPE_LEN - 1] = '\0';
186         newpart->gpt_part_info.bootable = info->bootable;
187 #ifdef CONFIG_PARTITION_UUIDS
188         strncpy(newpart->gpt_part_info.uuid, (const char *)info->uuid,
189                 UUID_STR_LEN);
190         /* UUID_STR_LEN is correct, as uuid[]'s length is UUID_STR_LEN+1 chars */
191         newpart->gpt_part_info.uuid[UUID_STR_LEN] = '\0';
192 #endif
193         newpart->partnum = partnum;
194
195         return newpart;
196 }
197
198 static void print_gpt_info(void)
199 {
200         struct list_head *pos;
201         struct disk_part *curr;
202
203         list_for_each(pos, &disk_partitions) {
204                 curr = list_entry(pos, struct disk_part, list);
205                 printf("Partition %d:\n", curr->partnum);
206                 printf("1st block %x, size %x\n", (unsigned)curr->gpt_part_info.start,
207                        (unsigned)curr->gpt_part_info.size);
208                 printf("Block size %lu, name %s\n", curr->gpt_part_info.blksz,
209                        curr->gpt_part_info.name);
210                 printf("Type %s, bootable %d\n", curr->gpt_part_info.type,
211                        curr->gpt_part_info.bootable);
212 #ifdef CONFIG_PARTITION_UUIDS
213                 printf("UUID %s\n", curr->gpt_part_info.uuid);
214 #endif
215                 printf("\n");
216         }
217 }
218
219 /*
220  * read partition info into disk_partitions list where
221  * it can be printed or modified
222  */
223 static int get_gpt_info(struct blk_desc *dev_desc)
224 {
225         /* start partition numbering at 1, as U-Boot does */
226         int valid_parts = 0, p, ret;
227         disk_partition_t info;
228         struct disk_part *new_disk_part;
229
230         if (disk_partitions.next == NULL)
231                 INIT_LIST_HEAD(&disk_partitions);
232
233         for (p = 1; p <= MAX_SEARCH_PARTITIONS; p++) {
234                 ret = part_get_info(dev_desc, p, &info);
235                 if (ret)
236                         continue;
237
238                 /* Add 1 here because counter is zero-based but p1 is
239                    the first partition */
240                 new_disk_part = allocate_disk_part(&info, valid_parts+1);
241                 if (IS_ERR(new_disk_part))
242                         goto out;
243
244                 list_add_tail(&new_disk_part->list, &disk_partitions);
245                 valid_parts++;
246         }
247         if (valid_parts == 0) {
248                 printf("** No valid partitions found **\n");
249                 goto out;
250         }
251         return valid_parts;
252  out:
253         if (valid_parts >= 1)
254                 del_gpt_info();
255         return -ENODEV;
256 }
257
258 /* a wrapper to test get_gpt_info */
259 static int do_get_gpt_info(struct blk_desc *dev_desc)
260 {
261         int ret;
262
263         ret = get_gpt_info(dev_desc);
264         if (ret > 0) {
265                 print_gpt_info();
266                 del_gpt_info();
267                 return 0;
268         }
269         return ret;
270 }
271 #endif
272
273 /**
274  * set_gpt_info(): Fill partition information from string
275  *              function allocates memory, remember to free!
276  *
277  * @param dev_desc - pointer block device descriptor
278  * @param str_part - pointer to string with partition information
279  * @param str_disk_guid - pointer to pointer to allocated string with disk guid
280  * @param partitions - pointer to pointer to allocated partitions array
281  * @param parts_count - number of partitions
282  *
283  * @return - zero on success, otherwise error
284  *
285  */
286 static int set_gpt_info(struct blk_desc *dev_desc,
287                         const char *str_part,
288                         char **str_disk_guid,
289                         disk_partition_t **partitions,
290                         u8 *parts_count)
291 {
292         char *tok, *str, *s;
293         int i;
294         char *val, *p;
295         int p_count;
296         disk_partition_t *parts;
297         int errno = 0;
298         uint64_t size_ll, start_ll;
299         lbaint_t offset = 0;
300
301         debug("%s:  lba num: 0x%x %d\n", __func__,
302               (unsigned int)dev_desc->lba, (unsigned int)dev_desc->lba);
303
304         if (str_part == NULL)
305                 return -1;
306
307         str = strdup(str_part);
308
309         /* extract disk guid */
310         s = str;
311         val = extract_val(str, "uuid_disk");
312         if (!val) {
313 #ifdef CONFIG_RANDOM_UUID
314                 *str_disk_guid = malloc(UUID_STR_LEN + 1);
315                 gen_rand_uuid_str(*str_disk_guid, UUID_STR_FORMAT_STD);
316 #else
317                 free(str);
318                 return -2;
319 #endif
320         } else {
321                 val = strsep(&val, ";");
322                 if (extract_env(val, &p))
323                         p = val;
324                 *str_disk_guid = strdup(p);
325                 free(val);
326                 /* Move s to first partition */
327                 strsep(&s, ";");
328         }
329         if (strlen(s) == 0)
330                 return -3;
331
332         i = strlen(s) - 1;
333         if (s[i] == ';')
334                 s[i] = '\0';
335
336         /* calculate expected number of partitions */
337         p_count = 1;
338         p = s;
339         while (*p) {
340                 if (*p++ == ';')
341                         p_count++;
342         }
343
344         /* allocate memory for partitions */
345         parts = calloc(sizeof(disk_partition_t), p_count);
346
347         /* retrieve partitions data from string */
348         for (i = 0; i < p_count; i++) {
349                 tok = strsep(&s, ";");
350
351                 if (tok == NULL)
352                         break;
353
354                 /* uuid */
355                 val = extract_val(tok, "uuid");
356                 if (!val) {
357                         /* 'uuid' is optional if random uuid's are enabled */
358 #ifdef CONFIG_RANDOM_UUID
359                         gen_rand_uuid_str(parts[i].uuid, UUID_STR_FORMAT_STD);
360 #else
361                         errno = -4;
362                         goto err;
363 #endif
364                 } else {
365                         if (extract_env(val, &p))
366                                 p = val;
367                         if (strlen(p) >= sizeof(parts[i].uuid)) {
368                                 printf("Wrong uuid format for partition %d\n", i);
369                                 errno = -4;
370                                 goto err;
371                         }
372                         strcpy((char *)parts[i].uuid, p);
373                         free(val);
374                 }
375 #ifdef CONFIG_PARTITION_TYPE_GUID
376                 /* guid */
377                 val = extract_val(tok, "type");
378                 if (val) {
379                         /* 'type' is optional */
380                         if (extract_env(val, &p))
381                                 p = val;
382                         if (strlen(p) >= sizeof(parts[i].type_guid)) {
383                                 printf("Wrong type guid format for partition %d\n",
384                                        i);
385                                 errno = -4;
386                                 goto err;
387                         }
388                         strcpy((char *)parts[i].type_guid, p);
389                         free(val);
390                 }
391 #endif
392                 /* name */
393                 val = extract_val(tok, "name");
394                 if (!val) { /* name is mandatory */
395                         errno = -4;
396                         goto err;
397                 }
398                 if (extract_env(val, &p))
399                         p = val;
400                 if (strlen(p) >= sizeof(parts[i].name)) {
401                         errno = -4;
402                         goto err;
403                 }
404                 strcpy((char *)parts[i].name, p);
405                 free(val);
406
407                 /* size */
408                 val = extract_val(tok, "size");
409                 if (!val) { /* 'size' is mandatory */
410                         errno = -4;
411                         goto err;
412                 }
413                 if (extract_env(val, &p))
414                         p = val;
415                 if ((strcmp(p, "-") == 0)) {
416                         /* Let part efi module to auto extend the size */
417                         parts[i].size = 0;
418                 } else {
419                         size_ll = ustrtoull(p, &p, 0);
420                         parts[i].size = lldiv(size_ll, dev_desc->blksz);
421                 }
422
423                 free(val);
424
425                 /* start address */
426                 val = extract_val(tok, "start");
427                 if (val) { /* start address is optional */
428                         if (extract_env(val, &p))
429                                 p = val;
430                         start_ll = ustrtoull(p, &p, 0);
431                         parts[i].start = lldiv(start_ll, dev_desc->blksz);
432                         free(val);
433                 }
434
435                 offset += parts[i].size + parts[i].start;
436
437                 /* bootable */
438                 if (found_key(tok, "bootable"))
439                         parts[i].bootable = 1;
440         }
441
442         *parts_count = p_count;
443         *partitions = parts;
444         free(str);
445
446         return 0;
447 err:
448         free(str);
449         free(*str_disk_guid);
450         free(parts);
451
452         return errno;
453 }
454
455 static int gpt_default(struct blk_desc *blk_dev_desc, const char *str_part)
456 {
457         int ret;
458         char *str_disk_guid;
459         u8 part_count = 0;
460         disk_partition_t *partitions = NULL;
461
462         /* fill partitions */
463         ret = set_gpt_info(blk_dev_desc, str_part,
464                         &str_disk_guid, &partitions, &part_count);
465         if (ret) {
466                 if (ret == -1)
467                         printf("No partition list provided\n");
468                 if (ret == -2)
469                         printf("Missing disk guid\n");
470                 if ((ret == -3) || (ret == -4))
471                         printf("Partition list incomplete\n");
472                 return -1;
473         }
474
475         /* save partitions layout to disk */
476         ret = gpt_restore(blk_dev_desc, str_disk_guid, partitions, part_count);
477         free(str_disk_guid);
478         free(partitions);
479
480         return ret;
481 }
482
483 static int gpt_verify(struct blk_desc *blk_dev_desc, const char *str_part)
484 {
485         ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header, gpt_head, 1,
486                                      blk_dev_desc->blksz);
487         disk_partition_t *partitions = NULL;
488         gpt_entry *gpt_pte = NULL;
489         char *str_disk_guid;
490         u8 part_count = 0;
491         int ret = 0;
492
493         /* fill partitions */
494         ret = set_gpt_info(blk_dev_desc, str_part,
495                         &str_disk_guid, &partitions, &part_count);
496         if (ret) {
497                 if (ret == -1) {
498                         printf("No partition list provided - only basic check\n");
499                         ret = gpt_verify_headers(blk_dev_desc, gpt_head,
500                                                  &gpt_pte);
501                         goto out;
502                 }
503                 if (ret == -2)
504                         printf("Missing disk guid\n");
505                 if ((ret == -3) || (ret == -4))
506                         printf("Partition list incomplete\n");
507                 return -1;
508         }
509
510         /* Check partition layout with provided pattern */
511         ret = gpt_verify_partitions(blk_dev_desc, partitions, part_count,
512                                     gpt_head, &gpt_pte);
513         free(str_disk_guid);
514         free(partitions);
515  out:
516         free(gpt_pte);
517         return ret;
518 }
519
520 static int do_disk_guid(struct blk_desc *dev_desc, char * const namestr)
521 {
522         int ret;
523         char disk_guid[UUID_STR_LEN + 1];
524
525         ret = get_disk_guid(dev_desc, disk_guid);
526         if (ret < 0)
527                 return CMD_RET_FAILURE;
528
529         if (namestr)
530                 setenv(namestr, disk_guid);
531         else
532                 printf("%s\n", disk_guid);
533
534         return ret;
535 }
536
537 /**
538  * do_gpt(): Perform GPT operations
539  *
540  * @param cmdtp - command name
541  * @param flag
542  * @param argc
543  * @param argv
544  *
545  * @return zero on success; otherwise error
546  */
547 static int do_gpt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
548 {
549         int ret = CMD_RET_SUCCESS;
550         int dev = 0;
551         char *ep;
552         struct blk_desc *blk_dev_desc = NULL;
553
554         if (argc < 4 || argc > 5)
555                 return CMD_RET_USAGE;
556
557         dev = (int)simple_strtoul(argv[3], &ep, 10);
558         if (!ep || ep[0] != '\0') {
559                 printf("'%s' is not a number\n", argv[3]);
560                 return CMD_RET_USAGE;
561         }
562         blk_dev_desc = blk_get_dev(argv[2], dev);
563         if (!blk_dev_desc) {
564                 printf("%s: %s dev %d NOT available\n",
565                        __func__, argv[2], dev);
566                 return CMD_RET_FAILURE;
567         }
568
569         if ((strcmp(argv[1], "write") == 0) && (argc == 5)) {
570                 printf("Writing GPT: ");
571                 ret = gpt_default(blk_dev_desc, argv[4]);
572         } else if ((strcmp(argv[1], "verify") == 0)) {
573                 ret = gpt_verify(blk_dev_desc, argv[4]);
574                 printf("Verify GPT: ");
575         } else if (strcmp(argv[1], "guid") == 0) {
576                 ret = do_disk_guid(blk_dev_desc, argv[4]);
577 #ifdef CONFIG_CMD_GPT_RENAME
578         } else if (strcmp(argv[1], "read") == 0) {
579                 ret = do_get_gpt_info(blk_dev_desc);
580 #endif
581         } else {
582                 return CMD_RET_USAGE;
583         }
584
585         if (ret) {
586                 printf("error!\n");
587                 return CMD_RET_FAILURE;
588         }
589
590         printf("success!\n");
591         return CMD_RET_SUCCESS;
592 }
593
594 U_BOOT_CMD(gpt, CONFIG_SYS_MAXARGS, 1, do_gpt,
595         "GUID Partition Table",
596         "<command> <interface> <dev> <partitions_list>\n"
597         " - GUID partition table restoration and validity check\n"
598         " Restore or verify GPT information on a device connected\n"
599         " to interface\n"
600         " Example usage:\n"
601         " gpt write mmc 0 $partitions\n"
602         " gpt verify mmc 0 $partitions\n"
603         " read <interface> <dev>\n"
604         "    - read GPT into a data structure for manipulation\n"
605         " guid <interface> <dev>\n"
606         "    - print disk GUID\n"
607         " guid <interface> <dev> <varname>\n"
608         "    - set environment variable to disk GUID\n"
609         " Example usage:\n"
610         " gpt guid mmc 0\n"
611         " gpt guid mmc 0 varname\n"
612 );