Reformat cgptlib to kernel style

No code changes, just reformatting.

BUG=none
BRANCH=none
TEST=make runtests

Change-Id: Ib8748df93c64395c88e1f789805393fcfe3ac419
Signed-off-by: Randall Spangler <rspangler@chromium.org>
Reviewed-on: https://gerrit.chromium.org/gerrit/42397
Reviewed-by: Bill Richardson <wfrichar@chromium.org>
This commit is contained in:
Randall Spangler
2013-01-30 12:52:02 -08:00
committed by ChromeBot
parent 7993f257af
commit cefe12c105
6 changed files with 832 additions and 752 deletions

View File

@@ -1,4 +1,4 @@
/* Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
/* 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.
*/
@@ -10,150 +10,173 @@
#include "utility.h"
#include "vboot_api.h"
int GptInit(GptData *gpt) {
int retval;
int GptInit(GptData *gpt)
{
int retval;
gpt->modified = 0;
gpt->current_kernel = CGPT_KERNEL_ENTRY_NOT_FOUND;
gpt->current_priority = 999;
gpt->modified = 0;
gpt->current_kernel = CGPT_KERNEL_ENTRY_NOT_FOUND;
gpt->current_priority = 999;
retval = GptSanityCheck(gpt);
if (GPT_SUCCESS != retval) {
VBDEBUG(("GptInit() failed sanity check\n"));
return retval;
}
retval = GptSanityCheck(gpt);
if (GPT_SUCCESS != retval) {
VBDEBUG(("GptInit() failed sanity check\n"));
return retval;
}
GptRepair(gpt);
return GPT_SUCCESS;
GptRepair(gpt);
return GPT_SUCCESS;
}
int GptNextKernelEntry(GptData *gpt, uint64_t *start_sector, uint64_t *size)
{
GptHeader *header = (GptHeader *)gpt->primary_header;
GptEntry *entries = (GptEntry *)gpt->primary_entries;
GptEntry *e;
int new_kernel = CGPT_KERNEL_ENTRY_NOT_FOUND;
int new_prio = 0;
uint32_t i;
int GptNextKernelEntry(GptData* gpt, uint64_t* start_sector, uint64_t* size) {
GptHeader* header = (GptHeader*)gpt->primary_header;
GptEntry* entries = (GptEntry*)gpt->primary_entries;
GptEntry* e;
int new_kernel = CGPT_KERNEL_ENTRY_NOT_FOUND;
int new_prio = 0;
uint32_t i;
/*
* If we already found a kernel, continue the scan at the current
* kernel's priority, in case there is another kernel with the same
* priority.
*/
if (gpt->current_kernel != CGPT_KERNEL_ENTRY_NOT_FOUND) {
for (i = gpt->current_kernel + 1;
i < header->number_of_entries; i++) {
e = entries + i;
if (!IsKernelEntry(e))
continue;
VBDEBUG(("GptNextKernelEntry looking at same prio "
"partition %d\n", i+1));
VBDEBUG(("GptNextKernelEntry s%d t%d p%d\n",
GetEntrySuccessful(e), GetEntryTries(e),
GetEntryPriority(e)));
if (!(GetEntrySuccessful(e) || GetEntryTries(e)))
continue;
if (GetEntryPriority(e) == gpt->current_priority) {
gpt->current_kernel = i;
*start_sector = e->starting_lba;
*size = e->ending_lba - e->starting_lba + 1;
VBDEBUG(("GptNextKernelEntry likes it\n"));
return GPT_SUCCESS;
}
}
}
/* If we already found a kernel, continue the scan at the current
* kernel's prioity, in case there is another kernel with the same
* priority. */
if (gpt->current_kernel != CGPT_KERNEL_ENTRY_NOT_FOUND) {
for (i = gpt->current_kernel + 1; i < header->number_of_entries; i++) {
e = entries + i;
if (!IsKernelEntry(e))
continue;
VBDEBUG(("GptNextKernelEntry looking at same prio partition %d\n", i+1));
VBDEBUG(("GptNextKernelEntry s%d t%d p%d\n",
GetEntrySuccessful(e), GetEntryTries(e), GetEntryPriority(e)));
if (!(GetEntrySuccessful(e) || GetEntryTries(e)))
continue;
if (GetEntryPriority(e) == gpt->current_priority) {
gpt->current_kernel = i;
*start_sector = e->starting_lba;
*size = e->ending_lba - e->starting_lba + 1;
VBDEBUG(("GptNextKernelEntry likes that one\n"));
return GPT_SUCCESS;
}
}
}
/*
* We're still here, so scan for the remaining kernel with the highest
* priority less than the previous attempt.
*/
for (i = 0, e = entries; i < header->number_of_entries; i++, e++) {
int current_prio = GetEntryPriority(e);
if (!IsKernelEntry(e))
continue;
VBDEBUG(("GptNextKernelEntry looking at new prio "
"partition %d\n", i+1));
VBDEBUG(("GptNextKernelEntry s%d t%d p%d\n",
GetEntrySuccessful(e), GetEntryTries(e),
GetEntryPriority(e)));
if (!(GetEntrySuccessful(e) || GetEntryTries(e)))
continue;
if (current_prio >= gpt->current_priority) {
/* Already returned this kernel in a previous call */
continue;
}
if (current_prio > new_prio) {
new_kernel = i;
new_prio = current_prio;
}
}
/* We're still here, so scan for the remaining kernel with the
* highest priority less than the previous attempt. */
for (i = 0, e = entries; i < header->number_of_entries; i++, e++) {
int current_prio = GetEntryPriority(e);
if (!IsKernelEntry(e))
continue;
VBDEBUG(("GptNextKernelEntry looking at new prio partition %d\n", i+1));
VBDEBUG(("GptNextKernelEntry s%d t%d p%d\n",
GetEntrySuccessful(e), GetEntryTries(e), GetEntryPriority(e)));
if (!(GetEntrySuccessful(e) || GetEntryTries(e)))
continue;
if (current_prio >= gpt->current_priority)
continue; /* Already returned this kernel in a previous call */
if (current_prio > new_prio) {
new_kernel = i;
new_prio = current_prio;
}
}
/*
* Save what we found. Note that if we didn't find a new kernel,
* new_prio will still be -1, so future calls to this function will
* also fail.
*/
gpt->current_kernel = new_kernel;
gpt->current_priority = new_prio;
/* Save what we found. Note that if we didn't find a new kernel,
* new_prio will still be -1, so future calls to this function will
* also fail. */
gpt->current_kernel = new_kernel;
gpt->current_priority = new_prio;
if (CGPT_KERNEL_ENTRY_NOT_FOUND == new_kernel) {
VBDEBUG(("GptNextKernelEntry no more kernels\n"));
return GPT_ERROR_NO_VALID_KERNEL;
}
if (CGPT_KERNEL_ENTRY_NOT_FOUND == new_kernel) {
VBDEBUG(("GptNextKernelEntry no more kernels\n"));
return GPT_ERROR_NO_VALID_KERNEL;
}
VBDEBUG(("GptNextKernelEntry likes partition %d\n", new_kernel+1));
e = entries + new_kernel;
*start_sector = e->starting_lba;
*size = e->ending_lba - e->starting_lba + 1;
return GPT_SUCCESS;
VBDEBUG(("GptNextKernelEntry likes partition %d\n", new_kernel + 1));
e = entries + new_kernel;
*start_sector = e->starting_lba;
*size = e->ending_lba - e->starting_lba + 1;
return GPT_SUCCESS;
}
int GptUpdateKernelEntry(GptData *gpt, uint32_t update_type)
{
GptHeader *header = (GptHeader *)gpt->primary_header;
GptEntry *entries = (GptEntry *)gpt->primary_entries;
GptEntry *e = entries + gpt->current_kernel;
uint16_t previous_attr = e->attrs.fields.gpt_att;
int GptUpdateKernelEntry(GptData* gpt, uint32_t update_type) {
GptHeader* header = (GptHeader*)gpt->primary_header;
GptEntry* entries = (GptEntry*)gpt->primary_entries;
GptEntry* e = entries + gpt->current_kernel;
uint16_t previous_attr = e->attrs.fields.gpt_att;
if (gpt->current_kernel == CGPT_KERNEL_ENTRY_NOT_FOUND)
return GPT_ERROR_INVALID_UPDATE_TYPE;
if (!IsKernelEntry(e))
return GPT_ERROR_INVALID_UPDATE_TYPE;
if (gpt->current_kernel == CGPT_KERNEL_ENTRY_NOT_FOUND)
return GPT_ERROR_INVALID_UPDATE_TYPE;
if (!IsKernelEntry(e))
return GPT_ERROR_INVALID_UPDATE_TYPE;
switch (update_type) {
case GPT_UPDATE_ENTRY_TRY: {
/* Used up a try */
int tries;
if (GetEntrySuccessful(e)) {
/*
* Successfully booted this partition, so tries field
* is ignored.
*/
return GPT_SUCCESS;
}
tries = GetEntryTries(e);
if (tries > 1) {
/* Still have tries left */
SetEntryTries(e, tries - 1);
break;
}
/* Out of tries, so drop through and mark partition bad. */
}
case GPT_UPDATE_ENTRY_BAD: {
/* Giving up on this partition entirely. */
if (!GetEntrySuccessful(e)) {
/*
* Only clear tries and priority if the successful bit
* is not set.
*/
e->attrs.fields.gpt_att = previous_attr &
~(CGPT_ATTRIBUTE_TRIES_MASK |
CGPT_ATTRIBUTE_PRIORITY_MASK);
}
break;
}
default:
return GPT_ERROR_INVALID_UPDATE_TYPE;
}
switch (update_type) {
case GPT_UPDATE_ENTRY_TRY: {
/* Used up a try */
int tries;
if (GetEntrySuccessful(e))
return GPT_SUCCESS; /* Successfully booted this partition, so
* tries field is ignored. */
tries = GetEntryTries(e);
if (tries > 1) {
/* Still have tries left */
SetEntryTries(e, tries - 1);
break;
}
/* Out of tries, so drop through and mark partition bad. */
}
case GPT_UPDATE_ENTRY_BAD: {
/* Giving up on this partition entirely. */
if (!GetEntrySuccessful(e)) {
/* Only clear tries and priority if the successful bit is not set. */
e->attrs.fields.gpt_att = previous_attr & ~(
CGPT_ATTRIBUTE_TRIES_MASK |
CGPT_ATTRIBUTE_PRIORITY_MASK);
}
break;
}
default:
return GPT_ERROR_INVALID_UPDATE_TYPE;
}
/* If no change to attributes, we're done */
if (e->attrs.fields.gpt_att == previous_attr)
return GPT_SUCCESS;
/* If no change to attributes, we're done */
if (e->attrs.fields.gpt_att == previous_attr)
return GPT_SUCCESS;
/* Update the CRCs */
header->entries_crc32 = Crc32((const uint8_t *)entries,
header->size_of_entry *
header->number_of_entries);
header->header_crc32 = HeaderCrc(header);
gpt->modified |= GPT_MODIFIED_HEADER1 | GPT_MODIFIED_ENTRIES1;
/* Update the CRCs */
header->entries_crc32 = Crc32((const uint8_t *)entries,
header->size_of_entry *
header->number_of_entries);
header->header_crc32 = HeaderCrc(header);
gpt->modified |= GPT_MODIFIED_HEADER1 | GPT_MODIFIED_ENTRIES1;
/*
* Use the repair function to update the other copy of the GPT. This
* is a tad inefficient, but is much faster than the disk I/O to update
* the GPT on disk so it doesn't matter.
*/
gpt->valid_headers = MASK_PRIMARY;
gpt->valid_entries = MASK_PRIMARY;
GptRepair(gpt);
/* Use the repair function to update the other copy of the GPT.
* This is a tad inefficient, but is much faster than the disk I/O
* to update the GPT on disk so it doesn't matter. */
gpt->valid_headers = MASK_PRIMARY;
gpt->valid_entries = MASK_PRIMARY;
GptRepair(gpt);
return GPT_SUCCESS;
return GPT_SUCCESS;
}

View File

@@ -1,4 +1,4 @@
/* Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
/* 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.
*/
@@ -10,396 +10,410 @@
#include "utility.h"
int CheckParameters(GptData *gpt) {
/* Currently, we only support 512-byte sector. In the future, we may support
* larger sector. */
if (gpt->sector_bytes != 512)
return GPT_ERROR_INVALID_SECTOR_SIZE;
/* The sector number of a drive should be reasonable. If the given value is
* too small to contain basic GPT structure (PMBR + Headers + Entries),
* the value is wrong. */
if (gpt->drive_sectors < (1 + 2 * (1 + GPT_ENTRIES_SECTORS)))
return GPT_ERROR_INVALID_SECTOR_NUMBER;
return GPT_SUCCESS;
}
uint32_t HeaderCrc(GptHeader* h) {
uint32_t crc32, original_crc32;
/* Original CRC is calculated with the CRC field 0. */
original_crc32 = h->header_crc32;
h->header_crc32 = 0;
crc32 = Crc32((const uint8_t *)h, h->size);
h->header_crc32 = original_crc32;
return crc32;
}
int CheckHeader(GptHeader *h, int is_secondary, uint64_t drive_sectors) {
if (!h)
return 1;
/* Make sure we're looking at a header of reasonable size before
* attempting to calculate CRC. */
if (Memcmp(h->signature, GPT_HEADER_SIGNATURE, GPT_HEADER_SIGNATURE_SIZE) &&
Memcmp(h->signature, GPT_HEADER_SIGNATURE2, GPT_HEADER_SIGNATURE_SIZE))
return 1;
if (h->revision != GPT_HEADER_REVISION)
return 1;
if (h->size < MIN_SIZE_OF_HEADER || h->size > MAX_SIZE_OF_HEADER)
return 1;
/* Check CRC before looking at remaining fields */
if (HeaderCrc(h) != h->header_crc32)
return 1;
/* Reserved fields must be zero. */
if (h->reserved_zero)
return 1;
/* Could check that padding is zero, but that doesn't matter to us. */
/* If entry size is different than our struct, we won't be able to
* parse it. Technically, any size 2^N where N>=7 is valid. */
if (h->size_of_entry != sizeof(GptEntry))
return 1;
if ((h->number_of_entries < MIN_NUMBER_OF_ENTRIES) ||
(h->number_of_entries > MAX_NUMBER_OF_ENTRIES) ||
(h->number_of_entries * h->size_of_entry != TOTAL_ENTRIES_SIZE))
return 1;
/* Check locations for the header and its entries. The primary
* immediately follows the PMBR, and is followed by its entries.
* The secondary is at the end of the drive, preceded by its
* entries. */
if (is_secondary) {
if (h->my_lba != drive_sectors - 1)
return 1;
if (h->entries_lba != h->my_lba - GPT_ENTRIES_SECTORS)
return 1;
} else {
if (h->my_lba != 1)
return 1;
if (h->entries_lba != h->my_lba + 1)
return 1;
}
/* 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. */
if (h->first_usable_lba < 2 + GPT_ENTRIES_SECTORS)
return 1;
if (h->last_usable_lba >= drive_sectors - 1 - GPT_ENTRIES_SECTORS)
return 1;
if (h->first_usable_lba > h->last_usable_lba)
return 1;
/* Success */
return 0;
}
/* Return non-zero if the entry is unused, 0 if it is used. */
int IsUnusedEntry(const GptEntry* e) {
static Guid zero = {{{0, 0, 0, 0, 0, {0, 0, 0, 0, 0, 0}}}};
return !Memcmp(&zero, (const uint8_t*)(&e->type), sizeof(zero));
}
/* Returns non-zero if the entry is a Chrome OS kernel partition, else 0. */
int IsKernelEntry(const GptEntry* e) {
static Guid chromeos_kernel = GPT_ENT_TYPE_CHROMEOS_KERNEL;
return !Memcmp(&e->type, &chromeos_kernel, sizeof(Guid));
}
int CheckEntries(GptEntry* entries, GptHeader* h) {
GptEntry* entry;
uint32_t crc32;
uint32_t i;
/* Check CRC before examining entries. */
crc32 = Crc32((const uint8_t *)entries,
h->size_of_entry * h->number_of_entries);
if (crc32 != h->entries_crc32)
return GPT_ERROR_CRC_CORRUPTED;
/* Check all entries. */
for (i = 0, entry = entries; i < h->number_of_entries; i++, entry++) {
GptEntry* e2;
uint32_t i2;
if (IsUnusedEntry(entry))
continue;
/* Entry must be in valid region. */
if ((entry->starting_lba < h->first_usable_lba) ||
(entry->ending_lba > h->last_usable_lba) ||
(entry->ending_lba < entry->starting_lba))
return GPT_ERROR_OUT_OF_REGION;
/* Entry must not overlap other entries. */
for (i2 = 0, e2 = entries; i2 < h->number_of_entries; i2++, e2++) {
if (i2 == i || IsUnusedEntry(e2))
continue;
if ((entry->starting_lba >= e2->starting_lba) &&
(entry->starting_lba <= e2->ending_lba))
return GPT_ERROR_START_LBA_OVERLAP;
if ((entry->ending_lba >= e2->starting_lba) &&
(entry->ending_lba <= e2->ending_lba))
return GPT_ERROR_END_LBA_OVERLAP;
/* UniqueGuid field must be unique. */
if (0 == Memcmp(&entry->unique, &e2->unique, sizeof(Guid)))
return GPT_ERROR_DUP_GUID;
}
}
/* Success */
return 0;
}
/* Returns 0 if the GptHeaders are the same for all fields which don't
* differ between the primary and secondary headers - that is, all
* fields other than:
*
* my_lba
* alternate_lba
* entries_lba */
int HeaderFieldsSame(GptHeader *h1, GptHeader *h2) {
if (Memcmp(h1->signature, h2->signature, sizeof(h1->signature)))
return 1;
if (h1->revision != h2->revision)
return 1;
if (h1->size != h2->size)
return 1;
if (h1->reserved_zero != h2->reserved_zero)
return 1;
if (h1->first_usable_lba != h2->first_usable_lba)
return 1;
if (h1->last_usable_lba != h2->last_usable_lba)
return 1;
if (Memcmp(&h1->disk_uuid, &h2->disk_uuid, sizeof(Guid)))
return 1;
if (h1->number_of_entries != h2->number_of_entries)
return 1;
if (h1->size_of_entry != h2->size_of_entry)
return 1;
if (h1->entries_crc32 != h2->entries_crc32)
return 1;
return 0;
}
int GptSanityCheck(GptData *gpt) {
int retval;
GptHeader* header1 = (GptHeader*)(gpt->primary_header);
GptHeader* header2 = (GptHeader*)(gpt->secondary_header);
GptEntry* entries1 = (GptEntry*)(gpt->primary_entries);
GptEntry* entries2 = (GptEntry*)(gpt->secondary_entries);
GptHeader* goodhdr = NULL;
gpt->valid_headers = 0;
gpt->valid_entries = 0;
retval = CheckParameters(gpt);
if (retval != GPT_SUCCESS)
return retval;
/* Check both headers; we need at least one valid header. */
if (0 == CheckHeader(header1, 0, gpt->drive_sectors)) {
gpt->valid_headers |= MASK_PRIMARY;
goodhdr = header1;
}
if (0 == CheckHeader(header2, 1, gpt->drive_sectors)) {
gpt->valid_headers |= MASK_SECONDARY;
if (!goodhdr)
goodhdr = header2;
}
if (!gpt->valid_headers)
return GPT_ERROR_INVALID_HEADERS;
/* Checks if entries are valid.
*
* Note that we use the same header in both checks. This way we'll
* catch the case where (header1,entries1) and (header2,entries2)
* are both valid, but (entries1 != entries2). */
if (0 == CheckEntries(entries1, goodhdr))
gpt->valid_entries |= MASK_PRIMARY;
if (0 == CheckEntries(entries2, goodhdr))
gpt->valid_entries |= MASK_SECONDARY;
/* If both headers are good but neither entries were good, check the
* entries with the secondary header. */
if (MASK_BOTH == gpt->valid_headers && !gpt->valid_entries) {
if (0 == CheckEntries(entries1, header2))
gpt->valid_entries |= MASK_PRIMARY;
if (0 == CheckEntries(entries2, header2))
gpt->valid_entries |= MASK_SECONDARY;
if (gpt->valid_entries) {
/* Sure enough, header2 had a good CRC for one of the entries. Mark
* header1 invalid, so we'll update its entries CRC. */
gpt->valid_headers &= ~MASK_PRIMARY;
goodhdr = header2;
}
}
if (!gpt->valid_entries)
return GPT_ERROR_INVALID_ENTRIES;
/* Now that we've determined which header contains a good CRC for
* the entries, make sure the headers are otherwise identical. */
if (MASK_BOTH == gpt->valid_headers &&
0 != HeaderFieldsSame(header1, header2))
gpt->valid_headers &= ~MASK_SECONDARY;
return GPT_SUCCESS;
}
void GptRepair(GptData *gpt) {
GptHeader* header1 = (GptHeader*)(gpt->primary_header);
GptHeader* header2 = (GptHeader*)(gpt->secondary_header);
GptEntry* entries1 = (GptEntry*)(gpt->primary_entries);
GptEntry* entries2 = (GptEntry*)(gpt->secondary_entries);
int entries_size;
/* Need at least one good header and one good set of entries. */
if (MASK_NONE == gpt->valid_headers || MASK_NONE == gpt->valid_entries)
return;
/* Repair headers if necessary */
if (MASK_PRIMARY == gpt->valid_headers) {
/* Primary is good, secondary is bad */
Memcpy(header2, header1, sizeof(GptHeader));
header2->my_lba = gpt->drive_sectors - 1;
header2->alternate_lba = 1;
header2->entries_lba = header2->my_lba - GPT_ENTRIES_SECTORS;
header2->header_crc32 = HeaderCrc(header2);
gpt->modified |= GPT_MODIFIED_HEADER2;
}
else if (MASK_SECONDARY == gpt->valid_headers) {
/* Secondary is good, primary is bad */
Memcpy(header1, header2, sizeof(GptHeader));
header1->my_lba = 1;
header1->alternate_lba = gpt->drive_sectors - 1;
header1->entries_lba = header1->my_lba + 1;
header1->header_crc32 = HeaderCrc(header1);
gpt->modified |= GPT_MODIFIED_HEADER1;
}
gpt->valid_headers = MASK_BOTH;
/* Repair entries if necessary */
entries_size = header1->size_of_entry * header1->number_of_entries;
if (MASK_PRIMARY == gpt->valid_entries) {
/* Primary is good, secondary is bad */
Memcpy(entries2, entries1, entries_size);
gpt->modified |= GPT_MODIFIED_ENTRIES2;
}
else if (MASK_SECONDARY == gpt->valid_entries) {
/* Secondary is good, primary is bad */
Memcpy(entries1, entries2, entries_size);
gpt->modified |= GPT_MODIFIED_ENTRIES1;
}
gpt->valid_entries = MASK_BOTH;
}
int GetEntrySuccessful(const GptEntry* e) {
return (e->attrs.fields.gpt_att & CGPT_ATTRIBUTE_SUCCESSFUL_MASK) >>
CGPT_ATTRIBUTE_SUCCESSFUL_OFFSET;
}
int GetEntryPriority(const GptEntry* e) {
return (e->attrs.fields.gpt_att & CGPT_ATTRIBUTE_PRIORITY_MASK) >>
CGPT_ATTRIBUTE_PRIORITY_OFFSET;
}
int GetEntryTries(const GptEntry* e) {
return (e->attrs.fields.gpt_att & CGPT_ATTRIBUTE_TRIES_MASK) >>
CGPT_ATTRIBUTE_TRIES_OFFSET;
}
void SetEntrySuccessful(GptEntry* e, int successful) {
if (successful)
e->attrs.fields.gpt_att |= CGPT_ATTRIBUTE_SUCCESSFUL_MASK;
else
e->attrs.fields.gpt_att &= ~CGPT_ATTRIBUTE_SUCCESSFUL_MASK;
}
void SetEntryPriority(GptEntry* e, int priority) {
e->attrs.fields.gpt_att &= ~CGPT_ATTRIBUTE_PRIORITY_MASK;
e->attrs.fields.gpt_att |= (priority << CGPT_ATTRIBUTE_PRIORITY_OFFSET) &
CGPT_ATTRIBUTE_PRIORITY_MASK;
}
void SetEntryTries(GptEntry* e, int tries) {
e->attrs.fields.gpt_att &= ~CGPT_ATTRIBUTE_TRIES_MASK;
e->attrs.fields.gpt_att |= (tries << CGPT_ATTRIBUTE_TRIES_OFFSET) &
CGPT_ATTRIBUTE_TRIES_MASK;
}
void GetCurrentKernelUniqueGuid(GptData *gpt, void *dest) {
GptEntry* entries = (GptEntry*)gpt->primary_entries;
GptEntry* e = entries + gpt->current_kernel;
Memcpy(dest, &e->unique, sizeof(Guid));
}
const char* GptErrorText(int error_code)
int CheckParameters(GptData *gpt)
{
switch(error_code) {
case GPT_SUCCESS:
return "none";
/* Currently, we only support 512-byte sectors. */
if (gpt->sector_bytes != 512)
return GPT_ERROR_INVALID_SECTOR_SIZE;
case GPT_ERROR_NO_VALID_KERNEL:
return "Invalid kernel";
/*
* Sector count of a drive should be reasonable. If the given value is
* too small to contain basic GPT structure (PMBR + Headers + Entries),
* the value is wrong.
*/
if (gpt->drive_sectors < (1 + 2 * (1 + GPT_ENTRIES_SECTORS)))
return GPT_ERROR_INVALID_SECTOR_NUMBER;
case GPT_ERROR_INVALID_HEADERS:
return "Invalid headers";
case GPT_ERROR_INVALID_ENTRIES:
return "Invalid entries";
case GPT_ERROR_INVALID_SECTOR_SIZE:
return "Invalid sector size";
case GPT_ERROR_INVALID_SECTOR_NUMBER:
return "Invalid sector number";
case GPT_ERROR_INVALID_UPDATE_TYPE:
return "Invalid update type";
case GPT_ERROR_CRC_CORRUPTED:
return "Entries' crc corrupted";
case GPT_ERROR_OUT_OF_REGION:
return "Entry outside of valid region";
case GPT_ERROR_START_LBA_OVERLAP:
return "Starting LBA overlaps";
case GPT_ERROR_END_LBA_OVERLAP:
return "Ending LBA overlaps";
case GPT_ERROR_DUP_GUID:
return "Duplicated GUID";
default:
break;
};
return "Unknown";
return GPT_SUCCESS;
}
uint32_t HeaderCrc(GptHeader *h)
{
uint32_t crc32, original_crc32;
/* Original CRC is calculated with the CRC field 0. */
original_crc32 = h->header_crc32;
h->header_crc32 = 0;
crc32 = Crc32((const uint8_t *)h, h->size);
h->header_crc32 = original_crc32;
return crc32;
}
int CheckHeader(GptHeader *h, int is_secondary, uint64_t drive_sectors)
{
if (!h)
return 1;
/*
* Make sure we're looking at a header of reasonable size before
* attempting to calculate CRC.
*/
if (Memcmp(h->signature, GPT_HEADER_SIGNATURE,
GPT_HEADER_SIGNATURE_SIZE) &&
Memcmp(h->signature, GPT_HEADER_SIGNATURE2,
GPT_HEADER_SIGNATURE_SIZE))
return 1;
if (h->revision != GPT_HEADER_REVISION)
return 1;
if (h->size < MIN_SIZE_OF_HEADER || h->size > MAX_SIZE_OF_HEADER)
return 1;
/* Check CRC before looking at remaining fields */
if (HeaderCrc(h) != h->header_crc32)
return 1;
/* Reserved fields must be zero. */
if (h->reserved_zero)
return 1;
/* Could check that padding is zero, but that doesn't matter to us. */
/*
* If entry size is different than our struct, we won't be able to
* parse it. Technically, any size 2^N where N>=7 is valid.
*/
if (h->size_of_entry != sizeof(GptEntry))
return 1;
if ((h->number_of_entries < MIN_NUMBER_OF_ENTRIES) ||
(h->number_of_entries > MAX_NUMBER_OF_ENTRIES) ||
(h->number_of_entries * h->size_of_entry != TOTAL_ENTRIES_SIZE))
return 1;
/*
* Check locations for the header and its entries. The primary
* immediately follows the PMBR, and is followed by its entries. The
* secondary is at the end of the drive, preceded by its entries.
*/
if (is_secondary) {
if (h->my_lba != drive_sectors - 1)
return 1;
if (h->entries_lba != h->my_lba - GPT_ENTRIES_SECTORS)
return 1;
} else {
if (h->my_lba != 1)
return 1;
if (h->entries_lba != h->my_lba + 1)
return 1;
}
/*
* 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.
*/
if (h->first_usable_lba < 2 + GPT_ENTRIES_SECTORS)
return 1;
if (h->last_usable_lba >= drive_sectors - 1 - GPT_ENTRIES_SECTORS)
return 1;
if (h->first_usable_lba > h->last_usable_lba)
return 1;
/* Success */
return 0;
}
int IsUnusedEntry(const GptEntry *e)
{
static Guid zero = {{{0, 0, 0, 0, 0, {0, 0, 0, 0, 0, 0}}}};
return !Memcmp(&zero, (const uint8_t*)(&e->type), sizeof(zero));
}
int IsKernelEntry(const GptEntry *e)
{
static Guid chromeos_kernel = GPT_ENT_TYPE_CHROMEOS_KERNEL;
return !Memcmp(&e->type, &chromeos_kernel, sizeof(Guid));
}
int CheckEntries(GptEntry *entries, GptHeader *h)
{
GptEntry *entry;
uint32_t crc32;
uint32_t i;
/* Check CRC before examining entries. */
crc32 = Crc32((const uint8_t *)entries,
h->size_of_entry * h->number_of_entries);
if (crc32 != h->entries_crc32)
return GPT_ERROR_CRC_CORRUPTED;
/* Check all entries. */
for (i = 0, entry = entries; i < h->number_of_entries; i++, entry++) {
GptEntry *e2;
uint32_t i2;
if (IsUnusedEntry(entry))
continue;
/* Entry must be in valid region. */
if ((entry->starting_lba < h->first_usable_lba) ||
(entry->ending_lba > h->last_usable_lba) ||
(entry->ending_lba < entry->starting_lba))
return GPT_ERROR_OUT_OF_REGION;
/* Entry must not overlap other entries. */
for (i2 = 0, e2 = entries; i2 < h->number_of_entries;
i2++, e2++) {
if (i2 == i || IsUnusedEntry(e2))
continue;
if ((entry->starting_lba >= e2->starting_lba) &&
(entry->starting_lba <= e2->ending_lba))
return GPT_ERROR_START_LBA_OVERLAP;
if ((entry->ending_lba >= e2->starting_lba) &&
(entry->ending_lba <= e2->ending_lba))
return GPT_ERROR_END_LBA_OVERLAP;
/* UniqueGuid field must be unique. */
if (0 == Memcmp(&entry->unique, &e2->unique,
sizeof(Guid)))
return GPT_ERROR_DUP_GUID;
}
}
/* Success */
return 0;
}
int HeaderFieldsSame(GptHeader *h1, GptHeader *h2)
{
if (Memcmp(h1->signature, h2->signature, sizeof(h1->signature)))
return 1;
if (h1->revision != h2->revision)
return 1;
if (h1->size != h2->size)
return 1;
if (h1->reserved_zero != h2->reserved_zero)
return 1;
if (h1->first_usable_lba != h2->first_usable_lba)
return 1;
if (h1->last_usable_lba != h2->last_usable_lba)
return 1;
if (Memcmp(&h1->disk_uuid, &h2->disk_uuid, sizeof(Guid)))
return 1;
if (h1->number_of_entries != h2->number_of_entries)
return 1;
if (h1->size_of_entry != h2->size_of_entry)
return 1;
if (h1->entries_crc32 != h2->entries_crc32)
return 1;
return 0;
}
int GptSanityCheck(GptData *gpt)
{
int retval;
GptHeader *header1 = (GptHeader *)(gpt->primary_header);
GptHeader *header2 = (GptHeader *)(gpt->secondary_header);
GptEntry *entries1 = (GptEntry *)(gpt->primary_entries);
GptEntry *entries2 = (GptEntry *)(gpt->secondary_entries);
GptHeader *goodhdr = NULL;
gpt->valid_headers = 0;
gpt->valid_entries = 0;
retval = CheckParameters(gpt);
if (retval != GPT_SUCCESS)
return retval;
/* Check both headers; we need at least one valid header. */
if (0 == CheckHeader(header1, 0, gpt->drive_sectors)) {
gpt->valid_headers |= MASK_PRIMARY;
goodhdr = header1;
}
if (0 == CheckHeader(header2, 1, gpt->drive_sectors)) {
gpt->valid_headers |= MASK_SECONDARY;
if (!goodhdr)
goodhdr = header2;
}
if (!gpt->valid_headers)
return GPT_ERROR_INVALID_HEADERS;
/*
* Check if entries are valid.
*
* Note that we use the same header in both checks. This way we'll
* catch the case where (header1,entries1) and (header2,entries2) are
* both valid, but (entries1 != entries2).
*/
if (0 == CheckEntries(entries1, goodhdr))
gpt->valid_entries |= MASK_PRIMARY;
if (0 == CheckEntries(entries2, goodhdr))
gpt->valid_entries |= MASK_SECONDARY;
/*
* If both headers are good but neither entries were good, check the
* entries with the secondary header.
*/
if (MASK_BOTH == gpt->valid_headers && !gpt->valid_entries) {
if (0 == CheckEntries(entries1, header2))
gpt->valid_entries |= MASK_PRIMARY;
if (0 == CheckEntries(entries2, header2))
gpt->valid_entries |= MASK_SECONDARY;
if (gpt->valid_entries) {
/*
* Sure enough, header2 had a good CRC for one of the
* entries. Mark header1 invalid, so we'll update its
* entries CRC.
*/
gpt->valid_headers &= ~MASK_PRIMARY;
goodhdr = header2;
}
}
if (!gpt->valid_entries)
return GPT_ERROR_INVALID_ENTRIES;
/*
* Now that we've determined which header contains a good CRC for
* the entries, make sure the headers are otherwise identical.
*/
if (MASK_BOTH == gpt->valid_headers &&
0 != HeaderFieldsSame(header1, header2))
gpt->valid_headers &= ~MASK_SECONDARY;
return GPT_SUCCESS;
}
void GptRepair(GptData *gpt)
{
GptHeader *header1 = (GptHeader *)(gpt->primary_header);
GptHeader *header2 = (GptHeader *)(gpt->secondary_header);
GptEntry *entries1 = (GptEntry *)(gpt->primary_entries);
GptEntry *entries2 = (GptEntry *)(gpt->secondary_entries);
int entries_size;
/* Need at least one good header and one good set of entries. */
if (MASK_NONE == gpt->valid_headers || MASK_NONE == gpt->valid_entries)
return;
/* Repair headers if necessary */
if (MASK_PRIMARY == gpt->valid_headers) {
/* Primary is good, secondary is bad */
Memcpy(header2, header1, sizeof(GptHeader));
header2->my_lba = gpt->drive_sectors - 1;
header2->alternate_lba = 1;
header2->entries_lba = header2->my_lba - GPT_ENTRIES_SECTORS;
header2->header_crc32 = HeaderCrc(header2);
gpt->modified |= GPT_MODIFIED_HEADER2;
}
else if (MASK_SECONDARY == gpt->valid_headers) {
/* Secondary is good, primary is bad */
Memcpy(header1, header2, sizeof(GptHeader));
header1->my_lba = 1;
header1->alternate_lba = gpt->drive_sectors - 1;
header1->entries_lba = header1->my_lba + 1;
header1->header_crc32 = HeaderCrc(header1);
gpt->modified |= GPT_MODIFIED_HEADER1;
}
gpt->valid_headers = MASK_BOTH;
/* Repair entries if necessary */
entries_size = header1->size_of_entry * header1->number_of_entries;
if (MASK_PRIMARY == gpt->valid_entries) {
/* Primary is good, secondary is bad */
Memcpy(entries2, entries1, entries_size);
gpt->modified |= GPT_MODIFIED_ENTRIES2;
}
else if (MASK_SECONDARY == gpt->valid_entries) {
/* Secondary is good, primary is bad */
Memcpy(entries1, entries2, entries_size);
gpt->modified |= GPT_MODIFIED_ENTRIES1;
}
gpt->valid_entries = MASK_BOTH;
}
int GetEntrySuccessful(const GptEntry *e)
{
return (e->attrs.fields.gpt_att & CGPT_ATTRIBUTE_SUCCESSFUL_MASK) >>
CGPT_ATTRIBUTE_SUCCESSFUL_OFFSET;
}
int GetEntryPriority(const GptEntry *e)
{
return (e->attrs.fields.gpt_att & CGPT_ATTRIBUTE_PRIORITY_MASK) >>
CGPT_ATTRIBUTE_PRIORITY_OFFSET;
}
int GetEntryTries(const GptEntry *e)
{
return (e->attrs.fields.gpt_att & CGPT_ATTRIBUTE_TRIES_MASK) >>
CGPT_ATTRIBUTE_TRIES_OFFSET;
}
void SetEntrySuccessful(GptEntry *e, int successful)
{
if (successful)
e->attrs.fields.gpt_att |= CGPT_ATTRIBUTE_SUCCESSFUL_MASK;
else
e->attrs.fields.gpt_att &= ~CGPT_ATTRIBUTE_SUCCESSFUL_MASK;
}
void SetEntryPriority(GptEntry *e, int priority)
{
e->attrs.fields.gpt_att &= ~CGPT_ATTRIBUTE_PRIORITY_MASK;
e->attrs.fields.gpt_att |=
(priority << CGPT_ATTRIBUTE_PRIORITY_OFFSET) &
CGPT_ATTRIBUTE_PRIORITY_MASK;
}
void SetEntryTries(GptEntry *e, int tries)
{
e->attrs.fields.gpt_att &= ~CGPT_ATTRIBUTE_TRIES_MASK;
e->attrs.fields.gpt_att |= (tries << CGPT_ATTRIBUTE_TRIES_OFFSET) &
CGPT_ATTRIBUTE_TRIES_MASK;
}
void GetCurrentKernelUniqueGuid(GptData *gpt, void *dest)
{
GptEntry *entries = (GptEntry *)gpt->primary_entries;
GptEntry *e = entries + gpt->current_kernel;
Memcpy(dest, &e->unique, sizeof(Guid));
}
const char *GptErrorText(int error_code)
{
switch(error_code) {
case GPT_SUCCESS:
return "none";
case GPT_ERROR_NO_VALID_KERNEL:
return "Invalid kernel";
case GPT_ERROR_INVALID_HEADERS:
return "Invalid headers";
case GPT_ERROR_INVALID_ENTRIES:
return "Invalid entries";
case GPT_ERROR_INVALID_SECTOR_SIZE:
return "Invalid sector size";
case GPT_ERROR_INVALID_SECTOR_NUMBER:
return "Invalid sector number";
case GPT_ERROR_INVALID_UPDATE_TYPE:
return "Invalid update type";
case GPT_ERROR_CRC_CORRUPTED:
return "Entries' crc corrupted";
case GPT_ERROR_OUT_OF_REGION:
return "Entry outside of valid region";
case GPT_ERROR_START_LBA_OVERLAP:
return "Starting LBA overlaps";
case GPT_ERROR_END_LBA_OVERLAP:
return "Ending LBA overlaps";
case GPT_ERROR_DUP_GUID:
return "Duplicated GUID";
default:
break;
};
return "Unknown";
}

View File

@@ -42,67 +42,68 @@
#include "crc32.h"
static uint32_t crc32_tab[] = {
0x00000000U, 0x77073096U, 0xee0e612cU, 0x990951baU, 0x076dc419U,
0x706af48fU, 0xe963a535U, 0x9e6495a3U, 0x0edb8832U, 0x79dcb8a4U,
0xe0d5e91eU, 0x97d2d988U, 0x09b64c2bU, 0x7eb17cbdU, 0xe7b82d07U,
0x90bf1d91U, 0x1db71064U, 0x6ab020f2U, 0xf3b97148U, 0x84be41deU,
0x1adad47dU, 0x6ddde4ebU, 0xf4d4b551U, 0x83d385c7U, 0x136c9856U,
0x646ba8c0U, 0xfd62f97aU, 0x8a65c9ecU, 0x14015c4fU, 0x63066cd9U,
0xfa0f3d63U, 0x8d080df5U, 0x3b6e20c8U, 0x4c69105eU, 0xd56041e4U,
0xa2677172U, 0x3c03e4d1U, 0x4b04d447U, 0xd20d85fdU, 0xa50ab56bU,
0x35b5a8faU, 0x42b2986cU, 0xdbbbc9d6U, 0xacbcf940U, 0x32d86ce3U,
0x45df5c75U, 0xdcd60dcfU, 0xabd13d59U, 0x26d930acU, 0x51de003aU,
0xc8d75180U, 0xbfd06116U, 0x21b4f4b5U, 0x56b3c423U, 0xcfba9599U,
0xb8bda50fU, 0x2802b89eU, 0x5f058808U, 0xc60cd9b2U, 0xb10be924U,
0x2f6f7c87U, 0x58684c11U, 0xc1611dabU, 0xb6662d3dU, 0x76dc4190U,
0x01db7106U, 0x98d220bcU, 0xefd5102aU, 0x71b18589U, 0x06b6b51fU,
0x9fbfe4a5U, 0xe8b8d433U, 0x7807c9a2U, 0x0f00f934U, 0x9609a88eU,
0xe10e9818U, 0x7f6a0dbbU, 0x086d3d2dU, 0x91646c97U, 0xe6635c01U,
0x6b6b51f4U, 0x1c6c6162U, 0x856530d8U, 0xf262004eU, 0x6c0695edU,
0x1b01a57bU, 0x8208f4c1U, 0xf50fc457U, 0x65b0d9c6U, 0x12b7e950U,
0x8bbeb8eaU, 0xfcb9887cU, 0x62dd1ddfU, 0x15da2d49U, 0x8cd37cf3U,
0xfbd44c65U, 0x4db26158U, 0x3ab551ceU, 0xa3bc0074U, 0xd4bb30e2U,
0x4adfa541U, 0x3dd895d7U, 0xa4d1c46dU, 0xd3d6f4fbU, 0x4369e96aU,
0x346ed9fcU, 0xad678846U, 0xda60b8d0U, 0x44042d73U, 0x33031de5U,
0xaa0a4c5fU, 0xdd0d7cc9U, 0x5005713cU, 0x270241aaU, 0xbe0b1010U,
0xc90c2086U, 0x5768b525U, 0x206f85b3U, 0xb966d409U, 0xce61e49fU,
0x5edef90eU, 0x29d9c998U, 0xb0d09822U, 0xc7d7a8b4U, 0x59b33d17U,
0x2eb40d81U, 0xb7bd5c3bU, 0xc0ba6cadU, 0xedb88320U, 0x9abfb3b6U,
0x03b6e20cU, 0x74b1d29aU, 0xead54739U, 0x9dd277afU, 0x04db2615U,
0x73dc1683U, 0xe3630b12U, 0x94643b84U, 0x0d6d6a3eU, 0x7a6a5aa8U,
0xe40ecf0bU, 0x9309ff9dU, 0x0a00ae27U, 0x7d079eb1U, 0xf00f9344U,
0x8708a3d2U, 0x1e01f268U, 0x6906c2feU, 0xf762575dU, 0x806567cbU,
0x196c3671U, 0x6e6b06e7U, 0xfed41b76U, 0x89d32be0U, 0x10da7a5aU,
0x67dd4accU, 0xf9b9df6fU, 0x8ebeeff9U, 0x17b7be43U, 0x60b08ed5U,
0xd6d6a3e8U, 0xa1d1937eU, 0x38d8c2c4U, 0x4fdff252U, 0xd1bb67f1U,
0xa6bc5767U, 0x3fb506ddU, 0x48b2364bU, 0xd80d2bdaU, 0xaf0a1b4cU,
0x36034af6U, 0x41047a60U, 0xdf60efc3U, 0xa867df55U, 0x316e8eefU,
0x4669be79U, 0xcb61b38cU, 0xbc66831aU, 0x256fd2a0U, 0x5268e236U,
0xcc0c7795U, 0xbb0b4703U, 0x220216b9U, 0x5505262fU, 0xc5ba3bbeU,
0xb2bd0b28U, 0x2bb45a92U, 0x5cb36a04U, 0xc2d7ffa7U, 0xb5d0cf31U,
0x2cd99e8bU, 0x5bdeae1dU, 0x9b64c2b0U, 0xec63f226U, 0x756aa39cU,
0x026d930aU, 0x9c0906a9U, 0xeb0e363fU, 0x72076785U, 0x05005713U,
0x95bf4a82U, 0xe2b87a14U, 0x7bb12baeU, 0x0cb61b38U, 0x92d28e9bU,
0xe5d5be0dU, 0x7cdcefb7U, 0x0bdbdf21U, 0x86d3d2d4U, 0xf1d4e242U,
0x68ddb3f8U, 0x1fda836eU, 0x81be16cdU, 0xf6b9265bU, 0x6fb077e1U,
0x18b74777U, 0x88085ae6U, 0xff0f6a70U, 0x66063bcaU, 0x11010b5cU,
0x8f659effU, 0xf862ae69U, 0x616bffd3U, 0x166ccf45U, 0xa00ae278U,
0xd70dd2eeU, 0x4e048354U, 0x3903b3c2U, 0xa7672661U, 0xd06016f7U,
0x4969474dU, 0x3e6e77dbU, 0xaed16a4aU, 0xd9d65adcU, 0x40df0b66U,
0x37d83bf0U, 0xa9bcae53U, 0xdebb9ec5U, 0x47b2cf7fU, 0x30b5ffe9U,
0xbdbdf21cU, 0xcabac28aU, 0x53b39330U, 0x24b4a3a6U, 0xbad03605U,
0xcdd70693U, 0x54de5729U, 0x23d967bfU, 0xb3667a2eU, 0xc4614ab8U,
0x5d681b02U, 0x2a6f2b94U, 0xb40bbe37U, 0xc30c8ea1U, 0x5a05df1bU,
0x2d02ef8dU
0x00000000U, 0x77073096U, 0xee0e612cU, 0x990951baU, 0x076dc419U,
0x706af48fU, 0xe963a535U, 0x9e6495a3U, 0x0edb8832U, 0x79dcb8a4U,
0xe0d5e91eU, 0x97d2d988U, 0x09b64c2bU, 0x7eb17cbdU, 0xe7b82d07U,
0x90bf1d91U, 0x1db71064U, 0x6ab020f2U, 0xf3b97148U, 0x84be41deU,
0x1adad47dU, 0x6ddde4ebU, 0xf4d4b551U, 0x83d385c7U, 0x136c9856U,
0x646ba8c0U, 0xfd62f97aU, 0x8a65c9ecU, 0x14015c4fU, 0x63066cd9U,
0xfa0f3d63U, 0x8d080df5U, 0x3b6e20c8U, 0x4c69105eU, 0xd56041e4U,
0xa2677172U, 0x3c03e4d1U, 0x4b04d447U, 0xd20d85fdU, 0xa50ab56bU,
0x35b5a8faU, 0x42b2986cU, 0xdbbbc9d6U, 0xacbcf940U, 0x32d86ce3U,
0x45df5c75U, 0xdcd60dcfU, 0xabd13d59U, 0x26d930acU, 0x51de003aU,
0xc8d75180U, 0xbfd06116U, 0x21b4f4b5U, 0x56b3c423U, 0xcfba9599U,
0xb8bda50fU, 0x2802b89eU, 0x5f058808U, 0xc60cd9b2U, 0xb10be924U,
0x2f6f7c87U, 0x58684c11U, 0xc1611dabU, 0xb6662d3dU, 0x76dc4190U,
0x01db7106U, 0x98d220bcU, 0xefd5102aU, 0x71b18589U, 0x06b6b51fU,
0x9fbfe4a5U, 0xe8b8d433U, 0x7807c9a2U, 0x0f00f934U, 0x9609a88eU,
0xe10e9818U, 0x7f6a0dbbU, 0x086d3d2dU, 0x91646c97U, 0xe6635c01U,
0x6b6b51f4U, 0x1c6c6162U, 0x856530d8U, 0xf262004eU, 0x6c0695edU,
0x1b01a57bU, 0x8208f4c1U, 0xf50fc457U, 0x65b0d9c6U, 0x12b7e950U,
0x8bbeb8eaU, 0xfcb9887cU, 0x62dd1ddfU, 0x15da2d49U, 0x8cd37cf3U,
0xfbd44c65U, 0x4db26158U, 0x3ab551ceU, 0xa3bc0074U, 0xd4bb30e2U,
0x4adfa541U, 0x3dd895d7U, 0xa4d1c46dU, 0xd3d6f4fbU, 0x4369e96aU,
0x346ed9fcU, 0xad678846U, 0xda60b8d0U, 0x44042d73U, 0x33031de5U,
0xaa0a4c5fU, 0xdd0d7cc9U, 0x5005713cU, 0x270241aaU, 0xbe0b1010U,
0xc90c2086U, 0x5768b525U, 0x206f85b3U, 0xb966d409U, 0xce61e49fU,
0x5edef90eU, 0x29d9c998U, 0xb0d09822U, 0xc7d7a8b4U, 0x59b33d17U,
0x2eb40d81U, 0xb7bd5c3bU, 0xc0ba6cadU, 0xedb88320U, 0x9abfb3b6U,
0x03b6e20cU, 0x74b1d29aU, 0xead54739U, 0x9dd277afU, 0x04db2615U,
0x73dc1683U, 0xe3630b12U, 0x94643b84U, 0x0d6d6a3eU, 0x7a6a5aa8U,
0xe40ecf0bU, 0x9309ff9dU, 0x0a00ae27U, 0x7d079eb1U, 0xf00f9344U,
0x8708a3d2U, 0x1e01f268U, 0x6906c2feU, 0xf762575dU, 0x806567cbU,
0x196c3671U, 0x6e6b06e7U, 0xfed41b76U, 0x89d32be0U, 0x10da7a5aU,
0x67dd4accU, 0xf9b9df6fU, 0x8ebeeff9U, 0x17b7be43U, 0x60b08ed5U,
0xd6d6a3e8U, 0xa1d1937eU, 0x38d8c2c4U, 0x4fdff252U, 0xd1bb67f1U,
0xa6bc5767U, 0x3fb506ddU, 0x48b2364bU, 0xd80d2bdaU, 0xaf0a1b4cU,
0x36034af6U, 0x41047a60U, 0xdf60efc3U, 0xa867df55U, 0x316e8eefU,
0x4669be79U, 0xcb61b38cU, 0xbc66831aU, 0x256fd2a0U, 0x5268e236U,
0xcc0c7795U, 0xbb0b4703U, 0x220216b9U, 0x5505262fU, 0xc5ba3bbeU,
0xb2bd0b28U, 0x2bb45a92U, 0x5cb36a04U, 0xc2d7ffa7U, 0xb5d0cf31U,
0x2cd99e8bU, 0x5bdeae1dU, 0x9b64c2b0U, 0xec63f226U, 0x756aa39cU,
0x026d930aU, 0x9c0906a9U, 0xeb0e363fU, 0x72076785U, 0x05005713U,
0x95bf4a82U, 0xe2b87a14U, 0x7bb12baeU, 0x0cb61b38U, 0x92d28e9bU,
0xe5d5be0dU, 0x7cdcefb7U, 0x0bdbdf21U, 0x86d3d2d4U, 0xf1d4e242U,
0x68ddb3f8U, 0x1fda836eU, 0x81be16cdU, 0xf6b9265bU, 0x6fb077e1U,
0x18b74777U, 0x88085ae6U, 0xff0f6a70U, 0x66063bcaU, 0x11010b5cU,
0x8f659effU, 0xf862ae69U, 0x616bffd3U, 0x166ccf45U, 0xa00ae278U,
0xd70dd2eeU, 0x4e048354U, 0x3903b3c2U, 0xa7672661U, 0xd06016f7U,
0x4969474dU, 0x3e6e77dbU, 0xaed16a4aU, 0xd9d65adcU, 0x40df0b66U,
0x37d83bf0U, 0xa9bcae53U, 0xdebb9ec5U, 0x47b2cf7fU, 0x30b5ffe9U,
0xbdbdf21cU, 0xcabac28aU, 0x53b39330U, 0x24b4a3a6U, 0xbad03605U,
0xcdd70693U, 0x54de5729U, 0x23d967bfU, 0xb3667a2eU, 0xc4614ab8U,
0x5d681b02U, 0x2a6f2b94U, 0xb40bbe37U, 0xc30c8ea1U, 0x5a05df1bU,
0x2d02ef8dU
};
/* Returns a 32-bit CRC of the contents of the buffer. */
uint32_t Crc32(const void *buffer, uint32_t len) {
uint8_t *byte = (uint8_t*)buffer;
uint32_t i;
uint32_t value = ~0U;
for (i = 0; i < len; ++i)
value = crc32_tab[(value ^ byte[i]) & 0xff] ^ (value >> 8);
return value ^ ~0U;
uint32_t Crc32(const void *buffer, uint32_t len)
{
uint8_t *byte = (uint8_t *)buffer;
uint32_t i;
uint32_t value = ~0U;
for (i = 0; i < len; ++i)
value = crc32_tab[(value ^ byte[i]) & 0xff] ^ (value >> 8);
return value ^ ~0U;
}

View File

@@ -9,20 +9,20 @@
#include "sysincludes.h"
enum {
GPT_SUCCESS = 0,
GPT_ERROR_NO_VALID_KERNEL,
GPT_ERROR_INVALID_HEADERS,
GPT_ERROR_INVALID_ENTRIES,
GPT_ERROR_INVALID_SECTOR_SIZE,
GPT_ERROR_INVALID_SECTOR_NUMBER,
GPT_ERROR_INVALID_UPDATE_TYPE,
GPT_ERROR_CRC_CORRUPTED,
GPT_ERROR_OUT_OF_REGION,
GPT_ERROR_START_LBA_OVERLAP,
GPT_ERROR_END_LBA_OVERLAP,
GPT_ERROR_DUP_GUID,
/* Number of errors */
GPT_ERROR_COUNT
GPT_SUCCESS = 0,
GPT_ERROR_NO_VALID_KERNEL,
GPT_ERROR_INVALID_HEADERS,
GPT_ERROR_INVALID_ENTRIES,
GPT_ERROR_INVALID_SECTOR_SIZE,
GPT_ERROR_INVALID_SECTOR_NUMBER,
GPT_ERROR_INVALID_UPDATE_TYPE,
GPT_ERROR_CRC_CORRUPTED,
GPT_ERROR_OUT_OF_REGION,
GPT_ERROR_START_LBA_OVERLAP,
GPT_ERROR_END_LBA_OVERLAP,
GPT_ERROR_DUP_GUID,
/* Number of errors */
GPT_ERROR_COUNT
};
/* Bit masks for GptData.modified field. */
@@ -31,56 +31,64 @@ enum {
#define GPT_MODIFIED_ENTRIES1 0x04
#define GPT_MODIFIED_ENTRIES2 0x08
#define TOTAL_ENTRIES_SIZE 16384 /* Size of GptData.primary_entries
* and secondary_entries: 128
* bytes/entry * 128 entries. */
/*
* Size of GptData.primary_entries and secondary_entries: 128 bytes/entry * 128
* entries.
*/
#define TOTAL_ENTRIES_SIZE 16384
/* 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 */
/*
* 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. */
GPT_UPDATE_ENTRY_BAD = 2,
/* The currently selected kernel partition failed validation. Mark entry as
* invalid. */
/*
* System will be trying to boot the currently selected kernel
* partition. Update its try count if necessary.
*/
GPT_UPDATE_ENTRY_TRY = 1,
/*
* The currently selected kernel partition failed validation. Mark
* entry as invalid.
*/
GPT_UPDATE_ENTRY_BAD = 2,
};
typedef struct {
/* Fill in the following fields before calling GptInit() */
uint8_t *primary_header; /* GPT primary header, from sector 1 of disk
* (size: 512 bytes) */
uint8_t *secondary_header; /* GPT secondary header, from last sector of
* disk (size: 512 bytes) */
uint8_t *primary_entries; /* primary GPT table, follows primary header
* (size: 16 KB) */
uint8_t *secondary_entries; /* secondary GPT table, precedes secondary
* header (size: 16 KB) */
uint32_t sector_bytes; /* Size of a LBA sector, in bytes */
uint64_t drive_sectors; /* Size of drive in LBA sectors, in sectors */
/* Fill in the following fields before calling GptInit() */
/* GPT primary header, from sector 1 of disk (size: 512 bytes) */
uint8_t *primary_header;
/* GPT secondary header, from last sector of disk (size: 512 bytes) */
uint8_t *secondary_header;
/* Primary GPT table, follows primary header (size: 16 KB) */
uint8_t *primary_entries;
/* Secondary GPT table, precedes secondary header (size: 16 KB) */
uint8_t *secondary_entries;
/* Size of a LBA sector, in bytes */
uint32_t sector_bytes;
/* Size of drive in LBA sectors, in sectors */
uint64_t drive_sectors;
/* Outputs */
uint8_t modified; /* Which inputs have been modified?
* 0x01 = header1
* 0x02 = header2
* 0x04 = table1
* 0x08 = table2 */
int current_kernel; /* the current chromeos kernel index in partition table.
* -1 means not found on drive. Note that GPT partition
* numbers are traditionally 1-based, but we're using
* a zero-based index here.
*/
/* Outputs */
/* Which inputs have been modified? GPT_MODIFIED_* */
uint8_t modified;
/*
* The current chromeos kernel index in partition table. -1 means not
* found on drive. Note that GPT partition numbers are traditionally
* 1-based, but we're using a zero-based index here.
*/
int current_kernel;
/* Internal variables */
uint32_t valid_headers, valid_entries;
int current_priority;
/* Internal variables */
uint32_t valid_headers, valid_entries;
int current_priority;
} GptData;
int GptInit(GptData* gpt);
/* Initializes the GPT data structure's internal state. The following fields
* must be filled before calling this function:
/**
* Initializes the GPT data structure's internal state.
*
* The following fields must be filled before calling this function:
*
* primary_header
* secondary_header
@@ -100,19 +108,23 @@ int GptInit(GptData* gpt);
* GPT_ERROR_INVALID_SECTOR_SIZE, size of a sector is not supported,
* GPT_ERROR_INVALID_SECTOR_NUMBER, number of sectors in drive is invalid (too
* small) */
int GptInit(GptData *gpt);
int GptNextKernelEntry(GptData* gpt, uint64_t* start_sector, uint64_t* size);
/* Provides the location of the next kernel partition, in order of decreasing
* priority. On return the start_sector parameter contains the LBA sector
* for the start of the kernel partition, and the size parameter contains the
* size of the kernel partition in LBA sectors. gpt.current_kernel contains
* the partition index of the current chromeos kernel partition.
/**
* Provides the location of the next kernel partition, in order of decreasing
* priority.
*
* On return the start_sector parameter contains the LBA sector for the start
* of the kernel partition, and the size parameter contains the size of the
* kernel partition in LBA sectors. gpt.current_kernel contains the partition
* index of the current chromeos kernel partition.
*
* Returns GPT_SUCCESS if successful, else
* GPT_ERROR_NO_VALID_KERNEL, no avaliable kernel, enters recovery mode */
int GptNextKernelEntry(GptData *gpt, uint64_t *start_sector, uint64_t *size);
int GptUpdateKernelEntry(GptData* gpt, uint32_t update_type);
/* Updates the kernel entry with the specified index, using the specified type
/**
* Updates the kernel entry with the specified index, using the specified type
* of update (GPT_UPDATE_ENTRY_*).
*
* On return the modified field may be set, if the GPT data has been modified
@@ -121,5 +133,6 @@ int GptUpdateKernelEntry(GptData* gpt, uint32_t update_type);
* Returns GPT_SUCCESS if successful, else
* GPT_ERROR_INVALID_UPDATE_TYPE, invalid 'update_type' is given.
*/
int GptUpdateKernelEntry(GptData *gpt, uint32_t update_type);
#endif /* VBOOT_REFERENCE_CGPTLIB_H_ */

View File

@@ -10,13 +10,15 @@
#include "cgptlib.h"
#include "gpt.h"
/* If gpt->current_kernel is this value, means either:
/*
* 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.
/*
* Bit definitions and masks for GPT attributes.
*
* 63-61 -- (reserved)
* 60 -- read-only
@@ -55,80 +57,107 @@
/* Defines GPT sizes */
#define GPT_PMBR_SECTOR 1 /* size (in sectors) of PMBR */
#define GPT_HEADER_SECTOR 1
#define GPT_ENTRIES_SECTORS 32 /* assume sector size if 512 bytes, then
* (TOTAL_ENTRIES_SIZE / 512) = 32 */
/*
* Entries sectors assumes sector size if 512 bytes; then (TOTAL_ENTRIES_SIZE /
* 512) = 32
*/
#define GPT_ENTRIES_SECTORS 32
/* alias name of index in internal array for primary and secondary header and
* entries. */
/*
* Alias name of index in internal array for primary and secondary header and
* entries.
*/
enum {
/* constants for index */
PRIMARY = 0,
SECONDARY = 1,
ANY_VALID = 9999, /* accept any between primary and secondary */
/* constants for index */
PRIMARY = 0,
SECONDARY = 1,
ANY_VALID = 9999, /* accept any between primary and secondary */
/* constants for bit mask */
MASK_NONE = 0,
MASK_PRIMARY = 1,
MASK_SECONDARY = 2,
MASK_BOTH = 3,
/* constants for bit mask */
MASK_NONE = 0,
MASK_PRIMARY = 1,
MASK_SECONDARY = 2,
MASK_BOTH = 3,
};
/* Verify GptData parameters are sane. */
/**
* Verify GptData parameters are sane.
*/
int CheckParameters(GptData* gpt);
/* Check header fields.
/**
* Check header fields.
*
* Returns 0 if header is valid, 1 if invalid. */
int CheckHeader(GptHeader* h, int is_secondary, uint64_t drive_sectors);
* Returns 0 if header is valid, 1 if invalid.
*/
int CheckHeader(GptHeader *h, int is_secondary, uint64_t drive_sectors);
/* Calculate and return the header CRC. */
uint32_t HeaderCrc(GptHeader* h);
/**
* Calculate and return the header CRC.
*/
uint32_t HeaderCrc(GptHeader *h);
/* Check entries.
/**
* Check entries.
*
* Returns 0 if entries are valid, 1 if invalid. */
int CheckEntries(GptEntry* entries, GptHeader* h);
* Returns 0 if entries are valid, 1 if invalid.
*/
int CheckEntries(GptEntry *entries, GptHeader *h);
/* Return 0 if the GptHeaders are the same for all fields which don't
* differ between the primary and secondary headers - that is, all
* fields other than:
/**
* Return 0 if the GptHeaders are the same for all fields which don't differ
* between the primary and secondary headers - that is, all fields other than:
*
* my_lba
* alternate_lba
* entries_lba */
* entries_lba
*/
int HeaderFieldsSame(GptHeader *h1, GptHeader *h2);
/* Check GptData, headers, entries.
/**
* Check GptData, headers, entries.
*
* If successful, sets gpt->valid_headers and gpt->valid_entries and returns
* GPT_SUCCESS.
*
* On error, returns a GPT_ERROR_* return code. */
int GptSanityCheck(GptData* gpt);
* On error, returns a GPT_ERROR_* return code.
*/
int GptSanityCheck(GptData *gpt);
/* Repairs GPT data by copying from one set of valid headers/entries to the
/**
* Repair GPT data by copying from one set of valid headers/entries to the
* other. Assumes GptSanityCheck() has been run to determine which headers
* and/or entries are already valid. */
void GptRepair(GptData* gpt);
* and/or entries are already valid.
*/
void GptRepair(GptData *gpt);
/* Getters and setters for partition attribute fields. */
int GetEntrySuccessful(const GptEntry* e);
int GetEntryPriority(const GptEntry* e);
int GetEntryTries(const GptEntry* e);
void SetEntrySuccessful(GptEntry* e, int successful);
void SetEntryPriority(GptEntry* e, int priority);
void SetEntryTries(GptEntry* e, int tries);
/* Return 1 if the entry is unused, 0 if it is used. */
int IsUnusedEntry(const GptEntry* e);
int GetEntrySuccessful(const GptEntry *e);
int GetEntryPriority(const GptEntry *e);
int GetEntryTries(const GptEntry *e);
void SetEntrySuccessful(GptEntry *e, int successful);
void SetEntryPriority(GptEntry *e, int priority);
void SetEntryTries(GptEntry *e, int tries);
/* Returns 1 if the entry is a Chrome OS kernel partition, else 0. */
int IsKernelEntry(const GptEntry* e);
/**
* Return 1 if the entry is unused, 0 if it is used.
*/
int IsUnusedEntry(const GptEntry *e);
/* Copies the current kernel partition's UniquePartitionGuid to the dest */
/**
* Return 1 if the entry is a Chrome OS kernel partition, else 0.
*/
int IsKernelEntry(const GptEntry *e);
/**
* Copy the current kernel partition's UniquePartitionGuid to the dest.
*/
void GetCurrentKernelUniqueGuid(GptData *gpt, void *dest);
/* Returns a pointer to text describing the passed in error */
const char* GptErrorText(int error_code);
/**
* Return a pointer to text describing the passed in error.
*/
const char *GptErrorText(int error_code);
#endif /* VBOOT_REFERENCE_CGPTLIB_INTERNAL_H_ */

View File

@@ -1,4 +1,4 @@
/* Copyright (c) 2010-2011 The Chromium OS Authors. All rights reserved.
/* 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.
*
@@ -19,97 +19,97 @@ __pragma(pack(push,1)) /* Support packing for MSVC. */
#define GPT_HEADER_SIGNATURE_SIZE sizeof(GPT_HEADER_SIGNATURE)
#define GPT_HEADER_REVISION 0x00010000
/* The first 3 numbers should be stored in network-endian format
* according to the GUID RFC. The UEFI spec appendix A claims they
* should be stored in little-endian format. But they need to be
* _displayed_ in network-endian format, which is also how they're
* documented in the specs.
/*
* The first 3 numbers should be stored in network-endian format according to
* the GUID RFC. The UEFI spec appendix A claims they should be stored in
* little-endian format. But they need to be _displayed_ in network-endian
* format, which is also how they're documented in the specs.
*
* Since what we have here are little-endian constants, they're
* byte-swapped from the normal display order. */
* Since what we have here are little-endian constants, they're byte-swapped
* from the normal display order.
*/
#define GPT_ENT_TYPE_UNUSED \
{{{0x00000000,0x0000,0x0000,0x00,0x00,{0x00,0x00,0x00,0x00,0x00,0x00}}}}
{{{0x00000000,0x0000,0x0000,0x00,0x00,{0x00,0x00,0x00,0x00,0x00,0x00}}}}
#define GPT_ENT_TYPE_EFI \
{{{0xc12a7328,0xf81f,0x11d2,0xba,0x4b,{0x00,0xa0,0xc9,0x3e,0xc9,0x3b}}}}
{{{0xc12a7328,0xf81f,0x11d2,0xba,0x4b,{0x00,0xa0,0xc9,0x3e,0xc9,0x3b}}}}
#define GPT_ENT_TYPE_CHROMEOS_FIRMWARE \
{{{0xcab6e88e,0xabf3,0x4102,0xa0,0x7a,{0xd4,0xbb,0x9b,0xe3,0xc1,0xd3}}}}
{{{0xcab6e88e,0xabf3,0x4102,0xa0,0x7a,{0xd4,0xbb,0x9b,0xe3,0xc1,0xd3}}}}
#define GPT_ENT_TYPE_CHROMEOS_KERNEL \
{{{0xfe3a2a5d,0x4f32,0x41a7,0xb7,0x25,{0xac,0xcc,0x32,0x85,0xa3,0x09}}}}
{{{0xfe3a2a5d,0x4f32,0x41a7,0xb7,0x25,{0xac,0xcc,0x32,0x85,0xa3,0x09}}}}
#define GPT_ENT_TYPE_CHROMEOS_ROOTFS \
{{{0x3cb8e202,0x3b7e,0x47dd,0x8a,0x3c,{0x7f,0xf2,0xa1,0x3c,0xfc,0xec}}}}
{{{0x3cb8e202,0x3b7e,0x47dd,0x8a,0x3c,{0x7f,0xf2,0xa1,0x3c,0xfc,0xec}}}}
#define GPT_ENT_TYPE_CHROMEOS_RESERVED \
{{{0x2e0a753d,0x9e48,0x43b0,0x83,0x37,{0xb1,0x51,0x92,0xcb,0x1b,0x5e}}}}
{{{0x2e0a753d,0x9e48,0x43b0,0x83,0x37,{0xb1,0x51,0x92,0xcb,0x1b,0x5e}}}}
#define GPT_ENT_TYPE_LINUX_DATA \
{{{0xebd0a0a2,0xb9e5,0x4433,0x87,0xc0,{0x68,0xb6,0xb7,0x26,0x99,0xc7}}}}
{{{0xebd0a0a2,0xb9e5,0x4433,0x87,0xc0,{0x68,0xb6,0xb7,0x26,0x99,0xc7}}}}
#define UUID_NODE_LEN 6
#define GUID_SIZE 16
/* GUID definition.
* Defined in appendix A of EFI standard.
*/
/* GUID definition. Defined in appendix A of EFI standard. */
typedef struct {
union {
struct {
uint32_t time_low;
uint16_t time_mid;
uint16_t time_high_and_version;
uint8_t clock_seq_high_and_reserved;
uint8_t clock_seq_low;
uint8_t node[UUID_NODE_LEN];
} Uuid;
uint8_t raw[GUID_SIZE];
} u;
union {
struct {
uint32_t time_low;
uint16_t time_mid;
uint16_t time_high_and_version;
uint8_t clock_seq_high_and_reserved;
uint8_t clock_seq_low;
uint8_t node[UUID_NODE_LEN];
} Uuid;
uint8_t raw[GUID_SIZE];
} u;
} __attribute__((packed)) Guid;
#define GUID_EXPECTED_SIZE GUID_SIZE
/* GPT header defines how many partitions exist on a drive and sectors managed.
* For every drive device, there are 2 headers, primary and secondary.
* Most of fields are duplicated except my_lba and entries_lba.
/*
* GPT header defines how many partitions exist on a drive and sectors managed.
* For every drive device, there are 2 headers, primary and secondary. Most of
* fields are duplicated except my_lba and entries_lba.
*
* You may find more details in chapter 5 of EFI standard.
*/
typedef struct {
char signature[8];
uint32_t revision;
uint32_t size;
uint32_t header_crc32;
uint32_t reserved_zero;
uint64_t my_lba;
uint64_t alternate_lba;
uint64_t first_usable_lba;
uint64_t last_usable_lba;
Guid disk_uuid;
uint64_t entries_lba;
uint32_t number_of_entries;
uint32_t size_of_entry;
uint32_t entries_crc32;
/* Remainder of sector is reserved and should be 0 */
char signature[8];
uint32_t revision;
uint32_t size;
uint32_t header_crc32;
uint32_t reserved_zero;
uint64_t my_lba;
uint64_t alternate_lba;
uint64_t first_usable_lba;
uint64_t last_usable_lba;
Guid disk_uuid;
uint64_t entries_lba;
uint32_t number_of_entries;
uint32_t size_of_entry;
uint32_t entries_crc32;
/* Remainder of sector is reserved and should be 0 */
} __attribute__((packed)) GptHeader;
#define GPTHEADER_EXPECTED_SIZE 92
/* GPT partition entry defines the starting and ending LBAs of a partition.
* It also contains the unique GUID, type, and attribute bits.
/*
* GPT partition entry defines the starting and ending LBAs of a partition. It
* also contains the unique GUID, type, and attribute bits.
*
* You may find more details in chapter 5 of EFI standard.
*/
typedef struct {
Guid type;
Guid unique;
uint64_t starting_lba;
uint64_t ending_lba;
union {
struct {
uint16_t reserved[3];
uint16_t gpt_att;
} __attribute__((packed)) fields;
uint64_t whole;
} attrs;
uint16_t name[36]; /* UTF-16 encoded partition name */
/* Remainder of entry is reserved and should be 0 */
Guid type;
Guid unique;
uint64_t starting_lba;
uint64_t ending_lba;
union {
struct {
uint16_t reserved[3];
uint16_t gpt_att;
} __attribute__((packed)) fields;
uint64_t whole;
} attrs;
uint16_t name[36]; /* UTF-16 encoded partition name */
/* Remainder of entry is reserved and should be 0 */
} __attribute__((packed)) GptEntry;
#define GPTENTRY_EXPECTED_SIZE 128