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
21enum { FMT_NORMAL, FMT_PRETTY, FMT_FLASHROM, FMT_HUMAN };
22
23/* global variables */
24static int opt_extract;
25static int opt_format = FMT_NORMAL;
26static int opt_overlap;
27static char *progname;
28static void *base_of_rom;
29static size_t size_of_rom;
30static int opt_gaps;
31
32/* Return 0 if successful */
33static 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
153struct dup_s {
154	char *name;
155	struct dup_s *next;
156};
157
158struct 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
169static struct node_s *all_nodes;
170
171static 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
188static 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
198static int gapcount;
199static 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
209static 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
233static 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
242static 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
250static 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
258static 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
271static 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
290static 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
396static 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
410static void print_help(const char *name)
411{
412	printf(usage, name);
413}
414
415static 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
516DECLARE_FUTIL_COMMAND(dump_fmap, do_dump_fmap,
517		      VBOOT_VERSION_ALL,
518		      "Display FMAP contents from a firmware image",
519		      print_help);
520