blob: 1ab5342afe00bbb25e7d4d80545debb7bfe2a7ba [file] [log] [blame]
/* 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 <string.h>
#include "cgptlib.h"
#include "cgptlib_internal.h"
#include "cgptlib_test.h"
#include "crc32.h"
#include "crc32_test.h"
#include "gpt.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 KERNEL_B 1
#define ROOTFS_A 2
#define ROOTFS_B 3
#define KERNEL_X 2 /* Overload ROOTFS_A, for some GetNext tests */
#define KERNEL_Y 3 /* Overload ROOTFS_B, for some GetNext tests */
#define DEFAULT_SECTOR_SIZE 512
#define MAX_SECTOR_SIZE 4096
#define DEFAULT_DRIVE_SECTORS 467
#define PARTITION_ENTRIES_SIZE TOTAL_ENTRIES_SIZE /* 16384 */
static const Guid guid_zero = {{{0, 0, 0, 0, 0, {0, 0, 0, 0, 0, 0}}}};
static const Guid guid_kernel = GPT_ENT_TYPE_CHROMEOS_KERNEL;
static const Guid guid_rootfs = GPT_ENT_TYPE_CHROMEOS_ROOTFS;
/* Given a GptData pointer, first re-calculate entries CRC32 value,
* then reset header CRC32 value to 0, and calculate header CRC32 value.
* Both primary and secondary are updated. */
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);
}
/* 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. */
static 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. */
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) - sizeof(header->padding);
header->reserved = 0;
header->my_lba = 1;
header->alternate_lba = DEFAULT_DRIVE_SECTORS - 1;
header->first_usable_lba = 34;
header->last_usable_lba = DEFAULT_DRIVE_SECTORS - 1 - 32 - 1; /* 433 */
header->entries_lba = 2;
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 = 232;
Memcpy(&entries[2].type, &chromeos_rootfs, sizeof(chromeos_rootfs));
entries[2].starting_lba = 234;
entries[2].ending_lba = 331;
Memcpy(&entries[3].type, &chromeos_kernel, sizeof(chromeos_kernel));
entries[3].starting_lba = 334;
entries[3].ending_lba = 430;
Memset(header->padding, 0, sizeof(header->padding));
/* 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);
}
/* Tests if the default structure returned by BuildTestGptData() is good. */
static int TestBuildTestGptData() {
GptData* gpt;
gpt = GetEmptyGptData();
BuildTestGptData(gpt);
EXPECT(GPT_SUCCESS == GptInit(gpt));
return TEST_OK;
}
/* Tests if wrong sector_bytes or drive_sectors is detected by GptInit().
* Currently we only support 512 bytes per sector.
* In the future, we may support other sizes.
* A too small drive_sectors should be rejected by GptInit(). */
static 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 header CRC in two copies are calculated. */
static int HeaderCrcTest() {
GptData* gpt = GetEmptyGptData();
GptHeader* h1 = (GptHeader*)gpt->primary_header;
BuildTestGptData(gpt);
EXPECT(HeaderCrc(h1) == h1->header_crc32);
/* CRC covers first byte of header */
BuildTestGptData(gpt);
gpt->primary_header[0] ^= 0xa5;
EXPECT(HeaderCrc(h1) != h1->header_crc32);
/* CRC covers last byte of header */
BuildTestGptData(gpt);
gpt->primary_header[h1->size - 1] ^= 0x5a;
EXPECT(HeaderCrc(h1) != h1->header_crc32);
/* CRC only covers header */
BuildTestGptData(gpt);
gpt->primary_header[h1->size] ^= 0x5a;
EXPECT(HeaderCrc(h1) == h1->header_crc32);
return TEST_OK;
}
/* Tests if signature ("EFI PART") is checked. */
static int SignatureTest() {
GptData* gpt = GetEmptyGptData();
GptHeader* h1 = (GptHeader*)gpt->primary_header;
GptHeader* h2 = (GptHeader*)gpt->secondary_header;
int i;
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() {
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() {
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;
}
/* Tests if CRC is checked. */
static int CrcFieldTest() {
GptData* gpt = GetEmptyGptData();
GptHeader* h1 = (GptHeader*)gpt->primary_header;
GptHeader* h2 = (GptHeader*)gpt->secondary_header;
BuildTestGptData(gpt);
/* Modify a field that the header verification doesn't care about */
h1->entries_crc32++;
h2->entries_crc32++;
EXPECT(1 == CheckHeader(h1, 0, gpt->drive_sectors));
EXPECT(1 == CheckHeader(h2, 1, gpt->drive_sectors));
/* Refresh the CRC; should pass now */
RefreshCrc32(gpt);
EXPECT(0 == CheckHeader(h1, 0, gpt->drive_sectors));
EXPECT(0 == CheckHeader(h2, 1, gpt->drive_sectors));
return TEST_OK;
}
/* Tests if reserved fields are checked.
* We'll try non-zero values to test. */
static int ReservedFieldsTest() {
GptData* gpt = GetEmptyGptData();
GptHeader* h1 = (GptHeader*)gpt->primary_header;
GptHeader* h2 = (GptHeader*)gpt->secondary_header;
BuildTestGptData(gpt);
h1->reserved ^= 0x12345678; /* whatever random */
h2->reserved ^= 0x12345678; /* whatever random */
RefreshCrc32(gpt);
EXPECT(1 == CheckHeader(h1, 0, gpt->drive_sectors));
EXPECT(1 == CheckHeader(h2, 1, gpt->drive_sectors));
#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() {
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() {
GptData* gpt = GetEmptyGptData();
GptHeader* h1 = (GptHeader*)gpt->primary_header;
GptHeader* h2 = (GptHeader*)gpt->secondary_header;
BuildTestGptData(gpt);
h1->number_of_entries--;
h2->number_of_entries /= 2;
RefreshCrc32(gpt);
EXPECT(1 == CheckHeader(h1, 0, gpt->drive_sectors));
EXPECT(1 == CheckHeader(h2, 1, gpt->drive_sectors));
return TEST_OK;
}
/* Tests if myLBA field is checked (1 for primary, last for secondary). */
static int MyLbaTest() {
GptData* gpt = GetEmptyGptData();
GptHeader* h1 = (GptHeader*)gpt->primary_header;
GptHeader* h2 = (GptHeader*)gpt->secondary_header;
/* myLBA depends on primary vs secondary flag */
BuildTestGptData(gpt);
EXPECT(1 == CheckHeader(h1, 1, gpt->drive_sectors));
EXPECT(1 == CheckHeader(h2, 0, gpt->drive_sectors));
BuildTestGptData(gpt);
h1->my_lba--;
h2->my_lba--;
RefreshCrc32(gpt);
EXPECT(1 == CheckHeader(h1, 0, gpt->drive_sectors));
EXPECT(1 == CheckHeader(h2, 1, gpt->drive_sectors));
BuildTestGptData(gpt);
h1->my_lba = 2;
h2->my_lba--;
RefreshCrc32(gpt);
EXPECT(1 == CheckHeader(h1, 0, gpt->drive_sectors));
EXPECT(1 == CheckHeader(h2, 1, gpt->drive_sectors));
/* 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);
EXPECT(1 == CheckHeader(h1, 0, gpt->drive_sectors));
EXPECT(1 == CheckHeader(h2, 1, gpt->drive_sectors));
BuildTestGptData(gpt);
h1->entries_lba--;
h2->entries_lba--;
RefreshCrc32(gpt);
EXPECT(1 == CheckHeader(h1, 0, gpt->drive_sectors));
EXPECT(1 == CheckHeader(h2, 1, gpt->drive_sectors));
return TEST_OK;
}
/* Tests if FirstUsableLBA and LastUsableLBA are checked.
* FirstUsableLBA must be after the end of the primary GPT table array.
* LastUsableLBA must be before the start of the secondary GPT table array.
* FirstUsableLBA <= LastUsableLBA. */
static int FirstUsableLbaAndLastUsableLbaTest() {
GptData* gpt = GetEmptyGptData();
GptHeader* h1 = (GptHeader*)gpt->primary_header;
GptHeader* h2 = (GptHeader*)gpt->secondary_header;
int i;
struct {
uint64_t primary_entries_lba;
uint64_t primary_first_usable_lba;
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;
}
/* Tests if PartitionEntryArrayCRC32 is checked.
* PartitionEntryArrayCRC32 must be calculated over SizeOfPartitionEntry *
* NumberOfPartitionEntries bytes.
*/
static int EntriesCrcTest() {
GptData* gpt = GetEmptyGptData();
GptHeader* h1 = (GptHeader*)gpt->primary_header;
GptEntry* e1 = (GptEntry*)(gpt->primary_entries);
GptEntry* e2 = (GptEntry*)(gpt->secondary_entries);
/* Modify the first byte of primary entries, and expect the CRC is wrong. */
BuildTestGptData(gpt);
EXPECT(0 == CheckEntries(e1, h1, gpt->drive_sectors));
EXPECT(0 == CheckEntries(e2, h1, gpt->drive_sectors));
gpt->primary_entries[0] ^= 0xa5; /* just XOR a non-zero value */
gpt->secondary_entries[TOTAL_ENTRIES_SIZE-1] ^= 0x5a;
EXPECT(1 == CheckEntries(e1, h1, gpt->drive_sectors));
EXPECT(1 == CheckEntries(e2, h1, gpt->drive_sectors));
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
*/
static int ValidEntryTest() {
GptData* gpt = GetEmptyGptData();
GptHeader* h1 = (GptHeader*)gpt->primary_header;
GptEntry* e1 = (GptEntry*)(gpt->primary_entries);
/* error case: entry.StartingLBA < header.FirstUsableLBA */
BuildTestGptData(gpt);
e1[0].starting_lba = h1->first_usable_lba - 1;
RefreshCrc32(gpt);
EXPECT(1 == CheckEntries(e1, h1, gpt->drive_sectors));
/* error case: entry.EndingLBA > header.LastUsableLBA */
BuildTestGptData(gpt);
e1[2].ending_lba = h1->last_usable_lba + 1;
RefreshCrc32(gpt);
EXPECT(1 == CheckEntries(e1, h1, gpt->drive_sectors));
/* error case: entry.StartingLBA > entry.EndingLBA */
BuildTestGptData(gpt);
e1[3].starting_lba = e1[3].ending_lba + 1;
RefreshCrc32(gpt);
EXPECT(1 == CheckEntries(e1, h1, gpt->drive_sectors));
/* case: non active entry should be ignored. */
BuildTestGptData(gpt);
Memset(&e1[1].type, 0, sizeof(e1[1].type));
e1[1].starting_lba = e1[1].ending_lba + 1;
RefreshCrc32(gpt);
EXPECT(0 == CheckEntries(e1, h1, gpt->drive_sectors));
return TEST_OK;
}
/* Tests if overlapped partition tables can be detected. */
static int OverlappedPartitionTest() {
GptData* gpt = GetEmptyGptData();
GptHeader* h = (GptHeader*)gpt->primary_header;
GptEntry* e = (GptEntry*)gpt->primary_entries;
int i, j;
struct {
int overlapped;
struct {
int active;
uint64_t starting_lba;
uint64_t ending_lba;
} entries[16]; /* enough for testing. */
} cases[] = {
{0, {{0, 100, 199}}},
{0, {{1, 100, 199}}},
{0, {{1, 100, 150}, {1, 200, 250}, {1, 300, 350}}},
{1, {{1, 200, 299}, {1, 100, 199}, {1, 100, 100}}},
{1, {{1, 200, 299}, {1, 100, 199}, {1, 299, 299}}},
{0, {{1, 300, 399}, {1, 200, 299}, {1, 100, 199}}},
{1, {{1, 100, 199}, {1, 199, 299}, {1, 299, 399}}},
{1, {{1, 100, 199}, {1, 200, 299}, {1, 75, 399}}},
{1, {{1, 100, 199}, {1, 75, 250}, {1, 200, 299}}},
{1, {{1, 75, 150}, {1, 100, 199}, {1, 200, 299}}},
{1, {{1, 200, 299}, {1, 100, 199}, {1, 300, 399}, {1, 100, 399}}},
{0, {{1, 200, 299}, {1, 100, 199}, {1, 300, 399}, {0, 100, 399}}},
{1, {{1, 200, 300}, {1, 100, 200}, {1, 100, 400}, {1, 300, 400}}},
{1, {{0, 200, 300}, {1, 100, 200}, {1, 100, 400}, {1, 300, 400}}},
{0, {{1, 200, 300}, {1, 100, 199}, {0, 100, 400}, {0, 300, 400}}},
{1, {{1, 200, 299}, {1, 100, 199}, {1, 199, 199}}},
{0, {{1, 200, 299}, {0, 100, 199}, {1, 199, 199}}},
{0, {{1, 200, 299}, {1, 100, 199}, {0, 199, 199}}},
{1, {{1, 199, 199}, {1, 200, 200}, {1, 201, 201}, {1, 202, 202},
{1, 203, 203}, {1, 204, 204}, {1, 205, 205}, {1, 206, 206},
{1, 207, 207}, {1, 208, 208}, {1, 199, 199}}},
{0, {{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);
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));
e[j].starting_lba = cases[i].entries[j].starting_lba;
e[j].ending_lba = cases[i].entries[j].ending_lba;
}
RefreshCrc32(gpt);
EXPECT(cases[i].overlapped == CheckEntries(e, h, gpt->drive_sectors));
}
return TEST_OK;
}
/* Test both sanity checking and repair. */
static int SanityCheckTest() {
GptData* gpt = GetEmptyGptData();
GptHeader* h1 = (GptHeader*)gpt->primary_header;
/* 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);
/* Modify headers */
BuildTestGptData(gpt);
gpt->primary_header[0]++;
gpt->secondary_header[0]++;
EXPECT(GPT_ERROR_INVALID_HEADERS == GptSanityCheck(gpt));
EXPECT(0 == gpt->valid_headers);
EXPECT(0 == gpt->valid_entries);
/* Repair can't fix completely busted headers */
GptRepair(gpt);
EXPECT(GPT_ERROR_INVALID_HEADERS == GptSanityCheck(gpt));
EXPECT(0 == gpt->valid_headers);
EXPECT(0 == gpt->valid_entries);
EXPECT(0 == gpt->modified);
BuildTestGptData(gpt);
gpt->primary_header[0]++;
EXPECT(GPT_SUCCESS == GptSanityCheck(gpt));
EXPECT(MASK_SECONDARY == gpt->valid_headers);
EXPECT(MASK_BOTH == gpt->valid_entries);
GptRepair(gpt);
EXPECT(GPT_SUCCESS == GptSanityCheck(gpt));
EXPECT(MASK_BOTH == gpt->valid_headers);
EXPECT(MASK_BOTH == gpt->valid_entries);
EXPECT(GPT_MODIFIED_HEADER1 == gpt->modified);
BuildTestGptData(gpt);
gpt->secondary_header[0]++;
EXPECT(GPT_SUCCESS == GptSanityCheck(gpt));
EXPECT(MASK_PRIMARY == gpt->valid_headers);
EXPECT(MASK_BOTH == gpt->valid_entries);
GptRepair(gpt);
EXPECT(GPT_SUCCESS == GptSanityCheck(gpt));
EXPECT(MASK_BOTH == gpt->valid_headers);
EXPECT(MASK_BOTH == gpt->valid_entries);
EXPECT(GPT_MODIFIED_HEADER2 == gpt->modified);
/* Modify header1 and update its CRC. Since header2 is now different than
* header1, it'll be the one considered invalid. */
BuildTestGptData(gpt);
h1->size++;
RefreshCrc32(gpt);
EXPECT(GPT_SUCCESS == GptSanityCheck(gpt));
EXPECT(MASK_PRIMARY == gpt->valid_headers);
EXPECT(MASK_BOTH == gpt->valid_entries);
GptRepair(gpt);
EXPECT(GPT_SUCCESS == GptSanityCheck(gpt));
EXPECT(MASK_BOTH == gpt->valid_headers);
EXPECT(MASK_BOTH == gpt->valid_entries);
EXPECT(GPT_MODIFIED_HEADER2 == gpt->modified);
/* Modify entries */
BuildTestGptData(gpt);
gpt->primary_entries[0]++;
gpt->secondary_entries[0]++;
EXPECT(GPT_ERROR_INVALID_ENTRIES == GptSanityCheck(gpt));
EXPECT(MASK_BOTH == gpt->valid_headers);
EXPECT(MASK_NONE == gpt->valid_entries);
/* Repair can't fix both copies of entries being bad, either. */
GptRepair(gpt);
EXPECT(GPT_ERROR_INVALID_ENTRIES == GptSanityCheck(gpt));
EXPECT(MASK_BOTH == gpt->valid_headers);
EXPECT(MASK_NONE == gpt->valid_entries);
EXPECT(0 == gpt->modified);
BuildTestGptData(gpt);
gpt->primary_entries[0]++;
EXPECT(GPT_SUCCESS == GptSanityCheck(gpt));
EXPECT(MASK_BOTH == gpt->valid_headers);
EXPECT(MASK_SECONDARY == gpt->valid_entries);
GptRepair(gpt);
EXPECT(GPT_SUCCESS == GptSanityCheck(gpt));
EXPECT(MASK_BOTH == gpt->valid_headers);
EXPECT(MASK_BOTH == gpt->valid_entries);
EXPECT(GPT_MODIFIED_ENTRIES1 == gpt->modified);
BuildTestGptData(gpt);
gpt->secondary_entries[0]++;
EXPECT(GPT_SUCCESS == GptSanityCheck(gpt));
EXPECT(MASK_BOTH == gpt->valid_headers);
EXPECT(MASK_PRIMARY == gpt->valid_entries);
GptRepair(gpt);
EXPECT(GPT_SUCCESS == GptSanityCheck(gpt));
EXPECT(MASK_BOTH == gpt->valid_headers);
EXPECT(MASK_BOTH == gpt->valid_entries);
EXPECT(GPT_MODIFIED_ENTRIES2 == gpt->modified);
/* Test cross-correction (h1+e2, h2+e1) */
BuildTestGptData(gpt);
gpt->primary_header[0]++;
gpt->secondary_entries[0]++;
EXPECT(GPT_SUCCESS == GptSanityCheck(gpt));
EXPECT(MASK_SECONDARY == gpt->valid_headers);
EXPECT(MASK_PRIMARY == gpt->valid_entries);
GptRepair(gpt);
EXPECT(GPT_SUCCESS == GptSanityCheck(gpt));
EXPECT(MASK_BOTH == gpt->valid_headers);
EXPECT(MASK_BOTH == gpt->valid_entries);
EXPECT((GPT_MODIFIED_HEADER1 | GPT_MODIFIED_ENTRIES2) == gpt->modified);
BuildTestGptData(gpt);
gpt->secondary_header[0]++;
gpt->primary_entries[0]++;
EXPECT(GPT_SUCCESS == GptSanityCheck(gpt));
EXPECT(MASK_PRIMARY == gpt->valid_headers);
EXPECT(MASK_SECONDARY == gpt->valid_entries);
GptRepair(gpt);
EXPECT(GPT_SUCCESS == GptSanityCheck(gpt));
EXPECT(MASK_BOTH == gpt->valid_headers);
EXPECT(MASK_BOTH == gpt->valid_entries);
EXPECT((GPT_MODIFIED_HEADER2 | GPT_MODIFIED_ENTRIES1) == gpt->modified);
/* 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() {
GptData* gpt = GetEmptyGptData();
GptEntry* e = (GptEntry*)(gpt->primary_entries);
e->attributes = 0x0000000000000000LLU;
SetEntrySuccessful(e, 1);
EXPECT(0x0100000000000000LLU == e->attributes);
EXPECT(1 == GetEntrySuccessful(e));
e->attributes = 0xFFFFFFFFFFFFFFFFLLU;
SetEntrySuccessful(e, 0);
EXPECT(0xFEFFFFFFFFFFFFFFLLU == e->attributes);
EXPECT(0 == GetEntrySuccessful(e));
e->attributes = 0x0000000000000000LLU;
SetEntryTries(e, 15);
EXPECT(15 == GetEntryTries(e));
EXPECT(0x00F0000000000000LLU == e->attributes);
e->attributes = 0xFFFFFFFFFFFFFFFFLLU;
SetEntryTries(e, 0);
EXPECT(0xFF0FFFFFFFFFFFFFLLU == e->attributes);
EXPECT(0 == GetEntryTries(e));
e->attributes = 0x0000000000000000LLU;
SetEntryPriority(e, 15);
EXPECT(0x000F000000000000LLU == e->attributes);
EXPECT(15 == GetEntryPriority(e));
e->attributes = 0xFFFFFFFFFFFFFFFFLLU;
SetEntryPriority(e, 0);
EXPECT(0xFFF0FFFFFFFFFFFFLLU == e->attributes);
EXPECT(0 == GetEntryPriority(e));
e->attributes = 0xFFFFFFFFFFFFFFFFLLU;
EXPECT(1 == GetEntrySuccessful(e));
EXPECT(15 == GetEntryPriority(e));
EXPECT(15 == GetEntryTries(e));
e->attributes = 0x0123000000000000LLU;
EXPECT(1 == GetEntrySuccessful(e));
EXPECT(2 == GetEntryTries(e));
EXPECT(3 == GetEntryPriority(e));
return TEST_OK;
}
static int EntryTypeTest() {
GptData* gpt = GetEmptyGptData();
GptEntry* e = (GptEntry*)(gpt->primary_entries);
Memcpy(&e->type, &guid_zero, sizeof(Guid));
EXPECT(1 == IsUnusedEntry(e));
EXPECT(0 == IsKernelEntry(e));
Memcpy(&e->type, &guid_kernel, sizeof(Guid));
EXPECT(0 == IsUnusedEntry(e));
EXPECT(1 == IsKernelEntry(e));
Memcpy(&e->type, &guid_rootfs, sizeof(Guid));
EXPECT(0 == IsUnusedEntry(e));
EXPECT(0 == IsKernelEntry(e));
return TEST_OK;
}
/* Make an entry unused by clearing its type. */
static void FreeEntry(GptEntry* e) {
Memset(&e->type, 0, sizeof(Guid));
}
/* Set up an entry. */
static void FillEntry(GptEntry* e, int is_kernel,
int priority, int successful, int tries) {
Memcpy(&e->type, (is_kernel ? &guid_kernel : &guid_zero), sizeof(Guid));
SetEntryPriority(e, priority);
SetEntrySuccessful(e, successful);
SetEntryTries(e, tries);
}
/* Invalidate all kernel entries and expect GptNextKernelEntry() cannot find
* any usable kernel entry.
*/
static int NoValidKernelEntryTest() {
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 GetNextNormalTest() {
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() {
GptData* gpt = GetEmptyGptData();
GptEntry* e1 = (GptEntry*)(gpt->primary_entries);
uint64_t start, size;
/* Priority 3, 4, 0, 4 - should boot order B, Y, A */
BuildTestGptData(gpt);
FillEntry(e1 + KERNEL_A, 1, 3, 1, 0);
FillEntry(e1 + KERNEL_B, 1, 4, 1, 0);
FillEntry(e1 + KERNEL_X, 1, 0, 1, 0);
FillEntry(e1 + KERNEL_Y, 1, 4, 1, 0);
RefreshCrc32(gpt);
GptInit(gpt);
EXPECT(GPT_SUCCESS == GptNextKernelEntry(gpt, &start, &size));
EXPECT(KERNEL_B == gpt->current_kernel);
EXPECT(GPT_SUCCESS == GptNextKernelEntry(gpt, &start, &size));
EXPECT(KERNEL_Y == gpt->current_kernel);
EXPECT(GPT_SUCCESS == GptNextKernelEntry(gpt, &start, &size));
EXPECT(KERNEL_A == gpt->current_kernel);
EXPECT(GPT_ERROR_NO_VALID_KERNEL == GptNextKernelEntry(gpt, &start, &size));
return TEST_OK;
}
static int GetNextTriesTest() {
GptData* gpt = GetEmptyGptData();
GptEntry* e1 = (GptEntry*)(gpt->primary_entries);
uint64_t start, size;
/* Tries=nonzero is attempted just like success, but tries=0 isn't */
BuildTestGptData(gpt);
FillEntry(e1 + KERNEL_A, 1, 2, 1, 0);
FillEntry(e1 + KERNEL_B, 1, 3, 0, 0);
FillEntry(e1 + KERNEL_X, 1, 4, 0, 1);
FillEntry(e1 + KERNEL_Y, 1, 0, 0, 5);
RefreshCrc32(gpt);
GptInit(gpt);
EXPECT(GPT_SUCCESS == GptNextKernelEntry(gpt, &start, &size));
EXPECT(KERNEL_X == gpt->current_kernel);
EXPECT(GPT_SUCCESS == GptNextKernelEntry(gpt, &start, &size));
EXPECT(KERNEL_A == gpt->current_kernel);
EXPECT(GPT_ERROR_NO_VALID_KERNEL == GptNextKernelEntry(gpt, &start, &size));
return TEST_OK;
}
static int GptUpdateTest() {
GptData* gpt = GetEmptyGptData();
GptEntry* e = (GptEntry*)(gpt->primary_entries);
GptEntry* e2 = (GptEntry*)(gpt->secondary_entries);
uint64_t start, size;
/* Tries=nonzero is attempted just like success, but tries=0 isn't */
BuildTestGptData(gpt);
FillEntry(e + KERNEL_A, 1, 4, 1, 0);
FillEntry(e + KERNEL_B, 1, 3, 0, 2);
FillEntry(e + KERNEL_X, 1, 2, 0, 2);
RefreshCrc32(gpt);
GptInit(gpt);
gpt->modified = 0; /* Nothing modified yet */
/* Successful kernel */
EXPECT(GPT_SUCCESS == GptNextKernelEntry(gpt, &start, &size));
EXPECT(KERNEL_A == gpt->current_kernel);
EXPECT(1 == GetEntrySuccessful(e + KERNEL_A));
EXPECT(4 == GetEntryPriority(e + KERNEL_A));
EXPECT(0 == GetEntryTries(e + KERNEL_A));
EXPECT(1 == GetEntrySuccessful(e2 + KERNEL_A));
EXPECT(4 == GetEntryPriority(e2 + KERNEL_A));
EXPECT(0 == GetEntryTries(e2 + KERNEL_A));
/* Trying successful kernel changes nothing */
EXPECT(GPT_SUCCESS == GptUpdateKernelEntry(gpt, GPT_UPDATE_ENTRY_TRY));
EXPECT(1 == GetEntrySuccessful(e + KERNEL_A));
EXPECT(4 == GetEntryPriority(e + KERNEL_A));
EXPECT(0 == GetEntryTries(e + KERNEL_A));
EXPECT(0 == gpt->modified);
/* Marking it bad does, though */
EXPECT(GPT_SUCCESS == GptUpdateKernelEntry(gpt, GPT_UPDATE_ENTRY_BAD));
EXPECT(0 == GetEntrySuccessful(e + KERNEL_A));
EXPECT(0 == GetEntryPriority(e + KERNEL_A));
EXPECT(0 == GetEntryTries(e + KERNEL_A));
/* Which affects both copies of the partition entries */
EXPECT(0 == GetEntrySuccessful(e2 + KERNEL_A));
EXPECT(0 == GetEntryPriority(e2 + KERNEL_A));
EXPECT(0 == GetEntryTries(e2 + KERNEL_A));
/* And that's caused the GPT to need updating */
EXPECT(0x0F == gpt->modified);
/* Kernel with tries */
EXPECT(GPT_SUCCESS == GptNextKernelEntry(gpt, &start, &size));
EXPECT(KERNEL_B == gpt->current_kernel);
EXPECT(0 == GetEntrySuccessful(e + KERNEL_B));
EXPECT(3 == GetEntryPriority(e + KERNEL_B));
EXPECT(2 == GetEntryTries(e + KERNEL_B));
/* Marking it bad clears it */
EXPECT(GPT_SUCCESS == GptUpdateKernelEntry(gpt, GPT_UPDATE_ENTRY_BAD));
EXPECT(0 == GetEntrySuccessful(e + KERNEL_B));
EXPECT(0 == GetEntryPriority(e + KERNEL_B));
EXPECT(0 == GetEntryTries(e + KERNEL_B));
/* Another kernel with tries */
EXPECT(GPT_SUCCESS == GptNextKernelEntry(gpt, &start, &size));
EXPECT(KERNEL_X == gpt->current_kernel);
EXPECT(0 == GetEntrySuccessful(e + KERNEL_X));
EXPECT(2 == GetEntryPriority(e + KERNEL_X));
EXPECT(2 == GetEntryTries(e + KERNEL_X));
/* Trying it uses up a try */
EXPECT(GPT_SUCCESS == GptUpdateKernelEntry(gpt, GPT_UPDATE_ENTRY_TRY));
EXPECT(0 == GetEntrySuccessful(e + KERNEL_X));
EXPECT(2 == GetEntryPriority(e + KERNEL_X));
EXPECT(1 == GetEntryTries(e + KERNEL_X));
EXPECT(0 == GetEntrySuccessful(e2 + KERNEL_X));
EXPECT(2 == GetEntryPriority(e2 + KERNEL_X));
EXPECT(1 == GetEntryTries(e2 + KERNEL_X));
/* Trying it again marks it inactive */
EXPECT(GPT_SUCCESS == GptUpdateKernelEntry(gpt, GPT_UPDATE_ENTRY_TRY));
EXPECT(0 == GetEntrySuccessful(e + KERNEL_X));
EXPECT(0 == GetEntryPriority(e + KERNEL_X));
EXPECT(0 == GetEntryTries(e + KERNEL_X));
return TEST_OK;
}
/* Given an invalid kernel type, and expect GptUpdateKernelEntry() returns
* GPT_ERROR_INVALID_UPDATE_TYPE. */
static int UpdateInvalidKernelTypeTest() {
GptData* 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;
}
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(HeaderCrcTest), },
{ 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(EntryAttributeGetSetTest), },
{ TEST_CASE(EntryTypeTest), },
{ TEST_CASE(GetNextNormalTest), },
{ TEST_CASE(GetNextPrioTest), },
{ TEST_CASE(GetNextTriesTest), },
{ TEST_CASE(GptUpdateTest), },
{ TEST_CASE(UpdateInvalidKernelTypeTest), },
{ 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;
}