]> git.sur5r.net Git - u-boot/blob - cmd/gpt.c
Merge branch 'master' of git://www.denx.de/git/u-boot-imx
[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
23 /**
24  * extract_env(): Expand env name from string format '&{env_name}'
25  *                and return pointer to the env (if the env is set)
26  *
27  * @param str - pointer to string
28  * @param env - pointer to pointer to extracted env
29  *
30  * @return - zero on successful expand and env is set
31  */
32 static int extract_env(const char *str, char **env)
33 {
34         int ret = -1;
35         char *e, *s;
36 #ifdef CONFIG_RANDOM_UUID
37         char uuid_str[UUID_STR_LEN + 1];
38 #endif
39
40         if (!str || strlen(str) < 4)
41                 return -1;
42
43         if (!((strncmp(str, "${", 2) == 0) && (str[strlen(str) - 1] == '}')))
44                 return -1;
45
46         s = strdup(str);
47         if (s == NULL)
48                 return -1;
49
50         memset(s + strlen(s) - 1, '\0', 1);
51         memmove(s, s + 2, strlen(s) - 1);
52
53         e = getenv(s);
54         if (e == NULL) {
55 #ifdef CONFIG_RANDOM_UUID
56                 debug("%s unset. ", str);
57                 gen_rand_uuid_str(uuid_str, UUID_STR_FORMAT_GUID);
58                 setenv(s, uuid_str);
59
60                 e = getenv(s);
61                 if (e) {
62                         debug("Set to random.\n");
63                         ret = 0;
64                 } else {
65                         debug("Can't get random UUID.\n");
66                 }
67 #else
68                 debug("%s unset.\n", str);
69 #endif
70         } else {
71                 debug("%s get from environment.\n", str);
72                 ret = 0;
73         }
74
75         *env = e;
76         free(s);
77
78         return ret;
79 }
80
81 /**
82  * extract_val(): Extract value from a key=value pair list (comma separated).
83  *                Only value for the given key is returend.
84  *                Function allocates memory for the value, remember to free!
85  *
86  * @param str - pointer to string with key=values pairs
87  * @param key - pointer to the key to search for
88  *
89  * @return - pointer to allocated string with the value
90  */
91 static char *extract_val(const char *str, const char *key)
92 {
93         char *v, *k;
94         char *s, *strcopy;
95         char *new = NULL;
96
97         strcopy = strdup(str);
98         if (strcopy == NULL)
99                 return NULL;
100
101         s = strcopy;
102         while (s) {
103                 v = strsep(&s, ",");
104                 if (!v)
105                         break;
106                 k = strsep(&v, "=");
107                 if (!k)
108                         break;
109                 if  (strcmp(k, key) == 0) {
110                         new = strdup(v);
111                         break;
112                 }
113         }
114
115         free(strcopy);
116
117         return new;
118 }
119
120 /**
121  * found_key(): Found key without value in parameter list (comma separated).
122  *
123  * @param str - pointer to string with key
124  * @param key - pointer to the key to search for
125  *
126  * @return - true on found key
127  */
128 static bool found_key(const char *str, const char *key)
129 {
130         char *k;
131         char *s, *strcopy;
132         bool result = false;
133
134         strcopy = strdup(str);
135         if (!strcopy)
136                 return NULL;
137
138         s = strcopy;
139         while (s) {
140                 k = strsep(&s, ",");
141                 if (!k)
142                         break;
143                 if  (strcmp(k, key) == 0) {
144                         result = true;
145                         break;
146                 }
147         }
148
149         free(strcopy);
150
151         return result;
152 }
153
154 /**
155  * set_gpt_info(): Fill partition information from string
156  *              function allocates memory, remember to free!
157  *
158  * @param dev_desc - pointer block device descriptor
159  * @param str_part - pointer to string with partition information
160  * @param str_disk_guid - pointer to pointer to allocated string with disk guid
161  * @param partitions - pointer to pointer to allocated partitions array
162  * @param parts_count - number of partitions
163  *
164  * @return - zero on success, otherwise error
165  *
166  */
167 static int set_gpt_info(struct blk_desc *dev_desc,
168                         const char *str_part,
169                         char **str_disk_guid,
170                         disk_partition_t **partitions,
171                         u8 *parts_count)
172 {
173         char *tok, *str, *s;
174         int i;
175         char *val, *p;
176         int p_count;
177         disk_partition_t *parts;
178         int errno = 0;
179         uint64_t size_ll, start_ll;
180         lbaint_t offset = 0;
181
182         debug("%s:  lba num: 0x%x %d\n", __func__,
183               (unsigned int)dev_desc->lba, (unsigned int)dev_desc->lba);
184
185         if (str_part == NULL)
186                 return -1;
187
188         str = strdup(str_part);
189
190         /* extract disk guid */
191         s = str;
192         val = extract_val(str, "uuid_disk");
193         if (!val) {
194 #ifdef CONFIG_RANDOM_UUID
195                 *str_disk_guid = malloc(UUID_STR_LEN + 1);
196                 gen_rand_uuid_str(*str_disk_guid, UUID_STR_FORMAT_STD);
197 #else
198                 free(str);
199                 return -2;
200 #endif
201         } else {
202                 val = strsep(&val, ";");
203                 if (extract_env(val, &p))
204                         p = val;
205                 *str_disk_guid = strdup(p);
206                 free(val);
207                 /* Move s to first partition */
208                 strsep(&s, ";");
209         }
210         if (strlen(s) == 0)
211                 return -3;
212
213         i = strlen(s) - 1;
214         if (s[i] == ';')
215                 s[i] = '\0';
216
217         /* calculate expected number of partitions */
218         p_count = 1;
219         p = s;
220         while (*p) {
221                 if (*p++ == ';')
222                         p_count++;
223         }
224
225         /* allocate memory for partitions */
226         parts = calloc(sizeof(disk_partition_t), p_count);
227
228         /* retrieve partitions data from string */
229         for (i = 0; i < p_count; i++) {
230                 tok = strsep(&s, ";");
231
232                 if (tok == NULL)
233                         break;
234
235                 /* uuid */
236                 val = extract_val(tok, "uuid");
237                 if (!val) {
238                         /* 'uuid' is optional if random uuid's are enabled */
239 #ifdef CONFIG_RANDOM_UUID
240                         gen_rand_uuid_str(parts[i].uuid, UUID_STR_FORMAT_STD);
241 #else
242                         errno = -4;
243                         goto err;
244 #endif
245                 } else {
246                         if (extract_env(val, &p))
247                                 p = val;
248                         if (strlen(p) >= sizeof(parts[i].uuid)) {
249                                 printf("Wrong uuid format for partition %d\n", i);
250                                 errno = -4;
251                                 goto err;
252                         }
253                         strcpy((char *)parts[i].uuid, p);
254                         free(val);
255                 }
256 #ifdef CONFIG_PARTITION_TYPE_GUID
257                 /* guid */
258                 val = extract_val(tok, "type");
259                 if (val) {
260                         /* 'type' is optional */
261                         if (extract_env(val, &p))
262                                 p = val;
263                         if (strlen(p) >= sizeof(parts[i].type_guid)) {
264                                 printf("Wrong type guid format for partition %d\n",
265                                        i);
266                                 errno = -4;
267                                 goto err;
268                         }
269                         strcpy((char *)parts[i].type_guid, p);
270                         free(val);
271                 }
272 #endif
273                 /* name */
274                 val = extract_val(tok, "name");
275                 if (!val) { /* name is mandatory */
276                         errno = -4;
277                         goto err;
278                 }
279                 if (extract_env(val, &p))
280                         p = val;
281                 if (strlen(p) >= sizeof(parts[i].name)) {
282                         errno = -4;
283                         goto err;
284                 }
285                 strcpy((char *)parts[i].name, p);
286                 free(val);
287
288                 /* size */
289                 val = extract_val(tok, "size");
290                 if (!val) { /* 'size' is mandatory */
291                         errno = -4;
292                         goto err;
293                 }
294                 if (extract_env(val, &p))
295                         p = val;
296                 if ((strcmp(p, "-") == 0)) {
297                         /* Let part efi module to auto extend the size */
298                         parts[i].size = 0;
299                 } else {
300                         size_ll = ustrtoull(p, &p, 0);
301                         parts[i].size = lldiv(size_ll, dev_desc->blksz);
302                 }
303
304                 free(val);
305
306                 /* start address */
307                 val = extract_val(tok, "start");
308                 if (val) { /* start address is optional */
309                         if (extract_env(val, &p))
310                                 p = val;
311                         start_ll = ustrtoull(p, &p, 0);
312                         parts[i].start = lldiv(start_ll, dev_desc->blksz);
313                         free(val);
314                 }
315
316                 offset += parts[i].size + parts[i].start;
317
318                 /* bootable */
319                 if (found_key(tok, "bootable"))
320                         parts[i].bootable = 1;
321         }
322
323         *parts_count = p_count;
324         *partitions = parts;
325         free(str);
326
327         return 0;
328 err:
329         free(str);
330         free(*str_disk_guid);
331         free(parts);
332
333         return errno;
334 }
335
336 static int gpt_default(struct blk_desc *blk_dev_desc, const char *str_part)
337 {
338         int ret;
339         char *str_disk_guid;
340         u8 part_count = 0;
341         disk_partition_t *partitions = NULL;
342
343         /* fill partitions */
344         ret = set_gpt_info(blk_dev_desc, str_part,
345                         &str_disk_guid, &partitions, &part_count);
346         if (ret) {
347                 if (ret == -1)
348                         printf("No partition list provided\n");
349                 if (ret == -2)
350                         printf("Missing disk guid\n");
351                 if ((ret == -3) || (ret == -4))
352                         printf("Partition list incomplete\n");
353                 return -1;
354         }
355
356         /* save partitions layout to disk */
357         ret = gpt_restore(blk_dev_desc, str_disk_guid, partitions, part_count);
358         free(str_disk_guid);
359         free(partitions);
360
361         return ret;
362 }
363
364 static int gpt_verify(struct blk_desc *blk_dev_desc, const char *str_part)
365 {
366         ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header, gpt_head, 1,
367                                      blk_dev_desc->blksz);
368         disk_partition_t *partitions = NULL;
369         gpt_entry *gpt_pte = NULL;
370         char *str_disk_guid;
371         u8 part_count = 0;
372         int ret = 0;
373
374         /* fill partitions */
375         ret = set_gpt_info(blk_dev_desc, str_part,
376                         &str_disk_guid, &partitions, &part_count);
377         if (ret) {
378                 if (ret == -1) {
379                         printf("No partition list provided - only basic check\n");
380                         ret = gpt_verify_headers(blk_dev_desc, gpt_head,
381                                                  &gpt_pte);
382                         goto out;
383                 }
384                 if (ret == -2)
385                         printf("Missing disk guid\n");
386                 if ((ret == -3) || (ret == -4))
387                         printf("Partition list incomplete\n");
388                 return -1;
389         }
390
391         /* Check partition layout with provided pattern */
392         ret = gpt_verify_partitions(blk_dev_desc, partitions, part_count,
393                                     gpt_head, &gpt_pte);
394         free(str_disk_guid);
395         free(partitions);
396  out:
397         free(gpt_pte);
398         return ret;
399 }
400
401 /**
402  * do_gpt(): Perform GPT operations
403  *
404  * @param cmdtp - command name
405  * @param flag
406  * @param argc
407  * @param argv
408  *
409  * @return zero on success; otherwise error
410  */
411 static int do_gpt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
412 {
413         int ret = CMD_RET_SUCCESS;
414         int dev = 0;
415         char *ep;
416         struct blk_desc *blk_dev_desc = NULL;
417
418         if (argc < 4 || argc > 5)
419                 return CMD_RET_USAGE;
420
421         dev = (int)simple_strtoul(argv[3], &ep, 10);
422         if (!ep || ep[0] != '\0') {
423                 printf("'%s' is not a number\n", argv[3]);
424                 return CMD_RET_USAGE;
425         }
426         blk_dev_desc = blk_get_dev(argv[2], dev);
427         if (!blk_dev_desc) {
428                 printf("%s: %s dev %d NOT available\n",
429                        __func__, argv[2], dev);
430                 return CMD_RET_FAILURE;
431         }
432
433         if ((strcmp(argv[1], "write") == 0) && (argc == 5)) {
434                 printf("Writing GPT: ");
435                 ret = gpt_default(blk_dev_desc, argv[4]);
436         } else if ((strcmp(argv[1], "verify") == 0)) {
437                 ret = gpt_verify(blk_dev_desc, argv[4]);
438                 printf("Verify GPT: ");
439         } else {
440                 return CMD_RET_USAGE;
441         }
442
443         if (ret) {
444                 printf("error!\n");
445                 return CMD_RET_FAILURE;
446         }
447
448         printf("success!\n");
449         return CMD_RET_SUCCESS;
450 }
451
452 U_BOOT_CMD(gpt, CONFIG_SYS_MAXARGS, 1, do_gpt,
453         "GUID Partition Table",
454         "<command> <interface> <dev> <partitions_list>\n"
455         " - GUID partition table restoration and validity check\n"
456         " Restore or verify GPT information on a device connected\n"
457         " to interface\n"
458         " Example usage:\n"
459         " gpt write mmc 0 $partitions\n"
460         " gpt verify mmc 0 $partitions\n"
461 );