summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBill Richardson <wfrichar@chromium.org>2014-07-15 12:52:19 -0700
committerchrome-internal-fetch <chrome-internal-fetch@google.com>2014-07-31 22:46:27 +0000
commit6f3961507e73f90cec665896dece884e86be560a (patch)
tree0f11e2ca91c3c5be6ed30748b0d9224c6ede456a
parentb52d7b7acb14743747489ed7323b416bc001503b (diff)
downloadvboot-6f3961507e73f90cec665896dece884e86be560a.tar.gz
futility: Add remaining vboot binary utilities
This change adds these formerly external utilities into the futility binary: dev_sign_file dump_kernel_config gbb_utility vbutil_firmware vbutil_kernel These target binaries will remain independent of futility, since they are not directly related to verified boot: cgpt crossystem tpm_init_temp_fix tpmc Also, dumpRSAPublicKey is removed from the target, since it is only used on the build host to create new keypairs. This change also add several additional tests. BUG=chromium:224734 BRANCH=ToT CQ-DEPEND=CL:210391,CL:210568,CL:210587 TEST=manual make runtests make clean Also build and test: - normal image - test image - recovery image - firmware shellball Note that this CL depends on simultaneous changes to the chromeos-initramfs ebuild. Change-Id: If791b5e9b5aac218ceafa9f45fc1785f16b91a64 Signed-off-by: Bill Richardson <wfrichar@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/210403
-rw-r--r--Makefile114
-rw-r--r--futility/cmd_dev_sign_file.c (renamed from utility/dev_sign_file.c)8
-rw-r--r--futility/cmd_dump_kernel_config.c (renamed from utility/dump_kernel_config.c)8
-rw-r--r--futility/cmd_foo.c21
-rw-r--r--futility/cmd_gbb_utility.c611
-rw-r--r--futility/cmd_hey.c20
-rw-r--r--futility/cmd_vbutil_firmware.c (renamed from utility/vbutil_firmware.c)8
-rw-r--r--futility/cmd_vbutil_kernel.c (renamed from utility/vbutil_kernel.c)30
-rw-r--r--futility/dump_kernel_config_lib.c (renamed from utility/dump_kernel_config_lib.c)2
-rw-r--r--futility/futility.c112
-rw-r--r--futility/kernel_blob.h (renamed from utility/include/kernel_blob.h)2
-rw-r--r--tests/futility/binary_editor.c39
-rwxr-xr-xtests/futility/run_test_scripts.sh61
-rwxr-xr-xtests/futility/test_dump_fmap.sh13
-rwxr-xr-xtests/futility/test_gbb_utility.sh206
-rwxr-xr-xtests/futility/test_main.sh40
-rw-r--r--utility/gbb_utility.cc4
17 files changed, 1010 insertions, 289 deletions
diff --git a/Makefile b/Makefile
index d7742f45..4a481036 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+# Copyright 2013 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.
@@ -43,23 +43,17 @@ export BUILD
# Stuff for 'make install'
INSTALL = install
DESTDIR = /usr/local/bin
-OLDDIR = old_bins
# Where exactly do the pieces go?
-# FT_DIR = futility target directory - where it will be on the target
-# F_DIR = futility install directory - where it gets put right now
-# UB_DIR = userspace binary directory for futility's exec() targets
-# VB_DIR = target vboot directory - for dev-mode-only helpers, keys, etc.
+# UB_DIR = utility binary directory
+# VB_DIR = vboot binary directory for dev-mode-only scripts
ifeq (${MINIMAL},)
-# Host install just puts everything in one place
-FT_DIR=${DESTDIR}
-F_DIR=${DESTDIR}
-UB_DIR=${DESTDIR}/${OLDDIR}
+# Host install just puts everything where it's told
+UB_DIR=${DESTDIR}
+VB_DIR=${DESTDIR}
else
-# Target install puts things into DESTDIR subdirectories
-FT_DIR=/usr/bin
-F_DIR=${DESTDIR}${FT_DIR}
-UB_DIR=${F_DIR}/${OLDDIR}
+# Target install puts things into subdirectories under DESTDIR
+UB_DIR=${DESTDIR}/usr/bin
VB_DIR=${DESTDIR}/usr/share/vboot/bin
endif
@@ -143,10 +137,6 @@ CC ?= gcc
CFLAGS += -DCHROMEOS_ENVIRONMENT -Wall -Werror ${DEBUG_FLAGS}
endif
-ifneq (${OLDDIR},)
-CFLAGS += -DOLDDIR=${OLDDIR}
-endif
-
ifneq (${DEBUG},)
CFLAGS += -DVBOOT_DEBUG
endif
@@ -365,6 +355,7 @@ UTILLIB_SRCS = \
cgpt/flash_ts.c \
cgpt/flash_ts_drv.c \
firmware/lib/cgptlib/mtdlib.c \
+ futility/dump_kernel_config_lib.c \
host/arch/${ARCH}/lib/crossystem_arch.c \
host/lib/crossystem.c \
host/lib/file_keys.c \
@@ -375,8 +366,7 @@ UTILLIB_SRCS = \
host/lib/host_misc.c \
host/lib/util_misc.c \
host/lib/host_signature.c \
- host/lib/signature_digest.c \
- utility/dump_kernel_config_lib.c
+ host/lib/signature_digest.c
UTILLIB_OBJS = ${UTILLIB_SRCS:%.c=${BUILD}/%.o}
ALL_OBJS += ${UTILLIB_OBJS}
@@ -403,10 +393,10 @@ HOSTLIB_SRCS = \
firmware/stub/tpm_lite_stub.c \
firmware/stub/utility_stub.c \
firmware/stub/vboot_api_stub_init.c \
+ futility/dump_kernel_config_lib.c \
host/arch/${ARCH}/lib/crossystem_arch.c \
host/lib/crossystem.c \
- host/lib/host_misc.c \
- utility/dump_kernel_config_lib.c
+ host/lib/host_misc.c
HOSTLIB_OBJS = ${HOSTLIB_SRCS:%.c=${BUILD}/%.o}
ALL_OBJS += ${HOSTLIB_OBJS}
@@ -429,7 +419,7 @@ TINYHOSTLIB_SRCS = \
firmware/lib/cgptlib/mtdlib.c \
firmware/lib/utility_string.c \
firmware/stub/utility_stub.c \
- utility/dump_kernel_config_lib.c
+ futility/dump_kernel_config_lib.c
TINYHOSTLIB_OBJS = ${TINYHOSTLIB_SRCS:%.c=${BUILD}/%.o}
@@ -477,22 +467,18 @@ endif
# These utilities should be linked statically.
UTIL_NAMES_STATIC = \
- utility/crossystem \
- utility/gbb_utility
+ utility/crossystem
UTIL_NAMES = ${UTIL_NAMES_STATIC} \
- utility/dev_sign_file \
- utility/dump_kernel_config \
- utility/dumpRSAPublicKey \
utility/tpm_init_temp_fix \
- utility/tpmc \
- utility/vbutil_firmware \
- utility/vbutil_kernel
+ utility/tpmc
+# TODO: Do we still need eficompress and efidecompress for anything?
ifeq (${MINIMAL},)
UTIL_NAMES += \
utility/bmpblk_font \
utility/bmpblk_utility \
+ utility/dumpRSAPublicKey \
utility/eficompress \
utility/efidecompress \
utility/load_kernel_test \
@@ -533,50 +519,32 @@ FUTIL_BIN = ${BUILD}/futility/futility
# But we still need both static (tiny) and dynamic (with openssl) versions.
FUTIL_STATIC_BIN = ${FUTIL_BIN}_s
-# These are the executables to be replaced with symlinks.
-FUTIL_OLD = \
- bmpblk_font \
- bmpblk_utility \
- cgpt \
- chromeos-tpm-recovery \
- crossystem \
- dev_debug_vboot \
- dev_make_keypair \
+# These are the executables that are now built in to futility. We'll create
+# symlinks for these so the old names will still work.
+# TODO: Do we still need dev_sign_file for anything?
+FUTIL_BUILTIN = \
dev_sign_file \
- dumpRSAPublicKey \
dump_fmap \
dump_kernel_config \
- eficompress \
- efidecompress \
- enable_dev_usb_boot \
gbb_utility \
- load_kernel_test \
- make_dev_firmware.sh \
- make_dev_ssd.sh \
- pad_digest_utility \
- resign_firmwarefd.sh \
- set_gbb_flags.sh \
- signature_digest_utility \
- tpm-nvsize \
- tpm_init_temp_fix \
- tpmc \
vbutil_firmware \
vbutil_kernel \
vbutil_key \
- vbutil_keyblock \
- vbutil_what_keys \
- verify_data
+ vbutil_keyblock
FUTIL_STATIC_SRCS = \
futility/futility.c \
futility/cmd_dump_fmap.c \
- futility/cmd_foo.c
+ futility/cmd_gbb_utility.c
FUTIL_SRCS = \
$(FUTIL_STATIC_SRCS) \
+ futility/cmd_dev_sign_file.c \
+ futility/cmd_dump_kernel_config.c \
+ futility/cmd_vbutil_firmware.c \
+ futility/cmd_vbutil_kernel.c \
futility/cmd_vbutil_key.c \
- futility/cmd_vbutil_keyblock.c \
- futility/cmd_hey.c
+ futility/cmd_vbutil_keyblock.c
FUTIL_LDS = futility/futility.lds
@@ -628,6 +596,7 @@ TEST_NAMES = \
tests/vboot_firmware_tests \
tests/vboot_kernel_tests \
tests/vboot_nvstorage_test \
+ tests/futility/binary_editor \
tests/futility/test_not_really
ifdef REGION_READ
@@ -906,15 +875,10 @@ utils_install: ${UTIL_BINS} ${UTIL_SCRIPTS}
.PHONY: signing_install
signing_install: ${SIGNING_SCRIPTS} ${SIGNING_SCRIPTS_DEV} ${SIGNING_COMMON}
@$(PRINTF) " INSTALL SIGNING\n"
- ${Q}mkdir -p ${UB_DIR}
+ ${Q}mkdir -p ${UB_DIR} ${VB_DIR}
${Q}${INSTALL} -t ${UB_DIR} ${SIGNING_SCRIPTS}
- ${Q}${INSTALL} -t ${UB_DIR} ${SIGNING_SCRIPTS_DEV}
- ${Q}${INSTALL} -t ${UB_DIR} -m 'u=rw,go=r,a-s' ${SIGNING_COMMON}
-ifneq (${VB_DIR},)
- ${Q}mkdir -p ${VB_DIR}
- ${Q}for prog in $(notdir ${SIGNING_SCRIPTS_DEV}); do \
- ln -sf "${FT_DIR}/futility" "${VB_DIR}/$$prog"; done
-endif
+ ${Q}${INSTALL} -t ${VB_DIR} ${SIGNING_SCRIPTS_DEV}
+ ${Q}${INSTALL} -t ${VB_DIR} -m 'u=rw,go=r,a-s' ${SIGNING_COMMON}
# ----------------------------------------------------------------------------
# new Firmware Utility
@@ -934,10 +898,10 @@ ${FUTIL_BIN}: ${FUTIL_LDS} ${FUTIL_OBJS} ${UTILLIB}
.PHONY: futil_install
futil_install: ${FUTIL_BIN}
@$(PRINTF) " INSTALL futility\n"
- ${Q}mkdir -p ${F_DIR}
- ${Q}${INSTALL} -t ${F_DIR} ${FUTIL_BIN} ${FUTIL_STATIC_BIN}
- ${Q}for prog in ${FUTIL_OLD}; do \
- ln -sf futility "${F_DIR}/$$prog"; done
+ ${Q}mkdir -p ${UB_DIR}
+ ${Q}${INSTALL} -t ${UB_DIR} ${FUTIL_BIN} ${FUTIL_STATIC_BIN}
+ ${Q}for prog in ${FUTIL_BUILTIN}; do \
+ ln -sf futility "${UB_DIR}/$$prog"; done
# ----------------------------------------------------------------------------
# Utility to generate TLCL structure definition header file.
@@ -1004,9 +968,6 @@ ${BUILD}/%.o: %.cc
# ----------------------------------------------------------------------------
# Here are the special tweaks to the generic rules.
-# GBB utility needs C++ linker. TODO: It shouldn't.
-${BUILD}/utility/gbb_utility: LD = ${CXX}
-
# Because we play some clever linker script games to add new commands without
# changing any header files, futility must be linked with ld.bfd, not gold.
${FUTIL_BIN}: LDFLAGS += -fuse-ld=bfd
@@ -1018,9 +979,6 @@ CRYPTO_LIBS := $(shell ${PKG_CONFIG} --libs libcrypto)
${BUILD}/utility/dumpRSAPublicKey: LDLIBS += ${CRYPTO_LIBS}
${BUILD}/utility/pad_digest_utility: LDLIBS += ${CRYPTO_LIBS}
${BUILD}/utility/signature_digest_utility: LDLIBS += ${CRYPTO_LIBS}
-${BUILD}/utility/dev_sign_file: LDLIBS += ${CRYPTO_LIBS}
-${BUILD}/utility/vbutil_firmware: LDLIBS += ${CRYPTO_LIBS}
-${BUILD}/utility/vbutil_kernel: LDLIBS += ${CRYPTO_LIBS}
${BUILD}/host/linktest/main: LDLIBS += ${CRYPTO_LIBS}
${BUILD}/tests/vb2_common2_tests: LDLIBS += ${CRYPTO_LIBS}
diff --git a/utility/dev_sign_file.c b/futility/cmd_dev_sign_file.c
index b20423ff..3b2b640b 100644
--- a/utility/dev_sign_file.c
+++ b/futility/cmd_dev_sign_file.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+/* Copyright 2011 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.
*
@@ -18,6 +18,7 @@
#include <unistd.h>
#include "cryptolib.h"
+#include "futility.h"
#include "host_common.h"
#include "kernel_blob.h"
#include "vboot_common.h"
@@ -273,7 +274,7 @@ static int Verify(const char* filename, const char* vblock_file,
}
-int main(int argc, char* argv[]) {
+int do_dev_sign_file(int argc, char* argv[]) {
char* filename = NULL;
char* keyblock_file = NULL;
char* signprivate_file = NULL;
@@ -353,3 +354,6 @@ int main(int argc, char* argv[]) {
/* NOTREACHED */
return 1;
}
+
+DECLARE_FUTIL_COMMAND(dev_sign_file, do_dev_sign_file,
+ "Sign or verify dev-mode files (DEPRECATED)");
diff --git a/utility/dump_kernel_config.c b/futility/cmd_dump_kernel_config.c
index ec0ee5ab..2d15f99f 100644
--- a/utility/dump_kernel_config.c
+++ b/futility/cmd_dump_kernel_config.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+/* Copyright 2012 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.
*
@@ -11,6 +11,7 @@
#include <sys/mman.h>
#include <unistd.h>
+#include "futility.h"
#include "vboot_host.h"
enum {
@@ -33,7 +34,7 @@ static int PrintHelp(void) {
return 1;
}
-int main(int argc, char* argv[]) {
+int do_dump_kernel_config(int argc, char* argv[]) {
char *infile = NULL;
char *config = NULL;
uint64_t kernel_body_load_address = USE_PREAMBLE_LOAD_ADDR;
@@ -87,3 +88,6 @@ int main(int argc, char* argv[]) {
free(config);
return 0;
}
+
+DECLARE_FUTIL_COMMAND(dump_kernel_config, do_dump_kernel_config,
+ "Prints the kernel command line");
diff --git a/futility/cmd_foo.c b/futility/cmd_foo.c
deleted file mode 100644
index e008b3a0..00000000
--- a/futility/cmd_foo.c
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright (c) 2013 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 <stdio.h>
-
-#include "futility.h"
-
-static int do_something(int argc, char *argv[])
-{
- int i;
- printf("this is %s\n", __func__);
- for (i = 0; i < argc; i++)
- printf("argv[%d] = %s\n", i, argv[i]);
- return 0;
-}
-
-DECLARE_FUTIL_COMMAND(foo, do_something, "invoke a foo");
-DECLARE_FUTIL_COMMAND(bar, do_something, "go to bar");
diff --git a/futility/cmd_gbb_utility.c b/futility/cmd_gbb_utility.c
new file mode 100644
index 00000000..a8554562
--- /dev/null
+++ b/futility/cmd_gbb_utility.c
@@ -0,0 +1,611 @@
+/*
+ * 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 <getopt.h>
+#include <inttypes.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 "futility.h"
+#include "gbb_header.h"
+
+static void help_and_quit(const char *prog) {
+ fprintf(stderr, "\n"
+ "Usage: %s [-g|-s|-c] [OPTIONS] bios_file [output_file]\n"
+ "\n"
+ "GET MODE:\n"
+ "-g, --get (default)\tGet (read) from bios_file, "
+ "with following options:\n"
+ " --hwid \tReport hardware id (default).\n"
+ " --flags \tReport header flags.\n"
+ " -k, --rootkey=FILE \tFile name to export Root Key.\n"
+ " -b, --bmpfv=FILE \tFile name to export Bitmap FV.\n"
+ " -r --recoverykey=FILE\tFile name to export Recovery Key.\n"
+ "\n"
+ "SET MODE:\n"
+ "-s, --set \tSet (write) to bios_file, "
+ "with following options:\n"
+ " -o, --output=FILE \tNew file name for ouptput.\n"
+ " --hwid=HWID \tThe new hardware id to be changed.\n"
+ " --flags=FLAGS \tThe new (numeric) flags value.\n"
+ " -k, --rootkey=FILE \tFile name of new Root Key.\n"
+ " -b, --bmpfv=FILE \tFile name of new Bitmap FV.\n"
+ " -r --recoverykey=FILE\tFile name of new Recovery Key.\n"
+ "\n"
+ "CREATE MODE:\n"
+ "-c, --create=hwid_size,rootkey_size,bmpfv_size,recoverykey_size\n"
+ " \tCreate a GBB blob by given size list.\n"
+ "SAMPLE:\n"
+ " %s -g bios.bin\n"
+ " %s --set --hwid='New Model' -k key.bin bios.bin newbios.bin\n"
+ " %s -c 0x100,0x1000,0x03DE80,0x1000 gbb.blob\n\n",
+ prog, prog, prog, prog);
+ exit(1);
+}
+
+/* Command line options */
+static struct option long_opts[] = {
+ /* name hasarg *flag val */
+ {"get", 0, NULL, 'g' },
+ {"set", 0, NULL, 's' },
+ {"create", 1, NULL, 'c' },
+ {"output", 1, NULL, 'o' },
+ {"rootkey", 1, NULL, 'k' },
+ {"bmpfv", 1, NULL, 'b' },
+ {"recoverykey", 1, NULL, 'R' },
+ {"hwid", 2, NULL, 'i' },
+ {"flags", 2, NULL, 'L' },
+ { NULL, 0, NULL, 0 },
+};
+static char *short_opts = ":gsc:o:k:b:R:r:h:i:L:f:";
+
+static int errorcnt;
+
+static int ValidGBB(GoogleBinaryBlockHeader *gbb, size_t maxlen)
+{
+ uint32_t i;
+ char *s;
+
+ if (gbb->major_version != GBB_MAJOR_VER)
+ goto bad;
+ if (gbb->header_size != GBB_HEADER_SIZE || gbb->header_size > maxlen)
+ goto bad;
+ if (gbb->hwid_offset < GBB_HEADER_SIZE)
+ goto bad;
+ if (gbb->hwid_offset + gbb->hwid_size > maxlen)
+ goto bad;
+ if (gbb->hwid_size) {
+ /* Make sure the HWID is null-terminated (assumes ASCII, not unicode). */
+ s = (char *)((char *)gbb + gbb->hwid_offset);
+ for (i = 0; i < gbb->hwid_size; i++)
+ if (*s++ == '\0')
+ break;
+ if (i >= gbb->hwid_size)
+ goto bad;
+ }
+ if (gbb->rootkey_offset < GBB_HEADER_SIZE)
+ goto bad;
+ if (gbb->rootkey_offset + gbb->rootkey_size > maxlen)
+ goto bad;
+ if (gbb->bmpfv_offset < GBB_HEADER_SIZE)
+ goto bad;
+ if (gbb->bmpfv_offset + gbb->bmpfv_size > maxlen)
+ goto bad;
+ if (gbb->recovery_key_offset < GBB_HEADER_SIZE)
+ goto bad;
+ if (gbb->recovery_key_offset + gbb->recovery_key_size > maxlen)
+ goto bad;
+
+ return 1;
+
+bad:
+ errorcnt++;
+ return 0;
+}
+
+#define GBB_SEARCH_STRIDE 4
+GoogleBinaryBlockHeader *FindGbbHeader(uint8_t *ptr, size_t size)
+{
+ size_t i;
+ GoogleBinaryBlockHeader *tmp, *gbb_header = NULL;
+ int count = 0;
+
+ for (i = 0;
+ i <= size - GBB_SEARCH_STRIDE;
+ i += GBB_SEARCH_STRIDE) {
+ if (0 != memcmp(ptr + i, GBB_SIGNATURE, GBB_SIGNATURE_SIZE))
+ continue;
+
+ /* Found something. See if it's any good. */
+ tmp = (GoogleBinaryBlockHeader *)(ptr + i);
+ if (ValidGBB(tmp, size - i))
+ if (!count++)
+ gbb_header = tmp;
+ }
+
+ switch (count) {
+ case 0:
+ errorcnt++;
+ return NULL;
+ case 1:
+ return gbb_header;
+ default:
+ fprintf(stderr, "ERROR: multiple GBB headers found\n");
+ errorcnt++;
+ return NULL;
+ }
+}
+
+static uint8_t *create_gbb(const char *desc, off_t *sizeptr)
+{
+ char *str, *sizes, *param, *e = NULL;
+ size_t size = GBB_HEADER_SIZE;
+ int i = 0;
+ /* Danger Will Robinson! four entries ==> four paramater blocks */
+ uint32_t val[] = {0, 0, 0, 0};
+ uint8_t *buf;
+ GoogleBinaryBlockHeader *gbb;
+
+ sizes = strdup(desc);
+ if (!sizes) {
+ errorcnt++;
+ fprintf(stderr, "ERROR: strdup() failed: %s\n", strerror(errno));
+ return NULL;
+ }
+
+ for (str = sizes; (param = strtok(str, ", ")) != NULL; str = NULL) {
+ val[i] = (uint32_t)strtoul(param, &e, 0);
+ if (e && *e) {
+ errorcnt++;
+ fprintf(stderr, "ERROR: invalid creation parameter: \"%s\"\n", param);
+ free(sizes);
+ return NULL;
+ }
+ size += val[i++];
+ if (i > ARRAY_SIZE(val))
+ break;
+ }
+
+ buf = (uint8_t *)calloc(1, size);
+ if (!buf) {
+ errorcnt++;
+ fprintf(stderr, "ERROR: can't malloc %zu bytes: %s\n",
+ size, strerror(errno));
+ free(sizes);
+ return NULL;
+ } else if (sizeptr) {
+ *sizeptr = size;
+ }
+
+ gbb = (GoogleBinaryBlockHeader *)buf;
+ memcpy(gbb->signature, GBB_SIGNATURE, GBB_SIGNATURE_SIZE);
+ gbb->major_version = GBB_MAJOR_VER;
+ gbb->minor_version = GBB_MINOR_VER;
+ gbb->header_size = GBB_HEADER_SIZE;
+ gbb->flags = 0;
+
+ i = GBB_HEADER_SIZE;
+ gbb->hwid_offset = i;
+ gbb->hwid_size = val[0];
+ i += val[0];
+
+ gbb->rootkey_offset = i;
+ gbb->rootkey_size = val[1];
+ i += val[1];
+
+ gbb->bmpfv_offset = i;
+ gbb->bmpfv_size = val[2];
+ i += val[2];
+
+ gbb->recovery_key_offset = i;
+ gbb->recovery_key_size = val[3];
+ i += val[1];
+
+ free(sizes);
+ return buf;
+}
+
+uint8_t *read_entire_file(const char *filename, off_t *sizeptr)
+{
+ FILE *fp = NULL;
+ uint8_t *buf = NULL;
+ struct stat sb;
+
+ fp = fopen(filename, "rb");
+ if (!fp) {
+ fprintf(stderr, "ERROR: Unable to open %s for reading: %s\n",
+ filename, strerror(errno));
+ goto fail;
+ }
+
+ if (0 != fstat(fileno(fp), &sb)) {
+ fprintf(stderr, "ERROR: can't fstat %s: %s\n",
+ filename, strerror(errno));
+ goto fail;
+ }
+ if (sizeptr)
+ *sizeptr = sb.st_size;
+
+ buf = (uint8_t *)malloc(sb.st_size);
+ if (!buf) {
+ fprintf(stderr, "ERROR: can't malloc %" PRIi64 " bytes: %s\n",
+ sb.st_size, strerror(errno));
+ goto fail;
+ }
+
+ if (1 != fread(buf, sb.st_size, 1, fp)) {
+ fprintf(stderr, "ERROR: Unable to read from %s: %s\n",
+ filename, strerror(errno));
+ goto fail;
+ }
+
+ if (fp && 0 != fclose(fp)) {
+ fprintf(stderr, "ERROR: Unable to close %s: %s\n",
+ filename, strerror(errno));
+ goto fail;
+ }
+
+ return buf;
+
+fail:
+ errorcnt++;
+
+ if (buf)
+ free(buf);
+
+ if (fp && 0 != fclose(fp))
+ fprintf(stderr, "ERROR: Unable to close %s: %s\n",
+ filename, strerror(errno));
+ return NULL;
+}
+
+static int write_to_file(const char *msg, const char *filename,
+ uint8_t *start, size_t size)
+{
+ FILE *fp;
+ int r = 0;
+
+ fp = fopen(filename, "wb");
+ if (!fp) {
+ fprintf(stderr, "ERROR: Unable to open %s for writing: %s\n",
+ filename, strerror(errno));
+ errorcnt++;
+ return errno;
+ }
+
+ /* Don't write zero bytes */
+ if (size && 1 != fwrite(start, size, 1, fp)) {
+ fprintf(stderr, "ERROR: Unable to write to %s: %s\n",
+ filename, strerror(errno));
+ errorcnt++;
+ r = errno;
+ }
+
+ if (0 != fclose(fp)) {
+ fprintf(stderr, "ERROR: Unable to close %s: %s\n",
+ filename, strerror(errno));
+ errorcnt++;
+ if (!r)
+ r = errno;
+ }
+
+ if (!r && msg)
+ printf("%s %s\n", msg, filename);
+
+ return r;
+}
+
+static int read_from_file(const char *msg, const char *filename,
+ uint8_t *start, uint32_t size)
+{
+ FILE *fp;
+ struct stat sb;
+ size_t count;
+ int r = 0;
+
+ fp = fopen(filename, "rb");
+ if (!fp) {
+ fprintf(stderr, "ERROR: Unable to open %s for reading: %s\n",
+ filename, strerror(errno));
+ errorcnt++;
+ return errno;
+ }
+
+ if (0 != fstat(fileno(fp), &sb)) {
+ fprintf(stderr, "ERROR: can't fstat %s: %s\n",
+ filename, strerror(errno));
+ errorcnt++;
+ r = errno;
+ goto done_close;
+ }
+
+ if (sb.st_size > size) {
+ fprintf(stderr, "ERROR: file %s exceeds capacity (%" PRIu32 ")\n",
+ filename, size);
+ errorcnt++;
+ r = errno;
+ goto done_close;
+ }
+
+ /* It's okay if we read less than size. That's just the max. */
+ count = fread(start, 1, size, fp);
+ if (ferror(fp)) {
+ fprintf(stderr, "ERROR: Read %zu/%" PRIi64 " bytes from %s: %s\n",
+ count, sb.st_size, filename, strerror(errno));
+ errorcnt++;
+ r = errno;
+ }
+
+done_close:
+ if (0 != fclose(fp)) {
+ fprintf(stderr, "ERROR: Unable to close %s: %s\n",
+ filename, strerror(errno));
+ errorcnt++;
+ if (!r)
+ r = errno;
+ }
+
+ if (!r && msg)
+ printf(" - import %s from %s: success\n", msg, filename);
+
+ return r;
+}
+
+static int do_gbb_utility(int argc, char *argv[])
+{
+ enum do_what_now {DO_GET, DO_SET, DO_CREATE} mode = DO_GET;
+ char *infile = NULL;
+ char *outfile = NULL;
+ char *opt_create = NULL;
+ char *opt_rootkey = NULL;
+ char *opt_bmpfv = NULL;
+ char *opt_recoverykey = NULL;
+ char *opt_hwid = NULL;
+ char *opt_flags = NULL;
+ int sel_hwid = 0;
+ int sel_flags = 0;
+ uint8_t *inbuf = NULL;
+ off_t filesize;
+ uint8_t *outbuf = NULL;
+ GoogleBinaryBlockHeader *gbb;
+ uint8_t *gbb_base;
+ int i;
+
+ opterr = 0; /* quiet, you */
+ while ((i = getopt_long(argc, argv, short_opts, long_opts, 0)) != -1) {
+ switch (i) {
+ case 'g':
+ mode = DO_GET;
+ break;
+ case 's':
+ mode = DO_SET;
+ break;
+ case 'c':
+ mode = DO_CREATE;
+ opt_create = optarg;
+ break;
+ case 'o':
+ outfile = optarg;
+ break;
+ case 'k':
+ opt_rootkey = optarg;
+ break;
+ case 'b':
+ opt_bmpfv = optarg;
+ break;
+ case 'R': case 'r':
+ opt_recoverykey = optarg;
+ break;
+ case 'i': case 'h':
+ /* --hwid is optional: might be null, which could be okay */
+ opt_hwid = optarg;
+ sel_hwid = 1;
+ break;
+ case 'L': case 'f':
+ /* --flags is optional: might be null, which could be okay */
+ opt_flags = optarg;
+ sel_flags = 1;
+ break;
+ case '?':
+ errorcnt++;
+ if (optopt)
+ fprintf(stderr, "ERROR: unrecognized option: -%c\n", optopt);
+ else if (argv[optind - 1] )
+ fprintf(stderr, "ERROR: unrecognized option (possibly \"%s\")\n",
+ argv[optind - 1]);
+ else
+ fprintf(stderr, "ERROR: unrecognized option\n");
+ break;
+ case ':':
+ errorcnt++;
+ if (argv[optind - 1])
+ fprintf(stderr, "ERROR: missing argument to -%c (%s)\n",
+ optopt, argv[optind - 1]);
+ else
+ fprintf(stderr, "ERROR: missing argument to -%c\n", optopt);
+ break;
+ default:
+ errorcnt++;
+ fprintf(stderr, "ERROR: unexpected error while parsing options\n");
+ }
+ }
+
+ /* Problems? */
+ if (errorcnt)
+ help_and_quit(argv[0]);
+
+ /* Now try to do something */
+ switch (mode) {
+ case DO_GET:
+ if (argc - optind < 1) {
+ fprintf(stderr, "\nERROR: missing input filename\n");
+ help_and_quit(argv[0]);
+ } else {
+ infile = argv[optind++];
+ }
+
+ /* With no args, show the HWID */
+ if (!opt_rootkey && !opt_bmpfv && !opt_recoverykey && !sel_flags)
+ sel_hwid = 1;
+
+ inbuf = read_entire_file(infile, &filesize);
+ if (!inbuf)
+ break;
+
+ gbb = FindGbbHeader(inbuf, filesize);
+ if (!gbb) {
+ fprintf(stderr, "ERROR: No GBB found in %s\n", infile);
+ break;
+ }
+ gbb_base = (uint8_t *)gbb;
+
+ /* Get the stuff */
+ if (sel_hwid)
+ printf("hardware_id: %s\n",
+ gbb->hwid_size ? (char *)(gbb_base + gbb->hwid_offset) : "");
+ if (sel_flags)
+ printf("flags: 0x%08x\n", gbb->flags);
+ if (opt_rootkey)
+ write_to_file(" - exported root_key to file:", opt_rootkey,
+ gbb_base + gbb->rootkey_offset,
+ gbb->rootkey_size);
+ if (opt_bmpfv)
+ write_to_file(" - exported bmp_fv to file:", opt_bmpfv,
+ gbb_base + gbb->bmpfv_offset,
+ gbb->bmpfv_size);
+ if (opt_recoverykey)
+ write_to_file(" - exported recovery_key to file:", opt_recoverykey,
+ gbb_base + gbb->recovery_key_offset,
+ gbb->recovery_key_size);
+ break;
+
+ case DO_SET:
+ if (argc - optind < 1) {
+ fprintf(stderr, "\nERROR: missing input filename\n");
+ help_and_quit(argv[0]);
+ }
+ infile = argv[optind++];
+ if (!outfile)
+ outfile = (argc - optind < 1) ? infile : argv[optind++];
+
+ if (sel_hwid && !opt_hwid) {
+ fprintf(stderr, "\nERROR: missing new HWID value\n");
+ help_and_quit(argv[0]);
+ }
+ if (sel_flags && (!opt_flags || !*opt_flags)) {
+ fprintf(stderr, "\nERROR: missing new flags value\n");
+ help_and_quit(argv[0]);
+ }
+
+ /* With no args, we'll either copy it unchanged or do nothing */
+ inbuf = read_entire_file(infile, &filesize);
+ if (!inbuf)
+ break;
+
+ gbb = FindGbbHeader(inbuf, filesize);
+ if (!gbb) {
+ fprintf(stderr, "ERROR: No GBB found in %s\n", infile);
+ break;
+ }
+ gbb_base = (uint8_t *)gbb;
+
+ outbuf = (uint8_t *)malloc(filesize);
+ if (!outbuf) {
+ errorcnt++;
+ fprintf(stderr, "ERROR: can't malloc %" PRIi64 " bytes: %s\n",
+ filesize, strerror(errno));
+ break;
+ }
+
+ /* Switch pointers to outbuf */
+ memcpy(outbuf, inbuf, filesize);
+ gbb = FindGbbHeader(outbuf, filesize);
+ if (!gbb) {
+ fprintf(stderr, "INTERNAL ERROR: No GBB found in outbuf\n");
+ exit(1);
+ }
+ gbb_base = (uint8_t *)gbb;
+
+ if (opt_hwid) {
+ if (strlen(opt_hwid) + 1 > gbb->hwid_size) {
+ fprintf(stderr,
+ "ERROR: null-terminated HWID exceeds capacity (%d)\n",
+ gbb->hwid_size);
+ errorcnt++;
+ } else {
+ strcpy((char *)(gbb_base + gbb->hwid_offset), opt_hwid);
+ }
+ }
+
+ if (opt_flags) {
+ char *e = NULL;
+ uint32_t val;
+ val = (uint32_t)strtoul(opt_flags, &e, 0);
+ if (e && *e) {
+ fprintf(stderr, "ERROR: invalid flags value: %s\n", opt_flags);
+ errorcnt++;
+ } else {
+ gbb->flags = val;
+ }
+ }
+
+ if (opt_rootkey)
+ read_from_file("root_key", opt_rootkey,
+ gbb_base + gbb->rootkey_offset,
+ gbb->rootkey_size);
+ if (opt_bmpfv)
+ read_from_file("bmp_fv", opt_bmpfv,
+ gbb_base + gbb->bmpfv_offset,
+ gbb->bmpfv_size);
+ if (opt_recoverykey)
+ read_from_file("recovery_key", opt_recoverykey,
+ gbb_base + gbb->recovery_key_offset,
+ gbb->recovery_key_size);
+
+ /* Write it out if there are no problems. */
+ if (!errorcnt)
+ write_to_file("successfully saved new image to:",
+ outfile, outbuf, filesize);
+
+ break;
+
+ case DO_CREATE:
+ if (!outfile) {
+ if (argc - optind < 1) {
+ fprintf(stderr, "\nERROR: missing output filename\n");
+ help_and_quit(argv[0]);
+ }
+ outfile = argv[optind++];
+ }
+ /* Parse the creation args */
+ outbuf = create_gbb(opt_create, &filesize);
+ if (!outbuf) {
+ fprintf(stderr,
+ "\nERROR: unable to parse creation spec (%s)\n", opt_create);
+ help_and_quit(argv[0]);
+ }
+ if (!errorcnt)
+ write_to_file("successfully created new GBB to:",
+ outfile, outbuf, filesize);
+ break;
+ }
+
+
+ if (inbuf)
+ free(inbuf);
+ if (outbuf)
+ free(outbuf);
+ return !!errorcnt;
+}
+
+DECLARE_FUTIL_COMMAND(gbb_utility, do_gbb_utility,
+ "Utility to manage Google Binary Block (GBB)");
diff --git a/futility/cmd_hey.c b/futility/cmd_hey.c
deleted file mode 100644
index efd47d39..00000000
--- a/futility/cmd_hey.c
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (c) 2013 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 <stdio.h>
-
-#include "futility.h"
-
-static int do_something(int argc, char *argv[])
-{
- int i;
- printf("this is %s\n", __func__);
- for (i = 0; i < argc; i++)
- printf("argv[%d] = %s\n", i, argv[i]);
- return 0;
-}
-
-DECLARE_FUTIL_COMMAND(hey, do_something, "shout");
diff --git a/utility/vbutil_firmware.c b/futility/cmd_vbutil_firmware.c
index f11d7b54..b58fa2be 100644
--- a/utility/vbutil_firmware.c
+++ b/futility/cmd_vbutil_firmware.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+/* Copyright 2011 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.
*
@@ -13,6 +13,7 @@
#include <unistd.h>
#include "cryptolib.h"
+#include "futility.h"
#include "host_common.h"
#include "kernel_blob.h"
#include "util_misc.h"
@@ -290,7 +291,7 @@ static int Verify(const char* infile, const char* signpubkey,
}
-int main(int argc, char* argv[]) {
+int do_vbutil_firmware(int argc, char* argv[]) {
char* filename = NULL;
char* key_block_file = NULL;
@@ -371,3 +372,6 @@ int main(int argc, char* argv[]) {
return PrintHelp();
}
}
+
+DECLARE_FUTIL_COMMAND(vbutil_firmware, do_vbutil_firmware,
+ "Verified boot firmware utility");
diff --git a/utility/vbutil_kernel.c b/futility/cmd_vbutil_kernel.c
index 5fed77ac..d62f72dc 100644
--- a/utility/vbutil_kernel.c
+++ b/futility/cmd_vbutil_kernel.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+/* Copyright 2012 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.
*
@@ -21,6 +21,7 @@
#include <unistd.h>
#include "cryptolib.h"
+#include "futility.h"
#include "host_common.h"
#include "kernel_blob.h"
#include "util_misc.h"
@@ -211,21 +212,21 @@ static unsigned int find_cmdline_start(char *input, unsigned int max_len) {
/* Here are globals containing all the bits & pieces I'm working on. */
/* The individual parts that go into the kernel blob */
-uint8_t *g_kernel_data;
-uint64_t g_kernel_size;
-uint8_t *g_param_data;
-uint64_t g_param_size;
-uint8_t *g_config_data;
-uint64_t g_config_size;
-uint8_t *g_bootloader_data;
-uint64_t g_bootloader_size;
-uint64_t g_bootloader_address;
+static uint8_t *g_kernel_data;
+static uint64_t g_kernel_size;
+static uint8_t *g_param_data;
+static uint64_t g_param_size;
+static uint8_t *g_config_data;
+static uint64_t g_config_size;
+static uint8_t *g_bootloader_data;
+static uint64_t g_bootloader_size;
+static uint64_t g_bootloader_address;
/* The individual parts of the verification blob (including the data that
* immediately follows the headers) */
-VbKeyBlockHeader* g_keyblock;
-VbKernelPreambleHeader* g_preamble;
+static VbKeyBlockHeader* g_keyblock;
+static VbKernelPreambleHeader* g_preamble;
/****************************************************************************/
@@ -695,7 +696,7 @@ static int Verify(uint8_t* kernel_blob,
/****************************************************************************/
-int main(int argc, char* argv[]) {
+int do_vbutil_kernel(int argc, char* argv[]) {
char* filename = NULL;
char* oldfile = NULL;
char* keyblock_file = NULL;
@@ -966,3 +967,6 @@ int main(int argc, char* argv[]) {
fprintf(stderr, "You must specify a mode: --pack, --repack or --verify\n");
return PrintHelp(progname);
}
+
+DECLARE_FUTIL_COMMAND(vbutil_kernel, do_vbutil_kernel,
+ "Verified boot kernel utility");
diff --git a/utility/dump_kernel_config_lib.c b/futility/dump_kernel_config_lib.c
index 819ca921..63c2899f 100644
--- a/utility/dump_kernel_config_lib.c
+++ b/futility/dump_kernel_config_lib.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+/* Copyright 2012 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.
*
diff --git a/futility/futility.c b/futility/futility.c
index 5e852c33..deef3061 100644
--- a/futility/futility.c
+++ b/futility/futility.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+ * Copyright 2013 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.
*/
@@ -18,13 +18,6 @@
#define MYNAME "futility"
#define MYNAME_S MYNAME "_s"
-#ifdef OLDDIR
-#define XSTR(A) STR(A)
-#define STR(A) #A
-#define SUBDIR XSTR(OLDDIR)
-#else
-#define SUBDIR "old_bins"
-#endif
/* File to use for logging, if present */
#define LOGFILE "/tmp/futility.log"
@@ -39,21 +32,15 @@ static const char * const usage= "\n\
Usage: " MYNAME " PROGRAM|COMMAND [args...]\n\
\n\
This is the unified firmware utility, which will eventually replace\n\
-all the distinct userspace tools formerly produced by the\n\
+most of the distinct verified boot tools formerly produced by the\n\
vboot_reference package.\n\
\n\
-When symlinked under the name of one of those previous tools, it can\n\
-do one of two things: either it will fully implement the original\n\
-behavior, or (until that functionality is complete) it will just exec\n\
-the original binary.\n\
-\n\
-In either case it can append some usage information to " LOGFILE "\n\
-to help improve coverage and correctness.\n\
+When symlinked under the name of one of those previous tools, should\n\
+fully implement the original behavior. It can also be invoked directly\n\
+as " MYNAME ", followed by the original name as the first argument.\n\
\n\
-If you invoke it directly instead of via a symlink, it requires one\n\
-argument, which is the name of the old binary to exec. That binary\n\
-must be located in a directory named \"" SUBDIR "\" underneath\n\
-the " MYNAME " executable.\n\
+In either case it will append some usage information to " LOGFILE "\n\
+(iff that file exists), to help improve coverage and correctness.\n\
\n";
static int do_help(int argc, char *argv[])
@@ -63,7 +50,7 @@ static int do_help(int argc, char *argv[])
fputs(usage, stdout);
- printf("The following commands are built-in:\n");
+ printf("The following commands are built-in:\n\n");
for (cmd = futil_cmds; *cmd; cmd++)
printf(" %-20s %s\n", (*cmd)->name, (*cmd)->shorthelp);
@@ -77,16 +64,19 @@ static int do_help(int argc, char *argv[])
return 0;
}
-DECLARE_FUTIL_COMMAND(help, do_help, "show a bit of help");
+DECLARE_FUTIL_COMMAND(help, do_help,
+ "Show a bit of help (you're looking at it)");
-/* Deprecated functions can't be invoked through symlinks. */
+/*
+ * These are built-in functions that we'd like to abandon completely someday.
+ * TODO: If no one complains, get rid of them.
+ */
static char *dep_cmds[] = {
- "eficompress",
- "efidecompress",
+ "dev_sign_file",
};
static const char * const dep_usage= "\n\
-The program \"%s\" is deprecated.\n\
+The program \"%s\" is deprecated and may go away soon.\n\
\n\
If you feel this is in error, please open a bug at\n\
\n\
@@ -223,25 +213,21 @@ static void log_args(int argc, char *argv[])
/******************************************************************************/
/* Here we go */
-#ifdef COVERAGE
-void __gcov_flush(void);
-#endif
-
int main(int argc, char *argv[], char *envp[])
{
- char *progname;
+ char *fullname, *progname;
char truename[PATH_MAX];
- char oldname[PATH_MAX];
char buf[80];
pid_t myproc;
ssize_t r;
- char *s;
struct futil_cmd_t **cmd;
int i;
+ int via_symlink = 0;
log_args(argc, argv);
/* How were we invoked? */
+ fullname = strdup(argv[0]);
progname = strrchr(argv[0], '/');
if (progname)
progname++;
@@ -259,18 +245,18 @@ int main(int argc, char *argv[], char *envp[])
argc--;
argv++;
- /* So now what name do we want to invoke? */
+ /* So now what function do we want to invoke? */
progname = strrchr(argv[0], '/');
if (progname)
progname++;
else
progname = argv[0];
} else { /* Invoked by symlink */
-
- /* Block any deprecated functions. */
- for (i = 0; i < ARRAY_SIZE(dep_cmds); i++)
- if (0 == strcmp(dep_cmds[i], progname))
- deprecated(progname);
+ via_symlink = 1;
+ /* Block any deprecated functions. */
+ for (i = 0; i < ARRAY_SIZE(dep_cmds); i++)
+ if (0 == strcmp(dep_cmds[i], progname))
+ deprecated(progname);
}
/* See if it's asking for something we know how to do ourselves */
@@ -278,9 +264,14 @@ int main(int argc, char *argv[], char *envp[])
if (0 == strcmp((*cmd)->name, progname))
return (*cmd)->handler(argc, argv);
- /* Nope, it must be wrapped */
+ /* Nope */
+ if (!via_symlink) {
+ do_help(0, 0);
+ exit(1);
+ }
+
+ /* Complain about bogus symlink */
- /* The old binaries live under the true executable. Find out where that is. */
myproc = getpid();
snprintf(buf, sizeof(buf), "/proc/%d/exe", myproc);
r = readlink(buf, truename, PATH_MAX - 1);
@@ -288,37 +279,18 @@ int main(int argc, char *argv[], char *envp[])
fprintf(stderr, "%s is lost: %s => %s: %s\n", MYNAME, argv[0],
buf, strerror(errno));
exit(1);
- } else if (r == PATH_MAX - 1) {
- /* Yes, it might _just_ fit, but we'll count that as wrong anyway. We can't
- * determine the right size using the example in the readlink manpage,
- * because the /proc symlink returns an st_size of 0. */
- fprintf(stderr, "%s is too long: %s => %s\n", MYNAME, argv[0], buf);
- exit(1);
}
-
truename[r] = '\0';
- s = strrchr(truename, '/'); /* Find the true directory */
- if (s) {
- *s = '\0';
- } else { /* I don't think this can happen */
- fprintf(stderr, "%s says %s doesn't make sense\n", MYNAME, truename);
- exit(1);
- }
- /* If the old binary path doesn't fit, just give up. */
- r = snprintf(oldname, PATH_MAX, "%s/%s/%s", truename, SUBDIR, progname);
- if (r >= PATH_MAX) {
- fprintf(stderr, "%s/%s/%s is too long\n", truename, SUBDIR, progname);
- exit(1);
- }
- fflush(0);
-#ifdef COVERAGE
- /* Write gcov data prior to exec. */
- __gcov_flush();
-#endif
- execve(oldname, argv, envp);
-
- fprintf(stderr, "%s failed to exec %s: %s\n", MYNAME,
- oldname, strerror(errno));
+ fprintf(stderr, "\n\
+The program\n\n %s\n\nis a symlink to\n\n %s\n\
+\n\
+However, " MYNAME " doesn't know how to implement that function.\n\
+\n\
+This is probably an error in your installation. If the problem persists\n\
+after a fresh checkout/build/install, please open a bug at\n\
+\n\
+ http://dev.chromium.org/for-testers/bug-reporting-guidelines\n\
+\n", fullname, truename);
return 1;
}
diff --git a/utility/include/kernel_blob.h b/futility/kernel_blob.h
index f9175f65..173d48d2 100644
--- a/utility/include/kernel_blob.h
+++ b/futility/kernel_blob.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
+// Copyright 2010 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.
//
diff --git a/tests/futility/binary_editor.c b/tests/futility/binary_editor.c
new file mode 100644
index 00000000..f6a971d3
--- /dev/null
+++ b/tests/futility/binary_editor.c
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ *
+ * This is a very simple binary editor, used to create corrupted structs for
+ * testing. It copies stdin to stdout, replacing bytes beginning at the given
+ * offset with the specified 8-bit values.
+ *
+ * There is NO conversion checking of the arguments.
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+int main(int argc, char *argv[])
+{
+ uint32_t offset, curpos, curarg;
+ int c;
+
+ if (argc < 3) {
+ fprintf(stderr, "Need two or more args: OFFSET VAL [VAL...]\n");
+ return 1;
+ }
+
+ offset = (uint32_t)strtoul(argv[1], 0, 0);
+ curarg = 2;
+ for ( curpos = 0; (c = fgetc(stdin)) != EOF; curpos++) {
+
+ if (curpos == offset && curarg < argc) {
+ c = (uint8_t)strtoul(argv[curarg++], 0, 0);
+ offset++;
+ }
+
+ fputc(c, stdout);
+ }
+ return 0;
+}
diff --git a/tests/futility/run_test_scripts.sh b/tests/futility/run_test_scripts.sh
index f8c25ef9..7c8d86b5 100755
--- a/tests/futility/run_test_scripts.sh
+++ b/tests/futility/run_test_scripts.sh
@@ -1,5 +1,5 @@
#!/bin/bash -eu
-# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+# Copyright 2013 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.
@@ -13,7 +13,6 @@ BINDIR="$1"
shift
FUTILITY="$BINDIR/futility"
-OLDDIR="$BINDIR/old_bins"
# The Makefile should export the $BUILD directory, but if it's not just warn
@@ -28,7 +27,6 @@ OUTDIR="${BUILD}/tests/futility_test_results"
# Let each test know where to find things...
export FUTILITY
-export OLDDIR
export SCRIPTDIR
export OUTDIR
@@ -36,6 +34,7 @@ export OUTDIR
TESTS="
${SCRIPTDIR}/test_main.sh
${SCRIPTDIR}/test_dump_fmap.sh
+${SCRIPTDIR}/test_gbb_utility.sh
"
@@ -44,62 +43,6 @@ pass=0
progs=0
##############################################################################
-# But first, we'll just test the wrapped functions. This will go away when
-# everything is built in (chromium:196079).
-
-# Here are the old programs to be wrapped
-# TODO(crbug.com/224734): dev_debug_vboot isn't tested right now.
-PROGS=${*:-cgpt crossystem dev_sign_file dumpRSAPublicKey
- dump_kernel_config enable_dev_usb_boot gbb_utility
- tpm_init_temp_fix tpmc vbutil_firmware vbutil_kernel
- vbutil_what_keys}
-
-# For now just compare results of invoking each program with no args.
-# TODO: Create true rigorous tests for every program.
-echo "-- old_bins --"
-for i in $PROGS; do
- : $(( progs++ ))
-
- # Try the real thing first
- echo -n "$i ... "
- rc=$("${OLDDIR}/$i" \
- 1>"${OUTDIR}/$i.stdout.orig" 2>"${OUTDIR}/$i.stderr.orig" \
- || echo "$?")
- echo "${rc:-0}" > "${OUTDIR}/$i.return.orig"
-
- # Then try the symlink
- rc=$("$BINDIR/$i" 1>"${OUTDIR}/$i.stdout.link" \
- 2>"${OUTDIR}/$i.stderr.link" || echo "$?")
- echo "${rc:-0}" > "${OUTDIR}/$i.return.link"
-
- # And finally try the explicit wrapper
- rc=$("$FUTILITY" "$i" 1>"${OUTDIR}/$i.stdout.futil" \
- 2>"${OUTDIR}/$i.stderr.futil" || echo "$?")
- echo "${rc:-0}" > "${OUTDIR}/$i.return.futil"
-
- # Different?
- if cmp -s "${OUTDIR}/$i.return.orig" "${OUTDIR}/$i.return.link" &&
- cmp -s "${OUTDIR}/$i.stdout.orig" "${OUTDIR}/$i.stdout.link" &&
- cmp -s "${OUTDIR}/$i.stderr.orig" "${OUTDIR}/$i.stderr.link" &&
- cmp -s "${OUTDIR}/$i.return.orig" "${OUTDIR}/$i.return.futil" &&
- cmp -s "${OUTDIR}/$i.stdout.orig" "${OUTDIR}/$i.stdout.futil" &&
- cmp -s "${OUTDIR}/$i.stderr.orig" "${OUTDIR}/$i.stderr.futil" ; then
- green "passed"
- : $(( pass++ ))
- rm -f ${OUTDIR}/$i.{stdout,stderr,return}.{orig,link,futil}
- else
- red "failed"
- fi
-done
-
-# How many wrapped executables are left to incorporate? Did we check them all?
-xprogs=$(find ${OLDDIR} -type f -perm /111 | wc -l)
-if [ $xprogs -gt 0 ]; then
- yellow "${progs}/${xprogs} wrapped executables tested"
-fi
-
-
-##############################################################################
# Invoke the scripts that test the builtin functions.
echo "-- builtin --"
diff --git a/tests/futility/test_dump_fmap.sh b/tests/futility/test_dump_fmap.sh
index d8e37c10..b74c0de0 100755
--- a/tests/futility/test_dump_fmap.sh
+++ b/tests/futility/test_dump_fmap.sh
@@ -1,13 +1,13 @@
#!/bin/bash -eux
-# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+# Copyright 2013 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"
-TMP="$me.tmp"
# Good FMAP
"$FUTILITY" dump_fmap -f "${SCRIPTDIR}/data_fmap.bin" > "$TMP"
@@ -19,17 +19,18 @@ cmp "${SCRIPTDIR}/data_fmap_expect_p.txt" "$TMP"
"$FUTILITY" dump_fmap -h "${SCRIPTDIR}/data_fmap.bin" > "$TMP"
cmp "${SCRIPTDIR}/data_fmap_expect_h.txt" "$TMP"
+
# This should fail because the input file is truncated and doesn't really
# contain the stuff that the FMAP claims it does.
-! "$FUTILITY" dump_fmap -x "${SCRIPTDIR}/data_fmap.bin" FMAP
+if "$FUTILITY" dump_fmap -x "${SCRIPTDIR}/data_fmap.bin" FMAP; then false; fi
# However, this should work.
"$FUTILITY" dump_fmap -x "${SCRIPTDIR}/data_fmap.bin" SI_DESC > "$TMP"
cmp "${SCRIPTDIR}/data_fmap_expect_x.txt" "$TMP"
-# This FMAP has problems, and will fail.
-! "$FUTILITY" dump_fmap -h "${SCRIPTDIR}/data_fmap2.bin" > "$TMP"
+# This FMAP has problems, and should fail.
+if "$FUTILITY" dump_fmap -h "${SCRIPTDIR}/data_fmap2.bin" > "$TMP"; then false; fi
cmp "${SCRIPTDIR}/data_fmap2_expect_h.txt" "$TMP"
"$FUTILITY" dump_fmap -hh "${SCRIPTDIR}/data_fmap2.bin" > "$TMP"
@@ -40,5 +41,5 @@ cmp "${SCRIPTDIR}/data_fmap2_expect_hhH.txt" "$TMP"
# cleanup
-rm -f "$TMP" FMAP SI_DESC
+rm -f ${TMP}* FMAP SI_DESC
exit 0
diff --git a/tests/futility/test_gbb_utility.sh b/tests/futility/test_gbb_utility.sh
new file mode 100755
index 00000000..26e5ef1a
--- /dev/null
+++ b/tests/futility/test_gbb_utility.sh
@@ -0,0 +1,206 @@
+#!/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"
+
+# Helper utility to modify binary blobs
+REPLACE="${BUILD_RUN}/tests/futility/binary_editor"
+
+# First, let's test the basic functionality
+
+# For simplicity, we'll use the same size for all properties.
+${FUTILITY} gbb_utility -c 16,0x10,16,0x10 ${TMP}.blob
+
+# Flags
+${FUTILITY} gbb_utility -s --flags=0xdeadbeef ${TMP}.blob
+${FUTILITY} gbb_utility -g --flags ${TMP}.blob | grep -i 0xdeadbeef
+
+# HWID length should include the terminating null - this is too long
+if ${FUTILITY} gbb_utility -s -i "0123456789ABCDEF" ${TMP}.blob; then false; fi
+# This works
+${FUTILITY} gbb_utility -s -i "0123456789ABCDE" ${TMP}.blob
+# Read it back?
+${FUTILITY} gbb_utility -g ${TMP}.blob | grep "0123456789ABCDE"
+
+# Same kind of tests for the other fields, but they need binary files.
+
+# too long
+dd if=/dev/urandom bs=17 count=1 of=${TMP}.data1.toolong
+dd if=/dev/urandom bs=17 count=1 of=${TMP}.data2.toolong
+dd if=/dev/urandom bs=17 count=1 of=${TMP}.data3.toolong
+if ${FUTILITY} gbb_utility -s --rootkey ${TMP}.data1.toolong ${TMP}.blob; then false; fi
+if ${FUTILITY} gbb_utility -s --recoverykey ${TMP}.data2.toolong ${TMP}.blob; then false; fi
+if ${FUTILITY} gbb_utility -s --bmpfv ${TMP}.data3.toolong ${TMP}.blob; then false; fi
+
+# shorter than max should be okay, though
+dd if=/dev/urandom bs=10 count=1 of=${TMP}.data1.short
+dd if=/dev/urandom bs=10 count=1 of=${TMP}.data2.short
+dd if=/dev/urandom bs=10 count=1 of=${TMP}.data3.short
+${FUTILITY} gbb_utility -s \
+ --rootkey ${TMP}.data1.short \
+ --recoverykey ${TMP}.data2.short \
+ --bmpfv ${TMP}.data3.short ${TMP}.blob
+# read 'em back
+${FUTILITY} gbb_utility -g \
+ --rootkey ${TMP}.read1 \
+ --recoverykey ${TMP}.read2 \
+ --bmpfv ${TMP}.read3 ${TMP}.blob
+# Verify (but remember, it's short)
+cmp -n 10 ${TMP}.data1.short ${TMP}.read1
+cmp -n 10 ${TMP}.data2.short ${TMP}.read2
+cmp -n 10 ${TMP}.data3.short ${TMP}.read3
+
+# Okay
+dd if=/dev/urandom bs=16 count=1 of=${TMP}.data1
+dd if=/dev/urandom bs=16 count=1 of=${TMP}.data2
+dd if=/dev/urandom bs=16 count=1 of=${TMP}.data3
+${FUTILITY} gbb_utility -s --rootkey ${TMP}.data1 ${TMP}.blob
+${FUTILITY} gbb_utility -s --recoverykey ${TMP}.data2 ${TMP}.blob
+${FUTILITY} gbb_utility -s --bmpfv ${TMP}.data3 ${TMP}.blob
+
+# Read 'em back.
+${FUTILITY} gbb_utility -g --rootkey ${TMP}.read1 ${TMP}.blob
+${FUTILITY} gbb_utility -g --recoverykey ${TMP}.read2 ${TMP}.blob
+${FUTILITY} gbb_utility -g --bmpfv ${TMP}.read3 ${TMP}.blob
+# Verify
+cmp ${TMP}.data1 ${TMP}.read1
+cmp ${TMP}.data2 ${TMP}.read2
+cmp ${TMP}.data3 ${TMP}.read3
+
+
+# Okay, creating GBB blobs seems to work. Now let's make sure that corrupted
+# blobs are rejected.
+
+# Danger Will Robinson! We assume that ${TMP}.blob has this binary struct:
+#
+# Field Offset Value
+#
+# signature: 0x0000 $GBB
+# major_version: 0x0004 0x0001
+# minor_version: 0x0006 0x0001
+# header_size: 0x0008 0x00000080
+# flags: 0x000c 0xdeadbeef
+# hwid_offset: 0x0010 0x00000080
+# hwid_size: 0x0014 0x00000010
+# rootkey_offset: 0x0018 0x00000090
+# rootkey_size: 0x001c 0x00000010
+# bmpfv_offset: 0x0020 0x000000a0
+# bmpfv_size: 0x0024 0x00000010
+# recovery_key_offset: 0x0028 0x000000b0
+# recovery_key_size: 0x002c 0x00000010
+# pad: 0x0030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+# 0x0040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+# 0x0050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+# 0x0060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+# 0x0070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+# (HWID) 0x0080 30 31 32 33 34 35 36 37 38 39 41 42 43 44 45 00
+# (rootkey) 0x0090 xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx
+# (bmpfv) 0x00a0 xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx
+# (recovery_key) 0x00b0 xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx
+# 0x00c0 <EOF>
+#
+
+# bad major_version
+cat ${TMP}.blob | ${REPLACE} 0x4 2 > ${TMP}.blob.bad
+if ${FUTILITY} gbb_utility ${TMP}.blob.bad; then false; fi
+
+# header size too large
+cat ${TMP}.blob | ${REPLACE} 0x8 0x81 > ${TMP}.blob.bad
+if ${FUTILITY} gbb_utility ${TMP}.blob.bad; then false; fi
+
+# header size too small
+cat ${TMP}.blob | ${REPLACE} 0x8 0x7f > ${TMP}.blob.bad
+if ${FUTILITY} gbb_utility ${TMP}.blob.bad; then false; fi
+
+# HWID not null-terminated is invalid
+cat ${TMP}.blob | ${REPLACE} 0x8f 0x41 > ${TMP}.blob.bad
+if ${FUTILITY} gbb_utility ${TMP}.blob.bad; then false; fi
+
+# HWID of length zero is okay
+cat ${TMP}.blob | ${REPLACE} 0x14 0x00 > ${TMP}.blob.ok
+${FUTILITY} gbb_utility ${TMP}.blob.ok
+# And HWID of length 1 consisting only of '\0' is okay, too.
+cat ${TMP}.blob | ${REPLACE} 0x14 0x01 | ${REPLACE} 0x80 0x00 > ${TMP}.blob.ok
+${FUTILITY} gbb_utility ${TMP}.blob.ok
+
+# zero-length HWID not null-terminated is invalid
+cat ${TMP}.blob | ${REPLACE} 0x8f 0x41 > ${TMP}.blob.bad
+if ${FUTILITY} gbb_utility ${TMP}.blob.bad; then false; fi
+
+# hwid_offset < GBB_HEADER_SIZE is invalid
+cat ${TMP}.blob | ${REPLACE} 0x10 0x7f > ${TMP}.blob.bad
+if ${FUTILITY} gbb_utility ${TMP}.blob.bad; then false; fi
+cat ${TMP}.blob | ${REPLACE} 0x10 0x00 > ${TMP}.blob.bad
+if ${FUTILITY} gbb_utility ${TMP}.blob.bad; then false; fi
+
+# rootkey_offset < GBB_HEADER_SIZE is invalid
+cat ${TMP}.blob | ${REPLACE} 0x18 0x7f > ${TMP}.blob.bad
+if ${FUTILITY} gbb_utility ${TMP}.blob.bad; then false; fi
+cat ${TMP}.blob | ${REPLACE} 0x18 0x00 > ${TMP}.blob.bad
+if ${FUTILITY} gbb_utility ${TMP}.blob.bad; then false; fi
+
+# bmpfv_offset < GBB_HEADER_SIZE is invalid
+cat ${TMP}.blob | ${REPLACE} 0x20 0x7f > ${TMP}.blob.bad
+if ${FUTILITY} gbb_utility ${TMP}.blob.bad; then false; fi
+cat ${TMP}.blob | ${REPLACE} 0x20 0x00 > ${TMP}.blob.bad
+if ${FUTILITY} gbb_utility ${TMP}.blob.bad; then false; fi
+
+# recovery_key_offset < GBB_HEADER_SIZE is invalid
+cat ${TMP}.blob | ${REPLACE} 0x28 0x7f > ${TMP}.blob.bad
+if ${FUTILITY} gbb_utility ${TMP}.blob.bad; then false; fi
+cat ${TMP}.blob | ${REPLACE} 0x28 0x00 > ${TMP}.blob.bad
+if ${FUTILITY} gbb_utility ${TMP}.blob.bad; then false; fi
+
+# hwid: offset + size == end of file is okay; beyond is invalid
+cat ${TMP}.blob | ${REPLACE} 0x14 0x40 > ${TMP}.blob.bad
+${FUTILITY} gbb_utility -g ${TMP}.blob.bad
+cat ${TMP}.blob | ${REPLACE} 0x14 0x41 > ${TMP}.blob.bad
+if ${FUTILITY} gbb_utility -g ${TMP}.blob.bad; then false; fi
+
+# rootkey: offset + size == end of file is okay; beyond is invalid
+cat ${TMP}.blob | ${REPLACE} 0x1c 0x30 > ${TMP}.blob.bad
+${FUTILITY} gbb_utility -g ${TMP}.blob.bad
+cat ${TMP}.blob | ${REPLACE} 0x1c 0x31 > ${TMP}.blob.bad
+if ${FUTILITY} gbb_utility -g ${TMP}.blob.bad; then false; fi
+
+# bmpfv: offset + size == end of file is okay; beyond is invalid
+cat ${TMP}.blob | ${REPLACE} 0x24 0x20 > ${TMP}.blob.bad
+${FUTILITY} gbb_utility -g ${TMP}.blob.bad
+cat ${TMP}.blob | ${REPLACE} 0x24 0x21 > ${TMP}.blob.bad
+if ${FUTILITY} gbb_utility -g ${TMP}.blob.bad; then false; fi
+
+# recovery_key: offset + size == end of file is okay; beyond is invalid
+cat ${TMP}.blob | ${REPLACE} 0x2c 0x10 > ${TMP}.blob.bad
+${FUTILITY} gbb_utility -g ${TMP}.blob.bad
+cat ${TMP}.blob | ${REPLACE} 0x2c 0x11 > ${TMP}.blob.bad
+if ${FUTILITY} gbb_utility -g ${TMP}.blob.bad; then false; fi
+
+# hwid_size == 0 doesn't complain, but can't be set
+cat ${TMP}.blob | ${REPLACE} 0x14 0x00 > ${TMP}.blob.bad
+${FUTILITY} gbb_utility -g ${TMP}.blob.bad
+if ${FUTILITY} gbb_utility -s -i "A" ${TMP}.blob.bad; then false; fi
+
+# rootkey_size == 0 gives warning, gets nothing, can't be set
+cat ${TMP}.blob | ${REPLACE} 0x1c 0x00 > ${TMP}.blob.bad
+${FUTILITY} gbb_utility -g --rootkey ${TMP}.read1 ${TMP}.blob.bad
+if ${FUTILITY} gbb_utility -s --rootkey ${TMP}.data1 ${TMP}.blob.bad; then false; fi
+
+# bmpfv_size == 0 gives warning, gets nothing, can't be set
+cat ${TMP}.blob | ${REPLACE} 0x24 0x00 > ${TMP}.blob.bad
+${FUTILITY} gbb_utility -g --bmpfv ${TMP}.read3 ${TMP}.blob.bad
+if ${FUTILITY} gbb_utility -s --bmpfv ${TMP}.data3 ${TMP}.blob.bad; then false; fi
+
+# recovery_key_size == 0 gives warning, gets nothing, can't be set
+cat ${TMP}.blob | ${REPLACE} 0x2c 0x00 > ${TMP}.blob.bad
+${FUTILITY} gbb_utility -g --recoverykey ${TMP}.read2 ${TMP}.blob.bad
+if ${FUTILITY} gbb_utility -s --recoverykey ${TMP}.data2 ${TMP}.blob.bad; then false; fi
+
+# cleanup
+rm -f ${TMP}*
+exit 0
diff --git a/tests/futility/test_main.sh b/tests/futility/test_main.sh
index 9bf7ca6d..62177e68 100755
--- a/tests/futility/test_main.sh
+++ b/tests/futility/test_main.sh
@@ -1,26 +1,32 @@
#!/bin/bash -eux
-# Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
+# 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"
-TMP="$me.tmp"
-
-# Built-in do-nothing commands.
-# TODO(crbug.com/224734): Remove these when we have enough built-in commands
-"$FUTILITY" foo hi
-"$FUTILITY" bar there
-"$FUTILITY" hey boys
# No args returns nonzero exit code
"$FUTILITY" && false
-"$FUTILITY" help > "$TMP"
-grep Usage "$TMP"
-# TODO(crbug.com/224734): Make sure all built-in commands have help, too.
+# Make sure all built-in commands are listed and have help
+expected=\
+'dev_sign_file
+dump_fmap
+dump_kernel_config
+gbb_utility
+help
+vbutil_firmware
+vbutil_kernel
+vbutil_key
+vbutil_keyblock'
+got=$("$FUTILITY" help |
+ egrep '^[[:space:]]+[^[:space:]]+[[:space:]]+[^[:space:]]+' |
+ awk '{print $1}')
+[ "$expected" = "$got" ]
# It's weird but okay if the command is a full path.
"$FUTILITY" /fake/path/to/help > "$TMP"
@@ -35,6 +41,16 @@ touch "$LOG"
grep "$FUTILITY" "$LOG"
rm "$LOG"
+# Make sure deprecated functions fail via symlink
+ln -sf "$FUTILITY" dev_sign_file
+if ./dev_sign_file 2>${TMP}.outmsg ; then false; fi
+grep deprecated ${TMP}.outmsg
+# They may still fail when invoked through futility (this one does),
+# but with a different error message.
+"$FUTILITY" dev_sign_file 1>${TMP}.outmsg2 2>&1 || true
+if grep deprecated ${TMP}.outmsg2; then false; fi
+
+
# cleanup
-rm -f "$TMP"
+rm -f ${TMP}*
exit 0
diff --git a/utility/gbb_utility.cc b/utility/gbb_utility.cc
index 980521c5..2a8c2347 100644
--- a/utility/gbb_utility.cc
+++ b/utility/gbb_utility.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Copyright 2011 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.
//
@@ -609,7 +609,7 @@ static bool parse_creation_param(const string &input_string,
if (*parsed && *parsed != ',')
return false;
output_vector->push_back(param);
- input = parsed + 1;
+ input = *parsed ? parsed + 1 : parsed;
} while (*input);
return true;