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