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