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