1 /* Copyright 2013 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 #include <errno.h>
7 #include <fcntl.h>
8 #include <getopt.h>
9 #include <inttypes.h>
10 #include <stdint.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <sys/mman.h>
15 #include <sys/stat.h>
16 #include <sys/types.h>
17 #include <unistd.h>
18
19 #include "fmap.h"
20 #include "futility.h"
21
22 #define PRESERVE "preserve"
23 #define NOT_PRESERVE "not-preserve"
24 #define IS_PRESERVE(flags) \
25 ((flags & FMAP_AREA_PRESERVE) ? PRESERVE : NOT_PRESERVE)
26
27 /*
28 * FMT_NORMAL: This format contains info related to fmap and areas including
29 * their name, offset and size in multiple lines per area
30 * FMT_PARSER: This format is parsable by scripts, it contains info about areas
31 * including their name, offset and size
32 * FMT_FLASHROM: This format is understandable by 'flashrom', it contains info
33 * about areas including their name, first and last offsets
34 * FMT_HUMAN: This format is human reader friendly, it includes hierarchy based
35 * indentation. It contains info about areas including their name, first and
36 * last offsets and size
37 * FMT_FLASH_EC: This format is understandable by 'flash_ec' script, it contains
38 * info about areas including their name and preserve flag status
39 */
40 typedef enum {
41 FMT_NORMAL,
42 FMT_PARSER,
43 FMT_FLASHROM,
44 FMT_HUMAN,
45 FMT_FLASH_EC
46 } format_t;
47
48 /* Return 0 if successful */
normal_fmap(const FmapHeader * fmh,const void * base_of_rom,size_t size_of_rom,bool extract,format_t format,const char * const * names,size_t names_len)49 static int normal_fmap(const FmapHeader *fmh,
50 const void *base_of_rom, size_t size_of_rom,
51 bool extract, format_t format,
52 const char *const *names, size_t names_len)
53 {
54 int retval = 0;
55 char buf[80]; /* DWR: magic number */
56 const FmapAreaHeader *ah = (const FmapAreaHeader *) (fmh + 1);
57 /* Size must greater than 0, else behavior is undefined. */
58 struct {
59 char *outname;
60 bool found;
61 } sections[names_len >= 1 ? names_len : 1];
62
63 memset(sections, 0, sizeof(sections));
64
65 if (extract) {
66 /* prepare the filenames to write areas to */
67 for (int i = 0; i < names_len; i++) {
68 const char *a = names[i];
69 char *f = strchr(a, ':');
70 if (!f)
71 continue;
72 if (a == f || *(f+1) == '\0') {
73 ERROR("argument \"%s\" is bogus\n", a);
74 retval = 1;
75 continue;
76 }
77 *f++ = '\0';
78 sections[i].outname = f;
79 }
80 if (retval)
81 return retval;
82 }
83
84 if (FMT_NORMAL == format) {
85 snprintf(buf, FMAP_SIGNATURE_SIZE + 1, "%s",
86 fmh->fmap_signature);
87 printf("fmap_signature %s\n", buf);
88 printf("fmap_version: %d.%d\n",
89 fmh->fmap_ver_major, fmh->fmap_ver_minor);
90 printf("fmap_base: 0x%" PRIx64 "\n", fmh->fmap_base);
91 printf("fmap_size: 0x%08x (%d)\n", fmh->fmap_size,
92 fmh->fmap_size);
93 snprintf(buf, FMAP_NAMELEN + 1, "%s", fmh->fmap_name);
94 printf("fmap_name: %s\n", buf);
95 printf("fmap_nareas: %d\n", fmh->fmap_nareas);
96 }
97
98 for (uint16_t i = 0; i < fmh->fmap_nareas; i++, ah++) {
99 snprintf(buf, FMAP_NAMELEN + 1, "%s", ah->area_name);
100 char *outname = NULL;
101
102 if (names_len) {
103 bool found = false;
104 for (int j = 0; j < names_len; j++)
105 if (!strcmp(names[j], buf)) {
106 found = true;
107 sections[j].found = true;
108 outname = sections[j].outname;
109 break;
110 }
111 if (!found)
112 continue;
113 }
114
115 switch (format) {
116 case FMT_PARSER:
117 printf("%s %d %d\n", buf, ah->area_offset,
118 ah->area_size);
119 break;
120 case FMT_FLASHROM:
121 if (ah->area_size)
122 printf("0x%08x:0x%08x %s\n", ah->area_offset,
123 ah->area_offset + ah->area_size - 1,
124 buf);
125 break;
126 case FMT_FLASH_EC:
127 if (ah->area_size)
128 printf("%s %d %d %s\n", buf, ah->area_offset, ah->area_size,
129 IS_PRESERVE(ah->area_flags));
130 break;
131 default:
132 printf("area: %d\n", i + 1);
133 printf("area_offset: 0x%08x\n", ah->area_offset);
134 printf("area_size: 0x%08x (%d)\n", ah->area_size,
135 ah->area_size);
136 printf("area_name: %s\n", buf);
137 }
138
139 if (extract) {
140 if (!outname) {
141 for (char *s = buf; *s; s++)
142 if (*s == ' ')
143 *s = '_';
144 outname = buf;
145 }
146 FILE *fp = fopen(outname, "wb");
147 if (!fp) {
148 ERROR("can't open %s: %s\n",
149 outname, strerror(errno));
150 retval = 1;
151 } else if (!ah->area_size) {
152 ERROR("section %s has zero size\n", buf);
153 retval = 1;
154 } else if (ah->area_offset + ah->area_size >
155 size_of_rom) {
156 ERROR("section %s is larger than the image\n", buf);
157 retval = 1;
158 } else if (1 != fwrite(base_of_rom + ah->area_offset,
159 ah->area_size, 1, fp)) {
160 ERROR("can't write %s: %s\n",
161 buf, strerror(errno));
162 retval = 1;
163 } else {
164 if (FMT_NORMAL == format)
165 printf("saved as \"%s\"\n", outname);
166 }
167 if (fp)
168 fclose(fp);
169 }
170 }
171
172 for (int j = 0; j < names_len; j++)
173 if (!sections[j].found) {
174 ERROR("FMAP section %s not found\n", names[j]);
175 retval = 1;
176 }
177
178 return retval;
179 }
180
181 /****************************************************************************/
182 /* Stuff for human-readable form */
183
184 struct dup_s {
185 char *name;
186 struct dup_s *next;
187 };
188
189 struct node_s {
190 char *name;
191 uint32_t start;
192 uint32_t size;
193 uint32_t end;
194 struct node_s *parent;
195 int num_children;
196 struct node_s **child;
197 struct dup_s *alias;
198 };
199
200 static struct node_s *all_nodes;
201
sort_nodes(int num,struct node_s * ary[])202 static void sort_nodes(int num, struct node_s *ary[])
203 {
204 /* bubble-sort is quick enough with only a few entries */
205 for (unsigned int i = 0; i < num; i++) {
206 for (unsigned int j = i + 1; j < num; j++) {
207 if (ary[j]->start > ary[i]->start) {
208 struct node_s *tmp = ary[i];
209 ary[i] = ary[j];
210 ary[j] = tmp;
211 }
212 }
213 }
214 }
215
line(int indent,const char * name,uint32_t start,uint32_t end,uint32_t size,const char * append)216 static void line(int indent, const char *name, uint32_t start, uint32_t end,
217 uint32_t size, const char *append)
218 {
219 for (int i = 0; i < indent; i++)
220 printf(" ");
221 printf("%-25s %08x %08x %08x%s\n", name, start, end, size,
222 append ? append : "");
223 }
224
empty(int indent,uint32_t start,uint32_t end,char * name,bool gaps,int * gapcount)225 static void empty(int indent, uint32_t start, uint32_t end, char *name, bool gaps, int *gapcount)
226 {
227 char buf[80];
228 if (gaps) {
229 sprintf(buf, " // gap in %s", name);
230 line(indent + 1, "", start, end, end - start, buf);
231 }
232 (*gapcount)++;
233 }
234
show(struct node_s * p,int indent,int show_first,bool show_gaps,int * gapcount)235 static void show(struct node_s *p, int indent, int show_first, bool show_gaps, int *gapcount)
236 {
237 struct dup_s *alias;
238 if (show_first) {
239 line(indent, p->name, p->start, p->end, p->size, 0);
240 for (alias = p->alias; alias; alias = alias->next)
241 line(indent, alias->name, p->start, p->end, p->size,
242 " // DUPLICATE");
243 }
244 sort_nodes(p->num_children, p->child);
245 for (unsigned int i = 0; i < p->num_children; i++) {
246 if (i == 0 && p->end != p->child[i]->end)
247 empty(indent, p->child[i]->end, p->end, p->name, show_gaps, gapcount);
248 show(p->child[i], indent + show_first, 1, show_gaps, gapcount);
249 if (i < p->num_children - 1
250 && p->child[i]->start != p->child[i + 1]->end)
251 empty(indent, p->child[i + 1]->end, p->child[i]->start,
252 p->name, show_gaps, gapcount);
253 if (i == p->num_children - 1 && p->child[i]->start != p->start)
254 empty(indent, p->start, p->child[i]->start, p->name, show_gaps, gapcount);
255 }
256 }
257
overlaps(int i,int j)258 static int overlaps(int i, int j)
259 {
260 struct node_s *a = all_nodes + i;
261 struct node_s *b = all_nodes + j;
262
263 return ((a->start < b->start) && (b->start < a->end) &&
264 (b->start < a->end) && (a->end < b->end));
265 }
266
encloses(int i,int j)267 static int encloses(int i, int j)
268 {
269 struct node_s *a = all_nodes + i;
270 struct node_s *b = all_nodes + j;
271
272 return ((a->start <= b->start) && (a->end >= b->end));
273 }
274
duplicates(int i,int j)275 static int duplicates(int i, int j)
276 {
277 struct node_s *a = all_nodes + i;
278 struct node_s *b = all_nodes + j;
279
280 return ((a->start == b->start) && (a->end == b->end));
281 }
282
add_dupe(int i,int j,int numnodes)283 static void add_dupe(int i, int j, int numnodes)
284 {
285 struct dup_s *alias = (struct dup_s *) malloc(sizeof(struct dup_s));
286 alias->name = all_nodes[j].name;
287 alias->next = all_nodes[i].alias;
288 all_nodes[i].alias = alias;
289 for (int k = j; k < numnodes; k++)
290 all_nodes[k] = all_nodes[k + 1];
291 }
292
add_child(struct node_s * p,int n)293 static void add_child(struct node_s *p, int n)
294 {
295 if (p->num_children && !p->child) {
296 p->child =
297 (struct node_s **)calloc(p->num_children,
298 sizeof(struct node_s *));
299 if (!p->child) {
300 perror("calloc failed");
301 exit(1);
302 }
303 }
304 for (unsigned int i = 0; i < p->num_children; i++) {
305 if (!p->child[i]) {
306 p->child[i] = all_nodes + n;
307 return;
308 }
309 }
310 }
311
human_fmap(const FmapHeader * fmh,bool gaps,int overlap)312 static int human_fmap(const FmapHeader *fmh, bool gaps, int overlap)
313 {
314 int errorcnt = 0;
315
316 /* The challenge here is to generate a directed graph from the
317 * arbitrarily-ordered FMAP entries, and then to prune it until it's as
318 * simple (and deep) as possible. Overlapping regions are not allowed.
319 * Duplicate regions are okay, but may require special handling. */
320
321 /* Convert the FMAP info into our format. */
322 uint16_t numnodes = fmh->fmap_nareas;
323
324 /* plus one for the all-enclosing "root" */
325 all_nodes = (struct node_s *) calloc(numnodes + 1,
326 sizeof(struct node_s));
327 if (!all_nodes) {
328 perror("calloc failed");
329 return 1;
330 }
331 for (uint16_t i = 0; i < numnodes; i++) {
332 char buf[FMAP_NAMELEN + 1];
333 const FmapAreaHeader *ah = (FmapAreaHeader *) (fmh + 1);
334
335 strncpy(buf, ah[i].area_name, FMAP_NAMELEN);
336 buf[FMAP_NAMELEN] = '\0';
337 all_nodes[i].name = strdup(buf);
338 if (!all_nodes[i].name) {
339 perror("strdup failed");
340 return 1;
341 }
342 all_nodes[i].start = ah[i].area_offset;
343 all_nodes[i].size = ah[i].area_size;
344 all_nodes[i].end = ah[i].area_offset + ah[i].area_size;
345 }
346 /* Now add the root node */
347 all_nodes[numnodes].name = strdup("-entire flash-");
348 all_nodes[numnodes].start = fmh->fmap_base;
349 all_nodes[numnodes].size = fmh->fmap_size;
350 all_nodes[numnodes].end = fmh->fmap_base + fmh->fmap_size;
351
352 /* First, coalesce any duplicates */
353 for (uint16_t i = 0; i < numnodes; i++) {
354 for (uint16_t j = i + 1; j < numnodes; j++) {
355 if (duplicates(i, j)) {
356 add_dupe(i, j, numnodes);
357 numnodes--;
358 }
359 }
360 }
361
362 /* Each node should have at most one parent, which is the smallest
363 * enclosing node. Duplicate nodes "enclose" each other, but if there's
364 * already a relationship in one direction, we won't create another.
365 */
366 for (uint16_t i = 0; i < numnodes; i++) {
367 /* Find the smallest parent, which might be the root node. */
368 int k = numnodes;
369 for (uint16_t j = 0; j < numnodes; j++) { /* full O(N^2) comparison */
370 if (i == j)
371 continue;
372 if (overlaps(i, j)) {
373 printf("ERROR: %s and %s overlap\n",
374 all_nodes[i].name, all_nodes[j].name);
375 printf(" %s: %#x - %#x\n", all_nodes[i].name,
376 all_nodes[i].start, all_nodes[i].end);
377 printf(" %s: %#x - %#x\n", all_nodes[j].name,
378 all_nodes[j].start, all_nodes[j].end);
379 if (overlap < 2) {
380 printf("Use more -h args to ignore"
381 " this error\n");
382 errorcnt++;
383 }
384 continue;
385 }
386 if (encloses(j, i)
387 && all_nodes[j].size < all_nodes[k].size)
388 k = j;
389 }
390 all_nodes[i].parent = all_nodes + k;
391 }
392 if (errorcnt)
393 return 1;
394
395 /* Force those deadbeat parents to recognize their children */
396 for (uint16_t i = 0; i < numnodes; i++) /* how many */
397 if (all_nodes[i].parent)
398 all_nodes[i].parent->num_children++;
399 for (uint16_t i = 0; i < numnodes; i++) /* here they are */
400 if (all_nodes[i].parent)
401 add_child(all_nodes[i].parent, i);
402
403 /* Ready to go */
404 printf("# name start end size\n");
405 int gapcount = 0;
406 show(all_nodes + numnodes, 0, gaps, gaps, &gapcount);
407
408 if (gapcount && !gaps)
409 printf("\nWARNING: unused regions found. Use -H to see them\n");
410
411 return 0;
412 }
413
414 /* End of human-reable stuff */
415 /****************************************************************************/
416
417 static const char usage[] =
418 "\nUsage: " MYNAME " %s [OPTIONS] FLASHIMAGE [NAME...]\n\n"
419 "Display (and extract) the FMAP components from a BIOS image.\n"
420 "\n"
421 "Options:\n"
422 " -x Extract the named sections from the file\n"
423 " -h Use a human-readable format\n"
424 " -H With -h, display any gaps\n"
425 " -p Use a format easy to parse by scripts\n"
426 " -F Use the format expected by flashrom\n"
427 " -e Use the format expected by flash_ec\n"
428 "\n"
429 "Specify one or more NAMEs to dump only those sections.\n"
430 "\n";
431
print_help(int argc,char * argv[])432 static void print_help(int argc, char *argv[])
433 {
434 printf(usage, argv[0]);
435 }
436
437 enum {
438 OPT_HELP = 1000,
439 };
440 static const struct option long_opts[] = {
441 {"help", 0, 0, OPT_HELP},
442 {NULL, 0, 0, 0}
443 };
do_dump_fmap(int argc,char * argv[])444 static int do_dump_fmap(int argc, char *argv[])
445 {
446 int c;
447 int errorcnt = 0;
448 int retval = 1;
449 bool opt_extract = false;
450 int opt_overlap = 0;
451 bool opt_gaps = false;
452 format_t opt_format = FMT_NORMAL;
453
454 opterr = 0; /* quiet, you */
455 while ((c = getopt_long(argc, argv, ":xpFhHe", long_opts, 0)) != -1) {
456 switch (c) {
457 case 'x':
458 opt_extract = true;
459 break;
460 case 'p':
461 opt_format = FMT_PARSER;
462 break;
463 case 'e':
464 opt_format = FMT_FLASH_EC;
465 break;
466 case 'F':
467 opt_format = FMT_FLASHROM;
468 break;
469 case 'H':
470 opt_gaps = true;
471 VBOOT_FALLTHROUGH;
472 case 'h':
473 opt_format = FMT_HUMAN;
474 opt_overlap++;
475 break;
476 case OPT_HELP:
477 print_help(argc, argv);
478 return 0;
479 case '?':
480 ERROR("%s: unrecognized switch: -%c\n",
481 argv[0], optopt);
482 errorcnt++;
483 break;
484 case ':':
485 ERROR("%s: missing argument to -%c\n",
486 argv[0], optopt);
487 errorcnt++;
488 break;
489 default:
490 errorcnt++;
491 break;
492 }
493 }
494
495 if (errorcnt || optind >= argc) {
496 print_help(argc, argv);
497 return 1;
498 }
499
500 int fd = open(argv[optind], O_RDONLY);
501 if (fd < 0) {
502 ERROR("%s: can't open %s: %s\n",
503 argv[0], argv[optind], strerror(errno));
504 return 1;
505 }
506
507 struct stat sb;
508 if (fstat(fd, &sb)) {
509 ERROR("%s: can't stat %s: %s\n",
510 argv[0], argv[optind], strerror(errno));
511 close(fd);
512 return 1;
513 }
514
515 void *base_of_rom = mmap(0, sb.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
516 if (base_of_rom == MAP_FAILED) {
517 ERROR("%s: can't mmap %s: %s\n",
518 argv[0], argv[optind], strerror(errno));
519 close(fd);
520 return 1;
521 }
522 close(fd); /* done with this now */
523
524 const size_t size_of_rom = sb.st_size;
525
526 const FmapHeader *fmap = fmap_find(base_of_rom, size_of_rom);
527 if (fmap) {
528 switch (opt_format) {
529 case FMT_HUMAN:
530 retval = human_fmap(fmap, opt_gaps, opt_overlap);
531 break;
532 case FMT_NORMAL:
533 printf("hit at 0x%08x\n",
534 (uint32_t) ((char *)fmap - (char *)base_of_rom));
535 VBOOT_FALLTHROUGH;
536 default:
537 retval = normal_fmap(fmap, base_of_rom, size_of_rom,
538 opt_extract, opt_format,
539 (const char **)(argv + optind + 1),
540 argc - optind - 1);
541 }
542 } else {
543 ERROR("FMAP header not found in %s\n", argv[optind]);
544 }
545
546 if (munmap(base_of_rom, sb.st_size)) {
547 ERROR("%s: can't munmap %s: %s\n",
548 argv[0], argv[optind], strerror(errno));
549 return 1;
550 }
551
552 return retval;
553 }
554
555 DECLARE_FUTIL_COMMAND(dump_fmap, do_dump_fmap, VBOOT_VERSION_ALL,
556 "Display FMAP contents from a firmware image");
557