summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile1
-rw-r--r--futility/cmd_load_fmap.c202
-rwxr-xr-xtests/futility/run_test_scripts.sh1
-rwxr-xr-xtests/futility/test_load_fmap.sh43
4 files changed, 247 insertions, 0 deletions
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 <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