xref: /rk3399_rockchip-uboot/cmd/gpt.c (revision 09a49930e4154974dd918ed42d129d50c110c45a)
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 #include <linux/compat.h>
23 
24 static LIST_HEAD(disk_partitions);
25 
26 /**
27  * extract_env(): Expand env name from string format '&{env_name}'
28  *                and return pointer to the env (if the env is set)
29  *
30  * @param str - pointer to string
31  * @param env - pointer to pointer to extracted env
32  *
33  * @return - zero on successful expand and env is set
34  */
35 static int extract_env(const char *str, char **env)
36 {
37 	int ret = -1;
38 	char *e, *s;
39 #ifdef CONFIG_RANDOM_UUID
40 	char uuid_str[UUID_STR_LEN + 1];
41 #endif
42 
43 	if (!str || strlen(str) < 4)
44 		return -1;
45 
46 	if (!((strncmp(str, "${", 2) == 0) && (str[strlen(str) - 1] == '}')))
47 		return -1;
48 
49 	s = strdup(str);
50 	if (s == NULL)
51 		return -1;
52 
53 	memset(s + strlen(s) - 1, '\0', 1);
54 	memmove(s, s + 2, strlen(s) - 1);
55 
56 	e = getenv(s);
57 	if (e == NULL) {
58 #ifdef CONFIG_RANDOM_UUID
59 		debug("%s unset. ", str);
60 		gen_rand_uuid_str(uuid_str, UUID_STR_FORMAT_GUID);
61 		setenv(s, uuid_str);
62 
63 		e = getenv(s);
64 		if (e) {
65 			debug("Set to random.\n");
66 			ret = 0;
67 		} else {
68 			debug("Can't get random UUID.\n");
69 		}
70 #else
71 		debug("%s unset.\n", str);
72 #endif
73 	} else {
74 		debug("%s get from environment.\n", str);
75 		ret = 0;
76 	}
77 
78 	*env = e;
79 	free(s);
80 
81 	return ret;
82 }
83 
84 /**
85  * extract_val(): Extract value from a key=value pair list (comma separated).
86  *                Only value for the given key is returend.
87  *                Function allocates memory for the value, remember to free!
88  *
89  * @param str - pointer to string with key=values pairs
90  * @param key - pointer to the key to search for
91  *
92  * @return - pointer to allocated string with the value
93  */
94 static char *extract_val(const char *str, const char *key)
95 {
96 	char *v, *k;
97 	char *s, *strcopy;
98 	char *new = NULL;
99 
100 	strcopy = strdup(str);
101 	if (strcopy == NULL)
102 		return NULL;
103 
104 	s = strcopy;
105 	while (s) {
106 		v = strsep(&s, ",");
107 		if (!v)
108 			break;
109 		k = strsep(&v, "=");
110 		if (!k)
111 			break;
112 		if  (strcmp(k, key) == 0) {
113 			new = strdup(v);
114 			break;
115 		}
116 	}
117 
118 	free(strcopy);
119 
120 	return new;
121 }
122 
123 /**
124  * found_key(): Found key without value in parameter list (comma separated).
125  *
126  * @param str - pointer to string with key
127  * @param key - pointer to the key to search for
128  *
129  * @return - true on found key
130  */
131 static bool found_key(const char *str, const char *key)
132 {
133 	char *k;
134 	char *s, *strcopy;
135 	bool result = false;
136 
137 	strcopy = strdup(str);
138 	if (!strcopy)
139 		return NULL;
140 
141 	s = strcopy;
142 	while (s) {
143 		k = strsep(&s, ",");
144 		if (!k)
145 			break;
146 		if  (strcmp(k, key) == 0) {
147 			result = true;
148 			break;
149 		}
150 	}
151 
152 	free(strcopy);
153 
154 	return result;
155 }
156 
157 #ifdef CONFIG_CMD_GPT_RENAME
158 static void del_gpt_info(void)
159 {
160 	struct list_head *pos = &disk_partitions;
161 	struct disk_part *curr;
162 	while (!list_empty(pos)) {
163 		curr = list_entry(pos->next, struct disk_part, list);
164 		list_del(pos->next);
165 		free(curr);
166 	}
167 }
168 
169 static struct disk_part *allocate_disk_part(disk_partition_t *info, int partnum)
170 {
171 	struct disk_part *newpart;
172 	newpart = malloc(sizeof(*newpart));
173 	if (!newpart)
174 		return ERR_PTR(-ENOMEM);
175 	memset(newpart, '\0', sizeof(newpart));
176 
177 	newpart->gpt_part_info.start = info->start;
178 	newpart->gpt_part_info.size = info->size;
179 	newpart->gpt_part_info.blksz = info->blksz;
180 	strncpy((char *)newpart->gpt_part_info.name, (const char *)info->name,
181 		PART_NAME_LEN);
182 	newpart->gpt_part_info.name[PART_NAME_LEN - 1] = '\0';
183 	strncpy((char *)newpart->gpt_part_info.type, (const char *)info->type,
184 		PART_TYPE_LEN);
185 	newpart->gpt_part_info.type[PART_TYPE_LEN - 1] = '\0';
186 	newpart->gpt_part_info.bootable = info->bootable;
187 #ifdef CONFIG_PARTITION_UUIDS
188 	strncpy(newpart->gpt_part_info.uuid, (const char *)info->uuid,
189 		UUID_STR_LEN);
190 	/* UUID_STR_LEN is correct, as uuid[]'s length is UUID_STR_LEN+1 chars */
191 	newpart->gpt_part_info.uuid[UUID_STR_LEN] = '\0';
192 #endif
193 	newpart->partnum = partnum;
194 
195 	return newpart;
196 }
197 
198 static void print_gpt_info(void)
199 {
200 	struct list_head *pos;
201 	struct disk_part *curr;
202 
203 	list_for_each(pos, &disk_partitions) {
204 		curr = list_entry(pos, struct disk_part, list);
205 		printf("Partition %d:\n", curr->partnum);
206 		printf("1st block %x, size %x\n", (unsigned)curr->gpt_part_info.start,
207 		       (unsigned)curr->gpt_part_info.size);
208 		printf("Block size %lu, name %s\n", curr->gpt_part_info.blksz,
209 		       curr->gpt_part_info.name);
210 		printf("Type %s, bootable %d\n", curr->gpt_part_info.type,
211 		       curr->gpt_part_info.bootable);
212 #ifdef CONFIG_PARTITION_UUIDS
213 		printf("UUID %s\n", curr->gpt_part_info.uuid);
214 #endif
215 		printf("\n");
216 	}
217 }
218 
219 /*
220  * read partition info into disk_partitions list where
221  * it can be printed or modified
222  */
223 static int get_gpt_info(struct blk_desc *dev_desc)
224 {
225 	/* start partition numbering at 1, as U-Boot does */
226 	int valid_parts = 0, p, ret;
227 	disk_partition_t info;
228 	struct disk_part *new_disk_part;
229 
230 	if (disk_partitions.next == NULL)
231 		INIT_LIST_HEAD(&disk_partitions);
232 
233 	for (p = 1; p <= MAX_SEARCH_PARTITIONS; p++) {
234 		ret = part_get_info(dev_desc, p, &info);
235 		if (ret)
236 			continue;
237 
238 		/* Add 1 here because counter is zero-based but p1 is
239 		   the first partition */
240 		new_disk_part = allocate_disk_part(&info, valid_parts+1);
241 		if (IS_ERR(new_disk_part))
242 			goto out;
243 
244 		list_add_tail(&new_disk_part->list, &disk_partitions);
245 		valid_parts++;
246 	}
247 	if (valid_parts == 0) {
248 		printf("** No valid partitions found **\n");
249 		goto out;
250 	}
251 	return valid_parts;
252  out:
253 	if (valid_parts >= 1)
254 		del_gpt_info();
255 	return -ENODEV;
256 }
257 
258 /* a wrapper to test get_gpt_info */
259 static int do_get_gpt_info(struct blk_desc *dev_desc)
260 {
261 	int ret;
262 
263 	ret = get_gpt_info(dev_desc);
264 	if (ret > 0) {
265 		print_gpt_info();
266 		del_gpt_info();
267 		return 0;
268 	}
269 	return ret;
270 }
271 #endif
272 
273 /**
274  * set_gpt_info(): Fill partition information from string
275  *		function allocates memory, remember to free!
276  *
277  * @param dev_desc - pointer block device descriptor
278  * @param str_part - pointer to string with partition information
279  * @param str_disk_guid - pointer to pointer to allocated string with disk guid
280  * @param partitions - pointer to pointer to allocated partitions array
281  * @param parts_count - number of partitions
282  *
283  * @return - zero on success, otherwise error
284  *
285  */
286 static int set_gpt_info(struct blk_desc *dev_desc,
287 			const char *str_part,
288 			char **str_disk_guid,
289 			disk_partition_t **partitions,
290 			u8 *parts_count)
291 {
292 	char *tok, *str, *s;
293 	int i;
294 	char *val, *p;
295 	int p_count;
296 	disk_partition_t *parts;
297 	int errno = 0;
298 	uint64_t size_ll, start_ll;
299 	lbaint_t offset = 0;
300 
301 	debug("%s:  lba num: 0x%x %d\n", __func__,
302 	      (unsigned int)dev_desc->lba, (unsigned int)dev_desc->lba);
303 
304 	if (str_part == NULL)
305 		return -1;
306 
307 	str = strdup(str_part);
308 
309 	/* extract disk guid */
310 	s = str;
311 	val = extract_val(str, "uuid_disk");
312 	if (!val) {
313 #ifdef CONFIG_RANDOM_UUID
314 		*str_disk_guid = malloc(UUID_STR_LEN + 1);
315 		gen_rand_uuid_str(*str_disk_guid, UUID_STR_FORMAT_STD);
316 #else
317 		free(str);
318 		return -2;
319 #endif
320 	} else {
321 		val = strsep(&val, ";");
322 		if (extract_env(val, &p))
323 			p = val;
324 		*str_disk_guid = strdup(p);
325 		free(val);
326 		/* Move s to first partition */
327 		strsep(&s, ";");
328 	}
329 	if (strlen(s) == 0)
330 		return -3;
331 
332 	i = strlen(s) - 1;
333 	if (s[i] == ';')
334 		s[i] = '\0';
335 
336 	/* calculate expected number of partitions */
337 	p_count = 1;
338 	p = s;
339 	while (*p) {
340 		if (*p++ == ';')
341 			p_count++;
342 	}
343 
344 	/* allocate memory for partitions */
345 	parts = calloc(sizeof(disk_partition_t), p_count);
346 
347 	/* retrieve partitions data from string */
348 	for (i = 0; i < p_count; i++) {
349 		tok = strsep(&s, ";");
350 
351 		if (tok == NULL)
352 			break;
353 
354 		/* uuid */
355 		val = extract_val(tok, "uuid");
356 		if (!val) {
357 			/* 'uuid' is optional if random uuid's are enabled */
358 #ifdef CONFIG_RANDOM_UUID
359 			gen_rand_uuid_str(parts[i].uuid, UUID_STR_FORMAT_STD);
360 #else
361 			errno = -4;
362 			goto err;
363 #endif
364 		} else {
365 			if (extract_env(val, &p))
366 				p = val;
367 			if (strlen(p) >= sizeof(parts[i].uuid)) {
368 				printf("Wrong uuid format for partition %d\n", i);
369 				errno = -4;
370 				goto err;
371 			}
372 			strcpy((char *)parts[i].uuid, p);
373 			free(val);
374 		}
375 #ifdef CONFIG_PARTITION_TYPE_GUID
376 		/* guid */
377 		val = extract_val(tok, "type");
378 		if (val) {
379 			/* 'type' is optional */
380 			if (extract_env(val, &p))
381 				p = val;
382 			if (strlen(p) >= sizeof(parts[i].type_guid)) {
383 				printf("Wrong type guid format for partition %d\n",
384 				       i);
385 				errno = -4;
386 				goto err;
387 			}
388 			strcpy((char *)parts[i].type_guid, p);
389 			free(val);
390 		}
391 #endif
392 		/* name */
393 		val = extract_val(tok, "name");
394 		if (!val) { /* name is mandatory */
395 			errno = -4;
396 			goto err;
397 		}
398 		if (extract_env(val, &p))
399 			p = val;
400 		if (strlen(p) >= sizeof(parts[i].name)) {
401 			errno = -4;
402 			goto err;
403 		}
404 		strcpy((char *)parts[i].name, p);
405 		free(val);
406 
407 		/* size */
408 		val = extract_val(tok, "size");
409 		if (!val) { /* 'size' is mandatory */
410 			errno = -4;
411 			goto err;
412 		}
413 		if (extract_env(val, &p))
414 			p = val;
415 		if ((strcmp(p, "-") == 0)) {
416 			/* Let part efi module to auto extend the size */
417 			parts[i].size = 0;
418 		} else {
419 			size_ll = ustrtoull(p, &p, 0);
420 			parts[i].size = lldiv(size_ll, dev_desc->blksz);
421 		}
422 
423 		free(val);
424 
425 		/* start address */
426 		val = extract_val(tok, "start");
427 		if (val) { /* start address is optional */
428 			if (extract_env(val, &p))
429 				p = val;
430 			start_ll = ustrtoull(p, &p, 0);
431 			parts[i].start = lldiv(start_ll, dev_desc->blksz);
432 			free(val);
433 		}
434 
435 		offset += parts[i].size + parts[i].start;
436 
437 		/* bootable */
438 		if (found_key(tok, "bootable"))
439 			parts[i].bootable = 1;
440 	}
441 
442 	*parts_count = p_count;
443 	*partitions = parts;
444 	free(str);
445 
446 	return 0;
447 err:
448 	free(str);
449 	free(*str_disk_guid);
450 	free(parts);
451 
452 	return errno;
453 }
454 
455 static int gpt_default(struct blk_desc *blk_dev_desc, const char *str_part)
456 {
457 	int ret;
458 	char *str_disk_guid;
459 	u8 part_count = 0;
460 	disk_partition_t *partitions = NULL;
461 
462 	/* fill partitions */
463 	ret = set_gpt_info(blk_dev_desc, str_part,
464 			&str_disk_guid, &partitions, &part_count);
465 	if (ret) {
466 		if (ret == -1)
467 			printf("No partition list provided\n");
468 		if (ret == -2)
469 			printf("Missing disk guid\n");
470 		if ((ret == -3) || (ret == -4))
471 			printf("Partition list incomplete\n");
472 		return -1;
473 	}
474 
475 	/* save partitions layout to disk */
476 	ret = gpt_restore(blk_dev_desc, str_disk_guid, partitions, part_count);
477 	free(str_disk_guid);
478 	free(partitions);
479 
480 	return ret;
481 }
482 
483 static int gpt_verify(struct blk_desc *blk_dev_desc, const char *str_part)
484 {
485 	ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header, gpt_head, 1,
486 				     blk_dev_desc->blksz);
487 	disk_partition_t *partitions = NULL;
488 	gpt_entry *gpt_pte = NULL;
489 	char *str_disk_guid;
490 	u8 part_count = 0;
491 	int ret = 0;
492 
493 	/* fill partitions */
494 	ret = set_gpt_info(blk_dev_desc, str_part,
495 			&str_disk_guid, &partitions, &part_count);
496 	if (ret) {
497 		if (ret == -1) {
498 			printf("No partition list provided - only basic check\n");
499 			ret = gpt_verify_headers(blk_dev_desc, gpt_head,
500 						 &gpt_pte);
501 			goto out;
502 		}
503 		if (ret == -2)
504 			printf("Missing disk guid\n");
505 		if ((ret == -3) || (ret == -4))
506 			printf("Partition list incomplete\n");
507 		return -1;
508 	}
509 
510 	/* Check partition layout with provided pattern */
511 	ret = gpt_verify_partitions(blk_dev_desc, partitions, part_count,
512 				    gpt_head, &gpt_pte);
513 	free(str_disk_guid);
514 	free(partitions);
515  out:
516 	free(gpt_pte);
517 	return ret;
518 }
519 
520 static int do_disk_guid(struct blk_desc *dev_desc, char * const namestr)
521 {
522 	int ret;
523 	char disk_guid[UUID_STR_LEN + 1];
524 
525 	ret = get_disk_guid(dev_desc, disk_guid);
526 	if (ret < 0)
527 		return CMD_RET_FAILURE;
528 
529 	if (namestr)
530 		setenv(namestr, disk_guid);
531 	else
532 		printf("%s\n", disk_guid);
533 
534 	return ret;
535 }
536 
537 /**
538  * do_gpt(): Perform GPT operations
539  *
540  * @param cmdtp - command name
541  * @param flag
542  * @param argc
543  * @param argv
544  *
545  * @return zero on success; otherwise error
546  */
547 static int do_gpt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
548 {
549 	int ret = CMD_RET_SUCCESS;
550 	int dev = 0;
551 	char *ep;
552 	struct blk_desc *blk_dev_desc = NULL;
553 
554 	if (argc < 4 || argc > 5)
555 		return CMD_RET_USAGE;
556 
557 	dev = (int)simple_strtoul(argv[3], &ep, 10);
558 	if (!ep || ep[0] != '\0') {
559 		printf("'%s' is not a number\n", argv[3]);
560 		return CMD_RET_USAGE;
561 	}
562 	blk_dev_desc = blk_get_dev(argv[2], dev);
563 	if (!blk_dev_desc) {
564 		printf("%s: %s dev %d NOT available\n",
565 		       __func__, argv[2], dev);
566 		return CMD_RET_FAILURE;
567 	}
568 
569 	if ((strcmp(argv[1], "write") == 0) && (argc == 5)) {
570 		printf("Writing GPT: ");
571 		ret = gpt_default(blk_dev_desc, argv[4]);
572 	} else if ((strcmp(argv[1], "verify") == 0)) {
573 		ret = gpt_verify(blk_dev_desc, argv[4]);
574 		printf("Verify GPT: ");
575 	} else if (strcmp(argv[1], "guid") == 0) {
576 		ret = do_disk_guid(blk_dev_desc, argv[4]);
577 #ifdef CONFIG_CMD_GPT_RENAME
578 	} else if (strcmp(argv[1], "read") == 0) {
579 		ret = do_get_gpt_info(blk_dev_desc);
580 #endif
581 	} else {
582 		return CMD_RET_USAGE;
583 	}
584 
585 	if (ret) {
586 		printf("error!\n");
587 		return CMD_RET_FAILURE;
588 	}
589 
590 	printf("success!\n");
591 	return CMD_RET_SUCCESS;
592 }
593 
594 U_BOOT_CMD(gpt, CONFIG_SYS_MAXARGS, 1, do_gpt,
595 	"GUID Partition Table",
596 	"<command> <interface> <dev> <partitions_list>\n"
597 	" - GUID partition table restoration and validity check\n"
598 	" Restore or verify GPT information on a device connected\n"
599 	" to interface\n"
600 	" Example usage:\n"
601 	" gpt write mmc 0 $partitions\n"
602 	" gpt verify mmc 0 $partitions\n"
603 	" read <interface> <dev>\n"
604 	"    - read GPT into a data structure for manipulation\n"
605 	" guid <interface> <dev>\n"
606 	"    - print disk GUID\n"
607 	" guid <interface> <dev> <varname>\n"
608 	"    - set environment variable to disk GUID\n"
609 	" Example usage:\n"
610 	" gpt guid mmc 0\n"
611 	" gpt guid mmc 0 varname\n"
612 );
613