xref: /openbmc/u-boot/cmd/gpt.c (revision a5c289b9bca3805fa35e42f389dc8225c6b916be)
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   * found_key(): Found key without value in parameter list (comma separated).
126   *
127   * @param str - pointer to string with key
128   * @param key - pointer to the key to search for
129   *
130   * @return - true on found key
131   */
132  static bool found_key(const char *str, const char *key)
133  {
134  	char *k;
135  	char *s, *strcopy;
136  	bool result = false;
137  
138  	strcopy = strdup(str);
139  	if (!strcopy)
140  		return NULL;
141  
142  	s = strcopy;
143  	while (s) {
144  		k = strsep(&s, ",");
145  		if (!k)
146  			break;
147  		if  (strcmp(k, key) == 0) {
148  			result = true;
149  			break;
150  		}
151  	}
152  
153  	free(strcopy);
154  
155  	return result;
156  }
157  
158  /**
159   * set_gpt_info(): Fill partition information from string
160   *		function allocates memory, remember to free!
161   *
162   * @param dev_desc - pointer block device descriptor
163   * @param str_part - pointer to string with partition information
164   * @param str_disk_guid - pointer to pointer to allocated string with disk guid
165   * @param partitions - pointer to pointer to allocated partitions array
166   * @param parts_count - number of partitions
167   *
168   * @return - zero on success, otherwise error
169   *
170   */
171  static int set_gpt_info(struct blk_desc *dev_desc,
172  			const char *str_part,
173  			char **str_disk_guid,
174  			disk_partition_t **partitions,
175  			u8 *parts_count)
176  {
177  	char *tok, *str, *s;
178  	int i;
179  	char *val, *p;
180  	int p_count;
181  	disk_partition_t *parts;
182  	int errno = 0;
183  	uint64_t size_ll, start_ll;
184  	lbaint_t offset = 0;
185  
186  	debug("%s:  lba num: 0x%x %d\n", __func__,
187  	      (unsigned int)dev_desc->lba, (unsigned int)dev_desc->lba);
188  
189  	if (str_part == NULL)
190  		return -1;
191  
192  	str = strdup(str_part);
193  
194  	/* extract disk guid */
195  	s = str;
196  	val = extract_val(str, "uuid_disk");
197  	if (!val) {
198  #ifdef CONFIG_RANDOM_UUID
199  		*str_disk_guid = malloc(UUID_STR_LEN + 1);
200  		gen_rand_uuid_str(*str_disk_guid, UUID_STR_FORMAT_STD);
201  #else
202  		free(str);
203  		return -2;
204  #endif
205  	} else {
206  		val = strsep(&val, ";");
207  		if (extract_env(val, &p))
208  			p = val;
209  		*str_disk_guid = strdup(p);
210  		free(val);
211  		/* Move s to first partition */
212  		strsep(&s, ";");
213  	}
214  	if (strlen(s) == 0)
215  		return -3;
216  
217  	i = strlen(s) - 1;
218  	if (s[i] == ';')
219  		s[i] = '\0';
220  
221  	/* calculate expected number of partitions */
222  	p_count = 1;
223  	p = s;
224  	while (*p) {
225  		if (*p++ == ';')
226  			p_count++;
227  	}
228  
229  	/* allocate memory for partitions */
230  	parts = calloc(sizeof(disk_partition_t), p_count);
231  
232  	/* retrieve partitions data from string */
233  	for (i = 0; i < p_count; i++) {
234  		tok = strsep(&s, ";");
235  
236  		if (tok == NULL)
237  			break;
238  
239  		/* uuid */
240  		val = extract_val(tok, "uuid");
241  		if (!val) {
242  			/* 'uuid' is optional if random uuid's are enabled */
243  #ifdef CONFIG_RANDOM_UUID
244  			gen_rand_uuid_str(parts[i].uuid, UUID_STR_FORMAT_STD);
245  #else
246  			errno = -4;
247  			goto err;
248  #endif
249  		} else {
250  			if (extract_env(val, &p))
251  				p = val;
252  			if (strlen(p) >= sizeof(parts[i].uuid)) {
253  				printf("Wrong uuid format for partition %d\n", i);
254  				errno = -4;
255  				goto err;
256  			}
257  			strcpy((char *)parts[i].uuid, p);
258  			free(val);
259  		}
260  #ifdef CONFIG_PARTITION_TYPE_GUID
261  		/* guid */
262  		val = extract_val(tok, "type");
263  		if (val) {
264  			/* 'type' is optional */
265  			if (extract_env(val, &p))
266  				p = val;
267  			if (strlen(p) >= sizeof(parts[i].type_guid)) {
268  				printf("Wrong type guid format for partition %d\n",
269  				       i);
270  				errno = -4;
271  				goto err;
272  			}
273  			strcpy((char *)parts[i].type_guid, p);
274  			free(val);
275  		}
276  #endif
277  		/* name */
278  		val = extract_val(tok, "name");
279  		if (!val) { /* name is mandatory */
280  			errno = -4;
281  			goto err;
282  		}
283  		if (extract_env(val, &p))
284  			p = val;
285  		if (strlen(p) >= sizeof(parts[i].name)) {
286  			errno = -4;
287  			goto err;
288  		}
289  		strcpy((char *)parts[i].name, p);
290  		free(val);
291  
292  		/* size */
293  		val = extract_val(tok, "size");
294  		if (!val) { /* 'size' is mandatory */
295  			errno = -4;
296  			goto err;
297  		}
298  		if (extract_env(val, &p))
299  			p = val;
300  		if ((strcmp(p, "-") == 0)) {
301  			/* Let part efi module to auto extend the size */
302  			parts[i].size = 0;
303  		} else {
304  			size_ll = ustrtoull(p, &p, 0);
305  			parts[i].size = lldiv(size_ll, dev_desc->blksz);
306  		}
307  
308  		free(val);
309  
310  		/* start address */
311  		val = extract_val(tok, "start");
312  		if (val) { /* start address is optional */
313  			if (extract_env(val, &p))
314  				p = val;
315  			start_ll = ustrtoull(p, &p, 0);
316  			parts[i].start = lldiv(start_ll, dev_desc->blksz);
317  			free(val);
318  		}
319  
320  		offset += parts[i].size + parts[i].start;
321  
322  		/* bootable */
323  		if (found_key(tok, "bootable"))
324  			parts[i].bootable = 1;
325  	}
326  
327  	*parts_count = p_count;
328  	*partitions = parts;
329  	free(str);
330  
331  	return 0;
332  err:
333  	free(str);
334  	free(*str_disk_guid);
335  	free(parts);
336  
337  	return errno;
338  }
339  
340  static int gpt_default(struct blk_desc *blk_dev_desc, const char *str_part)
341  {
342  	int ret;
343  	char *str_disk_guid;
344  	u8 part_count = 0;
345  	disk_partition_t *partitions = NULL;
346  
347  	/* fill partitions */
348  	ret = set_gpt_info(blk_dev_desc, str_part,
349  			&str_disk_guid, &partitions, &part_count);
350  	if (ret) {
351  		if (ret == -1)
352  			printf("No partition list provided\n");
353  		if (ret == -2)
354  			printf("Missing disk guid\n");
355  		if ((ret == -3) || (ret == -4))
356  			printf("Partition list incomplete\n");
357  		return -1;
358  	}
359  
360  	/* save partitions layout to disk */
361  	ret = gpt_restore(blk_dev_desc, str_disk_guid, partitions, part_count);
362  	free(str_disk_guid);
363  	free(partitions);
364  
365  	return ret;
366  }
367  
368  static int gpt_verify(struct blk_desc *blk_dev_desc, const char *str_part)
369  {
370  	ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header, gpt_head, 1,
371  				     blk_dev_desc->blksz);
372  	disk_partition_t *partitions = NULL;
373  	gpt_entry *gpt_pte = NULL;
374  	char *str_disk_guid;
375  	u8 part_count = 0;
376  	int ret = 0;
377  
378  	/* fill partitions */
379  	ret = set_gpt_info(blk_dev_desc, str_part,
380  			&str_disk_guid, &partitions, &part_count);
381  	if (ret) {
382  		if (ret == -1) {
383  			printf("No partition list provided - only basic check\n");
384  			ret = gpt_verify_headers(blk_dev_desc, gpt_head,
385  						 &gpt_pte);
386  			goto out;
387  		}
388  		if (ret == -2)
389  			printf("Missing disk guid\n");
390  		if ((ret == -3) || (ret == -4))
391  			printf("Partition list incomplete\n");
392  		return -1;
393  	}
394  
395  	/* Check partition layout with provided pattern */
396  	ret = gpt_verify_partitions(blk_dev_desc, partitions, part_count,
397  				    gpt_head, &gpt_pte);
398  	free(str_disk_guid);
399  	free(partitions);
400   out:
401  	free(gpt_pte);
402  	return ret;
403  }
404  
405  /**
406   * do_gpt(): Perform GPT operations
407   *
408   * @param cmdtp - command name
409   * @param flag
410   * @param argc
411   * @param argv
412   *
413   * @return zero on success; otherwise error
414   */
415  static int do_gpt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
416  {
417  	int ret = CMD_RET_SUCCESS;
418  	int dev = 0;
419  	char *ep;
420  	struct blk_desc *blk_dev_desc = NULL;
421  
422  	if (argc < 4 || argc > 5)
423  		return CMD_RET_USAGE;
424  
425  	dev = (int)simple_strtoul(argv[3], &ep, 10);
426  	if (!ep || ep[0] != '\0') {
427  		printf("'%s' is not a number\n", argv[3]);
428  		return CMD_RET_USAGE;
429  	}
430  	blk_dev_desc = blk_get_dev(argv[2], dev);
431  	if (!blk_dev_desc) {
432  		printf("%s: %s dev %d NOT available\n",
433  		       __func__, argv[2], dev);
434  		return CMD_RET_FAILURE;
435  	}
436  
437  	if ((strcmp(argv[1], "write") == 0) && (argc == 5)) {
438  		printf("Writing GPT: ");
439  		ret = gpt_default(blk_dev_desc, argv[4]);
440  	} else if ((strcmp(argv[1], "verify") == 0)) {
441  		ret = gpt_verify(blk_dev_desc, argv[4]);
442  		printf("Verify GPT: ");
443  	} else {
444  		return CMD_RET_USAGE;
445  	}
446  
447  	if (ret) {
448  		printf("error!\n");
449  		return CMD_RET_FAILURE;
450  	}
451  
452  	printf("success!\n");
453  	return CMD_RET_SUCCESS;
454  }
455  
456  U_BOOT_CMD(gpt, CONFIG_SYS_MAXARGS, 1, do_gpt,
457  	"GUID Partition Table",
458  	"<command> <interface> <dev> <partitions_list>\n"
459  	" - GUID partition table restoration and validity check\n"
460  	" Restore or verify GPT information on a device connected\n"
461  	" to interface\n"
462  	" Example usage:\n"
463  	" gpt write mmc 0 $partitions\n"
464  	" gpt verify mmc 0 $partitions\n"
465  );
466