diff options
-rw-r--r-- | Makefile | 1 | ||||
-rw-r--r-- | futility/cmd_load_fmap.c | 202 | ||||
-rwxr-xr-x | tests/futility/run_test_scripts.sh | 1 | ||||
-rwxr-xr-x | tests/futility/test_load_fmap.sh | 43 |
4 files changed, 247 insertions, 0 deletions
@@ -542,6 +542,7 @@ FUTIL_SRCS = \ $(FUTIL_STATIC_SRCS) \ futility/cmd_dev_sign_file.c \ futility/cmd_dump_kernel_config.c \ + futility/cmd_load_fmap.c \ futility/cmd_vbutil_firmware.c \ futility/cmd_vbutil_kernel.c \ futility/cmd_vbutil_key.c \ diff --git a/futility/cmd_load_fmap.c b/futility/cmd_load_fmap.c new file mode 100644 index 00000000..3b10aa9f --- /dev/null +++ b/futility/cmd_load_fmap.c @@ -0,0 +1,202 @@ +/* + * 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 help_and_quit(const char *prog) +{ + fprintf(stderr, usage, prog, prog); + exit(1); +} + +static const struct option long_opts[] = { + /* name hasarg *flag val */ + {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; + void *mmap_ptr = 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 '?': + 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) + help_and_quit(argv[0]); + + if (argc - optind < 2) { + fprintf(stderr, + "You must specify an input file" + " and at least one AREA:file argument\n"); + help_and_quit(argv[0]); + } + + infile = argv[optind++]; + + /* okay, let's do it ... */ + if (outfile) + 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 |= map_it(fd, 1, &mmap_ptr, &len); + if (errorcnt) + goto done_file; + buf = (uint8_t *)mmap_ptr; + + 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 |= unmap_it(fd, 1, mmap_ptr, 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, + "Replace the contents of specified FMAP areas"); diff --git a/tests/futility/run_test_scripts.sh b/tests/futility/run_test_scripts.sh index 3369bedf..a1aa24cb 100755 --- a/tests/futility/run_test_scripts.sh +++ b/tests/futility/run_test_scripts.sh @@ -42,6 +42,7 @@ export OUTDIR TESTS=" ${SCRIPTDIR}/test_main.sh ${SCRIPTDIR}/test_dump_fmap.sh +${SCRIPTDIR}/test_load_fmap.sh ${SCRIPTDIR}/test_gbb_utility.sh ${SCRIPTDIR}/test_resign_firmware.sh " diff --git a/tests/futility/test_load_fmap.sh b/tests/futility/test_load_fmap.sh new file mode 100755 index 00000000..c5e61633 --- /dev/null +++ b/tests/futility/test_load_fmap.sh @@ -0,0 +1,43 @@ +#!/bin/bash -eux +# 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. + +me=${0##*/} +TMP="$me.tmp" + +# Work in scratch directory +cd "$OUTDIR" + + +IN=${SCRIPTDIR}/data/bios_link_mp.bin +BIOS=${TMP}.bios.bin + +cp ${IN} ${BIOS} + +AREAS="RW_SECTION_A VBLOCK_B BOOT_STUB" + +# Extract good blobs first +${FUTILITY} dump_fmap -x ${BIOS} ${AREAS} + +# Save the good blobs, make same-size random blobs, create command +CMDS="" +for a in ${AREAS}; do + size=$(stat -c '%s' $a) + mv $a $a.good + dd if=/dev/urandom of=$a.rand bs=$size count=1 + CMDS="$CMDS $a:$a.rand" +done + +# Poke the new blobs in +${FUTILITY} load_fmap ${BIOS} ${CMDS} + +# Pull them back out and see if they match +${FUTILITY} dump_fmap -x ${BIOS} ${AREAS} +for a in ${AREAS}; do + cmp $a $a.rand +done + +# cleanup +rm -f ${TMP}* ${AREAS} *.rand *.good +exit 0 |