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