diff --git a/tests/Makefile b/tests/Makefile index 7381d42717..ebba95f007 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -42,7 +42,7 @@ big_firmware_tests: big_firmware_tests.c rollback_index_mock.c test_common.c big_kernel_tests: big_kernel_tests.c rollback_index_mock.c test_common.c $(CC) $(CFLAGS) $(INCLUDES) $^ -o $@ $(LIBS) -cgptlib_test: cgptlib_test.c quick_sort_test.c crc32_test.c +cgptlib_test: cgptlib_test.c crc32_test.c $(CC) $(CFLAGS) -ansi $(INCLUDES) $^ -o $@ $(LIBS) firmware_image_tests: firmware_image_tests.c rollback_index_mock.c test_common.c diff --git a/tests/cgptlib_test.c b/tests/cgptlib_test.c index 72fb5e6c21..11b44fb2d8 100644 --- a/tests/cgptlib_test.c +++ b/tests/cgptlib_test.c @@ -3,14 +3,14 @@ * found in the LICENSE file. */ -#include "cgptlib_test.h" #include + #include "cgptlib.h" #include "cgptlib_internal.h" +#include "cgptlib_test.h" #include "crc32.h" #include "crc32_test.h" #include "gpt.h" -#include "quick_sort_test.h" #include "utility.h" /* Testing partition layout (sector_bytes=512) @@ -29,19 +29,26 @@ * 467 */ #define KERNEL_A 0 -#define ROOTFS_A 1 -#define ROOTFS_B 2 -#define KERNEL_B 3 +#define KERNEL_B 1 +#define ROOTFS_A 2 +#define ROOTFS_B 3 +#define KERNEL_X 2 /* Overload ROOTFS_A, for some GetNext tests */ +#define KERNEL_Y 3 /* Overload ROOTFS_B, for some GetNext tests */ #define DEFAULT_SECTOR_SIZE 512 #define MAX_SECTOR_SIZE 4096 #define DEFAULT_DRIVE_SECTORS 467 #define PARTITION_ENTRIES_SIZE TOTAL_ENTRIES_SIZE /* 16384 */ +static const Guid guid_zero = {{{0, 0, 0, 0, 0, {0, 0, 0, 0, 0, 0}}}}; +static const Guid guid_kernel = GPT_ENT_TYPE_CHROMEOS_KERNEL; +static const Guid guid_rootfs = GPT_ENT_TYPE_CHROMEOS_ROOTFS; + + /* Given a GptData pointer, first re-calculate entries CRC32 value, * then reset header CRC32 value to 0, and calculate header CRC32 value. * Both primary and secondary are updated. */ -void RefreshCrc32(GptData *gpt) { +static void RefreshCrc32(GptData* gpt) { GptHeader *header, *header2; GptEntry *entries, *entries2; @@ -62,25 +69,29 @@ void RefreshCrc32(GptData *gpt) { header2->header_crc32 = Crc32((uint8_t*)header2, header2->size); } -void ZeroHeaders(GptData* gpt) { + +static void ZeroHeaders(GptData* gpt) { Memset(gpt->primary_header, 0, MAX_SECTOR_SIZE); Memset(gpt->secondary_header, 0, MAX_SECTOR_SIZE); } -void ZeroEntries(GptData* gpt) { + +static void ZeroEntries(GptData* gpt) { Memset(gpt->primary_entries, 0, PARTITION_ENTRIES_SIZE); Memset(gpt->secondary_entries, 0, PARTITION_ENTRIES_SIZE); } -void ZeroHeadersEntries(GptData* gpt) { + +static void ZeroHeadersEntries(GptData* gpt) { ZeroHeaders(gpt); ZeroEntries(gpt); } + /* Returns a pointer to a static GptData instance (no free is required). * All fields are zero except 4 pointers linking to header and entries. * All content of headers and entries are zero. */ -GptData* GetEmptyGptData() { +static GptData* GetEmptyGptData() { static GptData gpt; static uint8_t primary_header[MAX_SECTOR_SIZE]; static uint8_t primary_entries[PARTITION_ENTRIES_SIZE]; @@ -100,12 +111,13 @@ GptData* GetEmptyGptData() { return &gpt; } + /* Fills in most of fields and creates the layout described in the top of this * file. Before calling this function, primary/secondary header/entries must * have been pointed to the buffer, say, a gpt returned from GetEmptyGptData(). * This function returns a good (valid) copy of GPT layout described in top of * this file. */ -void BuildTestGptData(GptData *gpt) { +static void BuildTestGptData(GptData* gpt) { GptHeader *header, *header2; GptEntry *entries, *entries2; Guid chromeos_kernel = GPT_ENT_TYPE_CHROMEOS_KERNEL; @@ -116,15 +128,18 @@ void BuildTestGptData(GptData *gpt) { gpt->current_kernel = CGPT_KERNEL_ENTRY_NOT_FOUND; gpt->valid_headers = MASK_BOTH; gpt->valid_entries = MASK_BOTH; + gpt->modified = 0; /* build primary */ header = (GptHeader*)gpt->primary_header; entries = (GptEntry*)gpt->primary_entries; - Memcpy(header->signature, GPT_HEADER_SIGNATURE, sizeof(GPT_HEADER_SIGNATURE)); + Memcpy(header->signature, GPT_HEADER_SIGNATURE, + sizeof(GPT_HEADER_SIGNATURE)); header->revision = GPT_HEADER_REVISION; header->size = sizeof(GptHeader) - sizeof(header->padding); header->reserved = 0; header->my_lba = 1; + header->alternate_lba = DEFAULT_DRIVE_SECTORS - 1; header->first_usable_lba = 34; header->last_usable_lba = DEFAULT_DRIVE_SECTORS - 1 - 32 - 1; /* 433 */ header->entries_lba = 2; @@ -135,14 +150,14 @@ void BuildTestGptData(GptData *gpt) { entries[0].ending_lba = 133; Memcpy(&entries[1].type, &chromeos_rootfs, sizeof(chromeos_rootfs)); entries[1].starting_lba = 134; - entries[1].ending_lba = 233; + entries[1].ending_lba = 232; Memcpy(&entries[2].type, &chromeos_rootfs, sizeof(chromeos_rootfs)); entries[2].starting_lba = 234; - entries[2].ending_lba = 333; + entries[2].ending_lba = 331; Memcpy(&entries[3].type, &chromeos_kernel, sizeof(chromeos_kernel)); entries[3].starting_lba = 334; - entries[3].ending_lba = 433; - header->padding = 0; + entries[3].ending_lba = 430; + Memset(header->padding, 0, sizeof(header->padding)); /* build secondary */ header2 = (GptHeader*)gpt->secondary_header; @@ -150,56 +165,30 @@ void BuildTestGptData(GptData *gpt) { Memcpy(header2, header, sizeof(GptHeader)); Memcpy(entries2, entries, PARTITION_ENTRIES_SIZE); header2->my_lba = DEFAULT_DRIVE_SECTORS - 1; /* 466 */ + header2->alternate_lba = 1; header2->entries_lba = DEFAULT_DRIVE_SECTORS - 1 - 32; /* 434 */ RefreshCrc32(gpt); } -/* Dumps memory starting from [vp] with [len] bytes. - * Prints [memo] if not NULL. Example output: - * - * 00 01 02 03 04 05 06 07 - 08 09 0a 0b 0c 0d 0e 0f - * 10 11 12 13 14 15 16 17 - 18 19 1a 1b 1c 1d 1e 1f - * ... - */ -static void Dump(void *vp, int len, char* memo) { - uint8_t *start = vp; - int i; - if (memo) printf("--[%s]----------\n", memo); - for (i = 0; i < len; ++i) { - printf("%02x%s", start[i], - (!(~i & 15) ? "\n" : - !(~i & 7) ? " - ": " ")); - } - if (i&15) printf("\n"); -} - -/* More formatted dump with GptData. */ -void DumpGptData(GptData *gpt) { - printf("DumpGptData(%p)...\n", gpt); - Dump(gpt, sizeof(gpt), NULL); - Dump(gpt->primary_header, sizeof(GptHeader), "Primary header"); - Dump(gpt->primary_entries, sizeof(GptEntry) * 8, "Primary entries"); - Dump(gpt->secondary_header, sizeof(GptHeader), "Secondary header"); - Dump(gpt->secondary_entries, sizeof(GptEntry) * 8, - "Secondary entries"); -} /* Tests if the default structure returned by BuildTestGptData() is good. */ -int TestBuildTestGptData() { - GptData *gpt; +static int TestBuildTestGptData() { + GptData* gpt; + gpt = GetEmptyGptData(); BuildTestGptData(gpt); EXPECT(GPT_SUCCESS == GptInit(gpt)); return TEST_OK; } + /* Tests if wrong sector_bytes or drive_sectors is detected by GptInit(). * Currently we only support 512 bytes per sector. * In the future, we may support other sizes. * A too small drive_sectors should be rejected by GptInit(). */ -int ParameterTests() { - GptData *gpt; +static int ParameterTests() { + GptData* gpt; struct { uint32_t sector_bytes; uint64_t drive_sectors; @@ -226,316 +215,287 @@ int ParameterTests() { return TEST_OK; } + +/* Tests if header CRC in two copies are calculated. */ +static int HeaderCrcTest() { + GptData* gpt = GetEmptyGptData(); + GptHeader* h1 = (GptHeader*)gpt->primary_header; + + BuildTestGptData(gpt); + EXPECT(HeaderCrc(h1) == h1->header_crc32); + + /* CRC covers first byte of header */ + BuildTestGptData(gpt); + gpt->primary_header[0] ^= 0xa5; + EXPECT(HeaderCrc(h1) != h1->header_crc32); + + /* CRC covers last byte of header */ + BuildTestGptData(gpt); + gpt->primary_header[h1->size - 1] ^= 0x5a; + EXPECT(HeaderCrc(h1) != h1->header_crc32); + + /* CRC only covers header */ + BuildTestGptData(gpt); + gpt->primary_header[h1->size] ^= 0x5a; + EXPECT(HeaderCrc(h1) == h1->header_crc32); + + return TEST_OK; +} + + /* Tests if signature ("EFI PART") is checked. */ -int SignatureTest() { +static int SignatureTest() { + GptData* gpt = GetEmptyGptData(); + GptHeader* h1 = (GptHeader*)gpt->primary_header; + GptHeader* h2 = (GptHeader*)gpt->secondary_header; int i; - GptData *gpt; - int test_mask; - GptHeader *headers[2]; - gpt = GetEmptyGptData(); - headers[PRIMARY] = (GptHeader*)gpt->primary_header; - headers[SECONDARY] = (GptHeader*)gpt->secondary_header; - - for (test_mask = MASK_PRIMARY; test_mask <= MASK_BOTH; ++test_mask) { - for (i = 0; i < 8; ++i) { - BuildTestGptData(gpt); - if (test_mask & MASK_PRIMARY) - headers[PRIMARY]->signature[i] ^= 0xff; - if (test_mask & MASK_SECONDARY) - headers[SECONDARY]->signature[i] ^= 0xff; - EXPECT((MASK_BOTH ^ test_mask) == CheckHeaderSignature(gpt)); - } + for (i = 0; i < 8; ++i) { + BuildTestGptData(gpt); + h1->signature[i] ^= 0xff; + h2->signature[i] ^= 0xff; + RefreshCrc32(gpt); + EXPECT(1 == CheckHeader(h1, 0, gpt->drive_sectors)); + EXPECT(1 == CheckHeader(h2, 1, gpt->drive_sectors)); } return TEST_OK; } + /* The revision we currently support is GPT_HEADER_REVISION. * If the revision in header is not that, we expect the header is invalid. */ -int RevisionTest() { - GptData *gpt; +static int RevisionTest() { + GptData* gpt = GetEmptyGptData(); + GptHeader* h1 = (GptHeader*)gpt->primary_header; + GptHeader* h2 = (GptHeader*)gpt->secondary_header; + int i; + struct { uint32_t value_to_test; - int is_valid_value; + int expect_rv; } cases[] = { - {0x01000000, 0}, - {0x00010000, 1}, /* GPT_HEADER_REVISION */ - {0x00000100, 0}, - {0x00000001, 0}, - {0x23010456, 0}, + {0x01000000, 1}, + {0x00010000, 0}, /* GPT_HEADER_REVISION */ + {0x00000100, 1}, + {0x00000001, 1}, + {0x23010456, 1}, }; - int i; - int test_mask; - GptHeader *headers[2]; - uint32_t valid_headers; - - gpt = GetEmptyGptData(); - headers[PRIMARY] = (GptHeader*)gpt->primary_header; - headers[SECONDARY] = (GptHeader*)gpt->secondary_header; for (i = 0; i < ARRAY_SIZE(cases); ++i) { - for (test_mask = MASK_PRIMARY; test_mask <= MASK_BOTH; ++test_mask) { - BuildTestGptData(gpt); - if (test_mask & MASK_PRIMARY) - headers[PRIMARY]->revision = cases[i].value_to_test; - if (test_mask & MASK_SECONDARY) - headers[SECONDARY]->revision = cases[i].value_to_test; - valid_headers = CheckRevision(gpt); - if (cases[i].is_valid_value) - EXPECT(MASK_BOTH == valid_headers); - else - EXPECT((MASK_BOTH ^ test_mask) == valid_headers); - } + BuildTestGptData(gpt); + h1->revision = cases[i].value_to_test; + h2->revision = cases[i].value_to_test; + RefreshCrc32(gpt); + + EXPECT(CheckHeader(h1, 0, gpt->drive_sectors) == cases[i].expect_rv); + EXPECT(CheckHeader(h2, 1, gpt->drive_sectors) == cases[i].expect_rv); } return TEST_OK; } -int SizeTest() { - GptData *gpt; + +static int SizeTest() { + GptData* gpt = GetEmptyGptData(); + GptHeader* h1 = (GptHeader*)gpt->primary_header; + GptHeader* h2 = (GptHeader*)gpt->secondary_header; + int i; + struct { uint32_t value_to_test; - int is_valid_value; + int expect_rv; } cases[] = { - {91, 0}, - {92, 1}, - {93, 1}, - {511, 1}, - {512, 1}, - {513, 0}, + {91, 1}, + {92, 0}, + {93, 0}, + {511, 0}, + {512, 0}, + {513, 1}, }; - int i; - int test_mask; - GptHeader *headers[2]; - uint32_t valid_headers; - - gpt = GetEmptyGptData(); - headers[PRIMARY] = (GptHeader*)gpt->primary_header; - headers[SECONDARY] = (GptHeader*)gpt->secondary_header; for (i = 0; i < ARRAY_SIZE(cases); ++i) { - for (test_mask = MASK_PRIMARY; test_mask <= MASK_BOTH; ++test_mask) { - BuildTestGptData(gpt); - if (test_mask & MASK_PRIMARY) - headers[PRIMARY]->size = cases[i].value_to_test; - if (test_mask & MASK_SECONDARY) - headers[SECONDARY]->size = cases[i].value_to_test; - valid_headers = CheckSize(gpt); - if (cases[i].is_valid_value) - EXPECT(MASK_BOTH == valid_headers); - else - EXPECT((MASK_BOTH ^ test_mask) == valid_headers); - } + BuildTestGptData(gpt); + h1->size = cases[i].value_to_test; + h2->size = cases[i].value_to_test; + RefreshCrc32(gpt); + + EXPECT(CheckHeader(h1, 0, gpt->drive_sectors) == cases[i].expect_rv); + EXPECT(CheckHeader(h2, 1, gpt->drive_sectors) == cases[i].expect_rv); } return TEST_OK; } + +/* Tests if CRC is checked. */ +static int CrcFieldTest() { + GptData* gpt = GetEmptyGptData(); + GptHeader* h1 = (GptHeader*)gpt->primary_header; + GptHeader* h2 = (GptHeader*)gpt->secondary_header; + + BuildTestGptData(gpt); + /* Modify a field that the header verification doesn't care about */ + h1->entries_crc32++; + h2->entries_crc32++; + EXPECT(1 == CheckHeader(h1, 0, gpt->drive_sectors)); + EXPECT(1 == CheckHeader(h2, 1, gpt->drive_sectors)); + /* Refresh the CRC; should pass now */ + RefreshCrc32(gpt); + EXPECT(0 == CheckHeader(h1, 0, gpt->drive_sectors)); + EXPECT(0 == CheckHeader(h2, 1, gpt->drive_sectors)); + + return TEST_OK; +} + + /* Tests if reserved fields are checked. * We'll try non-zero values to test. */ -int ReservedFieldsTest() { - GptData *gpt; - GptHeader *primary_header, *secondary_header; +static int ReservedFieldsTest() { + GptData* gpt = GetEmptyGptData(); + GptHeader* h1 = (GptHeader*)gpt->primary_header; + GptHeader* h2 = (GptHeader*)gpt->secondary_header; - gpt = GetEmptyGptData(); - primary_header = (GptHeader*)gpt->primary_header; - secondary_header = (GptHeader*)gpt->secondary_header; - - /* expect secondary is still valid. */ BuildTestGptData(gpt); - primary_header->reserved ^= 0x12345678; /* whatever random */ - EXPECT(MASK_SECONDARY == CheckReservedFields(gpt)); + h1->reserved ^= 0x12345678; /* whatever random */ + h2->reserved ^= 0x12345678; /* whatever random */ + RefreshCrc32(gpt); + EXPECT(1 == CheckHeader(h1, 0, gpt->drive_sectors)); + EXPECT(1 == CheckHeader(h2, 1, gpt->drive_sectors)); - /* expect secondary is still valid. */ +#ifdef PADDING_CHECKED + /* TODO: padding check is currently disabled */ BuildTestGptData(gpt); - primary_header->padding ^= 0x12345678; /* whatever random */ - EXPECT(MASK_SECONDARY == CheckReservedFields(gpt)); - - /* expect primary is still valid. */ - BuildTestGptData(gpt); - secondary_header->reserved ^= 0x12345678; /* whatever random */ - EXPECT(MASK_PRIMARY == CheckReservedFields(gpt)); - - /* expect primary is still valid. */ - BuildTestGptData(gpt); - secondary_header->padding ^= 0x12345678; /* whatever random */ - EXPECT(MASK_PRIMARY == CheckReservedFields(gpt)); + h1->padding[12] ^= 0x34; /* whatever random */ + h2->padding[56] ^= 0x78; /* whatever random */ + RefreshCrc32(gpt); + EXPECT(1 == CheckHeader(h1, 0, gpt->drive_sectors)); + EXPECT(1 == CheckHeader(h2, 1, gpt->drive_sectors)); +#endif return TEST_OK; } -/* Tests if myLBA field is checked (1 for primary, last for secondary). */ -int MyLbaTest() { - GptData *gpt; - int test_mask; - GptHeader *headers[2]; - uint32_t valid_headers; - gpt = GetEmptyGptData(); - headers[PRIMARY] = (GptHeader*)gpt->primary_header; - headers[SECONDARY] = (GptHeader*)gpt->secondary_header; +/* Technically, any size which is 2^N where N > 6 should work, but our + * library only supports one size. */ +static int SizeOfPartitionEntryTest() { + GptData* gpt = GetEmptyGptData(); + GptHeader* h1 = (GptHeader*)gpt->primary_header; + GptHeader* h2 = (GptHeader*)gpt->secondary_header; + int i; - for (test_mask = MASK_PRIMARY; test_mask <= MASK_BOTH; ++test_mask) { - BuildTestGptData(gpt); - if (test_mask & MASK_PRIMARY) - ++headers[PRIMARY]->my_lba; - if (test_mask & MASK_SECONDARY) - --headers[SECONDARY]->my_lba; - valid_headers = CheckMyLba(gpt); - EXPECT((MASK_BOTH ^ test_mask) == valid_headers); - } - return TEST_OK; -} - -/* Tests if SizeOfPartitionEntry is checked. SizeOfPartitionEntry must be - * between 128 and 512, and a multiple of 8. */ -int SizeOfPartitionEntryTest() { - GptData *gpt; struct { uint32_t value_to_test; - int is_valid_value; + int expect_rv; } cases[] = { - {127, 0}, - {128, 1}, - {129, 0}, - {130, 0}, - {131, 0}, - {132, 0}, - {133, 0}, - {134, 0}, - {135, 0}, - {136, 1}, - {144, 1}, - {160, 1}, - {192, 1}, + {127, 1}, + {128, 0}, + {129, 1}, {256, 1}, - {384, 1}, - {504, 1}, {512, 1}, - {513, 0}, - {520, 0}, }; - int i; - int test_mask; - GptHeader *headers[2]; - uint32_t valid_headers; - - gpt = GetEmptyGptData(); - headers[PRIMARY] = (GptHeader*)gpt->primary_header; - headers[SECONDARY] = (GptHeader*)gpt->secondary_header; + /* Check size of entryes */ for (i = 0; i < ARRAY_SIZE(cases); ++i) { - for (test_mask = MASK_PRIMARY; test_mask <= MASK_BOTH; ++test_mask) { - BuildTestGptData(gpt); - if (test_mask & MASK_PRIMARY) { - headers[PRIMARY]->size_of_entry = cases[i].value_to_test; - headers[PRIMARY]->number_of_entries = - TOTAL_ENTRIES_SIZE / cases[i].value_to_test; - } - if (test_mask & MASK_SECONDARY) { - headers[SECONDARY]->size_of_entry = cases[i].value_to_test; - headers[SECONDARY]->number_of_entries = - TOTAL_ENTRIES_SIZE / cases[i].value_to_test; - } - valid_headers = CheckSizeOfPartitionEntry(gpt); - if (cases[i].is_valid_value) - EXPECT(MASK_BOTH == valid_headers); - else - EXPECT((MASK_BOTH ^ test_mask) == valid_headers); - } - } - return TEST_OK; -} - -/* Tests if NumberOfPartitionEntries is checes. NumberOfPartitionEntries must - * be between 32 and 512, and SizeOfPartitionEntry * NumberOfPartitionEntries - * must be 16384. */ -int NumberOfPartitionEntriesTest() { - GptData *gpt; - struct { - uint32_t size_of_entry; - uint32_t number_of_entries; - int is_valid_value; - } cases[] = { - {111, 147, 0}, - {111, 149, 0}, - {128, 32, 0}, - {128, 64, 0}, - {128, 127, 0}, - {128, 128, 1}, - {128, 129, 0}, - {128, 256, 0}, - {256, 32, 0}, - {256, 64, 1}, - {256, 128, 0}, - {256, 256, 0}, - {512, 32, 1}, - {512, 64, 0}, - {512, 128, 0}, - {512, 256, 0}, - {1024, 128, 0}, - }; - int i; - int test_mask; - GptHeader *headers[2]; - uint32_t valid_headers; - - gpt = GetEmptyGptData(); - headers[PRIMARY] = (GptHeader*)gpt->primary_header; - headers[SECONDARY] = (GptHeader*)gpt->secondary_header; - - for (i = 0; i < ARRAY_SIZE(cases); ++i) { - for (test_mask = MASK_PRIMARY; test_mask <= MASK_BOTH; ++test_mask) { - BuildTestGptData(gpt); - if (test_mask & MASK_PRIMARY) { - headers[PRIMARY]->size_of_entry = cases[i].size_of_entry; - headers[PRIMARY]->number_of_entries = cases[i].number_of_entries; - } - if (test_mask & MASK_SECONDARY) { - headers[SECONDARY]->size_of_entry = cases[i].size_of_entry; - headers[SECONDARY]->number_of_entries = cases[i].number_of_entries; - } - valid_headers = CheckNumberOfEntries(gpt); - if (cases[i].is_valid_value) - EXPECT(MASK_BOTH == valid_headers); - else - EXPECT((MASK_BOTH ^ test_mask) == valid_headers); - } - } - return TEST_OK; -} - -/* Tests if PartitionEntryLBA in primary/secondary headers is checked. */ -int PartitionEntryLbaTest() { - GptData *gpt; - int test_mask; - GptHeader *headers[2]; - uint32_t valid_headers; - - gpt = GetEmptyGptData(); - headers[PRIMARY] = (GptHeader*)gpt->primary_header; - headers[SECONDARY] = (GptHeader*)gpt->secondary_header; - - for (test_mask = MASK_PRIMARY; test_mask <= MASK_BOTH; ++test_mask) { BuildTestGptData(gpt); - if (test_mask & MASK_PRIMARY) - headers[PRIMARY]->entries_lba = 0; - if (test_mask & MASK_SECONDARY) - headers[SECONDARY]->entries_lba = DEFAULT_DRIVE_SECTORS - 31 - 1; - valid_headers = CheckEntriesLba(gpt); - EXPECT((MASK_BOTH ^ test_mask) == valid_headers); + h1->size_of_entry = cases[i].value_to_test; + h2->size_of_entry = cases[i].value_to_test; + h1->number_of_entries = TOTAL_ENTRIES_SIZE / cases[i].value_to_test; + h2->number_of_entries = TOTAL_ENTRIES_SIZE / cases[i].value_to_test; + RefreshCrc32(gpt); + + EXPECT(CheckHeader(h1, 0, gpt->drive_sectors) == cases[i].expect_rv); + EXPECT(CheckHeader(h2, 1, gpt->drive_sectors) == cases[i].expect_rv); } + return TEST_OK; } + +/* Technically, any size which is 2^N where N > 6 should work, but our + * library only supports one size. */ +static int NumberOfPartitionEntriesTest() { + GptData* gpt = GetEmptyGptData(); + GptHeader* h1 = (GptHeader*)gpt->primary_header; + GptHeader* h2 = (GptHeader*)gpt->secondary_header; + + BuildTestGptData(gpt); + h1->number_of_entries--; + h2->number_of_entries /= 2; + RefreshCrc32(gpt); + EXPECT(1 == CheckHeader(h1, 0, gpt->drive_sectors)); + EXPECT(1 == CheckHeader(h2, 1, gpt->drive_sectors)); + + return TEST_OK; +} + + +/* Tests if myLBA field is checked (1 for primary, last for secondary). */ +static int MyLbaTest() { + GptData* gpt = GetEmptyGptData(); + GptHeader* h1 = (GptHeader*)gpt->primary_header; + GptHeader* h2 = (GptHeader*)gpt->secondary_header; + + /* myLBA depends on primary vs secondary flag */ + BuildTestGptData(gpt); + EXPECT(1 == CheckHeader(h1, 1, gpt->drive_sectors)); + EXPECT(1 == CheckHeader(h2, 0, gpt->drive_sectors)); + + BuildTestGptData(gpt); + h1->my_lba--; + h2->my_lba--; + RefreshCrc32(gpt); + EXPECT(1 == CheckHeader(h1, 0, gpt->drive_sectors)); + EXPECT(1 == CheckHeader(h2, 1, gpt->drive_sectors)); + + BuildTestGptData(gpt); + h1->my_lba = 2; + h2->my_lba--; + RefreshCrc32(gpt); + EXPECT(1 == CheckHeader(h1, 0, gpt->drive_sectors)); + EXPECT(1 == CheckHeader(h2, 1, gpt->drive_sectors)); + + BuildTestGptData(gpt); + h1->alternate_lba++; + h2->alternate_lba++; + RefreshCrc32(gpt); + EXPECT(1 == CheckHeader(h1, 0, gpt->drive_sectors)); + EXPECT(1 == CheckHeader(h2, 1, gpt->drive_sectors)); + + BuildTestGptData(gpt); + h1->alternate_lba--; + h2->alternate_lba--; + RefreshCrc32(gpt); + EXPECT(1 == CheckHeader(h1, 0, gpt->drive_sectors)); + EXPECT(1 == CheckHeader(h2, 1, gpt->drive_sectors)); + + BuildTestGptData(gpt); + h1->entries_lba++; + h2->entries_lba++; + RefreshCrc32(gpt); + EXPECT(1 == CheckHeader(h1, 0, gpt->drive_sectors)); + EXPECT(1 == CheckHeader(h2, 1, gpt->drive_sectors)); + + BuildTestGptData(gpt); + h1->entries_lba--; + h2->entries_lba--; + RefreshCrc32(gpt); + EXPECT(1 == CheckHeader(h1, 0, gpt->drive_sectors)); + EXPECT(1 == CheckHeader(h2, 1, gpt->drive_sectors)); + + return TEST_OK; +} + + /* Tests if FirstUsableLBA and LastUsableLBA are checked. * 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. */ -int FirstUsableLbaAndLastUsableLbaTest() { - GptData *gpt; - GptHeader *primary_header, *secondary_header; - uint32_t valid_headers; +static int FirstUsableLbaAndLastUsableLbaTest() { + GptData* gpt = GetEmptyGptData(); + GptHeader* h1 = (GptHeader*)gpt->primary_header; + GptHeader* h2 = (GptHeader*)gpt->secondary_header; int i; + struct { uint64_t primary_entries_lba; uint64_t primary_first_usable_lba; @@ -543,197 +503,61 @@ int FirstUsableLbaAndLastUsableLbaTest() { uint64_t secondary_first_usable_lba; uint64_t secondary_last_usable_lba; uint64_t secondary_entries_lba; - int expected_masks; + int primary_rv; + int secondary_rv; } cases[] = { - {2, 34, 433, 34, 433, 434, MASK_BOTH}, - {2, 34, 432, 34, 430, 434, MASK_BOTH}, - {2, 33, 433, 33, 433, 434, MASK_NONE}, - {3, 34, 433, 35, 433, 434, MASK_SECONDARY}, - {3, 35, 433, 33, 433, 434, MASK_PRIMARY}, - {2, 34, 434, 34, 433, 434, MASK_SECONDARY}, - {2, 34, 433, 34, 434, 434, MASK_PRIMARY}, - {2, 35, 433, 35, 433, 434, MASK_BOTH}, - {2, 433, 433, 433, 433, 434, MASK_BOTH}, - {2, 434, 433, 434, 434, 434, MASK_NONE}, - {2, 433, 34, 34, 433, 434, MASK_SECONDARY}, - {2, 34, 433, 433, 34, 434, MASK_PRIMARY}, + {2, 34, 433, 34, 433, 434, 0, 0}, + {2, 34, 432, 34, 430, 434, 0, 0}, + {2, 33, 433, 33, 433, 434, 1, 1}, + {2, 34, 434, 34, 433, 434, 1, 0}, + {2, 34, 433, 34, 434, 434, 0, 1}, + {2, 35, 433, 35, 433, 434, 0, 0}, + {2, 433, 433, 433, 433, 434, 0, 0}, + {2, 434, 433, 434, 434, 434, 1, 1}, + {2, 433, 34, 34, 433, 434, 1, 0}, + {2, 34, 433, 433, 34, 434, 0, 1}, }; - gpt = GetEmptyGptData(); - primary_header = (GptHeader*)gpt->primary_header; - secondary_header = (GptHeader*)gpt->secondary_header; - for (i = 0; i < ARRAY_SIZE(cases); ++i) { BuildTestGptData(gpt); - primary_header->entries_lba = cases[i].primary_entries_lba; - primary_header->first_usable_lba = cases[i].primary_first_usable_lba; - primary_header->last_usable_lba = cases[i].primary_last_usable_lba; - secondary_header->entries_lba = cases[i].secondary_entries_lba; - secondary_header->first_usable_lba = cases[i].secondary_first_usable_lba; - secondary_header->last_usable_lba = cases[i].secondary_last_usable_lba; - valid_headers = CheckValidUsableLbas(gpt); - EXPECT(cases[i].expected_masks == valid_headers); + h1->entries_lba = cases[i].primary_entries_lba; + h1->first_usable_lba = cases[i].primary_first_usable_lba; + h1->last_usable_lba = cases[i].primary_last_usable_lba; + h2->entries_lba = cases[i].secondary_entries_lba; + h2->first_usable_lba = cases[i].secondary_first_usable_lba; + h2->last_usable_lba = cases[i].secondary_last_usable_lba; + RefreshCrc32(gpt); + + EXPECT(CheckHeader(h1, 0, gpt->drive_sectors) == cases[i].primary_rv); + EXPECT(CheckHeader(h2, 1, gpt->drive_sectors) == cases[i].secondary_rv); } return TEST_OK; } -/* Tests if header CRC in two copies are calculated. */ -int HeaderCrcTest() { - GptData *gpt; - GptHeader *primary_header, *secondary_header; - - gpt = GetEmptyGptData(); - primary_header = (GptHeader*)gpt->primary_header; - secondary_header = (GptHeader*)gpt->secondary_header; - - /* Modify the first byte of primary header, and expect the CRC is wrong. */ - BuildTestGptData(gpt); - gpt->primary_header[0] ^= 0xa5; /* just XOR a non-zero value */ - EXPECT(MASK_SECONDARY == CheckHeaderCrc(gpt)); - - /* Modify the last byte of secondary header, and expect the CRC is wrong. */ - BuildTestGptData(gpt); - gpt->secondary_header[secondary_header->size-1] ^= 0x5a; - EXPECT(MASK_PRIMARY == CheckHeaderCrc(gpt)); - - /* Modify out of CRC range, expect CRC is still right. */ - BuildTestGptData(gpt); - gpt->primary_header[primary_header->size] ^= 0x87; - EXPECT(MASK_BOTH == CheckHeaderCrc(gpt)); - - /* Very long header (actually invalid header). Expect will be ignored. */ - primary_header->size = 0x12345678; - secondary_header->size = 0x87654321; - gpt->valid_headers = MASK_NONE; - EXPECT(MASK_NONE == CheckHeaderCrc(gpt)); - - return TEST_OK; -} /* Tests if PartitionEntryArrayCRC32 is checked. * PartitionEntryArrayCRC32 must be calculated over SizeOfPartitionEntry * * NumberOfPartitionEntries bytes. */ -int EntriesCrcTest() { - GptData *gpt; - - gpt = GetEmptyGptData(); +static int EntriesCrcTest() { + GptData* gpt = GetEmptyGptData(); + GptHeader* h1 = (GptHeader*)gpt->primary_header; + GptEntry* e1 = (GptEntry*)(gpt->primary_entries); + GptEntry* e2 = (GptEntry*)(gpt->secondary_entries); /* Modify the first byte of primary entries, and expect the CRC is wrong. */ BuildTestGptData(gpt); + EXPECT(0 == CheckEntries(e1, h1, gpt->drive_sectors)); + EXPECT(0 == CheckEntries(e2, h1, gpt->drive_sectors)); gpt->primary_entries[0] ^= 0xa5; /* just XOR a non-zero value */ - EXPECT(MASK_SECONDARY == CheckEntriesCrc(gpt)); - - /* Modify the last byte of secondary entries, and expect the CRC is wrong. */ - BuildTestGptData(gpt); gpt->secondary_entries[TOTAL_ENTRIES_SIZE-1] ^= 0x5a; - EXPECT(MASK_PRIMARY == CheckEntriesCrc(gpt)); + EXPECT(1 == CheckEntries(e1, h1, gpt->drive_sectors)); + EXPECT(1 == CheckEntries(e2, h1, gpt->drive_sectors)); return TEST_OK; } -/* Tests if GptInit() handles non-identical partition entries well. - * Two copies of partition table entries must be identical. If not, we trust the - * primary table entries, and mark secondary as modified. */ -int IdenticalEntriesTest() { - GptData *gpt; - - gpt = GetEmptyGptData(); - - /* Tests RepairEntries() first. */ - BuildTestGptData(gpt); - EXPECT(0 == RepairEntries(gpt, MASK_BOTH)); - gpt->secondary_entries[0] ^= 0xa5; /* XOR any number */ - EXPECT(GPT_MODIFIED_ENTRIES2 == RepairEntries(gpt, MASK_BOTH)); - EXPECT(GPT_MODIFIED_ENTRIES2 == RepairEntries(gpt, MASK_PRIMARY)); - EXPECT(GPT_MODIFIED_ENTRIES1 == RepairEntries(gpt, MASK_SECONDARY)); - EXPECT(0 == RepairEntries(gpt, MASK_NONE)); - - /* The first byte is different. We expect secondary entries is marked as - * modified. */ - BuildTestGptData(gpt); - gpt->primary_entries[0] ^= 0xff; - RefreshCrc32(gpt); - EXPECT(GPT_SUCCESS == GptInit(gpt)); - EXPECT(GPT_MODIFIED_ENTRIES2 == gpt->modified); - EXPECT(0 == Memcmp(gpt->primary_entries, gpt->secondary_entries, - TOTAL_ENTRIES_SIZE)); - - /* The last byte is different, but the primary entries CRC is bad. - * We expect primary entries is marked as modified. */ - BuildTestGptData(gpt); - gpt->primary_entries[TOTAL_ENTRIES_SIZE-1] ^= 0xff; - EXPECT(GPT_SUCCESS == GptInit(gpt)); - EXPECT(GPT_MODIFIED_ENTRIES1 == gpt->modified); - EXPECT(0 == Memcmp(gpt->primary_entries, gpt->secondary_entries, - TOTAL_ENTRIES_SIZE)); - - return TEST_OK; -} - -/* Tests if GptInit() handles synonymous headers well. - * Note that two partition headers are NOT bit-swise identical. - * For exmaple, my_lba must be different (pointing to respective self). - * So in normal case, they are synonymous, not identical. - * If not synonymous, we trust the primary partition header, and - * overwrite secondary, then mark secondary as modified.*/ -int SynonymousHeaderTest() { - GptData *gpt; - GptHeader *primary_header, *secondary_header; - - gpt = GetEmptyGptData(); - primary_header = (GptHeader*)gpt->primary_header; - secondary_header = (GptHeader*)gpt->secondary_header; - - /* Tests RepairHeader() for synonymous cases first. */ - BuildTestGptData(gpt); - EXPECT(0 == RepairHeader(gpt, MASK_BOTH)); - EXPECT(GPT_MODIFIED_HEADER2 == RepairHeader(gpt, MASK_PRIMARY)); - EXPECT(GPT_MODIFIED_HEADER1 == RepairHeader(gpt, MASK_SECONDARY)); - EXPECT(0 == RepairHeader(gpt, MASK_NONE)); - /* Then tests non-synonymous cases. */ - BuildTestGptData(gpt); - ++secondary_header->first_usable_lba; /* chnage any bit */ - EXPECT(GPT_MODIFIED_HEADER2 == RepairHeader(gpt, MASK_BOTH)); - EXPECT(primary_header->first_usable_lba == - secondary_header->first_usable_lba); - /* ---- */ - BuildTestGptData(gpt); - --secondary_header->last_usable_lba; - EXPECT(GPT_MODIFIED_HEADER2 == RepairHeader(gpt, MASK_BOTH)); - EXPECT(primary_header->last_usable_lba == secondary_header->last_usable_lba); - /* ---- */ - BuildTestGptData(gpt); - ++secondary_header->number_of_entries; - EXPECT(GPT_MODIFIED_HEADER2 == RepairHeader(gpt, MASK_BOTH)); - EXPECT(primary_header->number_of_entries == - secondary_header->number_of_entries); - /* ---- */ - BuildTestGptData(gpt); - --secondary_header->size_of_entry; - EXPECT(GPT_MODIFIED_HEADER2 == RepairHeader(gpt, MASK_BOTH)); - EXPECT(primary_header->size_of_entry == - secondary_header->size_of_entry); - /* ---- */ - BuildTestGptData(gpt); - secondary_header->disk_uuid.u.raw[0] ^= 0x56; - EXPECT(GPT_MODIFIED_HEADER2 == RepairHeader(gpt, MASK_BOTH)); - EXPECT(0 == Memcmp(&primary_header->disk_uuid, - &secondary_header->disk_uuid, sizeof(Guid))); - - /* Consider header repairing in GptInit(). */ - BuildTestGptData(gpt); - ++secondary_header->first_usable_lba; - RefreshCrc32(gpt); - EXPECT(GPT_SUCCESS == GptInit(gpt)); - EXPECT((gpt->modified & (GPT_MODIFIED_HEADER1 | GPT_MODIFIED_HEADER2)) == - GPT_MODIFIED_HEADER2); - EXPECT(primary_header->first_usable_lba == - secondary_header->first_usable_lba); - - return TEST_OK; -} /* Tests if partition geometry is checked. * All active (non-zero PartitionTypeGUID) partition entries should have: @@ -741,53 +565,47 @@ int SynonymousHeaderTest() { * entry.EndingLBA <= header.LastUsableLBA * entry.StartingLBA <= entry.EndingLBA */ -int ValidEntryTest() { - GptData *gpt; - GptHeader *primary_header, *secondary_header; - GptEntry *primary_entries, *secondary_entries; - - gpt = GetEmptyGptData(); - primary_header = (GptHeader*)gpt->primary_header; - secondary_header = (GptHeader*)gpt->secondary_header; - primary_entries = (GptEntry*)gpt->primary_entries; - secondary_entries = (GptEntry*)gpt->secondary_entries; +static int ValidEntryTest() { + GptData* gpt = GetEmptyGptData(); + GptHeader* h1 = (GptHeader*)gpt->primary_header; + GptEntry* e1 = (GptEntry*)(gpt->primary_entries); /* error case: entry.StartingLBA < header.FirstUsableLBA */ BuildTestGptData(gpt); - primary_entries[0].starting_lba = primary_header->first_usable_lba - 1; - EXPECT(MASK_SECONDARY == CheckValidEntries(gpt)); - secondary_entries[1].starting_lba = secondary_header->first_usable_lba - 1; - EXPECT(MASK_NONE == CheckValidEntries(gpt)); + e1[0].starting_lba = h1->first_usable_lba - 1; + RefreshCrc32(gpt); + EXPECT(1 == CheckEntries(e1, h1, gpt->drive_sectors)); /* error case: entry.EndingLBA > header.LastUsableLBA */ BuildTestGptData(gpt); - primary_entries[2].ending_lba = primary_header->last_usable_lba + 1; - EXPECT(MASK_SECONDARY == CheckValidEntries(gpt)); - secondary_entries[3].ending_lba = secondary_header->last_usable_lba + 1; - EXPECT(MASK_NONE == CheckValidEntries(gpt)); + e1[2].ending_lba = h1->last_usable_lba + 1; + RefreshCrc32(gpt); + EXPECT(1 == CheckEntries(e1, h1, gpt->drive_sectors)); /* error case: entry.StartingLBA > entry.EndingLBA */ BuildTestGptData(gpt); - primary_entries[3].starting_lba = primary_entries[3].ending_lba + 1; - EXPECT(MASK_SECONDARY == CheckValidEntries(gpt)); - secondary_entries[1].starting_lba = secondary_entries[1].ending_lba + 1; - EXPECT(MASK_NONE == CheckValidEntries(gpt)); + e1[3].starting_lba = e1[3].ending_lba + 1; + RefreshCrc32(gpt); + EXPECT(1 == CheckEntries(e1, h1, gpt->drive_sectors)); /* case: non active entry should be ignored. */ BuildTestGptData(gpt); - Memset(&primary_entries[1].type, 0, sizeof(primary_entries[1].type)); - primary_entries[1].starting_lba = primary_entries[1].ending_lba + 1; - EXPECT(MASK_BOTH == CheckValidEntries(gpt)); - Memset(&secondary_entries[2].type, 0, sizeof(secondary_entries[2].type)); - secondary_entries[2].starting_lba = secondary_entries[2].ending_lba + 1; - EXPECT(MASK_BOTH == CheckValidEntries(gpt)); + Memset(&e1[1].type, 0, sizeof(e1[1].type)); + e1[1].starting_lba = e1[1].ending_lba + 1; + RefreshCrc32(gpt); + EXPECT(0 == CheckEntries(e1, h1, gpt->drive_sectors)); return TEST_OK; } + /* Tests if overlapped partition tables can be detected. */ -int OverlappedPartitionTest() { - GptData *gpt; +static int OverlappedPartitionTest() { + GptData* gpt = GetEmptyGptData(); + GptHeader* h = (GptHeader*)gpt->primary_header; + GptEntry* e = (GptEntry*)gpt->primary_entries; + int i, j; + struct { int overlapped; struct { @@ -796,271 +614,463 @@ int OverlappedPartitionTest() { uint64_t ending_lba; } entries[16]; /* enough for testing. */ } cases[] = { - {0, {{0, 100, 199}, {0, 0, 0}}}, - {0, {{1, 100, 199}, {0, 0, 0}}}, - {0, {{1, 100, 150}, {1, 200, 250}, {1, 300, 350}, {0, 0, 0}}}, - {1, {{1, 200, 299}, {1, 100, 199}, {1, 100, 100}, {0, 0, 0}}}, - {1, {{1, 200, 299}, {1, 100, 199}, {1, 299, 299}, {0, 0, 0}}}, - {0, {{1, 300, 399}, {1, 200, 299}, {1, 100, 199}, {0, 0, 0}}}, - {1, {{1, 100, 199}, {1, 199, 299}, {1, 299, 399}, {0, 0, 0}}}, - {1, {{1, 100, 199}, {1, 200, 299}, {1, 75, 399}, {0, 0, 0}}}, - {1, {{1, 100, 199}, {1, 75, 250}, {1, 200, 299}, {0, 0, 0}}}, - {1, {{1, 75, 150}, {1, 100, 199}, {1, 200, 299}, {0, 0, 0}}}, - {1, {{1, 200, 299}, {1, 100, 199}, {1, 300, 399}, {1, 100, 399}, - {0, 0, 0}}}, - {0, {{1, 200, 299}, {1, 100, 199}, {1, 300, 399}, {0, 100, 399}, - {0, 0, 0}}}, - {1, {{1, 200, 300}, {1, 100, 200}, {1, 100, 400}, {1, 300, 400}, - {0, 0, 0}}}, - {1, {{0, 200, 300}, {1, 100, 200}, {1, 100, 400}, {1, 300, 400}, - {0, 0, 0}}}, - {0, {{1, 200, 300}, {1, 100, 199}, {0, 100, 400}, {0, 300, 400}, - {0, 0, 0}}}, - {1, {{1, 200, 299}, {1, 100, 199}, {1, 199, 199}, {0, 0, 0}}}, - {0, {{1, 200, 299}, {0, 100, 199}, {1, 199, 199}, {0, 0, 0}}}, - {0, {{1, 200, 299}, {1, 100, 199}, {0, 199, 199}, {0, 0, 0}}}, + {0, {{0, 100, 199}}}, + {0, {{1, 100, 199}}}, + {0, {{1, 100, 150}, {1, 200, 250}, {1, 300, 350}}}, + {1, {{1, 200, 299}, {1, 100, 199}, {1, 100, 100}}}, + {1, {{1, 200, 299}, {1, 100, 199}, {1, 299, 299}}}, + {0, {{1, 300, 399}, {1, 200, 299}, {1, 100, 199}}}, + {1, {{1, 100, 199}, {1, 199, 299}, {1, 299, 399}}}, + {1, {{1, 100, 199}, {1, 200, 299}, {1, 75, 399}}}, + {1, {{1, 100, 199}, {1, 75, 250}, {1, 200, 299}}}, + {1, {{1, 75, 150}, {1, 100, 199}, {1, 200, 299}}}, + {1, {{1, 200, 299}, {1, 100, 199}, {1, 300, 399}, {1, 100, 399}}}, + {0, {{1, 200, 299}, {1, 100, 199}, {1, 300, 399}, {0, 100, 399}}}, + {1, {{1, 200, 300}, {1, 100, 200}, {1, 100, 400}, {1, 300, 400}}}, + {1, {{0, 200, 300}, {1, 100, 200}, {1, 100, 400}, {1, 300, 400}}}, + {0, {{1, 200, 300}, {1, 100, 199}, {0, 100, 400}, {0, 300, 400}}}, + {1, {{1, 200, 299}, {1, 100, 199}, {1, 199, 199}}}, + {0, {{1, 200, 299}, {0, 100, 199}, {1, 199, 199}}}, + {0, {{1, 200, 299}, {1, 100, 199}, {0, 199, 199}}}, {1, {{1, 199, 199}, {1, 200, 200}, {1, 201, 201}, {1, 202, 202}, {1, 203, 203}, {1, 204, 204}, {1, 205, 205}, {1, 206, 206}, - {1, 207, 207}, {1, 208, 208}, {1, 199, 199}, {0, 0, 0}}}, + {1, 207, 207}, {1, 208, 208}, {1, 199, 199}}}, {0, {{1, 199, 199}, {1, 200, 200}, {1, 201, 201}, {1, 202, 202}, {1, 203, 203}, {1, 204, 204}, {1, 205, 205}, {1, 206, 206}, - {1, 207, 207}, {1, 208, 208}, {0, 199, 199}, {0, 0, 0}}}, + {1, 207, 207}, {1, 208, 208}, {0, 199, 199}}}, }; - Guid any_type = GPT_ENT_TYPE_CHROMEOS_KERNEL; - int i, j; - int test_mask; - GptEntry *entries[2]; - gpt = GetEmptyGptData(); - entries[PRIMARY] = (GptEntry*)gpt->primary_entries; - entries[SECONDARY] = (GptEntry*)gpt->secondary_entries; for (i = 0; i < ARRAY_SIZE(cases); ++i) { - for (test_mask = MASK_PRIMARY; test_mask <= MASK_BOTH; ++test_mask) { - BuildTestGptData(gpt); - ZeroEntries(gpt); - for(j = 0; j < ARRAY_SIZE(cases[0].entries); ++j) { - if (!cases[i].entries[j].starting_lba) break; - if (test_mask & MASK_PRIMARY) { - if (cases[i].entries[j].active) - Memcpy(&entries[PRIMARY][j].type, &any_type, sizeof(any_type)); - entries[PRIMARY][j].starting_lba = cases[i].entries[j].starting_lba; - entries[PRIMARY][j].ending_lba = cases[i].entries[j].ending_lba; - } - if (test_mask & MASK_SECONDARY) { - if (cases[i].entries[j].active) - Memcpy(&entries[SECONDARY][j].type, &any_type, sizeof(any_type)); - entries[SECONDARY][j].starting_lba = cases[i].entries[j].starting_lba; - entries[SECONDARY][j].ending_lba = cases[i].entries[j].ending_lba; - } - } - EXPECT((cases[i].overlapped * test_mask) == - (OverlappedEntries(entries[PRIMARY], j) | - (OverlappedEntries(entries[SECONDARY], j) << SECONDARY)) - ); + BuildTestGptData(gpt); + ZeroEntries(gpt); + for(j = 0; j < ARRAY_SIZE(cases[0].entries); ++j) { + if (!cases[i].entries[j].starting_lba) + break; - EXPECT((MASK_BOTH ^ (cases[i].overlapped * test_mask)) == - CheckOverlappedPartition(gpt)); + if (cases[i].entries[j].active) + Memcpy(&e[j].type, &guid_kernel, sizeof(Guid)); + e[j].starting_lba = cases[i].entries[j].starting_lba; + e[j].ending_lba = cases[i].entries[j].ending_lba; } + RefreshCrc32(gpt); + + EXPECT(cases[i].overlapped == CheckEntries(e, h, gpt->drive_sectors)); } return TEST_OK; } -/* Tests if GptInit() can survive in different corrupt header/entries - * combinations, like: - * primary GPT header - valid - * primary partition table - invalid - * secondary GPT header - invalid - * secondary partition table - valid - */ -int CorruptCombinationTest() { - GptData *gpt; - GptHeader *primary_header, *secondary_header; - GptEntry *primary_entries, *secondary_entries; - gpt = GetEmptyGptData(); - primary_header = (GptHeader*)gpt->primary_header; - secondary_header = (GptHeader*)gpt->secondary_header; - primary_entries = (GptEntry*)gpt->primary_entries; - secondary_entries = (GptEntry*)gpt->secondary_entries; +/* Test both sanity checking and repair. */ +static int SanityCheckTest() { + GptData* gpt = GetEmptyGptData(); + GptHeader* h1 = (GptHeader*)gpt->primary_header; - /* Make primary entries and secondary header invalid, we expect GptInit() - * can recover them (returns GPT_SUCCESS and MODIFIED flasgs). */ + /* Unmodified test data is completely sane */ BuildTestGptData(gpt); - primary_entries[0].type.u.raw[0] ^= 0x33; - secondary_header->header_crc32 ^= 0x55; - EXPECT(GPT_SUCCESS == GptInit(gpt)); + EXPECT(GPT_SUCCESS == GptSanityCheck(gpt)); + EXPECT(MASK_BOTH == gpt->valid_headers); + EXPECT(MASK_BOTH == gpt->valid_entries); + /* Repair doesn't damage it */ + GptRepair(gpt); + EXPECT(GPT_SUCCESS == GptSanityCheck(gpt)); + EXPECT(MASK_BOTH == gpt->valid_headers); + EXPECT(MASK_BOTH == gpt->valid_entries); + EXPECT(0 == gpt->modified); + + /* Modify headers */ + BuildTestGptData(gpt); + gpt->primary_header[0]++; + gpt->secondary_header[0]++; + EXPECT(GPT_ERROR_INVALID_HEADERS == GptSanityCheck(gpt)); + EXPECT(0 == gpt->valid_headers); + EXPECT(0 == gpt->valid_entries); + /* Repair can't fix completely busted headers */ + GptRepair(gpt); + EXPECT(GPT_ERROR_INVALID_HEADERS == GptSanityCheck(gpt)); + EXPECT(0 == gpt->valid_headers); + EXPECT(0 == gpt->valid_entries); + EXPECT(0 == gpt->modified); + + BuildTestGptData(gpt); + gpt->primary_header[0]++; + EXPECT(GPT_SUCCESS == GptSanityCheck(gpt)); + EXPECT(MASK_SECONDARY == gpt->valid_headers); + EXPECT(MASK_BOTH == gpt->valid_entries); + GptRepair(gpt); + EXPECT(GPT_SUCCESS == GptSanityCheck(gpt)); + EXPECT(MASK_BOTH == gpt->valid_headers); + EXPECT(MASK_BOTH == gpt->valid_entries); + EXPECT(GPT_MODIFIED_HEADER1 == gpt->modified); + + BuildTestGptData(gpt); + gpt->secondary_header[0]++; + EXPECT(GPT_SUCCESS == GptSanityCheck(gpt)); + EXPECT(MASK_PRIMARY == gpt->valid_headers); + EXPECT(MASK_BOTH == gpt->valid_entries); + GptRepair(gpt); + EXPECT(GPT_SUCCESS == GptSanityCheck(gpt)); + EXPECT(MASK_BOTH == gpt->valid_headers); + EXPECT(MASK_BOTH == gpt->valid_entries); + EXPECT(GPT_MODIFIED_HEADER2 == gpt->modified); + + /* Modify header1 and update its CRC. Since header2 is now different than + * header1, it'll be the one considered invalid. */ + BuildTestGptData(gpt); + h1->size++; + RefreshCrc32(gpt); + EXPECT(GPT_SUCCESS == GptSanityCheck(gpt)); + EXPECT(MASK_PRIMARY == gpt->valid_headers); + EXPECT(MASK_BOTH == gpt->valid_entries); + GptRepair(gpt); + EXPECT(GPT_SUCCESS == GptSanityCheck(gpt)); + EXPECT(MASK_BOTH == gpt->valid_headers); + EXPECT(MASK_BOTH == gpt->valid_entries); + EXPECT(GPT_MODIFIED_HEADER2 == gpt->modified); + + /* Modify entries */ + BuildTestGptData(gpt); + gpt->primary_entries[0]++; + gpt->secondary_entries[0]++; + EXPECT(GPT_ERROR_INVALID_ENTRIES == GptSanityCheck(gpt)); + EXPECT(MASK_BOTH == gpt->valid_headers); + EXPECT(MASK_NONE == gpt->valid_entries); + /* Repair can't fix both copies of entries being bad, either. */ + GptRepair(gpt); + EXPECT(GPT_ERROR_INVALID_ENTRIES == GptSanityCheck(gpt)); + EXPECT(MASK_BOTH == gpt->valid_headers); + EXPECT(MASK_NONE == gpt->valid_entries); + EXPECT(0 == gpt->modified); + + BuildTestGptData(gpt); + gpt->primary_entries[0]++; + EXPECT(GPT_SUCCESS == GptSanityCheck(gpt)); + EXPECT(MASK_BOTH == gpt->valid_headers); + EXPECT(MASK_SECONDARY == gpt->valid_entries); + GptRepair(gpt); + EXPECT(GPT_SUCCESS == GptSanityCheck(gpt)); + EXPECT(MASK_BOTH == gpt->valid_headers); + EXPECT(MASK_BOTH == gpt->valid_entries); + EXPECT(GPT_MODIFIED_ENTRIES1 == gpt->modified); + + BuildTestGptData(gpt); + gpt->secondary_entries[0]++; + EXPECT(GPT_SUCCESS == GptSanityCheck(gpt)); + EXPECT(MASK_BOTH == gpt->valid_headers); + EXPECT(MASK_PRIMARY == gpt->valid_entries); + GptRepair(gpt); + EXPECT(GPT_SUCCESS == GptSanityCheck(gpt)); + EXPECT(MASK_BOTH == gpt->valid_headers); + EXPECT(MASK_BOTH == gpt->valid_entries); + EXPECT(GPT_MODIFIED_ENTRIES2 == gpt->modified); + + /* Test cross-correction (h1+e2, h2+e1) */ + BuildTestGptData(gpt); + gpt->primary_header[0]++; + gpt->secondary_entries[0]++; + EXPECT(GPT_SUCCESS == GptSanityCheck(gpt)); + EXPECT(MASK_SECONDARY == gpt->valid_headers); + EXPECT(MASK_PRIMARY == gpt->valid_entries); + GptRepair(gpt); + EXPECT(GPT_SUCCESS == GptSanityCheck(gpt)); + EXPECT(MASK_BOTH == gpt->valid_headers); + EXPECT(MASK_BOTH == gpt->valid_entries); + EXPECT((GPT_MODIFIED_HEADER1 | GPT_MODIFIED_ENTRIES2) == gpt->modified); + + BuildTestGptData(gpt); + gpt->secondary_header[0]++; + gpt->primary_entries[0]++; + EXPECT(GPT_SUCCESS == GptSanityCheck(gpt)); + EXPECT(MASK_PRIMARY == gpt->valid_headers); + EXPECT(MASK_SECONDARY == gpt->valid_entries); + GptRepair(gpt); + EXPECT(GPT_SUCCESS == GptSanityCheck(gpt)); + EXPECT(MASK_BOTH == gpt->valid_headers); + EXPECT(MASK_BOTH == gpt->valid_entries); EXPECT((GPT_MODIFIED_HEADER2 | GPT_MODIFIED_ENTRIES1) == gpt->modified); - EXPECT(0 == Memcmp(primary_entries, secondary_entries, TOTAL_ENTRIES_SIZE)); - /* We expect the modified header/entries can pass GptInit(). */ - EXPECT(GPT_SUCCESS == GptInit(gpt)); - EXPECT(0 == gpt->modified); - /* Make primary header invalid (the entries is not damaged actually). */ + /* Test mismatched pairs (h1+e1 valid, h2+e2 valid but different. + * This simulates a partial update of the drive. */ BuildTestGptData(gpt); - primary_header->entries_crc32 ^= 0x73; - EXPECT(GPT_SUCCESS == GptInit(gpt)); - /* After header is repaired, the entries are valid actually. */ - EXPECT((gpt->modified & (GPT_MODIFIED_HEADER1 | GPT_MODIFIED_HEADER2)) == - GPT_MODIFIED_HEADER1); - /* We expect the modified header/entries can pass GptInit(). */ - EXPECT(GPT_SUCCESS == GptInit(gpt)); - EXPECT(0 == gpt->modified); + gpt->secondary_entries[0]++; + RefreshCrc32(gpt); + EXPECT(GPT_SUCCESS == GptSanityCheck(gpt)); + EXPECT(MASK_PRIMARY == gpt->valid_headers); + EXPECT(MASK_PRIMARY == gpt->valid_entries); + GptRepair(gpt); + EXPECT(GPT_SUCCESS == GptSanityCheck(gpt)); + EXPECT(MASK_BOTH == gpt->valid_headers); + EXPECT(MASK_BOTH == gpt->valid_entries); + EXPECT((GPT_MODIFIED_HEADER2 | GPT_MODIFIED_ENTRIES2) == gpt->modified); return TEST_OK; } + +static int EntryAttributeGetSetTest() { + GptData* gpt = GetEmptyGptData(); + GptEntry* e = (GptEntry*)(gpt->primary_entries); + + e->attributes = 0x0000000000000000; + SetEntrySuccessful(e, 1); + EXPECT(0x0100000000000000 == e->attributes); + EXPECT(1 == GetEntrySuccessful(e)); + e->attributes = 0xFFFFFFFFFFFFFFFF; + SetEntrySuccessful(e, 0); + EXPECT(0xFEFFFFFFFFFFFFFF == e->attributes); + EXPECT(0 == GetEntrySuccessful(e)); + + e->attributes = 0x0000000000000000; + SetEntryTries(e, 15); + EXPECT(15 == GetEntryTries(e)); + EXPECT(0x00F0000000000000 == e->attributes); + e->attributes = 0xFFFFFFFFFFFFFFFF; + SetEntryTries(e, 0); + EXPECT(0xFF0FFFFFFFFFFFFF == e->attributes); + EXPECT(0 == GetEntryTries(e)); + + e->attributes = 0x0000000000000000; + SetEntryPriority(e, 15); + EXPECT(0x000F000000000000 == e->attributes); + EXPECT(15 == GetEntryPriority(e)); + e->attributes = 0xFFFFFFFFFFFFFFFF; + SetEntryPriority(e, 0); + EXPECT(0xFFF0FFFFFFFFFFFF == e->attributes); + EXPECT(0 == GetEntryPriority(e)); + + e->attributes = 0xFFFFFFFFFFFFFFFF; + EXPECT(1 == GetEntrySuccessful(e)); + EXPECT(15 == GetEntryPriority(e)); + EXPECT(15 == GetEntryTries(e)); + + e->attributes = 0x0123000000000000; + EXPECT(1 == GetEntrySuccessful(e)); + EXPECT(2 == GetEntryTries(e)); + EXPECT(3 == GetEntryPriority(e)); + + return TEST_OK; +} + + +static int EntryTypeTest() { + GptData* gpt = GetEmptyGptData(); + GptEntry* e = (GptEntry*)(gpt->primary_entries); + + Memcpy(&e->type, &guid_zero, sizeof(Guid)); + EXPECT(1 == IsUnusedEntry(e)); + EXPECT(0 == IsKernelEntry(e)); + + Memcpy(&e->type, &guid_kernel, sizeof(Guid)); + EXPECT(0 == IsUnusedEntry(e)); + EXPECT(1 == IsKernelEntry(e)); + + Memcpy(&e->type, &guid_rootfs, sizeof(Guid)); + EXPECT(0 == IsUnusedEntry(e)); + EXPECT(0 == IsKernelEntry(e)); + + return TEST_OK; +} + + +/* Make an entry unused by clearing its type. */ +static void FreeEntry(GptEntry* e) { + Memset(&e->type, 0, sizeof(Guid)); +} + + +/* Set up an entry. */ +static void FillEntry(GptEntry* e, int is_kernel, + int priority, int successful, int tries) { + Memcpy(&e->type, (is_kernel ? &guid_kernel : &guid_zero), sizeof(Guid)); + SetEntryPriority(e, priority); + SetEntrySuccessful(e, successful); + SetEntryTries(e, tries); +} + + /* Invalidate all kernel entries and expect GptNextKernelEntry() cannot find * any usable kernel entry. */ -int NoValidKernelEntryTest() { - GptData *gpt; - GptEntry *entries, *entries2; - - gpt = GetEmptyGptData(); - entries = (GptEntry*)gpt->primary_entries; - entries2 = (GptEntry*)gpt->secondary_entries; +static int NoValidKernelEntryTest() { + GptData* gpt = GetEmptyGptData(); + GptEntry* e1 = (GptEntry*)(gpt->primary_entries); BuildTestGptData(gpt); - entries[KERNEL_A].attributes |= CGPT_ATTRIBUTE_BAD_MASK; - Memset(&entries[KERNEL_B].type, 0, sizeof(Guid)); + SetEntryPriority(e1 + KERNEL_A, 0); + FreeEntry(e1 + KERNEL_B); RefreshCrc32(gpt); - EXPECT(GPT_ERROR_NO_VALID_KERNEL == GptNextKernelEntry(gpt, NULL, NULL)); return TEST_OK; } -/* This is the combination test. Both kernel A and B could be either inactive - * or invalid. We expect GptNextKetnelEntry() returns good kernel or - * GPT_ERROR_NO_VALID_KERNEL if no kernel is available. */ -enum FAILURE_MASK { - MASK_INACTIVE = 1, - MASK_BAD_ENTRY = 2, - MASK_FAILURE_BOTH = 3, -}; -void BreakAnEntry(GptEntry *entry, enum FAILURE_MASK failure) { - if (failure & MASK_INACTIVE) - Memset(&entry->type, 0, sizeof(Guid)); - if (failure & MASK_BAD_ENTRY) - entry->attributes |= CGPT_ATTRIBUTE_BAD_MASK; -} -int CombinationalNextKernelEntryTest() { - GptData *gpt; - enum { - MASK_KERNEL_A = 1, - MASK_KERNEL_B = 2, - MASK_KERNEL_BOTH = 3, - } kernel; - enum FAILURE_MASK failure; - uint64_t start_sector, size; - int retval; - - for (kernel = MASK_KERNEL_A; kernel <= MASK_KERNEL_BOTH; ++kernel) { - for (failure = MASK_INACTIVE; failure < MASK_FAILURE_BOTH; ++failure) { - gpt = GetEmptyGptData(); - BuildTestGptData(gpt); - - if (kernel & MASK_KERNEL_A) - BreakAnEntry(GetEntry(gpt, PRIMARY, KERNEL_A), failure); - if (kernel & MASK_KERNEL_B) - BreakAnEntry(GetEntry(gpt, PRIMARY, KERNEL_B), failure); - - retval = GptNextKernelEntry(gpt, &start_sector, &size); - - if (kernel == MASK_KERNEL_A) { - EXPECT(retval == GPT_SUCCESS); - EXPECT(start_sector == 334); - } else if (kernel == MASK_KERNEL_B) { - EXPECT(retval == GPT_SUCCESS); - EXPECT(start_sector == 34); - } else { /* MASK_KERNEL_BOTH */ - EXPECT(retval == GPT_ERROR_NO_VALID_KERNEL); - } - } - } - return TEST_OK; -} - -/* Increase tries value from zero, expect it won't explode/overflow after - * CGPT_ATTRIBUTE_TRIES_MASK. - */ -/* Tries would not count up after CGPT_ATTRIBUTE_MAX_TRIES. */ -#define EXPECTED_TRIES(tries) \ - ((tries >= CGPT_ATTRIBUTE_MAX_TRIES) ? CGPT_ATTRIBUTE_MAX_TRIES \ - : tries) -int IncreaseTriesTest() { - GptData *gpt; - int kernel_index[] = { - KERNEL_B, - KERNEL_A, - }; - int i, tries, j; - - gpt = GetEmptyGptData(); - for (i = 0; i < ARRAY_SIZE(kernel_index); ++i) { - GptEntry *entries[2] = { - (GptEntry*)gpt->primary_entries, - (GptEntry*)gpt->secondary_entries, - }; - int current; - - BuildTestGptData(gpt); - current = gpt->current_kernel = kernel_index[i]; - - for (tries = 0; tries < 2 * CGPT_ATTRIBUTE_MAX_TRIES; ++tries) { - for (j = 0; j < ARRAY_SIZE(entries); ++j) { - EXPECT(EXPECTED_TRIES(tries) == - ((entries[j][current].attributes & CGPT_ATTRIBUTE_TRIES_MASK) >> - CGPT_ATTRIBUTE_TRIES_OFFSET)); - } - - EXPECT(GPT_SUCCESS == GptUpdateKernelEntry(gpt, GPT_UPDATE_ENTRY_TRY)); - /* The expected tries value will be checked in next iteration. */ - - if (tries < CGPT_ATTRIBUTE_MAX_TRIES) - EXPECT((GPT_MODIFIED_HEADER1 | GPT_MODIFIED_ENTRIES1 | - GPT_MODIFIED_HEADER2 | GPT_MODIFIED_ENTRIES2) == gpt->modified); - gpt->modified = 0; /* reset before next test */ - EXPECT(0 == - Memcmp(entries[PRIMARY], entries[SECONDARY], TOTAL_ENTRIES_SIZE)); - } - } - return TEST_OK; -} - -/* Mark a kernel as bad. Expect: - * 1. the both bad bits of kernel A in primary and secondary entries are set. - * 2. headers and entries are marked as modified. - * 3. primary and secondary entries are identical. - */ -int MarkBadKernelEntryTest() { - GptData *gpt; - GptEntry *entries, *entries2; - - gpt = GetEmptyGptData(); - entries = (GptEntry*)gpt->primary_entries; - entries2 = (GptEntry*)gpt->secondary_entries; +static int GetNextNormalTest() { + GptData* gpt = GetEmptyGptData(); + GptEntry* e1 = (GptEntry*)(gpt->primary_entries); + uint64_t start, size; + /* Normal case - both kernels successful */ BuildTestGptData(gpt); - gpt->current_kernel = KERNEL_A; - EXPECT(GPT_SUCCESS == GptUpdateKernelEntry(gpt, GPT_UPDATE_ENTRY_BAD)); - EXPECT((GPT_MODIFIED_HEADER1 | GPT_MODIFIED_ENTRIES1 | - GPT_MODIFIED_HEADER2 | GPT_MODIFIED_ENTRIES2) == gpt->modified); - EXPECT(entries[KERNEL_A].attributes & CGPT_ATTRIBUTE_BAD_MASK); - EXPECT(entries2[KERNEL_A].attributes & CGPT_ATTRIBUTE_BAD_MASK); - EXPECT(0 == Memcmp(entries, entries2, TOTAL_ENTRIES_SIZE)); + FillEntry(e1 + KERNEL_A, 1, 2, 1, 0); + FillEntry(e1 + KERNEL_B, 1, 2, 1, 0); + RefreshCrc32(gpt); + GptInit(gpt); + + EXPECT(GPT_SUCCESS == GptNextKernelEntry(gpt, &start, &size)); + EXPECT(KERNEL_A == gpt->current_kernel); + EXPECT(34 == start); + EXPECT(100 == size); + + EXPECT(GPT_SUCCESS == GptNextKernelEntry(gpt, &start, &size)); + EXPECT(KERNEL_B == gpt->current_kernel); + EXPECT(134 == start); + EXPECT(99 == size); + + EXPECT(GPT_ERROR_NO_VALID_KERNEL == GptNextKernelEntry(gpt, &start, &size)); + EXPECT(-1 == gpt->current_kernel); + + /* Call as many times as you want; you won't get another kernel... */ + EXPECT(GPT_ERROR_NO_VALID_KERNEL == GptNextKernelEntry(gpt, &start, &size)); + EXPECT(-1 == gpt->current_kernel); return TEST_OK; } + +static int GetNextPrioTest() { + GptData* gpt = GetEmptyGptData(); + GptEntry* e1 = (GptEntry*)(gpt->primary_entries); + uint64_t start, size; + + /* Priority 3, 4, 0, 4 - should boot order B, Y, A */ + BuildTestGptData(gpt); + FillEntry(e1 + KERNEL_A, 1, 3, 1, 0); + FillEntry(e1 + KERNEL_B, 1, 4, 1, 0); + FillEntry(e1 + KERNEL_X, 1, 0, 1, 0); + FillEntry(e1 + KERNEL_Y, 1, 4, 1, 0); + RefreshCrc32(gpt); + GptInit(gpt); + + EXPECT(GPT_SUCCESS == GptNextKernelEntry(gpt, &start, &size)); + EXPECT(KERNEL_B == gpt->current_kernel); + EXPECT(GPT_SUCCESS == GptNextKernelEntry(gpt, &start, &size)); + EXPECT(KERNEL_Y == gpt->current_kernel); + EXPECT(GPT_SUCCESS == GptNextKernelEntry(gpt, &start, &size)); + EXPECT(KERNEL_A == gpt->current_kernel); + EXPECT(GPT_ERROR_NO_VALID_KERNEL == GptNextKernelEntry(gpt, &start, &size)); + + return TEST_OK; +} + + +static int GetNextTriesTest() { + GptData* gpt = GetEmptyGptData(); + GptEntry* e1 = (GptEntry*)(gpt->primary_entries); + uint64_t start, size; + + /* Tries=nonzero is attempted just like success, but tries=0 isn't */ + BuildTestGptData(gpt); + FillEntry(e1 + KERNEL_A, 1, 2, 1, 0); + FillEntry(e1 + KERNEL_B, 1, 3, 0, 0); + FillEntry(e1 + KERNEL_X, 1, 4, 0, 1); + FillEntry(e1 + KERNEL_Y, 1, 0, 0, 5); + RefreshCrc32(gpt); + GptInit(gpt); + + EXPECT(GPT_SUCCESS == GptNextKernelEntry(gpt, &start, &size)); + EXPECT(KERNEL_X == gpt->current_kernel); + EXPECT(GPT_SUCCESS == GptNextKernelEntry(gpt, &start, &size)); + EXPECT(KERNEL_A == gpt->current_kernel); + EXPECT(GPT_ERROR_NO_VALID_KERNEL == GptNextKernelEntry(gpt, &start, &size)); + + return TEST_OK; +} + + +static int GptUpdateTest() { + GptData* gpt = GetEmptyGptData(); + GptEntry* e = (GptEntry*)(gpt->primary_entries); + GptEntry* e2 = (GptEntry*)(gpt->secondary_entries); + uint64_t start, size; + + /* Tries=nonzero is attempted just like success, but tries=0 isn't */ + BuildTestGptData(gpt); + FillEntry(e + KERNEL_A, 1, 4, 1, 0); + FillEntry(e + KERNEL_B, 1, 3, 0, 2); + FillEntry(e + KERNEL_X, 1, 2, 0, 2); + RefreshCrc32(gpt); + GptInit(gpt); + gpt->modified = 0; /* Nothing modified yet */ + + /* Successful kernel */ + EXPECT(GPT_SUCCESS == GptNextKernelEntry(gpt, &start, &size)); + EXPECT(KERNEL_A == gpt->current_kernel); + EXPECT(1 == GetEntrySuccessful(e + KERNEL_A)); + EXPECT(4 == GetEntryPriority(e + KERNEL_A)); + EXPECT(0 == GetEntryTries(e + KERNEL_A)); + EXPECT(1 == GetEntrySuccessful(e2 + KERNEL_A)); + EXPECT(4 == GetEntryPriority(e2 + KERNEL_A)); + EXPECT(0 == GetEntryTries(e2 + KERNEL_A)); + /* Trying successful kernel changes nothing */ + EXPECT(GPT_SUCCESS == GptUpdateKernelEntry(gpt, GPT_UPDATE_ENTRY_TRY)); + EXPECT(1 == GetEntrySuccessful(e + KERNEL_A)); + EXPECT(4 == GetEntryPriority(e + KERNEL_A)); + EXPECT(0 == GetEntryTries(e + KERNEL_A)); + EXPECT(0 == gpt->modified); + /* Marking it bad does, though */ + EXPECT(GPT_SUCCESS == GptUpdateKernelEntry(gpt, GPT_UPDATE_ENTRY_BAD)); + EXPECT(0 == GetEntrySuccessful(e + KERNEL_A)); + EXPECT(0 == GetEntryPriority(e + KERNEL_A)); + EXPECT(0 == GetEntryTries(e + KERNEL_A)); + /* Which affects both copies of the partition entries */ + EXPECT(0 == GetEntrySuccessful(e2 + KERNEL_A)); + EXPECT(0 == GetEntryPriority(e2 + KERNEL_A)); + EXPECT(0 == GetEntryTries(e2 + KERNEL_A)); + /* And that's caused the GPT to need updating */ + EXPECT(0x0F == gpt->modified); + + /* Kernel with tries */ + EXPECT(GPT_SUCCESS == GptNextKernelEntry(gpt, &start, &size)); + EXPECT(KERNEL_B == gpt->current_kernel); + EXPECT(0 == GetEntrySuccessful(e + KERNEL_B)); + EXPECT(3 == GetEntryPriority(e + KERNEL_B)); + EXPECT(2 == GetEntryTries(e + KERNEL_B)); + /* Marking it bad clears it */ + EXPECT(GPT_SUCCESS == GptUpdateKernelEntry(gpt, GPT_UPDATE_ENTRY_BAD)); + EXPECT(0 == GetEntrySuccessful(e + KERNEL_B)); + EXPECT(0 == GetEntryPriority(e + KERNEL_B)); + EXPECT(0 == GetEntryTries(e + KERNEL_B)); + + /* Another kernel with tries */ + EXPECT(GPT_SUCCESS == GptNextKernelEntry(gpt, &start, &size)); + EXPECT(KERNEL_X == gpt->current_kernel); + EXPECT(0 == GetEntrySuccessful(e + KERNEL_X)); + EXPECT(2 == GetEntryPriority(e + KERNEL_X)); + EXPECT(2 == GetEntryTries(e + KERNEL_X)); + /* Trying it uses up a try */ + EXPECT(GPT_SUCCESS == GptUpdateKernelEntry(gpt, GPT_UPDATE_ENTRY_TRY)); + EXPECT(0 == GetEntrySuccessful(e + KERNEL_X)); + EXPECT(2 == GetEntryPriority(e + KERNEL_X)); + EXPECT(1 == GetEntryTries(e + KERNEL_X)); + EXPECT(0 == GetEntrySuccessful(e2 + KERNEL_X)); + EXPECT(2 == GetEntryPriority(e2 + KERNEL_X)); + EXPECT(1 == GetEntryTries(e2 + KERNEL_X)); + /* Trying it again marks it inactive */ + EXPECT(GPT_SUCCESS == GptUpdateKernelEntry(gpt, GPT_UPDATE_ENTRY_TRY)); + EXPECT(0 == GetEntrySuccessful(e + KERNEL_X)); + EXPECT(0 == GetEntryPriority(e + KERNEL_X)); + EXPECT(0 == GetEntryTries(e + KERNEL_X)); + + return TEST_OK; +} + + /* Given an invalid kernel type, and expect GptUpdateKernelEntry() returns * GPT_ERROR_INVALID_UPDATE_TYPE. */ -int UpdateInvalidKernelTypeTest() { - GptData *gpt; +static int UpdateInvalidKernelTypeTest() { + GptData* gpt = GetEmptyGptData(); - gpt = GetEmptyGptData(); BuildTestGptData(gpt); gpt->current_kernel = 0; /* anything, but not CGPT_KERNEL_ENTRY_NOT_FOUND */ EXPECT(GPT_ERROR_INVALID_UPDATE_TYPE == @@ -1069,63 +1079,6 @@ int UpdateInvalidKernelTypeTest() { return TEST_OK; } -/* A normal boot case: - * GptInit() - * GptNextKernelEntry() - * GptUpdateKernelEntry() - */ -int NormalBootCase() { - GptData *gpt; - GptEntry *entries; - uint64_t start_sector, size; - - gpt = GetEmptyGptData(); - entries = (GptEntry*)gpt->primary_entries; - BuildTestGptData(gpt); - - EXPECT(GPT_SUCCESS == GptInit(gpt)); - EXPECT(GPT_SUCCESS == GptNextKernelEntry(gpt, &start_sector, &size)); - EXPECT(start_sector == 34); /* Kernel A, see top of this file. */ - EXPECT(size == 100); - - EXPECT(GPT_SUCCESS == GptUpdateKernelEntry(gpt, GPT_UPDATE_ENTRY_TRY)); - EXPECT(((entries[KERNEL_A].attributes & CGPT_ATTRIBUTE_TRIES_MASK) >> - CGPT_ATTRIBUTE_TRIES_OFFSET) == 1); - - return TEST_OK; -} - -/* Higher priority kernel should boot first. - * KERNEL_A is low priority - * KERNEL_B is high priority. - * We expect KERNEL_B is selected in first run, and then KERNEL_A. - * We also expect the GptNextKernelEntry() wraps back to KERNEL_B if it's called - * after twice. - */ -int HigherPriorityTest() { - GptData *gpt; - GptEntry *entries; - - gpt = GetEmptyGptData(); - entries = (GptEntry*)gpt->primary_entries; - BuildTestGptData(gpt); - - SetPriority(gpt, PRIMARY, KERNEL_A, 0); - SetPriority(gpt, PRIMARY, KERNEL_B, 1); - RefreshCrc32(gpt); - - EXPECT(GPT_SUCCESS == GptInit(gpt)); - EXPECT(GPT_SUCCESS == GptNextKernelEntry(gpt, NULL, NULL)); - EXPECT(KERNEL_B == gpt->current_kernel); - - EXPECT(GPT_SUCCESS == GptNextKernelEntry(gpt, NULL, NULL)); - EXPECT(KERNEL_A == gpt->current_kernel); - - EXPECT(GPT_SUCCESS == GptNextKernelEntry(gpt, NULL, NULL)); - EXPECT(KERNEL_B == gpt->current_kernel); - - return TEST_OK; -} int main(int argc, char *argv[]) { int i; @@ -1137,31 +1090,28 @@ int main(int argc, char *argv[]) { } test_cases[] = { { TEST_CASE(TestBuildTestGptData), }, { TEST_CASE(ParameterTests), }, + { TEST_CASE(HeaderCrcTest), }, { TEST_CASE(SignatureTest), }, { TEST_CASE(RevisionTest), }, { TEST_CASE(SizeTest), }, + { TEST_CASE(CrcFieldTest), }, { TEST_CASE(ReservedFieldsTest), }, - { TEST_CASE(MyLbaTest), }, { TEST_CASE(SizeOfPartitionEntryTest), }, { TEST_CASE(NumberOfPartitionEntriesTest), }, - { TEST_CASE(PartitionEntryLbaTest), }, + { TEST_CASE(MyLbaTest), }, { TEST_CASE(FirstUsableLbaAndLastUsableLbaTest), }, - { TEST_CASE(HeaderCrcTest), }, { TEST_CASE(EntriesCrcTest), }, - { TEST_CASE(IdenticalEntriesTest), }, - { TEST_CASE(SynonymousHeaderTest), }, { TEST_CASE(ValidEntryTest), }, { TEST_CASE(OverlappedPartitionTest), }, - { TEST_CASE(CorruptCombinationTest), }, - { TEST_CASE(TestQuickSortFixed), }, - { TEST_CASE(TestQuickSortRandom), }, + { TEST_CASE(SanityCheckTest), }, { TEST_CASE(NoValidKernelEntryTest), }, - { TEST_CASE(CombinationalNextKernelEntryTest), }, - { TEST_CASE(IncreaseTriesTest), }, - { TEST_CASE(MarkBadKernelEntryTest), }, + { TEST_CASE(EntryAttributeGetSetTest), }, + { TEST_CASE(EntryTypeTest), }, + { TEST_CASE(GetNextNormalTest), }, + { TEST_CASE(GetNextPrioTest), }, + { TEST_CASE(GetNextTriesTest), }, + { TEST_CASE(GptUpdateTest), }, { TEST_CASE(UpdateInvalidKernelTypeTest), }, - { TEST_CASE(NormalBootCase), }, - { TEST_CASE(HigherPriorityTest), }, { TEST_CASE(TestCrc32TestVectors), }, }; diff --git a/tests/quick_sort_test.c b/tests/quick_sort_test.c deleted file mode 100644 index d63d08b72c..0000000000 --- a/tests/quick_sort_test.c +++ /dev/null @@ -1,137 +0,0 @@ -/* 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 "quick_sort_test.h" -#include "cgptlib_test.h" -#include "quick_sort.h" -#include "utility.h" - -#define MAX_NUMBER_OF_TEST_ELEMENTS 16 - -/* callback function for QuickSort. - * To get ascent results, this function returns 1 if a < b. - * Returns 0 if a >= b. */ -int ascent_compare(const void *a_, const void *b_) { - const int *a = a_; - const int *b = b_; - if (*a < *b) return 1; - return 0; -} - -/* Used to verify if an array is sorted as ascent which means - * 'a' (previous element) is smaller than 'b' (back element). - * Returns 1 for ture, 0 for false. */ -int ascent_verify(const int a, const int b) { - return (a <= b) ? 1 : 0; -} - -/* callback function for QuickSort. - * To get descent results, this function returns 1 if a > b. - * Returns 0 if a <= b. */ -int descent_compare(const void *a_, const void *b_) { - const int *a = a_; - const int *b = b_; - if (*a > *b) return 1; - return 0; -} - -/* Used to verify if an array is sorted as descent which means - * 'a' (previous element) is lager than 'b' (back element). - * Returns 1 for ture, 0 for false. */ -int descent_verify(const int a, const int b) { - return (a >= b) ? 1 : 0; -} - -/* We provide 2 ways to sort the array. One for ascent, and another for descent. - */ -struct { - int (*compare)(const void *a, const void *b); - int (*verify)(const int a, const int b); -} directions[] = { - { ascent_compare, ascent_verify, }, - { descent_compare, descent_verify, }, -}; - -/* Here are the fixed patterns to test. Especially those corner cases that - * random test cannot easily reproduce. */ -struct { - int number; /* number of integers saved in array */ - int unsorted[MAX_NUMBER_OF_TEST_ELEMENTS]; -} test_data[] = { - {0, {}, }, - {1, {0, }, }, - {2, {1, 1,}, }, - {2, {1, 2,}, }, - {2, {2, 1,}, }, - {3, {1, 3, 2}, }, - {3, {2, 1, 3}, }, - {4, {1, 1, 3, 2}, }, - {4, {3, 1, 2, 2}, }, - {4, {1, 3, 3, 2}, }, - {5, {1, 2, 3, 4, 5}, }, - {5, {5, 5, 5, 3, 3}, }, - {5, {5, 1, 3, 2, 4}, }, - {5, {4, 5, 2, 3, 1}, }, - {6, {5, 4, 3, 2, 1, 6}, }, - {7, {5, 4, 3, 2, 1, 6, 7}, }, - {7, {2, 5, 4, 6, 7, 1, 3}, }, - {7, {7, 6, 1, 5, 3, 4, 2}, }, -}; - -int TestQuickSortFixed() { - int data; - int dir; - int sorted[MAX_NUMBER_OF_TEST_ELEMENTS]; - - for (dir = 0; dir < ARRAY_SIZE(directions); ++dir) { - for (data = 0; data < ARRAY_SIZE(test_data); ++data) { - int i; - for (i = 0; i < test_data[data].number; ++i) - sorted[i] = test_data[data].unsorted[i]; - - QuickSort(sorted, test_data[data].number, sizeof(int), - directions[dir].compare); - - for (i = 0; i < test_data[data].number - 1; ++i) - EXPECT(directions[dir].verify(sorted[i], sorted[i + 1])); - } - } - - return TEST_OK; -} - -/* Random test. We don't really need a truely random test. A pseudo-random - * pattern with large 'try_num' is good enough to test. - */ -static uint32_t Random() { - static uint32_t seed = 0x600613; /* 'GOOGLE' :-) */ - return (seed = seed * 701 + 179); -} - -int TestQuickSortRandom() { - int try_num; - int i, dir; - - for (dir = 0; dir < ARRAY_SIZE(directions); ++dir) { - for (try_num = 100000; try_num > 0; --try_num) { - int number_of_elements; - int *p; - - number_of_elements = Random() % 181; - p = Malloc(sizeof(int) * number_of_elements); - for (i = 0; i < number_of_elements; ++i) - p[i] = Random() % 173; - - QuickSort(p, number_of_elements, sizeof(int), directions[dir].compare); - - for (i = 0; i < number_of_elements - 1; ++i) - EXPECT(directions[dir].verify(p[i], p[i + 1])); - - Free(p); - } - } - - return TEST_OK; -} diff --git a/tests/quick_sort_test.h b/tests/quick_sort_test.h deleted file mode 100644 index 8b40687993..0000000000 --- a/tests/quick_sort_test.h +++ /dev/null @@ -1,11 +0,0 @@ -/* 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. - */ -#ifndef VBOOT_REFERENCE_QUICK_SORT_TEST_H_ -#define VBOOT_REFERENCE_QUICK_SORT_TEST_H_ - -int TestQuickSortFixed(); -int TestQuickSortRandom(); - -#endif /* VBOOT_REFERENCE_QUICK_SORT_TEST_H_ */ diff --git a/utility/Makefile b/utility/Makefile index 8406e0e6bc..23a6e1719a 100644 --- a/utility/Makefile +++ b/utility/Makefile @@ -26,6 +26,7 @@ TARGET_BINS = dumpRSAPublicKey \ firmware_utility \ gbb_utility \ kernel_utility \ + load_kernel_test \ signature_digest_utility \ verify_data @@ -52,6 +53,9 @@ kernel_utility: kernel_utility.cc $(LIBS) $(FWLIB) $(CXX) $(CFLAGS) $(INCLUDES) -ggdb -D__STDC_LIMIT_MACROS $< \ -o $@ $(LIBS) $(FWLIB) -lcrypto +load_kernel_test: load_kernel_test.c $(LIBS) $(FWLIB) + $(CC) $(CFLAGS) $(INCLUDES) $< -o $@ $(LIBS) $(FWLIB) -lcrypto + signature_digest_utility: signature_digest_utility.c $(LIBS) $(FWLIB) $(CC) $(CFLAGS) $(INCLUDES) $< -o $@ $(LIBS) $(FWLIB) -lcrypto diff --git a/utility/cgpt/Makefile b/utility/cgpt/Makefile index a6f7e79ca0..78de27f773 100644 --- a/utility/cgpt/Makefile +++ b/utility/cgpt/Makefile @@ -1,9 +1,12 @@ + # Copyright (c) 2009 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. TOP ?= ../../ CC ?= cc +# Include /usr/include from inside the chroot, so that we get a version +# of endian.h which contains endian-conversion macros - htole32(), etc. INCLUDES += -I$(TOP)/common/include -I$(TOP)/../../../chroot/usr/include/ CFLAGS += -Wall -Werror -ggdb LIBS += $(FWLIB) @@ -13,7 +16,7 @@ DESTDIR ?= /opt/bin all: cgpt cgpt: cgpt.o cgpt_add_modify_delete.o cgpt_attribute.o cgpt_dev.o \ - cgpt_options.o cgpt_repair.o cgpt_show.o $(LIBS) + cgpt_options.o cgpt_repair.o cgpt_show.o cgpt_tofix.o $(LIBS) $(CC) -o cgpt $(CFLAGS) $^ .c.o: $(INCLUDES) diff --git a/utility/cgpt/cgpt.c b/utility/cgpt/cgpt.c index eaec6f8902..7cfc4c12fb 100644 --- a/utility/cgpt/cgpt.c +++ b/utility/cgpt/cgpt.c @@ -14,6 +14,7 @@ #define __USE_FILE_OFFSET64 #define _LARGEFILE64_SOURCE #include "cgpt.h" +#include "cgpt_tofix.h" #include #include #include diff --git a/utility/cgpt/cgpt.h b/utility/cgpt/cgpt.h index 3bc1f82fd0..a6759903c5 100644 --- a/utility/cgpt/cgpt.h +++ b/utility/cgpt/cgpt.h @@ -10,6 +10,7 @@ #include #include #include "cgptlib.h" +#include "gpt.h" enum { CGPT_OK = 0, diff --git a/utility/cgpt/cgpt_add_modify_delete.c b/utility/cgpt/cgpt_add_modify_delete.c index 38a568a602..c5fbe94e7b 100644 --- a/utility/cgpt/cgpt_add_modify_delete.c +++ b/utility/cgpt/cgpt_add_modify_delete.c @@ -9,6 +9,7 @@ #include #include "cgpt.h" #include "cgptlib_internal.h" +#include "cgpt_tofix.h" #include "utility.h" static struct number_range diff --git a/utility/cgpt/cgpt_attribute.c b/utility/cgpt/cgpt_attribute.c index a54d7bab52..70c89931ee 100644 --- a/utility/cgpt/cgpt_attribute.c +++ b/utility/cgpt/cgpt_attribute.c @@ -9,6 +9,7 @@ #include #include "cgpt.h" #include "cgptlib_internal.h" +#include "cgpt_tofix.h" #include "utility.h" static struct number_range @@ -126,8 +127,6 @@ int CgptAttribute(int argc, char *argv[]) { } } - if (bad != NOT_INITED) - SetBad(&drive.gpt, PRIMARY, partition, bad); if (successful != NOT_INITED) SetSuccessful(&drive.gpt, PRIMARY, partition, successful); if (tries != NOT_INITED) @@ -136,8 +135,11 @@ int CgptAttribute(int argc, char *argv[]) { SetPriority(&drive.gpt, PRIMARY, partition, priority); /* Claims primary is good, then secondary will be overwritten. */ + /* TODO: rspangler broke this during cgptlib refactoring; need to + * update this to match new internal APIs. */ RepairEntries(&drive.gpt, MASK_PRIMARY); RepairHeader(&drive.gpt, MASK_PRIMARY); + /* Forces headers and entries are modified so that CRC32 will be re-calculated * and headers and entries will be updated to drive. */ drive.gpt.modified |= (GPT_MODIFIED_HEADER1 | GPT_MODIFIED_ENTRIES1 | diff --git a/utility/cgpt/cgpt_show.c b/utility/cgpt/cgpt_show.c index 703e6dffad..91bf1ef5e5 100644 --- a/utility/cgpt/cgpt_show.c +++ b/utility/cgpt/cgpt_show.c @@ -9,6 +9,7 @@ #include #include "cgpt.h" #include "cgptlib_internal.h" +#include "cgpt_tofix.h" #include "utility.h" /* Integers to store parsed argument. */ diff --git a/utility/cgpt/cgpt_tofix.c b/utility/cgpt/cgpt_tofix.c new file mode 100644 index 0000000000..50aed529d9 --- /dev/null +++ b/utility/cgpt/cgpt_tofix.c @@ -0,0 +1,269 @@ +/* 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. + * + * Functions to fix, after cgptlib cleanup. + */ +#include +#include +#include +#include "cgpt.h" +#include "cgptlib_internal.h" +#include "cgpt_tofix.h" +#include "crc32.h" +#include "utility.h" + +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]; +} + + +/* 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); + } +} + +/* 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); + + // There is no bad attribute + assert(0); +} + +int GetBad(GptData *gpt, int secondary, int entry_index) { + GptEntry *entry; + entry = GetEntry(gpt, secondary, entry_index); + + // There is no bad attribute + assert(0); + return 0; +} + +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; +} + +/* 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; +} + +/* 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; +} + +/* 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; + + 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; +} + +/* TODO: HORRIBLY broken non-implemented functions. These will be + * fixed as part of a second stage of refactoring to use the new + * cgptlib_internal functions. */ +uint32_t CheckOverlappedPartition(GptData *gpt) { + return 1; +} + +uint32_t CheckValidEntries(GptData *gpt) { + return 1; +} + +int NonZeroGuid(const Guid *guid) { + return 1; +} diff --git a/utility/cgpt/cgpt_tofix.h b/utility/cgpt/cgpt_tofix.h new file mode 100644 index 0000000000..8e645df4eb --- /dev/null +++ b/utility/cgpt/cgpt_tofix.h @@ -0,0 +1,40 @@ +/* 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. + */ + +#ifndef CGPT_TOFIX_H_ +#define CGPT_TOFIX_H_ + +#include +#include "cgptlib.h" +#include "cgptlib_internal.h" +#include "gpt.h" + +/* TODO: This is stuff copied out of cgptlib. cgptlib doesn't need it anymore,but currently the cgpt tool does. */ + +const char *GptError(int errno); + +int IsSynonymous(const GptHeader* a, const GptHeader* b); +uint8_t RepairEntries(GptData *gpt, const uint32_t valid_entries); +uint8_t RepairHeader(GptData *gpt, const uint32_t valid_headers); +void UpdateCrc(GptData *gpt); +int NonZeroGuid(const Guid *guid); +uint32_t CheckValidEntries(GptData *gpt); +uint32_t CheckOverlappedPartition(GptData *gpt); + +GptEntry *GetEntry(GptData *gpt, int secondary, int entry_index); +void SetPriority(GptData *gpt, int secondary, int entry_index, int priority); +int GetPriority(GptData *gpt, int secondary, int entry_index); +void SetBad(GptData *gpt, int secondary, int entry_index, int bad); +int GetBad(GptData *gpt, int secondary, int entry_index); +void SetTries(GptData *gpt, int secondary, int entry_index, int tries); +int GetTries(GptData *gpt, int secondary, int entry_index); +void SetSuccessful(GptData *gpt, int secondary, int entry_index, int success); +int GetSuccessful(GptData *gpt, int secondary, int entry_index); + +/* Get number of entries value in primary header */ +uint32_t GetNumberOfEntries(const GptData *gpt); + + +#endif /* CGPT_TOFIX_H_ */ diff --git a/utility/load_kernel_test.c b/utility/load_kernel_test.c new file mode 100644 index 0000000000..ad3983570a --- /dev/null +++ b/utility/load_kernel_test.c @@ -0,0 +1,125 @@ +/* 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. + */ + +/* Routines for verifying a file's signature. Useful in testing the core + * RSA verification implementation. + */ + +#include +#include +#include +#include + +#include "load_kernel_fw.h" +#include "boot_device.h" +#include "rollback_index.h" +#include "utility.h" + +/* ANSI Color coding sequences. */ +#define COL_GREEN "\e[1;32m" +#define COL_RED "\e[0;31m" +#define COL_STOP "\e[m" + + +#define LBA_BYTES 512 +#define KERNEL_BUFFER_SIZE 0x600000 + +/* Global variables for stub functions */ +static LoadKernelParams lkp; +static FILE *image_file = NULL; + + +/* Boot device stub implementations to read from the image file */ +int BootDeviceReadLBA(uint64_t lba_start, uint64_t lba_count, void *buffer) { + printf("Read(%ld, %ld)\n", lba_start, lba_count); + + if (lba_start > lkp.ending_lba || + lba_start + lba_count - 1 > lkp.ending_lba) { + fprintf(stderr, "Read overrun: %ld + %ld > %ld\n", + lba_start, lba_count, lkp.ending_lba); + return 1; + } + + fseek(image_file, lba_start * lkp.bytes_per_lba, SEEK_SET); + if (1 != fread(buffer, lba_count * lkp.bytes_per_lba, 1, image_file)) { + fprintf(stderr, "Read error."); + return 1; + } + return 0; +} + + +int BootDeviceWriteLBA(uint64_t lba_start, uint64_t lba_count, + const void *buffer) { + printf("Write(%ld, %ld)\n", lba_start, lba_count); + + if (lba_start > lkp.ending_lba || + lba_start + lba_count - 1 > lkp.ending_lba) { + fprintf(stderr, "Read overrun: %ld + %ld > %ld\n", + lba_start, lba_count, lkp.ending_lba); + return 1; + } + + /* TODO: enable writes, once we're sure it won't trash our example file */ + return 0; + + fseek(image_file, lba_start * lkp.bytes_per_lba, SEEK_SET); + if (1 != fwrite(buffer, lba_count * lkp.bytes_per_lba, 1, image_file)) { + fprintf(stderr, "Read error."); + return 1; + } + return 0; +} + + +/* Main routine */ +int main(int argc, char* argv[]) { + + const char *image_name; + int rv; + + Memset(&lkp, 0, sizeof(LoadKernelParams)); + lkp.bytes_per_lba = LBA_BYTES; + + /* Read command line parameters */ + if (2 > argc) { + fprintf(stderr, "usage: %s \n", argv[0]); + return 1; + } + image_name = argv[1]; + printf("Reading from image: %s\n", image_name); + + /* TODO: Read header signing key blob */ + lkp.header_sign_key_blob = NULL; + + /* Get image size */ + image_file = fopen(image_name, "rb"); + if (!image_file) { + fprintf(stderr, "Unable to open image file %s\n", image_name); + return 1; + } + fseek(image_file, 0, SEEK_END); + lkp.ending_lba = (ftell(image_file) / LBA_BYTES) - 1; + rewind(image_file); + printf("Ending LBA: %ld\n", lkp.ending_lba); + + /* Allocate a buffer for the kernel */ + lkp.kernel_buffer = Malloc(KERNEL_BUFFER_SIZE); + if(!lkp.kernel_buffer) { + fprintf(stderr, "Unable to allocate kernel buffer.\n"); + return 1; + } + + /* TODO: Option for boot mode */ + lkp.boot_mode = BOOT_MODE_NORMAL; + + /* Call LoadKernel() */ + rv = LoadKernel(&lkp); + printf("LoadKernel() returned %d\n", rv); + + fclose(image_file); + Free(lkp.kernel_buffer); + return 0; +} diff --git a/vboot_firmware/lib/cgptlib/cgptlib.c b/vboot_firmware/lib/cgptlib/cgptlib.c index 334c578967..2cf606204d 100644 --- a/vboot_firmware/lib/cgptlib/cgptlib.c +++ b/vboot_firmware/lib/cgptlib/cgptlib.c @@ -4,806 +4,142 @@ */ #include "cgptlib.h" -#include #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<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 < (GPT_PMBR_SECTOR + - GPT_HEADER_SECTOR * 2 + - GPT_ENTRIES_SECTORS * 2)) - return GPT_ERROR_INVALID_SECTOR_NUMBER; - - 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 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); - } - } - } - 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; - } - } - 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; - - 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; + gpt->current_priority = 999; + retval = GptSanityCheck(gpt); + if (GPT_SUCCESS != retval) + return retval; + + GptRepair(gpt); 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; - } +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; - 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 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; + } } + } - if (scan == end) break; - scan = (scan + 1) % header->number_of_entries; - } while (1); + /* 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; + } + } - if (gpt->current_kernel == CGPT_KERNEL_ENTRY_NOT_FOUND) + /* 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 (CGPT_KERNEL_ENTRY_NOT_FOUND == new_kernel) return GPT_ERROR_NO_VALID_KERNEL; - entry = GetEntry(gpt, PRIMARY, gpt->current_kernel); - assert(entry->starting_lba <= entry->ending_lba); - - if (start_sector) *start_sector = entry->starting_lba; - if (size) *size = entry->ending_lba - entry->starting_lba + 1; - + e = entries + new_kernel; + *start_sector = e->starting_lba; + *size = e->ending_lba - e->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))); +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_INVALID_UPDATE_TYPE; + if (!IsKernelEntry(e)) + return GPT_ERROR_INVALID_UPDATE_TYPE; - /* 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; } diff --git a/vboot_firmware/lib/cgptlib/cgptlib_internal.c b/vboot_firmware/lib/cgptlib/cgptlib_internal.c new file mode 100644 index 0000000000..3d2dc0b305 --- /dev/null +++ b/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; +} diff --git a/vboot_firmware/lib/cgptlib/include/cgptlib.h b/vboot_firmware/lib/cgptlib/include/cgptlib.h index 7195ee7a66..acf91dc088 100644 --- a/vboot_firmware/lib/cgptlib/include/cgptlib.h +++ b/vboot_firmware/lib/cgptlib/include/cgptlib.h @@ -6,7 +6,6 @@ #ifndef VBOOT_REFERENCE_CGPTLIB_H_ #define VBOOT_REFERENCE_CGPTLIB_H_ -#include "gpt.h" #include enum { @@ -19,8 +18,6 @@ enum { GPT_ERROR_INVALID_UPDATE_TYPE, }; -const char *GptError(int errno); - /* Bit masks for GptData.modified field. */ #define GPT_MODIFIED_HEADER1 0x01 #define GPT_MODIFIED_HEADER2 0x02 @@ -40,33 +37,6 @@ enum { * invalid. */ }; -/* Defines ChromeOS-specific limitation on GPT */ -#define MIN_SIZE_OF_HEADER 92 -#define MAX_SIZE_OF_HEADER 512 -#define MIN_SIZE_OF_ENTRY 128 -#define MAX_SIZE_OF_ENTRY 512 -#define SIZE_OF_ENTRY_MULTIPLE 8 -#define MIN_NUMBER_OF_ENTRIES 32 -#define MAX_NUMBER_OF_ENTRIES 512 -#define TOTAL_ENTRIES_SIZE 16384 /* usual case is 128 bytes * 128 entries */ - -/* Defines GPT sizes */ -#define GPT_PMBR_SECTOR 1 /* size (in sectors) of PMBR */ -#define GPT_HEADER_SECTOR 1 -#define GPT_ENTRIES_SECTORS 32 /* assume sector size if 512 bytes, then - * (TOTAL_ENTRIES_SIZE / 512) = 32 */ - -/* alias name of index in internal array for primary and secondary header and - * entries. */ -enum { - PRIMARY = 0, - SECONDARY = 1, - MASK_NONE = 0, - MASK_PRIMARY = 1, - MASK_SECONDARY = 2, - MASK_BOTH = 3, -}; - typedef struct { /* Fill in the following fields before calling GptInit() */ uint8_t *primary_header; /* GPT primary header, from sector 1 of disk @@ -91,9 +61,10 @@ typedef struct { /* Internal variables */ uint32_t valid_headers, valid_entries; + int current_priority; } GptData; -int GptInit(GptData *gpt); +int GptInit(GptData* gpt); /* Initializes the GPT data structure's internal state. The following fields * must be filled before calling this function: * @@ -116,7 +87,7 @@ int GptInit(GptData *gpt); * GPT_ERROR_INVALID_SECTOR_NUMBER, number of sectors in drive is invalid (too * small) */ -int GptNextKernelEntry(GptData *gpt, uint64_t *start_sector, uint64_t *size); +int GptNextKernelEntry(GptData* gpt, uint64_t* start_sector, uint64_t* size); /* Provides the location of the next kernel partition, in order of decreasing * priority. On return the start_sector parameter contains the LBA sector * for the start of the kernel partition, and the size parameter contains the @@ -126,7 +97,7 @@ int GptNextKernelEntry(GptData *gpt, uint64_t *start_sector, uint64_t *size); * Returns GPT_SUCCESS if successful, else * GPT_ERROR_NO_VALID_KERNEL, no avaliable kernel, enters recovery mode */ -int GptUpdateKernelEntry(GptData *gpt, uint32_t update_type); +int GptUpdateKernelEntry(GptData* gpt, uint32_t update_type); /* Updates the kernel entry with the specified index, using the specified type * of update (GPT_UPDATE_ENTRY_*). * diff --git a/vboot_firmware/lib/cgptlib/include/cgptlib_internal.h b/vboot_firmware/lib/cgptlib/include/cgptlib_internal.h index b4b63c535d..195f4d6c33 100644 --- a/vboot_firmware/lib/cgptlib/include/cgptlib_internal.h +++ b/vboot_firmware/lib/cgptlib/include/cgptlib_internal.h @@ -8,46 +8,7 @@ #include #include "cgptlib.h" - -int CheckParameters(GptData *gpt); -uint32_t CheckHeaderSignature(GptData *gpt); -uint32_t CheckRevision(GptData *gpt); -uint32_t CheckSize(GptData *gpt); -uint32_t CheckReservedFields(GptData *gpt); -uint32_t CheckMyLba(GptData *gpt); -uint32_t CheckSizeOfPartitionEntry(GptData *gpt); -uint32_t CheckNumberOfEntries(GptData *gpt); -uint32_t CheckEntriesLba(GptData *gpt); -uint32_t CheckValidUsableLbas(GptData *gpt); -uint32_t CheckHeaderCrc(GptData *gpt); -uint32_t CheckEntriesCrc(GptData *gpt); -int NonZeroGuid(const Guid *guid); -uint32_t CheckValidEntries(GptData *gpt); -typedef struct { - uint64_t starting; - uint64_t ending; -} pair_t; -int OverlappedEntries(GptEntry *entries, uint32_t number_of_entries); -uint32_t CheckOverlappedPartition(GptData *gpt); -int IsSynonymous(const GptHeader* a, const GptHeader* b); -uint8_t RepairEntries(GptData *gpt, const uint32_t valid_entries); -uint8_t RepairHeader(GptData *gpt, const uint32_t valid_headers); -void UpdateCrc(GptData *gpt); -int GptSanityCheck(GptData *gpt); -void GptRepair(GptData *gpt); - -GptEntry *GetEntry(GptData *gpt, int secondary, int entry_index); -void SetPriority(GptData *gpt, int secondary, int entry_index, int priority); -int GetPriority(GptData *gpt, int secondary, int entry_index); -void SetBad(GptData *gpt, int secondary, int entry_index, int bad); -int GetBad(GptData *gpt, int secondary, int entry_index); -void SetTries(GptData *gpt, int secondary, int entry_index, int tries); -int GetTries(GptData *gpt, int secondary, int entry_index); -void SetSuccessful(GptData *gpt, int secondary, int entry_index, int success); -int GetSuccessful(GptData *gpt, int secondary, int entry_index); - -/* Get number of entries value in primary header */ -uint32_t GetNumberOfEntries(const GptData *gpt); +#include "gpt.h" /* If gpt->current_kernel is this value, means either: * 1. an initial value before scanning GPT entries, @@ -61,17 +22,11 @@ uint32_t GetNumberOfEntries(const GptData *gpt); * 62 -- hidden * 60 -- read-only * : - * 57 -- bad kernel entry * 56 -- success * 55,52 -- tries * 51,48 -- priority * 0 -- system partition */ -#define CGPT_ATTRIBUTE_BAD_OFFSET 57 -#define CGPT_ATTRIBUTE_MAX_BAD (1ULL) -#define CGPT_ATTRIBUTE_BAD_MASK (CGPT_ATTRIBUTE_MAX_BAD << \ - CGPT_ATTRIBUTE_BAD_OFFSET) - #define CGPT_ATTRIBUTE_SUCCESSFUL_OFFSET 56 #define CGPT_ATTRIBUTE_MAX_SUCCESSFUL (1ULL) #define CGPT_ATTRIBUTE_SUCCESSFUL_MASK (CGPT_ATTRIBUTE_MAX_SUCCESSFUL << \ @@ -87,4 +42,75 @@ uint32_t GetNumberOfEntries(const GptData *gpt); #define CGPT_ATTRIBUTE_PRIORITY_MASK (CGPT_ATTRIBUTE_MAX_PRIORITY << \ CGPT_ATTRIBUTE_PRIORITY_OFFSET) +/* Defines ChromeOS-specific limitation on GPT */ +/* TODO: Move these to cgptlib_internal.h */ +#define MIN_SIZE_OF_HEADER 92 +#define MAX_SIZE_OF_HEADER 512 +#define MIN_SIZE_OF_ENTRY 128 +#define MAX_SIZE_OF_ENTRY 512 +#define SIZE_OF_ENTRY_MULTIPLE 8 +#define MIN_NUMBER_OF_ENTRIES 32 +#define MAX_NUMBER_OF_ENTRIES 512 +#define TOTAL_ENTRIES_SIZE 16384 /* usual case is 128 bytes * 128 entries */ + +/* Defines GPT sizes */ +#define GPT_PMBR_SECTOR 1 /* size (in sectors) of PMBR */ +#define GPT_HEADER_SECTOR 1 +#define GPT_ENTRIES_SECTORS 32 /* assume sector size if 512 bytes, then + * (TOTAL_ENTRIES_SIZE / 512) = 32 */ + +/* alias name of index in internal array for primary and secondary header and + * entries. */ +enum { + PRIMARY = 0, + SECONDARY = 1, + MASK_NONE = 0, + MASK_PRIMARY = 1, + MASK_SECONDARY = 2, + MASK_BOTH = 3, +}; + +/* Verify GptData parameters are sane. */ +int CheckParameters(GptData* gpt); + +/* Check header fields. + * + * Returns 0 if header is valid, 1 if invalid. */ +int CheckHeader(GptHeader* h, int is_secondary, uint64_t drive_sectors); + +/* Calculate and return the header CRC. */ +uint32_t HeaderCrc(GptHeader* h); + +/* Check entries. + * + * Returns 0 if entries are valid, 1 if invalid. */ +int CheckEntries(GptEntry* entries, GptHeader* h, uint64_t drive_sectors); + +/* Check GptData, headers, entries. + * + * If successful, sets gpt->valid_headers and gpt->valid_entries and returns + * GPT_SUCCESS. + * + * On error, returns a GPT_ERROR_* return code. */ +int GptSanityCheck(GptData* gpt); + +/* Repairs GPT data by copying from one set of valid headers/entries to the + * other. Assumes GptSanityCheck() has been run to determine which headers + * and/or entries are already valid. */ +void GptRepair(GptData* gpt); + +/* Getters and setters for partition attribute fields. */ +int GetEntrySuccessful(const GptEntry* e); +int GetEntryPriority(const GptEntry* e); +int GetEntryTries(const GptEntry* e); +void SetEntrySuccessful(GptEntry* e, int successful); +void SetEntryPriority(GptEntry* e, int priority); +void SetEntryTries(GptEntry* e, int tries); + +/* Return 1 if the entry is unused, 0 if it is used. */ +int IsUnusedEntry(const GptEntry* e); + +/* Returns 1 if the entry is a Chrome OS kernel partition, else 0. */ +int IsKernelEntry(const GptEntry* e); + #endif /* VBOOT_REFERENCE_CGPTLIB_INTERNAL_H_ */ diff --git a/vboot_firmware/lib/cgptlib/include/gpt.h b/vboot_firmware/lib/cgptlib/include/gpt.h index 5d49b17b73..79d19f7f17 100644 --- a/vboot_firmware/lib/cgptlib/include/gpt.h +++ b/vboot_firmware/lib/cgptlib/include/gpt.h @@ -16,16 +16,33 @@ #define GPT_HEADER_SIGNATURE_SIZE sizeof(GPT_HEADER_SIGNATURE) #define GPT_HEADER_REVISION 0x00010000 -#define GPT_ENT_TYPE_EFI \ - {{{0xc12a7328,0xf81f,0x11d2,0xba,0x4b,{0x00,0xa0,0xc9,0x3e,0xc9,0x3b}}}} +/* The first 3 numbers should be stored in network-endian format + * according to the GUID RFC. The UEFI spec appendix A claims they + * should be stored in little-endian format. But they need to be + * _displayed_ in network-endian format, which is also how they're + * documented in the specs. + * + * Since what we have here are little-endian constants, they're + * byte-swapped from the normal display order. */ #define GPT_ENT_TYPE_UNUSED \ {{{0x00000000,0x0000,0x0000,0x00,0x00,{0x00,0x00,0x00,0x00,0x00,0x00}}}} +/* Hey, that one's right regardless of endianness... */ + +#define GPT_ENT_TYPE_EFI \ + {{{0x28732ac1,0x1ff8,0xd211,0xba,0x4b,{0x00,0xa0,0xc9,0x3e,0xc9,0x3b}}}} +/* c12a7328-f81f-11d2-ba4b-00a0c93ec93b */ + #define GPT_ENT_TYPE_CHROMEOS_KERNEL \ - {{{0xfe3a2a5d,0x4f32,0x41a7,0xb7,0x25,{0xac,0xcc,0x32,0x85,0xa3,0x09}}}} + {{{0x5d2a3afe,0x324f,0xa741,0xb7,0x25,{0xac,0xcc,0x32,0x85,0xa3,0x09}}}} +/* FE3A2A5D-4F32-41A7-B725-ACCC3285A309 */ + #define GPT_ENT_TYPE_CHROMEOS_ROOTFS \ - {{{0x3cb8e202,0x3b7e,0x47dd,0x8a,0x3c,{0x7f,0xf2,0xa1,0x3c,0xfc,0xec}}}} + {{{0x02e2b83c,0x7e3b,0xdd47,0x8a,0x3c,{0x7f,0xf2,0xa1,0x3c,0xfc,0xec}}}} +/* 3CB8E202-3B7E-47DD-8A3C-7FF2A13CFCEC */ + #define GPT_ENT_TYPE_CHROMEOS_RESERVED \ - {{{0x2e0a753d,0x9e48,0x43b0,0x83,0x37,{0xb1,0x51,0x92,0xcb,0x1b,0x5e}}}} + {{{0x3d750a2e,0x489e,0xb043,0x83,0x37,{0xb1,0x51,0x92,0xcb,0x1b,0x5e}}}} +/* 2e0a753d-9e48-43b0-8337-b15192cb1b5e */ #define UUID_NODE_LEN 6 #define GUID_SIZE 16 @@ -69,7 +86,7 @@ typedef struct { uint32_t number_of_entries; uint32_t size_of_entry; uint32_t entries_crc32; - uint32_t padding; /* since header size must be a multiple of 8, pad here. */ + uint8_t padding[512 - 92]; /* Pad to end of sector */ } GptHeader; /* GPT partition entry defines the starting and ending LBAs of a partition. diff --git a/vboot_firmware/lib/cgptlib/include/quick_sort.h b/vboot_firmware/lib/cgptlib/include/quick_sort.h deleted file mode 100644 index 260be1af36..0000000000 --- a/vboot_firmware/lib/cgptlib/include/quick_sort.h +++ /dev/null @@ -1,23 +0,0 @@ -/* 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. - */ -#ifndef VBOOT_REFERENCE_QSORT_H_ -#define VBOOT_REFERENCE_QSORT_H_ - -#define MAX_QUICK_SORT_ELEMENT_SIZE 512 - -/* QuickSort() is an in-place, and unstable quick sort implementation. - * Given a compare function, this function sorts elements for you. - * 'elements' points to the base of un-sorted array. - * 'number_of_elements' indicates the number of elements in array. - * 'size' indicates the size (in bytes) of an element unit. - * The 'compare_function' should return true if 'a' precedes 'b'. - * - * NOTE: For performance issue, we reserve a static buffer for swap. - * So that the 'size' cannot exceed the buffer size - * (MAX_QUICK_SORT_ELEMENT_SIZE). */ -void QuickSort(void *elements, int number_of_elements, int size, - int (*compare_function)(const void *a, const void *b)); - -#endif /* VBOOT_REFERENCE_QSORT_H_ */ diff --git a/vboot_firmware/lib/cgptlib/quick_sort.c b/vboot_firmware/lib/cgptlib/quick_sort.c deleted file mode 100644 index 4af045de65..0000000000 --- a/vboot_firmware/lib/cgptlib/quick_sort.c +++ /dev/null @@ -1,59 +0,0 @@ -/* 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 "quick_sort.h" -#include -#include "utility.h" - -/* Make sure we won't overflow the buffer. */ -#define SAFE_SIZE(size) \ - ((size > MAX_QUICK_SORT_ELEMENT_SIZE) ? MAX_QUICK_SORT_ELEMENT_SIZE : size) - -/* Swap a and b. Since the type of 'a' and 'b' are unknown, caller must indicate - * 'size' bytes. */ -void Swap(void *a, void *b, int size) { - static uint8_t buffer[MAX_QUICK_SORT_ELEMENT_SIZE]; - Memcpy(buffer, a, SAFE_SIZE(size)); - Memcpy(a, b, SAFE_SIZE(size)); - Memcpy(b, buffer, SAFE_SIZE(size)); -} - -/* Given a pivot and left/right boundary, this function returns a new pivot, - * and ensure the elements in the left-side of pivot are preceding elements, - * and elements in the right-side of pivot are following elements. */ -static int -Partition(int (*compare_function)(const void *a, const void *b), - void *vp_elements, int size, int left, int right, int pivot) { - uint8_t *elements = vp_elements; - int i; - - Swap(&elements[right * size], &elements[pivot * size], size); - for (i = left; i < right; ++i) { - if (compare_function(&elements[i * size], &elements[right * size])) { - Swap(&elements[left * size], &elements[i * size], size); - ++left; - } - } - Swap(&elements[left * size], &elements[right * size], size); - return left; -} - -/* Given left and right boundary, this function returns a sorted elements in - * that range. */ -static void -RecursiveQuickSort(void *elements, int left, int right, int size, - int (*compare_function)(const void *a, const void *b)) { - int pivot; - - if (right <= left) return; - pivot = (left + right) / 2; - pivot = Partition(compare_function, elements, size, left, right, pivot); - RecursiveQuickSort(elements, left, pivot - 1, size, compare_function); - RecursiveQuickSort(elements, pivot + 1, right, size, compare_function); -} - -void QuickSort(void *elements, int number_of_elements, int size, - int (*compare_function)(const void *a, const void *b)) { - RecursiveQuickSort(elements, 0, number_of_elements - 1, size, compare_function); -} diff --git a/vboot_firmware/lib/load_kernel_fw.c b/vboot_firmware/lib/load_kernel_fw.c index 8ec440b599..dec92d199a 100644 --- a/vboot_firmware/lib/load_kernel_fw.c +++ b/vboot_firmware/lib/load_kernel_fw.c @@ -14,6 +14,46 @@ #include "rollback_index.h" #include "utility.h" +#define GPT_ENTRIES_SIZE 16384 /* Bytes to read for GPT entries */ + +// TODO: for testing +#include +#include "cgptlib_internal.h" + +/* TODO: Remove this terrible hack which fakes partition attributes + * for the kernel partitions so that GptNextKernelEntry() won't + * choke. */ +void FakePartitionAttributes(GptData* gpt) { + GptEntry* entries = (GptEntry*)gpt->primary_entries; + GptEntry* e; + int i; + printf("Hacking partition attributes...\n"); + printf("Note that GUIDs below have first 3 fields endian-swapped\n"); + + for (i = 0, e = entries; i < 12; i++, e++) { + + printf("%2d %08x %04x %04x %02x %02x %02x %02x %02x %02x %02x %02x\n", + i, + e->type.u.Uuid.time_low, + e->type.u.Uuid.time_mid, + e->type.u.Uuid.time_high_and_version, + e->type.u.Uuid.clock_seq_high_and_reserved, + e->type.u.Uuid.clock_seq_low, + e->type.u.Uuid.node[0], + e->type.u.Uuid.node[1], + e->type.u.Uuid.node[2], + e->type.u.Uuid.node[3], + e->type.u.Uuid.node[4], + e->type.u.Uuid.node[5] + ); + if (!IsKernelEntry(e)) + continue; + printf("Hacking attributes for kernel partition %d\n", i); + SetEntryPriority(e, 2); + SetEntrySuccessful(e, 1); + } +} + int AllocAndReadGptData(GptData *gptdata) { /* Allocates and reads GPT data from the drive. The sector_bytes and @@ -22,7 +62,7 @@ int AllocAndReadGptData(GptData *gptdata) { * * Returns 0 if successful, 1 if error. */ - uint64_t entries_sectors = TOTAL_ENTRIES_SIZE / gptdata->sector_bytes; + uint64_t entries_sectors = GPT_ENTRIES_SIZE / gptdata->sector_bytes; /* No data to be written yet */ gptdata->modified = 0; @@ -30,22 +70,22 @@ int AllocAndReadGptData(GptData *gptdata) { /* Allocate all buffers */ gptdata->primary_header = (uint8_t*)Malloc(gptdata->sector_bytes); gptdata->secondary_header = (uint8_t*)Malloc(gptdata->sector_bytes); - gptdata->primary_entries = (uint8_t*)Malloc(TOTAL_ENTRIES_SIZE); - gptdata->secondary_entries = (uint8_t*)Malloc(TOTAL_ENTRIES_SIZE); + gptdata->primary_entries = (uint8_t*)Malloc(GPT_ENTRIES_SIZE); + gptdata->secondary_entries = (uint8_t*)Malloc(GPT_ENTRIES_SIZE); if (gptdata->primary_header == NULL || gptdata->secondary_header == NULL || gptdata->primary_entries == NULL || gptdata->secondary_entries == NULL) return 1; - /* Read data from the drive */ - if (0 != BootDeviceReadLBA(0, 1, gptdata->primary_header)) + /* Read data from the drive, skipping the protective MBR */ + if (0 != BootDeviceReadLBA(1, 1, gptdata->primary_header)) return 1; - if (0 != BootDeviceReadLBA(1, entries_sectors, gptdata->primary_entries)) + if (0 != BootDeviceReadLBA(2, entries_sectors, gptdata->primary_entries)) return 1; if (0 != BootDeviceReadLBA(gptdata->drive_sectors - entries_sectors - 1, entries_sectors, gptdata->secondary_entries)) return 1; - if (0 != BootDeviceReadLBA(gptdata->drive_sectors - entries_sectors - 1, + if (0 != BootDeviceReadLBA(gptdata->drive_sectors - 1, 1, gptdata->secondary_header)) return 1; @@ -56,17 +96,17 @@ void WriteAndFreeGptData(GptData *gptdata) { /* Writes any changes for the GPT data back to the drive, then frees the * buffers. */ - uint64_t entries_sectors = TOTAL_ENTRIES_SIZE / gptdata->sector_bytes; + uint64_t entries_sectors = GPT_ENTRIES_SIZE / gptdata->sector_bytes; if (gptdata->primary_header) { if (gptdata->modified & GPT_MODIFIED_HEADER1) - BootDeviceWriteLBA(0, 1, gptdata->primary_header); + BootDeviceWriteLBA(1, 1, gptdata->primary_header); Free(gptdata->primary_header); } if (gptdata->primary_entries) { if (gptdata->modified & GPT_MODIFIED_ENTRIES1) - BootDeviceWriteLBA(1, entries_sectors, gptdata->primary_entries); + BootDeviceWriteLBA(2, entries_sectors, gptdata->primary_entries); Free(gptdata->primary_entries); } @@ -81,8 +121,9 @@ void WriteAndFreeGptData(GptData *gptdata) { if (gptdata->modified & GPT_MODIFIED_HEADER2) BootDeviceWriteLBA(gptdata->drive_sectors - entries_sectors - 1, 1, gptdata->secondary_header); - BootDeviceWriteLBA(0, 1, gptdata->primary_header); - Free(gptdata->primary_header); + BootDeviceWriteLBA(gptdata->drive_sectors - 1, 1, + gptdata->secondary_header); + Free(gptdata->secondary_header); } /* TODO: What to do with return codes from the writes? */ } @@ -112,6 +153,7 @@ int LoadKernel(LoadKernelParams* params) { GetStoredVersions(KERNEL_VERSIONS, &tpm_kernel_key_version, &tpm_kernel_version); + do { /* Read GPT data */ gpt.sector_bytes = blba; @@ -119,33 +161,45 @@ int LoadKernel(LoadKernelParams* params) { if (0 != AllocAndReadGptData(&gpt)) break; + fprintf(stderr, "RRS1\n"); + /* Initialize GPT library */ if (GPT_SUCCESS != GptInit(&gpt)) break; + /* TODO: TERRIBLE KLUDGE - fake partition attributes */ + FakePartitionAttributes(&gpt); + /* Allocate kernel header and image work buffers */ kbuf = (uint8_t*)Malloc(KBUF_SIZE); if (!kbuf) break; + kbuf_sectors = KBUF_SIZE / blba; kim = (KernelImage*)Malloc(sizeof(KernelImage)); if (!kim) break; + fprintf(stderr, "RRS2\n"); + /* Loop over candidate kernel partitions */ while (GPT_SUCCESS == GptNextKernelEntry(&gpt, &part_start, &part_size)) { RSAPublicKey *kernel_sign_key = NULL; int kernel_start, kernel_sectors; + fprintf(stderr, "RRS3\n"); + /* Found at least one kernel partition. */ found_partition = 1; /* Read the first part of the kernel partition */ if (part_size < kbuf_sectors) continue; - if (1 != BootDeviceReadLBA(part_start, kbuf_sectors, kbuf)) + if (0 != BootDeviceReadLBA(part_start, kbuf_sectors, kbuf)) continue; + fprintf(stderr, "RRS4\n"); + /* Verify the kernel header and preamble */ if (VERIFY_KERNEL_SUCCESS != VerifyKernelHeader( params->header_sign_key_blob, @@ -157,6 +211,8 @@ int LoadKernel(LoadKernelParams* params) { continue; } + fprintf(stderr, "RRS5\n"); + /* Check for rollback of key version */ if (kim->kernel_key_version < tpm_kernel_key_version) { RSAPublicKeyFree(kernel_sign_key);