mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2025-11-24 18:25:10 +00:00
cgpt supports GptNextKernelEntry() and GptUpdateKernelEntry()
Review URL: http://codereview.chromium.org/1922004
This commit is contained in:
232
cgptlib/cgpt.c
232
cgptlib/cgpt.c
@@ -465,6 +465,14 @@ void UpdateCrc(GptData *gpt) {
|
||||
primary_header = (GptHeader*)gpt->primary_header;
|
||||
secondary_header = (GptHeader*)gpt->secondary_header;
|
||||
|
||||
if (gpt->modified & GPT_MODIFIED_ENTRIES1) {
|
||||
primary_header->entries_crc32 =
|
||||
Crc32(gpt->primary_entries, TOTAL_ENTRIES_SIZE);
|
||||
}
|
||||
if (gpt->modified & GPT_MODIFIED_ENTRIES2) {
|
||||
secondary_header->entries_crc32 =
|
||||
Crc32(gpt->secondary_entries, TOTAL_ENTRIES_SIZE);
|
||||
}
|
||||
if (gpt->modified & GPT_MODIFIED_HEADER1) {
|
||||
primary_header->header_crc32 = 0;
|
||||
primary_header->header_crc32 = Crc32(
|
||||
@@ -475,14 +483,6 @@ void UpdateCrc(GptData *gpt) {
|
||||
secondary_header->header_crc32 = Crc32(
|
||||
(const uint8_t *)secondary_header, secondary_header->size);
|
||||
}
|
||||
if (gpt->modified & GPT_MODIFIED_ENTRIES1) {
|
||||
primary_header->entries_crc32 =
|
||||
Crc32(gpt->primary_entries, TOTAL_ENTRIES_SIZE);
|
||||
}
|
||||
if (gpt->modified & GPT_MODIFIED_ENTRIES2) {
|
||||
secondary_header->entries_crc32 =
|
||||
Crc32(gpt->secondary_entries, TOTAL_ENTRIES_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
/* Does every sanity check, and returns if any header/entries needs to be
|
||||
@@ -528,25 +528,219 @@ int GptInit(GptData *gpt) {
|
||||
|
||||
UpdateCrc(gpt);
|
||||
|
||||
/* FIXME: will remove the next line soon. */
|
||||
gpt->current_kernel = 1;
|
||||
gpt->current_kernel = CGPT_KERNEL_ENTRY_NOT_FOUND;
|
||||
|
||||
return GPT_SUCCESS;
|
||||
}
|
||||
|
||||
/* stub code */
|
||||
static int start[] = { 34, 10034 };
|
||||
/* Helper function to get a pointer to the partition entry.
|
||||
* 'secondary' is either PRIMARY or SECONDARY.
|
||||
* 'entry_index' is the partition index: [0, number_of_entries).
|
||||
*/
|
||||
GptEntry *GetEntry(GptData *gpt, int secondary, int entry_index) {
|
||||
GptHeader *header;
|
||||
uint8_t *entries;
|
||||
|
||||
if (secondary == PRIMARY) {
|
||||
header = (GptHeader*)gpt->primary_header;
|
||||
entries = gpt->primary_entries;
|
||||
} else {
|
||||
header = (GptHeader*)gpt->secondary_header;
|
||||
entries = gpt->secondary_entries;
|
||||
}
|
||||
|
||||
return (GptEntry*)(&entries[header->size_of_entry * entry_index]);
|
||||
}
|
||||
|
||||
/* The following functions are helpers to access attributes bit more easily.
|
||||
* 'secondary' is either PRIMARY or SECONDARY.
|
||||
* 'entry_index' is the partition index: [0, number_of_entries).
|
||||
*
|
||||
* Get*() return the exact value (shifted and masked).
|
||||
*/
|
||||
void SetPriority(GptData *gpt, int secondary, int entry_index, int priority) {
|
||||
GptEntry *entry;
|
||||
entry = GetEntry(gpt, secondary, entry_index);
|
||||
|
||||
assert(priority >= 0 && priority <= CGPT_ATTRIBUTE_MAX_PRIORITY);
|
||||
entry->attributes &= ~CGPT_ATTRIBUTE_PRIORITY_MASK;
|
||||
entry->attributes |= (uint64_t)priority << CGPT_ATTRIBUTE_PRIORITY_OFFSET;
|
||||
}
|
||||
|
||||
int GetPriority(GptData *gpt, int secondary, int entry_index) {
|
||||
GptEntry *entry;
|
||||
entry = GetEntry(gpt, secondary, entry_index);
|
||||
return (entry->attributes & CGPT_ATTRIBUTE_PRIORITY_MASK) >>
|
||||
CGPT_ATTRIBUTE_PRIORITY_OFFSET;
|
||||
}
|
||||
|
||||
void SetBad(GptData *gpt, int secondary, int entry_index, int bad) {
|
||||
GptEntry *entry;
|
||||
entry = GetEntry(gpt, secondary, entry_index);
|
||||
|
||||
assert(bad >= 0 && bad <= CGPT_ATTRIBUTE_MAX_BAD);
|
||||
entry->attributes &= ~CGPT_ATTRIBUTE_BAD_MASK;
|
||||
entry->attributes |= (uint64_t)bad << CGPT_ATTRIBUTE_BAD_OFFSET;
|
||||
}
|
||||
|
||||
int GetBad(GptData *gpt, int secondary, int entry_index) {
|
||||
GptEntry *entry;
|
||||
entry = GetEntry(gpt, secondary, entry_index);
|
||||
return (entry->attributes & CGPT_ATTRIBUTE_BAD_MASK) >>
|
||||
CGPT_ATTRIBUTE_BAD_OFFSET;
|
||||
}
|
||||
|
||||
void SetTries(GptData *gpt, int secondary, int entry_index, int tries) {
|
||||
GptEntry *entry;
|
||||
entry = GetEntry(gpt, secondary, entry_index);
|
||||
|
||||
assert(tries >= 0 && tries <= CGPT_ATTRIBUTE_MAX_TRIES);
|
||||
entry->attributes &= ~CGPT_ATTRIBUTE_TRIES_MASK;
|
||||
entry->attributes |= (uint64_t)tries << CGPT_ATTRIBUTE_TRIES_OFFSET;
|
||||
}
|
||||
|
||||
int GetTries(GptData *gpt, int secondary, int entry_index) {
|
||||
GptEntry *entry;
|
||||
entry = GetEntry(gpt, secondary, entry_index);
|
||||
return (entry->attributes & CGPT_ATTRIBUTE_TRIES_MASK) >>
|
||||
CGPT_ATTRIBUTE_TRIES_OFFSET;
|
||||
}
|
||||
|
||||
void SetSuccess(GptData *gpt, int secondary, int entry_index, int success) {
|
||||
GptEntry *entry;
|
||||
entry = GetEntry(gpt, secondary, entry_index);
|
||||
|
||||
assert(success >= 0 && success <= CGPT_ATTRIBUTE_MAX_SUCCESS);
|
||||
entry->attributes &= ~CGPT_ATTRIBUTE_SUCCESS_MASK;
|
||||
entry->attributes |= (uint64_t)success << CGPT_ATTRIBUTE_SUCCESS_OFFSET;
|
||||
}
|
||||
|
||||
int GetSuccess(GptData *gpt, int secondary, int entry_index) {
|
||||
GptEntry *entry;
|
||||
entry = GetEntry(gpt, secondary, entry_index);
|
||||
return (entry->attributes & CGPT_ATTRIBUTE_SUCCESS_MASK) >>
|
||||
CGPT_ATTRIBUTE_SUCCESS_OFFSET;
|
||||
}
|
||||
|
||||
/* Compare two priority values. Actually it is a circular priority, which is:
|
||||
* 3 > 2 > 1 > 0, but 0 > 3. (-1 means very low, and anyone is higher than -1)
|
||||
*
|
||||
* Return 1 if 'a' has higher priority than 'b'.
|
||||
*/
|
||||
int IsHigherPriority(int a, int b) {
|
||||
if ((a == 0) && (b == CGPT_ATTRIBUTE_MAX_PRIORITY))
|
||||
return 1;
|
||||
else if ((a == CGPT_ATTRIBUTE_MAX_PRIORITY) && (b == 0))
|
||||
return 0;
|
||||
else
|
||||
return (a > b) ? 1 : 0;
|
||||
}
|
||||
|
||||
/* This function walks through the whole partition table (see note below),
|
||||
* and pick up the active and valid (not marked as bad) kernel entry with
|
||||
* *highest* priority (except gpt->current_kernel itself).
|
||||
*
|
||||
* Returns start_sector and its size if a candidate kernel is found.
|
||||
*
|
||||
* Note: in the first walk (gpt->current_kernel==CGPT_KERNEL_ENTRY_NOT_FOUND),
|
||||
* the scan range is whole table. But in later scans, we only scan
|
||||
* (header->number_of_entries - 1) entries because we are looking for
|
||||
* next kernel with lower priority (consider the case that highest
|
||||
* priority kernel is still active and valid).
|
||||
*/
|
||||
int GptNextKernelEntry(GptData *gpt, uint64_t *start_sector, uint64_t *size) {
|
||||
/* FIXME: the following code is not really code, just returns anything */
|
||||
gpt->current_kernel ^= 1;
|
||||
if (start_sector) *start_sector = start[gpt->current_kernel];
|
||||
if (size) *size = 10000;
|
||||
GptHeader *header;
|
||||
GptEntry *entry;
|
||||
int scan, current_priority;
|
||||
int begin, end; /* [begin, end], which end is included. */
|
||||
Guid chromeos_kernel = GPT_ENT_TYPE_CHROMEOS_KERNEL;
|
||||
|
||||
header = (GptHeader*)gpt->primary_header;
|
||||
current_priority = -1; /* pretty low priority */
|
||||
if (gpt->current_kernel == CGPT_KERNEL_ENTRY_NOT_FOUND) {
|
||||
begin = 0;
|
||||
end = header->number_of_entries - 1;
|
||||
} else {
|
||||
begin = (gpt->current_kernel + 1) % header->number_of_entries;
|
||||
end = (gpt->current_kernel - 1 + header->number_of_entries) %
|
||||
header->number_of_entries;
|
||||
}
|
||||
|
||||
scan = begin;
|
||||
do {
|
||||
entry = GetEntry(gpt, PRIMARY, scan);
|
||||
if (!Memcmp(&entry->type, &chromeos_kernel, sizeof(Guid)) &&
|
||||
!GetBad(gpt, PRIMARY, scan) &&
|
||||
((gpt->current_kernel == CGPT_KERNEL_ENTRY_NOT_FOUND) ||
|
||||
(IsHigherPriority(GetPriority(gpt, PRIMARY, scan),
|
||||
current_priority)))) {
|
||||
gpt->current_kernel = scan;
|
||||
current_priority = GetPriority(gpt, PRIMARY, gpt->current_kernel);
|
||||
}
|
||||
|
||||
if (scan == end) break;
|
||||
scan = (scan + 1) % header->number_of_entries;
|
||||
} while (1);
|
||||
|
||||
if (gpt->current_kernel == CGPT_KERNEL_ENTRY_NOT_FOUND)
|
||||
return GPT_ERROR_NO_VALID_KERNEL;
|
||||
|
||||
entry = GetEntry(gpt, PRIMARY, gpt->current_kernel);
|
||||
assert(entry->starting_lba <= entry->ending_lba);
|
||||
|
||||
if (start_sector) *start_sector = entry->starting_lba;
|
||||
if (size) *size = entry->ending_lba - entry->starting_lba + 1;
|
||||
|
||||
return GPT_SUCCESS;
|
||||
}
|
||||
|
||||
/* Given a update_type, this function updates the corresponding bits in GptData.
|
||||
*
|
||||
* Returns GPT_SUCCESS if no error. gpt->modified is set if any header and
|
||||
* entries needs to be updated to hard drive.
|
||||
* GPT_ERROR_INVALID_UPDATE_TYPE if given an invalid update_type.
|
||||
*/
|
||||
int GptUpdateKernelEntry(GptData *gpt, uint32_t update_type) {
|
||||
/* FIXME: the following code is not really code, just return anything */
|
||||
gpt->modified |= (GPT_MODIFIED_HEADER1 | GPT_MODIFIED_ENTRIES1) <<
|
||||
gpt->current_kernel;
|
||||
Guid chromeos_type = GPT_ENT_TYPE_CHROMEOS_KERNEL;
|
||||
int primary_is_modified = 0;
|
||||
|
||||
assert(gpt->current_kernel != CGPT_KERNEL_ENTRY_NOT_FOUND);
|
||||
assert(!Memcmp(&(GetEntry(gpt, PRIMARY, gpt->current_kernel)->type),
|
||||
&chromeos_type, sizeof(Guid)));
|
||||
|
||||
/* Modify primary entries first, then copy to secondary later. */
|
||||
switch (update_type) {
|
||||
case GPT_UPDATE_ENTRY_TRY: {
|
||||
/* Increase tries value until CGPT_ATTRIBUTE_MAX_TRIES. */
|
||||
int tries;
|
||||
tries = GetTries(gpt, PRIMARY, gpt->current_kernel);
|
||||
if (tries < CGPT_ATTRIBUTE_MAX_TRIES) {
|
||||
++tries;
|
||||
SetTries(gpt, PRIMARY, gpt->current_kernel, tries);
|
||||
primary_is_modified = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case GPT_UPDATE_ENTRY_BAD: {
|
||||
GetEntry(gpt, PRIMARY, gpt->current_kernel)->attributes |=
|
||||
CGPT_ATTRIBUTE_BAD_MASK;
|
||||
primary_is_modified = 1;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
return GPT_ERROR_INVALID_UPDATE_TYPE;
|
||||
}
|
||||
}
|
||||
|
||||
if (primary_is_modified) {
|
||||
/* Claim only primary is valid so that secondary is overwritten. */
|
||||
RepairEntries(gpt, MASK_PRIMARY);
|
||||
/* Actually two entries are dirty now.
|
||||
* Also two headers are dirty because entries_crc32 has been updated. */
|
||||
gpt->modified |= (GPT_MODIFIED_HEADER1 | GPT_MODIFIED_ENTRIES1 |
|
||||
GPT_MODIFIED_HEADER2 | GPT_MODIFIED_ENTRIES2);
|
||||
UpdateCrc(gpt);
|
||||
}
|
||||
|
||||
return GPT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ enum {
|
||||
GPT_ERROR_INVALID_ENTRIES,
|
||||
GPT_ERROR_INVALID_SECTOR_SIZE,
|
||||
GPT_ERROR_INVALID_SECTOR_NUMBER,
|
||||
GPT_ERROR_INVALID_UPDATE_TYPE,
|
||||
};
|
||||
|
||||
/* Bit masks for GptData.modified field. */
|
||||
@@ -24,12 +25,18 @@ enum {
|
||||
#define GPT_MODIFIED_ENTRIES1 0x04
|
||||
#define GPT_MODIFIED_ENTRIES2 0x08
|
||||
|
||||
#define GPT_UPDATE_ENTRY_TRY 1
|
||||
/* The 'update_type' of GptUpdateKernelEntry()
|
||||
* We expose TRY and BAD only because those are what verified boot needs.
|
||||
* For more precise control on GPT attribute bits, please refer to
|
||||
* gpt_internal.h */
|
||||
enum {
|
||||
GPT_UPDATE_ENTRY_TRY = 1,
|
||||
/* System will be trying to boot the currently selected kernel partition.
|
||||
* Update its try count if necessary. */
|
||||
#define GPT_UPDATE_ENTRY_BAD 2
|
||||
GPT_UPDATE_ENTRY_BAD = 2,
|
||||
/* The currently selected kernel partition failed validation. Mark entry as
|
||||
* invalid. */
|
||||
};
|
||||
|
||||
/* Defines ChromeOS-specific limitation on GPT */
|
||||
#define MIN_SIZE_OF_HEADER 92
|
||||
@@ -79,17 +86,25 @@ typedef struct {
|
||||
* 0x08 = table2 */
|
||||
|
||||
/* Internal state */
|
||||
uint8_t current_kernel; /* the current kernel index */
|
||||
int current_kernel; /* the current chromeos kernel index in partition table.
|
||||
* -1 means not found on drive. */
|
||||
} GptData;
|
||||
|
||||
int GptInit(GptData *gpt);
|
||||
/* Initializes the GPT data structure's internal state. The header1, header2,
|
||||
* table1, table2, and drive_size fields should be filled in first.
|
||||
/* Initializes the GPT data structure's internal state. The following fields
|
||||
* must be filled before calling this function:
|
||||
*
|
||||
* primary_header
|
||||
* secondary_header
|
||||
* primary_entries
|
||||
* secondary_entries
|
||||
* sector_bytes
|
||||
* drive_sectors
|
||||
*
|
||||
* On return the modified field may be set, if the GPT data has been modified
|
||||
* and should be written to disk.
|
||||
*
|
||||
* Returns 0 if successful, non-zero if error:
|
||||
* Returns GPT_SUCCESS if successful, non-zero if error:
|
||||
* GPT_ERROR_INVALID_HEADERS, both partition table headers are invalid, enters
|
||||
* recovery mode,
|
||||
* GPT_ERROR_INVALID_ENTRIES, both partition table entries are invalid, enters
|
||||
@@ -104,7 +119,7 @@ int GptNextKernelEntry(GptData *gpt, uint64_t *start_sector, uint64_t *size);
|
||||
* for the start of the kernel partition, and the size parameter contains the
|
||||
* size of the kernel partition in LBA sectors.
|
||||
*
|
||||
* Returns 0 if successful, else
|
||||
* Returns GPT_SUCCESS if successful, else
|
||||
* GPT_ERROR_NO_VALID_KERNEL, no avaliable kernel, enters recovery mode */
|
||||
|
||||
int GptUpdateKernelEntry(GptData *gpt, uint32_t update_type);
|
||||
@@ -114,6 +129,8 @@ int GptUpdateKernelEntry(GptData *gpt, uint32_t update_type);
|
||||
* On return the modified field may be set, if the GPT data has been modified
|
||||
* and should be written to disk.
|
||||
*
|
||||
* Returns 0 if successful, 1 if error. */
|
||||
* Returns GPT_SUCCESS if successful, else
|
||||
* GPT_ERROR_INVALID_UPDATE_TYPE, invalid 'update_type' is given.
|
||||
*/
|
||||
|
||||
#endif /* VBOOT_REFERENCE_CGPT_H_ */
|
||||
|
||||
@@ -9,8 +9,6 @@
|
||||
#include <stdint.h>
|
||||
#include "cgpt.h"
|
||||
|
||||
/* Internal use only.
|
||||
* Don't use them unless you know what you are doing. */
|
||||
int CheckParameters(GptData *gpt);
|
||||
uint32_t CheckHeaderSignature(GptData *gpt);
|
||||
uint32_t CheckRevision(GptData *gpt);
|
||||
@@ -33,4 +31,52 @@ typedef struct {
|
||||
uint64_t ending;
|
||||
} pair_t;
|
||||
|
||||
GptEntry *GetEntry(GptData *gpt, int secondary, int entry_index);
|
||||
void SetPriority(GptData *gpt, int secondary, int entry_index, int priority);
|
||||
int GetPriority(GptData *gpt, int secondary, int entry_index);
|
||||
void SetBad(GptData *gpt, int secondary, int entry_index, int bad);
|
||||
int GetBad(GptData *gpt, int secondary, int entry_index);
|
||||
void SetTries(GptData *gpt, int secondary, int entry_index, int tries);
|
||||
int GetTries(GptData *gpt, int secondary, int entry_index);
|
||||
void SetSuccess(GptData *gpt, int secondary, int entry_index, int success);
|
||||
int GetSuccess(GptData *gpt, int secondary, int entry_index);
|
||||
|
||||
/* If gpt->current_kernel is this value, means either:
|
||||
* 1. an initial value before scanning GPT entries,
|
||||
* 2. after scanning, no any valid kernel is found.
|
||||
*/
|
||||
#define CGPT_KERNEL_ENTRY_NOT_FOUND (-1)
|
||||
|
||||
/* Bit definitions and masks for GPT attributes.
|
||||
*
|
||||
* 63 -- do not automounting
|
||||
* 62 -- hidden
|
||||
* 60 -- read-only
|
||||
* :
|
||||
* 57 -- bad kernel entry
|
||||
* 56 -- success
|
||||
* 55,52 -- tries
|
||||
* 51,48 -- priority
|
||||
* 0 -- system partition
|
||||
*/
|
||||
#define CGPT_ATTRIBUTE_BAD_OFFSET 57
|
||||
#define CGPT_ATTRIBUTE_MAX_BAD (1ULL)
|
||||
#define CGPT_ATTRIBUTE_BAD_MASK (CGPT_ATTRIBUTE_MAX_BAD << \
|
||||
CGPT_ATTRIBUTE_BAD_OFFSET)
|
||||
|
||||
#define CGPT_ATTRIBUTE_SUCCESS_OFFSET 56
|
||||
#define CGPT_ATTRIBUTE_MAX_SUCCESS (1ULL)
|
||||
#define CGPT_ATTRIBUTE_SUCCESS_MASK (CGPT_ATTRIBUTE_MAX_SUCCESS << \
|
||||
CGPT_ATTRIBUTE_SUCCESS_OFFSET)
|
||||
|
||||
#define CGPT_ATTRIBUTE_TRIES_OFFSET 52
|
||||
#define CGPT_ATTRIBUTE_MAX_TRIES (15ULL)
|
||||
#define CGPT_ATTRIBUTE_TRIES_MASK (CGPT_ATTRIBUTE_MAX_TRIES << \
|
||||
CGPT_ATTRIBUTE_TRIES_OFFSET)
|
||||
|
||||
#define CGPT_ATTRIBUTE_PRIORITY_OFFSET 48
|
||||
#define CGPT_ATTRIBUTE_MAX_PRIORITY (15ULL)
|
||||
#define CGPT_ATTRIBUTE_PRIORITY_MASK (CGPT_ATTRIBUTE_MAX_PRIORITY << \
|
||||
CGPT_ATTRIBUTE_PRIORITY_OFFSET)
|
||||
|
||||
#endif /* VBOOT_REFERENCE_CGPT_INTERNAL_H_ */
|
||||
|
||||
@@ -15,17 +15,23 @@
|
||||
/* 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
|
||||
* 134 100 kernel B
|
||||
* 234 100 root A
|
||||
* 334 100 root B
|
||||
* 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
|
||||
@@ -87,6 +93,9 @@ GptData* GetEmptyGptData() {
|
||||
gpt.secondary_entries = secondary_entries;
|
||||
ZeroHeadersEntries(&gpt);
|
||||
|
||||
/* Initialize GptData internal states. */
|
||||
gpt.current_kernel = CGPT_KERNEL_ENTRY_NOT_FOUND;
|
||||
|
||||
return &gpt;
|
||||
}
|
||||
|
||||
@@ -99,9 +108,11 @@ 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;
|
||||
@@ -119,10 +130,10 @@ void BuildTestGptData(GptData *gpt) {
|
||||
Memcpy(&entries[0].type, &chromeos_kernel, sizeof(chromeos_kernel));
|
||||
entries[0].starting_lba = 34;
|
||||
entries[0].ending_lba = 133;
|
||||
Memcpy(&entries[1].type, &chromeos_kernel, sizeof(chromeos_kernel));
|
||||
Memcpy(&entries[1].type, &chromeos_rootfs, sizeof(chromeos_rootfs));
|
||||
entries[1].starting_lba = 134;
|
||||
entries[1].ending_lba = 233;
|
||||
Memcpy(&entries[2].type, &chromeos_kernel, sizeof(chromeos_kernel));
|
||||
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));
|
||||
@@ -888,6 +899,223 @@ int CorruptCombinationTest() {
|
||||
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;
|
||||
@@ -916,6 +1144,13 @@ int main(int argc, char *argv[]) {
|
||||
{ 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), },
|
||||
};
|
||||
|
||||
for (i = 0; i < sizeof(test_cases)/sizeof(test_cases[0]); ++i) {
|
||||
|
||||
Reference in New Issue
Block a user