mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2025-11-24 02:05:01 +00:00
1181 lines
39 KiB
C
1181 lines
39 KiB
C
/* Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
* found in the LICENSE file.
|
|
*/
|
|
|
|
#include "cgptlib_test.h"
|
|
#include <string.h>
|
|
#include "cgptlib.h"
|
|
#include "cgptlib_internal.h"
|
|
#include "crc32.h"
|
|
#include "crc32_test.h"
|
|
#include "gpt.h"
|
|
#include "quick_sort_test.h"
|
|
#include "utility.h"
|
|
|
|
/* Testing partition layout (sector_bytes=512)
|
|
*
|
|
* LBA Size Usage
|
|
* ---------------------------------------------------------
|
|
* 0 1 PMBR
|
|
* 1 1 primary partition header
|
|
* 2 32 primary partition entries (128B * 128)
|
|
* 34 100 kernel A (index: 0)
|
|
* 134 100 root A (index: 1)
|
|
* 234 100 root B (index: 2)
|
|
* 334 100 kernel B (index: 3)
|
|
* 434 32 secondary partition entries
|
|
* 466 1 secondary partition header
|
|
* 467
|
|
*/
|
|
#define KERNEL_A 0
|
|
#define ROOTFS_A 1
|
|
#define ROOTFS_B 2
|
|
#define KERNEL_B 3
|
|
|
|
#define DEFAULT_SECTOR_SIZE 512
|
|
#define MAX_SECTOR_SIZE 4096
|
|
#define DEFAULT_DRIVE_SECTORS 467
|
|
#define PARTITION_ENTRIES_SIZE TOTAL_ENTRIES_SIZE /* 16384 */
|
|
|
|
/* Given a GptData pointer, first re-calculate entries CRC32 value,
|
|
* then reset header CRC32 value to 0, and calculate header CRC32 value.
|
|
* Both primary and secondary are updated. */
|
|
void RefreshCrc32(GptData *gpt) {
|
|
GptHeader *header, *header2;
|
|
GptEntry *entries, *entries2;
|
|
|
|
header = (GptHeader*)gpt->primary_header;
|
|
entries = (GptEntry*)gpt->primary_entries;
|
|
header2 = (GptHeader*)gpt->secondary_header;
|
|
entries2 = (GptEntry*)gpt->secondary_entries;
|
|
|
|
header->entries_crc32 =
|
|
Crc32((uint8_t*)entries,
|
|
header->number_of_entries * header->size_of_entry);
|
|
header->header_crc32 = 0;
|
|
header->header_crc32 = Crc32((uint8_t*)header, header->size);
|
|
header2->entries_crc32 =
|
|
Crc32((uint8_t*)entries2,
|
|
header2->number_of_entries * header2->size_of_entry);
|
|
header2->header_crc32 = 0;
|
|
header2->header_crc32 = Crc32((uint8_t*)header2, header2->size);
|
|
}
|
|
|
|
void ZeroHeaders(GptData* gpt) {
|
|
Memset(gpt->primary_header, 0, MAX_SECTOR_SIZE);
|
|
Memset(gpt->secondary_header, 0, MAX_SECTOR_SIZE);
|
|
}
|
|
|
|
void ZeroEntries(GptData* gpt) {
|
|
Memset(gpt->primary_entries, 0, PARTITION_ENTRIES_SIZE);
|
|
Memset(gpt->secondary_entries, 0, PARTITION_ENTRIES_SIZE);
|
|
}
|
|
|
|
void ZeroHeadersEntries(GptData* gpt) {
|
|
ZeroHeaders(gpt);
|
|
ZeroEntries(gpt);
|
|
}
|
|
|
|
/* Returns a pointer to a static GptData instance (no free is required).
|
|
* All fields are zero except 4 pointers linking to header and entries.
|
|
* All content of headers and entries are zero. */
|
|
GptData* GetEmptyGptData() {
|
|
static GptData gpt;
|
|
static uint8_t primary_header[MAX_SECTOR_SIZE];
|
|
static uint8_t primary_entries[PARTITION_ENTRIES_SIZE];
|
|
static uint8_t secondary_header[MAX_SECTOR_SIZE];
|
|
static uint8_t secondary_entries[PARTITION_ENTRIES_SIZE];
|
|
|
|
Memset(&gpt, 0, sizeof(gpt));
|
|
gpt.primary_header = primary_header;
|
|
gpt.primary_entries = primary_entries;
|
|
gpt.secondary_header = secondary_header;
|
|
gpt.secondary_entries = secondary_entries;
|
|
ZeroHeadersEntries(&gpt);
|
|
|
|
/* Initialize GptData internal states. */
|
|
gpt.current_kernel = CGPT_KERNEL_ENTRY_NOT_FOUND;
|
|
|
|
return &gpt;
|
|
}
|
|
|
|
/* Fills in most of fields and creates the layout described in the top of this
|
|
* file. Before calling this function, primary/secondary header/entries must
|
|
* have been pointed to the buffer, say, a gpt returned from GetEmptyGptData().
|
|
* This function returns a good (valid) copy of GPT layout described in top of
|
|
* this file. */
|
|
void BuildTestGptData(GptData *gpt) {
|
|
GptHeader *header, *header2;
|
|
GptEntry *entries, *entries2;
|
|
Guid chromeos_kernel = GPT_ENT_TYPE_CHROMEOS_KERNEL;
|
|
Guid chromeos_rootfs = GPT_ENT_TYPE_CHROMEOS_ROOTFS;
|
|
|
|
gpt->sector_bytes = DEFAULT_SECTOR_SIZE;
|
|
gpt->drive_sectors = DEFAULT_DRIVE_SECTORS;
|
|
gpt->current_kernel = CGPT_KERNEL_ENTRY_NOT_FOUND;
|
|
|
|
/* build primary */
|
|
header = (GptHeader*)gpt->primary_header;
|
|
entries = (GptEntry*)gpt->primary_entries;
|
|
Memcpy(header->signature, GPT_HEADER_SIGNATURE, sizeof(GPT_HEADER_SIGNATURE));
|
|
header->revision = GPT_HEADER_REVISION;
|
|
header->size = sizeof(GptHeader) - sizeof(header->padding);
|
|
header->reserved = 0;
|
|
header->my_lba = 1;
|
|
header->first_usable_lba = 34;
|
|
header->last_usable_lba = DEFAULT_DRIVE_SECTORS - 1 - 32 - 1; /* 433 */
|
|
header->entries_lba = 2;
|
|
header->number_of_entries = 128; /* 512B / 128B * 32sectors = 128 entries */
|
|
header->size_of_entry = 128; /* bytes */
|
|
Memcpy(&entries[0].type, &chromeos_kernel, sizeof(chromeos_kernel));
|
|
entries[0].starting_lba = 34;
|
|
entries[0].ending_lba = 133;
|
|
Memcpy(&entries[1].type, &chromeos_rootfs, sizeof(chromeos_rootfs));
|
|
entries[1].starting_lba = 134;
|
|
entries[1].ending_lba = 233;
|
|
Memcpy(&entries[2].type, &chromeos_rootfs, sizeof(chromeos_rootfs));
|
|
entries[2].starting_lba = 234;
|
|
entries[2].ending_lba = 333;
|
|
Memcpy(&entries[3].type, &chromeos_kernel, sizeof(chromeos_kernel));
|
|
entries[3].starting_lba = 334;
|
|
entries[3].ending_lba = 433;
|
|
header->padding = 0;
|
|
|
|
/* build secondary */
|
|
header2 = (GptHeader*)gpt->secondary_header;
|
|
entries2 = (GptEntry*)gpt->secondary_entries;
|
|
Memcpy(header2, header, sizeof(GptHeader));
|
|
Memcpy(entries2, entries, PARTITION_ENTRIES_SIZE);
|
|
header2->my_lba = DEFAULT_DRIVE_SECTORS - 1; /* 466 */
|
|
header2->entries_lba = DEFAULT_DRIVE_SECTORS - 1 - 32; /* 434 */
|
|
|
|
RefreshCrc32(gpt);
|
|
}
|
|
|
|
/* Dumps memory starting from [vp] with [len] bytes.
|
|
* Prints [memo] if not NULL. Example output:
|
|
*
|
|
* 00 01 02 03 04 05 06 07 - 08 09 0a 0b 0c 0d 0e 0f
|
|
* 10 11 12 13 14 15 16 17 - 18 19 1a 1b 1c 1d 1e 1f
|
|
* ...
|
|
*/
|
|
static void Dump(void *vp, int len, char* memo) {
|
|
uint8_t *start = vp;
|
|
int i;
|
|
if (memo) printf("--[%s]----------\n", memo);
|
|
for (i = 0; i < len; ++i) {
|
|
printf("%02x%s", start[i],
|
|
(!(~i & 15) ? "\n" :
|
|
!(~i & 7) ? " - ": " "));
|
|
}
|
|
if (i&15) printf("\n");
|
|
}
|
|
|
|
/* More formatted dump with GptData. */
|
|
void DumpGptData(GptData *gpt) {
|
|
printf("DumpGptData(%p)...\n", gpt);
|
|
Dump(gpt, sizeof(gpt), NULL);
|
|
Dump(gpt->primary_header, sizeof(GptHeader), "Primary header");
|
|
Dump(gpt->primary_entries, sizeof(GptEntry) * 8, "Primary entries");
|
|
Dump(gpt->secondary_header, sizeof(GptHeader), "Secondary header");
|
|
Dump(gpt->secondary_entries, sizeof(GptEntry) * 8,
|
|
"Secondary entries");
|
|
}
|
|
|
|
/* Tests if the default structure returned by BuildTestGptData() is good. */
|
|
int TestBuildTestGptData() {
|
|
GptData *gpt;
|
|
gpt = GetEmptyGptData();
|
|
BuildTestGptData(gpt);
|
|
EXPECT(GPT_SUCCESS == GptInit(gpt));
|
|
return TEST_OK;
|
|
}
|
|
|
|
/* Tests if wrong sector_bytes or drive_sectors is detected by GptInit().
|
|
* Currently we only support 512 bytes per sector.
|
|
* In the future, we may support other sizes.
|
|
* A too small drive_sectors should be rejected by GptInit(). */
|
|
int ParameterTests() {
|
|
GptData *gpt;
|
|
struct {
|
|
uint32_t sector_bytes;
|
|
uint64_t drive_sectors;
|
|
int expected_retval;
|
|
} cases[] = {
|
|
{512, DEFAULT_DRIVE_SECTORS, GPT_SUCCESS},
|
|
{520, DEFAULT_DRIVE_SECTORS, GPT_ERROR_INVALID_SECTOR_SIZE},
|
|
{512, 0, GPT_ERROR_INVALID_SECTOR_NUMBER},
|
|
{512, 66, GPT_ERROR_INVALID_SECTOR_NUMBER},
|
|
{512, GPT_PMBR_SECTOR + GPT_HEADER_SECTOR * 2 + GPT_ENTRIES_SECTORS * 2,
|
|
GPT_SUCCESS},
|
|
{4096, DEFAULT_DRIVE_SECTORS, GPT_ERROR_INVALID_SECTOR_SIZE},
|
|
};
|
|
int i;
|
|
|
|
gpt = GetEmptyGptData();
|
|
for (i = 0; i < ARRAY_SIZE(cases); ++i) {
|
|
BuildTestGptData(gpt);
|
|
gpt->sector_bytes = cases[i].sector_bytes;
|
|
gpt->drive_sectors = cases[i].drive_sectors;
|
|
EXPECT(cases[i].expected_retval == CheckParameters(gpt));
|
|
}
|
|
|
|
return TEST_OK;
|
|
}
|
|
|
|
/* Tests if signature ("EFI PART") is checked. */
|
|
int SignatureTest() {
|
|
int i;
|
|
GptData *gpt;
|
|
int test_mask;
|
|
GptHeader *headers[2];
|
|
|
|
gpt = GetEmptyGptData();
|
|
headers[PRIMARY] = (GptHeader*)gpt->primary_header;
|
|
headers[SECONDARY] = (GptHeader*)gpt->secondary_header;
|
|
|
|
for (test_mask = MASK_PRIMARY; test_mask <= MASK_BOTH; ++test_mask) {
|
|
for (i = 0; i < 8; ++i) {
|
|
BuildTestGptData(gpt);
|
|
if (test_mask & MASK_PRIMARY)
|
|
headers[PRIMARY]->signature[i] ^= 0xff;
|
|
if (test_mask & MASK_SECONDARY)
|
|
headers[SECONDARY]->signature[i] ^= 0xff;
|
|
EXPECT((MASK_BOTH ^ test_mask) == CheckHeaderSignature(gpt));
|
|
}
|
|
}
|
|
|
|
return TEST_OK;
|
|
}
|
|
|
|
/* The revision we currently support is GPT_HEADER_REVISION.
|
|
* If the revision in header is not that, we expect the header is invalid. */
|
|
int RevisionTest() {
|
|
GptData *gpt;
|
|
struct {
|
|
uint32_t value_to_test;
|
|
int is_valid_value;
|
|
} cases[] = {
|
|
{0x01000000, 0},
|
|
{0x00010000, 1}, /* GPT_HEADER_REVISION */
|
|
{0x00000100, 0},
|
|
{0x00000001, 0},
|
|
{0x23010456, 0},
|
|
};
|
|
int i;
|
|
int test_mask;
|
|
GptHeader *headers[2];
|
|
uint32_t valid_headers;
|
|
|
|
gpt = GetEmptyGptData();
|
|
headers[PRIMARY] = (GptHeader*)gpt->primary_header;
|
|
headers[SECONDARY] = (GptHeader*)gpt->secondary_header;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(cases); ++i) {
|
|
for (test_mask = MASK_PRIMARY; test_mask <= MASK_BOTH; ++test_mask) {
|
|
BuildTestGptData(gpt);
|
|
if (test_mask & MASK_PRIMARY)
|
|
headers[PRIMARY]->revision = cases[i].value_to_test;
|
|
if (test_mask & MASK_SECONDARY)
|
|
headers[SECONDARY]->revision = cases[i].value_to_test;
|
|
valid_headers = CheckRevision(gpt);
|
|
if (cases[i].is_valid_value)
|
|
EXPECT(MASK_BOTH == valid_headers);
|
|
else
|
|
EXPECT((MASK_BOTH ^ test_mask) == valid_headers);
|
|
}
|
|
}
|
|
return TEST_OK;
|
|
}
|
|
|
|
int SizeTest() {
|
|
GptData *gpt;
|
|
struct {
|
|
uint32_t value_to_test;
|
|
int is_valid_value;
|
|
} cases[] = {
|
|
{91, 0},
|
|
{92, 1},
|
|
{93, 1},
|
|
{511, 1},
|
|
{512, 1},
|
|
{513, 0},
|
|
};
|
|
int i;
|
|
int test_mask;
|
|
GptHeader *headers[2];
|
|
uint32_t valid_headers;
|
|
|
|
gpt = GetEmptyGptData();
|
|
headers[PRIMARY] = (GptHeader*)gpt->primary_header;
|
|
headers[SECONDARY] = (GptHeader*)gpt->secondary_header;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(cases); ++i) {
|
|
for (test_mask = MASK_PRIMARY; test_mask <= MASK_BOTH; ++test_mask) {
|
|
BuildTestGptData(gpt);
|
|
if (test_mask & MASK_PRIMARY)
|
|
headers[PRIMARY]->size = cases[i].value_to_test;
|
|
if (test_mask & MASK_SECONDARY)
|
|
headers[SECONDARY]->size = cases[i].value_to_test;
|
|
valid_headers = CheckSize(gpt);
|
|
if (cases[i].is_valid_value)
|
|
EXPECT(MASK_BOTH == valid_headers);
|
|
else
|
|
EXPECT((MASK_BOTH ^ test_mask) == valid_headers);
|
|
}
|
|
}
|
|
return TEST_OK;
|
|
}
|
|
|
|
/* Tests if reserved fields are checked.
|
|
* We'll try non-zero values to test. */
|
|
int ReservedFieldsTest() {
|
|
GptData *gpt;
|
|
GptHeader *primary_header, *secondary_header;
|
|
|
|
gpt = GetEmptyGptData();
|
|
primary_header = (GptHeader*)gpt->primary_header;
|
|
secondary_header = (GptHeader*)gpt->secondary_header;
|
|
|
|
/* expect secondary is still valid. */
|
|
BuildTestGptData(gpt);
|
|
primary_header->reserved ^= 0x12345678; /* whatever random */
|
|
EXPECT(MASK_SECONDARY == CheckReservedFields(gpt));
|
|
|
|
/* expect secondary is still valid. */
|
|
BuildTestGptData(gpt);
|
|
primary_header->padding ^= 0x12345678; /* whatever random */
|
|
EXPECT(MASK_SECONDARY == CheckReservedFields(gpt));
|
|
|
|
/* expect primary is still valid. */
|
|
BuildTestGptData(gpt);
|
|
secondary_header->reserved ^= 0x12345678; /* whatever random */
|
|
EXPECT(MASK_PRIMARY == CheckReservedFields(gpt));
|
|
|
|
/* expect primary is still valid. */
|
|
BuildTestGptData(gpt);
|
|
secondary_header->padding ^= 0x12345678; /* whatever random */
|
|
EXPECT(MASK_PRIMARY == CheckReservedFields(gpt));
|
|
|
|
return TEST_OK;
|
|
}
|
|
|
|
/* Tests if myLBA field is checked (1 for primary, last for secondary). */
|
|
int MyLbaTest() {
|
|
GptData *gpt;
|
|
int test_mask;
|
|
GptHeader *headers[2];
|
|
uint32_t valid_headers;
|
|
|
|
gpt = GetEmptyGptData();
|
|
headers[PRIMARY] = (GptHeader*)gpt->primary_header;
|
|
headers[SECONDARY] = (GptHeader*)gpt->secondary_header;
|
|
|
|
for (test_mask = MASK_PRIMARY; test_mask <= MASK_BOTH; ++test_mask) {
|
|
BuildTestGptData(gpt);
|
|
if (test_mask & MASK_PRIMARY)
|
|
++headers[PRIMARY]->my_lba;
|
|
if (test_mask & MASK_SECONDARY)
|
|
--headers[SECONDARY]->my_lba;
|
|
valid_headers = CheckMyLba(gpt);
|
|
EXPECT((MASK_BOTH ^ test_mask) == valid_headers);
|
|
}
|
|
return TEST_OK;
|
|
}
|
|
|
|
/* Tests if SizeOfPartitionEntry is checked. SizeOfPartitionEntry must be
|
|
* between 128 and 512, and a multiple of 8. */
|
|
int SizeOfPartitionEntryTest() {
|
|
GptData *gpt;
|
|
struct {
|
|
uint32_t value_to_test;
|
|
int is_valid_value;
|
|
} cases[] = {
|
|
{127, 0},
|
|
{128, 1},
|
|
{129, 0},
|
|
{130, 0},
|
|
{131, 0},
|
|
{132, 0},
|
|
{133, 0},
|
|
{134, 0},
|
|
{135, 0},
|
|
{136, 1},
|
|
{144, 1},
|
|
{160, 1},
|
|
{192, 1},
|
|
{256, 1},
|
|
{384, 1},
|
|
{504, 1},
|
|
{512, 1},
|
|
{513, 0},
|
|
{520, 0},
|
|
};
|
|
int i;
|
|
int test_mask;
|
|
GptHeader *headers[2];
|
|
uint32_t valid_headers;
|
|
|
|
gpt = GetEmptyGptData();
|
|
headers[PRIMARY] = (GptHeader*)gpt->primary_header;
|
|
headers[SECONDARY] = (GptHeader*)gpt->secondary_header;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(cases); ++i) {
|
|
for (test_mask = MASK_PRIMARY; test_mask <= MASK_BOTH; ++test_mask) {
|
|
BuildTestGptData(gpt);
|
|
if (test_mask & MASK_PRIMARY) {
|
|
headers[PRIMARY]->size_of_entry = cases[i].value_to_test;
|
|
headers[PRIMARY]->number_of_entries =
|
|
TOTAL_ENTRIES_SIZE / cases[i].value_to_test;
|
|
}
|
|
if (test_mask & MASK_SECONDARY) {
|
|
headers[SECONDARY]->size_of_entry = cases[i].value_to_test;
|
|
headers[SECONDARY]->number_of_entries =
|
|
TOTAL_ENTRIES_SIZE / cases[i].value_to_test;
|
|
}
|
|
valid_headers = CheckSizeOfPartitionEntry(gpt);
|
|
if (cases[i].is_valid_value)
|
|
EXPECT(MASK_BOTH == valid_headers);
|
|
else
|
|
EXPECT((MASK_BOTH ^ test_mask) == valid_headers);
|
|
}
|
|
}
|
|
return TEST_OK;
|
|
}
|
|
|
|
/* Tests if NumberOfPartitionEntries is checes. NumberOfPartitionEntries must
|
|
* be between 32 and 512, and SizeOfPartitionEntry * NumberOfPartitionEntries
|
|
* must be 16384. */
|
|
int NumberOfPartitionEntriesTest() {
|
|
GptData *gpt;
|
|
struct {
|
|
uint32_t size_of_entry;
|
|
uint32_t number_of_entries;
|
|
int is_valid_value;
|
|
} cases[] = {
|
|
{111, 147, 0},
|
|
{111, 149, 0},
|
|
{128, 32, 0},
|
|
{128, 64, 0},
|
|
{128, 127, 0},
|
|
{128, 128, 1},
|
|
{128, 129, 0},
|
|
{128, 256, 0},
|
|
{256, 32, 0},
|
|
{256, 64, 1},
|
|
{256, 128, 0},
|
|
{256, 256, 0},
|
|
{512, 32, 1},
|
|
{512, 64, 0},
|
|
{512, 128, 0},
|
|
{512, 256, 0},
|
|
{1024, 128, 0},
|
|
};
|
|
int i;
|
|
int test_mask;
|
|
GptHeader *headers[2];
|
|
uint32_t valid_headers;
|
|
|
|
gpt = GetEmptyGptData();
|
|
headers[PRIMARY] = (GptHeader*)gpt->primary_header;
|
|
headers[SECONDARY] = (GptHeader*)gpt->secondary_header;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(cases); ++i) {
|
|
for (test_mask = MASK_PRIMARY; test_mask <= MASK_BOTH; ++test_mask) {
|
|
BuildTestGptData(gpt);
|
|
if (test_mask & MASK_PRIMARY) {
|
|
headers[PRIMARY]->size_of_entry = cases[i].size_of_entry;
|
|
headers[PRIMARY]->number_of_entries = cases[i].number_of_entries;
|
|
}
|
|
if (test_mask & MASK_SECONDARY) {
|
|
headers[SECONDARY]->size_of_entry = cases[i].size_of_entry;
|
|
headers[SECONDARY]->number_of_entries = cases[i].number_of_entries;
|
|
}
|
|
valid_headers = CheckNumberOfEntries(gpt);
|
|
if (cases[i].is_valid_value)
|
|
EXPECT(MASK_BOTH == valid_headers);
|
|
else
|
|
EXPECT((MASK_BOTH ^ test_mask) == valid_headers);
|
|
}
|
|
}
|
|
return TEST_OK;
|
|
}
|
|
|
|
/* Tests if PartitionEntryLBA in primary/secondary headers is checked. */
|
|
int PartitionEntryLbaTest() {
|
|
GptData *gpt;
|
|
int test_mask;
|
|
GptHeader *headers[2];
|
|
uint32_t valid_headers;
|
|
|
|
gpt = GetEmptyGptData();
|
|
headers[PRIMARY] = (GptHeader*)gpt->primary_header;
|
|
headers[SECONDARY] = (GptHeader*)gpt->secondary_header;
|
|
|
|
for (test_mask = MASK_PRIMARY; test_mask <= MASK_BOTH; ++test_mask) {
|
|
BuildTestGptData(gpt);
|
|
if (test_mask & MASK_PRIMARY)
|
|
headers[PRIMARY]->entries_lba = 0;
|
|
if (test_mask & MASK_SECONDARY)
|
|
headers[SECONDARY]->entries_lba = DEFAULT_DRIVE_SECTORS - 31 - 1;
|
|
valid_headers = CheckEntriesLba(gpt);
|
|
EXPECT((MASK_BOTH ^ test_mask) == valid_headers);
|
|
}
|
|
return TEST_OK;
|
|
}
|
|
|
|
/* Tests if FirstUsableLBA and LastUsableLBA are checked.
|
|
* FirstUsableLBA must be after the end of the primary GPT table array.
|
|
* LastUsableLBA must be before the start of the secondary GPT table array.
|
|
* FirstUsableLBA <= LastUsableLBA. */
|
|
int FirstUsableLbaAndLastUsableLbaTest() {
|
|
GptData *gpt;
|
|
GptHeader *primary_header, *secondary_header;
|
|
uint32_t valid_headers;
|
|
int i;
|
|
struct {
|
|
uint64_t primary_entries_lba;
|
|
uint64_t primary_first_usable_lba;
|
|
uint64_t primary_last_usable_lba;
|
|
uint64_t secondary_first_usable_lba;
|
|
uint64_t secondary_last_usable_lba;
|
|
uint64_t secondary_entries_lba;
|
|
int expected_masks;
|
|
} cases[] = {
|
|
{2, 34, 433, 34, 433, 434, MASK_BOTH},
|
|
{2, 34, 432, 34, 430, 434, MASK_BOTH},
|
|
{2, 33, 433, 33, 433, 434, MASK_NONE},
|
|
{3, 34, 433, 35, 433, 434, MASK_SECONDARY},
|
|
{3, 35, 433, 33, 433, 434, MASK_PRIMARY},
|
|
{2, 34, 434, 34, 433, 434, MASK_SECONDARY},
|
|
{2, 34, 433, 34, 434, 434, MASK_PRIMARY},
|
|
{2, 35, 433, 35, 433, 434, MASK_BOTH},
|
|
{2, 433, 433, 433, 433, 434, MASK_BOTH},
|
|
{2, 434, 433, 434, 434, 434, MASK_NONE},
|
|
{2, 433, 34, 34, 433, 434, MASK_SECONDARY},
|
|
{2, 34, 433, 433, 34, 434, MASK_PRIMARY},
|
|
};
|
|
|
|
gpt = GetEmptyGptData();
|
|
primary_header = (GptHeader*)gpt->primary_header;
|
|
secondary_header = (GptHeader*)gpt->secondary_header;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(cases); ++i) {
|
|
BuildTestGptData(gpt);
|
|
primary_header->entries_lba = cases[i].primary_entries_lba;
|
|
primary_header->first_usable_lba = cases[i].primary_first_usable_lba;
|
|
primary_header->last_usable_lba = cases[i].primary_last_usable_lba;
|
|
secondary_header->entries_lba = cases[i].secondary_entries_lba;
|
|
secondary_header->first_usable_lba = cases[i].secondary_first_usable_lba;
|
|
secondary_header->last_usable_lba = cases[i].secondary_last_usable_lba;
|
|
valid_headers = CheckValidUsableLbas(gpt);
|
|
EXPECT(cases[i].expected_masks == valid_headers);
|
|
}
|
|
|
|
return TEST_OK;
|
|
}
|
|
|
|
/* Tests if header CRC in two copies are calculated. */
|
|
int HeaderCrcTest() {
|
|
GptData *gpt;
|
|
GptHeader *primary_header, *secondary_header;
|
|
|
|
gpt = GetEmptyGptData();
|
|
primary_header = (GptHeader*)gpt->primary_header;
|
|
secondary_header = (GptHeader*)gpt->secondary_header;
|
|
|
|
/* Modify the first byte of primary header, and expect the CRC is wrong. */
|
|
BuildTestGptData(gpt);
|
|
gpt->primary_header[0] ^= 0xa5; /* just XOR a non-zero value */
|
|
EXPECT(MASK_SECONDARY == CheckHeaderCrc(gpt));
|
|
|
|
/* Modify the last byte of secondary header, and expect the CRC is wrong. */
|
|
BuildTestGptData(gpt);
|
|
gpt->secondary_header[secondary_header->size-1] ^= 0x5a;
|
|
EXPECT(MASK_PRIMARY == CheckHeaderCrc(gpt));
|
|
|
|
/* Modify out of CRC range, expect CRC is still right. */
|
|
BuildTestGptData(gpt);
|
|
gpt->primary_header[primary_header->size] ^= 0x87;
|
|
EXPECT(MASK_BOTH == CheckHeaderCrc(gpt));
|
|
|
|
return TEST_OK;
|
|
}
|
|
|
|
/* Tests if PartitionEntryArrayCRC32 is checked.
|
|
* PartitionEntryArrayCRC32 must be calculated over SizeOfPartitionEntry *
|
|
* NumberOfPartitionEntries bytes.
|
|
*/
|
|
int EntriesCrcTest() {
|
|
GptData *gpt;
|
|
|
|
gpt = GetEmptyGptData();
|
|
|
|
/* Modify the first byte of primary entries, and expect the CRC is wrong. */
|
|
BuildTestGptData(gpt);
|
|
gpt->primary_entries[0] ^= 0xa5; /* just XOR a non-zero value */
|
|
EXPECT(MASK_SECONDARY == CheckEntriesCrc(gpt));
|
|
|
|
/* Modify the last byte of secondary entries, and expect the CRC is wrong. */
|
|
BuildTestGptData(gpt);
|
|
gpt->secondary_entries[TOTAL_ENTRIES_SIZE-1] ^= 0x5a;
|
|
EXPECT(MASK_PRIMARY == CheckEntriesCrc(gpt));
|
|
|
|
return TEST_OK;
|
|
}
|
|
|
|
/* Tests if GptInit() handles non-identical partition entries well.
|
|
* Two copies of partition table entries must be identical. If not, we trust the
|
|
* primary table entries, and mark secondary as modified. */
|
|
int IdenticalEntriesTest() {
|
|
GptData *gpt;
|
|
|
|
gpt = GetEmptyGptData();
|
|
|
|
/* Tests RepairEntries() first. */
|
|
BuildTestGptData(gpt);
|
|
EXPECT(0 == RepairEntries(gpt, MASK_BOTH));
|
|
gpt->secondary_entries[0] ^= 0xa5; /* XOR any number */
|
|
EXPECT(GPT_MODIFIED_ENTRIES2 == RepairEntries(gpt, MASK_BOTH));
|
|
EXPECT(GPT_MODIFIED_ENTRIES2 == RepairEntries(gpt, MASK_PRIMARY));
|
|
EXPECT(GPT_MODIFIED_ENTRIES1 == RepairEntries(gpt, MASK_SECONDARY));
|
|
EXPECT(0 == RepairEntries(gpt, MASK_NONE));
|
|
|
|
/* The first byte is different. We expect secondary entries is marked as
|
|
* modified. */
|
|
BuildTestGptData(gpt);
|
|
gpt->primary_entries[0] ^= 0xff;
|
|
RefreshCrc32(gpt);
|
|
EXPECT(GPT_SUCCESS == GptInit(gpt));
|
|
EXPECT(GPT_MODIFIED_ENTRIES2 == gpt->modified);
|
|
EXPECT(0 == Memcmp(gpt->primary_entries, gpt->secondary_entries,
|
|
TOTAL_ENTRIES_SIZE));
|
|
|
|
/* The last byte is different, but the primary entries CRC is bad.
|
|
* We expect primary entries is marked as modified. */
|
|
BuildTestGptData(gpt);
|
|
gpt->primary_entries[TOTAL_ENTRIES_SIZE-1] ^= 0xff;
|
|
EXPECT(GPT_SUCCESS == GptInit(gpt));
|
|
EXPECT(GPT_MODIFIED_ENTRIES1 == gpt->modified);
|
|
EXPECT(0 == Memcmp(gpt->primary_entries, gpt->secondary_entries,
|
|
TOTAL_ENTRIES_SIZE));
|
|
|
|
return TEST_OK;
|
|
}
|
|
|
|
/* Tests if GptInit() handles synonymous headers well.
|
|
* Note that two partition headers are NOT bit-swise identical.
|
|
* For exmaple, my_lba must be different (pointing to respective self).
|
|
* So in normal case, they are synonymous, not identical.
|
|
* If not synonymous, we trust the primary partition header, and
|
|
* overwrite secondary, then mark secondary as modified.*/
|
|
int SynonymousHeaderTest() {
|
|
GptData *gpt;
|
|
GptHeader *primary_header, *secondary_header;
|
|
|
|
gpt = GetEmptyGptData();
|
|
primary_header = (GptHeader*)gpt->primary_header;
|
|
secondary_header = (GptHeader*)gpt->secondary_header;
|
|
|
|
/* Tests RepairHeader() for synonymous cases first. */
|
|
BuildTestGptData(gpt);
|
|
EXPECT(0 == RepairHeader(gpt, MASK_BOTH));
|
|
EXPECT(GPT_MODIFIED_HEADER2 == RepairHeader(gpt, MASK_PRIMARY));
|
|
EXPECT(GPT_MODIFIED_HEADER1 == RepairHeader(gpt, MASK_SECONDARY));
|
|
EXPECT(0 == RepairHeader(gpt, MASK_NONE));
|
|
/* Then tests non-synonymous cases. */
|
|
BuildTestGptData(gpt);
|
|
++secondary_header->first_usable_lba; /* chnage any bit */
|
|
EXPECT(GPT_MODIFIED_HEADER2 == RepairHeader(gpt, MASK_BOTH));
|
|
EXPECT(primary_header->first_usable_lba ==
|
|
secondary_header->first_usable_lba);
|
|
/* ---- */
|
|
BuildTestGptData(gpt);
|
|
--secondary_header->last_usable_lba;
|
|
EXPECT(GPT_MODIFIED_HEADER2 == RepairHeader(gpt, MASK_BOTH));
|
|
EXPECT(primary_header->last_usable_lba == secondary_header->last_usable_lba);
|
|
/* ---- */
|
|
BuildTestGptData(gpt);
|
|
++secondary_header->number_of_entries;
|
|
EXPECT(GPT_MODIFIED_HEADER2 == RepairHeader(gpt, MASK_BOTH));
|
|
EXPECT(primary_header->number_of_entries ==
|
|
secondary_header->number_of_entries);
|
|
/* ---- */
|
|
BuildTestGptData(gpt);
|
|
--secondary_header->size_of_entry;
|
|
EXPECT(GPT_MODIFIED_HEADER2 == RepairHeader(gpt, MASK_BOTH));
|
|
EXPECT(primary_header->size_of_entry ==
|
|
secondary_header->size_of_entry);
|
|
/* ---- */
|
|
BuildTestGptData(gpt);
|
|
secondary_header->disk_uuid.u.raw[0] ^= 0x56;
|
|
EXPECT(GPT_MODIFIED_HEADER2 == RepairHeader(gpt, MASK_BOTH));
|
|
EXPECT(0 == Memcmp(&primary_header->disk_uuid,
|
|
&secondary_header->disk_uuid, sizeof(Guid)));
|
|
|
|
/* Consider header repairing in GptInit(). */
|
|
BuildTestGptData(gpt);
|
|
++secondary_header->first_usable_lba;
|
|
RefreshCrc32(gpt);
|
|
EXPECT(GPT_SUCCESS == GptInit(gpt));
|
|
EXPECT(GPT_MODIFIED_HEADER2 == gpt->modified);
|
|
EXPECT(primary_header->first_usable_lba ==
|
|
secondary_header->first_usable_lba);
|
|
|
|
return TEST_OK;
|
|
}
|
|
|
|
/* Tests if partition geometry is checked.
|
|
* All active (non-zero PartitionTypeGUID) partition entries should have:
|
|
* entry.StartingLBA >= header.FirstUsableLBA
|
|
* entry.EndingLBA <= header.LastUsableLBA
|
|
* entry.StartingLBA <= entry.EndingLBA
|
|
*/
|
|
int ValidEntryTest() {
|
|
GptData *gpt;
|
|
GptHeader *primary_header, *secondary_header;
|
|
GptEntry *primary_entries, *secondary_entries;
|
|
|
|
gpt = GetEmptyGptData();
|
|
primary_header = (GptHeader*)gpt->primary_header;
|
|
secondary_header = (GptHeader*)gpt->secondary_header;
|
|
primary_entries = (GptEntry*)gpt->primary_entries;
|
|
secondary_entries = (GptEntry*)gpt->secondary_entries;
|
|
|
|
/* error case: entry.StartingLBA < header.FirstUsableLBA */
|
|
BuildTestGptData(gpt);
|
|
primary_entries[0].starting_lba = primary_header->first_usable_lba - 1;
|
|
EXPECT(MASK_SECONDARY == CheckValidEntries(gpt));
|
|
secondary_entries[1].starting_lba = secondary_header->first_usable_lba - 1;
|
|
EXPECT(MASK_NONE == CheckValidEntries(gpt));
|
|
|
|
/* error case: entry.EndingLBA > header.LastUsableLBA */
|
|
BuildTestGptData(gpt);
|
|
primary_entries[2].ending_lba = primary_header->last_usable_lba + 1;
|
|
EXPECT(MASK_SECONDARY == CheckValidEntries(gpt));
|
|
secondary_entries[3].ending_lba = secondary_header->last_usable_lba + 1;
|
|
EXPECT(MASK_NONE == CheckValidEntries(gpt));
|
|
|
|
/* error case: entry.StartingLBA > entry.EndingLBA */
|
|
BuildTestGptData(gpt);
|
|
primary_entries[3].starting_lba = primary_entries[3].ending_lba + 1;
|
|
EXPECT(MASK_SECONDARY == CheckValidEntries(gpt));
|
|
secondary_entries[1].starting_lba = secondary_entries[1].ending_lba + 1;
|
|
EXPECT(MASK_NONE == CheckValidEntries(gpt));
|
|
|
|
/* case: non active entry should be ignored. */
|
|
BuildTestGptData(gpt);
|
|
Memset(&primary_entries[1].type, 0, sizeof(primary_entries[1].type));
|
|
primary_entries[1].starting_lba = primary_entries[1].ending_lba + 1;
|
|
EXPECT(MASK_BOTH == CheckValidEntries(gpt));
|
|
Memset(&secondary_entries[2].type, 0, sizeof(secondary_entries[2].type));
|
|
secondary_entries[2].starting_lba = secondary_entries[2].ending_lba + 1;
|
|
EXPECT(MASK_BOTH == CheckValidEntries(gpt));
|
|
|
|
return TEST_OK;
|
|
}
|
|
|
|
/* Tests if overlapped partition tables can be detected. */
|
|
int OverlappedPartitionTest() {
|
|
GptData *gpt;
|
|
struct {
|
|
int overlapped;
|
|
struct {
|
|
int active;
|
|
uint64_t starting_lba;
|
|
uint64_t ending_lba;
|
|
} entries[16]; /* enough for testing. */
|
|
} cases[] = {
|
|
{0, {{0, 100, 199}, {0, 0, 0}}},
|
|
{0, {{1, 100, 199}, {0, 0, 0}}},
|
|
{0, {{1, 100, 150}, {1, 200, 250}, {1, 300, 350}, {0, 0, 0}}},
|
|
{1, {{1, 200, 299}, {1, 100, 199}, {1, 100, 100}, {0, 0, 0}}},
|
|
{1, {{1, 200, 299}, {1, 100, 199}, {1, 299, 299}, {0, 0, 0}}},
|
|
{0, {{1, 300, 399}, {1, 200, 299}, {1, 100, 199}, {0, 0, 0}}},
|
|
{1, {{1, 100, 199}, {1, 199, 299}, {1, 299, 399}, {0, 0, 0}}},
|
|
{1, {{1, 100, 199}, {1, 200, 299}, {1, 75, 399}, {0, 0, 0}}},
|
|
{1, {{1, 100, 199}, {1, 75, 250}, {1, 200, 299}, {0, 0, 0}}},
|
|
{1, {{1, 75, 150}, {1, 100, 199}, {1, 200, 299}, {0, 0, 0}}},
|
|
{1, {{1, 200, 299}, {1, 100, 199}, {1, 300, 399}, {1, 100, 399},
|
|
{0, 0, 0}}},
|
|
{0, {{1, 200, 299}, {1, 100, 199}, {1, 300, 399}, {0, 100, 399},
|
|
{0, 0, 0}}},
|
|
{1, {{1, 200, 300}, {1, 100, 200}, {1, 100, 400}, {1, 300, 400},
|
|
{0, 0, 0}}},
|
|
{1, {{0, 200, 300}, {1, 100, 200}, {1, 100, 400}, {1, 300, 400},
|
|
{0, 0, 0}}},
|
|
{0, {{1, 200, 300}, {1, 100, 199}, {0, 100, 400}, {0, 300, 400},
|
|
{0, 0, 0}}},
|
|
{1, {{1, 200, 299}, {1, 100, 199}, {1, 199, 199}, {0, 0, 0}}},
|
|
{0, {{1, 200, 299}, {0, 100, 199}, {1, 199, 199}, {0, 0, 0}}},
|
|
{0, {{1, 200, 299}, {1, 100, 199}, {0, 199, 199}, {0, 0, 0}}},
|
|
{1, {{1, 199, 199}, {1, 200, 200}, {1, 201, 201}, {1, 202, 202},
|
|
{1, 203, 203}, {1, 204, 204}, {1, 205, 205}, {1, 206, 206},
|
|
{1, 207, 207}, {1, 208, 208}, {1, 199, 199}, {0, 0, 0}}},
|
|
{0, {{1, 199, 199}, {1, 200, 200}, {1, 201, 201}, {1, 202, 202},
|
|
{1, 203, 203}, {1, 204, 204}, {1, 205, 205}, {1, 206, 206},
|
|
{1, 207, 207}, {1, 208, 208}, {0, 199, 199}, {0, 0, 0}}},
|
|
};
|
|
Guid any_type = GPT_ENT_TYPE_CHROMEOS_KERNEL;
|
|
int i, j;
|
|
int test_mask;
|
|
GptEntry *entries[2];
|
|
|
|
gpt = GetEmptyGptData();
|
|
entries[PRIMARY] = (GptEntry*)gpt->primary_entries;
|
|
entries[SECONDARY] = (GptEntry*)gpt->secondary_entries;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(cases); ++i) {
|
|
for (test_mask = MASK_PRIMARY; test_mask <= MASK_BOTH; ++test_mask) {
|
|
BuildTestGptData(gpt);
|
|
ZeroEntries(gpt);
|
|
for(j = 0; j < ARRAY_SIZE(cases[0].entries); ++j) {
|
|
if (!cases[i].entries[j].starting_lba) break;
|
|
if (test_mask & MASK_PRIMARY) {
|
|
if (cases[i].entries[j].active)
|
|
Memcpy(&entries[PRIMARY][j].type, &any_type, sizeof(any_type));
|
|
entries[PRIMARY][j].starting_lba = cases[i].entries[j].starting_lba;
|
|
entries[PRIMARY][j].ending_lba = cases[i].entries[j].ending_lba;
|
|
}
|
|
if (test_mask & MASK_SECONDARY) {
|
|
if (cases[i].entries[j].active)
|
|
Memcpy(&entries[SECONDARY][j].type, &any_type, sizeof(any_type));
|
|
entries[SECONDARY][j].starting_lba = cases[i].entries[j].starting_lba;
|
|
entries[SECONDARY][j].ending_lba = cases[i].entries[j].ending_lba;
|
|
}
|
|
}
|
|
EXPECT((cases[i].overlapped * test_mask) ==
|
|
(OverlappedEntries(entries[PRIMARY], j) |
|
|
(OverlappedEntries(entries[SECONDARY], j) << SECONDARY))
|
|
);
|
|
|
|
EXPECT((MASK_BOTH ^ (cases[i].overlapped * test_mask)) ==
|
|
CheckOverlappedPartition(gpt));
|
|
}
|
|
}
|
|
return TEST_OK;
|
|
}
|
|
|
|
/* Tests if GptInit() can survive in different corrupt header/entries
|
|
* combinations, like:
|
|
* primary GPT header - valid
|
|
* primary partition table - invalid
|
|
* secondary GPT header - invalid
|
|
* secondary partition table - valid
|
|
*/
|
|
int CorruptCombinationTest() {
|
|
GptData *gpt;
|
|
GptHeader *primary_header, *secondary_header;
|
|
GptEntry *primary_entries, *secondary_entries;
|
|
|
|
gpt = GetEmptyGptData();
|
|
primary_header = (GptHeader*)gpt->primary_header;
|
|
secondary_header = (GptHeader*)gpt->secondary_header;
|
|
primary_entries = (GptEntry*)gpt->primary_entries;
|
|
secondary_entries = (GptEntry*)gpt->secondary_entries;
|
|
|
|
/* Make primary entries and secondary header invalid, we expect GptInit()
|
|
* can recover them (returns GPT_SUCCESS and MODIFIED flasgs). */
|
|
BuildTestGptData(gpt);
|
|
primary_entries[0].type.u.raw[0] ^= 0x33;
|
|
secondary_header->header_crc32 ^= 0x55;
|
|
EXPECT(GPT_SUCCESS == GptInit(gpt));
|
|
EXPECT((GPT_MODIFIED_HEADER2 | GPT_MODIFIED_ENTRIES1) == gpt->modified);
|
|
EXPECT(0 == Memcmp(primary_entries, secondary_entries, TOTAL_ENTRIES_SIZE));
|
|
/* We expect the modified header/entries can pass GptInit(). */
|
|
EXPECT(GPT_SUCCESS == GptInit(gpt));
|
|
EXPECT(0 == gpt->modified);
|
|
|
|
/* Make primary header invalid (the entries is not damaged actually). */
|
|
BuildTestGptData(gpt);
|
|
primary_header->entries_crc32 ^= 0x73;
|
|
EXPECT(GPT_SUCCESS == GptInit(gpt));
|
|
/* After header is repaired, the entries are valid actually. */
|
|
EXPECT((GPT_MODIFIED_HEADER1) == gpt->modified);
|
|
/* We expect the modified header/entries can pass GptInit(). */
|
|
EXPECT(GPT_SUCCESS == GptInit(gpt));
|
|
EXPECT(0 == gpt->modified);
|
|
|
|
return TEST_OK;
|
|
}
|
|
|
|
/* Invalidate all kernel entries and expect GptNextKernelEntry() cannot find
|
|
* any usable kernel entry.
|
|
*/
|
|
int NoValidKernelEntryTest() {
|
|
GptData *gpt;
|
|
GptEntry *entries, *entries2;
|
|
|
|
gpt = GetEmptyGptData();
|
|
entries = (GptEntry*)gpt->primary_entries;
|
|
entries2 = (GptEntry*)gpt->secondary_entries;
|
|
|
|
BuildTestGptData(gpt);
|
|
entries[KERNEL_A].attributes |= CGPT_ATTRIBUTE_BAD_MASK;
|
|
Memset(&entries[KERNEL_B].type, 0, sizeof(Guid));
|
|
RefreshCrc32(gpt);
|
|
|
|
EXPECT(GPT_ERROR_NO_VALID_KERNEL == GptNextKernelEntry(gpt, NULL, NULL));
|
|
|
|
return TEST_OK;
|
|
}
|
|
|
|
/* This is the combination test. Both kernel A and B could be either inactive
|
|
* or invalid. We expect GptNextKetnelEntry() returns good kernel or
|
|
* GPT_ERROR_NO_VALID_KERNEL if no kernel is available. */
|
|
enum FAILURE_MASK {
|
|
MASK_INACTIVE = 1,
|
|
MASK_BAD_ENTRY = 2,
|
|
MASK_FAILURE_BOTH = 3,
|
|
};
|
|
void BreakAnEntry(GptEntry *entry, enum FAILURE_MASK failure) {
|
|
if (failure & MASK_INACTIVE)
|
|
Memset(&entry->type, 0, sizeof(Guid));
|
|
if (failure & MASK_BAD_ENTRY)
|
|
entry->attributes |= CGPT_ATTRIBUTE_BAD_MASK;
|
|
}
|
|
|
|
int CombinationalNextKernelEntryTest() {
|
|
GptData *gpt;
|
|
enum {
|
|
MASK_KERNEL_A = 1,
|
|
MASK_KERNEL_B = 2,
|
|
MASK_KERNEL_BOTH = 3,
|
|
} kernel;
|
|
enum FAILURE_MASK failure;
|
|
uint64_t start_sector, size;
|
|
int retval;
|
|
|
|
for (kernel = MASK_KERNEL_A; kernel <= MASK_KERNEL_BOTH; ++kernel) {
|
|
for (failure = MASK_INACTIVE; failure < MASK_FAILURE_BOTH; ++failure) {
|
|
gpt = GetEmptyGptData();
|
|
BuildTestGptData(gpt);
|
|
|
|
if (kernel & MASK_KERNEL_A)
|
|
BreakAnEntry(GetEntry(gpt, PRIMARY, KERNEL_A), failure);
|
|
if (kernel & MASK_KERNEL_B)
|
|
BreakAnEntry(GetEntry(gpt, PRIMARY, KERNEL_B), failure);
|
|
|
|
retval = GptNextKernelEntry(gpt, &start_sector, &size);
|
|
|
|
if (kernel == MASK_KERNEL_A) {
|
|
EXPECT(retval == GPT_SUCCESS);
|
|
EXPECT(start_sector == 334);
|
|
} else if (kernel == MASK_KERNEL_B) {
|
|
EXPECT(retval == GPT_SUCCESS);
|
|
EXPECT(start_sector == 34);
|
|
} else { /* MASK_KERNEL_BOTH */
|
|
EXPECT(retval == GPT_ERROR_NO_VALID_KERNEL);
|
|
}
|
|
}
|
|
}
|
|
return TEST_OK;
|
|
}
|
|
|
|
/* Increase tries value from zero, expect it won't explode/overflow after
|
|
* CGPT_ATTRIBUTE_TRIES_MASK.
|
|
*/
|
|
/* Tries would not count up after CGPT_ATTRIBUTE_MAX_TRIES. */
|
|
#define EXPECTED_TRIES(tries) \
|
|
((tries >= CGPT_ATTRIBUTE_MAX_TRIES) ? CGPT_ATTRIBUTE_MAX_TRIES \
|
|
: tries)
|
|
int IncreaseTriesTest() {
|
|
GptData *gpt;
|
|
int kernel_index[] = {
|
|
KERNEL_B,
|
|
KERNEL_A,
|
|
};
|
|
int i, tries, j;
|
|
|
|
gpt = GetEmptyGptData();
|
|
for (i = 0; i < ARRAY_SIZE(kernel_index); ++i) {
|
|
GptEntry *entries[2] = {
|
|
(GptEntry*)gpt->primary_entries,
|
|
(GptEntry*)gpt->secondary_entries,
|
|
};
|
|
int current;
|
|
|
|
BuildTestGptData(gpt);
|
|
current = gpt->current_kernel = kernel_index[i];
|
|
|
|
for (tries = 0; tries < 2 * CGPT_ATTRIBUTE_MAX_TRIES; ++tries) {
|
|
for (j = 0; j < ARRAY_SIZE(entries); ++j) {
|
|
EXPECT(EXPECTED_TRIES(tries) ==
|
|
((entries[j][current].attributes & CGPT_ATTRIBUTE_TRIES_MASK) >>
|
|
CGPT_ATTRIBUTE_TRIES_OFFSET));
|
|
}
|
|
|
|
EXPECT(GPT_SUCCESS == GptUpdateKernelEntry(gpt, GPT_UPDATE_ENTRY_TRY));
|
|
/* The expected tries value will be checked in next iteration. */
|
|
|
|
if (tries < CGPT_ATTRIBUTE_MAX_TRIES)
|
|
EXPECT((GPT_MODIFIED_HEADER1 | GPT_MODIFIED_ENTRIES1 |
|
|
GPT_MODIFIED_HEADER2 | GPT_MODIFIED_ENTRIES2) == gpt->modified);
|
|
gpt->modified = 0; /* reset before next test */
|
|
EXPECT(0 ==
|
|
Memcmp(entries[PRIMARY], entries[SECONDARY], TOTAL_ENTRIES_SIZE));
|
|
}
|
|
}
|
|
return TEST_OK;
|
|
}
|
|
|
|
/* Mark a kernel as bad. Expect:
|
|
* 1. the both bad bits of kernel A in primary and secondary entries are set.
|
|
* 2. headers and entries are marked as modified.
|
|
* 3. primary and secondary entries are identical.
|
|
*/
|
|
int MarkBadKernelEntryTest() {
|
|
GptData *gpt;
|
|
GptEntry *entries, *entries2;
|
|
|
|
gpt = GetEmptyGptData();
|
|
entries = (GptEntry*)gpt->primary_entries;
|
|
entries2 = (GptEntry*)gpt->secondary_entries;
|
|
|
|
BuildTestGptData(gpt);
|
|
gpt->current_kernel = KERNEL_A;
|
|
EXPECT(GPT_SUCCESS == GptUpdateKernelEntry(gpt, GPT_UPDATE_ENTRY_BAD));
|
|
EXPECT((GPT_MODIFIED_HEADER1 | GPT_MODIFIED_ENTRIES1 |
|
|
GPT_MODIFIED_HEADER2 | GPT_MODIFIED_ENTRIES2) == gpt->modified);
|
|
EXPECT(entries[KERNEL_A].attributes & CGPT_ATTRIBUTE_BAD_MASK);
|
|
EXPECT(entries2[KERNEL_A].attributes & CGPT_ATTRIBUTE_BAD_MASK);
|
|
EXPECT(0 == Memcmp(entries, entries2, TOTAL_ENTRIES_SIZE));
|
|
|
|
return TEST_OK;
|
|
}
|
|
|
|
/* Given an invalid kernel type, and expect GptUpdateKernelEntry() returns
|
|
* GPT_ERROR_INVALID_UPDATE_TYPE. */
|
|
int UpdateInvalidKernelTypeTest() {
|
|
GptData *gpt;
|
|
|
|
gpt = GetEmptyGptData();
|
|
BuildTestGptData(gpt);
|
|
gpt->current_kernel = 0; /* anything, but not CGPT_KERNEL_ENTRY_NOT_FOUND */
|
|
EXPECT(GPT_ERROR_INVALID_UPDATE_TYPE ==
|
|
GptUpdateKernelEntry(gpt, 99)); /* any invalid update_type value */
|
|
|
|
return TEST_OK;
|
|
}
|
|
|
|
/* A normal boot case:
|
|
* GptInit()
|
|
* GptNextKernelEntry()
|
|
* GptUpdateKernelEntry()
|
|
*/
|
|
int NormalBootCase() {
|
|
GptData *gpt;
|
|
GptEntry *entries;
|
|
uint64_t start_sector, size;
|
|
|
|
gpt = GetEmptyGptData();
|
|
entries = (GptEntry*)gpt->primary_entries;
|
|
BuildTestGptData(gpt);
|
|
|
|
EXPECT(GPT_SUCCESS == GptInit(gpt));
|
|
EXPECT(GPT_SUCCESS == GptNextKernelEntry(gpt, &start_sector, &size));
|
|
EXPECT(start_sector == 34); /* Kernel A, see top of this file. */
|
|
EXPECT(size == 100);
|
|
|
|
EXPECT(GPT_SUCCESS == GptUpdateKernelEntry(gpt, GPT_UPDATE_ENTRY_TRY));
|
|
EXPECT(((entries[KERNEL_A].attributes & CGPT_ATTRIBUTE_TRIES_MASK) >>
|
|
CGPT_ATTRIBUTE_TRIES_OFFSET) == 1);
|
|
|
|
return TEST_OK;
|
|
}
|
|
|
|
/* Higher priority kernel should boot first.
|
|
* KERNEL_A is low priority
|
|
* KERNEL_B is high priority.
|
|
* We expect KERNEL_B is selected in first run, and then KERNEL_A.
|
|
* We also expect the GptNextKernelEntry() wraps back to KERNEL_B if it's called
|
|
* after twice.
|
|
*/
|
|
int HigherPriorityTest() {
|
|
GptData *gpt;
|
|
GptEntry *entries;
|
|
|
|
gpt = GetEmptyGptData();
|
|
entries = (GptEntry*)gpt->primary_entries;
|
|
BuildTestGptData(gpt);
|
|
|
|
SetPriority(gpt, PRIMARY, KERNEL_A, 0);
|
|
SetPriority(gpt, PRIMARY, KERNEL_B, 1);
|
|
RefreshCrc32(gpt);
|
|
|
|
EXPECT(GPT_SUCCESS == GptInit(gpt));
|
|
EXPECT(GPT_SUCCESS == GptNextKernelEntry(gpt, NULL, NULL));
|
|
EXPECT(KERNEL_B == gpt->current_kernel);
|
|
|
|
EXPECT(GPT_SUCCESS == GptNextKernelEntry(gpt, NULL, NULL));
|
|
EXPECT(KERNEL_A == gpt->current_kernel);
|
|
|
|
EXPECT(GPT_SUCCESS == GptNextKernelEntry(gpt, NULL, NULL));
|
|
EXPECT(KERNEL_B == gpt->current_kernel);
|
|
|
|
return TEST_OK;
|
|
}
|
|
|
|
int main(int argc, char *argv[]) {
|
|
int i;
|
|
int error_count = 0;
|
|
struct {
|
|
char *name;
|
|
test_func fp;
|
|
int retval;
|
|
} test_cases[] = {
|
|
{ TEST_CASE(TestBuildTestGptData), },
|
|
{ TEST_CASE(ParameterTests), },
|
|
{ TEST_CASE(SignatureTest), },
|
|
{ TEST_CASE(RevisionTest), },
|
|
{ TEST_CASE(SizeTest), },
|
|
{ TEST_CASE(ReservedFieldsTest), },
|
|
{ TEST_CASE(MyLbaTest), },
|
|
{ TEST_CASE(SizeOfPartitionEntryTest), },
|
|
{ TEST_CASE(NumberOfPartitionEntriesTest), },
|
|
{ TEST_CASE(PartitionEntryLbaTest), },
|
|
{ TEST_CASE(FirstUsableLbaAndLastUsableLbaTest), },
|
|
{ TEST_CASE(HeaderCrcTest), },
|
|
{ TEST_CASE(EntriesCrcTest), },
|
|
{ TEST_CASE(IdenticalEntriesTest), },
|
|
{ TEST_CASE(SynonymousHeaderTest), },
|
|
{ TEST_CASE(ValidEntryTest), },
|
|
{ TEST_CASE(OverlappedPartitionTest), },
|
|
{ TEST_CASE(CorruptCombinationTest), },
|
|
{ TEST_CASE(TestQuickSortFixed), },
|
|
{ TEST_CASE(TestQuickSortRandom), },
|
|
{ TEST_CASE(NoValidKernelEntryTest), },
|
|
{ TEST_CASE(CombinationalNextKernelEntryTest), },
|
|
{ TEST_CASE(IncreaseTriesTest), },
|
|
{ TEST_CASE(MarkBadKernelEntryTest), },
|
|
{ TEST_CASE(UpdateInvalidKernelTypeTest), },
|
|
{ TEST_CASE(NormalBootCase), },
|
|
{ TEST_CASE(HigherPriorityTest), },
|
|
{ TEST_CASE(TestCrc32TestVectors), },
|
|
};
|
|
|
|
for (i = 0; i < sizeof(test_cases)/sizeof(test_cases[0]); ++i) {
|
|
printf("Running %s() ...\n", test_cases[i].name);
|
|
test_cases[i].retval = test_cases[i].fp();
|
|
if (test_cases[i].retval) {
|
|
printf(COL_RED "[ERROR]\n\n" COL_STOP);
|
|
++error_count;
|
|
} else {
|
|
printf(COL_GREEN "[PASS]\n\n" COL_STOP);
|
|
}
|
|
}
|
|
|
|
if (error_count) {
|
|
printf("\n--------------------------------------------------\n");
|
|
printf(COL_RED "The following %d test cases are failed:\n" COL_STOP,
|
|
error_count);
|
|
for (i = 0; i < sizeof(test_cases)/sizeof(test_cases[0]); ++i) {
|
|
if (test_cases[i].retval)
|
|
printf(" %s()\n", test_cases[i].name);
|
|
}
|
|
}
|
|
|
|
return (error_count) ? 1 : 0;
|
|
}
|