Home | History | Annotate | Download | only in futility
      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 */
     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 
    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 
    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;
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    410 static void print_help(const char *name)
    411 {
    412 	printf(usage, name);
    413 }
    414 
    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