xref: /aosp_15_r20/external/vboot_reference/cgpt/cgpt_show.c (revision 8617a60d3594060b7ecbd21bc622a7c14f3cf2bc)
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       int tries = (entry->attrs.fields.gpt_att &
128                    CGPT_ATTRIBUTE_TRIES_MASK) >>
129           CGPT_ATTRIBUTE_TRIES_OFFSET;
130       int successful = (entry->attrs.fields.gpt_att &
131                         CGPT_ATTRIBUTE_SUCCESSFUL_MASK) >>
132           CGPT_ATTRIBUTE_SUCCESSFUL_OFFSET;
133       int priority = (entry->attrs.fields.gpt_att &
134                       CGPT_ATTRIBUTE_PRIORITY_MASK) >>
135           CGPT_ATTRIBUTE_PRIORITY_OFFSET;
136       int error_counter = (entry->attrs.fields.gpt_att &
137                            CGPT_ATTRIBUTE_ERROR_COUNTER_MASK) >>
138           CGPT_ATTRIBUTE_ERROR_COUNTER_OFFSET;
139       clen = snprintf(contents, sizeof(contents),
140                       "priority=%d tries=%d successful=%d error_counter=%d ",
141                       priority, tries, successful, error_counter);
142     }
143 
144     if (entry->attrs.fields.required) {
145       clen += snprintf(contents + clen, sizeof(contents) - clen,
146                        "required=%d ", entry->attrs.fields.required);
147       require(clen < sizeof(contents));
148     }
149 
150     if (entry->attrs.fields.efi_ignore) {
151       clen += snprintf(contents + clen, sizeof(contents) - clen,
152                        "efi_ignore=%d ", entry->attrs.fields.efi_ignore);
153       require(clen < sizeof(contents));
154     }
155 
156     if (entry->attrs.fields.legacy_boot) {
157       clen += snprintf(contents + clen, sizeof(contents) - clen,
158                        "legacy_boot=%d ", entry->attrs.fields.legacy_boot);
159       require(clen < sizeof(contents));
160     }
161   } else {
162     clen = snprintf(contents, sizeof(contents),
163                     "[%x]", entry->attrs.fields.gpt_att);
164   }
165   require(clen < sizeof(contents));
166   if (clen)
167     printf(PARTITION_MORE, "Attr: ", contents);
168 }
169 
EntriesDetails(struct drive * drive,const int secondary,int raw)170 static void EntriesDetails(struct drive *drive, const int secondary, int raw) {
171   uint32_t i;
172 
173   for (i = 0; i < GetNumberOfEntries(drive); ++i) {
174     GptEntry *entry;
175     entry = GetEntry(&drive->gpt, secondary, i);
176 
177     if (GuidIsZero(&entry->type))
178       continue;
179 
180     EntryDetails(entry, i, raw);
181   }
182 }
183 
GptShow(struct drive * drive,CgptShowParams * params)184 static int GptShow(struct drive *drive, CgptShowParams *params) {
185   int gpt_retval;
186   if (GPT_SUCCESS != (gpt_retval = GptValidityCheck(&drive->gpt))) {
187     Error("GptValidityCheck() returned %d: %s\n",
188           gpt_retval, GptError(gpt_retval));
189     return CGPT_FAILED;
190   }
191 
192   if (params->partition) {                      // show single partition
193 
194     if (params->partition > GetNumberOfEntries(drive)) {
195       Error("invalid partition number: %d\n", params->partition);
196       return CGPT_FAILED;
197     }
198 
199     uint32_t index = params->partition - 1;
200     GptEntry *entry = GetEntry(&drive->gpt, ANY_VALID, index);
201     char buf[256];                      // scratch buffer for string conversion
202 
203     if (params->single_item) {
204       switch(params->single_item) {
205       case 'b':
206         printf("%" PRId64 "\n", entry->starting_lba);
207         break;
208       case 's': {
209         uint64_t size = 0;
210         // If these aren't actually defined, don't show anything
211         if (entry->ending_lba || entry->starting_lba)
212           size = entry->ending_lba - entry->starting_lba + 1;
213         printf("%" PRId64 "\n", size);
214         break;
215       }
216       case 't':
217         GuidToStr(&entry->type, buf, sizeof(buf));
218         printf("%s\n", buf);
219         break;
220       case 'u':
221         GuidToStr(&entry->unique, buf, sizeof(buf));
222         printf("%s\n", buf);
223         break;
224       case 'l':
225         UTF16ToUTF8(entry->name, sizeof(entry->name) / sizeof(entry->name[0]),
226                     (uint8_t *)buf, sizeof(buf));
227         printf("%s\n", buf);
228         break;
229       case 'S':
230         printf("%d\n", GetSuccessful(drive, ANY_VALID, index));
231         break;
232       case 'T':
233         printf("%d\n", GetTries(drive, ANY_VALID, index));
234         break;
235       case 'P':
236         printf("%d\n", GetPriority(drive, ANY_VALID, index));
237         break;
238       case 'R':
239         printf("%d\n", GetRequired(drive, ANY_VALID, index));
240         break;
241       case 'B':
242         printf("%d\n", GetLegacyBoot(drive, ANY_VALID, index));
243         break;
244       case 'A':
245         printf("%#x\n", entry->attrs.fields.gpt_att);
246         break;
247       }
248     } else {
249       printf(TITLE_FMT, "start", "size", "part", "contents");
250       EntryDetails(entry, index, params->numeric);
251     }
252 
253   } else if (params->quick) {                   // show all partitions, quickly
254     uint32_t i;
255     GptEntry *entry;
256     char type[GUID_STRLEN];
257 
258     for (i = 0; i < GetNumberOfEntries(drive); ++i) {
259       entry = GetEntry(&drive->gpt, ANY_VALID, i);
260 
261       if (GuidIsZero(&entry->type))
262         continue;
263 
264       if (!params->numeric && CGPT_OK == ResolveType(&entry->type, type)) {
265       } else {
266         GuidToStr(&entry->type, type, GUID_STRLEN);
267       }
268       printf(PARTITION_FMT, (uint64_t)entry->starting_lba,
269              (uint64_t)(entry->ending_lba - entry->starting_lba + 1),
270              i+1, type);
271     }
272   } else {                              // show all partitions
273     GptEntry *entries;
274 
275     if (params->debug || params->verbose) {
276       printf("Drive details:\n");
277       printf("    Total Size (bytes): %" PRIu64 "\n", drive->size);
278       printf("    LBA Size (bytes): %d\n", drive->gpt.sector_bytes);
279       if (drive->gpt.flags & GPT_FLAG_EXTERNAL) {
280         printf("    Drive (where GPT lives) Size (blocks): %" PRIu64 "\n",
281                drive->gpt.gpt_drive_sectors);
282         printf("    Drive (where partitions live) Size (blocks): %" PRIu64 "\n",
283                drive->gpt.streaming_drive_sectors);
284       } else {
285         // We know gpt_drive_sectors == streaming_drive_sectors here.
286         printf("    Drive Size (blocks): %" PRIu64 "\n",
287                drive->gpt.gpt_drive_sectors);
288       }
289       printf("\n");
290     }
291 
292     if (CGPT_OK != ReadPMBR(drive)) {
293       Error("Unable to read PMBR\n");
294       return CGPT_FAILED;
295     }
296 
297     printf(TITLE_FMT, "start", "size", "part", "contents");
298     char buf[256];                      // buffer for formatted PMBR content
299     PMBRToStr(&drive->pmbr, buf, sizeof(buf)); // will exit if buf is too small
300     printf(GPT_FMT, (uint64_t)0, (uint64_t)GPT_PMBR_SECTORS, "", buf);
301 
302     if (drive->gpt.ignored & MASK_PRIMARY) {
303       printf(GPT_FMT, (uint64_t)GPT_PMBR_SECTORS,
304              (uint64_t)GPT_HEADER_SECTORS, "IGNORED", "Pri GPT header");
305     } else {
306       if (drive->gpt.valid_headers & MASK_PRIMARY) {
307         printf(GPT_FMT, (uint64_t)GPT_PMBR_SECTORS,
308                (uint64_t)GPT_HEADER_SECTORS, "", "Pri GPT header");
309       } else {
310         printf(GPT_FMT, (uint64_t)GPT_PMBR_SECTORS,
311                (uint64_t)GPT_HEADER_SECTORS, "INVALID", "Pri GPT header");
312       }
313 
314       if (params->debug ||
315           ((drive->gpt.valid_headers & MASK_PRIMARY) && params->verbose)) {
316         GptHeader *header;
317         char indent[64];
318 
319         require(snprintf(indent, sizeof(indent), GPT_MORE) < sizeof(indent));
320         header = (GptHeader*)drive->gpt.primary_header;
321         entries = (GptEntry*)drive->gpt.primary_entries;
322         HeaderDetails(header, entries, indent, params->numeric);
323       }
324 
325       GptHeader* primary_header = (GptHeader*)drive->gpt.primary_header;
326       printf(GPT_FMT, (uint64_t)primary_header->entries_lba,
327              (uint64_t)CalculateEntriesSectors(primary_header,
328                          drive->gpt.sector_bytes),
329              drive->gpt.valid_entries & MASK_PRIMARY ? "" : "INVALID",
330              "Pri GPT table");
331 
332       if (params->debug ||
333           (drive->gpt.valid_entries & MASK_PRIMARY))
334         EntriesDetails(drive, PRIMARY, params->numeric);
335     }
336 
337     /****************************** Secondary *************************/
338     if (drive->gpt.ignored & MASK_SECONDARY) {
339       printf(GPT_FMT,
340              (uint64_t)(drive->gpt.gpt_drive_sectors - GPT_HEADER_SECTORS),
341              (uint64_t)GPT_HEADER_SECTORS, "IGNORED", "Sec GPT header");
342     } else {
343       GptHeader* secondary_header = (GptHeader*)drive->gpt.secondary_header;
344       printf(GPT_FMT, (uint64_t)secondary_header->entries_lba,
345              (uint64_t)CalculateEntriesSectors(secondary_header,
346                          drive->gpt.sector_bytes),
347              drive->gpt.valid_entries & MASK_SECONDARY ? "" : "INVALID",
348              "Sec GPT table");
349       /* We show secondary table details if any of following is true.
350        *   1. in debug mode.
351        *   2. primary table is being ignored
352        *   3. only secondary is valid.
353        *   4. secondary is not identical to primary.
354        */
355       if (params->debug || (drive->gpt.ignored & MASK_PRIMARY) ||
356           ((drive->gpt.valid_entries & MASK_SECONDARY) &&
357            (!(drive->gpt.valid_entries & MASK_PRIMARY) ||
358             memcmp(drive->gpt.primary_entries, drive->gpt.secondary_entries,
359                    secondary_header->number_of_entries *
360                    secondary_header->size_of_entry)))) {
361         EntriesDetails(drive, SECONDARY, params->numeric);
362       }
363 
364       if (drive->gpt.valid_headers & MASK_SECONDARY) {
365         printf(GPT_FMT,
366                (uint64_t)(drive->gpt.gpt_drive_sectors - GPT_HEADER_SECTORS),
367                (uint64_t)GPT_HEADER_SECTORS, "", "Sec GPT header");
368       } else {
369         printf(GPT_FMT, (uint64_t)GPT_PMBR_SECTORS,
370                (uint64_t)GPT_HEADER_SECTORS, "INVALID", "Sec GPT header");
371       }
372       /* We show secondary header if any of following is true:
373        *   1. in debug mode.
374        *   2. primary table is being ignored
375        *   3. only secondary is valid.
376        *   4. secondary is not synonymous to primary and not ignored.
377        */
378       if (params->debug || (drive->gpt.ignored & MASK_PRIMARY) ||
379           ((drive->gpt.valid_headers & MASK_SECONDARY) &&
380            (!(drive->gpt.valid_headers & MASK_PRIMARY) ||
381             !IsSynonymous((GptHeader*)drive->gpt.primary_header,
382                           (GptHeader*)drive->gpt.secondary_header)) &&
383            params->verbose)) {
384         GptHeader *header;
385         char indent[64];
386 
387         require(snprintf(indent, sizeof(indent), GPT_MORE) < sizeof(indent));
388         header = (GptHeader*)drive->gpt.secondary_header;
389         entries = (GptEntry*)drive->gpt.secondary_entries;
390         HeaderDetails(header, entries, indent, params->numeric);
391       }
392     }
393   }
394 
395   CheckValid(drive);
396 
397   return CGPT_OK;
398 }
399 
CgptShow(CgptShowParams * params)400 int CgptShow(CgptShowParams *params) {
401   struct drive drive;
402 
403   if (params == NULL)
404     return CGPT_FAILED;
405 
406   if (CGPT_OK != DriveOpen(params->drive_name, &drive, O_RDONLY,
407                            params->drive_size))
408     return CGPT_FAILED;
409 
410   int ret = GptShow(&drive, params);
411   DriveClose(&drive, 0);
412   return ret;
413 }
414