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