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
This commit is contained in:
Bill Richardson
2014-07-15 12:52:19 -07:00
committed by chrome-internal-fetch
parent b52d7b7acb
commit 6f3961507e
17 changed files with 1010 additions and 289 deletions

114
Makefile
View File

@@ -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}

View File

@@ -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)");

View File

@@ -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");

View File

@@ -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");

611
futility/cmd_gbb_utility.c Normal file
View File

@@ -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)");

View File

@@ -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");

View File

@@ -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");

View File

@@ -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");

View File

@@ -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.
*

View File

@@ -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\
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\
In either case it can append some usage information to " LOGFILE "\n\
to help improve coverage and correctness.\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;
}

View File

@@ -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.
//

View File

@@ -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;
}

View File

@@ -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
"
@@ -43,62 +42,6 @@ ${SCRIPTDIR}/test_dump_fmap.sh
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.

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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;