/* 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_test.h" #include #include "cgptlib.h" #include "cgptlib_internal.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) * * LBA Size Usage * --------------------------------------------------------- * 0 1 PMBR * 1 1 primary partition header * 2 32 primary partition entries (128B * 128) * 34 100 kernel A (index: 0) * 134 100 root A (index: 1) * 234 100 root B (index: 2) * 334 100 kernel B (index: 3) * 434 32 secondary partition entries * 466 1 secondary partition header * 467 */ #define KERNEL_A 0 #define ROOTFS_A 1 #define ROOTFS_B 2 #define KERNEL_B 3 #define DEFAULT_SECTOR_SIZE 512 #define MAX_SECTOR_SIZE 4096 #define DEFAULT_DRIVE_SECTORS 467 #define PARTITION_ENTRIES_SIZE TOTAL_ENTRIES_SIZE /* 16384 */ /* 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) { GptHeader *header, *header2; GptEntry *entries, *entries2; header = (GptHeader*)gpt->primary_header; entries = (GptEntry*)gpt->primary_entries; header2 = (GptHeader*)gpt->secondary_header; entries2 = (GptEntry*)gpt->secondary_entries; header->entries_crc32 = Crc32((uint8_t*)entries, header->number_of_entries * header->size_of_entry); header->header_crc32 = 0; header->header_crc32 = Crc32((uint8_t*)header, header->size); header2->entries_crc32 = Crc32((uint8_t*)entries2, header2->number_of_entries * header2->size_of_entry); header2->header_crc32 = 0; header2->header_crc32 = Crc32((uint8_t*)header2, header2->size); } void ZeroHeaders(GptData* gpt) { Memset(gpt->primary_header, 0, MAX_SECTOR_SIZE); Memset(gpt->secondary_header, 0, MAX_SECTOR_SIZE); } void ZeroEntries(GptData* gpt) { Memset(gpt->primary_entries, 0, PARTITION_ENTRIES_SIZE); Memset(gpt->secondary_entries, 0, PARTITION_ENTRIES_SIZE); } 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 gpt; static uint8_t primary_header[MAX_SECTOR_SIZE]; static uint8_t primary_entries[PARTITION_ENTRIES_SIZE]; static uint8_t secondary_header[MAX_SECTOR_SIZE]; static uint8_t secondary_entries[PARTITION_ENTRIES_SIZE]; Memset(&gpt, 0, sizeof(gpt)); gpt.primary_header = primary_header; gpt.primary_entries = primary_entries; gpt.secondary_header = secondary_header; gpt.secondary_entries = secondary_entries; ZeroHeadersEntries(&gpt); /* Initialize GptData internal states. */ gpt.current_kernel = CGPT_KERNEL_ENTRY_NOT_FOUND; 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) { GptHeader *header, *header2; GptEntry *entries, *entries2; Guid chromeos_kernel = GPT_ENT_TYPE_CHROMEOS_KERNEL; Guid chromeos_rootfs = GPT_ENT_TYPE_CHROMEOS_ROOTFS; gpt->sector_bytes = DEFAULT_SECTOR_SIZE; gpt->drive_sectors = DEFAULT_DRIVE_SECTORS; gpt->current_kernel = CGPT_KERNEL_ENTRY_NOT_FOUND; /* build primary */ header = (GptHeader*)gpt->primary_header; entries = (GptEntry*)gpt->primary_entries; 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->first_usable_lba = 34; header->last_usable_lba = DEFAULT_DRIVE_SECTORS - 1 - 32 - 1; /* 433 */ header->entries_lba = 2; header->number_of_entries = 128; /* 512B / 128B * 32sectors = 128 entries */ header->size_of_entry = 128; /* bytes */ Memcpy(&entries[0].type, &chromeos_kernel, sizeof(chromeos_kernel)); entries[0].starting_lba = 34; entries[0].ending_lba = 133; Memcpy(&entries[1].type, &chromeos_rootfs, sizeof(chromeos_rootfs)); entries[1].starting_lba = 134; entries[1].ending_lba = 233; Memcpy(&entries[2].type, &chromeos_rootfs, sizeof(chromeos_rootfs)); entries[2].starting_lba = 234; entries[2].ending_lba = 333; Memcpy(&entries[3].type, &chromeos_kernel, sizeof(chromeos_kernel)); entries[3].starting_lba = 334; entries[3].ending_lba = 433; header->padding = 0; /* build secondary */ header2 = (GptHeader*)gpt->secondary_header; entries2 = (GptEntry*)gpt->secondary_entries; Memcpy(header2, header, sizeof(GptHeader)); Memcpy(entries2, entries, PARTITION_ENTRIES_SIZE); header2->my_lba = DEFAULT_DRIVE_SECTORS - 1; /* 466 */ 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; 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; struct { uint32_t sector_bytes; uint64_t drive_sectors; int expected_retval; } cases[] = { {512, DEFAULT_DRIVE_SECTORS, GPT_SUCCESS}, {520, DEFAULT_DRIVE_SECTORS, GPT_ERROR_INVALID_SECTOR_SIZE}, {512, 0, GPT_ERROR_INVALID_SECTOR_NUMBER}, {512, 66, GPT_ERROR_INVALID_SECTOR_NUMBER}, {512, GPT_PMBR_SECTOR + GPT_HEADER_SECTOR * 2 + GPT_ENTRIES_SECTORS * 2, GPT_SUCCESS}, {4096, DEFAULT_DRIVE_SECTORS, GPT_ERROR_INVALID_SECTOR_SIZE}, }; int i; gpt = GetEmptyGptData(); for (i = 0; i < ARRAY_SIZE(cases); ++i) { BuildTestGptData(gpt); gpt->sector_bytes = cases[i].sector_bytes; gpt->drive_sectors = cases[i].drive_sectors; EXPECT(cases[i].expected_retval == CheckParameters(gpt)); } return TEST_OK; } /* Tests if signature ("EFI PART") is checked. */ int SignatureTest() { 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)); } } 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; struct { uint32_t value_to_test; int is_valid_value; } cases[] = { {0x01000000, 0}, {0x00010000, 1}, /* GPT_HEADER_REVISION */ {0x00000100, 0}, {0x00000001, 0}, {0x23010456, 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]->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); } } return TEST_OK; } int SizeTest() { GptData *gpt; struct { uint32_t value_to_test; int is_valid_value; } cases[] = { {91, 0}, {92, 1}, {93, 1}, {511, 1}, {512, 1}, {513, 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 = 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); } } 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; 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)); /* expect secondary is still valid. */ 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)); 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; 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; } 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}, {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; 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); } 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; int i; struct { uint64_t primary_entries_lba; uint64_t primary_first_usable_lba; uint64_t primary_last_usable_lba; uint64_t secondary_first_usable_lba; uint64_t secondary_last_usable_lba; uint64_t secondary_entries_lba; int expected_masks; } 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}, }; 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); } 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)); return TEST_OK; } /* Tests if PartitionEntryArrayCRC32 is checked. * PartitionEntryArrayCRC32 must be calculated over SizeOfPartitionEntry * * NumberOfPartitionEntries bytes. */ int EntriesCrcTest() { GptData *gpt; gpt = GetEmptyGptData(); /* Modify the first byte of primary entries, and expect the CRC is wrong. */ BuildTestGptData(gpt); 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)); 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_HEADER2 == gpt->modified); 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: * entry.StartingLBA >= header.FirstUsableLBA * 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; /* 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)); /* 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)); /* 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)); /* 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)); return TEST_OK; } /* Tests if overlapped partition tables can be detected. */ int OverlappedPartitionTest() { GptData *gpt; struct { int overlapped; struct { int active; uint64_t starting_lba; 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}}}, {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}}}, {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}}}, }; 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)) ); EXPECT((MASK_BOTH ^ (cases[i].overlapped * test_mask)) == CheckOverlappedPartition(gpt)); } } 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; /* Make primary entries and secondary header invalid, we expect GptInit() * can recover them (returns GPT_SUCCESS and MODIFIED flasgs). */ BuildTestGptData(gpt); primary_entries[0].type.u.raw[0] ^= 0x33; secondary_header->header_crc32 ^= 0x55; EXPECT(GPT_SUCCESS == GptInit(gpt)); 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). */ BuildTestGptData(gpt); primary_header->entries_crc32 ^= 0x73; EXPECT(GPT_SUCCESS == GptInit(gpt)); /* After header is repaired, the entries are valid actually. */ EXPECT((GPT_MODIFIED_HEADER1) == gpt->modified); /* We expect the modified header/entries can pass GptInit(). */ EXPECT(GPT_SUCCESS == GptInit(gpt)); EXPECT(0 == gpt->modified); return TEST_OK; } /* 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; BuildTestGptData(gpt); entries[KERNEL_A].attributes |= CGPT_ATTRIBUTE_BAD_MASK; Memset(&entries[KERNEL_B].type, 0, sizeof(Guid)); 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; 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)); return TEST_OK; } /* Given an invalid kernel type, and expect GptUpdateKernelEntry() returns * GPT_ERROR_INVALID_UPDATE_TYPE. */ int UpdateInvalidKernelTypeTest() { GptData *gpt; gpt = GetEmptyGptData(); BuildTestGptData(gpt); gpt->current_kernel = 0; /* anything, but not CGPT_KERNEL_ENTRY_NOT_FOUND */ EXPECT(GPT_ERROR_INVALID_UPDATE_TYPE == GptUpdateKernelEntry(gpt, 99)); /* any invalid update_type value */ 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; int error_count = 0; struct { char *name; test_func fp; int retval; } test_cases[] = { { TEST_CASE(TestBuildTestGptData), }, { TEST_CASE(ParameterTests), }, { TEST_CASE(SignatureTest), }, { TEST_CASE(RevisionTest), }, { TEST_CASE(SizeTest), }, { TEST_CASE(ReservedFieldsTest), }, { TEST_CASE(MyLbaTest), }, { TEST_CASE(SizeOfPartitionEntryTest), }, { TEST_CASE(NumberOfPartitionEntriesTest), }, { TEST_CASE(PartitionEntryLbaTest), }, { 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(NoValidKernelEntryTest), }, { TEST_CASE(CombinationalNextKernelEntryTest), }, { TEST_CASE(IncreaseTriesTest), }, { TEST_CASE(MarkBadKernelEntryTest), }, { TEST_CASE(UpdateInvalidKernelTypeTest), }, { TEST_CASE(NormalBootCase), }, { TEST_CASE(HigherPriorityTest), }, { TEST_CASE(TestCrc32TestVectors), }, }; for (i = 0; i < sizeof(test_cases)/sizeof(test_cases[0]); ++i) { printf("Running %s() ...\n", test_cases[i].name); test_cases[i].retval = test_cases[i].fp(); if (test_cases[i].retval) { printf(COL_RED "[ERROR]\n\n" COL_STOP); ++error_count; } else { printf(COL_GREEN "[PASS]\n\n" COL_STOP); } } if (error_count) { printf("\n--------------------------------------------------\n"); printf(COL_RED "The following %d test cases are failed:\n" COL_STOP, error_count); for (i = 0; i < sizeof(test_cases)/sizeof(test_cases[0]); ++i) { if (test_cases[i].retval) printf(" %s()\n", test_cases[i].name); } } return (error_count) ? 1 : 0; }