1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * (C) Copyright 2001 4 * Wolfgang Denk, DENX Software Engineering, wd@denx.de. 5 */ 6 7#include <common.h> 8#include <blk.h> 9#include <command.h> 10#include <env.h> 11#include <errno.h> 12#include <ide.h> 13#include <log.h> 14#include <malloc.h> 15#include <part.h> 16#include <ubifs_uboot.h> 17#include <dm/uclass.h> 18 19#undef PART_DEBUG 20 21#ifdef PART_DEBUG 22#define PRINTF(fmt,args...) printf (fmt ,##args) 23#else 24#define PRINTF(fmt,args...) 25#endif 26 27/* Check all partition types */ 28#define PART_TYPE_ALL -1 29 30/** 31 * part_driver_get_type() - Get a driver given its type 32 * 33 * @part_type: Partition type to find the driver for 34 * Return: Driver for that type, or NULL if none 35 */ 36static struct part_driver *part_driver_get_type(int part_type) 37{ 38 struct part_driver *drv = 39 ll_entry_start(struct part_driver, part_driver); 40 const int n_ents = ll_entry_count(struct part_driver, part_driver); 41 struct part_driver *entry; 42 43 for (entry = drv; entry != drv + n_ents; entry++) { 44 if (part_type == entry->part_type) 45 return entry; 46 } 47 48 /* Not found */ 49 return NULL; 50} 51 52/** 53 * part_driver_lookup_type() - Look up the partition driver for a blk device 54 * 55 * If @desc->part_type is PART_TYPE_UNKNOWN, this checks each parition driver 56 * against the blk device to see if there is a valid partition table acceptable 57 * to that driver. 58 * 59 * If @desc->part_type is already set, it just returns the driver for that 60 * type, without testing if the driver can find a valid partition on the 61 * descriptor. 62 * 63 * On success it updates @desc->part_type if set to PART_TYPE_UNKNOWN on entry 64 * 65 * @dev_desc: Device descriptor 66 * Return: Driver found, or NULL if none 67 */ 68static struct part_driver *part_driver_lookup_type(struct blk_desc *desc) 69{ 70 struct part_driver *drv = 71 ll_entry_start(struct part_driver, part_driver); 72 const int n_ents = ll_entry_count(struct part_driver, part_driver); 73 struct part_driver *entry; 74 75 if (desc->part_type == PART_TYPE_UNKNOWN) { 76 for (entry = drv; entry != drv + n_ents; entry++) { 77 int ret; 78 79 ret = entry->test(desc); 80 if (!ret) { 81 desc->part_type = entry->part_type; 82 return entry; 83 } 84 } 85 } else { 86 return part_driver_get_type(desc->part_type); 87 } 88 89 /* Not found */ 90 return NULL; 91} 92 93int part_get_type_by_name(const char *name) 94{ 95 struct part_driver *drv = 96 ll_entry_start(struct part_driver, part_driver); 97 const int n_ents = ll_entry_count(struct part_driver, part_driver); 98 struct part_driver *entry; 99 100 for (entry = drv; entry != drv + n_ents; entry++) { 101 if (!strcasecmp(name, entry->name)) 102 return entry->part_type; 103 } 104 105 /* Not found */ 106 return PART_TYPE_UNKNOWN; 107} 108 109/** 110 * get_dev_hwpart() - Get the descriptor for a device with hardware partitions 111 * 112 * @ifname: Interface name (e.g. "ide", "scsi") 113 * @dev: Device number (0 for first device on that interface, 1 for 114 * second, etc. 115 * @hwpart: Hardware partition, or 0 if none (used for MMC) 116 * Return: pointer to the block device, or NULL if not available, or an 117 * error occurred. 118 */ 119static struct blk_desc *get_dev_hwpart(const char *ifname, int dev, int hwpart) 120{ 121 struct blk_desc *desc; 122 int ret; 123 124 if (!blk_enabled()) 125 return NULL; 126 desc = blk_get_devnum_by_uclass_idname(ifname, dev); 127 if (!desc) { 128 debug("%s: No device for iface '%s', dev %d\n", __func__, 129 ifname, dev); 130 return NULL; 131 } 132 ret = blk_dselect_hwpart(desc, hwpart); 133 if (ret) { 134 debug("%s: Failed to select h/w partition: err-%d\n", __func__, 135 ret); 136 return NULL; 137 } 138 139 return desc; 140} 141 142struct blk_desc *blk_get_dev(const char *ifname, int dev) 143{ 144 if (!blk_enabled()) 145 return NULL; 146 147 return get_dev_hwpart(ifname, dev, 0); 148} 149 150/* ------------------------------------------------------------------------- */ 151/* 152 * reports device info to the user 153 */ 154 155#ifdef CONFIG_LBA48 156typedef uint64_t lba512_t; 157#else 158typedef lbaint_t lba512_t; 159#endif 160 161/* 162 * Overflowless variant of (block_count * mul_by / 2**right_shift) 163 * when 2**right_shift > mul_by 164 */ 165static lba512_t lba512_muldiv(lba512_t block_count, lba512_t mul_by, 166 int right_shift) 167{ 168 lba512_t bc_quot, bc_rem; 169 170 /* x * m / d == x / d * m + (x % d) * m / d */ 171 bc_quot = block_count >> right_shift; 172 bc_rem = block_count - (bc_quot << right_shift); 173 return bc_quot * mul_by + ((bc_rem * mul_by) >> right_shift); 174} 175 176void dev_print(struct blk_desc *desc) 177{ 178 lba512_t lba512; /* number of blocks if 512bytes block size */ 179 180 if (desc->type == DEV_TYPE_UNKNOWN) { 181 puts ("not available\n"); 182 return; 183 } 184 185 switch (desc->uclass_id) { 186 case UCLASS_SCSI: 187 printf("(%d:%d) Vendor: %s Prod.: %s Rev: %s\n", desc->target, 188 desc->lun, desc->vendor, desc->product, desc->revision); 189 break; 190 case UCLASS_IDE: 191 case UCLASS_AHCI: 192 printf("Model: %s Firm: %s Ser#: %s\n", desc->vendor, 193 desc->revision, desc->product); 194 break; 195 case UCLASS_MMC: 196 case UCLASS_USB: 197 case UCLASS_NVME: 198 case UCLASS_PVBLOCK: 199 case UCLASS_HOST: 200 case UCLASS_BLKMAP: 201 case UCLASS_RKMTD: 202 printf ("Vendor: %s Rev: %s Prod: %s\n", 203 desc->vendor, 204 desc->revision, 205 desc->product); 206 break; 207 case UCLASS_VIRTIO: 208 printf("%s VirtIO Block Device\n", desc->vendor); 209 break; 210 case UCLASS_EFI_MEDIA: 211 printf("EFI media Block Device %d\n", desc->devnum); 212 break; 213 case UCLASS_INVALID: 214 puts("device type unknown\n"); 215 return; 216 default: 217 printf("Unhandled device type: %i\n", desc->uclass_id); 218 return; 219 } 220 puts (" Type: "); 221 if (desc->removable) 222 puts ("Removable "); 223 switch (desc->type & 0x1F) { 224 case DEV_TYPE_HARDDISK: 225 puts ("Hard Disk"); 226 break; 227 case DEV_TYPE_CDROM: 228 puts ("CD ROM"); 229 break; 230 case DEV_TYPE_OPDISK: 231 puts ("Optical Device"); 232 break; 233 case DEV_TYPE_TAPE: 234 puts ("Tape"); 235 break; 236 default: 237 printf("# %02X #", desc->type & 0x1F); 238 break; 239 } 240 puts ("\n"); 241 if (desc->lba > 0L && desc->blksz > 0L) { 242 ulong mb, mb_quot, mb_rem, gb, gb_quot, gb_rem; 243 lbaint_t lba; 244 245 lba = desc->lba; 246 247 lba512 = lba * (desc->blksz / 512); 248 /* round to 1 digit */ 249 /* 2048 = (1024 * 1024) / 512 MB */ 250 mb = lba512_muldiv(lba512, 10, 11); 251 252 mb_quot = mb / 10; 253 mb_rem = mb - (10 * mb_quot); 254 255 gb = mb / 1024; 256 gb_quot = gb / 10; 257 gb_rem = gb - (10 * gb_quot); 258#ifdef CONFIG_LBA48 259 if (desc->lba48) 260 printf (" Supports 48-bit addressing\n"); 261#endif 262#if defined(CONFIG_SYS_64BIT_LBA) 263 printf (" Capacity: %lu.%lu MB = %lu.%lu GB (%llu x %lu)\n", 264 mb_quot, mb_rem, 265 gb_quot, gb_rem, 266 lba, 267 desc->blksz); 268#else 269 printf (" Capacity: %lu.%lu MB = %lu.%lu GB (%lu x %lu)\n", 270 mb_quot, mb_rem, 271 gb_quot, gb_rem, 272 (ulong)lba, 273 desc->blksz); 274#endif 275 } else { 276 puts (" Capacity: not available\n"); 277 } 278} 279 280void part_init(struct blk_desc *desc) 281{ 282 struct part_driver *drv = 283 ll_entry_start(struct part_driver, part_driver); 284 const int n_ents = ll_entry_count(struct part_driver, part_driver); 285 struct part_driver *entry; 286 287 blkcache_invalidate(desc->uclass_id, desc->devnum); 288 289 desc->part_type = PART_TYPE_UNKNOWN; 290 for (entry = drv; entry != drv + n_ents; entry++) { 291 int ret; 292 293 ret = entry->test(desc); 294 debug("%s: try '%s': ret=%d\n", __func__, entry->name, ret); 295 if (!ret) { 296 desc->part_type = entry->part_type; 297 break; 298 } 299 } 300} 301 302static void print_part_header(const char *type, struct blk_desc *desc) 303{ 304#if CONFIG_IS_ENABLED(MAC_PARTITION) || \ 305 CONFIG_IS_ENABLED(DOS_PARTITION) || \ 306 CONFIG_IS_ENABLED(ISO_PARTITION) || \ 307 CONFIG_IS_ENABLED(AMIGA_PARTITION) || \ 308 CONFIG_IS_ENABLED(EFI_PARTITION) 309 printf("\nPartition Map for %s device %d -- Partition Type: %s\n\n", 310 uclass_get_name(desc->uclass_id), desc->devnum, type); 311#endif /* any CONFIG_..._PARTITION */ 312} 313 314void part_print(struct blk_desc *desc) 315{ 316 struct part_driver *drv; 317 318 drv = part_driver_lookup_type(desc); 319 if (!drv) { 320 printf("## Unknown partition table type %x\n", 321 desc->part_type); 322 return; 323 } 324 325 PRINTF("## Testing for valid %s partition ##\n", drv->name); 326 print_part_header(drv->name, desc); 327 if (drv->print) 328 drv->print(desc); 329} 330 331int part_get_info_by_type(struct blk_desc *desc, int part, int part_type, 332 struct disk_partition *info) 333{ 334 struct part_driver *drv; 335 336 if (blk_enabled()) { 337 /* The common case is no UUID support */ 338 disk_partition_clr_uuid(info); 339 disk_partition_clr_type_guid(info); 340 341 if (part_type == PART_TYPE_UNKNOWN) { 342 drv = part_driver_lookup_type(desc); 343 } else { 344 drv = part_driver_get_type(part_type); 345 } 346 347 if (!drv) { 348 debug("## Unknown partition table type %x\n", 349 desc->part_type); 350 return -EPROTONOSUPPORT; 351 } 352 if (!drv->get_info) { 353 PRINTF("## Driver %s does not have the get_info() method\n", 354 drv->name); 355 return -ENOSYS; 356 } 357 if (drv->get_info(desc, part, info) == 0) { 358 PRINTF("## Valid %s partition found ##\n", drv->name); 359 return 0; 360 } 361 } 362 363 return -ENOENT; 364} 365 366int part_get_info(struct blk_desc *desc, int part, 367 struct disk_partition *info) 368{ 369 return part_get_info_by_type(desc, part, PART_TYPE_UNKNOWN, info); 370} 371 372int part_get_info_whole_disk(struct blk_desc *desc, 373 struct disk_partition *info) 374{ 375 info->start = 0; 376 info->size = desc->lba; 377 info->blksz = desc->blksz; 378 info->bootable = 0; 379 strcpy((char *)info->type, BOOT_PART_TYPE); 380 strcpy((char *)info->name, "Whole Disk"); 381 disk_partition_clr_uuid(info); 382 disk_partition_clr_type_guid(info); 383 384 return 0; 385} 386 387int blk_get_device_by_str(const char *ifname, const char *dev_hwpart_str, 388 struct blk_desc **desc) 389{ 390 char *ep; 391 char *dup_str = NULL; 392 const char *dev_str, *hwpart_str; 393 int dev, hwpart; 394 395 hwpart_str = strchr(dev_hwpart_str, '.'); 396 if (hwpart_str) { 397 dup_str = strdup(dev_hwpart_str); 398 dup_str[hwpart_str - dev_hwpart_str] = 0; 399 dev_str = dup_str; 400 hwpart_str++; 401 } else { 402 dev_str = dev_hwpart_str; 403 hwpart = 0; 404 } 405 406 dev = hextoul(dev_str, &ep); 407 if (*ep) { 408 printf("** Bad device specification %s %s **\n", 409 ifname, dev_str); 410 dev = -EINVAL; 411 goto cleanup; 412 } 413 414 if (hwpart_str) { 415 hwpart = hextoul(hwpart_str, &ep); 416 if (*ep) { 417 printf("** Bad HW partition specification %s %s **\n", 418 ifname, hwpart_str); 419 dev = -EINVAL; 420 goto cleanup; 421 } 422 } 423 424 *desc = get_dev_hwpart(ifname, dev, hwpart); 425 if (!(*desc) || ((*desc)->type == DEV_TYPE_UNKNOWN)) { 426 debug("** Bad device %s %s **\n", ifname, dev_hwpart_str); 427 dev = -ENODEV; 428 goto cleanup; 429 } 430 431 if (blk_enabled()) { 432 /* 433 * Updates the partition table for the specified hw partition. 434 * Always should be done, otherwise hw partition 0 will return 435 * stale data after displaying a non-zero hw partition. 436 */ 437 if ((*desc)->uclass_id == UCLASS_MMC) 438 part_init(*desc); 439 } 440 441cleanup: 442 free(dup_str); 443 return dev; 444} 445 446#define PART_UNSPECIFIED -2 447#define PART_AUTO -1 448int blk_get_device_part_str(const char *ifname, const char *dev_part_str, 449 struct blk_desc **desc, 450 struct disk_partition *info, int allow_whole_dev) 451{ 452 int ret; 453 const char *part_str; 454 char *dup_str = NULL; 455 const char *dev_str; 456 int dev; 457 char *ep; 458 int p; 459 int part; 460 struct disk_partition tmpinfo; 461 462 *desc = NULL; 463 memset(info, 0, sizeof(*info)); 464 465#if IS_ENABLED(CONFIG_SANDBOX) || IS_ENABLED(CONFIG_SEMIHOSTING) 466 /* 467 * Special-case a pseudo block device "hostfs", to allow access to the 468 * host's own filesystem. 469 */ 470 if (!strcmp(ifname, "hostfs")) { 471 strcpy((char *)info->type, BOOT_PART_TYPE); 472 strcpy((char *)info->name, "Host filesystem"); 473 474 return 0; 475 } 476#endif 477 478#if IS_ENABLED(CONFIG_CMD_UBIFS) && !IS_ENABLED(CONFIG_SPL_BUILD) 479 /* 480 * Special-case ubi, ubi goes through a mtd, rather than through 481 * a regular block device. 482 */ 483 if (!strcmp(ifname, "ubi")) { 484 if (!ubifs_is_mounted()) { 485 printf("UBIFS not mounted, use ubifsmount to mount volume first!\n"); 486 return -EINVAL; 487 } 488 489 strcpy((char *)info->type, BOOT_PART_TYPE); 490 strcpy((char *)info->name, "UBI"); 491 return 0; 492 } 493#endif 494 495 /* If no dev_part_str, use bootdevice environment variable */ 496 if (CONFIG_IS_ENABLED(ENV_SUPPORT)) { 497 if (!dev_part_str || !strlen(dev_part_str) || 498 !strcmp(dev_part_str, "-")) 499 dev_part_str = env_get("bootdevice"); 500 } 501 502 /* If still no dev_part_str, it's an error */ 503 if (!dev_part_str) { 504 printf("** No device specified **\n"); 505 ret = -ENODEV; 506 goto cleanup; 507 } 508 509 /* Separate device and partition ID specification */ 510 part_str = strchr(dev_part_str, ':'); 511 if (part_str) { 512 dup_str = strdup(dev_part_str); 513 dup_str[part_str - dev_part_str] = 0; 514 dev_str = dup_str; 515 part_str++; 516 } else { 517 dev_str = dev_part_str; 518 } 519 520 /* Look up the device */ 521 dev = blk_get_device_by_str(ifname, dev_str, desc); 522 if (dev < 0) { 523 printf("** Bad device specification %s %s **\n", 524 ifname, dev_str); 525 ret = dev; 526 goto cleanup; 527 } 528 529 /* Convert partition ID string to number */ 530 if (!part_str || !*part_str) { 531 part = PART_UNSPECIFIED; 532 } else if (!strcmp(part_str, "auto")) { 533 part = PART_AUTO; 534 } else { 535 /* Something specified -> use exactly that */ 536 part = (int)hextoul(part_str, &ep); 537 /* 538 * Less than whole string converted, 539 * or request for whole device, but caller requires partition. 540 */ 541 if (*ep || (part == 0 && !allow_whole_dev)) { 542 printf("** Bad partition specification %s %s **\n", 543 ifname, dev_part_str); 544 ret = -ENOENT; 545 goto cleanup; 546 } 547 } 548 549 /* 550 * No partition table on device, 551 * or user requested partition 0 (entire device). 552 */ 553 if (((*desc)->part_type == PART_TYPE_UNKNOWN) || !part) { 554 if (!(*desc)->lba) { 555 printf("** Bad device size - %s %s **\n", ifname, 556 dev_str); 557 ret = -EINVAL; 558 goto cleanup; 559 } 560 561 /* 562 * If user specified a partition ID other than 0, 563 * or the calling command only accepts partitions, 564 * it's an error. 565 */ 566 if ((part > 0) || (!allow_whole_dev)) { 567 printf("** No partition table - %s %s **\n", ifname, 568 dev_str); 569 ret = -EPROTONOSUPPORT; 570 goto cleanup; 571 } 572 573 (*desc)->log2blksz = LOG2((*desc)->blksz); 574 575 part_get_info_whole_disk(*desc, info); 576 577 ret = 0; 578 goto cleanup; 579 } 580 581 /* 582 * Now there's known to be a partition table, 583 * not specifying a partition means to pick partition 1. 584 */ 585 if (part == PART_UNSPECIFIED) 586 part = 1; 587 588 /* 589 * If user didn't specify a partition number, or did specify something 590 * other than "auto", use that partition number directly. 591 */ 592 if (part != PART_AUTO) { 593 ret = part_get_info(*desc, part, info); 594 if (ret) { 595 printf("** Invalid partition %d **\n", part); 596 goto cleanup; 597 } 598 } else { 599 /* 600 * Find the first bootable partition. 601 * If none are bootable, fall back to the first valid partition. 602 */ 603 part = 0; 604 for (p = 1; p <= MAX_SEARCH_PARTITIONS; p++) { 605 ret = part_get_info(*desc, p, info); 606 if (ret) 607 continue; 608 609 /* 610 * First valid partition, or new better partition? 611 * If so, save partition ID. 612 */ 613 if (!part || info->bootable) 614 part = p; 615 616 /* Best possible partition? Stop searching. */ 617 if (info->bootable) 618 break; 619 620 /* 621 * We now need to search further for best possible. 622 * If we what we just queried was the best so far, 623 * save the info since we over-write it next loop. 624 */ 625 if (part == p) 626 tmpinfo = *info; 627 } 628 /* If we found any acceptable partition */ 629 if (part) { 630 /* 631 * If we searched all possible partition IDs, 632 * return the first valid partition we found. 633 */ 634 if (p == MAX_SEARCH_PARTITIONS + 1) 635 *info = tmpinfo; 636 } else { 637 printf("** No valid partitions found **\n"); 638 goto cleanup; 639 } 640 } 641 if (strncmp((char *)info->type, BOOT_PART_TYPE, sizeof(info->type)) != 0) { 642 printf("** Invalid partition type \"%.32s\"" 643 " (expect \"" BOOT_PART_TYPE "\")\n", 644 info->type); 645 ret = -EINVAL; 646 goto cleanup; 647 } 648 649 (*desc)->log2blksz = LOG2((*desc)->blksz); 650 651 ret = part; 652 goto cleanup; 653 654cleanup: 655 free(dup_str); 656 return ret; 657} 658 659int part_get_info_by_name(struct blk_desc *desc, const char *name, 660 struct disk_partition *info) 661{ 662 struct part_driver *part_drv; 663 int ret; 664 int i; 665 666 part_drv = part_driver_lookup_type(desc); 667 if (!part_drv) 668 return -1; 669 670 if (!part_drv->get_info) { 671 log_debug("## Driver %s does not have the get_info() method\n", 672 part_drv->name); 673 return -ENOSYS; 674 } 675 676 for (i = 1; i < part_drv->max_entries; i++) { 677 ret = part_drv->get_info(desc, i, info); 678 if (ret != 0) { 679 /* 680 * Partition with this index can't be obtained, but 681 * further partitions might be, so keep checking. 682 */ 683 continue; 684 } 685 if (strcmp(name, (const char *)info->name) == 0) { 686 /* matched */ 687 return i; 688 } 689 } 690 691 return -ENOENT; 692} 693 694/** 695 * Get partition info from device number and partition name. 696 * 697 * Parse a device number and partition name string in the form of 698 * "devicenum.hwpartnum#partition_name", for example "0.1#misc". devicenum and 699 * hwpartnum are both optional, defaulting to 0. If the partition is found, 700 * sets desc and part_info accordingly with the information of the 701 * partition with the given partition_name. 702 * 703 * @param[in] dev_iface Device interface 704 * @param[in] dev_part_str Input string argument, like "0.1#misc" 705 * @param[out] desc Place to store the device description pointer 706 * @param[out] part_info Place to store the partition information 707 * Return: 0 on success, or a negative on error 708 */ 709static int part_get_info_by_dev_and_name(const char *dev_iface, 710 const char *dev_part_str, 711 struct blk_desc **desc, 712 struct disk_partition *part_info) 713{ 714 char *dup_str = NULL; 715 const char *dev_str, *part_str; 716 int ret; 717 718 /* Separate device and partition name specification */ 719 if (dev_part_str) 720 part_str = strchr(dev_part_str, '#'); 721 else 722 part_str = NULL; 723 724 if (part_str) { 725 dup_str = strdup(dev_part_str); 726 dup_str[part_str - dev_part_str] = 0; 727 dev_str = dup_str; 728 part_str++; 729 } else { 730 return -EINVAL; 731 } 732 733 ret = blk_get_device_by_str(dev_iface, dev_str, desc); 734 if (ret < 0) 735 goto cleanup; 736 737 ret = part_get_info_by_name(*desc, part_str, part_info); 738 if (ret < 0) 739 printf("Could not find \"%s\" partition\n", part_str); 740 741cleanup: 742 free(dup_str); 743 return ret; 744} 745 746int part_get_info_by_dev_and_name_or_num(const char *dev_iface, 747 const char *dev_part_str, 748 struct blk_desc **desc, 749 struct disk_partition *part_info, 750 int allow_whole_dev) 751{ 752 int ret; 753 754 /* Split the part_name if passed as "$dev_num#part_name". */ 755 ret = part_get_info_by_dev_and_name(dev_iface, dev_part_str, desc, 756 part_info); 757 if (ret >= 0) 758 return ret; 759 /* 760 * Couldn't lookup by name, try looking up the partition description 761 * directly. 762 */ 763 ret = blk_get_device_part_str(dev_iface, dev_part_str, desc, part_info, 764 allow_whole_dev); 765 if (ret < 0) 766 printf("Couldn't find partition %s %s\n", 767 dev_iface, dev_part_str); 768 return ret; 769} 770 771void part_set_generic_name(const struct blk_desc *desc, int part_num, 772 char *name) 773{ 774 char *devtype; 775 776 switch (desc->uclass_id) { 777 case UCLASS_IDE: 778 case UCLASS_AHCI: 779 devtype = "hd"; 780 break; 781 case UCLASS_SCSI: 782 devtype = "sd"; 783 break; 784 case UCLASS_USB: 785 devtype = "usbd"; 786 break; 787 case UCLASS_MMC: 788 devtype = "mmcsd"; 789 break; 790 default: 791 devtype = "xx"; 792 break; 793 } 794 795 sprintf(name, "%s%c%d", devtype, 'a' + desc->devnum, part_num); 796} 797 798int part_get_bootable(struct blk_desc *desc) 799{ 800 struct disk_partition info; 801 int p; 802 803 for (p = 1; p <= MAX_SEARCH_PARTITIONS; p++) { 804 int ret; 805 806 ret = part_get_info(desc, p, &info); 807 if (!ret && info.bootable) 808 return p; 809 } 810 811 return 0; 812} 813