xref: /openbmc/u-boot/cmd/gpt.c (revision ca6c5e03)
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