uboot/disk/part_efi.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2008 RuggedCom, Inc.
   3 * Richard Retanubun <RichardRetanubun@RuggedCom.com>
   4 *
   5 * See file CREDITS for list of people who contributed to this
   6 * project.
   7 *
   8 * This program is free software; you can redistribute it and/or
   9 * modify it under the terms of the GNU General Public License as
  10 * published by the Free Software Foundation; either version 2 of
  11 * the License, or (at your option) any later version.
  12 *
  13 * This program is distributed in the hope that it will be useful,
  14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16 * GNU General Public License for more details.
  17 *
  18 * You should have received a copy of the GNU General Public License
  19 * along with this program; if not, write to the Free Software
  20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
  21 * MA 02111-1307 USA
  22 */
  23
  24/*
  25 * Problems with CONFIG_SYS_64BIT_LBA:
  26 *
  27 * struct disk_partition.start in include/part.h is sized as ulong.
  28 * When CONFIG_SYS_64BIT_LBA is activated, lbaint_t changes from ulong to uint64_t.
  29 * For now, it is cast back to ulong at assignment.
  30 *
  31 * This limits the maximum size of addressable storage to < 2 Terra Bytes
  32 */
  33#include <common.h>
  34#include <command.h>
  35#include <ide.h>
  36#include <malloc.h>
  37#include "part_efi.h"
  38
  39#if defined(CONFIG_CMD_IDE) || \
  40    defined(CONFIG_CMD_MG_DISK) || \
  41    defined(CONFIG_CMD_SATA) || \
  42    defined(CONFIG_CMD_SCSI) || \
  43    defined(CONFIG_CMD_USB) || \
  44    defined(CONFIG_MMC) || \
  45    defined(CONFIG_SYSTEMACE)
  46
  47/* Convert char[2] in little endian format to the host format integer
  48 */
  49static inline unsigned short le16_to_int(unsigned char *le16)
  50{
  51        return ((le16[1] << 8) + le16[0]);
  52}
  53
  54/* Convert char[4] in little endian format to the host format integer
  55 */
  56static inline unsigned long le32_to_int(unsigned char *le32)
  57{
  58        return ((le32[3] << 24) + (le32[2] << 16) + (le32[1] << 8) + le32[0]);
  59}
  60
  61/* Convert char[8] in little endian format to the host format integer
  62 */
  63static inline unsigned long long le64_to_int(unsigned char *le64)
  64{
  65        return (((unsigned long long)le64[7] << 56) +
  66                ((unsigned long long)le64[6] << 48) +
  67                ((unsigned long long)le64[5] << 40) +
  68                ((unsigned long long)le64[4] << 32) +
  69                ((unsigned long long)le64[3] << 24) +
  70                ((unsigned long long)le64[2] << 16) +
  71                ((unsigned long long)le64[1] << 8) +
  72                (unsigned long long)le64[0]);
  73}
  74
  75/**
  76 * efi_crc32() - EFI version of crc32 function
  77 * @buf: buffer to calculate crc32 of
  78 * @len - length of buf
  79 *
  80 * Description: Returns EFI-style CRC32 value for @buf
  81 */
  82static inline unsigned long efi_crc32(const void *buf, unsigned long len)
  83{
  84        return crc32(0, buf, len);
  85}
  86
  87/*
  88 * Private function prototypes
  89 */
  90
  91static int pmbr_part_valid(struct partition *part);
  92static int is_pmbr_valid(legacy_mbr * mbr);
  93
  94static int is_gpt_valid(block_dev_desc_t * dev_desc, unsigned long long lba,
  95                                gpt_header * pgpt_head, gpt_entry ** pgpt_pte);
  96
  97static gpt_entry *alloc_read_gpt_entries(block_dev_desc_t * dev_desc,
  98                                gpt_header * pgpt_head);
  99
 100static int is_pte_valid(gpt_entry * pte);
 101
 102/*
 103 * Public Functions (include/part.h)
 104 */
 105
 106void print_part_efi(block_dev_desc_t * dev_desc)
 107{
 108        gpt_header gpt_head;
 109        gpt_entry **pgpt_pte = NULL;
 110        int i = 0;
 111
 112        if (!dev_desc) {
 113                printf("%s: Invalid Argument(s)\n", __FUNCTION__);
 114                return;
 115        }
 116        /* This function validates AND fills in the GPT header and PTE */
 117        if (is_gpt_valid(dev_desc, GPT_PRIMARY_PARTITION_TABLE_LBA,
 118                         &(gpt_head), pgpt_pte) != 1) {
 119                printf("%s: *** ERROR: Invalid GPT ***\n", __FUNCTION__);
 120                return;
 121        }
 122
 123        debug("%s: gpt-entry at 0x%08X\n", __FUNCTION__, (unsigned int)*pgpt_pte);
 124
 125        printf("Part  Start LBA  End LBA\n");
 126        for (i = 0; i < le32_to_int(gpt_head.num_partition_entries); i++) {
 127
 128                if (is_pte_valid(&(*pgpt_pte)[i])) {
 129                        printf("%s%d  0x%llX    0x%llX\n", GPT_ENTRY_NAME,
 130                                (i + 1),
 131                                le64_to_int((*pgpt_pte)[i].starting_lba),
 132                                le64_to_int((*pgpt_pte)[i].ending_lba));
 133                } else {
 134                        break;  /* Stop at the first non valid PTE */
 135                }
 136        }
 137
 138        /* Remember to free pte */
 139        if (*pgpt_pte != NULL) {
 140                debug("%s: Freeing pgpt_pte\n", __FUNCTION__);
 141                free(*pgpt_pte);
 142        }
 143        return;
 144}
 145
 146int get_partition_info_efi(block_dev_desc_t * dev_desc, int part,
 147                                disk_partition_t * info)
 148{
 149        gpt_header gpt_head;
 150        gpt_entry **pgpt_pte = NULL;
 151
 152        /* "part" argument must be at least 1 */
 153        if (!dev_desc || !info || part < 1) {
 154                printf("%s: Invalid Argument(s)\n", __FUNCTION__);
 155                return -1;
 156        }
 157
 158        /* This function validates AND fills in the GPT header and PTE */
 159        if (is_gpt_valid(dev_desc, GPT_PRIMARY_PARTITION_TABLE_LBA,
 160                        &(gpt_head), pgpt_pte) != 1) {
 161                printf("%s: *** ERROR: Invalid GPT ***\n", __FUNCTION__);
 162                return -1;
 163        }
 164
 165        /* The ulong casting limits the maximum disk size to 2 TB */
 166        info->start = (ulong) le64_to_int((*pgpt_pte)[part - 1].starting_lba);
 167        /* The ending LBA is inclusive, to calculate size, add 1 to it */
 168        info->size = ((ulong)le64_to_int((*pgpt_pte)[part - 1].ending_lba) + 1)
 169                     - info->start;
 170        info->blksz = GPT_BLOCK_SIZE;
 171
 172        sprintf((char *)info->name, "%s%d", GPT_ENTRY_NAME, part);
 173        sprintf((char *)info->type, "U-Boot");
 174
 175        debug("%s: start 0x%lX, size 0x%lX, name %s", __FUNCTION__,
 176                info->start, info->size, info->name);
 177
 178        /* Remember to free pte */
 179        if (*pgpt_pte != NULL) {
 180                debug("%s: Freeing pgpt_pte\n", __FUNCTION__);
 181                free(*pgpt_pte);
 182        }
 183        return 0;
 184}
 185
 186int test_part_efi(block_dev_desc_t * dev_desc)
 187{
 188        legacy_mbr legacymbr;
 189
 190        /* Read legacy MBR from block 0 and validate it */
 191        if ((dev_desc->block_read(dev_desc->dev, 0, 1, (ulong *) & legacymbr) != 1)
 192                || (is_pmbr_valid(&legacymbr) != 1)) {
 193                return -1;
 194        }
 195        return 0;
 196}
 197
 198/*
 199 * Private functions
 200 */
 201/*
 202 * pmbr_part_valid(): Check for EFI partition signature
 203 *
 204 * Returns: 1 if EFI GPT partition type is found.
 205 */
 206static int pmbr_part_valid(struct partition *part)
 207{
 208        if (part->sys_ind == EFI_PMBR_OSTYPE_EFI_GPT &&
 209                le32_to_int(part->start_sect) == 1UL) {
 210                return 1;
 211        }
 212
 213        return 0;
 214}
 215
 216/*
 217 * is_pmbr_valid(): test Protective MBR for validity
 218 *
 219 * Returns: 1 if PMBR is valid, 0 otherwise.
 220 * Validity depends on two things:
 221 *  1) MSDOS signature is in the last two bytes of the MBR
 222 *  2) One partition of type 0xEE is found, checked by pmbr_part_valid()
 223 */
 224static int is_pmbr_valid(legacy_mbr * mbr)
 225{
 226        int i = 0;
 227
 228        if (!mbr || le16_to_int(mbr->signature) != MSDOS_MBR_SIGNATURE) {
 229                return 0;
 230        }
 231
 232        for (i = 0; i < 4; i++) {
 233                if (pmbr_part_valid(&mbr->partition_record[i])) {
 234                        return 1;
 235                }
 236        }
 237        return 0;
 238}
 239
 240/**
 241 * is_gpt_valid() - tests one GPT header and PTEs for validity
 242 *
 243 * lba is the logical block address of the GPT header to test
 244 * gpt is a GPT header ptr, filled on return.
 245 * ptes is a PTEs ptr, filled on return.
 246 *
 247 * Description: returns 1 if valid,  0 on error.
 248 * If valid, returns pointers to PTEs.
 249 */
 250static int is_gpt_valid(block_dev_desc_t * dev_desc, unsigned long long lba,
 251                        gpt_header * pgpt_head, gpt_entry ** pgpt_pte)
 252{
 253        unsigned char crc32_backup[4] = { 0 };
 254        unsigned long calc_crc32;
 255        unsigned long long lastlba;
 256
 257        if (!dev_desc || !pgpt_head) {
 258                printf("%s: Invalid Argument(s)\n", __FUNCTION__);
 259                return 0;
 260        }
 261
 262        /* Read GPT Header from device */
 263        if (dev_desc->block_read(dev_desc->dev, lba, 1, pgpt_head) != 1) {
 264                printf("*** ERROR: Can't read GPT header ***\n");
 265                return 0;
 266        }
 267
 268        /* Check the GPT header signature */
 269        if (le64_to_int(pgpt_head->signature) != GPT_HEADER_SIGNATURE) {
 270                printf("GUID Partition Table Header signature is wrong:"
 271                        "0x%llX != 0x%llX\n",
 272                        (unsigned long long)le64_to_int(pgpt_head->signature),
 273                        (unsigned long long)GPT_HEADER_SIGNATURE);
 274                return 0;
 275        }
 276
 277        /* Check the GUID Partition Table CRC */
 278        memcpy(crc32_backup, pgpt_head->header_crc32, sizeof(crc32_backup));
 279        memset(pgpt_head->header_crc32, 0, sizeof(pgpt_head->header_crc32));
 280
 281        calc_crc32 = efi_crc32((const unsigned char *)pgpt_head,
 282                le32_to_int(pgpt_head->header_size));
 283
 284        memcpy(pgpt_head->header_crc32, crc32_backup, sizeof(crc32_backup));
 285
 286        if (calc_crc32 != le32_to_int(crc32_backup)) {
 287                printf("GUID Partition Table Header CRC is wrong:"
 288                        "0x%08lX != 0x%08lX\n",
 289                        le32_to_int(crc32_backup), calc_crc32);
 290                return 0;
 291        }
 292
 293        /* Check that the my_lba entry points to the LBA that contains the GPT */
 294        if (le64_to_int(pgpt_head->my_lba) != lba) {
 295                printf("GPT: my_lba incorrect: %llX != %llX\n",
 296                        (unsigned long long)le64_to_int(pgpt_head->my_lba),
 297                        (unsigned long long)lba);
 298                return 0;
 299        }
 300
 301        /* Check the first_usable_lba and last_usable_lba are within the disk. */
 302        lastlba = (unsigned long long)dev_desc->lba;
 303        if (le64_to_int(pgpt_head->first_usable_lba) > lastlba) {
 304                printf("GPT: first_usable_lba incorrect: %llX > %llX\n",
 305                        le64_to_int(pgpt_head->first_usable_lba), lastlba);
 306                return 0;
 307        }
 308        if (le64_to_int(pgpt_head->last_usable_lba) > lastlba) {
 309                printf("GPT: last_usable_lba incorrect: %llX > %llX\n",
 310                        le64_to_int(pgpt_head->last_usable_lba), lastlba);
 311                return 0;
 312        }
 313
 314        debug("GPT: first_usable_lba: %llX last_usable_lba %llX last lba %llX\n",
 315                le64_to_int(pgpt_head->first_usable_lba),
 316                le64_to_int(pgpt_head->last_usable_lba), lastlba);
 317
 318        /* Read and allocate Partition Table Entries */
 319        *pgpt_pte = alloc_read_gpt_entries(dev_desc, pgpt_head);
 320        if (*pgpt_pte == NULL) {
 321                printf("GPT: Failed to allocate memory for PTE\n");
 322                return 0;
 323        }
 324
 325        /* Check the GUID Partition Table Entry Array CRC */
 326        calc_crc32 = efi_crc32((const unsigned char *)*pgpt_pte,
 327                le32_to_int(pgpt_head->num_partition_entries) *
 328                le32_to_int(pgpt_head->sizeof_partition_entry));
 329
 330        if (calc_crc32 != le32_to_int(pgpt_head->partition_entry_array_crc32)) {
 331                printf("GUID Partition Table Entry Array CRC is wrong:"
 332                        "0x%08lX != 0x%08lX\n",
 333                        le32_to_int(pgpt_head->partition_entry_array_crc32),
 334                        calc_crc32);
 335
 336                if (*pgpt_pte != NULL) {
 337                        free(*pgpt_pte);
 338                }
 339                return 0;
 340        }
 341
 342        /* We're done, all's well */
 343        return 1;
 344}
 345
 346/**
 347 * alloc_read_gpt_entries(): reads partition entries from disk
 348 * @dev_desc
 349 * @gpt - GPT header
 350 *
 351 * Description: Returns ptes on success,  NULL on error.
 352 * Allocates space for PTEs based on information found in @gpt.
 353 * Notes: remember to free pte when you're done!
 354 */
 355static gpt_entry *alloc_read_gpt_entries(block_dev_desc_t * dev_desc,
 356                                         gpt_header * pgpt_head)
 357{
 358        size_t count = 0;
 359        gpt_entry *pte = NULL;
 360
 361        if (!dev_desc || !pgpt_head) {
 362                printf("%s: Invalid Argument(s)\n", __FUNCTION__);
 363                return NULL;
 364        }
 365
 366        count = le32_to_int(pgpt_head->num_partition_entries) *
 367                le32_to_int(pgpt_head->sizeof_partition_entry);
 368
 369        debug("%s: count = %lu * %lu = %u\n", __FUNCTION__,
 370                le32_to_int(pgpt_head->num_partition_entries),
 371                le32_to_int(pgpt_head->sizeof_partition_entry), count);
 372
 373        /* Allocate memory for PTE, remember to FREE */
 374        if (count != 0) {
 375                pte = malloc(count);
 376        }
 377
 378        if (count == 0 || pte == NULL) {
 379                printf("%s: ERROR: Can't allocate 0x%X bytes for GPT Entries\n",
 380                        __FUNCTION__, count);
 381                return NULL;
 382        }
 383
 384        /* Read GPT Entries from device */
 385        if (dev_desc->block_read (dev_desc->dev,
 386                (unsigned long)le64_to_int(pgpt_head->partition_entry_lba),
 387                (lbaint_t) (count / GPT_BLOCK_SIZE), pte)
 388                != (count / GPT_BLOCK_SIZE)) {
 389
 390                printf("*** ERROR: Can't read GPT Entries ***\n");
 391                free(pte);
 392                return NULL;
 393        }
 394        return pte;
 395}
 396
 397/**
 398 * is_pte_valid(): validates a single Partition Table Entry
 399 * @gpt_entry - Pointer to a single Partition Table Entry
 400 *
 401 * Description: returns 1 if valid,  0 on error.
 402 */
 403static int is_pte_valid(gpt_entry * pte)
 404{
 405        efi_guid_t unused_guid;
 406
 407        if (!pte) {
 408                printf("%s: Invalid Argument(s)\n", __FUNCTION__);
 409                return 0;
 410        }
 411
 412        /* Only one validation for now:
 413         * The GUID Partition Type != Unused Entry (ALL-ZERO)
 414         */
 415        memset(unused_guid.b, 0, sizeof(unused_guid.b));
 416
 417        if (memcmp(pte->partition_type_guid.b, unused_guid.b,
 418                sizeof(unused_guid.b)) == 0) {
 419
 420                debug("%s: Found an unused PTE GUID at 0x%08X\n", __FUNCTION__,
 421                (unsigned int)pte);
 422
 423                return 0;
 424        } else {
 425                return 1;
 426        }
 427}
 428#endif
 429