mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2025-11-24 02:05:01 +00:00
A lot of functions were added some time ago, nominally to support keeping the firmware in an MTD device that wasn't formatted with the GPT headers. That work was never completed, so these functions aren't used anywhere. We may want to resurrect this work at some future point. Until then, this CL just moves some of the functions into an "unused" file. BUG=chromium:231567 BRANCH=ToT TEST=manual All tests pass, all firmware and external repos build. Change-Id: I420dd52d1cea0418cedf2f8e834c61145915f20c Signed-off-by: Bill Richardson <wfrichar@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/207037 Reviewed-by: Randall Spangler <rspangler@chromium.org>
2009 lines
61 KiB
C
2009 lines
61 KiB
C
/* 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 <string.h>
|
|
|
|
#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 "errno.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_SECTOR + GPT_HEADER_SECTOR * 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);
|
|
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;
|
|
}
|
|
|
|
/* 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;
|
|
}
|