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