/* Copyright (c) 2013 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 #include #include "../cgpt/cgpt.h" #include "../cgpt/flash_ts.h" #include "cgptlib_internal.h" #include "cgptlib_test.h" #include "crc32.h" #include "crc32_test.h" #include "gpt.h" #include "mtdlib.h" #include "mtdlib_unused.h" #include "test_common.h" #define _STUB_IMPLEMENTATION_ #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 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; // cgpt_common.c requires these be defined if linked in. const char *progname = "CGPT-TEST"; const char *command = "TEST"; // Ramdisk for flash ts testing. static uint8_t *nand_drive = NULL; static uint32_t nand_drive_sz; static uint8_t *nand_bad_block_map = NULL; /* * Copy a random-for-this-program-only Guid into the dest. The num parameter * completely determines the Guid. */ static void SetGuid(void *dest, uint32_t num) { Guid g = {{{num,0xd450,0x44bc,0xa6,0x93, {0xb8,0xac,0x75,0x5f,0xcd,0x48}}}}; Memcpy(dest, &g, sizeof(Guid)); } /* * 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. */ static 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); } static void ZeroHeaders(GptData *gpt) { Memset(gpt->primary_header, 0, MAX_SECTOR_SIZE); Memset(gpt->secondary_header, 0, MAX_SECTOR_SIZE); } static void ZeroEntries(GptData *gpt) { Memset(gpt->primary_entries, 0, PARTITION_ENTRIES_SIZE); Memset(gpt->secondary_entries, 0, PARTITION_ENTRIES_SIZE); } static void ZeroHeadersEntries(GptData *gpt) { ZeroHeaders(gpt); ZeroEntries(gpt); } /* * Return 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. */ static GptData *GetEmptyGptData(void) { 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; } static MtdData *GetEmptyMtdData() { static MtdData mtd; Memset(&mtd, 0, sizeof(mtd)); mtd.current_kernel = CGPT_KERNEL_ENTRY_NOT_FOUND; return &mtd; } /* * Fill 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. */ static 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; 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)); header->revision = GPT_HEADER_REVISION; header->size = sizeof(GptHeader); header->reserved_zero = 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; /* 512B / 128B * 32sectors = 128 entries */ header->number_of_entries = 128; header->size_of_entry = 128; /* bytes */ Memcpy(&entries[0].type, &chromeos_kernel, sizeof(chromeos_kernel)); SetGuid(&entries[0].unique, 0); entries[0].starting_lba = 34; entries[0].ending_lba = 133; Memcpy(&entries[1].type, &chromeos_rootfs, sizeof(chromeos_rootfs)); SetGuid(&entries[1].unique, 1); entries[1].starting_lba = 134; entries[1].ending_lba = 232; Memcpy(&entries[2].type, &chromeos_rootfs, sizeof(chromeos_rootfs)); SetGuid(&entries[2].unique, 2); entries[2].starting_lba = 234; entries[2].ending_lba = 331; Memcpy(&entries[3].type, &chromeos_kernel, sizeof(chromeos_kernel)); SetGuid(&entries[3].unique, 3); entries[3].starting_lba = 334; entries[3].ending_lba = 430; /* 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->alternate_lba = 1; header2->entries_lba = DEFAULT_DRIVE_SECTORS - 1 - 32; /* 434 */ RefreshCrc32(gpt); } static void BuildTestMtdData(MtdData *mtd) { MtdDiskPartition *partitions; mtd->sector_bytes = DEFAULT_SECTOR_SIZE; mtd->drive_sectors = DEFAULT_DRIVE_SECTORS; mtd->current_kernel = CGPT_KERNEL_ENTRY_NOT_FOUND; mtd->modified = 0; Memset(&mtd->primary, 0, sizeof(mtd->primary)); Memcpy(mtd->primary.signature, MTD_DRIVE_SIGNATURE, sizeof(mtd->primary.signature)); mtd->primary.first_offset = 32 * DEFAULT_SECTOR_SIZE; mtd->primary.last_offset = DEFAULT_DRIVE_SECTORS * DEFAULT_SECTOR_SIZE - 1; mtd->primary.size = MTD_DRIVE_V1_SIZE; /* These values are not used directly by the library, but they are checked */ mtd->flash_page_bytes = mtd->sector_bytes * 8; mtd->flash_block_bytes = mtd->flash_page_bytes * 8; mtd->fts_block_offset = 1; mtd->fts_block_size = 1; partitions = &mtd->primary.partitions[0]; partitions[0].starting_offset = 34 * DEFAULT_SECTOR_SIZE; partitions[0].ending_offset = 134 * DEFAULT_SECTOR_SIZE - 1; partitions[0].flags = MTD_PARTITION_TYPE_CHROMEOS_KERNEL << MTD_ATTRIBUTE_TYPE_OFFSET; partitions[1].starting_offset = 134 * DEFAULT_SECTOR_SIZE; partitions[1].ending_offset = 233 * DEFAULT_SECTOR_SIZE - 1; partitions[1].flags = MTD_PARTITION_TYPE_CHROMEOS_ROOTFS << MTD_ATTRIBUTE_TYPE_OFFSET; partitions[2].starting_offset = 234 * DEFAULT_SECTOR_SIZE; partitions[2].ending_offset = 332 * DEFAULT_SECTOR_SIZE - 1; partitions[2].flags = MTD_PARTITION_TYPE_CHROMEOS_KERNEL << MTD_ATTRIBUTE_TYPE_OFFSET; partitions[3].starting_offset = 334 * DEFAULT_SECTOR_SIZE; partitions[3].ending_offset = 431 * DEFAULT_SECTOR_SIZE - 1; partitions[3].flags = MTD_PARTITION_TYPE_CHROMEOS_ROOTFS << MTD_ATTRIBUTE_TYPE_OFFSET; mtd->primary.crc32 = 0; mtd->primary.crc32 = Crc32(&mtd->primary, MTD_DRIVE_V1_SIZE); } /* * Test if the structures are the expected size; if this fails, struct packing * is not working properly. */ static int StructSizeTest(void) { EXPECT(GUID_EXPECTED_SIZE == sizeof(Guid)); EXPECT(GPTHEADER_EXPECTED_SIZE == sizeof(GptHeader)); EXPECT(GPTENTRY_EXPECTED_SIZE == sizeof(GptEntry)); EXPECT(MTDENTRY_EXPECTED_SIZE == sizeof(MtdDiskPartition)); EXPECT(MTDLAYOUT_EXPECTED_SIZE == sizeof(MtdDiskLayout)); return TEST_OK; } /* Test if the default structure returned by BuildTestGptData() is good. */ static int TestBuildTestGptData(void) { GptData *gpt; gpt = GetEmptyGptData(); BuildTestGptData(gpt); EXPECT(GPT_SUCCESS == GptInit(gpt)); gpt->sector_bytes = 0; EXPECT(GPT_ERROR_INVALID_SECTOR_SIZE == GptInit(gpt)); return TEST_OK; } static int TestBuildTestMtdData() { MtdData *mtd = GetEmptyMtdData(); BuildTestMtdData(mtd); EXPECT(GPT_SUCCESS == MtdInit(mtd)); return TEST_OK; } /* * Test 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(). * For MtdInit(), additionally test various flash geometries to verify * that only valid ones are accepted. */ static int ParameterTests(void) { GptData *gpt; MtdData *mtd; 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_SECTORS + GPT_HEADER_SECTORS * 2 + GPT_ENTRIES_SECTORS * 2, GPT_SUCCESS}, {4096, DEFAULT_DRIVE_SECTORS, GPT_ERROR_INVALID_SECTOR_SIZE}, }; struct { uint32_t sector_bytes; uint32_t drive_sectors; uint32_t flash_page_bytes; uint32_t flash_block_bytes; int expected_retval; } mtdcases[] = { {512, DEFAULT_DRIVE_SECTORS, 8*512, 8*512, GPT_SUCCESS}, {510, DEFAULT_DRIVE_SECTORS, 8*512, 8*512, GPT_ERROR_INVALID_SECTOR_SIZE}, {512, DEFAULT_DRIVE_SECTORS, 8*512, 8*512, GPT_SUCCESS}, {512, DEFAULT_DRIVE_SECTORS, 512, 8*512, GPT_SUCCESS}, {512, DEFAULT_DRIVE_SECTORS, 8*512, 10*512, GPT_ERROR_INVALID_FLASH_GEOMETRY}, {512, DEFAULT_DRIVE_SECTORS, 3*512, 9*512, GPT_SUCCESS}, {512, DEFAULT_DRIVE_SECTORS, 8*512, 6*512, GPT_ERROR_INVALID_FLASH_GEOMETRY}, {512, DEFAULT_DRIVE_SECTORS, 256, 6*512, GPT_ERROR_INVALID_FLASH_GEOMETRY}, {512, DEFAULT_DRIVE_SECTORS, 512, 6*512 + 256, GPT_ERROR_INVALID_FLASH_GEOMETRY}, }; 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)); } mtd = GetEmptyMtdData(); for (i = 0; i < ARRAY_SIZE(cases); ++i) { BuildTestMtdData(mtd); mtd->sector_bytes = mtdcases[i].sector_bytes; mtd->drive_sectors = mtdcases[i].drive_sectors; mtd->flash_block_bytes = mtdcases[i].flash_block_bytes; mtd->flash_page_bytes = mtdcases[i].flash_page_bytes; if(mtdcases[i].expected_retval != MtdCheckParameters(mtd)) { printf("i=%d\n",i); } EXPECT(mtdcases[i].expected_retval == MtdCheckParameters(mtd)); } return TEST_OK; } /* Test if header CRC in two copies are calculated. */ static int HeaderCrcTest(void) { 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; } /* Test if header-same comparison works. */ static int HeaderSameTest(void) { GptData *gpt = GetEmptyGptData(); GptHeader *h1 = (GptHeader *)gpt->primary_header; GptHeader *h2 = (GptHeader *)gpt->secondary_header; GptHeader h3; EXPECT(0 == HeaderFieldsSame(h1, h2)); Memcpy(&h3, h2, sizeof(h3)); h3.signature[0] ^= 0xba; EXPECT(1 == HeaderFieldsSame(h1, &h3)); Memcpy(&h3, h2, sizeof(h3)); h3.revision++; EXPECT(1 == HeaderFieldsSame(h1, &h3)); Memcpy(&h3, h2, sizeof(h3)); h3.size++; EXPECT(1 == HeaderFieldsSame(h1, &h3)); Memcpy(&h3, h2, sizeof(h3)); h3.reserved_zero++; EXPECT(1 == HeaderFieldsSame(h1, &h3)); Memcpy(&h3, h2, sizeof(h3)); h3.first_usable_lba++; EXPECT(1 == HeaderFieldsSame(h1, &h3)); Memcpy(&h3, h2, sizeof(h3)); h3.last_usable_lba++; EXPECT(1 == HeaderFieldsSame(h1, &h3)); Memcpy(&h3, h2, sizeof(h3)); h3.disk_uuid.u.raw[0] ^= 0xba; EXPECT(1 == HeaderFieldsSame(h1, &h3)); Memcpy(&h3, h2, sizeof(h3)); h3.number_of_entries++; EXPECT(1 == HeaderFieldsSame(h1, &h3)); Memcpy(&h3, h2, sizeof(h3)); h3.size_of_entry++; EXPECT(1 == HeaderFieldsSame(h1, &h3)); Memcpy(&h3, h2, sizeof(h3)); h3.entries_crc32++; EXPECT(1 == HeaderFieldsSame(h1, &h3)); return TEST_OK; } /* Test if signature ("EFI PART") is checked. */ static int SignatureTest(void) { GptData *gpt = GetEmptyGptData(); GptHeader *h1 = (GptHeader *)gpt->primary_header; GptHeader *h2 = (GptHeader *)gpt->secondary_header; int i; EXPECT(1 == CheckHeader(NULL, 0, gpt->drive_sectors)); 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. */ static int RevisionTest(void) { GptData *gpt = GetEmptyGptData(); GptHeader *h1 = (GptHeader *)gpt->primary_header; GptHeader *h2 = (GptHeader *)gpt->secondary_header; int i; struct { uint32_t value_to_test; int expect_rv; } cases[] = { {0x01000000, 1}, {0x00010000, 0}, /* GPT_HEADER_REVISION */ {0x00000100, 1}, {0x00000001, 1}, {0x23010456, 1}, }; for (i = 0; i < ARRAY_SIZE(cases); ++i) { 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; } static int SizeTest(void) { GptData *gpt = GetEmptyGptData(); GptHeader *h1 = (GptHeader *)gpt->primary_header; GptHeader *h2 = (GptHeader *)gpt->secondary_header; int i; struct { uint32_t value_to_test; int expect_rv; } cases[] = { {91, 1}, {92, 0}, {93, 0}, {511, 0}, {512, 0}, {513, 1}, }; for (i = 0; i < ARRAY_SIZE(cases); ++i) { 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; } /* Test if CRC is checked. */ static int CrcFieldTest(void) { 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; } /* Test if reserved fields are checked. We'll try non-zero values to test. */ static int ReservedFieldsTest(void) { GptData *gpt = GetEmptyGptData(); GptHeader *h1 = (GptHeader *)gpt->primary_header; GptHeader *h2 = (GptHeader *)gpt->secondary_header; BuildTestGptData(gpt); h1->reserved_zero ^= 0x12345678; /* whatever random */ h2->reserved_zero ^= 0x12345678; /* whatever random */ RefreshCrc32(gpt); EXPECT(1 == CheckHeader(h1, 0, gpt->drive_sectors)); EXPECT(1 == CheckHeader(h2, 1, gpt->drive_sectors)); #ifdef PADDING_CHECKED /* TODO: padding check is currently disabled */ BuildTestGptData(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; } /* * Technically, any size which is 2^N where N > 6 should work, but our * library only supports one size. */ static int SizeOfPartitionEntryTest(void) { GptData *gpt = GetEmptyGptData(); GptHeader *h1 = (GptHeader *)gpt->primary_header; GptHeader *h2 = (GptHeader *)gpt->secondary_header; int i; struct { uint32_t value_to_test; int expect_rv; } cases[] = { {127, 1}, {128, 0}, {129, 1}, {256, 1}, {512, 1}, }; /* Check size of entryes */ for (i = 0; i < ARRAY_SIZE(cases); ++i) { BuildTestGptData(gpt); 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(void) { 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; } /* Test if myLBA field is checked (1 for primary, last for secondary). */ static int MyLbaTest(void) { 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)); /* We should ignore the alternate_lba field entirely */ BuildTestGptData(gpt); h1->alternate_lba++; h2->alternate_lba++; RefreshCrc32(gpt); EXPECT(0 == CheckHeader(h1, 0, gpt->drive_sectors)); EXPECT(0 == CheckHeader(h2, 1, gpt->drive_sectors)); BuildTestGptData(gpt); h1->alternate_lba--; h2->alternate_lba--; RefreshCrc32(gpt); EXPECT(0 == CheckHeader(h1, 0, gpt->drive_sectors)); EXPECT(0 == CheckHeader(h2, 1, gpt->drive_sectors)); BuildTestGptData(gpt); h1->entries_lba++; h2->entries_lba++; RefreshCrc32(gpt); /* * We support a padding between primary GPT header and its entries. So * this still passes. */ EXPECT(0 == CheckHeader(h1, 0, gpt->drive_sectors)); /* * But the secondary table should fail because it would overlap the * header, which is now lying after its entry array. */ 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; } /* Test 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. */ static int FirstUsableLbaAndLastUsableLbaTest(void) { 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; uint64_t primary_last_usable_lba; uint64_t secondary_first_usable_lba; uint64_t secondary_last_usable_lba; uint64_t secondary_entries_lba; int primary_rv; int secondary_rv; } cases[] = { {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}, }; for (i = 0; i < ARRAY_SIZE(cases); ++i) { BuildTestGptData(gpt); 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; } /* * Test if PartitionEntryArrayCRC32 is checked. PartitionEntryArrayCRC32 must * be calculated over SizeOfPartitionEntry * NumberOfPartitionEntries bytes. */ static int EntriesCrcTest(void) { GptData *gpt = GetEmptyGptData(); GptHeader *h1 = (GptHeader *)gpt->primary_header; GptEntry *e1 = (GptEntry *)(gpt->primary_entries); GptEntry *e2 = (GptEntry *)(gpt->secondary_entries); /* Modify first byte of primary entries, and expect the CRC is wrong. */ BuildTestGptData(gpt); EXPECT(0 == CheckEntries(e1, h1)); EXPECT(0 == CheckEntries(e2, h1)); gpt->primary_entries[0] ^= 0xa5; /* just XOR a non-zero value */ gpt->secondary_entries[TOTAL_ENTRIES_SIZE-1] ^= 0x5a; EXPECT(GPT_ERROR_CRC_CORRUPTED == CheckEntries(e1, h1)); EXPECT(GPT_ERROR_CRC_CORRUPTED == CheckEntries(e2, h1)); return TEST_OK; } /* * Test 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 */ static int ValidEntryTest(void) { GptData *gpt = GetEmptyGptData(); GptHeader *h1 = (GptHeader *)gpt->primary_header; GptEntry *e1 = (GptEntry *)(gpt->primary_entries); MtdData *mtd = GetEmptyMtdData(); MtdDiskLayout *mh = &mtd->primary; MtdDiskPartition *me = mh->partitions; /* error case: entry.StartingLBA < header.FirstUsableLBA */ BuildTestGptData(gpt); e1[0].starting_lba = h1->first_usable_lba - 1; RefreshCrc32(gpt); EXPECT(GPT_ERROR_OUT_OF_REGION == CheckEntries(e1, h1)); BuildTestMtdData(mtd); if (mh->first_offset > 0) { me[0].starting_offset = mh->first_offset - 1; mh->crc32 = MtdHeaderCrc(mh); EXPECT(GPT_ERROR_OUT_OF_REGION == MtdCheckEntries(me, mh)); } /* error case: entry.EndingLBA > header.LastUsableLBA */ BuildTestGptData(gpt); e1[2].ending_lba = h1->last_usable_lba + 1; RefreshCrc32(gpt); EXPECT(GPT_ERROR_OUT_OF_REGION == CheckEntries(e1, h1)); BuildTestMtdData(mtd); me[0].ending_offset = mh->last_offset + 1; mh->crc32 = MtdHeaderCrc(mh); EXPECT(GPT_ERROR_OUT_OF_REGION == MtdCheckEntries(me, mh)); /* error case: entry.StartingLBA > entry.EndingLBA */ BuildTestGptData(gpt); e1[3].starting_lba = e1[3].ending_lba + 1; RefreshCrc32(gpt); EXPECT(GPT_ERROR_OUT_OF_REGION == CheckEntries(e1, h1)); BuildTestMtdData(mtd); me[0].starting_offset = me[0].ending_offset + 1; mh->crc32 = MtdHeaderCrc(mh); EXPECT(GPT_ERROR_OUT_OF_REGION == MtdCheckEntries(me, mh)); /* case: non active entry should be ignored. */ BuildTestGptData(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)); BuildTestMtdData(mtd); me[0].flags = 0; me[0].starting_offset = me[0].ending_offset + 1; mh->crc32 = MtdHeaderCrc(mh); EXPECT(GPT_SUCCESS == MtdCheckEntries(me, mh)); return TEST_OK; } /* Test if overlapped partition tables can be detected. */ static int OverlappedPartitionTest(void) { GptData *gpt = GetEmptyGptData(); GptHeader *h = (GptHeader *)gpt->primary_header; GptEntry *e = (GptEntry *)gpt->primary_entries; MtdData *mtd = GetEmptyMtdData(); MtdDiskLayout *mh = &mtd->primary; MtdDiskPartition *me = mh->partitions; int i, j; struct { int overlapped; struct { int active; uint64_t starting_lba; uint64_t ending_lba; } entries[16]; /* enough for testing. */ } cases[] = { {GPT_SUCCESS, {{0, 100, 199}}}, {GPT_SUCCESS, {{1, 100, 199}}}, {GPT_SUCCESS, {{1, 100, 150}, {1, 200, 250}, {1, 300, 350}}}, {GPT_ERROR_START_LBA_OVERLAP, {{1, 200, 299}, {1, 100, 199}, {1, 100, 100}}}, {GPT_ERROR_END_LBA_OVERLAP, {{1, 200, 299}, {1, 100, 199}, {1, 299, 299}}}, {GPT_SUCCESS, {{1, 300, 399}, {1, 200, 299}, {1, 100, 199}}}, {GPT_ERROR_END_LBA_OVERLAP, {{1, 100, 199}, {1, 199, 299}, {1, 299, 399}}}, {GPT_ERROR_START_LBA_OVERLAP, {{1, 100, 199}, {1, 200, 299}, {1, 75, 399}}}, {GPT_ERROR_START_LBA_OVERLAP, {{1, 100, 199}, {1, 75, 250}, {1, 200, 299}}}, {GPT_ERROR_END_LBA_OVERLAP, {{1, 75, 150}, {1, 100, 199}, {1, 200, 299}}}, {GPT_ERROR_START_LBA_OVERLAP, {{1, 200, 299}, {1, 100, 199}, {1, 300, 399}, {1, 100, 399}}}, {GPT_SUCCESS, {{1, 200, 299}, {1, 100, 199}, {1, 300, 399}, {0, 100, 399}}}, {GPT_ERROR_START_LBA_OVERLAP, {{1, 200, 300}, {1, 100, 200}, {1, 100, 400}, {1, 300, 400}}}, {GPT_ERROR_START_LBA_OVERLAP, {{0, 200, 300}, {1, 100, 200}, {1, 100, 400}, {1, 300, 400}}}, {GPT_SUCCESS, {{1, 200, 300}, {1, 100, 199}, {0, 100, 400}, {0, 300, 400}}}, {GPT_ERROR_END_LBA_OVERLAP, {{1, 200, 299}, {1, 100, 199}, {1, 199, 199}}}, {GPT_SUCCESS, {{1, 200, 299}, {0, 100, 199}, {1, 199, 199}}}, {GPT_SUCCESS, {{1, 200, 299}, {1, 100, 199}, {0, 199, 199}}}, {GPT_ERROR_START_LBA_OVERLAP, {{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}}}, {GPT_SUCCESS, {{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}}}, }; for (i = 0; i < ARRAY_SIZE(cases); ++i) { BuildTestGptData(gpt); BuildTestMtdData(mtd); Memset(mh->partitions, 0, sizeof(mh->partitions)); ZeroEntries(gpt); for(j = 0; j < ARRAY_SIZE(cases[0].entries); ++j) { if (!cases[i].entries[j].starting_lba) break; if (cases[i].entries[j].active) { Memcpy(&e[j].type, &guid_kernel, sizeof(Guid)); me[j].flags = MTD_PARTITION_TYPE_CHROMEOS_KERNEL << MTD_ATTRIBUTE_TYPE_OFFSET; } SetGuid(&e[j].unique, j); e[j].starting_lba = cases[i].entries[j].starting_lba; e[j].ending_lba = cases[i].entries[j].ending_lba; me[j].starting_offset = cases[i].entries[j].starting_lba * DEFAULT_SECTOR_SIZE; me[j].ending_offset = cases[i].entries[j].ending_lba * DEFAULT_SECTOR_SIZE; } RefreshCrc32(gpt); EXPECT(cases[i].overlapped == CheckEntries(e, h)); EXPECT(cases[i].overlapped == MtdCheckEntries(me, mh)); } return TEST_OK; } /* Test both sanity checking and repair. */ static int SanityCheckTest(void) { GptData *gpt = GetEmptyGptData(); GptHeader *h1 = (GptHeader *)gpt->primary_header; GptEntry *e1 = (GptEntry *)gpt->primary_entries; uint8_t *tempptr; /* Unmodified test data is completely sane */ BuildTestGptData(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); /* Invalid sector size should fail */ BuildTestGptData(gpt); gpt->sector_bytes = 1024; EXPECT(GPT_ERROR_INVALID_SECTOR_SIZE == GptSanityCheck(gpt)); /* 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); /* * Modify entries and recompute CRCs, then make both primary and * secondary entry pointers use the secondary data. The primary * header will have the wrong entries CRC, so we should fall back * to the secondary header. */ BuildTestGptData(gpt); e1->starting_lba++; RefreshCrc32(gpt); tempptr = gpt->primary_entries; gpt->primary_entries = gpt->secondary_entries; EXPECT(GPT_SUCCESS == GptSanityCheck(gpt)); EXPECT(MASK_SECONDARY == gpt->valid_headers); EXPECT(MASK_BOTH == gpt->valid_entries); gpt->primary_entries = tempptr; /* Modify both header and entries */ BuildTestGptData(gpt); gpt->primary_header[0]++; gpt->primary_entries[0]++; EXPECT(GPT_SUCCESS == GptSanityCheck(gpt)); EXPECT(MASK_SECONDARY == 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_HEADER1 | GPT_MODIFIED_ENTRIES1) == gpt->modified); BuildTestGptData(gpt); gpt->secondary_header[0]++; gpt->secondary_entries[0]++; 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); /* 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); /* * Test mismatched pairs (h1+e1 valid, h2+e2 valid but different. This * simulates a partial update of the drive. */ BuildTestGptData(gpt); 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(void) { GptData *gpt = GetEmptyGptData(); GptEntry *e = (GptEntry *)(gpt->primary_entries); MtdData *mtd = GetEmptyMtdData(); MtdDiskPartition *m = &mtd->primary.partitions[0]; e->attrs.whole = 0x0000000000000000ULL; SetEntrySuccessful(e, 1); EXPECT(0x0100000000000000ULL == e->attrs.whole); EXPECT(1 == GetEntrySuccessful(e)); e->attrs.whole = 0xFFFFFFFFFFFFFFFFULL; SetEntrySuccessful(e, 0); EXPECT(0xFEFFFFFFFFFFFFFFULL == e->attrs.whole); EXPECT(0 == GetEntrySuccessful(e)); m->flags = 0; MtdSetEntrySuccessful(m, 1); EXPECT(0x00000100 == m->flags); EXPECT(1 == MtdGetEntrySuccessful(m)); m->flags = ~0; MtdSetEntrySuccessful(m, 0); EXPECT(0xFFFFFEFF == m->flags); EXPECT(0 == MtdGetEntrySuccessful(m)); e->attrs.whole = 0x0000000000000000ULL; SetEntryTries(e, 15); EXPECT(15 == GetEntryTries(e)); EXPECT(0x00F0000000000000ULL == e->attrs.whole); e->attrs.whole = 0xFFFFFFFFFFFFFFFFULL; SetEntryTries(e, 0); EXPECT(0xFF0FFFFFFFFFFFFFULL == e->attrs.whole); EXPECT(0 == GetEntryTries(e)); m->flags = 0; MtdSetEntryTries(m, 15); EXPECT(0x000000F0 == m->flags); EXPECT(15 == MtdGetEntryTries(m)); m->flags = ~0; MtdSetEntryTries(m, 0); EXPECT(0xFFFFFF0F == m->flags); EXPECT(0 == MtdGetEntryTries(m)); e->attrs.whole = 0x0000000000000000ULL; SetEntryPriority(e, 15); EXPECT(0x000F000000000000ULL == e->attrs.whole); EXPECT(15 == GetEntryPriority(e)); e->attrs.whole = 0xFFFFFFFFFFFFFFFFULL; SetEntryPriority(e, 0); EXPECT(0xFFF0FFFFFFFFFFFFULL == e->attrs.whole); EXPECT(0 == GetEntryPriority(e)); m->flags = 0; MtdSetEntryPriority(m, 15); EXPECT(0x0000000F == m->flags); EXPECT(15 == MtdGetEntryPriority(m)); m->flags = ~0; MtdSetEntryPriority(m, 0); EXPECT(0xFFFFFFF0 == m->flags); EXPECT(0 == MtdGetEntryPriority(m)); e->attrs.whole = 0xFFFFFFFFFFFFFFFFULL; EXPECT(1 == GetEntrySuccessful(e)); EXPECT(15 == GetEntryPriority(e)); EXPECT(15 == GetEntryTries(e)); e->attrs.whole = 0x0123000000000000ULL; EXPECT(1 == GetEntrySuccessful(e)); EXPECT(2 == GetEntryTries(e)); EXPECT(3 == GetEntryPriority(e)); return TEST_OK; } static int EntryTypeTest(void) { 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)); } static void MtdFreeEntry(MtdDiskPartition *e) { MtdSetEntryType(e, MTD_PARTITION_TYPE_UNUSED); } /* 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); } static void MtdFillEntry(MtdDiskPartition *e, int is_kernel, int priority, int successful, int tries) { MtdSetEntryType(e, is_kernel ? MTD_PARTITION_TYPE_CHROMEOS_KERNEL : MTD_PARTITION_TYPE_CHROMEOS_FIRMWARE); MtdSetEntryPriority(e, priority); MtdSetEntrySuccessful(e, successful); MtdSetEntryTries(e, tries); } /* * Invalidate all kernel entries and expect GptNextKernelEntry() cannot find * any usable kernel entry. */ static int NoValidKernelEntryTest(void) { GptData *gpt = GetEmptyGptData(); GptEntry *e1 = (GptEntry *)(gpt->primary_entries); BuildTestGptData(gpt); SetEntryPriority(e1 + KERNEL_A, 0); FreeEntry(e1 + KERNEL_B); RefreshCrc32(gpt); EXPECT(GPT_ERROR_NO_VALID_KERNEL == GptNextKernelEntry(gpt, NULL, NULL)); return TEST_OK; } static int MtdNoValidKernelEntryTest(void) { MtdData *mtd = GetEmptyMtdData(); MtdDiskPartition *e1 = mtd->primary.partitions; BuildTestMtdData(mtd); MtdSetEntryPriority(e1 + KERNEL_A, 0); MtdFreeEntry(e1 + KERNEL_B); EXPECT(GPT_ERROR_NO_VALID_KERNEL == MtdNextKernelEntry(mtd, NULL, NULL)); return TEST_OK; } static int GetNextNormalTest(void) { GptData *gpt = GetEmptyGptData(); GptEntry *e1 = (GptEntry *)(gpt->primary_entries); uint64_t start, size; /* Normal case - both kernels successful */ BuildTestGptData(gpt); 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(void) { 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(void) { 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 MtdGetNextNormalTest(void) { MtdData *mtd = GetEmptyMtdData(); MtdDiskPartition *e1 = mtd->primary.partitions; uint64_t start, size; /* Normal case - both kernels successful */ BuildTestMtdData(mtd); MtdFillEntry(e1 + KERNEL_A, 1, 2, 1, 0); MtdFillEntry(e1 + KERNEL_B, 1, 2, 1, 0); mtd->primary.crc32 = MtdHeaderCrc(&mtd->primary); MtdInit(mtd); EXPECT(GPT_SUCCESS == MtdNextKernelEntry(mtd, &start, &size)); EXPECT(KERNEL_A == mtd->current_kernel); EXPECT(34 == start); EXPECT(100 == size); EXPECT(GPT_SUCCESS == MtdNextKernelEntry(mtd, &start, &size)); EXPECT(KERNEL_B == mtd->current_kernel); EXPECT(134 == start); EXPECT(99 == size); EXPECT(GPT_ERROR_NO_VALID_KERNEL == MtdNextKernelEntry(mtd, &start, &size)); EXPECT(-1 == mtd->current_kernel); /* Call as many times as you want; you won't get another kernel... */ EXPECT(GPT_ERROR_NO_VALID_KERNEL == MtdNextKernelEntry(mtd, &start, &size)); EXPECT(-1 == mtd->current_kernel); return TEST_OK; } static int MtdGetNextPrioTest(void) { MtdData *mtd = GetEmptyMtdData(); MtdDiskPartition *e1 = mtd->primary.partitions; uint64_t start, size; /* Priority 3, 4, 0, 4 - should boot order B, Y, A */ BuildTestMtdData(mtd); MtdFillEntry(e1 + KERNEL_A, 1, 3, 1, 0); MtdFillEntry(e1 + KERNEL_B, 1, 4, 1, 0); MtdFillEntry(e1 + KERNEL_X, 1, 0, 1, 0); MtdFillEntry(e1 + KERNEL_Y, 1, 4, 1, 0); mtd->primary.crc32 = MtdHeaderCrc(&mtd->primary); MtdInit(mtd); EXPECT(GPT_SUCCESS == MtdNextKernelEntry(mtd, &start, &size)); EXPECT(KERNEL_B == mtd->current_kernel); EXPECT(GPT_SUCCESS == MtdNextKernelEntry(mtd, &start, &size)); EXPECT(KERNEL_Y == mtd->current_kernel); EXPECT(GPT_SUCCESS == MtdNextKernelEntry(mtd, &start, &size)); EXPECT(KERNEL_A == mtd->current_kernel); EXPECT(GPT_ERROR_NO_VALID_KERNEL == MtdNextKernelEntry(mtd, &start, &size)); return TEST_OK; } static int MtdGetNextTriesTest(void) { MtdData *mtd = GetEmptyMtdData(); MtdDiskPartition *e1 = mtd->primary.partitions; uint64_t start, size; /* Tries=nonzero is attempted just like success, but tries=0 isn't */ BuildTestMtdData(mtd); MtdFillEntry(e1 + KERNEL_A, 1, 2, 1, 0); MtdFillEntry(e1 + KERNEL_B, 1, 3, 0, 0); MtdFillEntry(e1 + KERNEL_X, 1, 4, 0, 1); MtdFillEntry(e1 + KERNEL_Y, 1, 0, 0, 5); mtd->primary.crc32 = MtdHeaderCrc(&mtd->primary); MtdInit(mtd); EXPECT(GPT_SUCCESS == MtdNextKernelEntry(mtd, &start, &size)); EXPECT(KERNEL_X == mtd->current_kernel); EXPECT(GPT_SUCCESS == MtdNextKernelEntry(mtd, &start, &size)); EXPECT(KERNEL_A == mtd->current_kernel); EXPECT(GPT_ERROR_NO_VALID_KERNEL == MtdNextKernelEntry(mtd, &start, &size)); return TEST_OK; } static int MtdUpdateTest() { MtdData *mtd = GetEmptyMtdData(); MtdDiskPartition *e = &mtd->primary.partitions[0]; uint64_t start, size; BuildTestMtdData(mtd); /* Tries=nonzero is attempted just like success, but tries=0 isn't */ MtdFillEntry(e + KERNEL_A, 1, 4, 1, 0); MtdFillEntry(e + KERNEL_B, 1, 3, 0, 2); MtdFillEntry(e + KERNEL_X, 1, 2, 0, 2); mtd->primary.crc32 = MtdHeaderCrc(&mtd->primary); mtd->modified = 0; EXPECT(GPT_SUCCESS == MtdInit(mtd)); /* Successful kernel */ EXPECT(GPT_SUCCESS == MtdNextKernelEntry(mtd, &start, &size)); EXPECT(KERNEL_A == mtd->current_kernel); EXPECT(1 == MtdGetEntrySuccessful(e + KERNEL_A)); EXPECT(4 == MtdGetEntryPriority(e + KERNEL_A)); EXPECT(0 == MtdGetEntryTries(e + KERNEL_A)); /* Trying successful kernel changes nothing */ EXPECT(GPT_SUCCESS == MtdUpdateKernelEntry(mtd, GPT_UPDATE_ENTRY_TRY)); EXPECT(1 == MtdGetEntrySuccessful(e + KERNEL_A)); EXPECT(4 == MtdGetEntryPriority(e + KERNEL_A)); EXPECT(0 == MtdGetEntryTries(e + KERNEL_A)); EXPECT(0 == mtd->modified); /* Marking it bad also does not update it. */ EXPECT(GPT_SUCCESS == MtdUpdateKernelEntry(mtd, GPT_UPDATE_ENTRY_BAD)); EXPECT(1 == MtdGetEntrySuccessful(e + KERNEL_A)); EXPECT(4 == MtdGetEntryPriority(e + KERNEL_A)); EXPECT(0 == MtdGetEntryTries(e + KERNEL_A)); EXPECT(0 == mtd->modified); /* Kernel with tries */ EXPECT(GPT_SUCCESS == MtdNextKernelEntry(mtd, &start, &size)); EXPECT(KERNEL_B == mtd->current_kernel); EXPECT(0 == MtdGetEntrySuccessful(e + KERNEL_B)); EXPECT(3 == MtdGetEntryPriority(e + KERNEL_B)); EXPECT(2 == MtdGetEntryTries(e + KERNEL_B)); /* Marking it bad clears it */ EXPECT(GPT_SUCCESS == MtdUpdateKernelEntry(mtd, GPT_UPDATE_ENTRY_BAD)); EXPECT(0 == MtdGetEntrySuccessful(e + KERNEL_B)); EXPECT(0 == MtdGetEntryPriority(e + KERNEL_B)); EXPECT(0 == MtdGetEntryTries(e + KERNEL_B)); /* And that's caused the mtd to need updating */ EXPECT(1 == mtd->modified); /* Another kernel with tries */ EXPECT(GPT_SUCCESS == MtdNextKernelEntry(mtd, &start, &size)); EXPECT(KERNEL_X == mtd->current_kernel); EXPECT(0 == MtdGetEntrySuccessful(e + KERNEL_X)); EXPECT(2 == MtdGetEntryPriority(e + KERNEL_X)); EXPECT(2 == MtdGetEntryTries(e + KERNEL_X)); /* Trying it uses up a try */ EXPECT(GPT_SUCCESS == MtdUpdateKernelEntry(mtd, GPT_UPDATE_ENTRY_TRY)); EXPECT(0 == MtdGetEntrySuccessful(e + KERNEL_X)); EXPECT(2 == MtdGetEntryPriority(e + KERNEL_X)); EXPECT(1 == MtdGetEntryTries(e + KERNEL_X)); /* Trying it again marks it inactive */ EXPECT(GPT_SUCCESS == MtdUpdateKernelEntry(mtd, GPT_UPDATE_ENTRY_TRY)); EXPECT(0 == MtdGetEntrySuccessful(e + KERNEL_X)); EXPECT(0 == MtdGetEntryPriority(e + KERNEL_X)); EXPECT(0 == MtdGetEntryTries(e + KERNEL_X)); /* Can't update if entry isn't a kernel, or there isn't an entry */ MtdSetEntryType(e + KERNEL_X, MTD_PARTITION_TYPE_UNUSED); EXPECT(GPT_ERROR_INVALID_UPDATE_TYPE == MtdUpdateKernelEntry(mtd, GPT_UPDATE_ENTRY_BAD)); mtd->current_kernel = CGPT_KERNEL_ENTRY_NOT_FOUND; EXPECT(GPT_ERROR_INVALID_UPDATE_TYPE == MtdUpdateKernelEntry(mtd, GPT_UPDATE_ENTRY_BAD)); return TEST_OK; } static int GptUpdateTest(void) { 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 also does not update it. */ EXPECT(GPT_SUCCESS == GptUpdateKernelEntry(gpt, GPT_UPDATE_ENTRY_BAD)); EXPECT(1 == GetEntrySuccessful(e + KERNEL_A)); EXPECT(4 == GetEntryPriority(e + KERNEL_A)); EXPECT(0 == GetEntryTries(e + KERNEL_A)); EXPECT(0 == 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)); /* Which affects both copies of the partition entries */ EXPECT(0 == GetEntrySuccessful(e2 + KERNEL_B)); EXPECT(0 == GetEntryPriority(e2 + KERNEL_B)); EXPECT(0 == GetEntryTries(e2 + KERNEL_B)); /* And that's caused the GPT to need updating */ EXPECT(0x0F == gpt->modified); /* 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)); /* Can't update if entry isn't a kernel, or there isn't an entry */ Memcpy(&e[KERNEL_X].type, &guid_rootfs, sizeof(guid_rootfs)); EXPECT(GPT_ERROR_INVALID_UPDATE_TYPE == GptUpdateKernelEntry(gpt, GPT_UPDATE_ENTRY_BAD)); gpt->current_kernel = CGPT_KERNEL_ENTRY_NOT_FOUND; EXPECT(GPT_ERROR_INVALID_UPDATE_TYPE == GptUpdateKernelEntry(gpt, GPT_UPDATE_ENTRY_BAD)); return TEST_OK; } /* * Give an invalid kernel type, and expect GptUpdateKernelEntry() returns * GPT_ERROR_INVALID_UPDATE_TYPE. */ static int UpdateInvalidKernelTypeTest(void) { GptData *gpt = GetEmptyGptData(); BuildTestGptData(gpt); /* anything, but not CGPT_KERNEL_ENTRY_NOT_FOUND */ gpt->current_kernel = 0; /* any invalid update_type value */ EXPECT(GPT_ERROR_INVALID_UPDATE_TYPE == GptUpdateKernelEntry(gpt, 99)); return TEST_OK; } static int MtdUpdateInvalidKernelTypeTest(void) { MtdData *mtd = GetEmptyMtdData(); BuildTestMtdData(mtd); /* anything, but not CGPT_KERNEL_ENTRY_NOT_FOUND */ mtd->current_kernel = 0; /* any invalid update_type value */ EXPECT(GPT_ERROR_INVALID_UPDATE_TYPE == MtdUpdateKernelEntry(mtd, 99)); return TEST_OK; } /* Test duplicate UniqueGuids can be detected. */ static int DuplicateUniqueGuidTest(void) { GptData *gpt = GetEmptyGptData(); GptHeader *h = (GptHeader *)gpt->primary_header; GptEntry *e = (GptEntry *)gpt->primary_entries; int i, j; struct { int duplicate; struct { uint64_t starting_lba; uint64_t ending_lba; uint32_t type_guid; uint32_t unique_guid; } entries[16]; /* enough for testing. */ } cases[] = { {GPT_SUCCESS, {{100, 109, 1, 1}, {110, 119, 2, 2}, {120, 129, 3, 3}, {130, 139, 4, 4}, }}, {GPT_SUCCESS, {{100, 109, 1, 1}, {110, 119, 1, 2}, {120, 129, 2, 3}, {130, 139, 2, 4}, }}, {GPT_ERROR_DUP_GUID, {{100, 109, 1, 1}, {110, 119, 2, 2}, {120, 129, 3, 1}, {130, 139, 4, 4}, }}, {GPT_ERROR_DUP_GUID, {{100, 109, 1, 1}, {110, 119, 1, 2}, {120, 129, 2, 3}, {130, 139, 2, 2}, }}, }; for (i = 0; i < ARRAY_SIZE(cases); ++i) { BuildTestGptData(gpt); ZeroEntries(gpt); for(j = 0; j < ARRAY_SIZE(cases[0].entries); ++j) { if (!cases[i].entries[j].starting_lba) break; e[j].starting_lba = cases[i].entries[j].starting_lba; e[j].ending_lba = cases[i].entries[j].ending_lba; SetGuid(&e[j].type, cases[i].entries[j].type_guid); SetGuid(&e[j].unique, cases[i].entries[j].unique_guid); } RefreshCrc32(gpt); EXPECT(cases[i].duplicate == CheckEntries(e, h)); } return TEST_OK; } /* Test getting the current kernel GUID */ static int GetKernelGuidTest(void) { GptData *gpt = GetEmptyGptData(); GptEntry *e = (GptEntry *)gpt->primary_entries; Guid g; BuildTestGptData(gpt); gpt->current_kernel = 0; GetCurrentKernelUniqueGuid(gpt, &g); EXPECT(!Memcmp(&g, &e[0].unique, sizeof(Guid))); gpt->current_kernel = 1; GetCurrentKernelUniqueGuid(gpt, &g); EXPECT(!Memcmp(&g, &e[1].unique, sizeof(Guid))); return TEST_OK; } /* Test getting GPT error text strings */ static int ErrorTextTest(void) { int i; /* Known errors are not unknown */ for (i = 0; i < GPT_ERROR_COUNT; i++) { EXPECT(GptErrorText(i)); EXPECT(strcmp(GptErrorText(i), "Unknown")); } /* But other error values are */ EXPECT(!strcmp(GptErrorText(GPT_ERROR_COUNT), "Unknown")); return TEST_OK; } int nand_read_page(const nand_geom *nand, int page, void *buf, int size) { uint32_t ofs = page * nand->szofpg; uint32_t sz = size; if (ofs + sz > nand_drive_sz) { return -1; } Memcpy(buf, nand_drive + ofs, sz); return 0; } int nand_write_page(const nand_geom *nand, int page, const void *buf, int size) { uint32_t ofs = page * nand->szofpg; uint32_t sz = size; uint32_t i; if (ofs + sz > nand_drive_sz) { return -1; } for (i = 0; i < sz; i++) { if (nand_drive[ofs + i] != 0xff) { return -1; } } Memcpy(nand_drive + ofs, buf, sz); return 0; } int nand_erase_block(const nand_geom *nand, int block) { uint32_t ofs = block * nand->szofblk; uint32_t sz = nand->szofblk; if (ofs + sz > nand_drive_sz) { return -1; } if (!--nand_bad_block_map[block]) { return -1; } Memset(nand_drive + ofs, 0xFF, sz); return 0; } int nand_is_bad_block(const nand_geom *nand, int block) { return nand_bad_block_map[block] == 0; } static void nand_make_ramdisk() { if (nand_drive) { free(nand_drive); } if (nand_bad_block_map) { free(nand_bad_block_map); } nand_drive_sz = 1024 * 1024 * 16; nand_drive = (uint8_t *)malloc(nand_drive_sz); nand_bad_block_map = (uint8_t *)malloc(nand_drive_sz / 512); Memset(nand_drive, 0xff, nand_drive_sz); Memset(nand_bad_block_map, 0xff, nand_drive_sz / 512); } static int MtdFtsTest() { int MtdLoad(struct drive *drive, int sector_bytes); int MtdSave(struct drive *drive); int FlashGet(const char *key, uint8_t *data, uint32_t *bufsz); int FlashSet(const char *key, const uint8_t *data, uint32_t bufsz); int i, j, err; struct { int result; unsigned int offset, size, block_size_bytes, page_size_bytes; } cases[] = { { 0, 1, 2, 1024 * 1024, 1024 * 4 }, { 0, 1, 2, 1024 * 1024, 1024 * 16 }, /* Failure cases, non-power-of-2 */ { -ENODEV, 1, 2, 5000000, 1024 * 16 }, { -ENODEV, 1, 2, 1024 * 1024, 65535 }, /* Page > block */ { -ENODEV, 1, 2, 1024 * 16, 1024 * 1024 }, }; /* Check if the FTS store works */ for (i = 0; i < ARRAY_SIZE(cases); i++) { nand_make_ramdisk(); EXPECT(cases[i].result == flash_ts_init(cases[i].offset, cases[i].size, cases[i].page_size_bytes, cases[i].block_size_bytes, 512, 0)); if (cases[i].result == 0) { /* We should have a working FTS store now */ char buffer[64]; uint8_t blob[256], blob_read[256]; uint32_t sz = sizeof(blob_read); struct drive drive; /* Test the low level API */ EXPECT(0 == flash_ts_set("some_key", "some value")); flash_ts_get("some_key", buffer, sizeof(buffer)); EXPECT(0 == strcmp(buffer, "some value")); /* Check overwrite */ EXPECT(0 == flash_ts_set("some_key", "some other value")); flash_ts_get("some_key", buffer, sizeof(buffer)); EXPECT(0 == strcmp(buffer, "some other value")); /* Check delete */ EXPECT(0 == flash_ts_set("some_key", "")); /* Verify that re-initialization pulls the right record. */ flash_ts_init(cases[i].offset, cases[i].size, cases[i].page_size_bytes, cases[i].block_size_bytes, 512, 0); flash_ts_get("some_key", buffer, sizeof(buffer)); EXPECT(0 == strcmp(buffer, "")); /* Fill up the disk, eating all erase cycles */ for (j = 0; j < nand_drive_sz / 512; j++) { nand_bad_block_map[j] = 2; } for (j = 0; j < 999999; j++) { char str[32]; sprintf(str, "%d", j); err = flash_ts_set("some_new_key", str); if (err) { EXPECT(err == -ENOMEM); break; } /* Make sure we can figure out where the latest is. */ flash_ts_init(cases[i].offset, cases[i].size, cases[i].page_size_bytes, cases[i].block_size_bytes, 512, 0); flash_ts_get("some_new_key", buffer, sizeof(buffer)); EXPECT(0 == strcmp(buffer, str)); } EXPECT(j < 999999); /* We need our drive back. */ nand_make_ramdisk(); flash_ts_init(cases[i].offset, cases[i].size, cases[i].page_size_bytes, cases[i].block_size_bytes, 512, 0); for (j = 0; j < 256; j++) { blob[j] = j; } /* Hex conversion / blob storage */ EXPECT(0 == FlashSet("some_blob", blob, sizeof(blob))); EXPECT(0 == FlashGet("some_blob", blob_read, &sz)); EXPECT(sz == sizeof(blob_read)); EXPECT(0 == Memcmp(blob, blob_read, sizeof(blob))); BuildTestMtdData(&drive.mtd); drive.mtd.flash_block_bytes = cases[i].block_size_bytes; drive.mtd.flash_page_bytes = cases[i].page_size_bytes; drive.mtd.fts_block_offset = cases[i].offset; drive.mtd.fts_block_size = cases[i].size; drive.mtd.sector_bytes = 512; drive.mtd.drive_sectors = nand_drive_sz / 512; /* MTD-level API */ EXPECT(0 == MtdSave(&drive)); Memset(&drive.mtd.primary, 0, sizeof(drive.mtd.primary)); EXPECT(0 == MtdLoad(&drive, 512)); } } 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(StructSizeTest), }, { TEST_CASE(TestBuildTestGptData), }, { TEST_CASE(TestBuildTestMtdData), }, { TEST_CASE(ParameterTests), }, { TEST_CASE(HeaderCrcTest), }, { TEST_CASE(HeaderSameTest), }, { TEST_CASE(SignatureTest), }, { TEST_CASE(RevisionTest), }, { TEST_CASE(SizeTest), }, { TEST_CASE(CrcFieldTest), }, { TEST_CASE(ReservedFieldsTest), }, { TEST_CASE(SizeOfPartitionEntryTest), }, { TEST_CASE(NumberOfPartitionEntriesTest), }, { TEST_CASE(MyLbaTest), }, { TEST_CASE(FirstUsableLbaAndLastUsableLbaTest), }, { TEST_CASE(EntriesCrcTest), }, { TEST_CASE(ValidEntryTest), }, { TEST_CASE(OverlappedPartitionTest), }, { TEST_CASE(SanityCheckTest), }, { TEST_CASE(NoValidKernelEntryTest), }, { TEST_CASE(MtdNoValidKernelEntryTest), }, { TEST_CASE(EntryAttributeGetSetTest), }, { TEST_CASE(EntryTypeTest), }, { TEST_CASE(GetNextNormalTest), }, { TEST_CASE(GetNextPrioTest), }, { TEST_CASE(GetNextTriesTest), }, { TEST_CASE(MtdGetNextNormalTest), }, { TEST_CASE(MtdGetNextPrioTest), }, { TEST_CASE(MtdGetNextTriesTest), }, { TEST_CASE(GptUpdateTest), }, { TEST_CASE(MtdUpdateTest), }, { TEST_CASE(UpdateInvalidKernelTypeTest), }, { TEST_CASE(MtdUpdateInvalidKernelTypeTest), }, { TEST_CASE(DuplicateUniqueGuidTest), }, { TEST_CASE(TestCrc32TestVectors), }, { TEST_CASE(GetKernelGuidTest), }, { TEST_CASE(ErrorTextTest), }, { TEST_CASE(MtdFtsTest), }, }; 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; }