From 2e25e813419f2cd437164929543e452b28b89260 Mon Sep 17 00:00:00 2001 From: Bill Richardson Date: Wed, 3 Sep 2014 11:34:27 -0700 Subject: futility: add load_fmap command, useful for tests This adds a "load_fmap" command, which is pretty much the opposite of the "dump_fmap -x" command. It allows you to replace the content of any FMAP areas with new stuff, without mucking around with dd. There's a test for it, too. BUG=chromium:224734 BRANCH=ToT TEST=make runtests Signed-off-by: Bill Richardson Change-Id: I5a9ab249c9e63a9bb1a9b26feeb3ed757cd294f1 Reviewed-on: https://chromium-review.googlesource.com/216228 Reviewed-by: Randall Spangler --- Makefile | 1 + futility/cmd_load_fmap.c | 202 +++++++++++++++++++++++++++++++++++++ tests/futility/run_test_scripts.sh | 1 + tests/futility/test_load_fmap.sh | 43 ++++++++ 4 files changed, 247 insertions(+) create mode 100644 futility/cmd_load_fmap.c create mode 100755 tests/futility/test_load_fmap.sh diff --git a/Makefile b/Makefile index 9782b826..783190b1 100644 --- a/Makefile +++ b/Makefile @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 -- cgit v1.2.1