| Index: src/platform/vboot_reference/vboot_firmware/lib/cgptlib/cgptlib.c
|
| diff --git a/src/platform/vboot_reference/vboot_firmware/lib/cgptlib/cgptlib.c b/src/platform/vboot_reference/vboot_firmware/lib/cgptlib/cgptlib.c
|
| index 334c578967e1e5cf0af5a94811d7aa69c06d6e38..2cf606204d87a52b0a2b56da3e4b3a7031f02a9e 100644
|
| --- a/src/platform/vboot_reference/vboot_firmware/lib/cgptlib/cgptlib.c
|
| +++ b/src/platform/vboot_reference/vboot_firmware/lib/cgptlib/cgptlib.c
|
| @@ -4,806 +4,142 @@
|
| */
|
|
|
| #include "cgptlib.h"
|
| -#include <string.h>
|
| #include "cgptlib_internal.h"
|
| #include "crc32.h"
|
| #include "gpt.h"
|
| -#include "quick_sort.h"
|
| #include "utility.h"
|
|
|
| -/* Macro to invalidate a GPT header/entries */
|
| -#define INVALIDATE_HEADER(valid_headers, index) \
|
| - do { \
|
| - debug("- INVALIDATE_HEADER() at %s():%d\n", __FUNCTION__, __LINE__); \
|
| - valid_headers &= ~(1<<index); \
|
| - } while (0)
|
| -#define INVALIDATE_ENTRIES(valid_entries, index) \
|
| - do { \
|
| - debug("- INVALIDATE_ENTRIES() at %s():%d\n", __FUNCTION__, __LINE__); \
|
| - valid_entries &= ~(1<<index); \
|
| - } while (0)
|
|
|
| -const char *GptError(int errno) {
|
| - const char *error_string[] = {
|
| - /* GPT_SUCCESS */ "Success",
|
| - /* GPT_ERROR_NO_VALID_KERNEL */ "No valid kernel entry",
|
| - /* GPT_ERROR_INVALID_HEADERS */ "Both primary and secondary headers are "
|
| - "invalid.",
|
| - /* GPT_ERROR_INVALID_ENTRIES */ "Both primary and secondary entries are "
|
| - "invalid.",
|
| - /* GPT_ERROR_INVALID_SECTOR_SIZE */ "Invalid sector size",
|
| - /* GPT_ERROR_INVALID_SECTOR_NUMBER */ "Invalid sector number",
|
| - /* GPT_ERROR_INVALID_UPDATE_TYPE */ "Invalid update type",
|
| - };
|
| - return error_string[errno];
|
| -}
|
| +int GptInit(GptData *gpt) {
|
| + int retval;
|
|
|
| -/* Checks if sector_bytes and drive_sectors are valid values. */
|
| -int CheckParameters(GptData *gpt) {
|
| - /* Currently, we only support 512-byte sector. In the future, we may support
|
| - * larger sector. */
|
| - if (gpt->sector_bytes != 512)
|
| - return GPT_ERROR_INVALID_SECTOR_SIZE;
|
| + gpt->modified = 0;
|
| + gpt->current_kernel = CGPT_KERNEL_ENTRY_NOT_FOUND;
|
| + gpt->current_priority = 999;
|
|
|
| - /* The sector number of a drive should be reasonable. If the given value is
|
| - * too small to contain basic GPT structure (PMBR + Headers + Entries),
|
| - * the value is wrong. */
|
| - if (gpt->drive_sectors < (GPT_PMBR_SECTOR +
|
| - GPT_HEADER_SECTOR * 2 +
|
| - GPT_ENTRIES_SECTORS * 2))
|
| - return GPT_ERROR_INVALID_SECTOR_NUMBER;
|
| + retval = GptSanityCheck(gpt);
|
| + if (GPT_SUCCESS != retval)
|
| + return retval;
|
|
|
| + GptRepair(gpt);
|
| return GPT_SUCCESS;
|
| }
|
|
|
| -/* Expects header signature should be GPT_HEADER_SIGNATURE. */
|
| -uint32_t CheckHeaderSignature(GptData *gpt) {
|
| - GptHeader *headers[] = {
|
| - (GptHeader*)gpt->primary_header,
|
| - (GptHeader*)gpt->secondary_header,
|
| - };
|
| - int i;
|
| -
|
| - for (i = PRIMARY; i <= SECONDARY; ++i) {
|
| - if (Memcmp(headers[i]->signature,
|
| - GPT_HEADER_SIGNATURE,
|
| - GPT_HEADER_SIGNATURE_SIZE)) {
|
| - INVALIDATE_HEADER(gpt->valid_headers, i);
|
| - }
|
| - }
|
| - return gpt->valid_headers;
|
| -}
|
| -
|
| -/* The header revision should be GPT_HEADER_REVISION. */
|
| -uint32_t CheckRevision(GptData *gpt) {
|
| - GptHeader *headers[] = {
|
| - (GptHeader*)gpt->primary_header,
|
| - (GptHeader*)gpt->secondary_header,
|
| - };
|
| - int i;
|
| -
|
| - for (i = PRIMARY; i <= SECONDARY; ++i) {
|
| - if (headers[i]->revision != GPT_HEADER_REVISION)
|
| - INVALIDATE_HEADER(gpt->valid_headers, i);
|
| - }
|
| - return gpt->valid_headers;
|
| -}
|
| -
|
| -/* A valid header size should be between MIN_SIZE_OF_HEADER and
|
| - * MAX_SIZE_OF_HEADER. */
|
| -uint32_t CheckSize(GptData *gpt) {
|
| - GptHeader *headers[] = {
|
| - (GptHeader*)gpt->primary_header,
|
| - (GptHeader*)gpt->secondary_header,
|
| - };
|
| - int i;
|
| -
|
| - for (i = PRIMARY; i <= SECONDARY; ++i) {
|
| - if ((headers[i]->size < MIN_SIZE_OF_HEADER) ||
|
| - (headers[i]->size > MAX_SIZE_OF_HEADER))
|
| - INVALIDATE_HEADER(gpt->valid_headers, i);
|
| - }
|
| - return gpt->valid_headers;
|
| -}
|
| -
|
| -/* Reserved and padding fields should be zero. */
|
| -uint32_t CheckReservedFields(GptData *gpt) {
|
| - GptHeader *headers[] = {
|
| - (GptHeader*)gpt->primary_header,
|
| - (GptHeader*)gpt->secondary_header,
|
| - };
|
| - int i;
|
| -
|
| - for (i = PRIMARY; i <= SECONDARY; ++i) {
|
| - if (headers[i]->reserved || headers[i]->padding)
|
| - INVALIDATE_HEADER(gpt->valid_headers, i);
|
| - }
|
| - return gpt->valid_headers;
|
| -}
|
| -
|
| -/* my_lba field points to the header itself.
|
| - * So that the my_lba of primary header should be 1 (right after PMBR).
|
| - * The my_lba of secondary header should be the last secotr on drive. */
|
| -uint32_t CheckMyLba(GptData *gpt) {
|
| - GptHeader *primary_header, *secondary_header;
|
| -
|
| - primary_header = (GptHeader*)gpt->primary_header;
|
| - secondary_header = (GptHeader*)gpt->secondary_header;
|
| -
|
| - if (primary_header->my_lba != GPT_PMBR_SECTOR) /* 2nd sector on drive */
|
| - INVALIDATE_HEADER(gpt->valid_headers, PRIMARY);
|
| - if (secondary_header->my_lba != (gpt->drive_sectors - 1)) /* last sector */
|
| - INVALIDATE_HEADER(gpt->valid_headers, SECONDARY);
|
| - return gpt->valid_headers;
|
| -}
|
| -
|
| -/* SizeOfPartitionEntry must be between MIN_SIZE_OF_ENTRY and
|
| - * MAX_SIZE_OF_ENTRY, and a multiple of SIZE_OF_ENTRY_MULTIPLE. */
|
| -uint32_t CheckSizeOfPartitionEntry(GptData *gpt) {
|
| - GptHeader *headers[] = {
|
| - (GptHeader*)gpt->primary_header,
|
| - (GptHeader*)gpt->secondary_header,
|
| - };
|
| - int i;
|
| -
|
| - for (i = PRIMARY; i <= SECONDARY; ++i) {
|
| - uint32_t size_of_entry = headers[i]->size_of_entry;
|
| - if ((size_of_entry < MIN_SIZE_OF_ENTRY) ||
|
| - (size_of_entry > MAX_SIZE_OF_ENTRY) ||
|
| - (size_of_entry & (SIZE_OF_ENTRY_MULTIPLE - 1)))
|
| - INVALIDATE_HEADER(gpt->valid_headers, i);
|
| - }
|
| - return gpt->valid_headers;
|
| -}
|
| -
|
| -/* number_of_entries must be between MIN_NUMBER_OF_ENTRIES and
|
| - * MAX_NUMBER_OF_ENTRIES, and size_of_entry * number_of_entries must be
|
| - * equal to TOTAL_ENTRIES_SIZE. */
|
| -uint32_t CheckNumberOfEntries(GptData *gpt) {
|
| - GptHeader *headers[] = {
|
| - (GptHeader*)gpt->primary_header,
|
| - (GptHeader*)gpt->secondary_header,
|
| - };
|
| - int i;
|
| -
|
| - for (i = PRIMARY; i <= SECONDARY; ++i) {
|
| - uint32_t number_of_entries = headers[i]->number_of_entries;
|
| - if ((number_of_entries < MIN_NUMBER_OF_ENTRIES) ||
|
| - (number_of_entries > MAX_NUMBER_OF_ENTRIES) ||
|
| - (number_of_entries * headers[i]->size_of_entry != TOTAL_ENTRIES_SIZE))
|
| - INVALIDATE_HEADER(gpt->valid_headers, i);
|
| - }
|
| - return gpt->valid_headers;
|
| -}
|
| -
|
| -/* Make sure entries_lba is correct.
|
| - * 2 for primary entries
|
| - * drive_sectors-1-GPT_ENTRIES_SECTORS for secondary entries. */
|
| -uint32_t CheckEntriesLba(GptData *gpt) {
|
| - GptHeader *primary_header, *secondary_header;
|
| -
|
| - primary_header = (GptHeader*)gpt->primary_header;
|
| - secondary_header = (GptHeader*)gpt->secondary_header;
|
| -
|
| - /* We assume the primary partition entry table is located at the sector
|
| - * right after primary partition header. */
|
| - if (primary_header->entries_lba != (GPT_PMBR_SECTOR + GPT_HEADER_SECTOR))
|
| - INVALIDATE_HEADER(gpt->valid_headers, PRIMARY);
|
| - /* We assume the secondary partition entry table is the 32 sectors
|
| - * right before the secondary partition header. */
|
| - if (secondary_header->entries_lba !=
|
| - (gpt->drive_sectors - 1 - GPT_ENTRIES_SECTORS))
|
| - INVALIDATE_HEADER(gpt->valid_headers, SECONDARY);
|
| - return gpt->valid_headers;
|
| -}
|
| -
|
| -/* FirstUsableLBA must be after the end of the primary GPT table array.
|
| - * LastUsableLBA must be before the start of the secondary GPT table array.
|
| - * FirstUsableLBA <= LastUsableLBA. */
|
| -uint32_t CheckValidUsableLbas(GptData *gpt) {
|
| - uint64_t end_of_primary_entries;
|
| - uint64_t start_of_secondary_entries;
|
| - GptHeader *headers[] = {
|
| - (GptHeader*)gpt->primary_header,
|
| - (GptHeader*)gpt->secondary_header,
|
| - };
|
| - int i;
|
| -
|
| - end_of_primary_entries = GPT_PMBR_SECTOR + GPT_HEADER_SECTOR +
|
| - GPT_ENTRIES_SECTORS;
|
| - start_of_secondary_entries = (gpt->drive_sectors - 1 - GPT_ENTRIES_SECTORS);
|
| -
|
| - for (i = PRIMARY; i <= SECONDARY; ++i) {
|
| - if (headers[i]->first_usable_lba < end_of_primary_entries)
|
| - INVALIDATE_HEADER(gpt->valid_headers, i);
|
| - if (headers[i]->last_usable_lba >= start_of_secondary_entries)
|
| - INVALIDATE_HEADER(gpt->valid_headers, i);
|
| - if (headers[i]->first_usable_lba > headers[i]->last_usable_lba)
|
| - INVALIDATE_HEADER(gpt->valid_headers, i);
|
| - }
|
| -
|
| - if (headers[PRIMARY]->first_usable_lba - headers[PRIMARY]->entries_lba <
|
| - GPT_ENTRIES_SECTORS)
|
| - INVALIDATE_HEADER(gpt->valid_headers, PRIMARY);
|
| - if (headers[SECONDARY]->last_usable_lba >= headers[SECONDARY]->entries_lba)
|
| - INVALIDATE_HEADER(gpt->valid_headers, SECONDARY);
|
| -
|
| - return gpt->valid_headers;
|
| -}
|
| -
|
| -/* Checks header CRC */
|
| -uint32_t CheckHeaderCrc(GptData *gpt) {
|
| - uint32_t crc32, original_crc32;
|
| - GptHeader *headers[] = {
|
| - (GptHeader*)gpt->primary_header,
|
| - (GptHeader*)gpt->secondary_header,
|
| - };
|
| - int i;
|
| -
|
| - for (i = PRIMARY; i <= SECONDARY; ++i) {
|
| - if (!(gpt->valid_headers & (1 << i))) continue;
|
| - original_crc32 = headers[i]->header_crc32;
|
| - headers[i]->header_crc32 = 0;
|
| - crc32 = Crc32((const uint8_t *)headers[i], headers[i]->size);
|
| - headers[i]->header_crc32 = original_crc32;
|
| - if (crc32 != original_crc32)
|
| - INVALIDATE_HEADER(gpt->valid_headers, i);
|
| - }
|
| - return gpt->valid_headers;
|
| -}
|
|
|
| -/* Checks entries CRC */
|
| -uint32_t CheckEntriesCrc(GptData *gpt) {
|
| - uint32_t crc32;
|
| - GptHeader *headers[] = {
|
| - (GptHeader*)gpt->primary_header,
|
| - (GptHeader*)gpt->secondary_header,
|
| - };
|
| - GptEntry *entries[] = {
|
| - (GptEntry*)gpt->primary_entries,
|
| - (GptEntry*)gpt->secondary_entries,
|
| - };
|
| - uint32_t entries_crc32;
|
| +int GptNextKernelEntry(GptData* gpt, uint64_t* start_sector, uint64_t* size) {
|
| + GptHeader* header = (GptHeader*)gpt->primary_header;
|
| + GptEntry* entries = (GptEntry*)gpt->primary_entries;
|
| + GptEntry* e;
|
| + int new_kernel = CGPT_KERNEL_ENTRY_NOT_FOUND;
|
| + int new_prio = 0;
|
| int i;
|
|
|
| - if (gpt->valid_headers & MASK_PRIMARY)
|
| - entries_crc32 = headers[PRIMARY]->entries_crc32;
|
| - else
|
| - entries_crc32 = headers[SECONDARY]->entries_crc32;
|
| -
|
| - for (i = PRIMARY; i <= SECONDARY; ++i) {
|
| - crc32 = Crc32((const uint8_t *)entries[i], TOTAL_ENTRIES_SIZE);
|
| - if (crc32 != entries_crc32)
|
| - INVALIDATE_ENTRIES(gpt->valid_entries, i);
|
| - }
|
| - return gpt->valid_entries;
|
| -}
|
| -
|
| -/* Returns non-zero if the given GUID is non-zero. */
|
| -int NonZeroGuid(const Guid *guid) {
|
| - static Guid zero = {{{0, 0, 0, 0, 0, {0, 0, 0, 0, 0, 0}}}};
|
| - return Memcmp(&zero, guid, sizeof(zero));
|
| -}
|
| -
|
| -/* Checks if entries geometry is valid.
|
| - * All active (non-zero PartitionTypeGUID) partition entries should have:
|
| - * entry.StartingLBA >= header.FirstUsableLBA
|
| - * entry.EndingLBA <= header.LastUsableLBA
|
| - * entry.StartingLBA <= entry.EndingLBA
|
| - */
|
| -uint32_t CheckValidEntries(GptData *gpt) {
|
| - uint32_t valid_entries = MASK_BOTH;
|
| - GptHeader *headers[] = {
|
| - (GptHeader*)gpt->primary_header,
|
| - (GptHeader*)gpt->secondary_header,
|
| - };
|
| - GptEntry *entries[] = {
|
| - (GptEntry*)gpt->primary_entries,
|
| - (GptEntry*)gpt->secondary_entries,
|
| - };
|
| - uint32_t number_of_entries, size_of_entry;
|
| - uint64_t first_usable_lba, last_usable_lba;
|
| - int copy, entry_index;
|
| - GptEntry *entry;
|
| -
|
| - if (gpt->valid_headers & MASK_PRIMARY)
|
| - copy = PRIMARY;
|
| - else
|
| - copy = SECONDARY;
|
| - number_of_entries = headers[copy]->number_of_entries;
|
| - size_of_entry = headers[copy]->size_of_entry;
|
| - first_usable_lba = headers[copy]->first_usable_lba;
|
| - last_usable_lba = headers[copy]->last_usable_lba;
|
| -
|
| - for (copy = PRIMARY; copy <= SECONDARY; ++copy) {
|
| - for (entry_index = 0;
|
| - entry_index < number_of_entries;
|
| - ++entry_index) {
|
| - entry = (GptEntry*)&(((uint8_t*)entries[copy])
|
| - [entry_index * size_of_entry]);
|
| - if (NonZeroGuid(&entry->type)) {
|
| - if ((entry->starting_lba < first_usable_lba) ||
|
| - (entry->ending_lba > last_usable_lba) ||
|
| - (entry->ending_lba < entry->starting_lba))
|
| - INVALIDATE_ENTRIES(valid_entries, copy);
|
| + /* If we already found a kernel, continue the scan at the current
|
| + * kernel's prioity, in case there is another kernel with the same
|
| + * priority. */
|
| + if (gpt->current_kernel != CGPT_KERNEL_ENTRY_NOT_FOUND) {
|
| + for (i = gpt->current_kernel + 1; i < header->number_of_entries; i++) {
|
| + e = entries + i;
|
| + if (!IsKernelEntry(e))
|
| + continue;
|
| + if (!(GetEntrySuccessful(e) || GetEntryTries(e)))
|
| + continue;
|
| + if (GetEntryPriority(e) == gpt->current_priority) {
|
| + gpt->current_kernel = i;
|
| + *start_sector = e->starting_lba;
|
| + *size = e->ending_lba - e->starting_lba + 1;
|
| + return GPT_SUCCESS;
|
| }
|
| }
|
| }
|
| - return valid_entries;
|
| -}
|
| -
|
| -static pair_t pairs[MAX_NUMBER_OF_ENTRIES];
|
| -/* Callback function for QuickSort(). Returns 1 if 'a_' should precede 'b_'. */
|
| -int compare_pair(const void *a_, const void *b_) {
|
| - const pair_t *a = a_;
|
| - const pair_t *b = b_;
|
| - if (a->starting <= b->starting) return 1;
|
| - return 0;
|
| -}
|
|
|
| -/* First sorts by starting_lba, and traverse everyone once if its starting_lba
|
| - * is between previous starting_lba and ending_lba. If yes, overlapped.
|
| - * Returns 1 if overlap is found. */
|
| -int OverlappedEntries(GptEntry *entries, uint32_t number_of_entries) {
|
| - int i, num_of_pair = 0;
|
| - for (i = 0; i < number_of_entries; ++i) {
|
| - if (NonZeroGuid(&entries[i].type)) {
|
| - pairs[num_of_pair].starting = entries[i].starting_lba;
|
| - pairs[num_of_pair].ending = entries[i].ending_lba;
|
| - ++num_of_pair;
|
| + /* We're still here, so scan for the remaining kernel with the
|
| + * highest priority less than the previous attempt. */
|
| + for (i = 0, e = entries; i < header->number_of_entries; i++, e++) {
|
| + int current_prio = GetEntryPriority(e);
|
| + if (!IsKernelEntry(e))
|
| + continue;
|
| + if (!(GetEntrySuccessful(e) || GetEntryTries(e)))
|
| + continue;
|
| + if (current_prio >= gpt->current_priority)
|
| + continue; /* Already returned this kernel in a previous call */
|
| + if (current_prio > new_prio) {
|
| + new_kernel = i;
|
| + new_prio = current_prio;
|
| }
|
| }
|
| - QuickSort(&pairs, num_of_pair, sizeof(pair_t), compare_pair);
|
| -
|
| - for (i = 1; i < num_of_pair; ++i) {
|
| - if ((pairs[i].starting >= pairs[i-1].starting) &&
|
| - (pairs[i].starting <= pairs[i-1].ending))
|
| - return 1;
|
| - }
|
| -
|
| - return 0;
|
| -}
|
| -
|
| -/* Checks if any two partitions are overlapped in primary and secondary entries.
|
| - */
|
| -uint32_t CheckOverlappedPartition(GptData *gpt) {
|
| - GptHeader *headers[] = {
|
| - (GptHeader*)gpt->primary_header,
|
| - (GptHeader*)gpt->secondary_header,
|
| - };
|
| - GptEntry *entries[] = {
|
| - (GptEntry*)gpt->primary_entries,
|
| - (GptEntry*)gpt->secondary_entries,
|
| - };
|
| - int i;
|
| - uint32_t number_of_entries;
|
| -
|
| - if (gpt->valid_headers & MASK_PRIMARY)
|
| - number_of_entries = headers[PRIMARY]->number_of_entries;
|
| - else
|
| - number_of_entries = headers[SECONDARY]->number_of_entries;
|
| -
|
| - for (i = PRIMARY; i <= SECONDARY; ++i) {
|
| - if (OverlappedEntries(entries[i], number_of_entries))
|
| - INVALIDATE_ENTRIES(gpt->valid_entries, i);
|
| - }
|
| - return gpt->valid_entries;
|
| -}
|
| -
|
| -/* Primary entries and secondary entries should be bitwise identical.
|
| - * If two entries tables are valid, compare them. If not the same,
|
| - * overwrites secondary with primary (primary always has higher priority),
|
| - * and marks secondary as modified.
|
| - * If only one is valid, overwrites invalid one.
|
| - * If all are invalid, does nothing.
|
| - * This function returns bit masks for GptData.modified field.
|
| - * Note that CRC is NOT re-computed in this function.
|
| - */
|
| -uint8_t RepairEntries(GptData *gpt, const uint32_t valid_entries) {
|
| - if (valid_entries == MASK_BOTH) {
|
| - if (Memcmp(gpt->primary_entries, gpt->secondary_entries,
|
| - TOTAL_ENTRIES_SIZE)) {
|
| - Memcpy(gpt->secondary_entries, gpt->primary_entries, TOTAL_ENTRIES_SIZE);
|
| - return GPT_MODIFIED_ENTRIES2;
|
| - }
|
| - } else if (valid_entries == MASK_PRIMARY) {
|
| - Memcpy(gpt->secondary_entries, gpt->primary_entries, TOTAL_ENTRIES_SIZE);
|
| - return GPT_MODIFIED_ENTRIES2;
|
| - } else if (valid_entries == MASK_SECONDARY) {
|
| - Memcpy(gpt->primary_entries, gpt->secondary_entries, TOTAL_ENTRIES_SIZE);
|
| - return GPT_MODIFIED_ENTRIES1;
|
| - }
|
| -
|
| - return 0;
|
| -}
|
| -
|
| -/* Two headers are NOT bitwise identical. For example, my_lba pointers to header
|
| - * itself so that my_lba in primary and secondary is definitely different.
|
| - * Only the following fields should be identical.
|
| - *
|
| - * first_usable_lba
|
| - * last_usable_lba
|
| - * number_of_entries
|
| - * size_of_entry
|
| - * disk_uuid
|
| - *
|
| - * If any of above field are not matched, overwrite secondary with primary since
|
| - * we always trust primary.
|
| - * If any one of header is invalid, copy from another. */
|
| -int IsSynonymous(const GptHeader* a, const GptHeader* b) {
|
| - if ((a->first_usable_lba == b->first_usable_lba) &&
|
| - (a->last_usable_lba == b->last_usable_lba) &&
|
| - (a->number_of_entries == b->number_of_entries) &&
|
| - (a->size_of_entry == b->size_of_entry) &&
|
| - (!Memcmp(&a->disk_uuid, &b->disk_uuid, sizeof(Guid))))
|
| - return 1;
|
| - return 0;
|
| -}
|
| -
|
| -/* The above five fields are shared between primary and secondary headers.
|
| - * We can recover one header from another through copying those fields. */
|
| -void CopySynonymousParts(GptHeader* target, const GptHeader* source) {
|
| - target->first_usable_lba = source->first_usable_lba;
|
| - target->last_usable_lba = source->last_usable_lba;
|
| - target->number_of_entries = source->number_of_entries;
|
| - target->size_of_entry = source->size_of_entry;
|
| - Memcpy(&target->disk_uuid, &source->disk_uuid, sizeof(Guid));
|
| -}
|
| -
|
| -/* This function repairs primary and secondary headers if possible.
|
| - * If both headers are valid (CRC32 is correct) but
|
| - * a) indicate inconsistent usable LBA ranges,
|
| - * b) inconsistent partition entry size and number,
|
| - * c) inconsistent disk_uuid,
|
| - * we will use the primary header to overwrite secondary header.
|
| - * If primary is invalid (CRC32 is wrong), then we repair it from secondary.
|
| - * If secondary is invalid (CRC32 is wrong), then we repair it from primary.
|
| - * This function returns the bitmasks for modified header.
|
| - * Note that CRC value is NOT re-computed in this function. UpdateCrc() will
|
| - * do it later.
|
| - */
|
| -uint8_t RepairHeader(GptData *gpt, const uint32_t valid_headers) {
|
| - GptHeader *primary_header, *secondary_header;
|
|
|
| - primary_header = (GptHeader*)gpt->primary_header;
|
| - secondary_header = (GptHeader*)gpt->secondary_header;
|
| + /* Save what we found. Note that if we didn't find a new kernel,
|
| + * new_prio will still be -1, so future calls to this function will
|
| + * also fail. */
|
| + gpt->current_kernel = new_kernel;
|
| + gpt->current_priority = new_prio;
|
|
|
| - if (valid_headers == MASK_BOTH) {
|
| - if (!IsSynonymous(primary_header, secondary_header)) {
|
| - CopySynonymousParts(secondary_header, primary_header);
|
| - return GPT_MODIFIED_HEADER2;
|
| - }
|
| - } else if (valid_headers == MASK_PRIMARY) {
|
| - Memcpy(secondary_header, primary_header, primary_header->size);
|
| - secondary_header->my_lba = gpt->drive_sectors - 1; /* the last sector */
|
| - secondary_header->entries_lba = secondary_header->my_lba -
|
| - GPT_ENTRIES_SECTORS;
|
| - return GPT_MODIFIED_HEADER2;
|
| - } else if (valid_headers == MASK_SECONDARY) {
|
| - Memcpy(primary_header, secondary_header, secondary_header->size);
|
| - primary_header->my_lba = GPT_PMBR_SECTOR; /* the second sector on drive */
|
| - primary_header->entries_lba = primary_header->my_lba + GPT_HEADER_SECTOR;
|
| - return GPT_MODIFIED_HEADER1;
|
| - }
|
| -
|
| - return 0;
|
| -}
|
| -
|
| -/* Update CRC value if necessary. */
|
| -void UpdateCrc(GptData *gpt) {
|
| - GptHeader *primary_header, *secondary_header;
|
| -
|
| - primary_header = (GptHeader*)gpt->primary_header;
|
| - secondary_header = (GptHeader*)gpt->secondary_header;
|
| -
|
| - if (gpt->modified & GPT_MODIFIED_ENTRIES1) {
|
| - primary_header->entries_crc32 =
|
| - Crc32(gpt->primary_entries, TOTAL_ENTRIES_SIZE);
|
| - }
|
| - if (gpt->modified & GPT_MODIFIED_ENTRIES2) {
|
| - secondary_header->entries_crc32 =
|
| - Crc32(gpt->secondary_entries, TOTAL_ENTRIES_SIZE);
|
| - }
|
| - if (gpt->modified & GPT_MODIFIED_HEADER1) {
|
| - primary_header->header_crc32 = 0;
|
| - primary_header->header_crc32 = Crc32(
|
| - (const uint8_t *)primary_header, primary_header->size);
|
| - }
|
| - if (gpt->modified & GPT_MODIFIED_HEADER2) {
|
| - secondary_header->header_crc32 = 0;
|
| - secondary_header->header_crc32 = Crc32(
|
| - (const uint8_t *)secondary_header, secondary_header->size);
|
| - }
|
| -}
|
| -
|
| -/* This function only checks GptData.
|
| - * valid_headers and valid_entries are used to store the checking results.
|
| - *
|
| - * Returns:
|
| - * GPT_ERROR_INVALID_HEADERS -- both headers are invalid.
|
| - * GPT_ERROR_INVALID_ENTRIES -- both entries are invalid.
|
| - * GPT_SUCCESS -- everything looks fine.
|
| - */
|
| -int GptSanityCheck(GptData *gpt) {
|
| - int retval;
|
| -
|
| - assert(gpt);
|
| -
|
| - retval = CheckParameters(gpt);
|
| - if (retval != GPT_SUCCESS)
|
| - return retval;
|
| -
|
| - /* Initialize values */
|
| - gpt->valid_headers = MASK_BOTH;
|
| - gpt->valid_entries = MASK_BOTH;
|
| -
|
| - /* Start checking if header parameters are valid. */
|
| - CheckHeaderSignature(gpt);
|
| - CheckRevision(gpt);
|
| - CheckSize(gpt);
|
| - CheckReservedFields(gpt);
|
| - CheckMyLba(gpt);
|
| - CheckSizeOfPartitionEntry(gpt);
|
| - CheckNumberOfEntries(gpt);
|
| - CheckEntriesLba(gpt);
|
| - CheckValidUsableLbas(gpt);
|
| - CheckHeaderCrc(gpt);
|
| -
|
| - /* Returns error if we don't have any valid header to use. */
|
| - if (!gpt->valid_headers)
|
| - return GPT_ERROR_INVALID_HEADERS;
|
| -
|
| - /* Checks if entries are valid. */
|
| - CheckEntriesCrc(gpt);
|
| - CheckValidEntries(gpt);
|
| - CheckOverlappedPartition(gpt);
|
| -
|
| - /* Returns error if we don't have any valid entries to use. */
|
| - if (!gpt->valid_entries)
|
| - return GPT_ERROR_INVALID_ENTRIES;
|
| -
|
| - return GPT_SUCCESS;
|
| -}
|
| -
|
| -void GptRepair(GptData *gpt) {
|
| - gpt->modified |= RepairHeader(gpt, gpt->valid_headers);
|
| - gpt->modified |= RepairEntries(gpt, gpt->valid_entries);
|
| - UpdateCrc(gpt);
|
| -}
|
| -
|
| -/* Does every sanity check, and returns if any header/entries needs to be
|
| - * written back. */
|
| -int GptInit(GptData *gpt) {
|
| - int retval;
|
| -
|
| - retval = GptSanityCheck(gpt);
|
| - if (GPT_SUCCESS != retval) return retval;
|
| -
|
| - gpt->modified = 0;
|
| - GptRepair(gpt);
|
| -
|
| - gpt->current_kernel = CGPT_KERNEL_ENTRY_NOT_FOUND;
|
| + if (CGPT_KERNEL_ENTRY_NOT_FOUND == new_kernel)
|
| + return GPT_ERROR_NO_VALID_KERNEL;
|
|
|
| + e = entries + new_kernel;
|
| + *start_sector = e->starting_lba;
|
| + *size = e->ending_lba - e->starting_lba + 1;
|
| return GPT_SUCCESS;
|
| }
|
|
|
| -/* Helper function to get a pointer to the partition entry.
|
| - * 'secondary' is either PRIMARY or SECONDARY.
|
| - * 'entry_index' is the partition index: [0, number_of_entries).
|
| - */
|
| -GptEntry *GetEntry(GptData *gpt, int secondary, int entry_index) {
|
| - uint8_t *entries;
|
| -
|
| - if (secondary == PRIMARY) {
|
| - entries = gpt->primary_entries;
|
| - } else {
|
| - entries = gpt->secondary_entries;
|
| - }
|
|
|
| - return (GptEntry*)(&entries[GetNumberOfEntries(gpt) * entry_index]);
|
| -}
|
| -
|
| -/* The following functions are helpers to access attributes bit more easily.
|
| - * 'secondary' is either PRIMARY or SECONDARY.
|
| - * 'entry_index' is the partition index: [0, number_of_entries).
|
| - *
|
| - * Get*() return the exact value (shifted and masked).
|
| - */
|
| -void SetPriority(GptData *gpt, int secondary, int entry_index, int priority) {
|
| - GptEntry *entry;
|
| - entry = GetEntry(gpt, secondary, entry_index);
|
| -
|
| - assert(priority >= 0 && priority <= CGPT_ATTRIBUTE_MAX_PRIORITY);
|
| - entry->attributes &= ~CGPT_ATTRIBUTE_PRIORITY_MASK;
|
| - entry->attributes |= (uint64_t)priority << CGPT_ATTRIBUTE_PRIORITY_OFFSET;
|
| -}
|
| -
|
| -int GetPriority(GptData *gpt, int secondary, int entry_index) {
|
| - GptEntry *entry;
|
| - entry = GetEntry(gpt, secondary, entry_index);
|
| - return (entry->attributes & CGPT_ATTRIBUTE_PRIORITY_MASK) >>
|
| - CGPT_ATTRIBUTE_PRIORITY_OFFSET;
|
| -}
|
| -
|
| -void SetBad(GptData *gpt, int secondary, int entry_index, int bad) {
|
| - GptEntry *entry;
|
| - entry = GetEntry(gpt, secondary, entry_index);
|
| -
|
| - assert(bad >= 0 && bad <= CGPT_ATTRIBUTE_MAX_BAD);
|
| - entry->attributes &= ~CGPT_ATTRIBUTE_BAD_MASK;
|
| - entry->attributes |= (uint64_t)bad << CGPT_ATTRIBUTE_BAD_OFFSET;
|
| -}
|
| -
|
| -int GetBad(GptData *gpt, int secondary, int entry_index) {
|
| - GptEntry *entry;
|
| - entry = GetEntry(gpt, secondary, entry_index);
|
| - return (entry->attributes & CGPT_ATTRIBUTE_BAD_MASK) >>
|
| - CGPT_ATTRIBUTE_BAD_OFFSET;
|
| -}
|
| -
|
| -void SetTries(GptData *gpt, int secondary, int entry_index, int tries) {
|
| - GptEntry *entry;
|
| - entry = GetEntry(gpt, secondary, entry_index);
|
| -
|
| - assert(tries >= 0 && tries <= CGPT_ATTRIBUTE_MAX_TRIES);
|
| - entry->attributes &= ~CGPT_ATTRIBUTE_TRIES_MASK;
|
| - entry->attributes |= (uint64_t)tries << CGPT_ATTRIBUTE_TRIES_OFFSET;
|
| -}
|
| -
|
| -int GetTries(GptData *gpt, int secondary, int entry_index) {
|
| - GptEntry *entry;
|
| - entry = GetEntry(gpt, secondary, entry_index);
|
| - return (entry->attributes & CGPT_ATTRIBUTE_TRIES_MASK) >>
|
| - CGPT_ATTRIBUTE_TRIES_OFFSET;
|
| -}
|
| -
|
| -void SetSuccessful(GptData *gpt, int secondary, int entry_index, int success) {
|
| - GptEntry *entry;
|
| - entry = GetEntry(gpt, secondary, entry_index);
|
| -
|
| - assert(success >= 0 && success <= CGPT_ATTRIBUTE_MAX_SUCCESSFUL);
|
| - entry->attributes &= ~CGPT_ATTRIBUTE_SUCCESSFUL_MASK;
|
| - entry->attributes |= (uint64_t)success << CGPT_ATTRIBUTE_SUCCESSFUL_OFFSET;
|
| -}
|
| -
|
| -int GetSuccessful(GptData *gpt, int secondary, int entry_index) {
|
| - GptEntry *entry;
|
| - entry = GetEntry(gpt, secondary, entry_index);
|
| - return (entry->attributes & CGPT_ATTRIBUTE_SUCCESSFUL_MASK) >>
|
| - CGPT_ATTRIBUTE_SUCCESSFUL_OFFSET;
|
| -}
|
| -
|
| -uint32_t GetNumberOfEntries(const GptData *gpt) {
|
| - GptHeader *header = 0;
|
| - if (gpt->valid_headers & MASK_PRIMARY)
|
| - header = (GptHeader*)gpt->primary_header;
|
| - else if (gpt->valid_headers & MASK_SECONDARY)
|
| - header = (GptHeader*)gpt->secondary_header;
|
| - else
|
| - assert(0);
|
| - return header->number_of_entries;
|
| -}
|
| -
|
| -
|
| -/* Compare two priority values. Actually it is a circular priority, which is:
|
| - * 3 > 2 > 1 > 0, but 0 > 3. (-1 means very low, and anyone is higher than -1)
|
| - *
|
| - * Return 1 if 'a' has higher priority than 'b'.
|
| - */
|
| -int IsHigherPriority(int a, int b) {
|
| - if ((a == 0) && (b == CGPT_ATTRIBUTE_MAX_PRIORITY))
|
| - return 1;
|
| - else if ((a == CGPT_ATTRIBUTE_MAX_PRIORITY) && (b == 0))
|
| - return 0;
|
| - else
|
| - return (a > b) ? 1 : 0;
|
| -}
|
| -
|
| -/* This function walks through the whole partition table (see note below),
|
| - * and pick up the active and valid (not marked as bad) kernel entry with
|
| - * *highest* priority (except gpt->current_kernel itself).
|
| - *
|
| - * Returns start_sector and its size if a candidate kernel is found.
|
| - *
|
| - * Note: in the first walk (gpt->current_kernel==CGPT_KERNEL_ENTRY_NOT_FOUND),
|
| - * the scan range is whole table. But in later scans, we only scan
|
| - * (header->number_of_entries - 1) entries because we are looking for
|
| - * next kernel with lower priority (consider the case that highest
|
| - * priority kernel is still active and valid).
|
| - */
|
| -int GptNextKernelEntry(GptData *gpt, uint64_t *start_sector, uint64_t *size) {
|
| - GptHeader *header;
|
| - GptEntry *entry;
|
| - int scan, current_priority;
|
| - int begin, end; /* [begin, end], which end is included. */
|
| - Guid chromeos_kernel = GPT_ENT_TYPE_CHROMEOS_KERNEL;
|
| -
|
| - header = (GptHeader*)gpt->primary_header;
|
| - current_priority = -1; /* pretty low priority */
|
| - if (gpt->current_kernel == CGPT_KERNEL_ENTRY_NOT_FOUND) {
|
| - begin = 0;
|
| - end = header->number_of_entries - 1;
|
| - } else {
|
| - begin = (gpt->current_kernel + 1) % header->number_of_entries;
|
| - end = (gpt->current_kernel - 1 + header->number_of_entries) %
|
| - header->number_of_entries;
|
| - }
|
| -
|
| - scan = begin;
|
| - do {
|
| - entry = GetEntry(gpt, PRIMARY, scan);
|
| - if (!Memcmp(&entry->type, &chromeos_kernel, sizeof(Guid)) &&
|
| - !GetBad(gpt, PRIMARY, scan) &&
|
| - ((gpt->current_kernel == CGPT_KERNEL_ENTRY_NOT_FOUND) ||
|
| - (IsHigherPriority(GetPriority(gpt, PRIMARY, scan),
|
| - current_priority)))) {
|
| - gpt->current_kernel = scan;
|
| - current_priority = GetPriority(gpt, PRIMARY, gpt->current_kernel);
|
| - }
|
| -
|
| - if (scan == end) break;
|
| - scan = (scan + 1) % header->number_of_entries;
|
| - } while (1);
|
| +int GptUpdateKernelEntry(GptData* gpt, uint32_t update_type) {
|
| + GptHeader* header = (GptHeader*)gpt->primary_header;
|
| + GptEntry* entries = (GptEntry*)gpt->primary_entries;
|
| + GptEntry* e = entries + gpt->current_kernel;
|
| + uint64_t previous_attr = e->attributes;
|
|
|
| + /* TODO: need a better return code for these errors? */
|
| if (gpt->current_kernel == CGPT_KERNEL_ENTRY_NOT_FOUND)
|
| - return GPT_ERROR_NO_VALID_KERNEL;
|
| -
|
| - entry = GetEntry(gpt, PRIMARY, gpt->current_kernel);
|
| - assert(entry->starting_lba <= entry->ending_lba);
|
| + return GPT_ERROR_INVALID_UPDATE_TYPE;
|
| + if (!IsKernelEntry(e))
|
| + return GPT_ERROR_INVALID_UPDATE_TYPE;
|
|
|
| - if (start_sector) *start_sector = entry->starting_lba;
|
| - if (size) *size = entry->ending_lba - entry->starting_lba + 1;
|
| -
|
| - return GPT_SUCCESS;
|
| -}
|
| -
|
| -/* Given a update_type, this function updates the corresponding bits in GptData.
|
| - *
|
| - * Returns GPT_SUCCESS if no error. gpt->modified is set if any header and
|
| - * entries needs to be updated to hard drive.
|
| - * GPT_ERROR_INVALID_UPDATE_TYPE if given an invalid update_type.
|
| - */
|
| -int GptUpdateKernelEntry(GptData *gpt, uint32_t update_type) {
|
| - Guid chromeos_type = GPT_ENT_TYPE_CHROMEOS_KERNEL;
|
| - int primary_is_modified = 0;
|
| -
|
| - assert(gpt->current_kernel != CGPT_KERNEL_ENTRY_NOT_FOUND);
|
| - assert(!Memcmp(&(GetEntry(gpt, PRIMARY, gpt->current_kernel)->type),
|
| - &chromeos_type, sizeof(Guid)));
|
| -
|
| - /* Modify primary entries first, then copy to secondary later. */
|
| switch (update_type) {
|
| case GPT_UPDATE_ENTRY_TRY: {
|
| - /* Increase tries value until CGPT_ATTRIBUTE_MAX_TRIES. */
|
| - int tries;
|
| - tries = GetTries(gpt, PRIMARY, gpt->current_kernel);
|
| - if (tries < CGPT_ATTRIBUTE_MAX_TRIES) {
|
| - ++tries;
|
| - SetTries(gpt, PRIMARY, gpt->current_kernel, tries);
|
| - primary_is_modified = 1;
|
| - }
|
| - break;
|
| + /* Used up a try */
|
| + int tries;
|
| + if (GetEntrySuccessful(e))
|
| + return GPT_SUCCESS; /* Successfully booted this partition, so
|
| + * tries field is ignored. */
|
| + tries = GetEntryTries(e);
|
| + if (tries > 1) {
|
| + /* Still have tries left */
|
| + SetEntryTries(e, tries - 1);
|
| + break;
|
| + }
|
| + /* Out of tries, so drop through and mark partition bad. */
|
| }
|
| case GPT_UPDATE_ENTRY_BAD: {
|
| - GetEntry(gpt, PRIMARY, gpt->current_kernel)->attributes |=
|
| - CGPT_ATTRIBUTE_BAD_MASK;
|
| - primary_is_modified = 1;
|
| + /* Giving up on this partition entirely. */
|
| + e->attributes &= ~(CGPT_ATTRIBUTE_SUCCESSFUL_MASK |
|
| + CGPT_ATTRIBUTE_TRIES_MASK |
|
| + CGPT_ATTRIBUTE_PRIORITY_MASK);
|
| break;
|
| }
|
| - default: {
|
| + default:
|
| return GPT_ERROR_INVALID_UPDATE_TYPE;
|
| - }
|
| }
|
|
|
| - if (primary_is_modified) {
|
| - /* Claim only primary is valid so that secondary is overwritten. */
|
| - RepairEntries(gpt, MASK_PRIMARY);
|
| - /* Actually two entries are dirty now.
|
| - * Also two headers are dirty because entries_crc32 has been updated. */
|
| - gpt->modified |= (GPT_MODIFIED_HEADER1 | GPT_MODIFIED_ENTRIES1 |
|
| - GPT_MODIFIED_HEADER2 | GPT_MODIFIED_ENTRIES2);
|
| - UpdateCrc(gpt);
|
| - }
|
| + /* If no change to attributes, we're done */
|
| + if (e->attributes == previous_attr)
|
| + return GPT_SUCCESS;
|
| +
|
| + /* Update the CRCs */
|
| + header->entries_crc32 = Crc32((const uint8_t *)entries,
|
| + header->size_of_entry *
|
| + header->number_of_entries);
|
| + header->header_crc32 = HeaderCrc(header);
|
| + gpt->modified |= GPT_MODIFIED_HEADER1 | GPT_MODIFIED_ENTRIES1;
|
| +
|
| + /* Use the repair function to update the other copy of the GPT.
|
| + * This is a tad inefficient, but is much faster than the disk I/O
|
| + * to update the GPT on disk so it doesn't matter. */
|
| + gpt->valid_headers = MASK_PRIMARY;
|
| + gpt->valid_entries = MASK_PRIMARY;
|
| + GptRepair(gpt);
|
|
|
| return GPT_SUCCESS;
|
| }
|
|
|