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