1 // Copyright (c) 2012 The Chromium OS Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #define __STDC_FORMAT_MACROS 6 7 #include <string.h> 8 9 #include "cgpt.h" 10 #include "cgptlib_internal.h" 11 #include "crc32.h" 12 #include "vboot_host.h" 13 14 /* Generate output like: 15 * 16 * [AB-CD-EF-01] for group = 1 17 * [ABCD-EF01] for group = 3 (low byte first) 18 * 19 * Needs (size*3-1+3) bytes of space in 'buf' (included the tailing '\0'). 20 */ 21 #define BUFFER_SIZE(size) (size *3 - 1 + 3) 22 static short Uint8To2Chars(const uint8_t t) { 23 int h = t >> 4; 24 int l = t & 0xf; 25 h = (h >= 0xA) ? h - 0xA + 'A' : h + '0'; 26 l = (l >= 0xA) ? l - 0xA + 'A' : l + '0'; 27 return (h << 8) + l; 28 } 29 30 static void RawDump(const uint8_t *memory, const int size, 31 char *buf, int group) { 32 int i, outlen = 0; 33 buf[outlen++] = '['; 34 for (i = 0; i < size; ++i) { 35 short c2 = Uint8To2Chars(memory[i]); 36 buf[outlen++] = c2 >> 8; 37 buf[outlen++] = c2 & 0xff; 38 if (i != (size - 1) && ((i + 1) % group) == 0) 39 buf[outlen++] = '-'; 40 } 41 buf[outlen++] = ']'; 42 buf[outlen++] = '\0'; 43 } 44 45 /* Output formatters */ 46 #define TITLE_FMT "%12s%12s%8s %s\n" 47 #define GPT_FMT "%12d%12d%8s %s\n" 48 #define GPT_MORE "%12s%12s%8s ", "", "", "" 49 #define PARTITION_FMT "%12d%12d%8d %s\n" 50 #define PARTITION_MORE "%12s%12s%8s %s%s\n", "", "", "" 51 52 void PrintSignature(const char *indent, const char *sig, size_t n, int raw) { 53 size_t i; 54 printf("%sSig: ", indent); 55 if (!raw) { 56 printf("["); 57 for (i = 0; i < n; ++i) 58 printf("%c", sig[i]); 59 printf("]"); 60 } else { 61 char *buf = malloc(BUFFER_SIZE(n)); 62 RawDump((uint8_t *)sig, n, buf, 1); 63 printf("%s", buf); 64 free(buf); 65 } 66 printf("\n"); 67 } 68 69 static void HeaderDetails(GptHeader *header, GptEntry *entries, 70 const char *indent, int raw) { 71 PrintSignature(indent, header->signature, sizeof(header->signature), raw); 72 73 printf("%sRev: 0x%08x\n", indent, header->revision); 74 printf("%sSize: %d\n", indent, header->size); 75 printf("%sHeader CRC: 0x%08x %s\n", indent, header->header_crc32, 76 (HeaderCrc(header) != header->header_crc32) ? "(INVALID)" : ""); 77 printf("%sMy LBA: %lld\n", indent, (long long)header->my_lba); 78 printf("%sAlternate LBA: %lld\n", indent, (long long)header->alternate_lba); 79 printf("%sFirst LBA: %lld\n", indent, (long long)header->first_usable_lba); 80 printf("%sLast LBA: %lld\n", indent, (long long)header->last_usable_lba); 81 82 { /* For disk guid */ 83 char buf[GUID_STRLEN]; 84 GuidToStr(&header->disk_uuid, buf, GUID_STRLEN); 85 printf("%sDisk UUID: %s\n", indent, buf); 86 } 87 88 printf("%sEntries LBA: %lld\n", indent, (long long)header->entries_lba); 89 printf("%sNumber of entries: %d\n", indent, header->number_of_entries); 90 printf("%sSize of entry: %d\n", indent, header->size_of_entry); 91 printf("%sEntries CRC: 0x%08x %s\n", indent, header->entries_crc32, 92 header->entries_crc32 != 93 Crc32((const uint8_t *)entries,header->size_of_entry * 94 header->number_of_entries) 95 ? "INVALID" : "" 96 ); 97 } 98 99 void EntryDetails(GptEntry *entry, uint32_t index, int raw) { 100 char contents[256]; // scratch buffer for formatting output 101 uint8_t label[GPT_PARTNAME_LEN]; 102 char type[GUID_STRLEN], unique[GUID_STRLEN]; 103 104 UTF16ToUTF8(entry->name, sizeof(entry->name) / sizeof(entry->name[0]), 105 label, sizeof(label)); 106 require(snprintf(contents, sizeof(contents), 107 "Label: \"%s\"", label) < sizeof(contents)); 108 printf(PARTITION_FMT, (int)entry->starting_lba, 109 (int)(entry->ending_lba - entry->starting_lba + 1), 110 index+1, contents); 111 112 if (!raw && CGPT_OK == ResolveType(&entry->type, type)) { 113 printf(PARTITION_MORE, "Type: ", type); 114 } else { 115 GuidToStr(&entry->type, type, GUID_STRLEN); 116 printf(PARTITION_MORE, "Type: ", type); 117 } 118 GuidToStr(&entry->unique, unique, GUID_STRLEN); 119 printf(PARTITION_MORE, "UUID: ", unique); 120 121 if (!raw) { 122 if (GuidEqual(&guid_chromeos_kernel, &entry->type)) { 123 int tries = (entry->attrs.fields.gpt_att & 124 CGPT_ATTRIBUTE_TRIES_MASK) >> 125 CGPT_ATTRIBUTE_TRIES_OFFSET; 126 int successful = (entry->attrs.fields.gpt_att & 127 CGPT_ATTRIBUTE_SUCCESSFUL_MASK) >> 128 CGPT_ATTRIBUTE_SUCCESSFUL_OFFSET; 129 int priority = (entry->attrs.fields.gpt_att & 130 CGPT_ATTRIBUTE_PRIORITY_MASK) >> 131 CGPT_ATTRIBUTE_PRIORITY_OFFSET; 132 require(snprintf(contents, sizeof(contents), 133 "priority=%d tries=%d successful=%d", 134 priority, tries, successful) < sizeof(contents)); 135 printf(PARTITION_MORE, "Attr: ", contents); 136 } 137 } else { 138 require(snprintf(contents, sizeof(contents), 139 "[%x]", entry->attrs.fields.gpt_att) < sizeof(contents)); 140 printf(PARTITION_MORE, "Attr: ", contents); 141 } 142 } 143 144 void EntriesDetails(struct drive *drive, const int secondary, int raw) { 145 uint32_t i; 146 147 for (i = 0; i < GetNumberOfEntries(drive); ++i) { 148 GptEntry *entry; 149 entry = GetEntry(&drive->gpt, secondary, i); 150 151 if (GuidIsZero(&entry->type)) 152 continue; 153 154 EntryDetails(entry, i, raw); 155 } 156 } 157 158 static int GptShow(struct drive *drive, CgptShowParams *params) { 159 int gpt_retval; 160 if (GPT_SUCCESS != (gpt_retval = GptSanityCheck(&drive->gpt))) { 161 Error("GptSanityCheck() returned %d: %s\n", 162 gpt_retval, GptError(gpt_retval)); 163 return CGPT_FAILED; 164 } 165 166 if (params->partition) { // show single partition 167 168 if (params->partition > GetNumberOfEntries(drive)) { 169 Error("invalid partition number: %d\n", params->partition); 170 return CGPT_FAILED; 171 } 172 173 uint32_t index = params->partition - 1; 174 GptEntry *entry = GetEntry(&drive->gpt, ANY_VALID, index); 175 char buf[256]; // scratch buffer for string conversion 176 177 if (params->single_item) { 178 switch(params->single_item) { 179 case 'b': 180 printf("%" PRId64 "\n", entry->starting_lba); 181 break; 182 case 's': { 183 uint64_t size = 0; 184 // If these aren't actually defined, don't show anything 185 if (entry->ending_lba || entry->starting_lba) 186 size = entry->ending_lba - entry->starting_lba + 1; 187 printf("%" PRId64 "\n", size); 188 break; 189 } 190 case 't': 191 GuidToStr(&entry->type, buf, sizeof(buf)); 192 printf("%s\n", buf); 193 break; 194 case 'u': 195 GuidToStr(&entry->unique, buf, sizeof(buf)); 196 printf("%s\n", buf); 197 break; 198 case 'l': 199 UTF16ToUTF8(entry->name, sizeof(entry->name) / sizeof(entry->name[0]), 200 (uint8_t *)buf, sizeof(buf)); 201 printf("%s\n", buf); 202 break; 203 case 'S': 204 printf("%d\n", GetSuccessful(drive, ANY_VALID, index)); 205 break; 206 case 'T': 207 printf("%d\n", GetTries(drive, ANY_VALID, index)); 208 break; 209 case 'P': 210 printf("%d\n", GetPriority(drive, ANY_VALID, index)); 211 break; 212 case 'A': 213 printf("0x%x\n", entry->attrs.fields.gpt_att); 214 break; 215 } 216 } else { 217 printf(TITLE_FMT, "start", "size", "part", "contents"); 218 EntryDetails(entry, index, params->numeric); 219 } 220 221 } else if (params->quick) { // show all partitions, quickly 222 uint32_t i; 223 GptEntry *entry; 224 char type[GUID_STRLEN]; 225 226 for (i = 0; i < GetNumberOfEntries(drive); ++i) { 227 entry = GetEntry(&drive->gpt, ANY_VALID, i); 228 229 if (GuidIsZero(&entry->type)) 230 continue; 231 232 if (!params->numeric && CGPT_OK == ResolveType(&entry->type, type)) { 233 } else { 234 GuidToStr(&entry->type, type, GUID_STRLEN); 235 } 236 printf(PARTITION_FMT, (int)entry->starting_lba, 237 (int)(entry->ending_lba - entry->starting_lba + 1), 238 i+1, type); 239 } 240 } else { // show all partitions 241 GptEntry *entries; 242 243 if (CGPT_OK != ReadPMBR(drive)) { 244 Error("Unable to read PMBR\n"); 245 return CGPT_FAILED; 246 } 247 248 printf(TITLE_FMT, "start", "size", "part", "contents"); 249 char buf[256]; // buffer for formatted PMBR content 250 PMBRToStr(&drive->pmbr, buf, sizeof(buf)); // will exit if buf is too small 251 printf(GPT_FMT, 0, GPT_PMBR_SECTORS, "", buf); 252 253 if (drive->gpt.valid_headers & MASK_PRIMARY) { 254 printf(GPT_FMT, (int)GPT_PMBR_SECTORS, 255 (int)GPT_HEADER_SECTORS, "", "Pri GPT header"); 256 } else { 257 printf(GPT_FMT, (int)GPT_PMBR_SECTORS, 258 (int)GPT_HEADER_SECTORS, "INVALID", "Pri GPT header"); 259 } 260 261 if (params->debug || 262 ((drive->gpt.valid_headers & MASK_PRIMARY) && params->verbose)) { 263 GptHeader *header; 264 char indent[64]; 265 266 require(snprintf(indent, sizeof(indent), GPT_MORE) < sizeof(indent)); 267 header = (GptHeader*)drive->gpt.primary_header; 268 entries = (GptEntry*)drive->gpt.primary_entries; 269 HeaderDetails(header, entries, indent, params->numeric); 270 } 271 272 GptHeader* primary_header = (GptHeader*)drive->gpt.primary_header; 273 printf(GPT_FMT, (int)primary_header->entries_lba, 274 (int)CalculateEntriesSectors(primary_header), 275 drive->gpt.valid_entries & MASK_PRIMARY ? "" : "INVALID", 276 "Pri GPT table"); 277 278 if (params->debug || 279 (drive->gpt.valid_entries & MASK_PRIMARY)) 280 EntriesDetails(drive, PRIMARY, params->numeric); 281 282 /****************************** Secondary *************************/ 283 GptHeader* secondary_header = (GptHeader*)drive->gpt.secondary_header; 284 printf(GPT_FMT, (int)secondary_header->entries_lba, 285 (int)CalculateEntriesSectors(secondary_header), 286 drive->gpt.valid_entries & MASK_SECONDARY ? "" : "INVALID", 287 "Sec GPT table"); 288 /* We show secondary table details if any of following is true. 289 * 1. in debug mode. 290 * 2. only secondary is valid. 291 * 3. secondary is not identical to promary. 292 */ 293 if (params->debug || 294 ((drive->gpt.valid_entries & MASK_SECONDARY) && 295 (!(drive->gpt.valid_entries & MASK_PRIMARY) || 296 memcmp(drive->gpt.primary_entries, drive->gpt.secondary_entries, 297 secondary_header->number_of_entries * 298 secondary_header->size_of_entry)))) { 299 EntriesDetails(drive, SECONDARY, params->numeric); 300 } 301 302 if (drive->gpt.valid_headers & MASK_SECONDARY) 303 printf(GPT_FMT, (int)(drive->gpt.gpt_drive_sectors - GPT_HEADER_SECTORS), 304 (int)GPT_HEADER_SECTORS, "", "Sec GPT header"); 305 else 306 printf(GPT_FMT, (int)GPT_PMBR_SECTORS, 307 (int)GPT_HEADER_SECTORS, "INVALID", "Sec GPT header"); 308 /* We show secondary header if any of following is true: 309 * 1. in debug mode. 310 * 2. only secondary is valid. 311 * 3. secondary is not synonymous to primary. 312 */ 313 if (params->debug || 314 ((drive->gpt.valid_headers & MASK_SECONDARY) && 315 (!(drive->gpt.valid_headers & MASK_PRIMARY) || 316 !IsSynonymous((GptHeader*)drive->gpt.primary_header, 317 (GptHeader*)drive->gpt.secondary_header)) && 318 params->verbose)) { 319 GptHeader *header; 320 char indent[64]; 321 322 require(snprintf(indent, sizeof(indent), GPT_MORE) < sizeof(indent)); 323 header = (GptHeader*)drive->gpt.secondary_header; 324 entries = (GptEntry*)drive->gpt.secondary_entries; 325 HeaderDetails(header, entries, indent, params->numeric); 326 } 327 } 328 329 CheckValid(drive); 330 331 return CGPT_OK; 332 } 333 334 int CgptShow(CgptShowParams *params) { 335 struct drive drive; 336 337 if (params == NULL) 338 return CGPT_FAILED; 339 340 if (CGPT_OK != DriveOpen(params->drive_name, &drive, O_RDONLY, 341 params->drive_size)) 342 return CGPT_FAILED; 343 344 if (GptShow(&drive, params)) 345 return CGPT_FAILED; 346 347 DriveClose(&drive, 0); 348 return CGPT_OK; 349 } 350