blob: 3923815eed8f030c1201400bb6b2b8cacfc9dfff [file] [log] [blame]
/* Copyright (c) 2012 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.
*/
#define __STDC_FORMAT_MACROS
#include <string.h>
#include "cgpt.h"
#include "cgptlib_internal.h"
#include "crc32.h"
#include "vboot_host.h"
/* Generate output like:
*
* [AB-CD-EF-01] for group = 1
* [ABCD-EF01] for group = 3 (low byte first)
*
* Needs (size*3-1+3) bytes of space in 'buf' (included the tailing '\0').
*/
#define BUFFER_SIZE(size) (size *3 - 1 + 3)
static short Uint8To2Chars(const uint8_t t) {
int h = t >> 4;
int l = t & 0xf;
h = (h >= 0xA) ? h - 0xA + 'A' : h + '0';
l = (l >= 0xA) ? l - 0xA + 'A' : l + '0';
return (h << 8) + l;
}
static void RawDump(const uint8_t *memory, const int size,
char *buf, int group) {
int i, outlen = 0;
buf[outlen++] = '[';
for (i = 0; i < size; ++i) {
short c2 = Uint8To2Chars(memory[i]);
buf[outlen++] = c2 >> 8;
buf[outlen++] = c2 & 0xff;
if (i != (size - 1) && ((i + 1) % group) == 0)
buf[outlen++] = '-';
}
buf[outlen++] = ']';
buf[outlen++] = '\0';
}
/* Output formatters */
#define TITLE_FMT "%12s%12s%8s %s\n"
#define GPT_FMT "%12"PRId64"%12"PRId64"%8s %s\n"
#define GPT_MORE "%12s%12s%8s ", "", "", ""
#define PARTITION_FMT "%12"PRId64"%12"PRId64"%8d %s\n"
#define PARTITION_MORE "%12s%12s%8s %s%s\n", "", "", ""
static void PrintSignature(const char *indent, const char *sig, size_t n,
int raw) {
size_t i;
printf("%sSig: ", indent);
if (!raw) {
printf("[");
for (i = 0; i < n; ++i)
printf("%c", sig[i]);
printf("]");
} else {
char *buf = malloc(BUFFER_SIZE(n));
RawDump((uint8_t *)sig, n, buf, 1);
printf("%s", buf);
free(buf);
}
printf("\n");
}
static void HeaderDetails(GptHeader *header, GptEntry *entries,
const char *indent, int raw) {
PrintSignature(indent, header->signature, sizeof(header->signature), raw);
printf("%sRev: 0x%08x\n", indent, header->revision);
printf("%sSize: %d (blocks)\n", indent, header->size);
printf("%sHeader CRC: 0x%08x %s\n", indent, header->header_crc32,
(HeaderCrc(header) != header->header_crc32) ? "(INVALID)" : "");
printf("%sMy LBA: %lld\n", indent, (long long)header->my_lba);
printf("%sAlternate LBA: %lld\n", indent, (long long)header->alternate_lba);
printf("%sFirst LBA: %lld\n", indent, (long long)header->first_usable_lba);
printf("%sLast LBA: %lld\n", indent, (long long)header->last_usable_lba);
{ /* For disk guid */
char buf[GUID_STRLEN];
GuidToStr(&header->disk_uuid, buf, GUID_STRLEN);
printf("%sDisk UUID: %s\n", indent, buf);
}
printf("%sEntries LBA: %lld\n", indent, (long long)header->entries_lba);
printf("%sNumber of entries: %d\n", indent, header->number_of_entries);
printf("%sSize of entry: %d\n", indent, header->size_of_entry);
printf("%sEntries CRC: 0x%08x %s\n", indent, header->entries_crc32,
header->entries_crc32 !=
Crc32((const uint8_t *)entries,header->size_of_entry *
header->number_of_entries)
? "INVALID" : ""
);
}
void EntryDetails(GptEntry *entry, uint32_t index, int raw) {
char contents[256]; // scratch buffer for formatting output
uint8_t label[GPT_PARTNAME_LEN];
char type[GUID_STRLEN], unique[GUID_STRLEN];
int clen;
UTF16ToUTF8(entry->name, sizeof(entry->name) / sizeof(entry->name[0]),
label, sizeof(label));
require(snprintf(contents, sizeof(contents),
"Label: \"%s\"", label) < sizeof(contents));
printf(PARTITION_FMT, (uint64_t)entry->starting_lba,
(uint64_t)(entry->ending_lba - entry->starting_lba + 1),
index+1, contents);
if (!raw && CGPT_OK == ResolveType(&entry->type, type)) {
printf(PARTITION_MORE, "Type: ", type);
} else {
GuidToStr(&entry->type, type, GUID_STRLEN);
printf(PARTITION_MORE, "Type: ", type);
}
GuidToStr(&entry->unique, unique, GUID_STRLEN);
printf(PARTITION_MORE, "UUID: ", unique);
clen = 0;
if (!raw) {
if (GuidEqual(&guid_chromeos_kernel, &entry->type)) {
int tries = (entry->attrs.fields.gpt_att &
CGPT_ATTRIBUTE_TRIES_MASK) >>
CGPT_ATTRIBUTE_TRIES_OFFSET;
int successful = (entry->attrs.fields.gpt_att &
CGPT_ATTRIBUTE_SUCCESSFUL_MASK) >>
CGPT_ATTRIBUTE_SUCCESSFUL_OFFSET;
int priority = (entry->attrs.fields.gpt_att &
CGPT_ATTRIBUTE_PRIORITY_MASK) >>
CGPT_ATTRIBUTE_PRIORITY_OFFSET;
clen = snprintf(contents, sizeof(contents),
"priority=%d tries=%d successful=%d ",
priority, tries, successful);
}
if (entry->attrs.fields.required) {
clen += snprintf(contents + clen, sizeof(contents) - clen,
"required=%d ", entry->attrs.fields.required);
require(clen < sizeof(contents));
}
if (entry->attrs.fields.efi_ignore) {
clen += snprintf(contents + clen, sizeof(contents) - clen,
"efi_ignore=%d ", entry->attrs.fields.efi_ignore);
require(clen < sizeof(contents));
}
if (entry->attrs.fields.legacy_boot) {
clen += snprintf(contents + clen, sizeof(contents) - clen,
"legacy_boot=%d ", entry->attrs.fields.legacy_boot);
require(clen < sizeof(contents));
}
} else {
clen = snprintf(contents, sizeof(contents),
"[%x]", entry->attrs.fields.gpt_att);
}
require(clen < sizeof(contents));
if (clen)
printf(PARTITION_MORE, "Attr: ", contents);
}
static void EntriesDetails(struct drive *drive, const int secondary, int raw) {
uint32_t i;
for (i = 0; i < GetNumberOfEntries(drive); ++i) {
GptEntry *entry;
entry = GetEntry(&drive->gpt, secondary, i);
if (GuidIsZero(&entry->type))
continue;
EntryDetails(entry, i, raw);
}
}
static int GptShow(struct drive *drive, CgptShowParams *params) {
int gpt_retval;
if (GPT_SUCCESS != (gpt_retval = GptValidityCheck(&drive->gpt))) {
Error("GptValidityCheck() returned %d: %s\n",
gpt_retval, GptError(gpt_retval));
return CGPT_FAILED;
}
if (params->partition) { // show single partition
if (params->partition > GetNumberOfEntries(drive)) {
Error("invalid partition number: %d\n", params->partition);
return CGPT_FAILED;
}
uint32_t index = params->partition - 1;
GptEntry *entry = GetEntry(&drive->gpt, ANY_VALID, index);
char buf[256]; // scratch buffer for string conversion
if (params->single_item) {
switch(params->single_item) {
case 'b':
printf("%" PRId64 "\n", entry->starting_lba);
break;
case 's': {
uint64_t size = 0;
// If these aren't actually defined, don't show anything
if (entry->ending_lba || entry->starting_lba)
size = entry->ending_lba - entry->starting_lba + 1;
printf("%" PRId64 "\n", size);
break;
}
case 't':
GuidToStr(&entry->type, buf, sizeof(buf));
printf("%s\n", buf);
break;
case 'u':
GuidToStr(&entry->unique, buf, sizeof(buf));
printf("%s\n", buf);
break;
case 'l':
UTF16ToUTF8(entry->name, sizeof(entry->name) / sizeof(entry->name[0]),
(uint8_t *)buf, sizeof(buf));
printf("%s\n", buf);
break;
case 'S':
printf("%d\n", GetSuccessful(drive, ANY_VALID, index));
break;
case 'T':
printf("%d\n", GetTries(drive, ANY_VALID, index));
break;
case 'P':
printf("%d\n", GetPriority(drive, ANY_VALID, index));
break;
case 'R':
printf("%d\n", GetRequired(drive, ANY_VALID, index));
break;
case 'B':
printf("%d\n", GetLegacyBoot(drive, ANY_VALID, index));
break;
case 'A':
printf("%#x\n", entry->attrs.fields.gpt_att);
break;
}
} else {
printf(TITLE_FMT, "start", "size", "part", "contents");
EntryDetails(entry, index, params->numeric);
}
} else if (params->quick) { // show all partitions, quickly
uint32_t i;
GptEntry *entry;
char type[GUID_STRLEN];
for (i = 0; i < GetNumberOfEntries(drive); ++i) {
entry = GetEntry(&drive->gpt, ANY_VALID, i);
if (GuidIsZero(&entry->type))
continue;
if (!params->numeric && CGPT_OK == ResolveType(&entry->type, type)) {
} else {
GuidToStr(&entry->type, type, GUID_STRLEN);
}
printf(PARTITION_FMT, (uint64_t)entry->starting_lba,
(uint64_t)(entry->ending_lba - entry->starting_lba + 1),
i+1, type);
}
} else { // show all partitions
GptEntry *entries;
if (params->debug || params->verbose) {
printf("Drive details:\n");
printf(" Total Size (bytes): %" PRIu64 "\n", drive->size);
printf(" LBA Size (bytes): %d\n", drive->gpt.sector_bytes);
if (drive->gpt.flags & GPT_FLAG_EXTERNAL) {
printf(" Drive (where GPT lives) Size (blocks): %" PRIu64 "\n",
drive->gpt.gpt_drive_sectors);
printf(" Drive (where partitions live) Size (blocks): %" PRIu64 "\n",
drive->gpt.streaming_drive_sectors);
} else {
// We know gpt_drive_sectors == streaming_drive_sectors here.
printf(" Drive Size (blocks): %" PRIu64 "\n",
drive->gpt.gpt_drive_sectors);
}
printf("\n");
}
if (CGPT_OK != ReadPMBR(drive)) {
Error("Unable to read PMBR\n");
return CGPT_FAILED;
}
printf(TITLE_FMT, "start", "size", "part", "contents");
char buf[256]; // buffer for formatted PMBR content
PMBRToStr(&drive->pmbr, buf, sizeof(buf)); // will exit if buf is too small
printf(GPT_FMT, (uint64_t)0, (uint64_t)GPT_PMBR_SECTORS, "", buf);
if (drive->gpt.ignored & MASK_PRIMARY) {
printf(GPT_FMT, (uint64_t)GPT_PMBR_SECTORS,
(uint64_t)GPT_HEADER_SECTORS, "IGNORED", "Pri GPT header");
} else {
if (drive->gpt.valid_headers & MASK_PRIMARY) {
printf(GPT_FMT, (uint64_t)GPT_PMBR_SECTORS,
(uint64_t)GPT_HEADER_SECTORS, "", "Pri GPT header");
} else {
printf(GPT_FMT, (uint64_t)GPT_PMBR_SECTORS,
(uint64_t)GPT_HEADER_SECTORS, "INVALID", "Pri GPT header");
}
if (params->debug ||
((drive->gpt.valid_headers & MASK_PRIMARY) && params->verbose)) {
GptHeader *header;
char indent[64];
require(snprintf(indent, sizeof(indent), GPT_MORE) < sizeof(indent));
header = (GptHeader*)drive->gpt.primary_header;
entries = (GptEntry*)drive->gpt.primary_entries;
HeaderDetails(header, entries, indent, params->numeric);
}
GptHeader* primary_header = (GptHeader*)drive->gpt.primary_header;
printf(GPT_FMT, (uint64_t)primary_header->entries_lba,
(uint64_t)CalculateEntriesSectors(primary_header,
drive->gpt.sector_bytes),
drive->gpt.valid_entries & MASK_PRIMARY ? "" : "INVALID",
"Pri GPT table");
if (params->debug ||
(drive->gpt.valid_entries & MASK_PRIMARY))
EntriesDetails(drive, PRIMARY, params->numeric);
}
/****************************** Secondary *************************/
if (drive->gpt.ignored & MASK_SECONDARY) {
printf(GPT_FMT,
(uint64_t)(drive->gpt.gpt_drive_sectors - GPT_HEADER_SECTORS),
(uint64_t)GPT_HEADER_SECTORS, "IGNORED", "Sec GPT header");
} else {
GptHeader* secondary_header = (GptHeader*)drive->gpt.secondary_header;
printf(GPT_FMT, (uint64_t)secondary_header->entries_lba,
(uint64_t)CalculateEntriesSectors(secondary_header,
drive->gpt.sector_bytes),
drive->gpt.valid_entries & MASK_SECONDARY ? "" : "INVALID",
"Sec GPT table");
/* We show secondary table details if any of following is true.
* 1. in debug mode.
* 2. primary table is being ignored
* 3. only secondary is valid.
* 4. secondary is not identical to primary.
*/
if (params->debug || (drive->gpt.ignored & MASK_PRIMARY) ||
((drive->gpt.valid_entries & MASK_SECONDARY) &&
(!(drive->gpt.valid_entries & MASK_PRIMARY) ||
memcmp(drive->gpt.primary_entries, drive->gpt.secondary_entries,
secondary_header->number_of_entries *
secondary_header->size_of_entry)))) {
EntriesDetails(drive, SECONDARY, params->numeric);
}
if (drive->gpt.valid_headers & MASK_SECONDARY) {
printf(GPT_FMT,
(uint64_t)(drive->gpt.gpt_drive_sectors - GPT_HEADER_SECTORS),
(uint64_t)GPT_HEADER_SECTORS, "", "Sec GPT header");
} else {
printf(GPT_FMT, (uint64_t)GPT_PMBR_SECTORS,
(uint64_t)GPT_HEADER_SECTORS, "INVALID", "Sec GPT header");
}
/* We show secondary header if any of following is true:
* 1. in debug mode.
* 2. primary table is being ignored
* 3. only secondary is valid.
* 4. secondary is not synonymous to primary and not ignored.
*/
if (params->debug || (drive->gpt.ignored & MASK_PRIMARY) ||
((drive->gpt.valid_headers & MASK_SECONDARY) &&
(!(drive->gpt.valid_headers & MASK_PRIMARY) ||
!IsSynonymous((GptHeader*)drive->gpt.primary_header,
(GptHeader*)drive->gpt.secondary_header)) &&
params->verbose)) {
GptHeader *header;
char indent[64];
require(snprintf(indent, sizeof(indent), GPT_MORE) < sizeof(indent));
header = (GptHeader*)drive->gpt.secondary_header;
entries = (GptEntry*)drive->gpt.secondary_entries;
HeaderDetails(header, entries, indent, params->numeric);
}
}
}
CheckValid(drive);
return CGPT_OK;
}
int CgptShow(CgptShowParams *params) {
struct drive drive;
if (params == NULL)
return CGPT_FAILED;
if (CGPT_OK != DriveOpen(params->drive_name, &drive, O_RDONLY,
params->drive_size))
return CGPT_FAILED;
int ret = GptShow(&drive, params);
DriveClose(&drive, 0);
return ret;
}