Files
OpenCellular/futility/cmd_dump_fmap.c
Bill Richardson 1eae873b61 futility: Add global args to specify vboot API and format
The host-side futility tool will need to support all extant vboot
implementations. Some legacy futility commands only support the
original vb1 format, but others ("show" or "sign", for example)
may need to be instructed which formats to expect or emit.

This change adds some global args to specify the preferred
formats. It also cleans up a few [unused AFAICT] one-letter args
to avoid conflicts.

BUG=chromium:231574
BRANCH=none
TEST=make runtests

Nothing makes use of this yet, except the "help" command.

Change-Id: Ib79fa12af72b8860b9494e5d9e90b9572c006107
Signed-off-by: Bill Richardson <wfrichar@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/246765
Reviewed-by: Randall Spangler <rspangler@chromium.org>
2015-02-28 00:56:13 +00:00

520 lines
13 KiB
C

/*
* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "fmap.h"
#include "futility.h"
enum { FMT_NORMAL, FMT_PRETTY, FMT_FLASHROM, FMT_HUMAN };
/* global variables */
static int opt_extract;
static int opt_format = FMT_NORMAL;
static int opt_overlap;
static char *progname;
static void *base_of_rom;
static size_t size_of_rom;
static int opt_gaps;
/* Return 0 if successful */
static int dump_fmap(const FmapHeader *fmh, int argc, char *argv[])
{
int i, retval = 0;
char buf[80]; /* DWR: magic number */
const FmapAreaHeader *ah;
ah = (const FmapAreaHeader *) (fmh + 1);
char *extract_names[argc];
char *outname = 0;
if (opt_extract) {
/* prepare the filenames to write areas to */
memset(extract_names, 0, sizeof(extract_names));
for (i = 0; i < argc; i++) {
char *a = argv[i];
char *f = strchr(a, ':');
if (!f)
continue;
if (a == f || *(f+1) == '\0') {
fprintf(stderr,
"argument \"%s\" is bogus\n", a);
retval = 1;
continue;
}
*f++ = '\0';
extract_names[i] = f;
}
if (retval)
return retval;
}
if (FMT_NORMAL == opt_format) {
snprintf(buf, FMAP_SIGNATURE_SIZE + 1, "%s",
fmh->fmap_signature);
printf("fmap_signature %s\n", buf);
printf("fmap_version: %d.%d\n",
fmh->fmap_ver_major, fmh->fmap_ver_minor);
printf("fmap_base: 0x%" PRIx64 "\n", fmh->fmap_base);
printf("fmap_size: 0x%08x (%d)\n", fmh->fmap_size,
fmh->fmap_size);
snprintf(buf, FMAP_NAMELEN + 1, "%s", fmh->fmap_name);
printf("fmap_name: %s\n", buf);
printf("fmap_nareas: %d\n", fmh->fmap_nareas);
}
for (i = 0; i < fmh->fmap_nareas; i++, ah++) {
snprintf(buf, FMAP_NAMELEN + 1, "%s", ah->area_name);
if (argc) {
int j, found = 0;
outname = NULL;
for (j = 0; j < argc; j++)
if (!strcmp(argv[j], buf)) {
found = 1;
outname = extract_names[j];
break;
}
if (!found)
continue;
}
switch (opt_format) {
case FMT_PRETTY:
printf("%s %d %d\n", buf, ah->area_offset,
ah->area_size);
break;
case FMT_FLASHROM:
if (ah->area_size)
printf("0x%08x:0x%08x %s\n", ah->area_offset,
ah->area_offset + ah->area_size - 1,
buf);
break;
default:
printf("area: %d\n", i + 1);
printf("area_offset: 0x%08x\n", ah->area_offset);
printf("area_size: 0x%08x (%d)\n", ah->area_size,
ah->area_size);
printf("area_name: %s\n", buf);
}
if (opt_extract) {
char *s;
if (!outname) {
for (s = buf; *s; s++)
if (*s == ' ')
*s = '_';
outname = buf;
}
FILE *fp = fopen(outname, "wb");
if (!fp) {
fprintf(stderr, "%s: can't open %s: %s\n",
progname, outname, strerror(errno));
retval = 1;
} else if (!ah->area_size) {
fprintf(stderr,
"%s: section %s has zero size\n",
progname, buf);
} else if (ah->area_offset + ah->area_size >
size_of_rom) {
fprintf(stderr, "%s: section %s is larger"
" than the image\n", progname, buf);
retval = 1;
} else if (1 != fwrite(base_of_rom + ah->area_offset,
ah->area_size, 1, fp)) {
fprintf(stderr, "%s: can't write %s: %s\n",
progname, buf, strerror(errno));
retval = 1;
} else {
if (FMT_NORMAL == opt_format)
printf("saved as \"%s\"\n", outname);
}
fclose(fp);
}
}
return retval;
}
/****************************************************************************/
/* Stuff for human-readable form */
struct dup_s {
char *name;
struct dup_s *next;
};
struct node_s {
char *name;
uint32_t start;
uint32_t size;
uint32_t end;
struct node_s *parent;
int num_children;
struct node_s **child;
struct dup_s *alias;
};
static struct node_s *all_nodes;
static void sort_nodes(int num, struct node_s *ary[])
{
int i, j;
struct node_s *tmp;
/* bubble-sort is quick enough with only a few entries */
for (i = 0; i < num; i++) {
for (j = i + 1; j < num; j++) {
if (ary[j]->start > ary[i]->start) {
tmp = ary[i];
ary[i] = ary[j];
ary[j] = tmp;
}
}
}
}
static void line(int indent, char *name,
uint32_t start, uint32_t end, uint32_t size, char *append)
{
int i;
for (i = 0; i < indent; i++)
printf(" ");
printf("%-25s %08x %08x %08x%s\n", name, start, end, size,
append ? append : "");
}
static int gapcount;
static void empty(int indent, uint32_t start, uint32_t end, char *name)
{
char buf[80];
if (opt_gaps) {
sprintf(buf, " // gap in %s", name);
line(indent + 1, "", start, end, end - start, buf);
}
gapcount++;
}
static void show(struct node_s *p, int indent, int show_first)
{
int i;
struct dup_s *alias;
if (show_first) {
line(indent, p->name, p->start, p->end, p->size, 0);
for (alias = p->alias; alias; alias = alias->next)
line(indent, alias->name, p->start, p->end, p->size,
" // DUPLICATE");
}
sort_nodes(p->num_children, p->child);
for (i = 0; i < p->num_children; i++) {
if (i == 0 && p->end != p->child[i]->end)
empty(indent, p->child[i]->end, p->end, p->name);
show(p->child[i], indent + show_first, 1);
if (i < p->num_children - 1
&& p->child[i]->start != p->child[i + 1]->end)
empty(indent, p->child[i + 1]->end, p->child[i]->start,
p->name);
if (i == p->num_children - 1 && p->child[i]->start != p->start)
empty(indent, p->start, p->child[i]->start, p->name);
}
}
static int overlaps(int i, int j)
{
struct node_s *a = all_nodes + i;
struct node_s *b = all_nodes + j;
return ((a->start < b->start) && (b->start < a->end) &&
(b->start < a->end) && (a->end < b->end));
}
static int encloses(int i, int j)
{
struct node_s *a = all_nodes + i;
struct node_s *b = all_nodes + j;
return ((a->start <= b->start) && (a->end >= b->end));
}
static int duplicates(int i, int j)
{
struct node_s *a = all_nodes + i;
struct node_s *b = all_nodes + j;
return ((a->start == b->start) && (a->end == b->end));
}
static void add_dupe(int i, int j, int numnodes)
{
int k;
struct dup_s *alias;
alias = (struct dup_s *) malloc(sizeof(struct dup_s));
alias->name = all_nodes[j].name;
alias->next = all_nodes[i].alias;
all_nodes[i].alias = alias;
for (k = j; k < numnodes; k++)
all_nodes[k] = all_nodes[k + 1];
}
static void add_child(struct node_s *p, int n)
{
int i;
if (p->num_children && !p->child) {
p->child =
(struct node_s **)calloc(p->num_children,
sizeof(struct node_s *));
if (!p->child) {
perror("calloc failed");
exit(1);
}
}
for (i = 0; i < p->num_children; i++)
if (!p->child[i]) {
p->child[i] = all_nodes + n;
return;
}
}
static int human_fmap(const FmapHeader *fmh)
{
FmapAreaHeader *ah;
int i, j, errorcnt = 0;
int numnodes;
ah = (FmapAreaHeader *) (fmh + 1);
/* The challenge here is to generate a directed graph from the
* arbitrarily-ordered FMAP entries, and then to prune it until it's as
* simple (and deep) as possible. Overlapping regions are not allowed.
* Duplicate regions are okay, but may require special handling. */
/* Convert the FMAP info into our format. */
numnodes = fmh->fmap_nareas;
/* plus one for the all-enclosing "root" */
all_nodes = (struct node_s *) calloc(numnodes + 1,
sizeof(struct node_s));
if (!all_nodes) {
perror("calloc failed");
exit(1);
}
for (i = 0; i < numnodes; i++) {
char buf[FMAP_NAMELEN + 1];
strncpy(buf, ah[i].area_name, FMAP_NAMELEN);
buf[FMAP_NAMELEN] = '\0';
all_nodes[i].name = strdup(buf);
if (!all_nodes[i].name) {
perror("strdup failed");
exit(1);
}
all_nodes[i].start = ah[i].area_offset;
all_nodes[i].size = ah[i].area_size;
all_nodes[i].end = ah[i].area_offset + ah[i].area_size;
}
/* Now add the root node */
all_nodes[numnodes].name = strdup("-entire flash-");
all_nodes[numnodes].start = fmh->fmap_base;
all_nodes[numnodes].size = fmh->fmap_size;
all_nodes[numnodes].end = fmh->fmap_base + fmh->fmap_size;
/* First, coalesce any duplicates */
for (i = 0; i < numnodes; i++) {
for (j = i + 1; j < numnodes; j++) {
if (duplicates(i, j)) {
add_dupe(i, j, numnodes);
numnodes--;
}
}
}
/* Each node should have at most one parent, which is the smallest
* enclosing node. Duplicate nodes "enclose" each other, but if there's
* already a relationship in one direction, we won't create another.
*/
for (i = 0; i < numnodes; i++) {
/* Find the smallest parent, which might be the root node. */
int k = numnodes;
for (j = 0; j < numnodes; j++) { /* full O(N^2) comparison */
if (i == j)
continue;
if (overlaps(i, j)) {
printf("ERROR: %s and %s overlap\n",
all_nodes[i].name, all_nodes[j].name);
printf(" %s: 0x%x - 0x%x\n", all_nodes[i].name,
all_nodes[i].start, all_nodes[i].end);
printf(" %s: 0x%x - 0x%x\n", all_nodes[j].name,
all_nodes[j].start, all_nodes[j].end);
if (opt_overlap < 2) {
printf("Use more -h args to ignore"
" this error\n");
errorcnt++;
}
continue;
}
if (encloses(j, i)
&& all_nodes[j].size < all_nodes[k].size)
k = j;
}
all_nodes[i].parent = all_nodes + k;
}
if (errorcnt)
return 1;
/* Force those deadbeat parents to recognize their children */
for (i = 0; i < numnodes; i++) /* how many */
if (all_nodes[i].parent)
all_nodes[i].parent->num_children++;
for (i = 0; i < numnodes; i++) /* here they are */
if (all_nodes[i].parent)
add_child(all_nodes[i].parent, i);
/* Ready to go */
printf("# name start end size\n");
show(all_nodes + numnodes, 0, opt_gaps);
if (gapcount && !opt_gaps)
printf("\nWARNING: unused regions found. Use -H to see them\n");
return 0;
}
/* End of human-reable stuff */
/****************************************************************************/
static const char usage[] =
"\nUsage: " MYNAME " %s [OPTIONS] FLASHIMAGE [NAME...]\n\n"
"Display (and extract) the FMAP components from a BIOS image.\n"
"\n"
"Options:\n"
" -x Extract the named sections from the file\n"
" -h Use a human-readable format\n"
" -H With -h, display any gaps\n"
" -p Use a format easy to parse by scripts\n"
" -F Use the format expected by flashrom\n"
"\n"
"Specify one or more NAMEs to dump only those sections.\n"
"\n";
static void print_help(const char *name)
{
printf(usage, name);
}
static int do_dump_fmap(int argc, char *argv[])
{
int c;
int errorcnt = 0;
struct stat sb;
int fd;
const FmapHeader *fmap;
int retval = 1;
progname = argv[0];
opterr = 0; /* quiet, you */
while ((c = getopt(argc, argv, ":xpFhH")) != -1) {
switch (c) {
case 'x':
opt_extract = 1;
break;
case 'p':
opt_format = FMT_PRETTY;
break;
case 'F':
opt_format = FMT_FLASHROM;
break;
case 'H':
opt_gaps = 1;
/* fallthrough */
case 'h':
opt_format = FMT_HUMAN;
opt_overlap++;
break;
case '?':
fprintf(stderr, "%s: unrecognized switch: -%c\n",
progname, optopt);
errorcnt++;
break;
case ':':
fprintf(stderr, "%s: missing argument to -%c\n",
progname, optopt);
errorcnt++;
break;
default:
errorcnt++;
break;
}
}
if (errorcnt || optind >= argc) {
print_help(progname);
return 1;
}
if (0 != stat(argv[optind], &sb)) {
fprintf(stderr, "%s: can't stat %s: %s\n",
progname, argv[optind], strerror(errno));
return 1;
}
fd = open(argv[optind], O_RDONLY);
if (fd < 0) {
fprintf(stderr, "%s: can't open %s: %s\n",
progname, argv[optind], strerror(errno));
return 1;
}
base_of_rom =
mmap(0, sb.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
if (base_of_rom == (char *)-1) {
fprintf(stderr, "%s: can't mmap %s: %s\n",
progname, argv[optind], strerror(errno));
close(fd);
return 1;
}
close(fd); /* done with this now */
size_of_rom = sb.st_size;
fmap = fmap_find(base_of_rom, size_of_rom);
if (fmap) {
switch (opt_format) {
case FMT_HUMAN:
retval = human_fmap(fmap);
break;
case FMT_NORMAL:
printf("hit at 0x%08x\n",
(uint32_t) ((char *)fmap - (char *)base_of_rom));
/* fallthrough */
default:
retval =
dump_fmap(fmap, argc - optind - 1,
argv + optind + 1);
}
}
if (0 != munmap(base_of_rom, sb.st_size)) {
fprintf(stderr, "%s: can't munmap %s: %s\n",
progname, argv[optind], strerror(errno));
return 1;
}
return retval;
}
DECLARE_FUTIL_COMMAND(dump_fmap, do_dump_fmap,
VBOOT_VERSION_ALL,
"Display FMAP contents from a firmware image",
print_help);