2 * cmd_gpt.c -- GPT (GUID Partition Table) handling command
5 * Lukasz Majewski <l.majewski@majess.pl>
7 * Copyright (C) 2012 Samsung Electronics
8 * author: Lukasz Majewski <l.majewski@samsung.com>
9 * author: Piotr Wilczek <p.wilczek@samsung.com>
11 * SPDX-License-Identifier: GPL-2.0+
19 #include <linux/ctype.h>
22 #include <linux/compat.h>
24 static LIST_HEAD(disk_partitions);
27 * extract_env(): Expand env name from string format '&{env_name}'
28 * and return pointer to the env (if the env is set)
30 * @param str - pointer to string
31 * @param env - pointer to pointer to extracted env
33 * @return - zero on successful expand and env is set
35 static int extract_env(const char *str, char **env)
39 #ifdef CONFIG_RANDOM_UUID
40 char uuid_str[UUID_STR_LEN + 1];
43 if (!str || strlen(str) < 4)
46 if (!((strncmp(str, "${", 2) == 0) && (str[strlen(str) - 1] == '}')))
53 memset(s + strlen(s) - 1, '\0', 1);
54 memmove(s, s + 2, strlen(s) - 1);
58 #ifdef CONFIG_RANDOM_UUID
59 debug("%s unset. ", str);
60 gen_rand_uuid_str(uuid_str, UUID_STR_FORMAT_GUID);
65 debug("Set to random.\n");
68 debug("Can't get random UUID.\n");
71 debug("%s unset.\n", str);
74 debug("%s get from environment.\n", str);
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!
89 * @param str - pointer to string with key=values pairs
90 * @param key - pointer to the key to search for
92 * @return - pointer to allocated string with the value
94 static char *extract_val(const char *str, const char *key)
100 strcopy = strdup(str);
112 if (strcmp(k, key) == 0) {
124 * found_key(): Found key without value in parameter list (comma separated).
126 * @param str - pointer to string with key
127 * @param key - pointer to the key to search for
129 * @return - true on found key
131 static bool found_key(const char *str, const char *key)
137 strcopy = strdup(str);
146 if (strcmp(k, key) == 0) {
157 #ifdef CONFIG_CMD_GPT_RENAME
158 static void del_gpt_info(void)
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);
169 static struct disk_part *allocate_disk_part(disk_partition_t *info, int partnum)
171 struct disk_part *newpart;
172 newpart = malloc(sizeof(*newpart));
174 return ERR_PTR(-ENOMEM);
175 memset(newpart, '\0', sizeof(newpart));
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,
182 newpart->gpt_part_info.name[PART_NAME_LEN - 1] = '\0';
183 strncpy((char *)newpart->gpt_part_info.type, (const char *)info->type,
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,
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';
193 newpart->partnum = partnum;
198 static void print_gpt_info(void)
200 struct list_head *pos;
201 struct disk_part *curr;
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);
220 * read partition info into disk_partitions list where
221 * it can be printed or modified
223 static int get_gpt_info(struct blk_desc *dev_desc)
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;
230 if (disk_partitions.next == NULL)
231 INIT_LIST_HEAD(&disk_partitions);
233 for (p = 1; p <= MAX_SEARCH_PARTITIONS; p++) {
234 ret = part_get_info(dev_desc, p, &info);
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))
244 list_add_tail(&new_disk_part->list, &disk_partitions);
247 if (valid_parts == 0) {
248 printf("** No valid partitions found **\n");
253 if (valid_parts >= 1)
258 /* a wrapper to test get_gpt_info */
259 static int do_get_gpt_info(struct blk_desc *dev_desc)
263 ret = get_gpt_info(dev_desc);
274 * set_gpt_info(): Fill partition information from string
275 * function allocates memory, remember to free!
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
283 * @return - zero on success, otherwise error
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,
296 disk_partition_t *parts;
298 uint64_t size_ll, start_ll;
301 debug("%s: lba num: 0x%x %d\n", __func__,
302 (unsigned int)dev_desc->lba, (unsigned int)dev_desc->lba);
304 if (str_part == NULL)
307 str = strdup(str_part);
309 /* extract disk guid */
311 val = extract_val(str, "uuid_disk");
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);
321 val = strsep(&val, ";");
322 if (extract_env(val, &p))
324 *str_disk_guid = strdup(p);
326 /* Move s to first partition */
336 /* calculate expected number of partitions */
344 /* allocate memory for partitions */
345 parts = calloc(sizeof(disk_partition_t), p_count);
347 /* retrieve partitions data from string */
348 for (i = 0; i < p_count; i++) {
349 tok = strsep(&s, ";");
355 val = extract_val(tok, "uuid");
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);
365 if (extract_env(val, &p))
367 if (strlen(p) >= sizeof(parts[i].uuid)) {
368 printf("Wrong uuid format for partition %d\n", i);
372 strcpy((char *)parts[i].uuid, p);
375 #ifdef CONFIG_PARTITION_TYPE_GUID
377 val = extract_val(tok, "type");
379 /* 'type' is optional */
380 if (extract_env(val, &p))
382 if (strlen(p) >= sizeof(parts[i].type_guid)) {
383 printf("Wrong type guid format for partition %d\n",
388 strcpy((char *)parts[i].type_guid, p);
393 val = extract_val(tok, "name");
394 if (!val) { /* name is mandatory */
398 if (extract_env(val, &p))
400 if (strlen(p) >= sizeof(parts[i].name)) {
404 strcpy((char *)parts[i].name, p);
408 val = extract_val(tok, "size");
409 if (!val) { /* 'size' is mandatory */
413 if (extract_env(val, &p))
415 if ((strcmp(p, "-") == 0)) {
416 /* Let part efi module to auto extend the size */
419 size_ll = ustrtoull(p, &p, 0);
420 parts[i].size = lldiv(size_ll, dev_desc->blksz);
426 val = extract_val(tok, "start");
427 if (val) { /* start address is optional */
428 if (extract_env(val, &p))
430 start_ll = ustrtoull(p, &p, 0);
431 parts[i].start = lldiv(start_ll, dev_desc->blksz);
435 offset += parts[i].size + parts[i].start;
438 if (found_key(tok, "bootable"))
439 parts[i].bootable = 1;
442 *parts_count = p_count;
449 free(*str_disk_guid);
455 static int gpt_default(struct blk_desc *blk_dev_desc, const char *str_part)
460 disk_partition_t *partitions = NULL;
462 /* fill partitions */
463 ret = set_gpt_info(blk_dev_desc, str_part,
464 &str_disk_guid, &partitions, &part_count);
467 printf("No partition list provided\n");
469 printf("Missing disk guid\n");
470 if ((ret == -3) || (ret == -4))
471 printf("Partition list incomplete\n");
475 /* save partitions layout to disk */
476 ret = gpt_restore(blk_dev_desc, str_disk_guid, partitions, part_count);
483 static int gpt_verify(struct blk_desc *blk_dev_desc, const char *str_part)
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;
493 /* fill partitions */
494 ret = set_gpt_info(blk_dev_desc, str_part,
495 &str_disk_guid, &partitions, &part_count);
498 printf("No partition list provided - only basic check\n");
499 ret = gpt_verify_headers(blk_dev_desc, gpt_head,
504 printf("Missing disk guid\n");
505 if ((ret == -3) || (ret == -4))
506 printf("Partition list incomplete\n");
510 /* Check partition layout with provided pattern */
511 ret = gpt_verify_partitions(blk_dev_desc, partitions, part_count,
520 static int do_disk_guid(struct blk_desc *dev_desc, char * const namestr)
523 char disk_guid[UUID_STR_LEN + 1];
525 ret = get_disk_guid(dev_desc, disk_guid);
527 return CMD_RET_FAILURE;
530 setenv(namestr, disk_guid);
532 printf("%s\n", disk_guid);
538 * do_gpt(): Perform GPT operations
540 * @param cmdtp - command name
545 * @return zero on success; otherwise error
547 static int do_gpt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
549 int ret = CMD_RET_SUCCESS;
552 struct blk_desc *blk_dev_desc = NULL;
554 if (argc < 4 || argc > 5)
555 return CMD_RET_USAGE;
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;
562 blk_dev_desc = blk_get_dev(argv[2], dev);
564 printf("%s: %s dev %d NOT available\n",
565 __func__, argv[2], dev);
566 return CMD_RET_FAILURE;
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);
582 return CMD_RET_USAGE;
587 return CMD_RET_FAILURE;
590 printf("success!\n");
591 return CMD_RET_SUCCESS;
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"
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"
611 " gpt guid mmc 0 varname\n"