Index: src/platform/vboot_reference/vboot_firmware/lib/cgptlib/cgptlib_internal.c |
diff --git a/src/platform/vboot_reference/vboot_firmware/lib/cgptlib/cgptlib_internal.c b/src/platform/vboot_reference/vboot_firmware/lib/cgptlib/cgptlib_internal.c |
new file mode 100644 |
index 0000000000000000000000000000000000000000..3d2dc0b30591ccef33068ce33e59091e371ad171 |
--- /dev/null |
+++ b/src/platform/vboot_reference/vboot_firmware/lib/cgptlib/cgptlib_internal.c |
@@ -0,0 +1,348 @@ |
+/* Copyright (c) 2010 The Chromium OS Authors. All rights reserved. |
+ * Use of this source code is governed by a BSD-style license that can be |
+ * found in the LICENSE file. |
+ */ |
+ |
+#include "cgptlib.h" |
+#include "cgptlib_internal.h" |
+#include "crc32.h" |
+#include "gpt.h" |
+#include "utility.h" |
+ |
+ |
+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; |
+ |
+ /* 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 < (1 + 2 * (1 + GPT_ENTRIES_SECTORS))) |
+ return GPT_ERROR_INVALID_SECTOR_NUMBER; |
+ |
+ return GPT_SUCCESS; |
+} |
+ |
+ |
+uint32_t HeaderCrc(GptHeader* h) { |
+ uint32_t crc32, original_crc32; |
+ |
+ /* Original CRC is calculated with the CRC field 0. */ |
+ original_crc32 = h->header_crc32; |
+ h->header_crc32 = 0; |
+ crc32 = Crc32((const uint8_t *)h, h->size); |
+ h->header_crc32 = original_crc32; |
+ |
+ return crc32; |
+} |
+ |
+ |
+int CheckHeader(GptHeader *h, int is_secondary, uint64_t drive_sectors) { |
+ if (!h) |
+ return 1; |
+ |
+ /* Make sure we're looking at a header of reasonable size before |
+ * attempting to calculate CRC. */ |
+ if (Memcmp(h->signature, GPT_HEADER_SIGNATURE, GPT_HEADER_SIGNATURE_SIZE)) |
+ return 1; |
+ if (h->revision != GPT_HEADER_REVISION) |
+ return 1; |
+ if (h->size < MIN_SIZE_OF_HEADER || h->size > MAX_SIZE_OF_HEADER) |
+ return 1; |
+ |
+ /* Check CRC before looking at remaining fields */ |
+ if (HeaderCrc(h) != h->header_crc32) |
+ return 1; |
+ |
+ /* Reserved fields must be zero. */ |
+ if (h->reserved) |
+ return 1; |
+ |
+ /* TODO: Padding must be set to zero. */ |
+ |
+ /* If entry size is different than our struct, we won't be able to |
+ * parse it. Technically, any size 2^N where N>=7 is valid. */ |
+ if (h->size_of_entry != sizeof(GptEntry)) |
+ return 1; |
+ if ((h->number_of_entries < MIN_NUMBER_OF_ENTRIES) || |
+ (h->number_of_entries > MAX_NUMBER_OF_ENTRIES) || |
+ (h->number_of_entries * h->size_of_entry != TOTAL_ENTRIES_SIZE)) |
+ return 1; |
+ |
+ /* Check locations for the header and its entries. The primary |
+ * immediately follows the PMBR, and is followed by its entries. |
+ * The secondary is at the end of the drive, preceded by its |
+ * entries. */ |
+ if (is_secondary) { |
+ if ((h->my_lba != drive_sectors - 1) || (h->alternate_lba != 1)) |
+ return 1; |
+ if (h->entries_lba != h->my_lba - GPT_ENTRIES_SECTORS) |
+ return 1; |
+ } else { |
+ if ((h->my_lba != 1) || (h->alternate_lba != drive_sectors - 1)) |
+ return 1; |
+ if (h->entries_lba != h->my_lba + 1) |
+ return 1; |
+ } |
+ |
+ /* 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. */ |
+ if (h->first_usable_lba < 2 + GPT_ENTRIES_SECTORS) |
+ return 1; |
+ if (h->last_usable_lba >= drive_sectors - 1 - GPT_ENTRIES_SECTORS) |
+ return 1; |
+ if (h->first_usable_lba > h->last_usable_lba) |
+ return 1; |
+ |
+ /* Success */ |
+ return 0; |
+} |
+ |
+ |
+/* Return 1 if the entry is unused, 0 if it is used. */ |
+int IsUnusedEntry(const GptEntry* e) { |
+ static Guid zero = {{{0, 0, 0, 0, 0, {0, 0, 0, 0, 0, 0}}}}; |
+ return (Memcmp(&zero, (const uint8_t*)(&e->type), sizeof(zero)) ? 0 : 1); |
+} |
+ |
+/* Returns 1 if the entry is a Chrome OS kernel partition, else 0. */ |
+int IsKernelEntry(const GptEntry* e) { |
+ static Guid chromeos_kernel = GPT_ENT_TYPE_CHROMEOS_KERNEL; |
+ return (Memcmp(&e->type, &chromeos_kernel, sizeof(Guid)) ? 0 : 1); |
+} |
+ |
+ |
+int CheckEntries(GptEntry* entries, GptHeader* h, uint64_t drive_sectors) { |
+ |
+ GptEntry* entry; |
+ uint32_t crc32; |
+ int i; |
+ |
+ /* Check CRC before examining entries. */ |
+ crc32 = Crc32((const uint8_t *)entries, |
+ h->size_of_entry * h->number_of_entries); |
+ if (crc32 != h->entries_crc32) |
+ return 1; |
+ |
+ /* Check all entries. */ |
+ for (i = 0, entry = entries; i < h->number_of_entries; i++, entry++) { |
+ GptEntry* e2; |
+ int i2; |
+ |
+ if (IsUnusedEntry(entry)) |
+ continue; |
+ |
+ /* Entry must be in valid region. */ |
+ if ((entry->starting_lba < h->first_usable_lba) || |
+ (entry->ending_lba > h->last_usable_lba) || |
+ (entry->ending_lba < entry->starting_lba)) |
+ return 1; |
+ |
+ /* Entry must not overlap other entries. */ |
+ for (i2 = 0, e2 = entries; i2 < h->number_of_entries; i2++, e2++) { |
+ if (i2 == i || IsUnusedEntry(e2)) |
+ continue; |
+ |
+ if ((entry->starting_lba >= e2->starting_lba) && |
+ (entry->starting_lba <= e2->ending_lba)) |
+ return 1; |
+ if ((entry->ending_lba >= e2->starting_lba) && |
+ (entry->ending_lba <= e2->ending_lba)) |
+ return 1; |
+ } |
+ } |
+ |
+ /* Success */ |
+ return 0; |
+} |
+ |
+ |
+/* Returns 0 if the GptHeaders are the same for all fields which don't |
+ * differ between the primary and secondary headers - that is, all |
+ * fields other than: |
+ * |
+ * my_lba |
+ * alternate_lba |
+ * entries_lba */ |
+int HeaderFieldsSame(GptHeader *h1, GptHeader *h2) { |
+ if (Memcmp(h1->signature, h2->signature, sizeof(h1->signature))) |
+ return 1; |
+ if (h1->revision != h2->revision) |
+ return 1; |
+ if (h1->size != h2->size) |
+ return 1; |
+ if (h1->reserved != h2->reserved) |
+ return 1; |
+ if (h1->first_usable_lba != h2->first_usable_lba) |
+ return 1; |
+ if (h1->last_usable_lba != h2->last_usable_lba) |
+ return 1; |
+ if (Memcmp(&h1->disk_uuid, &h2->disk_uuid, sizeof(Guid))) |
+ return 1; |
+ if (h1->number_of_entries != h2->number_of_entries) |
+ return 1; |
+ if (h1->size_of_entry != h2->size_of_entry) |
+ return 1; |
+ if (h1->entries_crc32 != h2->entries_crc32) |
+ return 1; |
+ |
+ return 0; |
+} |
+ |
+ |
+int GptSanityCheck(GptData *gpt) { |
+ int retval; |
+ GptHeader* header1 = (GptHeader*)(gpt->primary_header); |
+ GptHeader* header2 = (GptHeader*)(gpt->secondary_header); |
+ GptEntry* entries1 = (GptEntry*)(gpt->primary_entries); |
+ GptEntry* entries2 = (GptEntry*)(gpt->secondary_entries); |
+ GptHeader* goodhdr = NULL; |
+ |
+ gpt->valid_headers = 0; |
+ gpt->valid_entries = 0; |
+ |
+ retval = CheckParameters(gpt); |
+ if (retval != GPT_SUCCESS) |
+ return retval; |
+ |
+ /* Check both headers; we need at least one valid header. */ |
+ if (0 == CheckHeader(header1, 0, gpt->drive_sectors)) { |
+ gpt->valid_headers |= MASK_PRIMARY; |
+ goodhdr = header1; |
+ } |
+ if (0 == CheckHeader(header2, 1, gpt->drive_sectors)) { |
+ gpt->valid_headers |= MASK_SECONDARY; |
+ if (!goodhdr) |
+ goodhdr = header2; |
+ } |
+ |
+ if (!gpt->valid_headers) |
+ return GPT_ERROR_INVALID_HEADERS; |
+ |
+ /* Checks if entries are valid. |
+ * |
+ * Note that we use the same header in both checks. This way we'll |
+ * catch the case where (header1,entries1) and (header2,entries2) |
+ * are both valid, but (entries1 != entries2). */ |
+ if (0 == CheckEntries(entries1, goodhdr, gpt->drive_sectors)) |
+ gpt->valid_entries |= MASK_PRIMARY; |
+ if (0 == CheckEntries(entries2, goodhdr, gpt->drive_sectors)) |
+ gpt->valid_entries |= MASK_SECONDARY; |
+ |
+ /* If both headers are good but neither entries were good, check the |
+ * entries with the secondary header. */ |
+ if (MASK_BOTH == gpt->valid_headers && !gpt->valid_entries) { |
+ if (0 == CheckEntries(entries1, header2, gpt->drive_sectors)) |
+ gpt->valid_entries |= MASK_PRIMARY; |
+ if (0 == CheckEntries(entries2, header2, gpt->drive_sectors)) |
+ gpt->valid_entries |= MASK_SECONDARY; |
+ if (gpt->valid_entries) { |
+ /* Sure enough, header2 had a good CRC for one of the entries. Mark |
+ * header1 invalid, so we'll update its entries CRC. */ |
+ gpt->valid_headers &= ~MASK_PRIMARY; |
+ goodhdr = header2; |
+ } |
+ } |
+ |
+ if (!gpt->valid_entries) |
+ return GPT_ERROR_INVALID_ENTRIES; |
+ |
+ /* Now that we've determined which header contains a good CRC for |
+ * the entries, make sure the headers are otherwise identical. */ |
+ if (MASK_BOTH == gpt->valid_headers && |
+ 0 != HeaderFieldsSame(header1, header2)) |
+ gpt->valid_headers &= ~MASK_SECONDARY; |
+ |
+ return GPT_SUCCESS; |
+} |
+ |
+ |
+void GptRepair(GptData *gpt) { |
+ GptHeader* header1 = (GptHeader*)(gpt->primary_header); |
+ GptHeader* header2 = (GptHeader*)(gpt->secondary_header); |
+ GptEntry* entries1 = (GptEntry*)(gpt->primary_entries); |
+ GptEntry* entries2 = (GptEntry*)(gpt->secondary_entries); |
+ int entries_size; |
+ |
+ /* Need at least one good header and one good set of entries. */ |
+ if (MASK_NONE == gpt->valid_headers || MASK_NONE == gpt->valid_entries) |
+ return; |
+ |
+ /* Repair headers if necessary */ |
+ if (MASK_PRIMARY == gpt->valid_headers) { |
+ /* Primary is good, secondary is bad */ |
+ Memcpy(header2, header1, sizeof(GptHeader)); |
+ header2->my_lba = gpt->drive_sectors - 1; |
+ header2->alternate_lba = 1; |
+ header2->entries_lba = header2->my_lba - GPT_ENTRIES_SECTORS; |
+ header2->header_crc32 = HeaderCrc(header2); |
+ gpt->modified |= GPT_MODIFIED_HEADER2; |
+ } |
+ else if (MASK_SECONDARY == gpt->valid_headers) { |
+ /* Secondary is good, primary is bad */ |
+ Memcpy(header1, header2, sizeof(GptHeader)); |
+ header1->my_lba = 1; |
+ header1->alternate_lba = gpt->drive_sectors - 1; |
+ header1->entries_lba = header1->my_lba + 1; |
+ header1->header_crc32 = HeaderCrc(header1); |
+ gpt->modified |= GPT_MODIFIED_HEADER1; |
+ } |
+ gpt->valid_headers = MASK_BOTH; |
+ |
+ /* Repair entries if necessary */ |
+ entries_size = header1->size_of_entry * header1->number_of_entries; |
+ if (MASK_PRIMARY == gpt->valid_entries) { |
+ /* Primary is good, secondary is bad */ |
+ Memcpy(entries2, entries1, entries_size); |
+ gpt->modified |= GPT_MODIFIED_ENTRIES2; |
+ } |
+ else if (MASK_SECONDARY == gpt->valid_entries) { |
+ /* Secondary is good, primary is bad */ |
+ Memcpy(entries1, entries2, entries_size); |
+ gpt->modified |= GPT_MODIFIED_ENTRIES1; |
+ } |
+ gpt->valid_entries = MASK_BOTH; |
+} |
+ |
+ |
+int GetEntrySuccessful(const GptEntry* e) { |
+ return (e->attributes & CGPT_ATTRIBUTE_SUCCESSFUL_MASK) >> |
+ CGPT_ATTRIBUTE_SUCCESSFUL_OFFSET; |
+} |
+ |
+ |
+int GetEntryPriority(const GptEntry* e) { |
+ return (e->attributes & CGPT_ATTRIBUTE_PRIORITY_MASK) >> |
+ CGPT_ATTRIBUTE_PRIORITY_OFFSET; |
+} |
+ |
+ |
+int GetEntryTries(const GptEntry* e) { |
+ return (e->attributes & CGPT_ATTRIBUTE_TRIES_MASK) >> |
+ CGPT_ATTRIBUTE_TRIES_OFFSET; |
+} |
+ |
+ |
+void SetEntrySuccessful(GptEntry* e, int successful) { |
+ if (successful) |
+ e->attributes |= CGPT_ATTRIBUTE_SUCCESSFUL_MASK; |
+ else |
+ e->attributes &= ~CGPT_ATTRIBUTE_SUCCESSFUL_MASK; |
+} |
+ |
+ |
+void SetEntryPriority(GptEntry* e, int priority) { |
+ e->attributes &= ~CGPT_ATTRIBUTE_PRIORITY_MASK; |
+ e->attributes |= ((uint64_t)priority << CGPT_ATTRIBUTE_PRIORITY_OFFSET) & |
+ CGPT_ATTRIBUTE_PRIORITY_MASK; |
+} |
+ |
+ |
+void SetEntryTries(GptEntry* e, int tries) { |
+ e->attributes &= ~CGPT_ATTRIBUTE_TRIES_MASK; |
+ e->attributes |= ((uint64_t)tries << CGPT_ATTRIBUTE_TRIES_OFFSET) & |
+ CGPT_ATTRIBUTE_TRIES_MASK; |
+} |