From 3dcf9dce04301d6d735f265652625fffb6758430 Mon Sep 17 00:00:00 2001 From: Randall Spangler Date: Wed, 2 Jun 2010 12:46:17 -0700 Subject: [PATCH] Much rearranging of cgptlib. Passes all its (new) unit tests. Also includes part of LoadKernel(), which I'll split into a separate CL. With some hacks, gets into VerifyKernel() before dying because I'm not passing in the right key blob. cgptlib is now pretty stable, and worth looking at. LoadKernel() less so. Thanks, Randall Review URL: http://codereview.chromium.org/2438005 --- tests/Makefile | 2 +- tests/cgptlib_test.c | 1522 ++++++++--------- tests/quick_sort_test.c | 137 -- tests/quick_sort_test.h | 11 - utility/Makefile | 4 + utility/cgpt/Makefile | 5 +- utility/cgpt/cgpt.c | 1 + utility/cgpt/cgpt.h | 1 + utility/cgpt/cgpt_add_modify_delete.c | 1 + utility/cgpt/cgpt_attribute.c | 6 +- utility/cgpt/cgpt_show.c | 1 + utility/cgpt/cgpt_tofix.c | 269 +++ utility/cgpt/cgpt_tofix.h | 40 + utility/load_kernel_test.c | 125 ++ vboot_firmware/lib/cgptlib/cgptlib.c | 864 ++-------- vboot_firmware/lib/cgptlib/cgptlib_internal.c | 348 ++++ vboot_firmware/lib/cgptlib/include/cgptlib.h | 37 +- .../lib/cgptlib/include/cgptlib_internal.h | 118 +- vboot_firmware/lib/cgptlib/include/gpt.h | 29 +- .../lib/cgptlib/include/quick_sort.h | 23 - vboot_firmware/lib/cgptlib/quick_sort.c | 59 - vboot_firmware/lib/load_kernel_fw.c | 82 +- 22 files changed, 1803 insertions(+), 1882 deletions(-) delete mode 100644 tests/quick_sort_test.c delete mode 100644 tests/quick_sort_test.h create mode 100644 utility/cgpt/cgpt_tofix.c create mode 100644 utility/cgpt/cgpt_tofix.h create mode 100644 utility/load_kernel_test.c create mode 100644 vboot_firmware/lib/cgptlib/cgptlib_internal.c delete mode 100644 vboot_firmware/lib/cgptlib/include/quick_sort.h delete mode 100644 vboot_firmware/lib/cgptlib/quick_sort.c 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);