| /* 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; | |
| } |