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