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