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)
Uint8To2Chars(const uint8_t t)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 
RawDump(const uint8_t * memory,const int size,char * buf,int group)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 
PrintSignature(const char * indent,const char * sig,size_t n,int raw)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 
HeaderDetails(GptHeader * header,GptEntry * entries,const char * indent,int raw)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 
EntryDetails(GptEntry * entry,uint32_t index,int raw)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 
EntriesDetails(struct drive * drive,const int secondary,int raw)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 
GptShow(struct drive * drive,CgptShowParams * params)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 
CgptShow(CgptShowParams * params)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