| /* | |
| * Copyright 2014 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 <getopt.h> | |
| #include <inttypes.h> | |
| #include <limits.h> | |
| #include <stddef.h> | |
| #include <stdint.h> | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <sys/stat.h> | |
| #include <sys/types.h> | |
| #include <unistd.h> | |
| #include "fmap.h" | |
| #include "futility.h" | |
| static const char usage[] = "\n" | |
| "Usage: " MYNAME " %s [OPTIONS] FILE AREA:file [AREA:file ...]\n" | |
| "\n" | |
| "Replace the contents of specific FMAP areas. This is the complement\n" | |
| "of " MYNAME " dump_fmap -x FILE AREA [AREA ...]\n" | |
| "\n" | |
| "Options:\n" | |
| " -o OUTFILE Write the result to this file, instead of modifying\n" | |
| " the input file. This is safer, since there are no\n" | |
| " safeguards against doing something stupid.\n" | |
| "\n" | |
| "Example:\n" | |
| "\n" | |
| " This will clear the RO_VPD area, and scramble VBLOCK_B:\n" | |
| "\n" | |
| " " MYNAME " %s bios.bin RO_VPD:/dev/zero VBLOCK_B:/dev/urandom\n" | |
| "\n"; | |
| static void print_help(int argc, char *argv[]) | |
| { | |
| printf(usage, argv[0], argv[0]); | |
| } | |
| enum { | |
| OPT_HELP = 1000, | |
| }; | |
| static const struct option long_opts[] = { | |
| /* name hasarg *flag val */ | |
| {"help", 0, NULL, OPT_HELP}, | |
| {NULL, 0, NULL, 0}, | |
| }; | |
| static char *short_opts = ":o:"; | |
| static int copy_to_area(char *file, uint8_t *buf, uint32_t len, char *area) | |
| { | |
| FILE *fp; | |
| int retval = 0; | |
| int n; | |
| fp = fopen(file, "r"); | |
| if (!fp) { | |
| fprintf(stderr, "area %s: can't open %s for reading: %s\n", | |
| area, file, strerror(errno)); | |
| return 1; | |
| } | |
| n = fread(buf, 1, len, fp); | |
| if (n == 0) { | |
| if (feof(fp)) | |
| fprintf(stderr, "area %s: unexpected EOF on %s\n", | |
| area, file); | |
| if (ferror(fp)) | |
| fprintf(stderr, "area %s: can't read from %s: %s\n", | |
| area, file, strerror(errno)); | |
| retval = 1; | |
| } else if (n < len) { | |
| fprintf(stderr, "Warning on area %s: only read %d " | |
| "(not %d) from %s\n", area, n, len, file); | |
| } | |
| if (0 != fclose(fp)) { | |
| fprintf(stderr, "area %s: error closing %s: %s\n", | |
| area, file, strerror(errno)); | |
| retval = 1; | |
| } | |
| return retval; | |
| } | |
| static int do_load_fmap(int argc, char *argv[]) | |
| { | |
| char *infile = 0; | |
| char *outfile = 0; | |
| uint8_t *buf; | |
| uint32_t len; | |
| FmapHeader *fmap; | |
| FmapAreaHeader *ah; | |
| int errorcnt = 0; | |
| int fd, i; | |
| opterr = 0; /* quiet, you */ | |
| while ((i = getopt_long(argc, argv, short_opts, long_opts, 0)) != -1) { | |
| switch (i) { | |
| case 'o': | |
| outfile = optarg; | |
| break; | |
| case OPT_HELP: | |
| print_help(argc, argv); | |
| return !!errorcnt; | |
| case '?': | |
| if (optopt) | |
| fprintf(stderr, "Unrecognized option: -%c\n", | |
| optopt); | |
| else | |
| fprintf(stderr, "Unrecognized option\n"); | |
| errorcnt++; | |
| break; | |
| case ':': | |
| fprintf(stderr, "Missing argument to -%c\n", optopt); | |
| errorcnt++; | |
| break; | |
| default: | |
| DIE; | |
| } | |
| } | |
| if (errorcnt) { | |
| print_help(argc, argv); | |
| return 1; | |
| } | |
| if (argc - optind < 2) { | |
| fprintf(stderr, | |
| "You must specify an input file" | |
| " and at least one AREA:file argument\n"); | |
| print_help(argc, argv); | |
| return 1; | |
| } | |
| infile = argv[optind++]; | |
| /* okay, let's do it ... */ | |
| if (outfile) | |
| futil_copy_file_or_die(infile, outfile); | |
| else | |
| outfile = infile; | |
| fd = open(outfile, O_RDWR); | |
| if (fd < 0) { | |
| fprintf(stderr, "Can't open %s: %s\n", | |
| outfile, strerror(errno)); | |
| return 1; | |
| } | |
| errorcnt |= futil_map_file(fd, MAP_RW, &buf, &len); | |
| if (errorcnt) | |
| goto done_file; | |
| fmap = fmap_find(buf, len); | |
| if (!fmap) { | |
| fprintf(stderr, "Can't find an FMAP in %s\n", infile); | |
| errorcnt++; | |
| goto done_map; | |
| } | |
| for (i = optind; i < argc; i++) { | |
| char *a = argv[i]; | |
| char *f = strchr(a, ':'); | |
| if (!f || a == f || *(f+1) == '\0') { | |
| fprintf(stderr, "argument \"%s\" is bogus\n", a); | |
| errorcnt++; | |
| break; | |
| } | |
| *f++ = '\0'; | |
| uint8_t *area_buf = fmap_find_by_name(buf, len, fmap, a, &ah); | |
| if (!area_buf) { | |
| fprintf(stderr, "Can't find area \"%s\" in FMAP\n", a); | |
| errorcnt++; | |
| break; | |
| } | |
| if (0 != copy_to_area(f, area_buf, ah->area_size, a)) { | |
| errorcnt++; | |
| break; | |
| } | |
| } | |
| done_map: | |
| errorcnt |= futil_unmap_file(fd, 1, buf, len); | |
| done_file: | |
| if (0 != close(fd)) { | |
| fprintf(stderr, "Error closing %s: %s\n", | |
| outfile, strerror(errno)); | |
| errorcnt++; | |
| } | |
| return !!errorcnt; | |
| } | |
| DECLARE_FUTIL_COMMAND(load_fmap, do_load_fmap, VBOOT_VERSION_ALL, | |
| "Replace the contents of specified FMAP areas"); | |