Merge cos-sdk into ToT

Includes changes for the most recent ChromeOS merge.

BUG=b/257271340
TEST=presubmit
RELEASE_NOTE=None

Change-Id: I9910568160c45bbbf5cc1a7267cf74dcba9d8aa3
diff --git a/.gitignore b/.gitignore
index 9edc243..bb49193 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,3 +11,4 @@
 scripts/newbitmaps/strings/localized_text/*/*.png
 target
 .idea
+*.swp
diff --git a/Android.mk b/Android.mk
index c54a3b7..e0ba6aa 100644
--- a/Android.mk
+++ b/Android.mk
@@ -67,7 +67,6 @@
 	firmware/stub/vboot_api_stub_sf.c
 
 VBSLK_SRCS += \
-	firmware/stub/vboot_api_stub.c \
 	firmware/stub/vboot_api_stub_disk.c \
 	firmware/stub/vboot_api_stub_stream.c
 
@@ -169,4 +168,3 @@
 LOCAL_STATIC_LIBRARIES := libvboot_util-host
 LOCAL_SHARED_LIBRARIES := libcrypto-host
 include $(BUILD_HOST_EXECUTABLE)
-
diff --git a/Makefile b/Makefile
index 4a07128..7731598 100644
--- a/Makefile
+++ b/Makefile
@@ -237,7 +237,7 @@
 
 # Code coverage
 ifneq ($(filter-out 0,${COV}),)
-  COV_FLAGS = -O0 --coverage -DCOVERAGE
+  COV_FLAGS = -Og --coverage -DCOVERAGE
   CFLAGS += ${COV_FLAGS}
   LDFLAGS += ${COV_FLAGS}
   COV_INFO = ${BUILD}/coverage.info
@@ -275,6 +275,13 @@
   LIBZIP_LIBS := $(shell ${PKG_CONFIG} --libs libzip)
 endif
 
+LIBARCHIVE_VERSION := $(shell ${PKG_CONFIG} --modversion libarchive 2>/dev/null)
+HAVE_LIBARCHIVE := $(if ${LIBARCHIVE_VERSION},1)
+ifneq ($(filter-out 0,${HAVE_LIBARCHIVE}),)
+  CFLAGS += -DHAVE_LIBARCHIVE $(shell ${PKG_CONFIG} --cflags libarchive)
+  LIBARCHIVE_LIBS := $(shell ${PKG_CONFIG} --libs libarchive)
+endif
+
 HAVE_CROSID := $(shell ${PKG_CONFIG} --exists crosid && echo 1)
 ifeq ($(HAVE_CROSID),1)
   CFLAGS += -DHAVE_CROSID $(shell ${PKG_CONFIG} --cflags crosid)
@@ -369,6 +376,7 @@
 	firmware/2lib/2gbb.c \
 	firmware/2lib/2hmac.c \
 	firmware/2lib/2kernel.c \
+	firmware/2lib/2load_kernel.c \
 	firmware/2lib/2misc.c \
 	firmware/2lib/2nvstorage.c \
 	firmware/2lib/2packed_key.c \
@@ -388,8 +396,6 @@
 	firmware/lib/cgptlib/cgptlib_internal.c \
 	firmware/lib/cgptlib/crc32.c \
 	firmware/lib/gpt_misc.c \
-	firmware/lib/vboot_api_kernel.c \
-	firmware/lib/vboot_kernel.c \
 	firmware/lib20/api_kernel.c \
 	firmware/lib20/kernel.c
 
@@ -423,7 +429,6 @@
 # TODO: split out other stub funcs too
 FWLIB_SRCS += \
 	firmware/stub/tpm_lite_stub.c \
-	firmware/stub/vboot_api_stub.c \
 	firmware/stub/vboot_api_stub_disk.c \
 	firmware/stub/vboot_api_stub_stream.c \
 	firmware/2lib/2stub.c
@@ -439,12 +444,14 @@
 ifneq ($(filter-out 0,${USE_FLASHROM}),)
 $(info building with libflashrom support)
 FLASHROM_LIBS := $(shell ${PKG_CONFIG} --libs flashrom)
-COMMONLIB_SRCS = \
+COMMONLIB_SRCS += \
 	host/lib/flashrom.c \
-	host/lib/flashrom_drv.c \
-	host/lib/subprocess.c
+	host/lib/flashrom_drv.c
 CFLAGS += -DUSE_FLASHROM
 endif
+COMMONLIB_SRCS += \
+	host/lib/subprocess.c \
+	host/lib/cbfstool.c
 
 # Intermediate library for the vboot_reference utilities to link against.
 UTILLIB = ${BUILD}/libvboot_util.a
@@ -515,7 +522,6 @@
 	firmware/lib/cgptlib/crc32.c \
 	firmware/lib/gpt_misc.c \
 	firmware/stub/tpm_lite_stub.c \
-	firmware/stub/vboot_api_stub.c \
 	firmware/stub/vboot_api_stub_disk.c \
 	futility/dump_kernel_config_lib.c \
 	$(CROSSYSTEM_ARCH_C) \
@@ -675,6 +681,7 @@
 FUTIL_SRCS += host/lib/flashrom_drv.c \
 	futility/flashrom_wp_drv.c \
 	futility/updater_archive.c \
+	futility/updater_manifest.c \
 	futility/updater_quirks.c \
 	futility/updater_utils.c \
 	futility/updater.c
@@ -693,10 +700,10 @@
 # Library of handy test functions.
 TESTLIB = ${BUILD}/tests/test.a
 
-TESTLIB_SRCS += \
-	tests/test_common.c \
-	tests/timer_utils.c \
-	tests/crc32_test.c
+TEST_COMMON_DIR = tests/common
+
+TESTLIB_SRCS += $(wildcard $(TEST_COMMON_DIR)/*.c)
+TESTLIB_SRCS += tests/crc32_test.c
 
 TESTLIB_OBJS = ${TESTLIB_SRCS:%.c=${BUILD}/%.o}
 TEST_OBJS += ${TESTLIB_OBJS}
@@ -709,10 +716,6 @@
 	tests/gpt_misc_tests \
 	tests/sha_benchmark \
 	tests/subprocess_tests \
-	tests/vboot_api_kernel4_tests \
-	tests/vboot_api_kernel_tests \
-	tests/vboot_kernel_tests \
-	tests/vboot_kernel2_tests \
 	tests/verify_kernel
 
 ifeq ($(filter-out 0,${MOCK_TPM})$(filter-out 0,${TPM2_MODE}),)
@@ -743,7 +746,10 @@
 	tests/vb2_host_key_tests \
 	tests/vb2_host_nvdata_flashrom_tests \
 	tests/vb2_kernel_tests \
+	tests/vb2_load_kernel_tests \
+	tests/vb2_load_kernel2_tests \
 	tests/vb2_misc_tests \
+	tests/vb2_misc2_tests \
 	tests/vb2_nvstorage_tests \
 	tests/vb2_rsa_utility_tests \
 	tests/vb2_recovery_reasons_tests \
@@ -1043,7 +1049,8 @@
 futil: ${FUTIL_BIN}
 
 # FUTIL_LIBS is shared by FUTIL_BIN and TEST_FUTIL_BINS.
-FUTIL_LIBS = ${CROSID_LIBS} ${CRYPTO_LIBS} ${LIBZIP_LIBS} ${FLASHROM_LIBS}
+FUTIL_LIBS = ${CROSID_LIBS} ${CRYPTO_LIBS} ${LIBZIP_LIBS} ${LIBARCHIVE_LIBS} \
+	${FLASHROM_LIBS}
 
 ${FUTIL_BIN}: LDLIBS += ${FUTIL_LIBS}
 ${FUTIL_BIN}: ${FUTIL_OBJS} ${UTILLIB} ${FWLIB}
@@ -1279,10 +1286,6 @@
 # tlcl_tests only works when MOCK_TPM is disabled
 	${RUNTEST} ${BUILD_RUN}/tests/tlcl_tests
 endif
-	${RUNTEST} ${BUILD_RUN}/tests/vboot_api_kernel4_tests
-	${RUNTEST} ${BUILD_RUN}/tests/vboot_api_kernel_tests
-	${RUNTEST} ${BUILD_RUN}/tests/vboot_kernel_tests
-	${RUNTEST} ${BUILD_RUN}/tests/vboot_kernel2_tests
 
 .PHONY: run2tests
 run2tests: install_for_test
@@ -1296,8 +1299,11 @@
 	${RUNTEST} ${BUILD_RUN}/tests/vb2_firmware_tests
 	${RUNTEST} ${BUILD_RUN}/tests/vb2_gbb_tests
 	${RUNTEST} ${BUILD_RUN}/tests/vb2_host_key_tests
+	${RUNTEST} ${BUILD_RUN}/tests/vb2_load_kernel_tests
+	${RUNTEST} ${BUILD_RUN}/tests/vb2_load_kernel2_tests
 	${RUNTEST} ${BUILD_RUN}/tests/vb2_kernel_tests
 	${RUNTEST} ${BUILD_RUN}/tests/vb2_misc_tests
+	${RUNTEST} ${BUILD_RUN}/tests/vb2_misc2_tests
 	${RUNTEST} ${BUILD_RUN}/tests/vb2_nvstorage_tests
 	${RUNTEST} ${BUILD_RUN}/tests/vb2_rsa_utility_tests
 	${RUNTEST} ${BUILD_RUN}/tests/vb2_secdata_firmware_tests
@@ -1340,13 +1346,18 @@
 	${Q}echo -e "\nruntests: \E[32;1mALL TESTS PASSED SUCCESSFULLY!\E[0;m\n"
 
 # Code coverage
+.PHONY: coverage
+ifeq ($(filter-out 0,${COV}),)
+coverage:
+	$(error Build coverage like this: make clean && COV=1 make coverage)
+else
 .PHONY: coverage_init
 coverage_init: install_for_test
 	rm -f ${COV_INFO}*
 	lcov -c -i -d . -b . -o ${COV_INFO}.initial
 
 .PHONY: coverage_html
-coverage_html:
+coverage_html: coverage_init runtests
 	lcov -c -d . -b . -o ${COV_INFO}.tests
 	lcov -a ${COV_INFO}.initial -a ${COV_INFO}.tests -o ${COV_INFO}.total
 	lcov -r ${COV_INFO}.total '/usr/*' -o ${COV_INFO}.local
@@ -1358,11 +1369,6 @@
 	lcov -e ${COV_INFO}.nostub '${SRCDIR}/firmware/*' \
 		-o ${COV_INFO}.firmware
 
-.PHONY: coverage
-ifeq ($(filter-out 0,${COV}),)
-coverage:
-	$(error Build coverage like this: make clean && COV=1 make coverage)
-else
 coverage: coverage_init runtests coverage_html
 endif
 
diff --git a/OWNERS b/OWNERS
index 21ecb22..b748e0e 100644
--- a/OWNERS
+++ b/OWNERS
@@ -2,4 +2,4 @@
 yupingso@chromium.org
 twawrzynczak@chromium.org
 hungte@chromium.org
-*
+roccochen@chromium.org
diff --git a/cgpt/cgpt_find.c b/cgpt/cgpt_find.c
index 8046aad..f50908f 100644
--- a/cgpt/cgpt_find.c
+++ b/cgpt/cgpt_find.c
@@ -243,8 +243,14 @@
           goto cleanup;
         }
       }
+      // Create a temp dir to work in.
+      if (mkdtemp(temp_dir) == NULL) {
+        perror("Cannot create a temporary directory.\n");
+        goto cleanup;
+      }
       if (ReadNorFlash(temp_dir) != 0) {
         perror("ReadNorFlash");
+        RemoveDir(temp_dir);
         goto cleanup;
       }
       char nor_file[64];
diff --git a/cgpt/cgpt_nor.c b/cgpt/cgpt_nor.c
index 1530271..e96ee27 100644
--- a/cgpt/cgpt_nor.c
+++ b/cgpt/cgpt_nor.c
@@ -203,20 +203,11 @@
 #define FLASHROM_RW_GPT_SEC "RW_GPT_SECONDARY:rw_gpt_2"
 #define FLASHROM_RW_GPT "RW_GPT:rw_gpt"
 
-// Read RW_GPT from NOR flash to "rw_gpt" in a temp dir |temp_dir_template|.
-// |temp_dir_template| is passed to mkdtemp() so it must satisfy all
-// requirements by mkdtemp.
+// Read RW_GPT from NOR flash to "rw_gpt" in a dir.
 // TODO(b:184812319): Replace this function with flashrom_read.
-int ReadNorFlash(char *temp_dir_template) {
+int ReadNorFlash(const char *dir) {
   int ret = 0;
 
-  // Create a temp dir to work in.
-  ret++;
-  if (mkdtemp(temp_dir_template) == NULL) {
-    Error("Cannot create a temporary directory.\n");
-    return ret;
-  }
-
   // Read RW_GPT section from NOR flash to "rw_gpt".
   ret++;
 
@@ -225,7 +216,7 @@
     Error("Cannot get current directory.\n");
     return ret;
   }
-  if (chdir(temp_dir_template) < 0) {
+  if (chdir(dir) < 0) {
     Error("Cannot change directory.\n");
     goto out_free;
   }
@@ -234,7 +225,6 @@
   // output.
   if (subprocess_run(argv, &subprocess_null, &subprocess_null, NULL) != 0) {
     Error("Cannot exec flashrom to read from RW_GPT section.\n");
-    RemoveDir(temp_dir_template);
   } else {
     ret = 0;
   }
@@ -248,6 +238,18 @@
   return ret;
 }
 
+static int FlashromWriteRegion(const char *region)
+{
+  const char *const argv[] = {FLASHROM_PATH, "-i", region, "-w", "--noverify-all"};
+  // Redirect stdout to /dev/null so that flashrom does not muck up cgpt's
+  // output.
+  if (subprocess_run(argv, &subprocess_null, &subprocess_null, NULL) != 0) {
+    Warning("Cannot write '%s' back with flashrom.\n", region);
+    return 1;
+  }
+  return 0;
+}
+
 // Write "rw_gpt" back to NOR flash. We write the file in two parts for safety.
 // TODO(b:184812319): Replace this function with flashrom_write.
 int WriteNorFlash(const char *dir) {
@@ -270,22 +272,11 @@
     Error("Cannot change directory.\n");
     goto out_free;
   }
-  const char *const argv1[] = {FLASHROM_PATH, "-i", FLASHROM_RW_GPT_PRI,
-                "-w", "--noverify-all"};
-  // Redirect stdout to /dev/null so that flashrom does not muck up cgpt's
-  // output.
-  if (subprocess_run(argv1, &subprocess_null, &subprocess_null, NULL) != 0) {
-    Warning("Cannot write the 1st half of rw_gpt back with flashrom.\n");
+  if (FlashromWriteRegion(FLASHROM_RW_GPT_PRI))
     nr_fails++;
-  }
-  const char *const argv2[] = {FLASHROM_PATH, "-i", FLASHROM_RW_GPT_SEC,
-                "-w", "--noverify-all"};
-  // Redirect stdout to /dev/null so that flashrom does not muck up cgpt's
-  // output.
-  if (subprocess_run(argv2, &subprocess_null, &subprocess_null, NULL) != 0) {
-    Warning("Cannot write the 2nd half of rw_gpt back with flashrom.\n");
+  if (FlashromWriteRegion(FLASHROM_RW_GPT_SEC))
     nr_fails++;
-  }
+
   if (chdir(cwd) < 0) {
     Error("Cannot change directory back to original.\n");
     goto out_free;
diff --git a/cgpt/cgpt_nor.h b/cgpt/cgpt_nor.h
index 4746089..93e9dde 100644
--- a/cgpt/cgpt_nor.h
+++ b/cgpt/cgpt_nor.h
@@ -23,10 +23,8 @@
 // Exec "rm" to remove |dir|.
 int RemoveDir(const char *dir);
 
-// Read RW_GPT from NOR flash to "rw_gpt" in a temp dir |temp_dir_template|.
-// |temp_dir_template| is passed to mkdtemp() so it must satisfy all
-// requirements by mkdtemp().
-int ReadNorFlash(char *temp_dir_template);
+// Read RW_GPT from NOR flash to "rw_gpt" in dir.
+int ReadNorFlash(const char *dir);
 
 // Write "rw_gpt" back to NOR flash. We write the file in two parts for safety.
 int WriteNorFlash(const char *dir);
diff --git a/cgpt/cgpt_wrapper.c b/cgpt/cgpt_wrapper.c
index d26682d..17336c1 100644
--- a/cgpt/cgpt_wrapper.c
+++ b/cgpt/cgpt_wrapper.c
@@ -82,9 +82,13 @@
   // Create a temp dir to work in.
   ret++;
   char temp_dir[] = "/tmp/cgpt_wrapper.XXXXXX";
-  if (ReadNorFlash(temp_dir) != 0) {
+  if (mkdtemp(temp_dir_template) == NULL) {
+    Error("Cannot create a temporary directory.\n");
     return ret;
   }
+  if (ReadNorFlash(temp_dir) != 0) {
+    goto cleanup;
+  }
   char rw_gpt_path[PATH_MAX];
   if (snprintf(rw_gpt_path, sizeof(rw_gpt_path), "%s/rw_gpt", temp_dir) < 0) {
     goto cleanup;
diff --git a/firmware/2lib/2api.c b/firmware/2lib/2api.c
index c041d8a..13b3a69 100644
--- a/firmware/2lib/2api.c
+++ b/firmware/2lib/2api.c
@@ -72,26 +72,21 @@
 	 */
 	vb2_check_recovery(ctx);
 
+	/* Decide the boot mode */
+	vb2_set_boot_mode(ctx);
+
 	/*
-	 * Check for possible reasons to ask the firmware to make display
-	 * available.  VB2_CONTEXT_RECOVERY_MODE may have been set above by
-	 * vb2_check_recovery.  VB2_SD_FLAG_DEV_MODE_ENABLED may have been set
-	 * above by vb2_check_dev_switch.  VB2_NV_DIAG_REQUEST may have been
-	 * set during the last boot in recovery mode.
+	 * Initialize display if VB2_NV_DISPLAY_REQUEST is set or in non-normal
+	 * boot mode.
 	 */
-	if (!(ctx->flags & VB2_CONTEXT_DISPLAY_INIT) &&
-	    (vb2_nv_get(ctx, VB2_NV_DISPLAY_REQUEST) ||
-	     sd->flags & VB2_SD_FLAG_DEV_MODE_ENABLED ||
-	     ctx->flags & VB2_CONTEXT_RECOVERY_MODE ||
-	     vb2_nv_get(ctx, VB2_NV_DIAG_REQUEST)))
+	if (vb2_nv_get(ctx, VB2_NV_DISPLAY_REQUEST) ||
+	    ctx->boot_mode != VB2_BOOT_MODE_NORMAL)
 		ctx->flags |= VB2_CONTEXT_DISPLAY_INIT;
+
 	/* Mark display as available for downstream vboot and vboot callers. */
 	if (ctx->flags & VB2_CONTEXT_DISPLAY_INIT)
 		sd->flags |= VB2_SD_FLAG_DISPLAY_AVAILABLE;
 
-	/* Decide the boot mode */
-	vb2_set_boot_mode(ctx);
-
 	/* Return error if recovery is needed */
 	if (ctx->flags & VB2_CONTEXT_RECOVERY_MODE) {
 		/* Always clear RAM when entering recovery mode */
@@ -370,3 +365,33 @@
 {
 	return vb2api_check_hash_get_digest(ctx, NULL, 0);
 }
+
+union vb2_fw_boot_info vb2api_get_fw_boot_info(struct vb2_context *ctx)
+{
+	union vb2_fw_boot_info info;
+
+	struct vb2_shared_data *sd = vb2_get_sd(ctx);
+
+	info.tries = vb2_nv_get(ctx, VB2_NV_TRY_COUNT);
+	info.slot = sd->fw_slot;
+	info.prev_slot = sd->last_fw_slot;
+	info.prev_result = sd->last_fw_result;
+	info.boot_mode = ctx->boot_mode;
+
+	VB2_DEBUG("boot_mode=`%s`\n", vb2_boot_mode_string(info.boot_mode));
+
+	if (ctx->flags & VB2_CONTEXT_RECOVERY_MODE) {
+		info.recovery_reason = sd->recovery_reason;
+		info.recovery_subcode = vb2_nv_get(ctx, VB2_NV_RECOVERY_SUBCODE);
+		VB2_DEBUG("recovery_reason: %#x / %#x\n",
+			  info.recovery_reason, info.recovery_subcode);
+	}
+
+	VB2_DEBUG("fw_tried=`%s` fw_try_count=%d "
+		  "fw_prev_tried=`%s` fw_prev_result=`%s`.\n",
+		  vb2_slot_string(info.slot), info.tries,
+		  vb2_slot_string(info.prev_slot),
+		  vb2_result_string(info.prev_result));
+
+	return info;
+}
diff --git a/firmware/2lib/2auxfw_sync.c b/firmware/2lib/2auxfw_sync.c
index eaea1d4..76ad722 100644
--- a/firmware/2lib/2auxfw_sync.c
+++ b/firmware/2lib/2auxfw_sync.c
@@ -48,12 +48,14 @@
 	return vb2ex_auxfw_check(severity);
 }
 
+test_mockable
 vb2_error_t vb2api_auxfw_sync(struct vb2_context *ctx)
 {
 	enum vb2_auxfw_update_severity fw_update = VB2_AUXFW_NO_UPDATE;
 
 	/* Check for update severity */
-	VB2_TRY(auxfw_sync_check_update(ctx, &fw_update));
+	VB2_TRY(auxfw_sync_check_update(ctx, &fw_update), ctx,
+		VB2_RECOVERY_AUXFW_UPDATE);
 
 	if (fw_update > VB2_AUXFW_NO_UPDATE) {
 		VB2_DEBUG("Updating auxfw\n");
diff --git a/firmware/2lib/2ec_sync.c b/firmware/2lib/2ec_sync.c
index e75313b..b1bc7eb 100644
--- a/firmware/2lib/2ec_sync.c
+++ b/firmware/2lib/2ec_sync.c
@@ -338,6 +338,7 @@
 	return sync_ec(ctx);
 }
 
+test_mockable
 vb2_error_t vb2api_ec_sync(struct vb2_context *ctx)
 {
 	struct vb2_shared_data *sd = vb2_get_sd(ctx);
diff --git a/firmware/2lib/2kernel.c b/firmware/2lib/2kernel.c
index 5b18cad..ff89e14 100644
--- a/firmware/2lib/2kernel.c
+++ b/firmware/2lib/2kernel.c
@@ -5,94 +5,12 @@
  * Kernel selection, loading, verification, and booting.
  */
 
+#include "2api.h"
 #include "2common.h"
-#include "2kernel.h"
 #include "2misc.h"
 #include "2nvstorage.h"
 #include "2rsa.h"
 #include "2secdata.h"
-#include "vboot_api.h"
-
-/**
- * Reset any NVRAM requests.
- *
- * @param ctx		Vboot context
- * @return 1 if a reboot is required, 0 otherwise.
- */
-static int vb2_reset_nv_requests(struct vb2_context *ctx)
-{
-	int need_reboot = 0;
-
-	if (vb2_nv_get(ctx, VB2_NV_DISPLAY_REQUEST)) {
-		VB2_DEBUG("Unset display request (undo display init)\n");
-		vb2_nv_set(ctx, VB2_NV_DISPLAY_REQUEST, 0);
-		need_reboot = 1;
-	}
-
-	if (vb2_nv_get(ctx, VB2_NV_DIAG_REQUEST)) {
-		VB2_DEBUG("Unset diagnostic request (undo display init)\n");
-		vb2_nv_set(ctx, VB2_NV_DIAG_REQUEST, 0);
-		need_reboot = 1;
-	}
-
-	return need_reboot;
-}
-
-vb2_error_t vb2_normal_boot(struct vb2_context *ctx)
-{
-	struct vb2_shared_data *sd = vb2_get_sd(ctx);
-	uint32_t max_rollforward = vb2_nv_get(ctx,
-					      VB2_NV_KERNEL_MAX_ROLLFORWARD);
-
-	/* Boot from fixed disk only */
-	VB2_DEBUG("Entering\n");
-
-	if (vb2_reset_nv_requests(ctx)) {
-		VB2_DEBUG("Normal mode: reboot to reset NVRAM requests\n");
-		return VB2_REQUEST_REBOOT;
-	}
-
-	vb2_error_t rv = VbTryLoadKernel(ctx, VB_DISK_FLAG_FIXED);
-
-	VB2_DEBUG("Checking if TPM kernel version needs advancing\n");
-
-	/*
-	 * Special case for when we're trying a slot with new firmware.
-	 * Firmware updates also usually change the kernel key, which means
-	 * that the new firmware can only boot a new kernel, and the old
-	 * firmware in the previous slot can only boot the previous kernel.
-	 *
-	 * Don't roll-forward the kernel version, because we don't yet know if
-	 * the new kernel will successfully boot.
-	 */
-	if (vb2_nv_get(ctx, VB2_NV_FW_RESULT) == VB2_FW_RESULT_TRYING) {
-		VB2_DEBUG("Trying new FW; skip kernel version roll-forward.\n");
-		return rv;
-	}
-
-	/*
-	 * Limit kernel version rollforward if needed.  Can't limit kernel
-	 * version to less than the version currently in the TPM.  That is,
-	 * we're limiting rollforward, not allowing rollback.
-	 */
-	if (max_rollforward < sd->kernel_version_secdata)
-		max_rollforward = sd->kernel_version_secdata;
-
-	if (sd->kernel_version > max_rollforward) {
-		VB2_DEBUG("Limiting TPM kernel version roll-forward "
-			  "to %#x < %#x\n",
-			  max_rollforward, sd->kernel_version);
-
-		sd->kernel_version = max_rollforward;
-	}
-
-	if (sd->kernel_version > sd->kernel_version_secdata) {
-		vb2_secdata_kernel_set(ctx, VB2_SECDATA_KERNEL_VERSIONS,
-				       sd->kernel_version);
-	}
-
-	return rv;
-}
 
 int vb2api_is_developer_signed(struct vb2_context *ctx)
 {
@@ -197,3 +115,165 @@
 
 	return VB2_SUCCESS;
 }
+
+static vb2_error_t handle_battery_cutoff(struct vb2_context *ctx)
+{
+	/*
+	 * Check if we need to cut-off battery. This should be done after EC
+	 * FW and auxfw are updated, and before the kernel is started.  This
+	 * is to make sure all firmware is up-to-date before shipping (which
+	 * is the typical use-case for cutoff).
+	 */
+	if (vb2_nv_get(ctx, VB2_NV_BATTERY_CUTOFF_REQUEST)) {
+		VB2_DEBUG("Request to cut-off battery\n");
+		vb2_nv_set(ctx, VB2_NV_BATTERY_CUTOFF_REQUEST, 0);
+
+		/* May lose power immediately, so commit our update now. */
+		VB2_TRY(vb2ex_commit_data(ctx));
+
+		vb2ex_ec_battery_cutoff();
+		return VB2_REQUEST_SHUTDOWN;
+	}
+
+	return VB2_SUCCESS;
+}
+
+vb2_error_t vb2api_kernel_phase2(struct vb2_context *ctx)
+{
+	struct vb2_shared_data *sd = vb2_get_sd(ctx);
+	vb2_gbb_flags_t gbb_flags = vb2api_gbb_get_flags(ctx);
+
+	VB2_DEBUG("GBB flags are %#x\n", gbb_flags);
+
+	/*
+	 * Do EC and auxfw software sync unless we're in recovery mode. This
+	 * has UI but it's just a single non-interactive WAIT screen.
+	 */
+	if (!(ctx->flags & VB2_CONTEXT_RECOVERY_MODE)) {
+		VB2_TRY(vb2api_ec_sync(ctx));
+		VB2_TRY(vb2api_auxfw_sync(ctx));
+		VB2_TRY(handle_battery_cutoff(ctx));
+	}
+
+	/*
+	 * If in the broken screen, save the recovery reason as subcode.
+	 * Otherwise, clear any leftover recovery requests or subcodes.
+	 */
+	vb2_clear_recovery(ctx);
+
+	/*
+	 * Clear the diagnostic request flag and commit nvdata to prevent
+	 * booting back into diagnostic mode when a forced system reset occurs.
+	 */
+	if (vb2_nv_get(ctx, VB2_NV_DIAG_REQUEST)) {
+		vb2_nv_set(ctx, VB2_NV_DIAG_REQUEST, 0);
+		/*
+		 * According to current FAFT design (firmware_MiniDiag), we
+		 * need an AP reset after MiniDiag test items to preserve the
+		 * CBMEM console logs. So we need to commit nvdata immediately
+		 * to prevent booting back to VB2_BOOT_MODE_DIAGNOSTICS.
+		 */
+		vb2ex_commit_data(ctx);
+	}
+
+	/* Select boot path */
+	switch (ctx->boot_mode) {
+	case VB2_BOOT_MODE_MANUAL_RECOVERY:
+	case VB2_BOOT_MODE_BROKEN_SCREEN:
+		/* If we're in recovery mode just to do memory retraining, all
+		   we need to do is reboot. */
+		if (sd->recovery_reason == VB2_RECOVERY_TRAIN_AND_REBOOT) {
+			VB2_DEBUG("Reboot after retraining in recovery\n");
+			return VB2_REQUEST_REBOOT;
+		}
+
+		/*
+		 * Need to commit nvdata changes immediately, since we will be
+		 * entering either manual recovery UI or BROKEN screen shortly.
+		 */
+		vb2ex_commit_data(ctx);
+		break;
+	case VB2_BOOT_MODE_DIAGNOSTICS:
+	case VB2_BOOT_MODE_DEVELOPER:
+		break;
+	case VB2_BOOT_MODE_NORMAL:
+		if (vb2_nv_get(ctx, VB2_NV_DISPLAY_REQUEST)) {
+			vb2_nv_set(ctx, VB2_NV_DISPLAY_REQUEST, 0);
+			VB2_DEBUG("Normal mode: "
+				  "reboot to unset display request\n");
+			return VB2_REQUEST_REBOOT;
+		}
+		break;
+	default:
+		return VB2_ERROR_ESCAPE_NO_BOOT;
+	}
+
+	return VB2_SUCCESS;
+}
+
+static void update_kernel_version(struct vb2_context *ctx)
+{
+	struct vb2_shared_data *sd = vb2_get_sd(ctx);
+	uint32_t max_rollforward =
+		vb2_nv_get(ctx, VB2_NV_KERNEL_MAX_ROLLFORWARD);
+
+	VB2_DEBUG("Checking if TPM kernel version needs advancing\n");
+
+	/*
+	 * Special case for when we're trying a slot with new firmware.
+	 * Firmware updates also usually change the kernel key, which means
+	 * that the new firmware can only boot a new kernel, and the old
+	 * firmware in the previous slot can only boot the previous kernel.
+	 *
+	 * Don't roll-forward the kernel version, because we don't yet know if
+	 * the new kernel will successfully boot.
+	 */
+	if (vb2_nv_get(ctx, VB2_NV_FW_RESULT) == VB2_FW_RESULT_TRYING) {
+		VB2_DEBUG("Trying new FW; "
+			  "skip kernel version roll-forward.\n");
+		return;
+	}
+
+	/*
+	 * Limit kernel version rollforward if needed.  Can't limit kernel
+	 * version to less than the version currently in the TPM.  That is,
+	 * we're limiting rollforward, not allowing rollback.
+	 */
+	if (max_rollforward < sd->kernel_version_secdata)
+		max_rollforward = sd->kernel_version_secdata;
+
+	if (sd->kernel_version > max_rollforward) {
+		VB2_DEBUG("Limiting TPM kernel version roll-forward "
+			  "to %#x < %#x\n",
+			  max_rollforward, sd->kernel_version);
+
+		sd->kernel_version = max_rollforward;
+	}
+
+	if (sd->kernel_version > sd->kernel_version_secdata) {
+		vb2_secdata_kernel_set(ctx, VB2_SECDATA_KERNEL_VERSIONS,
+				       sd->kernel_version);
+	}
+}
+
+vb2_error_t vb2api_kernel_finalize(struct vb2_context *ctx)
+{
+	vb2_gbb_flags_t gbb_flags = vb2api_gbb_get_flags(ctx);
+
+	/*
+	 * Disallow booting to kernel when NO_BOOT flag is set, except when
+	 * GBB flag disables software sync.
+	 */
+	if (!(gbb_flags & VB2_GBB_FLAG_DISABLE_EC_SOFTWARE_SYNC)
+	    && (ctx->flags & VB2_CONTEXT_EC_SYNC_SUPPORTED)
+	    && (ctx->flags & VB2_CONTEXT_NO_BOOT)) {
+		VB2_DEBUG("Blocking escape from NO_BOOT mode.\n");
+		vb2api_fail(ctx, VB2_RECOVERY_ESCAPE_NO_BOOT, 0);
+		return VB2_ERROR_ESCAPE_NO_BOOT;
+	}
+
+	if (ctx->boot_mode == VB2_BOOT_MODE_NORMAL)
+		update_kernel_version(ctx);
+
+	return VB2_SUCCESS;
+}
diff --git a/firmware/lib/vboot_kernel.c b/firmware/2lib/2load_kernel.c
similarity index 95%
rename from firmware/lib/vboot_kernel.c
rename to firmware/2lib/2load_kernel.c
index 1edf4a5..65e4053 100644
--- a/firmware/lib/vboot_kernel.c
+++ b/firmware/2lib/2load_kernel.c
@@ -16,7 +16,6 @@
 #include "cgptlib.h"
 #include "cgptlib_internal.h"
 #include "gpt_misc.h"
-#include "load_kernel_fw.h"
 #include "vboot_api.h"
 
 enum vb2_load_partition_flags {
@@ -348,7 +347,7 @@
  * @return VB2_SUCCESS, or non-zero error code.
  */
 static vb2_error_t vb2_load_partition(
-	struct vb2_context *ctx, VbSelectAndLoadKernelParams *params,
+	struct vb2_context *ctx, struct vb2_kernel_params *params,
 	VbExStream_t stream, uint32_t lpflags)
 {
 	uint32_t read_ms = 0, start_ts;
@@ -462,8 +461,8 @@
 }
 
 static vb2_error_t try_minios_kernel(struct vb2_context *ctx,
-				     VbSelectAndLoadKernelParams *params,
-				     VbDiskInfo *disk_info,
+				     struct vb2_kernel_params *params,
+				     struct vb2_disk_info *disk_info,
 				     uint64_t sector) {
 	VbExStream_t stream;
 	uint64_t sectors_left = disk_info->lba_count - sector;
@@ -471,7 +470,7 @@
 	vb2_error_t rv = VB2_ERROR_LK_NO_KERNEL_FOUND;
 
 	/* Re-open stream at correct offset to pass to vb2_load_partition. */
-	if (VbExStreamOpen(params->disk_handle, sector, sectors_left,
+	if (VbExStreamOpen(disk_info->handle, sector, sectors_left,
 			   &stream)) {
 		VB2_DEBUG("Unable to open disk handle.\n");
 		return rv;
@@ -488,8 +487,8 @@
 }
 
 static vb2_error_t try_minios_sectors(struct vb2_context *ctx,
-				      VbSelectAndLoadKernelParams *params,
-				      VbDiskInfo *disk_info,
+				      struct vb2_kernel_params *params,
+				      struct vb2_disk_info *disk_info,
 				      uint64_t start, uint64_t count)
 {
 	const uint32_t buf_size = count * disk_info->bytes_per_lba;
@@ -504,7 +503,7 @@
 		return rv;
 	}
 
-	if (VbExStreamOpen(params->disk_handle, start, count, &stream)) {
+	if (VbExStreamOpen(disk_info->handle, start, count, &stream)) {
 		VB2_DEBUG("Unable to open disk handle.\n");
 		free(buf);
 		return rv;
@@ -534,8 +533,8 @@
 }
 
 static vb2_error_t try_minios_sector_region(struct vb2_context *ctx,
-					    VbSelectAndLoadKernelParams *params,
-					    VbDiskInfo *disk_info,
+					    struct vb2_kernel_params *params,
+					    struct vb2_disk_info *disk_info,
 					    int end_region)
 {
 	const uint64_t disk_count_half = (disk_info->lba_count + 1) / 2;
@@ -577,14 +576,15 @@
  * the start and end of disks are considered, and the kernel must start exactly
  * at the first byte of the sector.
  */
-vb2_error_t LoadMiniOsKernel(struct vb2_context *ctx,
-			     VbSelectAndLoadKernelParams *params,
-			     VbDiskInfo *disk_info, uint32_t minios_flags)
+vb2_error_t vb2api_load_minios_kernel(struct vb2_context *ctx,
+				      struct vb2_kernel_params *params,
+				      struct vb2_disk_info *disk_info,
+				      uint32_t minios_flags)
 {
 	vb2_error_t rv;
 	int end_region_first = vb2_nv_get(ctx, VB2_NV_MINIOS_PRIORITY);
 
-	if (minios_flags & VB_MINIOS_FLAG_NON_ACTIVE)
+	if (minios_flags & VB2_MINIOS_FLAG_NON_ACTIVE)
 		rv = VB2_ERROR_UNKNOWN;  /* Ignore active partition */
 	else
 		rv = try_minios_sector_region(ctx, params, disk_info,
@@ -593,19 +593,16 @@
 	if (rv)
 		rv = try_minios_sector_region(ctx, params, disk_info,
 					      !end_region_first);
-	if (rv)
-		return rv;
 
-	rv = vb2ex_tpm_set_mode(VB2_TPM_MODE_DISABLED);
-	if (rv)
-		VB2_DEBUG("Failed to disable TPM\n");
+	if (rv == VB2_SUCCESS)
+		params->disk_handle = disk_info->handle;
 
 	return rv;
 }
 
-vb2_error_t LoadKernel(struct vb2_context *ctx,
-		       VbSelectAndLoadKernelParams *params,
-		       VbDiskInfo *disk_info)
+vb2_error_t vb2api_load_kernel(struct vb2_context *ctx,
+			       struct vb2_kernel_params *params,
+			       struct vb2_disk_info *disk_info)
 {
 	struct vb2_shared_data *sd = vb2_get_sd(ctx);
 	int found_partitions = 0;
@@ -621,7 +618,7 @@
 	gpt.streaming_drive_sectors = disk_info->streaming_lba_count
 		?: disk_info->lba_count;
 	gpt.gpt_drive_sectors = disk_info->lba_count;
-	gpt.flags = disk_info->flags & VB_DISK_FLAG_EXTERNAL_GPT
+	gpt.flags = disk_info->flags & VB2_DISK_FLAG_EXTERNAL_GPT
 			? GPT_FLAG_EXTERNAL : 0;
 	if (AllocAndReadGptData(disk_info->handle, &gpt)) {
 		VB2_DEBUG("Unable to read GPT data\n");
@@ -754,6 +751,7 @@
 
 		/* Success! */
 		rv = VB2_SUCCESS;
+		params->disk_handle = disk_info->handle;
 	} else if (found_partitions > 0) {
 		rv = VB2_ERROR_LK_INVALID_KERNEL_FOUND;
 	} else {
diff --git a/firmware/2lib/2misc.c b/firmware/2lib/2misc.c
index 6e92690..5735127 100644
--- a/firmware/2lib/2misc.c
+++ b/firmware/2lib/2misc.c
@@ -520,6 +520,7 @@
 		 VB2_SECDATA_KERNEL_FLAG_PHONE_RECOVERY_UI_DISABLED);
 }
 
+test_mockable
 int vb2api_diagnostic_ui_enabled(struct vb2_context *ctx)
 {
 	return !(vb2_secdata_kernel_get(ctx, VB2_SECDATA_KERNEL_FLAGS) &
diff --git a/firmware/2lib/2recovery_reasons.c b/firmware/2lib/2recovery_reasons.c
index 8c43dce..73fd812 100644
--- a/firmware/2lib/2recovery_reasons.c
+++ b/firmware/2lib/2recovery_reasons.c
@@ -113,7 +113,7 @@
 	/* 0x46 */ case VB2_RECOVERY_RW_SHARED_DATA:
 		return "Shared data error in rewritable firmware";
 	/* 0x47 */ case VB2_RECOVERY_DEPRECATED_RW_TEST_LK:
-		return "Test error from LoadKernel()";
+		return "Test error from vb2api_load_kernel()";
 	/* 0x48 */ case VB2_RECOVERY_DEPRECATED_RW_NO_DISK:
 		return "No bootable storage device in system";
 	/* 0x49 */ case VB2_RECOVERY_TPM_E_FAIL:
diff --git a/firmware/2lib/2rsa.c b/firmware/2lib/2rsa.c
index dcd8bad..eb07b04 100644
--- a/firmware/2lib/2rsa.c
+++ b/firmware/2lib/2rsa.c
@@ -14,7 +14,6 @@
 #include "2rsa_private.h"
 #include "2sha.h"
 #include "2sysincludes.h"
-#include "vboot_test.h"
 
 /**
  * a[] -= mod
diff --git a/firmware/2lib/2secdata_kernel.c b/firmware/2lib/2secdata_kernel.c
index 0d4208f..754552c 100644
--- a/firmware/2lib/2secdata_kernel.c
+++ b/firmware/2lib/2secdata_kernel.c
@@ -11,7 +11,6 @@
 #include "2secdata.h"
 #include "2secdata_struct.h"
 #include "2sysincludes.h"
-#include "vboot_test.h"
 
 #define MAJOR_VER(x) (((x) & 0xf0) >> 4)
 #define MINOR_VER(x) ((x) & 0x0f)
@@ -181,6 +180,7 @@
 	return VB2_SUCCESS;
 }
 
+test_mockable
 uint32_t vb2_secdata_kernel_get(struct vb2_context *ctx,
 				enum vb2_secdata_kernel_param param)
 {
@@ -213,6 +213,7 @@
 	return 0;
 }
 
+test_mockable
 void vb2_secdata_kernel_set(struct vb2_context *ctx,
 			    enum vb2_secdata_kernel_param param,
 			    uint32_t value)
@@ -273,6 +274,7 @@
 	VB2_REC_OR_DIE(ctx, "%s\n", msg);
 }
 
+test_mockable
 const uint8_t *vb2_secdata_kernel_get_ec_hash(struct vb2_context *ctx)
 {
 	struct vb2_shared_data *sd = vb2_get_sd(ctx);
diff --git a/firmware/2lib/2stub.c b/firmware/2lib/2stub.c
index da754ad..51d2eb2 100644
--- a/firmware/2lib/2stub.c
+++ b/firmware/2lib/2stub.c
@@ -38,11 +38,12 @@
 }
 
 __attribute__((weak))
-uint32_t vb2ex_mtime(void)
+vb2_error_t vb2ex_commit_data(struct vb2_context *ctx)
 {
-	struct timeval tv;
-	gettimeofday(&tv, NULL);
-	return tv.tv_sec * VB2_MSEC_PER_SEC + tv.tv_usec / VB2_USEC_PER_MSEC;
+	ctx->flags &= ~VB2_CONTEXT_SECDATA_FIRMWARE_CHANGED;
+	ctx->flags &= ~VB2_CONTEXT_SECDATA_KERNEL_CHANGED;
+	ctx->flags &= ~VB2_CONTEXT_NVDATA_CHANGED;
+	return VB2_SUCCESS;
 }
 
 __attribute__((weak))
@@ -159,105 +160,17 @@
 }
 
 /*****************************************************************************/
-/* UI-related stubs */
+/* Timer-related stubs */
 
 __attribute__((weak))
-const char *vb2ex_get_debug_info(struct vb2_context *ctx)
+uint32_t vb2ex_mtime(void)
 {
-	return NULL;
-}
-
-__attribute__((weak))
-const char *vb2ex_get_firmware_log(int reset)
-{
-	return NULL;
-}
-
-__attribute__((weak))
-vb2_error_t vb2ex_diag_get_storage_health(const char **out)
-{
-	*out = "mock";
-	return VB2_SUCCESS;
-}
-
-__attribute__((weak))
-vb2_error_t vb2ex_diag_get_storage_test_log(const char **out)
-{
-	*out = "mock";
-	return VB2_SUCCESS;
-}
-
-__attribute__((weak))
-vb2_error_t vb2ex_diag_memory_quick_test(int reset, const char **out)
-{
-	*out = "mock";
-	return VB2_SUCCESS;
-}
-
-__attribute__((weak))
-vb2_error_t vb2ex_diag_memory_full_test(int reset, const char **out)
-{
-	*out = "mock";
-	return VB2_SUCCESS;
+	struct timeval tv;
+	gettimeofday(&tv, NULL);
+	return tv.tv_sec * VB2_MSEC_PER_SEC + tv.tv_usec / VB2_USEC_PER_MSEC;
 }
 
 __attribute__((weak))
 void vb2ex_msleep(uint32_t msec)
 {
 }
-
-__attribute__((weak))
-void vb2ex_beep(uint32_t msec, uint32_t frequency)
-{
-}
-
-__attribute__((weak))
-uint32_t vb2ex_get_locale_count(void)
-{
-	return 0;
-}
-
-__attribute__((weak))
-uint32_t vb2ex_get_altfw_count(void)
-{
-	return 0;
-}
-
-__attribute__((weak))
-int vb2ex_physical_presence_pressed(void)
-{
-	return 0;
-}
-
-__attribute__((weak))
-vb2_error_t vb2ex_commit_data(struct vb2_context *ctx)
-{
-	ctx->flags &= ~VB2_CONTEXT_SECDATA_FIRMWARE_CHANGED;
-	ctx->flags &= ~VB2_CONTEXT_SECDATA_KERNEL_CHANGED;
-	ctx->flags &= ~VB2_CONTEXT_NVDATA_CHANGED;
-	return VB2_SUCCESS;
-}
-
-__attribute__((weak))
-vb2_error_t vb2ex_broken_screen_ui(struct vb2_context *ctx)
-{
-	return VB2_SUCCESS;
-}
-
-__attribute__((weak))
-vb2_error_t vb2ex_manual_recovery_ui(struct vb2_context *ctx)
-{
-	return VB2_SUCCESS;
-}
-
-__attribute__((weak))
-vb2_error_t vb2ex_developer_ui(struct vb2_context *ctx)
-{
-	return VB2_SUCCESS;
-}
-
-__attribute__((weak))
-vb2_error_t vb2ex_diagnostic_ui(struct vb2_context *ctx)
-{
-	return VB2_SUCCESS;
-}
diff --git a/firmware/2lib/include/2api.h b/firmware/2lib/include/2api.h
index 1430111..79283c2 100644
--- a/firmware/2lib/include/2api.h
+++ b/firmware/2lib/include/2api.h
@@ -24,6 +24,7 @@
 #include "2fw_hash_tags.h"
 #include "2gbb_flags.h"
 #include "2id.h"
+#include "2info.h"
 #include "2recovery_reasons.h"
 #include "2return_codes.h"
 #include "2rsa.h"
@@ -165,14 +166,14 @@
 
 	/*
 	 * System supports EC software sync.  Caller may set this flag at any
-	 * time before calling VbSelectAndLoadKernel().
+	 * time before calling vb2api_kernel_phase2().
 	 */
 	VB2_CONTEXT_EC_SYNC_SUPPORTED = (1 << 15),
 
 	/*
 	 * EC software sync is slow to update; warning screen should be
 	 * displayed.  Caller may set this flag at any time before calling
-	 * VbSelectAndLoadKernel().  Deprecated as part of chromium:1038259.
+	 * vb2api_kernel_phase2().  Deprecated as part of chromium:1038259.
 	 */
 	VB2_CONTEXT_DEPRECATED_EC_SYNC_SLOW = (1 << 16),
 
@@ -257,58 +258,12 @@
 	 * nvdata, FWMP or GBB flags.
 	 */
 	VB2_CONTEXT_DEV_BOOT_ALTFW_ALLOWED = (1 << 27),
-};
-
-/* Boot mode decided in vb2api_fw_phase1.
- *
- * Boot mode is a constant set by verified boot and may be read (but should not
- * be set or cleared) by the caller.
- * The boot modes are mutually exclusive. If a boot fulfill more than one
- * constraints of the listing boot modes, it will be set to the most important
- * one. The priority is the same as the listing order.
- */
-enum vb2_boot_mode {
-	/* Undefined, The boot mode is not set. */
-	VB2_BOOT_MODE_UNDEFINED = 0,
 
 	/*
-	 * Manual recovery boot, regardless of dev mode state.
-	 *
-	 * VB2_CONTEXT_RECOVERY_MODE is set and the recovery is physically
-	 * requested (a.k.a. Manual recovery).  All other recovery requests
-	 * including manual recovery requested by a (compromised) host will end
-	 * up with a broken screen.
+	 * If this is set after kernel verification, caller should disable the
+	 * TPM before jumping to kernel.
 	 */
-	VB2_BOOT_MODE_MANUAL_RECOVERY = 1,
-
-	/*
-	 * Broken screen.
-	 *
-	 * If a recovery boot is not a manual recovery (a.k.a. not requested
-	 * physically), the recovery is not allowed and will end up with
-	 * broken screen.
-	 */
-	VB2_BOOT_MODE_BROKEN_SCREEN = 2,
-
-	/*
-	 * Diagnostic boot.
-	 *
-	 * If diagnostic boot is enabled (a.k.a. vb2api_diagnostic_ui_enabled)
-	 * and the nvdata contains VB2_NV_DIAG_REQUEST from previous boot, it
-	 * will boot to diagnostic mode.
-	 */
-	VB2_BOOT_MODE_DIAGNOSTICS = 3,
-
-	/*
-	 * Developer boot: self-signed kernel okay.
-	 *
-	 * The developer mode switch is set (a.k.a. VB2_CONTEXT_DEVELOPER_MODE)
-	 * and we are in the developer boot mode.
-	 */
-	VB2_BOOT_MODE_DEVELOPER = 4,
-
-	/* Normal boot: kernel must be verified. */
-	VB2_BOOT_MODE_NORMAL = 5,
+	VB2_CONTEXT_DISABLE_TPM = (1 << 28),
 };
 
 /* Helper for aligning fields in vb2_context. */
@@ -391,10 +346,10 @@
 	 */
 
 	/*
-	 * Mutually exclusive boot mode.
+	 * Mutually exclusive boot mode (from enum vb2_boot_mode).
 	 * This constant is initialized after calling vb2api_fw_phase1().
 	 */
-	const enum vb2_boot_mode boot_mode;
+	const uint8_t boot_mode;
 };
 
 /* Resource index for vb2ex_read_resource() */
@@ -506,7 +461,8 @@
  *	use to verify kernel data - the recovery key from the GBB, or the
  *	kernel subkey from the firmware verification stage.
  *
- *	Kernel phase 2 is finding loading, and verifying the kernel partition.
+ *	Call vb2api_kernel_phase2().  Do EC and auxfw software sync, clear
+ *	recovery and commit nvdata if needed.
  *
  *	Find a boot device (you're on your own here).
  *
@@ -812,6 +768,142 @@
 vb2_error_t vb2api_kernel_phase1(struct vb2_context *ctx);
 
 /**
+ * Do kernel verification.
+ *
+ * Must be called after vb2api_kernel_phase1.
+ *
+ * @param ctx		Vboot context
+ * @return VB2_SUCCESS, or error code on error.
+ */
+vb2_error_t vb2api_kernel_phase2(struct vb2_context *ctx);
+
+/**
+ * Finalize for kernel verification stage.
+ *
+ * Handle NO_BOOT flag. Also, check and roll forward kernel version.
+ *
+ * @param ctx		Vboot context
+ * @return VB2_SUCCESS, or error code on error.
+ */
+vb2_error_t vb2api_kernel_finalize(struct vb2_context *ctx);
+
+struct vb2_kernel_params {
+	/* Inputs to vb2api_load_kernel(). */
+	/* Destination buffer for kernel (normally at 0x100000 on x86). */
+	void *kernel_buffer;
+	/* Size of kernel buffer in bytes. */
+	uint32_t kernel_buffer_size;
+
+	/*
+	 * Outputs from vb2api_load_kernel(); valid only if it returns success.
+	 */
+	/* Handle of disk containing loaded kernel. */
+	vb2ex_disk_handle_t disk_handle;
+	/* Partition number on disk to boot (1...M). */
+	uint32_t partition_number;
+	/* Address of bootloader image in RAM. */
+	uint64_t bootloader_address;
+	/* Size of bootloader image in bytes. */
+	uint32_t bootloader_size;
+	/* UniquePartitionGuid for boot partition. */
+	uint8_t partition_guid[16];
+	/* Flags set by signer. */
+	uint32_t flags;
+};
+
+/*****************************************************************************/
+/* Disk access */
+
+/* Flags for vb2_disk_info */
+
+/*
+ * Disk selection in the lower 16 bits (where the disk lives), and disk
+ * attributes in the higher 16 bits (extra information about the disk
+ * needed to access it correctly).
+ */
+#define VB2_DISK_FLAG_SELECT_MASK 0xffff
+#define VB2_DISK_FLAG_ATTRIBUTE_MASK (0xffff << 16)
+
+/*
+ * Disks are used in two ways:
+ * - As a random-access device to read and write the GPT
+ * - As a streaming device to read the kernel
+ * These are implemented differently on raw NAND vs eMMC/SATA/USB
+ * - On eMMC/SATA/USB, both of these refer to the same underlying
+ *   storage, so they have the same size and LBA size. In this case,
+ *   the GPT should not point to the same address as itself.
+ * - On raw NAND, the GPT is held on a portion of the SPI flash.
+ *   Random access GPT operations refer to the SPI and streaming
+ *   operations refer to NAND. The GPT may therefore point into
+ *   the same offsets as itself.
+ * These types are distinguished by the following flag and vb2_disk_info
+ * has separate fields to describe the random-access ("GPT") and
+ * streaming aspects of the disk. If a disk is random-access (i.e.
+ * not raw NAND) then these fields are equal.
+ */
+#define VB2_DISK_FLAG_EXTERNAL_GPT (1 << 16)
+
+/* Information on a single disk. */
+struct vb2_disk_info {
+	/* Disk handle. */
+	vb2ex_disk_handle_t handle;
+	/* Size of a random-access LBA sector in bytes. */
+	uint64_t bytes_per_lba;
+	/* Number of random-access LBA sectors on the device.
+	 * If streaming_lba_count is 0, this stands in for the size of the
+	 * randomly accessed portion as well as the streaming portion.
+	 * Otherwise, this is only the randomly-accessed portion. */
+	uint64_t lba_count;
+	/* Number of streaming sectors on the device. */
+	uint64_t streaming_lba_count;
+	/* Flags (see VB2_DISK_FLAG_* constants). */
+	uint32_t flags;
+	/*
+	 * Optional name string, for use in debugging.  May be empty or null if
+	 * not available.
+	 */
+	const char *name;
+};
+
+/**
+ * Attempt to load kernel from the specified device. On success, the output
+ * fields of params will be filled. The caller should set the input fields of
+ * params.
+ *
+ *
+ * @param ctx		Vboot context
+ * @param params	Params specific to loading the kernel
+ * @param disk_info	Disk from which to read kernel
+ *
+ * @return VB2_SUCCESS, or non-zero error code.
+ */
+vb2_error_t vb2api_load_kernel(struct vb2_context *ctx,
+			       struct vb2_kernel_params *params,
+			       struct vb2_disk_info *disk_info);
+
+/* miniOS flags */
+
+/* Boot from non-active miniOS partition only. */
+#define VB2_MINIOS_FLAG_NON_ACTIVE (1 << 0)
+
+/**
+ * Attempt to load miniOS kernel from the specified device. On success, the
+ * output fields of params will be filled. The caller should set the input
+ * fields of params.
+ *
+ * @param ctx		Vboot context
+ * @param params	Params specific to loading the kernel
+ * @param disk_info	Disk from which to read kernel
+ * @param minios_flags	Flags for miniOS
+ *
+ * @return VB2_SUCCESS, or non-zero error code.
+ */
+vb2_error_t vb2api_load_minios_kernel(struct vb2_context *ctx,
+				      struct vb2_kernel_params *params,
+				      struct vb2_disk_info *disk_info,
+				      uint32_t minios_flags);
+
+/**
  * Load the verified boot block (vblock) for a kernel.
  *
  * This function may be called multiple times, to load and verify the
@@ -1027,7 +1119,7 @@
 vb2_error_t vb2api_enable_developer_mode(struct vb2_context *ctx);
 
 /**
- * Request to disable developer mode by setting VB2_NV_DIAG_REQUEST.
+ * Request to disable developer mode by setting VB2_NV_DISABLE_DEV_REQUEST.
  *
  * @param ctx		Vboot context
  * @return VB2_SUCCESS if success; other errors if the check of
@@ -1433,124 +1525,7 @@
 vb2_error_t vb2ex_ec_battery_cutoff(void);
 
 /*****************************************************************************/
-/* Functions for UI display. */
-
-/**
- * UI for a non-manual recovery ("BROKEN").
- *
- * Enter the broken screen UI, which shows that an unrecoverable error was
- * encountered last boot. Wait for the user to physically reset or shut down.
- *
- * @param ctx		Vboot context
- * @return VB2_SUCCESS, or non-zero error code.
- */
-vb2_error_t vb2ex_broken_screen_ui(struct vb2_context *ctx);
-
-/**
- * UI for a manual recovery-mode boot.
- *
- * Enter the recovery menu, which prompts the user to insert recovery media,
- * navigate the step-by-step recovery, or enter developer mode if allowed.
- *
- * @param ctx		Vboot context
- * @return VB2_SUCCESS, or non-zero error code.
- */
-vb2_error_t vb2ex_manual_recovery_ui(struct vb2_context *ctx);
-
-/**
- * UI for a developer-mode boot.
- *
- * Enter the developer menu, which provides options to switch out of developer
- * mode, boot from external media, use legacy bootloader, or boot Chrome OS from
- * disk.
- *
- * If a timeout occurs, take the default boot action.
- *
- * @param ctx		Vboot context
- * @return VB2_SUCCESS, or non-zero error code.
- */
-vb2_error_t vb2ex_developer_ui(struct vb2_context *ctx);
-
-/**
- * UI for a diagnostic tools boot.
- *
- * Enter the diagnostic tools menu, which provides debug information and
- * diagnostic tests of various hardware components.
- *
- * @param ctx		Vboot context
- * @return VB2_SUCCESS, or non-zero error code.
- */
-vb2_error_t vb2ex_diagnostic_ui(struct vb2_context *ctx);
-
-/* Helpers for bitmask operations */
-#define VB2_SET_BIT(mask, index) ((mask) |= ((uint32_t)1 << (index)))
-#define VB2_CLR_BIT(mask, index) ((mask) &= ~((uint32_t)1 << (index)))
-#define VB2_GET_BIT(mask, index) ((mask) & ((uint32_t)1 << (index)))
-
-/**
- * Check that physical presence button is currently pressed by the user.
- *
- * @return 1 for pressed, 0 for not.
- */
-int vb2ex_physical_presence_pressed(void);
-
-/**
- * Get the number of supported locales.
- *
- * @return Number of locales.  0 if none or on error.
- */
-uint32_t vb2ex_get_locale_count(void);
-
-/**
- * Return the number of available alternate bootloaders.
- *
- * @return Number of alternate bootloaders.  0 if none or on error.
- */
-uint32_t vb2ex_get_altfw_count(void);
-
-/**
- * Run alternate bootloader.
- *
- * @param altfw_id	ID of alternate bootloader to run, where
- *                      altfw_id <= vb2ex_get_altfw_count().  0 for default,
- *                      which corresponds to an alternate bootloader in
- *                      the range 1 <= altfw_id <= vb2ex_getfw_count().
- * @return VB2_SUCCESS, or error code on error.
- */
-vb2_error_t vb2ex_run_altfw(uint32_t altfw_id);
-
-/**
- * Delay for at least the specified number of milliseconds.
- *
- * @param msec			Duration in milliseconds.
- */
-void vb2ex_msleep(uint32_t msec);
-
-/**
- * Play a beep tone of the specified frequency in Hz for the duration msec.
- *
- * This is effectively a sleep call that makes noise.  The implementation may
- * beep at a fixed frequency if frequency support is not available.  Regardless
- * of whether any errors occur, the callback is expected to delay for the
- * specified duration before returning.
- *
- * @param msec			Duration of beep in milliseconds.
- * @param frequency		Sound frequency in Hz.
- */
-void vb2ex_beep(uint32_t msec, uint32_t frequency);
-
-/**
- * Get the full debug info string.
- *
- * Return a pointer to the full debug info string which is guaranteed to be
- * null-terminated.  The function implementation should manage string memory
- * internally.  Subsequent calls may update the string and return the same
- * pointer, or return a new pointer if necessary.
- *
- * @param ctx		Vboot context
- * @return The pointer to the full debug info string.  NULL on error.
- */
-const char *vb2ex_get_debug_info(struct vb2_context *ctx);
+/* Functions for firmware UI. */
 
 /**
  * Get the vboot debug info.
@@ -1564,67 +1539,6 @@
  */
 char *vb2api_get_debug_info(struct vb2_context *ctx);
 
-/**
- * Get the full firmware log string.
- *
- * Return a pointer to the full firmware log string which is guaranteed to be
- * null-terminated.  The function implementation should snapshot the full
- * firmware log when it is called.  If `reset` is not zero, it will reset the
- * firmware log snapshot.
- *
- * @param reset		Discard the current firmware log snapshot and
- *			reacquire a new one.
- * @return The pointer to the full firmware log string.  NULL on error.
- */
-const char *vb2ex_get_firmware_log(int reset);
-
-/**
- * Get the health info of the storage.
- *
- * @param out	For returning a read-only pointer of full log string which is
- *		guaranteed to be null-terminated. The function will manage
- *		memory internally, so the returned pointer will only be valid
- *		until next call.
- * @return VB2_SUCCESS, or error code on error.
- */
-vb2_error_t vb2ex_diag_get_storage_health(const char **out);
-
-/**
- * Get the storage self-test log.
- *
- * @param out	For returning a read-only pointer of full log string which is
- *		guaranteed to be null-terminated. The function will manage
- *		memory internally, so the returned pointer will only be valid
- *		until next call.
- * @return The status of storage test. VB2_SUCCESS means the test is finished,
- * regardless of passing or failing. VB2_ERROR_EX_DIAG_TEST_RUNNING means
- * the test is still running. VB2_ERROR_EX_UNIMPLEMENTED means the storage
- * self-test is not supported on this device. Other non-zero codes for internal
- * errors.
- */
-vb2_error_t vb2ex_diag_get_storage_test_log(const char **out);
-
-/**
- * Get the memory diagnostic status. When it is called, it will take over the
- * control for a short period of time running memory test, and then return the
- * result of current status. If `reset` is not zero, it will reset the memory
- * test state.
- *
- * @param reset	Discard the current memory test result and re-initialize
- *		a new test.
- * @param out	For returning a read-only pointer of full log string which is
- *		guaranteed to be null-terminated. The function will manage
- *		memory internally, so the returned pointer will only be valid
- *		until next call.
- * @return The status of memory test. VB2_SUCCESS means the test is finished,
- * regardless of passing or failing. VB2_ERROR_EX_DIAG_TEST_RUNNING means
- * the test is still running but the output buffer was unchanged.
- * VB2_ERROR_EX_DIAG_TEST_UPDATED means the test is still running and the output
- * buffer was updated. Other non-zero codes for internal errors.
- */
-vb2_error_t vb2ex_diag_memory_quick_test(int reset, const char **out);
-vb2_error_t vb2ex_diag_memory_full_test(int reset, const char **out);
-
 /*****************************************************************************/
 /* Timer. */
 
@@ -1638,4 +1552,37 @@
  */
 uint32_t vb2ex_mtime(void);
 
+/**
+ * Delay for at least the specified number of milliseconds.
+ *
+ * @param msec			Duration in milliseconds.
+ */
+void vb2ex_msleep(uint32_t msec);
+
+union vb2_fw_boot_info {
+	uint8_t raw[4];
+	struct {
+		uint8_t tries       : 4;
+		uint8_t slot        : 1;
+		uint8_t prev_slot   : 1;
+		uint8_t prev_result : 2;
+		uint8_t boot_mode;
+		/* The following 2 bytes only exist for recovery mode */
+		uint8_t recovery_reason;
+		uint8_t recovery_subcode;
+	};
+};
+
+/**
+ * Return `vb2_fw_boot_info` and can be used
+ * to log information about the current boot in a compact format.
+ *
+ * Note: Only call this API at minimum after `vb2api_fw_phase2` function
+ * returns.
+ *
+ * @param ctx          Vboot context
+ * @return filled out vb2 info as per `union vb2_fw_boot_info`.
+ */
+union vb2_fw_boot_info vb2api_get_fw_boot_info(struct vb2_context *ctx);
+
 #endif  /* VBOOT_REFERENCE_2API_H_ */
diff --git a/firmware/2lib/include/2constants.h b/firmware/2lib/include/2constants.h
index 96dc999..c1e6bb9 100644
--- a/firmware/2lib/include/2constants.h
+++ b/firmware/2lib/include/2constants.h
@@ -80,6 +80,14 @@
 #ifndef __ASSEMBLER__
 #include <stdint.h>
 typedef uint32_t vb2_gbb_flags_t;
+/*
+ * We use disk handles rather than indices.  Using indices causes problems if
+ * a disk is removed/inserted in the middle of processing.
+ *
+ * TODO(b/181739551): move this to 2api.h when the VbExDisk* functions are
+ * removed from vboot_api.h.
+ */
+typedef void *vb2ex_disk_handle_t;
 #endif
 
 /* Size of legacy VbSharedDataHeader struct.  Defined here to avoid including
diff --git a/firmware/2lib/include/2info.h b/firmware/2lib/include/2info.h
new file mode 100644
index 0000000..be2ea60
--- /dev/null
+++ b/firmware/2lib/include/2info.h
@@ -0,0 +1,146 @@
+/* Copyright 2022 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.
+ *
+ * Helper functions to retrieve vboot firmware information.
+ */
+
+#ifndef VBOOT_REFERENCE_2INFO_H_
+#define VBOOT_REFERENCE_2INFO_H_
+
+/* Boot mode decided in vb2api_fw_phase1.
+ *
+ * Boot mode is a constant set by verified boot and may be read (but should not
+ * be set or cleared) by the caller.
+ * The boot modes are mutually exclusive. If a boot fulfill more than one
+ * constraints of the listing boot modes, it will be set to the most important
+ * one. The priority is the same as the listing order.
+ */
+enum vb2_boot_mode {
+	/* Undefined, The boot mode is not set. */
+	VB2_BOOT_MODE_UNDEFINED = 0,
+
+	/*
+	 * Manual recovery boot, regardless of dev mode state.
+	 *
+	 * VB2_CONTEXT_RECOVERY_MODE is set and the recovery is physically
+	 * requested (a.k.a. Manual recovery).  All other recovery requests
+	 * including manual recovery requested by a (compromised) host will end
+	 * up with a broken screen.
+	 */
+	VB2_BOOT_MODE_MANUAL_RECOVERY = 1,
+
+	/*
+	 * Broken screen.
+	 *
+	 * If a recovery boot is not a manual recovery (a.k.a. not requested
+	 * physically), the recovery is not allowed and will end up with
+	 * broken screen.
+	 */
+	VB2_BOOT_MODE_BROKEN_SCREEN = 2,
+
+	/*
+	 * Diagnostic boot.
+	 *
+	 * If diagnostic boot is enabled (a.k.a. vb2api_diagnostic_ui_enabled)
+	 * and the nvdata contains VB2_NV_DIAG_REQUEST from previous boot, it
+	 * will boot to diagnostic mode.
+	 */
+	VB2_BOOT_MODE_DIAGNOSTICS = 3,
+
+	/*
+	 * Developer boot: self-signed kernel okay.
+	 *
+	 * The developer mode switch is set (a.k.a. VB2_CONTEXT_DEVELOPER_MODE)
+	 * and we are in the developer boot mode.
+	 */
+	VB2_BOOT_MODE_DEVELOPER = 4,
+
+	/* Normal boot: kernel must be verified. */
+	VB2_BOOT_MODE_NORMAL = 5,
+};
+
+/* Firmware slot codes */
+enum vb2_fw_slot {
+	/* Slot A */
+	VB2_FW_SLOT_A = 0,
+
+	/* Slot B */
+	VB2_FW_SLOT_B = 1,
+};
+
+/* Firmware result codes for VB2_NV_FW_RESULT and VB2_NV_FW_PREV_RESULT */
+enum vb2_fw_result {
+	/* Unknown */
+	VB2_FW_RESULT_UNKNOWN = 0,
+
+	/* Trying a new slot, but haven't reached success/failure */
+	VB2_FW_RESULT_TRYING = 1,
+
+	/* Successfully booted to the OS */
+	VB2_FW_RESULT_SUCCESS = 2,
+
+	/* Known failure */
+	VB2_FW_RESULT_FAILURE = 3,
+};
+
+/**
+ * Convert Firmware Boot Mode into supported string
+ *
+ * @return char*   firmware boot mode string
+ */
+static inline const char *vb2_boot_mode_string(uint8_t boot_mode)
+{
+	switch ((enum vb2_boot_mode)boot_mode) {
+	/* 0x00 */ case VB2_BOOT_MODE_UNDEFINED:
+		return "Undefined";
+	/* 0x01 */ case VB2_BOOT_MODE_MANUAL_RECOVERY:
+		return "Manual recovery";
+	/* 0x02 */ case VB2_BOOT_MODE_BROKEN_SCREEN:
+		return "Broken screen";
+	/* 0x03 */ case VB2_BOOT_MODE_DIAGNOSTICS:
+		return "Diagnostic";
+	/* 0x04 */ case VB2_BOOT_MODE_DEVELOPER:
+		return "Developer";
+	/* 0x05 */ case VB2_BOOT_MODE_NORMAL:
+		return "Secure";
+	}
+
+	return "Unknown";
+}
+
+/**
+ * Convert Firmware Slot result into supported string
+ *
+ * @return char*   firmware slot result string
+ */
+static inline const char *vb2_result_string(uint8_t result)
+{
+	switch ((enum vb2_fw_result)result) {
+	/* 0x00 */ case VB2_FW_RESULT_UNKNOWN:
+		return "Unknown";
+	/* 0x01 */ case VB2_FW_RESULT_TRYING:
+		return "Trying";
+	/* 0x02 */ case VB2_FW_RESULT_SUCCESS:
+		return "Success";
+	/* 0x03 */ case VB2_FW_RESULT_FAILURE:
+		return "Failure";
+	}
+
+	return "Unknown";
+}
+
+/**
+ * Convert Firmware Slot into supported string
+ *
+ * @return char*   firmware slot name string
+ */
+static inline const char *vb2_slot_string(uint8_t slot)
+{
+	if ((enum vb2_fw_slot)slot == VB2_FW_SLOT_A)
+	/* 0x00 */ return "A";
+	else
+	/* 0x01 */ return "B";
+}
+
+#endif  /* VBOOT_REFERENCE_2INFO_H_ */
diff --git a/firmware/2lib/include/2kernel.h b/firmware/2lib/include/2kernel.h
deleted file mode 100644
index bed1a01..0000000
--- a/firmware/2lib/include/2kernel.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/* Copyright 2020 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.
- *
- * Kernel selection, loading, verification, and booting.
- */
-
-#ifndef VBOOT_REFERENCE_2KERNEL_H_
-#define VBOOT_REFERENCE_2KERNEL_H_
-
-#include "2common.h"
-
-/**
- * Handle a normal boot.
- *
- * @param ctx		Vboot context.
- * @return VB2_SUCCESS, or error code on error.
- */
-vb2_error_t vb2_normal_boot(struct vb2_context *ctx);
-
-#endif  /* VBOOT_REFERENCE_2KERNEL_H_ */
diff --git a/firmware/2lib/include/2nvstorage.h b/firmware/2lib/include/2nvstorage.h
index fe75823..a457250 100644
--- a/firmware/2lib/include/2nvstorage.h
+++ b/firmware/2lib/include/2nvstorage.h
@@ -129,21 +129,6 @@
 	VB2_NV_MINIOS_PRIORITY,
 };
 
-/* Firmware result codes for VB2_NV_FW_RESULT and VB2_NV_FW_PREV_RESULT */
-enum vb2_fw_result {
-	/* Unknown */
-	VB2_FW_RESULT_UNKNOWN = 0,
-
-	/* Trying a new slot, but haven't reached success/failure */
-	VB2_FW_RESULT_TRYING = 1,
-
-	/* Successfully booted to the OS */
-	VB2_FW_RESULT_SUCCESS = 2,
-
-	/* Known failure */
-	VB2_FW_RESULT_FAILURE = 3,
-};
-
 /*
  * Default value for VB2_NV_FIRMWARE_MAX_ROLLFORWARD on V1.  This preserves the
  * existing behavior that V1 systems will always roll forward the firmware
diff --git a/firmware/2lib/include/2recovery_reasons.h b/firmware/2lib/include/2recovery_reasons.h
index 30dbd2e..918e332 100644
--- a/firmware/2lib/include/2recovery_reasons.h
+++ b/firmware/2lib/include/2recovery_reasons.h
@@ -205,7 +205,7 @@
 	/* Shared data error in rewritable firmware */
 	VB2_RECOVERY_RW_SHARED_DATA = 0x46,
 
-	/* Test error from LoadKernel() (deprecated) */
+	/* Test error from vb2api_load_kernel() (deprecated) */
 	VB2_RECOVERY_DEPRECATED_RW_TEST_LK = 0x47,
 
 	/* No bootable disk found (deprecated, see 0x5a) */
diff --git a/firmware/2lib/include/2return_codes.h b/firmware/2lib/include/2return_codes.h
index 176c2c6..a7b7930 100644
--- a/firmware/2lib/include/2return_codes.h
+++ b/firmware/2lib/include/2return_codes.h
@@ -754,29 +754,32 @@
 	 * Deprecated: functionality removed with legacy UI (b/167643628) */
 	VB2_ERROR_DEPRECATED_EX_SET_VENDOR_DATA,
 
-	/* The memory test is running but the output buffer was unchanged. */
-	VB2_ERROR_EX_DIAG_TEST_RUNNING,
+	/* The memory test is running but the output buffer was unchanged.
+	   Deprecated with b/172339016. */
+	VB2_ERROR_DEPRECATED_EX_DIAG_TEST_RUNNING,
 
-	/* The memory test is running and the output buffer was updated. */
-	VB2_ERROR_EX_DIAG_TEST_UPDATED,
+	/* The memory test is running and the output buffer was updated.
+	   Deprecated with b/172339016. */
+	VB2_ERROR_DEPRECATED_EX_DIAG_TEST_UPDATED,
 
-	/* The memory test initialization failed. */
-	VB2_ERROR_EX_DIAG_TEST_INIT_FAILED,
+	/* The memory test initialization failed.
+	   Deprecated with b/172339016. */
+	VB2_ERROR_DEPRECATED_EX_DIAG_TEST_INIT_FAILED,
 
 	/**********************************************************************
-	 * LoadKernel errors
+	 * Kernel loading errors
 	 *
 	 * Should be ordered by specificity -- lower number means more specific.
 	 */
 	VB2_ERROR_LK = 0x100b0000,
 
-	/* Only an invalid kernel was found in LoadKernel() */
+	/* Only an invalid kernel was found in vb2api_load_kernel() */
 	VB2_ERROR_LK_INVALID_KERNEL_FOUND = 0x100b1000,
 
-	/* No kernel partitions were found in LoadKernel() */
+	/* No kernel partitions were found in vb2api_load_kernel() */
 	VB2_ERROR_LK_NO_KERNEL_FOUND = 0x100b2000,
 
-	/* No working block devices were found in VbTryLoadKernel() */
+	/* No working block devices were found */
 	VB2_ERROR_LK_NO_DISK_FOUND = 0x100b3000,
 
 	/**********************************************************************
@@ -843,6 +846,9 @@
 	/* Flashrom exited with failure status */
 	VB2_ERROR_FLASHROM,
 
+	/* cbfstool exited with failure status */
+	VB2_ERROR_CBFSTOOL,
+
 	/**********************************************************************
 	 * Errors generated by host library key functions
 	 */
diff --git a/firmware/include/gpt_misc.h b/firmware/include/gpt_misc.h
index aa46094..e01e0dc 100644
--- a/firmware/include/gpt_misc.h
+++ b/firmware/include/gpt_misc.h
@@ -76,9 +76,8 @@
  * depthcharge does not have logic to properly setup stored_on_device and
  * gpt_drive_sectors, but it does do a memset(gpt, 0, sizeof(GptData)). And so,
  * GPT_STORED_ON_DEVICE should be 0 to make stored_on_device compatible with
- * present behavior. At the same time, in vboot_kernel:LoadKernel(), and
- * cgpt_common:GptLoad(), we need to have simple shims to set gpt_drive_sectors
- * to drive_sectors.
+ * present behavior. At the same time, in vb2api_load_kernel() and GptLoad(),
+ * we need to have simple shims to set gpt_drive_sectors to drive_sectors.
  *
  * TODO(namnguyen): Remove those shims when the firmware can set these fields.
  */
@@ -157,12 +156,12 @@
  *
  * Returns 0 if successful, 1 if error.
  */
-int AllocAndReadGptData(VbExDiskHandle_t disk_handle, GptData *gptdata);
+int AllocAndReadGptData(vb2ex_disk_handle_t disk_handle, GptData *gptdata);
 
 /**
  * Write any changes for the GPT data back to the drive, then free the buffers.
  */
-int WriteAndFreeGptData(VbExDiskHandle_t disk_handle, GptData *gptdata);
+int WriteAndFreeGptData(vb2ex_disk_handle_t disk_handle, GptData *gptdata);
 
 /**
  * Return 1 if the entry is unused, 0 if it is used.
diff --git a/firmware/include/vboot_api.h b/firmware/include/vboot_api.h
index ce3ac2d..c2fc0ad 100644
--- a/firmware/include/vboot_api.h
+++ b/firmware/include/vboot_api.h
@@ -24,6 +24,7 @@
 #include <stdint.h>
 #include <stdlib.h>
 
+#include "../2lib/include/2constants.h"
 #include "../2lib/include/2return_codes.h"
 #include "gpt.h"
 
@@ -32,192 +33,12 @@
 #endif  /* __cplusplus */
 
 struct vb2_context;
+struct vb2_disk_info;
 typedef struct VbSharedDataHeader VbSharedDataHeader;
 
-
-/*****************************************************************************/
-/* Main entry points from firmware into vboot_reference */
-
-/*
- * We use disk handles rather than indices.  Using indices causes problems if
- * a disk is removed/inserted in the middle of processing.
- */
-typedef void *VbExDiskHandle_t;
-
-typedef struct VbSelectAndLoadKernelParams {
-	/* Inputs to VbSelectAndLoadKernel() */
-	/* Destination buffer for kernel (normally at 0x100000 on x86) */
-	void *kernel_buffer;
-	/* Size of kernel buffer in bytes */
-	uint32_t kernel_buffer_size;
-
-	/*
-	 * Outputs from VbSelectAndLoadKernel(); valid only if it returns
-	 * success.
-	 */
-	/* Handle of disk containing loaded kernel */
-	VbExDiskHandle_t disk_handle;
-	/* Partition number on disk to boot (1...M) */
-	uint32_t partition_number;
-	/* Address of bootloader image in RAM */
-	uint64_t bootloader_address;
-	/* Size of bootloader image in bytes */
-	uint32_t bootloader_size;
-	/* UniquePartitionGuid for boot partition */
-	uint8_t partition_guid[16];
-	/* Flags set by signer */
-	uint32_t flags;
-} VbSelectAndLoadKernelParams;
-
-/**
- * Select and loads the kernel.
- *
- * Returns VB2_SUCCESS if success, non-zero if error; on error, caller
- * should reboot. */
-vb2_error_t VbSelectAndLoadKernel(struct vb2_context *ctx,
-				  VbSelectAndLoadKernelParams *kparams);
-
-/**
- * Attempt loading a kernel from the specified type(s) of disks.
- *
- * If successful, sets kparams.disk_handle to the disk for the kernel and
- * returns VB2_SUCCESS.
- *
- * @param ctx			Vboot context
- * @param disk_flags		Flags to pass to VbExDiskGetInfo()
- * @return VB2_SUCCESS or the most specific VB2_ERROR_LK error.
- */
-vb2_error_t VbTryLoadKernel(struct vb2_context *ctx, uint32_t disk_flags);
-
-/* miniOS flags */
-
-/* Boot from non-active miniOS partition only */
-#define VB_MINIOS_FLAG_NON_ACTIVE (1 << 0)
-
-/**
- * Attempt loading a miniOS kernel from internal disk.
- *
- * Scans sectors at the start and end of the disk, and looks for miniOS kernels
- * starting at the beginning of the sector.  Attempts loading any miniOS
- * kernels found.
- *
- * If successful, sets lkp.disk_handle to the disk for the kernel and returns
- * VB2_SUCCESS.
- *
- * @param ctx			Vboot context
- * @param minios_flags		Flags for miniOS
- * @return VB2_SUCCESS or the most specific VB2_ERROR_LK error.
- */
-vb2_error_t VbTryLoadMiniOsKernel(struct vb2_context *ctx,
-				  uint32_t minios_flags);
-
 /*****************************************************************************/
 /* Disk access (previously in boot_device.h) */
 
-/* Flags for VbDisk APIs */
-
-/*
- * Disk selection in the lower 16 bits (where the disk lives), and disk
- * attributes in the higher 16 bits (extra information about the disk
- * needed to access it correctly).
- */
-#define VB_DISK_FLAG_SELECT_MASK 0xffff
-#define VB_DISK_FLAG_ATTRIBUTE_MASK (0xffff << 16)
-
-/* Disk is removable.  Example removable disks: SD cards, USB keys.  */
-#define VB_DISK_FLAG_REMOVABLE (1 << 0)
-/*
- * Disk is fixed.  If this flag is present, disk is internal to the system and
- * not removable.  Example fixed disks: internal SATA SSD, eMMC.
- */
-#define VB_DISK_FLAG_FIXED (1 << 1)
-/*
- * Note that VB_DISK_FLAG_REMOVABLE and VB_DISK_FLAG_FIXED are
- * mutually-exclusive for a single disk.  VbExDiskGetInfo() may specify both
- * flags to request disks of both types in a single call.
- *
- * At some point we could specify additional flags, but we don't currently
- * have a way to make use of these:
- *
- * USB              Device is known to be attached to USB.  Note that the SD
- *                  card reader inside x86 systems is attached to USB so this
- *                  isn't super useful.
- * SD               Device is known to be a SD card.  Note that external card
- *                  readers might not return this information, so also of
- *                  questionable use.
- * READ_ONLY        Device is known to be read-only.  Could be used by recovery
- *                  when processing read-only recovery image.
- */
-
-/*
- * Disks are used in two ways:
- * - As a random-access device to read and write the GPT
- * - As a streaming device to read the kernel
- * These are implemented differently on raw NAND vs eMMC/SATA/USB
- * - On eMMC/SATA/USB, both of these refer to the same underlying
- *   storage, so they have the same size and LBA size. In this case,
- *   the GPT should not point to the same address as itself.
- * - On raw NAND, the GPT is held on a portion of the SPI flash.
- *   Random access GPT operations refer to the SPI and streaming
- *   operations refer to NAND. The GPT may therefore point into
- *   the same offsets as itself.
- * These types are distinguished by the following flag and VbDiskInfo
- * has separate fields to describe the random-access ("GPT") and
- * streaming aspects of the disk. If a disk is random-access (i.e.
- * not raw NAND) then these fields are equal.
- */
-#define VB_DISK_FLAG_EXTERNAL_GPT (1 << 16)
-
-/* Information on a single disk */
-typedef struct VbDiskInfo {
-	/* Disk handle */
-	VbExDiskHandle_t handle;
-	/* Size of a random-access LBA sector in bytes */
-	uint64_t bytes_per_lba;
-	/* Number of random-access LBA sectors on the device.
-	 * If streaming_lba_count is 0, this stands in for the size of the
-	 * randomly accessed portion as well as the streaming portion.
-	 * Otherwise, this is only the randomly-accessed portion. */
-	uint64_t lba_count;
-	/* Number of streaming sectors on the device */
-	uint64_t streaming_lba_count;
-	/* Flags (see VB_DISK_FLAG_* constants) */
-	uint32_t flags;
-	/*
-	 * Optional name string, for use in debugging.  May be empty or null if
-	 * not available.
-	 */
-	const char *name;
-} VbDiskInfo;
-
-/**
- * Store information into [info] for all disks (storage devices) attached to
- * the system which match all of the disk_flags.
- *
- * On output, count indicates how many disks are present, and [infos_ptr]
- * points to a [count]-sized array of VbDiskInfo structs with the information
- * on those disks; this pointer must be freed by calling VbExDiskFreeInfo().
- * If count=0, infos_ptr may point to NULL.  If [infos_ptr] points to NULL
- * because count=0 or error, it is not necessary to call VbExDiskFreeInfo().
- *
- * A multi-function device (such as a 4-in-1 card reader) should provide
- * multiple disk handles.
- *
- * The firmware must not alter or free the list pointed to by [infos_ptr] until
- * VbExDiskFreeInfo() is called.
- */
-vb2_error_t VbExDiskGetInfo(VbDiskInfo **infos_ptr, uint32_t *count,
-			    uint32_t disk_flags);
-
-/**
- * Free a disk information list [infos] previously returned by
- * VbExDiskGetInfo().  If [preserve_handle] != NULL, the firmware must ensure
- * that handle remains valid after this call; all other handles from the info
- * list need not remain valid after this call.
- */
-vb2_error_t VbExDiskFreeInfo(VbDiskInfo *infos,
-			     VbExDiskHandle_t preserve_handle);
-
 /**
  * Read lba_count LBA sectors, starting at sector lba_start, from the disk,
  * into the buffer.
@@ -229,7 +50,7 @@
  * which as been removed), the function must return error but must not
  * crash.
  */
-vb2_error_t VbExDiskRead(VbExDiskHandle_t handle, uint64_t lba_start,
+vb2_error_t VbExDiskRead(vb2ex_disk_handle_t handle, uint64_t lba_start,
 			 uint64_t lba_count, void *buffer);
 
 /**
@@ -243,7 +64,7 @@
  * which as been removed), the function must return error but must not
  * crash.
  */
-vb2_error_t VbExDiskWrite(VbExDiskHandle_t handle, uint64_t lba_start,
+vb2_error_t VbExDiskWrite(vb2ex_disk_handle_t handle, uint64_t lba_start,
 			  uint64_t lba_count, const void *buffer);
 
 /* Streaming read interface */
@@ -263,7 +84,7 @@
  * device. It is not used to access the GPT. The size of the content addressed
  * is within streaming_lba_count.
  */
-vb2_error_t VbExStreamOpen(VbExDiskHandle_t handle, uint64_t lba_start,
+vb2_error_t VbExStreamOpen(vb2ex_disk_handle_t handle, uint64_t lba_start,
 			   uint64_t lba_count, VbExStream_t *stream_ptr);
 
 /**
diff --git a/firmware/lib/gpt_misc.c b/firmware/lib/gpt_misc.c
index 641ef37..b42d466 100644
--- a/firmware/lib/gpt_misc.c
+++ b/firmware/lib/gpt_misc.c
@@ -19,7 +19,7 @@
  *
  * Returns 0 if successful, 1 if error.
  */
-int AllocAndReadGptData(VbExDiskHandle_t disk_handle, GptData *gptdata)
+int AllocAndReadGptData(vb2ex_disk_handle_t disk_handle, GptData *gptdata)
 {
 	int primary_valid = 0, secondary_valid = 0;
 
@@ -126,7 +126,7 @@
  *
  * Returns 0 if successful, 1 if error.
  */
-int WriteAndFreeGptData(VbExDiskHandle_t disk_handle, GptData *gptdata)
+int WriteAndFreeGptData(vb2ex_disk_handle_t disk_handle, GptData *gptdata)
 {
 	int skip_primary = 0;
 	GptHeader *header;
diff --git a/firmware/lib/include/load_kernel_fw.h b/firmware/lib/include/load_kernel_fw.h
deleted file mode 100644
index 76d2556..0000000
--- a/firmware/lib/include/load_kernel_fw.h
+++ /dev/null
@@ -1,41 +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.
- *
- * High-level firmware API for loading and verifying kernel.
- * (Firmware Portion)
- */
-
-#ifndef VBOOT_REFERENCE_LOAD_KERNEL_FW_H_
-#define VBOOT_REFERENCE_LOAD_KERNEL_FW_H_
-
-#include "vboot_api.h"
-
-/**
- * Attempt to load kernel from the specified device.
- *
- * @param ctx		Vboot context
- * @param params	Params specific to loading the kernel
- * @param disk_info	Disk from which to read kernel
- *
- * Returns VB2_SUCCESS if successful.  If unsuccessful, returns an error code.
- */
-vb2_error_t LoadKernel(struct vb2_context *ctx,
-		       VbSelectAndLoadKernelParams *params,
-		       VbDiskInfo *disk_info);
-
-/**
- * Attempt to load miniOS kernel from the specified device.
- *
- * @param ctx		Vboot context
- * @param params	Params specific to loading the kernel
- * @param disk_info	Disk from which to read kernel
- * @param minios_flags	Flags for miniOS
- *
- * Returns VB2_SUCCESS if successful.  If unsuccessful, returns an error code.
- */
-vb2_error_t LoadMiniOsKernel(struct vb2_context *ctx,
-			     VbSelectAndLoadKernelParams *params,
-			     VbDiskInfo *disk_info, uint32_t minios_flags);
-
-#endif  /* VBOOT_REFERENCE_LOAD_KERNEL_FW_H_ */
diff --git a/firmware/lib/include/vboot_struct.h b/firmware/lib/include/vboot_struct.h
index 374dfd4..ea4a9ee 100644
--- a/firmware/lib/include/vboot_struct.h
+++ b/firmware/lib/include/vboot_struct.h
@@ -35,9 +35,9 @@
    Deprecated as part of chromium:1010389. */
 #define VBSD_FWB_TRIED        0x00000001
 /*
- * LoadKernel() verified the good kernel keyblock using the kernel subkey from
- * the firmware.  If this flag is not present, it just used the hash of the
- * kernel keyblock.
+ * vb2api_load_kernel() verified the good kernel keyblock using the kernel
+ * subkey from the firmware.  If this flag is not present, it just used the
+ * hash of the kernel keyblock.
  */
 #define VBSD_KERNEL_KEY_VERIFIED         0x00000002
 /* Developer switch was enabled at boot time */
@@ -111,21 +111,8 @@
 	/* Firmware lowest version found */
 	uint32_t fw_version_lowest;
 
-	/* Debugging information from LoadKernel() */
-	/* Number of times LoadKernel() called */
-	uint32_t lk_call_count;
 	/* Reserved for padding */
-	uint8_t reserved3[896];
-
-	/*
-	 * Offset and size of supplemental kernel data.  Reserve space for
-	 * these fields now, so that future LoadKernel() versions can store
-	 * information there without needing to shift down whatever data the
-	 * original LoadFirmware() might have put immediately following its
-	 * VbSharedDataHeader.
-	 */
-	uint64_t kernel_supplemental_offset;
-	uint64_t kernel_supplemental_size;
+	uint8_t reserved3[916];
 
 	/*
 	 * Fields added in version 2.  Before accessing, make sure that
@@ -137,7 +124,7 @@
 	uint8_t reserved4[7];
 	/* Flags from firmware keyblock */
 	uint64_t fw_keyblock_flags;
-	/* Kernel TPM version at start of VbSelectAndLoadKernel() */
+	/* Kernel TPM version at start of vb2api_kernel_phase1 */
 	uint32_t kernel_version_tpm_start;
 	/* Kernel lowest version found */
 	uint32_t kernel_version_lowest;
diff --git a/firmware/lib/include/vboot_test.h b/firmware/lib/include/vboot_test.h
deleted file mode 100644
index fb1f52a..0000000
--- a/firmware/lib/include/vboot_test.h
+++ /dev/null
@@ -1,17 +0,0 @@
-/* Copyright 2019 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 header is for APIs that are only used by test code.
- */
-
-#ifndef VBOOT_REFERENCE_TEST_API_H_
-#define VBOOT_REFERENCE_TEST_API_H_
-
-/****************************************************************************
- * vboot_api_kernel.c */
-
-struct VbSelectAndLoadKernelParams;
-struct VbSelectAndLoadKernelParams **VbApiKernelGetParamsPtr(void);
-
-#endif  /* VBOOT_REFERENCE_TEST_API_H_ */
diff --git a/firmware/lib/vboot_api_kernel.c b/firmware/lib/vboot_api_kernel.c
deleted file mode 100644
index 9e89620..0000000
--- a/firmware/lib/vboot_api_kernel.c
+++ /dev/null
@@ -1,264 +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.
- *
- * High-level firmware wrapper API - entry points for kernel selection
- */
-
-#include "2api.h"
-#include "2common.h"
-#include "2kernel.h"
-#include "2misc.h"
-#include "2nvstorage.h"
-#include "2rsa.h"
-#include "2secdata.h"
-#include "2sysincludes.h"
-#include "load_kernel_fw.h"
-#include "vboot_api.h"
-#include "vboot_struct.h"
-#include "vboot_test.h"
-
-/* Global variables */
-static VbSelectAndLoadKernelParams *kparams_ptr;
-
-#ifdef CHROMEOS_ENVIRONMENT
-/* Global variable accessor for unit tests */
-struct VbSelectAndLoadKernelParams **VbApiKernelGetParamsPtr(void)
-{
-	return &kparams_ptr;
-}
-#endif
-
-static vb2_error_t handle_battery_cutoff(struct vb2_context *ctx)
-{
-	/*
-	 * Check if we need to cut-off battery. This should be done after EC
-	 * FW and auxfw are updated, and before the kernel is started.  This
-	 * is to make sure all firmware is up-to-date before shipping (which
-	 * is the typical use-case for cutoff).
-	 */
-	if (vb2_nv_get(ctx, VB2_NV_BATTERY_CUTOFF_REQUEST)) {
-		VB2_DEBUG("Request to cut-off battery\n");
-		vb2_nv_set(ctx, VB2_NV_BATTERY_CUTOFF_REQUEST, 0);
-
-		/* May lose power immediately, so commit our update now. */
-		VB2_TRY(vb2ex_commit_data(ctx));
-
-		vb2ex_ec_battery_cutoff();
-		return VB2_REQUEST_SHUTDOWN;
-	}
-
-	return VB2_SUCCESS;
-}
-
-static int is_valid_disk(VbDiskInfo *info, uint32_t disk_flags)
-{
-	return info->bytes_per_lba >= 512 &&
-		(info->bytes_per_lba & (info->bytes_per_lba - 1)) == 0 &&
-		info->lba_count >= 16 &&
-		(info->flags & disk_flags & VB_DISK_FLAG_SELECT_MASK) &&
-		((info->flags & VB_DISK_FLAG_SELECT_MASK) &
-		 ((info->flags & VB_DISK_FLAG_SELECT_MASK) - 1)) == 0;
-}
-
-static vb2_error_t VbTryLoadKernelImpl(struct vb2_context *ctx,
-				       uint32_t disk_flags, int minios,
-				       uint32_t minios_flags)
-{
-	vb2_error_t rv = VB2_ERROR_LK_NO_DISK_FOUND;
-	VbDiskInfo* disk_info = NULL;
-	uint32_t disk_count = 0;
-	uint32_t i;
-	vb2_error_t new_rv;
-
-	/* TODO: Should have been set by VbSelectAndLoadKernel. Remove when
-	   this global is no longer needed. */
-	VB2_ASSERT(kparams_ptr);
-
-	kparams_ptr->disk_handle = NULL;
-
-	/* Find disks */
-	if (VB2_SUCCESS != VbExDiskGetInfo(&disk_info, &disk_count, disk_flags))
-		disk_count = 0;
-
-	/* Loop over disks */
-	for (i = 0; i < disk_count; i++) {
-		VB2_DEBUG("trying disk %d\n", (int)i);
-
-		if (!is_valid_disk(&disk_info[i], disk_flags)) {
-			VB2_DEBUG("  skipping: bytes_per_lba=%" PRIu64
-				  " lba_count=%" PRIu64 " flags=%#x\n",
-				  disk_info[i].bytes_per_lba,
-				  disk_info[i].lba_count,
-				  disk_info[i].flags);
-			continue;
-		}
-		kparams_ptr->disk_handle = disk_info[i].handle;
-
-		if (minios) {
-			new_rv = LoadMiniOsKernel(ctx, kparams_ptr,
-						  &disk_info[i], minios_flags);
-			VB2_DEBUG("LoadMiniOsKernel() = %#x\n", new_rv);
-		} else {
-			new_rv = LoadKernel(ctx, kparams_ptr, &disk_info[i]);
-			VB2_DEBUG("LoadKernel() = %#x\n", new_rv);
-		}
-
-		/* Stop now if we found a kernel. */
-		if (VB2_SUCCESS == new_rv) {
-			VbExDiskFreeInfo(disk_info, disk_info[i].handle);
-			return VB2_SUCCESS;
-		}
-
-		/* Don't update error if we already have a more specific one. */
-		if (VB2_ERROR_LK_INVALID_KERNEL_FOUND != rv)
-			rv = new_rv;
-	}
-
-	/* If we drop out of the loop, we didn't find any usable kernel. */
-	if (!(ctx->flags & VB2_CONTEXT_RECOVERY_MODE) &&
-	    !(ctx->flags & VB2_CONTEXT_DEVELOPER_MODE)) {
-		switch (rv) {
-		case VB2_ERROR_LK_INVALID_KERNEL_FOUND:
-			vb2api_fail(ctx, VB2_RECOVERY_RW_INVALID_OS, rv);
-			break;
-		case VB2_ERROR_LK_NO_KERNEL_FOUND:
-			vb2api_fail(ctx, VB2_RECOVERY_RW_NO_KERNEL, rv);
-			break;
-		case VB2_ERROR_LK_NO_DISK_FOUND:
-			vb2api_fail(ctx, VB2_RECOVERY_RW_NO_DISK, rv);
-			break;
-		default:
-			vb2api_fail(ctx, VB2_RECOVERY_LK_UNSPECIFIED, rv);
-			break;
-		}
-	}
-
-	/* If we didn't find any good kernels, don't return a disk handle. */
-	VbExDiskFreeInfo(disk_info, NULL);
-
-	return rv;
-}
-
-test_mockable
-vb2_error_t VbTryLoadKernel(struct vb2_context *ctx, uint32_t disk_flags)
-{
-	return VbTryLoadKernelImpl(ctx, disk_flags, 0, 0);
-}
-
-test_mockable
-vb2_error_t VbTryLoadMiniOsKernel(struct vb2_context *ctx,
-				  uint32_t minios_flags)
-{
-	return VbTryLoadKernelImpl(ctx, VB_DISK_FLAG_FIXED, 1, minios_flags);
-}
-
-vb2_error_t VbSelectAndLoadKernel(struct vb2_context *ctx,
-				  VbSelectAndLoadKernelParams *kparams)
-{
-	struct vb2_shared_data *sd = vb2_get_sd(ctx);
-	vb2_gbb_flags_t gbb_flags = vb2api_gbb_get_flags(ctx);
-
-	/* TODO: Send this argument through subsequent function calls, rather
-	   than relying on a global to pass it to VbTryLoadKernel. */
-	kparams_ptr = kparams;
-
-	/* Init nvstorage space. TODO(kitching): Remove once we add assertions
-	   to vb2_nv_get and vb2_nv_set. */
-	vb2_nv_init(ctx);
-
-	VB2_TRY(vb2api_kernel_phase1(ctx));
-
-	VB2_DEBUG("GBB flags are %#x\n", gbb_flags);
-
-	/*
-	 * Do EC and auxfw software sync unless we're in recovery mode. This
-	 * has UI but it's just a single non-interactive WAIT screen.
-	 */
-	if (!(ctx->flags & VB2_CONTEXT_RECOVERY_MODE)) {
-		VB2_TRY(vb2api_ec_sync(ctx));
-		VB2_TRY(vb2api_auxfw_sync(ctx));
-		VB2_TRY(handle_battery_cutoff(ctx));
-	}
-
-	/*
-	 * If in the broken screen, save the recovery reason as subcode.
-	 * Otherwise, clear any leftover recovery requests or subcodes.
-	 */
-	vb2_clear_recovery(ctx);
-
-	/* Select boot path */
-	switch (ctx->boot_mode) {
-	case VB2_BOOT_MODE_MANUAL_RECOVERY:
-	case VB2_BOOT_MODE_BROKEN_SCREEN:
-		/* If we're in recovery mode just to do memory retraining, all
-		   we need to do is reboot. */
-		if (sd->recovery_reason == VB2_RECOVERY_TRAIN_AND_REBOOT) {
-			VB2_DEBUG("Reboot after retraining in recovery\n");
-			return VB2_REQUEST_REBOOT;
-		}
-
-		/*
-		 * Need to commit nvdata changes immediately, since we will be
-		 * entering either manual recovery UI or BROKEN screen shortly.
-		 */
-		vb2ex_commit_data(ctx);
-
-		/*
-		 * In EFS2, recovery mode can be entered even when battery is
-		 * drained or damaged. EC-RO sets NO_BOOT flag in such case and
-		 * uses PD power to boot AP.
-		 *
-		 * TODO: Inform user why recovery failed to start.
-		 */
-		if (ctx->flags & VB2_CONTEXT_NO_BOOT)
-			VB2_DEBUG("NO_BOOT in RECOVERY mode\n");
-
-		/* Recovery boot.  This has UI. */
-		if (ctx->boot_mode == VB2_BOOT_MODE_MANUAL_RECOVERY)
-			VB2_TRY(vb2ex_manual_recovery_ui(ctx));
-		else
-			VB2_TRY(vb2ex_broken_screen_ui(ctx));
-		break;
-	case VB2_BOOT_MODE_DIAGNOSTICS:
-		/*
-		 * Need to clear the request flag and commit nvdata changes
-		 * immediately to avoid booting back into diagnostic tool when a
-		 * forced system reset occurs.
-		 */
-		vb2_nv_set(ctx, VB2_NV_DIAG_REQUEST, 0);
-		vb2ex_commit_data(ctx);
-
-		/* Diagnostic boot.  This has UI. */
-		VB2_TRY(vb2ex_diagnostic_ui(ctx));
-		/*
-		 * The diagnostic menu should either boot a rom, or
-		 * return either of reboot or shutdown.
-		 */
-		return VB2_REQUEST_REBOOT;
-	case VB2_BOOT_MODE_DEVELOPER:
-		/* Developer boot.  This has UI. */
-		VB2_TRY(vb2ex_developer_ui(ctx));
-		break;
-	case VB2_BOOT_MODE_NORMAL:
-		/* Normal boot */
-		VB2_TRY(vb2_normal_boot(ctx));
-		break;
-	default:
-		return VB2_ERROR_ESCAPE_NO_BOOT;
-	}
-
-	/*
-	 * Stop all cases returning SUCCESS against NO_BOOT flag except when
-	 * GBB flag disables software sync.
-	 */
-	if (!(gbb_flags & VB2_GBB_FLAG_DISABLE_EC_SOFTWARE_SYNC)
-	    && (ctx->flags & VB2_CONTEXT_EC_SYNC_SUPPORTED)
-	    && (ctx->flags & VB2_CONTEXT_NO_BOOT)) {
-		VB2_DEBUG("Blocking escape from NO_BOOT mode.\n");
-		vb2api_fail(ctx, VB2_RECOVERY_ESCAPE_NO_BOOT, 0);
-		return VB2_ERROR_ESCAPE_NO_BOOT;
-	}
-
-	return VB2_SUCCESS;
-}
diff --git a/firmware/stub/vboot_api_stub.c b/firmware/stub/vboot_api_stub.c
deleted file mode 100644
index 9335c8b..0000000
--- a/firmware/stub/vboot_api_stub.c
+++ /dev/null
@@ -1,25 +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.
- *
- * Stub implementations of firmware-provided API functions.
- */
-
-#include <stdint.h>
-
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/time.h>
-
-#include "2api.h"
-#include "2common.h"
-#include "vboot_api.h"
-#include "vboot_test.h"
-
-__attribute__((weak))
-vb2_error_t vb2ex_run_altfw(uint32_t altfw_id)
-{
-	return VB2_SUCCESS;
-}
diff --git a/firmware/stub/vboot_api_stub_disk.c b/firmware/stub/vboot_api_stub_disk.c
index 3244dc6..b09388e 100644
--- a/firmware/stub/vboot_api_stub_disk.c
+++ b/firmware/stub/vboot_api_stub_disk.c
@@ -15,35 +15,15 @@
 #include "2common.h"
 #include "vboot_api.h"
 
-
 __attribute__((weak))
-vb2_error_t VbExDiskGetInfo(VbDiskInfo** infos_ptr, uint32_t* count,
-			    uint32_t disk_flags)
-{
-	*infos_ptr = NULL;
-	*count = 0;
-	return VB2_SUCCESS;
-}
-
-
-__attribute__((weak))
-vb2_error_t VbExDiskFreeInfo(VbDiskInfo* infos_ptr,
-			     VbExDiskHandle_t preserve_handle)
-{
-	return VB2_SUCCESS;
-}
-
-
-__attribute__((weak))
-vb2_error_t VbExDiskRead(VbExDiskHandle_t handle, uint64_t lba_start,
+vb2_error_t VbExDiskRead(vb2ex_disk_handle_t handle, uint64_t lba_start,
 			 uint64_t lba_count, void* buffer)
 {
 	return VB2_SUCCESS;
 }
 
-
 __attribute__((weak))
-vb2_error_t VbExDiskWrite(VbExDiskHandle_t handle, uint64_t lba_start,
+vb2_error_t VbExDiskWrite(vb2ex_disk_handle_t handle, uint64_t lba_start,
 			  uint64_t lba_count, const void* buffer)
 {
 	return VB2_SUCCESS;
diff --git a/firmware/stub/vboot_api_stub_stream.c b/firmware/stub/vboot_api_stub_stream.c
index 68e7ec7..1a6a65c 100644
--- a/firmware/stub/vboot_api_stub_stream.c
+++ b/firmware/stub/vboot_api_stub_stream.c
@@ -16,7 +16,7 @@
 /* Internal struct to simulate a stream for sector-based disks */
 struct disk_stream {
 	/* Disk handle */
-	VbExDiskHandle_t handle;
+	vb2ex_disk_handle_t handle;
 
 	/* Next sector to read */
 	uint64_t sector;
@@ -26,7 +26,7 @@
 };
 
 __attribute__((weak))
-vb2_error_t VbExStreamOpen(VbExDiskHandle_t handle, uint64_t lba_start,
+vb2_error_t VbExStreamOpen(vb2ex_disk_handle_t handle, uint64_t lba_start,
 			   uint64_t lba_count, VbExStream_t *stream)
 {
 	struct disk_stream *s;
diff --git a/futility/cmd_gscvd.c b/futility/cmd_gscvd.c
index 7fff293..458c754 100644
--- a/futility/cmd_gscvd.c
+++ b/futility/cmd_gscvd.c
@@ -40,9 +40,9 @@
  *   AP firmware file is ~/tmp/guybrush-signed:
  *
   ./build/futility/futility gscvd --outfile ~/tmp/guybrush-signed \
-     -R 818100:10000,f00000:100,f80000:2000,f8c000:1000,0x00804000:0x00000800 \
-     -k ~/tmp/packed -p tests/devkeys/arv_platform.vbprivk -b 5a5a4352  \
-     -r tests/devkeys/arv_root.vbpubk ~/tmp/image-guybrush.serial.bin
+    -R 818100:10000,f00000:100,f80000:2000,f8c000:1000,0x00804000:0x00000800 \
+    -k ~/tmp/packed -p tests/devkeys/arv_platform.vbprivk -b 5a5a4352  \
+    -r tests/devkeys/arv_root.vbpubk ~/tmp/image-guybrush.serial.bin
  *------------
  * Command to validate a previously signed AP firmware file. The hash is the
  *  sha256sum of tests/devkeys/kernel_subkey.vbpubk:
@@ -60,6 +60,7 @@
 	/* name       hasarg *flag  val */
 	{"outfile",       1, NULL, OPT_OUTFILE},
 	{"ranges",        1, NULL, 'R'},
+	{"add_gbb",       0, NULL, 'G'},
 	{"board_id",      1, NULL, 'b'},
 	{"root_pub_key",  1, NULL, 'r'},
 	{"keyblock",      1, NULL, 'k'},
@@ -68,7 +69,7 @@
 	{}
 };
 
-static const char *short_opts = "R:b:hk:p:r:";
+static const char *short_opts = "R:Gb:hk:p:r:";
 
 static const char usage[] =
 	"\n"
@@ -82,8 +83,15 @@
 	"                                     hex tuples <offset>:<size>, the\n"
 	"                                     areas of the RO covered by the\n"
 	"                                     signature\n"
-	"  -b|--board_id  <hex value>      The Board ID of the board for which\n"
-	"                                     the image is being signed\n"
+	"  -G|--add_gbb                     Add the `GBB` FMAP section to the\n"
+	"                                     ranges covered by the signature.\n"
+	"                                     This option takes special care\n"
+	"                                     to exclude the HWID (and its\n"
+	"                                     digest) from this range.\n"
+	"  -b|--board_id  <string|hex>      The Board ID of the board for\n"
+	"                                     which the image is signed.\n"
+	"                                     Can be passed as a 4-letter\n"
+	"                                     string or a hexadecimal number.\n"
 	"  -r|--root_pub_key  <file>        The main public key, in .vbpubk\n"
 	"                                     format, used to verify platform\n"
 	"                                     key\n"
@@ -140,36 +148,25 @@
  *
  * @return 0 on success 1 on failure.
  */
-static int load_ap_firmware(const char *file_name, struct file_buf *file)
+static int load_ap_firmware(const char *file_name, struct file_buf *file,
+			int mode)
 {
-	int fd;
-	int rv;
+	if (futil_open_and_map_file(file_name, &file->fd, mode, &file->data,
+				    &file->len))
+		return 1;
 
-	fd = open(file_name, O_RDWR);
-	if (fd < 0) {
-		ERROR("Can't open %s: %s\n", file_name,
-		      strerror(errno));
+	if (!fmap_find_by_name(file->data, file->len, NULL, "RO_GSCVD",
+			       &file->ro_gscvd)) {
+		ERROR("Could not find RO_GSCVD in the FMAP\n");
+		futil_unmap_and_close_file(file->fd, mode, file->data,
+					   file->len);
+		file->fd = -1;
+		file->data = NULL;
+		file->len = 0;
 		return 1;
 	}
 
-	file->fd = fd;
-	do {
-		rv = 1;
-
-		if (futil_map_file(fd, MAP_RW, &file->data, &file->len)) {
-			file->data = NULL;
-			break;
-		}
-
-		if (!fmap_find_by_name(file->data, file->len, NULL, "RO_GSCVD",
-				       &file->ro_gscvd)) {
-			ERROR("Could not find RO_GSCVD in the FMAP\n");
-			break;
-		}
-		rv = 0;
-	} while (false);
-
-	return rv;
+	return 0;
 }
 
 /**
@@ -191,9 +188,6 @@
 	    in_range(range->offset + range->size, ah))
 		return true;
 
-	ERROR("Range %#x..+%#x does not fit in %s\n", range->offset,
-	      range->size, ah->area_name);
-
 	return false;
 }
 
@@ -237,6 +231,7 @@
 {
 	size_t i;
 	FmapAreaHeader *wp_ro;
+	FmapAreaHeader *si_all;
 	int errorcount;
 
 	if (!fmap_find_by_name(file->data, file->len, NULL, "WP_RO", &wp_ro)) {
@@ -244,13 +239,24 @@
 		return 1;
 	}
 
+	/* Intel boards can have an SI_ALL region that's not in WP_RO but is
+	   protected by platform-specific mechanisms, and may still contain
+	   components that we want to protect from physical attack. */
+	if (!fmap_find_by_name(file->data, file->len, NULL, "SI_ALL", &si_all))
+		si_all = NULL;
+
 	errorcount = 0;
 	for (i = 0; i < ranges->range_count; i++) {
 		size_t j;
 
-		/* Must fit into WP_RO. */
-		if (!range_fits(ranges->ranges + i, wp_ro))
+		/* Must fit into WP_RO or SI_ALL. */
+		if (!range_fits(ranges->ranges + i, wp_ro) &&
+		    (!si_all || !range_fits(ranges->ranges + i, si_all))) {
+			ERROR("Range %#x..+%#x does not fit in WP_RO/SI_ALL\n",
+				ranges->ranges[i].offset,
+				ranges->ranges[i].size);
 			errorcount++;
+		}
 
 		/* Must not overlap with RO_GSCVD. */
 		if (range_overlaps(ranges->ranges + i,
@@ -296,7 +302,6 @@
 		return -1;
 	}
 
-	output->range_count = 0;
 	cursor = str;
 	do {
 		char *colon;
@@ -346,6 +351,71 @@
 }
 
 /**
+ * Add GBB to ranges.
+ *
+ * Splits the `GBB` FMAP section into separate ranges to exclude the HWID string
+ * and the `hwid_digest` field in the header. Will also exclude the empty area
+ * behind the end of the actual GBB data.
+ *
+ * @param ranges pointer to the ranges container
+ * @param file   pointer to the AP firmware file layout descriptor
+ */
+static int add_gbb(struct gscvd_ro_ranges *ranges, const struct file_buf *file)
+{
+	FmapAreaHeader *area;
+
+	if (!fmap_find_by_name(file->data, file->len, NULL, "GBB", &area)) {
+		ERROR("Could not find a GBB area in the FMAP.\n");
+		return 1;
+	}
+
+	struct vb2_gbb_header *gbb = (void *)file->data + area->area_offset;
+	uint32_t maxlen;
+
+	if (!futil_valid_gbb_header(gbb, area->area_size, &maxlen)) {
+		ERROR("GBB is invalid.\n");
+		return 1;
+	}
+
+	/*
+	 * This implementation relies on the fact that no meaningful fields come
+	 * after the `hwid_digest` field in the header. If we ever make new GBB
+	 * versions that add more fields, the code below needs to be adapted.
+	 * Older versions than 1.2 or GBBs with a bmpblk are not expected with
+	 * GSCVD images.
+	 */
+	if (gbb->major_version != 1 || gbb->minor_version != 2 ||
+	    gbb->bmpfv_size != 0) {
+		ERROR("Unsupported GBB version.\n");
+		return 1;
+	}
+
+	uint32_t lower_key_offset = VB2_MIN(gbb->rootkey_offset,
+					    gbb->recovery_key_offset);
+	if (gbb->hwid_offset > lower_key_offset) {
+		ERROR("Weird GBB layout (HWID should come first)\n");
+		return 1;
+	}
+
+	if (ranges->range_count >= ARRAY_SIZE(ranges->ranges) - 2) {
+		ERROR("Too many ranges, can't fit GBB!\n");
+		return 1;
+	}
+
+	ranges->ranges[ranges->range_count].offset = area->area_offset;
+	ranges->ranges[ranges->range_count].size =
+				offsetof(struct vb2_gbb_header, hwid_digest);
+	ranges->range_count++;
+
+	ranges->ranges[ranges->range_count].offset = area->area_offset +
+						     lower_key_offset;
+	ranges->ranges[ranges->range_count].size = maxlen - lower_key_offset;
+	ranges->range_count++;
+
+	return 0;
+}
+
+/**
  * Calculate hash of the RO ranges.
  *
  * @param ap_firmware_file  pointer to the AP firmware file layout descriptor
@@ -807,7 +877,7 @@
 
 		rv = -1; /* Speculative, will be cleared on success. */
 
-		if (load_ap_firmware(file_name, &ap_firmware_file))
+		if (load_ap_firmware(file_name, &ap_firmware_file, FILE_RO))
 			break;
 
 		/* Copy ranges from gscvd to local structure. */
@@ -842,16 +912,24 @@
 			break;
 		}
 
-		if (validate_pubk_signature(&gvd->root_key_header,
-					    kblock))
+		if (validate_pubk_signature(&gvd->root_key_header, kblock)) {
+			ERROR("Keyblock not signed by root key\n");
 			break;
+		}
 
-		if (validate_gvd_signature(gvd, &kblock->data_key))
+		if (validate_gvd_signature(gvd, &kblock->data_key)) {
+			ERROR("GVD not signed by platform key\n");
 			break;
+		}
 
 		rv = 0;
 	} while (false);
 
+	if (ap_firmware_file.fd != -1)
+		futil_unmap_and_close_file(ap_firmware_file.fd, FILE_RO,
+					   ap_firmware_file.data,
+					   ap_firmware_file.len);
+
 	return rv;
 }
 
@@ -890,6 +968,7 @@
 {
 	int i;
 	int longindex;
+	bool do_gbb = false;
 	char *infile = NULL;
 	char *outfile = NULL;
 	char *work_file = NULL;
@@ -918,13 +997,24 @@
 				errorcount++;
 			}
 			break;
+		case 'G':
+			do_gbb = true;
+			break;
 		case 'b': {
 			char *e;
 			long long bid;
 
+			if (strlen(optarg) == 4) {
+				board_id = optarg[0] << 24 |
+					   optarg[1] << 16 |
+					   optarg[2] << 8 |
+					   optarg[3];
+				break;
+			}
+
 			bid = strtoull(optarg, &e, 16);
 			if (*e || (bid >= UINT32_MAX)) {
-				ERROR("Board ID value '%s' is invalid\n",
+				ERROR("Cannot parse Board ID '%s'\n",
 				      optarg);
 				errorcount++;
 			} else {
@@ -980,15 +1070,36 @@
 		return validate_gscvd(argc - 1, argv + 1);
 
 	if (optind != (argc - 1)) {
-		ERROR("Misformatted command line\n%s\n", usage);
-		return 1;
+		ERROR("Misformatted command line\n");
+		goto usage_out;
 	}
 
-	if (errorcount || !ranges.range_count || !root_pubk || !kblock ||
-	    !plat_privk || (board_id == UINT32_MAX)) {
-		/* Error message(s) should have been printed by now. */
-		ERROR("%s\n", usage);
-		return 1;
+	if (errorcount) /* Error message(s) should have been printed by now. */
+		goto usage_out;
+
+	if (!root_pubk) {
+		ERROR("Missing --root_pub_key argument\n");
+		goto usage_out;
+	}
+
+	if (!kblock) {
+		ERROR("Missing --keyblock argument\n");
+		goto usage_out;
+	}
+
+	if (!plat_privk) {
+		ERROR("Missing --platform_priv argument\n");
+		goto usage_out;
+	}
+
+	if (board_id == UINT32_MAX) {
+		ERROR("Missing --board_id argument\n");
+		goto usage_out;
+	}
+
+	if (!ranges.range_count && !do_gbb) {
+		ERROR("Missing --ranges argument\n");
+		goto usage_out;
 	}
 
 	infile = argv[optind];
@@ -1009,7 +1120,10 @@
 		if (validate_privk(kblock, plat_privk))
 			break;
 
-		if (load_ap_firmware(work_file, &ap_firmware_file))
+		if (load_ap_firmware(work_file, &ap_firmware_file, FILE_RW))
+			break;
+
+		if (do_gbb && add_gbb(&ranges, &ap_firmware_file))
 			break;
 
 		if (verify_ranges(&ranges, &ap_firmware_file))
@@ -1033,17 +1147,16 @@
 	free(kblock);
 	vb2_private_key_free(plat_privk);
 
-	/* Now flush the file. */
-	if (ap_firmware_file.data) {
-		rv |= futil_unmap_file(ap_firmware_file.fd, true,
-				       ap_firmware_file.data,
-				       ap_firmware_file.len);
-	}
-
 	if (ap_firmware_file.fd != -1)
-		close(ap_firmware_file.fd);
+		futil_unmap_and_close_file(ap_firmware_file.fd, FILE_RW,
+					   ap_firmware_file.data,
+					   ap_firmware_file.len);
 
 	return rv;
+
+usage_out:
+	fputs(usage, stderr);
+	return 1;
 }
 
 DECLARE_FUTIL_COMMAND(gscvd, do_gscvd, VBOOT_VERSION_2_1,
diff --git a/futility/cmd_load_fmap.c b/futility/cmd_load_fmap.c
index 818fda8..4ab194e 100644
--- a/futility/cmd_load_fmap.c
+++ b/futility/cmd_load_fmap.c
@@ -149,22 +149,16 @@
 	else
 		outfile = infile;
 
-	fd = open(outfile, O_RDWR);
-	if (fd < 0) {
-		fprintf(stderr, "Can't open %s: %s\n",
-			outfile, strerror(errno));
-		return 1;
-	}
+	errorcnt |= futil_open_and_map_file(outfile, &fd, FILE_RW, &buf, &len);
 
-	errorcnt |= futil_map_file(fd, MAP_RW, &buf, &len);
 	if (errorcnt)
-		goto done_file;
+		goto done;
 
 	fmap = fmap_find(buf, len);
 	if (!fmap) {
 		fprintf(stderr, "Can't find an FMAP in %s\n", infile);
 		errorcnt++;
-		goto done_map;
+		goto done;
 	}
 
 	for (i = optind; i < argc; i++) {
@@ -190,20 +184,10 @@
 		}
 	}
 
-done_map:
-	errorcnt |= futil_unmap_file(fd, 1, buf, len);
-
-done_file:
-
-	if (0 != close(fd)) {
-		fprintf(stderr, "Error closing %s: %s\n",
-			outfile, strerror(errno));
-		errorcnt++;
-	}
-
+done:
+	errorcnt |= futil_unmap_and_close_file(fd, FILE_RW, buf, len);
 	return !!errorcnt;
 }
 
 DECLARE_FUTIL_COMMAND(load_fmap, do_load_fmap, VBOOT_VERSION_ALL,
 		      "Replace the contents of specified FMAP areas");
-
diff --git a/futility/cmd_show.c b/futility/cmd_show.c
index dbe6edd..9816106 100644
--- a/futility/cmd_show.c
+++ b/futility/cmd_show.c
@@ -86,31 +86,49 @@
 	       packed_key_sha1_string(data_key));
 }
 
-int ft_show_pubkey(const char *name, uint8_t *buf, uint32_t len, void *data)
+int ft_show_pubkey(const char *name, void *data)
 {
-	struct vb2_packed_key *pubkey = (struct vb2_packed_key *)buf;
+	int fd = -1;
+	struct vb2_packed_key *pubkey;
+	uint32_t len;
+	int rv = 0;
+
+	if (futil_open_and_map_file(name, &fd, FILE_RO, (uint8_t **)&pubkey,
+				     &len))
+		return 1;
 
 	if (vb2_packed_key_looks_ok(pubkey, len)) {
 		printf("%s looks bogus\n", name);
-		return 1;
+		rv = 1;
+		goto done;
 	}
 
 	printf("Public Key file:       %s\n", name);
 	show_pubkey(pubkey, "  ");
 
-	return 0;
+done:
+	futil_unmap_and_close_file(fd, FILE_RO, (uint8_t *)pubkey, len);
+	return rv;
 }
 
-int ft_show_privkey(const char *name, uint8_t *buf, uint32_t len, void *data)
+int ft_show_privkey(const char *name, void *data)
 {
-	struct vb2_packed_private_key *pkey =
-		(struct vb2_packed_private_key *)buf;
+	int fd = -1;
+	int rv = 0;
+	struct vb2_packed_private_key *pkey = NULL;
+	uint32_t len;
 	struct vb2_private_key key;
-	const unsigned char *start = pkey->key_data;
+	const unsigned char *start;
 
+	if (futil_open_and_map_file(name, &fd, FILE_RO, (uint8_t **)&pkey,
+				     &len))
+		return 1;
+
+	start = pkey->key_data;
 	if (len <= sizeof(*pkey)) {
 		printf("%s looks bogus\n", name);
-		return 1;
+		rv = 1;
+		goto done;
 	}
 	len -= sizeof(*pkey);
 	key.rsa_private_key = d2i_RSAPrivateKey(NULL, &start, len);
@@ -122,20 +140,30 @@
 	printf("  Key sha1sum:         %s\n",
 	       private_key_sha1_string(&key));
 
-	return 0;
+done:
+	futil_unmap_and_close_file(fd, FILE_RO, (uint8_t *)pkey, len);
+	return rv;
 }
 
-int ft_show_keyblock(const char *name, uint8_t *buf, uint32_t len, void *data)
+int ft_show_keyblock(const char *name, void *data)
 {
-	struct vb2_keyblock *block = (struct vb2_keyblock *)buf;
+	struct vb2_keyblock *block;
 	struct vb2_public_key *sign_key = show_option.k;
 	int good_sig = 0;
 	int retval = 0;
+	int fd = -1;
+	uint32_t len;
+
+	retval = futil_open_and_map_file(name, &fd, FILE_RO, (uint8_t **)&block,
+					 &len);
+	if (retval)
+		return 1;
 
 	/* Check the hash only first */
 	if (0 != vb2_verify_keyblock_hash(block, len, &wb)) {
 		printf("%s is invalid\n", name);
-		return 1;
+		retval = 1;
+		goto done;
 	}
 
 	/* Check the signature if we have one */
@@ -148,11 +176,13 @@
 
 	show_keyblock(block, name, !!sign_key, good_sig);
 
+done:
+	futil_unmap_and_close_file(fd, FILE_RO, (uint8_t *)block, len);
 	return retval;
 }
 
-int ft_show_fw_preamble(const char *name, uint8_t *buf, uint32_t len,
-			void *data)
+int show_fw_preamble_buf(const char *name, uint8_t *buf, uint32_t len,
+			 void *data)
 {
 	struct vb2_keyblock *keyblock = (struct vb2_keyblock *)buf;
 	struct bios_state_s *state = (struct bios_state_s *)data;
@@ -282,17 +312,40 @@
 	return retval;
 }
 
-int ft_show_kernel_preamble(const char *name, uint8_t *buf, uint32_t len,
-			    void *data)
+int ft_show_fw_preamble(const char *name, void *data)
 {
-	struct vb2_keyblock *keyblock = (struct vb2_keyblock *)buf;
+	int rv = 0;
+	int fd = -1;
+	uint8_t *buf;
+	uint32_t len;
+
+	if (futil_open_and_map_file(name, &fd, FILE_RO, &buf, &len))
+		return 1;
+
+	rv = show_fw_preamble_buf(name, buf, len, data);
+
+	futil_unmap_and_close_file(fd, FILE_RO, buf, len);
+	return rv;
+}
+
+int ft_show_kernel_preamble(const char *name, void *data)
+{
+	struct vb2_keyblock *keyblock;
 	struct vb2_public_key *sign_key = show_option.k;
-	int retval = 0;
+	int retval = 1;
+	int fd = -1;
+	uint8_t *buf;
+	uint32_t len;
+
+	if (futil_open_and_map_file(name, &fd, FILE_RO, &buf, &len))
+		return 1;
+
+	keyblock = (struct vb2_keyblock *)buf;
 
 	/* Check the hash... */
 	if (VB2_SUCCESS != vb2_verify_keyblock_hash(keyblock, len, &wb)) {
 		printf("%s keyblock component is invalid\n", name);
-		return 1;
+		goto done;
 	}
 
 	/* If we have a key, check the signature too */
@@ -304,13 +357,10 @@
 	printf("Kernel partition:        %s\n", name);
 	show_keyblock(keyblock, NULL, !!sign_key, good_sig);
 
-	if (show_option.strict && (!sign_key || !good_sig))
-		retval = 1;
-
 	struct vb2_public_key data_key;
 	if (VB2_SUCCESS != vb2_unpack_key(&data_key, &keyblock->data_key)) {
 		fprintf(stderr, "Error parsing data key in %s\n", name);
-		return 1;
+		goto done;
 	}
 
 	uint32_t more = keyblock->keyblock_size;
@@ -320,7 +370,7 @@
 	if (VB2_SUCCESS != vb2_verify_kernel_preamble(pre2, len - more,
 						      &data_key, &wb)) {
 		printf("%s is invalid\n", name);
-		return 1;
+		goto done;
 	}
 
 	printf("Kernel Preamble:\n");
@@ -367,20 +417,24 @@
 	if (!kernel_blob) {
 		/* TODO: Is this always a failure? The preamble is okay. */
 		fprintf(stderr, "No kernel blob available to verify.\n");
-		return 1;
+		goto done;
 	}
 
 	if (VB2_SUCCESS !=
 	    vb2_verify_data(kernel_blob, kernel_size, &pre2->body_signature,
 			    &data_key, &wb)) {
 		fprintf(stderr, "Error verifying kernel body.\n");
-		return 1;
+		goto done;
 	}
 
 	printf("Body verification succeeded.\n");
 
 	printf("Config:\n%s\n", kernel_blob + kernel_cmd_line_offset(pre2));
 
+	if (!show_option.strict || (sign_key && good_sig))
+		retval = 0;
+done:
+	futil_unmap_and_close_file(fd, FILE_RO, buf, len);
 	return retval;
 }
 
@@ -476,9 +530,8 @@
 	uint8_t *pubkbuf = NULL;
 	struct vb2_public_key pubk2;
 	char *infile = 0;
-	int ifd, i;
+	int i;
 	int errorcnt = 0;
-	uint8_t *buf;
 	uint32_t len;
 	char *e = 0;
 	int type_override = 0;
@@ -585,34 +638,14 @@
 
 	for (i = optind; i < argc; i++) {
 		infile = argv[i];
-		ifd = open(infile, O_RDONLY);
-		if (ifd < 0) {
-			errorcnt++;
-			fprintf(stderr, "Can't open %s: %s\n",
-				infile, strerror(errno));
-			continue;
-		}
-
-		if (0 != futil_map_file(ifd, MAP_RO, &buf, &len)) {
-			errorcnt++;
-			goto boo;
-		}
 
 		/* Allow the user to override the type */
 		if (type_override)
 			type = show_option.type;
 		else
-			type = futil_file_type_buf(buf, len);
+			futil_file_type(infile, &type);
 
-		errorcnt += futil_file_type_show(type, infile, buf, len);
-
-		errorcnt += futil_unmap_file(ifd, MAP_RO, buf, len);
-boo:
-		if (close(ifd)) {
-			errorcnt++;
-			fprintf(stderr, "Error when closing %s: %s\n",
-				infile, strerror(errno));
-		}
+		errorcnt += futil_file_type_show(type, infile);
 	}
 
 done:
@@ -635,4 +668,5 @@
 
 DECLARE_FUTIL_COMMAND(verify, do_verify,
 		      VBOOT_VERSION_ALL,
-		      "Verify the signatures of various binary components");
+		      "Verify the signatures of various binary components. "
+		      "This does not verify GSCVD contents.");
diff --git a/futility/cmd_sign.c b/futility/cmd_sign.c
index be8b634..2f9a017 100644
--- a/futility/cmd_sign.c
+++ b/futility/cmd_sign.c
@@ -29,8 +29,11 @@
 #include "util_misc.h"
 #include "vb1_helper.h"
 
+#define DEFAULT_KEYSETDIR "/usr/share/vboot/devkeys"
+
 /* Options */
 struct sign_option_s sign_option = {
+	.keysetdir = DEFAULT_KEYSETDIR,
 	.version = 1,
 	.arch = ARCH_UNSPECIFIED,
 	.kloadaddr = CROS_32BIT_ENTRY_ADDR,
@@ -55,14 +58,21 @@
 }
 
 /* This wraps/signs a public key, producing a keyblock. */
-int ft_sign_pubkey(const char *name, uint8_t *buf, uint32_t len, void *data)
+int ft_sign_pubkey(const char *name, void *data)
 {
-	struct vb2_packed_key *data_key = (struct vb2_packed_key *)buf;
+	struct vb2_packed_key *data_key;
+	uint32_t data_len;
 	struct vb2_keyblock *block;
+	int rv = 1;
+	int fd = -1;
 
-	if (vb2_packed_key_looks_ok(data_key, len)) {
-		fprintf(stderr, "Public key looks bad.\n");
+	if (futil_open_and_map_file(name, &fd, FILE_MODE_SIGN(sign_option),
+				    (uint8_t **)&data_key, &data_len))
 		return 1;
+
+	if (vb2_packed_key_looks_ok(data_key, data_len)) {
+		fprintf(stderr, "Public key looks bad.\n");
+		goto done;
 	}
 
 	if (sign_option.pem_signpriv) {
@@ -82,7 +92,7 @@
 				fprintf(stderr,
 					"Unable to read PEM signing key: %s\n",
 					strerror(errno));
-				return 1;
+				goto done;
 			}
 			block = vb2_create_keyblock(data_key,
 						    sign_option.signprivate,
@@ -95,20 +105,24 @@
 	}
 
 	/* Write it out */
-	return WriteSomeParts(sign_option.outfile,
-			      block, block->keyblock_size,
-			      NULL, 0);
+	rv = WriteSomeParts(sign_option.outfile, block, block->keyblock_size,
+			    NULL, 0);
+done:
+	futil_unmap_and_close_file(fd, FILE_MODE_SIGN(sign_option),
+				   (uint8_t *)data_key, data_len);
+	return rv;
 }
 
-int ft_sign_raw_kernel(const char *name, uint8_t *buf, uint32_t len,
-		       void *data)
+int ft_sign_raw_kernel(const char *name, void *data)
 {
-	uint8_t *vmlinuz_data, *kblob_data, *vblock_data;
+	uint8_t *vmlinuz_data = NULL, *kblob_data = NULL, *vblock_data = NULL;
 	uint32_t vmlinuz_size, kblob_size, vblock_size;
-	int rv;
+	int rv = 1;
+	int fd = -1;
 
-	vmlinuz_data = buf;
-	vmlinuz_size = len;
+	if (futil_open_and_map_file(name, &fd, FILE_MODE_SIGN(sign_option),
+				    &vmlinuz_data, &vmlinuz_size))
+		return 1;
 
 	kblob_data = CreateKernelBlob(
 		vmlinuz_data, vmlinuz_size,
@@ -118,7 +132,7 @@
 		&kblob_size);
 	if (!kblob_data) {
 		fprintf(stderr, "Unable to create kernel blob\n");
-		return 1;
+		goto done;
 	}
 	VB2_DEBUG("kblob_size = %#x\n", kblob_size);
 
@@ -131,8 +145,7 @@
 				     sign_option.flags, &vblock_size);
 	if (!vblock_data) {
 		fprintf(stderr, "Unable to sign kernel blob\n");
-		free(kblob_data);
-		return 1;
+		goto done;
 	}
 	VB2_DEBUG("vblock_size = %#x\n", vblock_size);
 
@@ -150,22 +163,26 @@
 				    vblock_data, vblock_size,
 				    kblob_data, kblob_size);
 
+done:
+	futil_unmap_and_close_file(fd, FILE_MODE_SIGN(sign_option),
+				   vmlinuz_data, vmlinuz_size);
 	free(vblock_data);
 	free(kblob_data);
 	return rv;
 }
 
-int ft_sign_kern_preamble(const char *name, uint8_t *buf, uint32_t len,
-			  void *data)
+int ft_sign_kern_preamble(const char *name, void *data)
 {
-	uint8_t *kpart_data, *kblob_data, *vblock_data;
+	uint8_t *kpart_data = NULL, *kblob_data = NULL, *vblock_data = NULL;
 	uint32_t kpart_size, kblob_size, vblock_size;
 	struct vb2_keyblock *keyblock = NULL;
 	struct vb2_kernel_preamble *preamble = NULL;
-	int rv = 0;
+	int rv = 1;
+	int fd = -1;
 
-	kpart_data = buf;
-	kpart_size = len;
+	if (futil_open_and_map_file(name, &fd, FILE_MODE_SIGN(sign_option),
+				    &kpart_data, &kpart_size))
+		return 1;
 
 	/* Note: This just sets some static pointers. It doesn't malloc. */
 	kblob_data = unpack_kernel_partition(kpart_data, kpart_size,
@@ -174,7 +191,7 @@
 
 	if (!kblob_data) {
 		fprintf(stderr, "Unable to unpack kernel partition\n");
-		return 1;
+		goto done;
 	}
 
 	/*
@@ -192,7 +209,7 @@
 					sign_option.config_data,
 					sign_option.config_size)) {
 		fprintf(stderr, "Unable to update config\n");
-		return 1;
+		goto done;
 	}
 
 	/* Preserve the version unless a new one is given */
@@ -219,7 +236,7 @@
 				     &vblock_size);
 	if (!vblock_data) {
 		fprintf(stderr, "Unable to sign kernel blob\n");
-		return 1;
+		goto done;
 	}
 	VB2_DEBUG("vblock_size = %#x\n", vblock_size);
 
@@ -238,24 +255,33 @@
 		 * all our modifications to the buffer will get flushed to
 		 * disk when we close it. */
 		memcpy(kpart_data, vblock_data, vblock_size);
+		rv = 0;
 	}
-
+done:
+	futil_unmap_and_close_file(fd, FILE_MODE_SIGN(sign_option), kpart_data,
+				   kpart_size);
 	free(vblock_data);
 	return rv;
 }
 
 
-int ft_sign_raw_firmware(const char *name, uint8_t *buf, uint32_t len,
-			 void *data)
+int ft_sign_raw_firmware(const char *name, void *data)
 {
-	struct vb2_signature *body_sig;
-	struct vb2_fw_preamble *preamble;
-	int rv;
+	struct vb2_signature *body_sig = NULL;
+	struct vb2_fw_preamble *preamble = NULL;
+	uint8_t *buf;
+	uint32_t len;
+	int rv = 1;
+	int fd = -1;
+
+	if (futil_open_and_map_file(name, &fd, FILE_MODE_SIGN(sign_option),
+				    &buf, &len))
+		return 1;
 
 	body_sig = vb2_calculate_signature(buf, len, sign_option.signprivate);
 	if (!body_sig) {
 		fprintf(stderr, "Error calculating body signature\n");
-		return 1;
+		goto done;
 	}
 
 	preamble = vb2_create_fw_preamble(
@@ -267,7 +293,7 @@
 	if (!preamble) {
 		fprintf(stderr, "Error creating firmware preamble.\n");
 		free(body_sig);
-		return 1;
+		goto done;
 	}
 
 	rv = WriteSomeParts(sign_option.outfile,
@@ -275,12 +301,94 @@
 			    sign_option.keyblock->keyblock_size,
 			    preamble, preamble->preamble_size);
 
+done:
+	futil_unmap_and_close_file(fd, FILE_MODE_SIGN(sign_option), buf, len);
 	free(preamble);
 	free(body_sig);
 
 	return rv;
 }
 
+static int load_keyset(void)
+{
+	char *buf = NULL;
+	int errorcnt = 0;
+	const char *s = NULL;
+	const char *b = NULL;
+	const char *k = NULL;
+	const char *format;
+	struct stat sb;
+
+	if (!sign_option.keysetdir)
+		FATAL("Keyset should never be NULL. Aborting\n");
+
+	/* Failure means this is not a directory */
+	if (stat(sign_option.keysetdir, &sb) == -1 ||
+	    (sb.st_mode & S_IFMT) != S_IFDIR)
+		format = "%s%s.%s";
+	else
+		format = "%s/%s.%s";
+
+	switch (sign_option.type) {
+	case FILE_TYPE_BIOS_IMAGE:
+	case FILE_TYPE_RAW_FIRMWARE:
+		s = "firmware_data_key";
+		b = "firmware";
+		k = "kernel_subkey";
+		break;
+	case FILE_TYPE_RAW_KERNEL:
+		s = "kernel_data_key";
+		b = "kernel";
+		break;
+	case FILE_TYPE_KERN_PREAMBLE:
+		s = "kernel_data_key";
+		break;
+	default:
+		return 0;
+	}
+
+	if (s && !sign_option.signprivate) {
+		if (asprintf(&buf, format, sign_option.keysetdir, s,
+			     "vbprivk") <= 0)
+			FATAL("Failed to allocate string\n");
+		INFO("Loading private data key from default keyset: %s\n", buf);
+		sign_option.signprivate = vb2_read_private_key(buf);
+		if (!sign_option.signprivate) {
+			ERROR("Error reading %s\n", buf);
+			errorcnt++;
+		}
+		free(buf);
+	}
+
+	if (b && !sign_option.keyblock) {
+		if (asprintf(&buf, format, sign_option.keysetdir, b,
+			     "keyblock") <= 0)
+			FATAL("Failed to allocate string\n");
+		INFO("Loading keyblock from default keyset: %s\n", buf);
+		sign_option.keyblock = vb2_read_keyblock(buf);
+		if (!sign_option.keyblock) {
+			ERROR("Error reading %s\n", buf);
+			errorcnt++;
+		}
+		free(buf);
+	}
+
+	if (k && !sign_option.kernel_subkey) {
+		if (asprintf(&buf, format, sign_option.keysetdir, k,
+			     "vbpubk") <= 0)
+			FATAL("Failed to allocate string\n");
+		INFO("Loading kernel subkey from default keyset: %s\n", buf);
+		sign_option.kernel_subkey = vb2_read_packed_key(buf);
+		if (!sign_option.kernel_subkey) {
+			ERROR("Error reading %s\n", buf);
+			errorcnt++;
+		}
+		free(buf);
+	}
+
+	return errorcnt;
+}
+
 static const char usage_pubkey[] = "\n"
 	"To sign a public key / create a new keyblock:\n"
 	"\n"
@@ -313,41 +421,46 @@
 	"To sign a raw firmware blob (FW_MAIN_A/B):\n"
 	"\n"
 	"Required PARAMS:\n"
-	"  -s|--signprivate FILE.vbprivk    The private firmware data key\n"
-	"  -b|--keyblock    FILE.keyblock   The keyblock containing the\n"
-	"                                     public firmware data key\n"
-	"  -k|--kernelkey   FILE.vbpubk     The public kernel subkey\n"
 	"  -v|--version     NUM             The firmware version number\n"
 	"  [--fv]           INFILE"
 	"          The raw firmware blob (FW_MAIN_A/B)\n"
 	"  [--outfile]      OUTFILE         Output VBLOCK_A/B\n"
 	"\n"
 	"Optional PARAMS:\n"
+	"  -s|--signprivate FILE.vbprivk    The private firmware data key\n"
+	"  -b|--keyblock    FILE.keyblock   The keyblock containing the\n"
+	"                                     public firmware data key\n"
+	"  -k|--kernelkey   FILE.vbpubk     The public kernel subkey\n"
 	"  -f|--flags       NUM             The preamble flags value"
 	" (default is 0)\n"
+	"  -K|--keyset      PATH            Prefix of private firmware data"
+	" key,\n"
+	"                                   keyblock and public kernel"
+	" subkey.\n"
+	"                                   Prefix must be valid path with\n"
+	"                                   optional file name prefix.\n"
+	"                                   Used as defaults for -s, -b and"
+	" -k,\n"
+	"                                   if not passed expliticly\n"
+	"                                   (default is '%s')\n"
 	"\n";
 static void print_help_raw_firmware(int argc, char *argv[])
 {
-	puts(usage_fw_main);
+	printf(usage_fw_main, DEFAULT_KEYSETDIR);
 }
 
 static const char usage_bios[] = "\n"
 	"To sign a complete firmware image (bios.bin):\n"
 	"\n"
 	"Required PARAMS:\n"
+	"  [--infile]       INFILE          Input firmware image (modified\n"
+	"                                     in place if no OUTFILE given)\n"
+	"\n"
+	"Optional PARAMS:\n"
 	"  -s|--signprivate FILE.vbprivk    The private firmware data key\n"
 	"  -b|--keyblock    FILE.keyblock   The keyblock containing the\n"
 	"                                     public firmware data key\n"
 	"  -k|--kernelkey   FILE.vbpubk     The public kernel subkey\n"
-	"  [--infile]       INFILE          Input firmware image (modified\n"
-	"                                     in place if no OUTFILE given)\n"
-	"\n"
-	"These are required if the A and B firmware differ:\n"
-	"  -S|--devsign     FILE.vbprivk    The DEV private firmware data key\n"
-	"  -B|--devkeyblock FILE.keyblock   The keyblock containing the\n"
-	"                                     DEV public firmware data key\n"
-	"\n"
-	"Optional PARAMS:\n"
 	"  -v|--version     NUM             The firmware version number"
 	" (default %d)\n"
 	"  -f|--flags       NUM             The preamble flags value"
@@ -355,21 +468,27 @@
 	"                                     unchanged, or 0 if unknown)\n"
 	"  -d|--loemdir     DIR             Local OEM output vblock directory\n"
 	"  -l|--loemid      STRING          Local OEM vblock suffix\n"
+	"  -K|--keyset      PATH            Prefix of private firmware data"
+	" key,\n"
+	"                                   keyblock and public kernel"
+	" subkey.\n"
+	"                                   Prefix must be valid path with\n"
+	"                                   optional file name prefix.\n"
+	"                                   Used as defaults for -s, -b and"
+	" -k,\n"
+	"                                   if not passed expliticly\n"
+	"                                   (default is '%s')\n"
 	"  [--outfile]      OUTFILE         Output firmware image\n"
 	"\n";
 static void print_help_bios_image(int argc, char *argv[])
 {
-	printf(usage_bios, sign_option.version);
+	printf(usage_bios, sign_option.version, DEFAULT_KEYSETDIR);
 }
 
 static const char usage_new_kpart[] = "\n"
 	"To create a new kernel partition image (/dev/sda2, /dev/mmcblk0p2):\n"
 	"\n"
 	"Required PARAMS:\n"
-	"  -s|--signprivate FILE.vbprivk"
-	"    The private key to sign the kernel blob\n"
-	"  -b|--keyblock    FILE.keyblock   Keyblock containing the public\n"
-	"                                     key to verify the kernel blob\n"
 	"  -v|--version     NUM             The kernel version number\n"
 	"  --bootloader     FILE            Bootloader stub\n"
 	"  --config         FILE            The kernel commandline file\n"
@@ -379,6 +498,10 @@
 	"  [--outfile]      OUTFILE         Output kernel partition or vblock\n"
 	"\n"
 	"Optional PARAMS:\n"
+	"  -s|--signprivate FILE.vbprivk"
+	"    The private key to sign the kernel blob\n"
+	"  -b|--keyblock    FILE.keyblock   Keyblock containing the public\n"
+	"                                     key to verify the kernel blob\n"
 	"  --kloadaddr      NUM"
 	"             RAM address to load the kernel body\n"
 	"                                     (default %#x)\n"
@@ -387,22 +510,33 @@
 	" --vblockonly                      Emit just the vblock (requires a\n"
 	"                                     distinct outfile)\n"
 	"  -f|--flags       NUM             The preamble flags value\n"
+	"  -K|--keyset      DIR             Path to directory containing"
+	" private\n"
+	"                                   kernel data key, and keyblock\n"
+	"  -K|--keyset      PATH            Prefix of private kernel data key\n"
+	"                                   and keyblock.\n"
+	"                                   Prefix must be valid path with\n"
+	"                                   optional file name prefix.\n"
+	"                                   Used as defaults for -s and -b,\n"
+	"                                   if not passed expliticly\n"
+	"                                   (default is '%s')\n"
 	"\n";
 static void print_help_raw_kernel(int argc, char *argv[])
 {
-	printf(usage_new_kpart, sign_option.kloadaddr, sign_option.padding);
+	printf(usage_new_kpart, sign_option.kloadaddr, sign_option.padding,
+	       DEFAULT_KEYSETDIR);
 }
 
 static const char usage_old_kpart[] = "\n"
 	"To resign an existing kernel partition (/dev/sda2, /dev/mmcblk0p2):\n"
 	"\n"
 	"Required PARAMS:\n"
-	"  -s|--signprivate FILE.vbprivk"
-	"    The private key to sign the kernel blob\n"
 	"  [--infile]       INFILE          Input kernel partition (modified\n"
 	"                                     in place if no OUTFILE given)\n"
 	"\n"
 	"Optional PARAMS:\n"
+	"  -s|--signprivate FILE.vbprivk"
+	"    The private key to sign the kernel blob\n"
 	"  -b|--keyblock    FILE.keyblock   Keyblock containing the public\n"
 	"                                     key to verify the kernel blob\n"
 	"  -v|--version     NUM             The kernel version number\n"
@@ -413,10 +547,17 @@
 	"  --vblockonly                     Emit just the vblock (requires a\n"
 	"                                     distinct OUTFILE)\n"
 	"  -f|--flags       NUM             The preamble flags value\n"
+	"  -K|--keyset      PATH            Prefix of private kernel data"
+	" key.\n"
+	"                                   Prefix must be valid path with\n"
+	"                                   optional file name prefix.\n"
+	"                                   Used as default for -s,\n"
+	"                                   if not passed expliticly\n"
+	"                                   (default is '%s')\n"
 	"\n";
 static void print_help_kern_preamble(int argc, char *argv[])
 {
-	printf(usage_old_kpart, sign_option.padding);
+	printf(usage_old_kpart, sign_option.padding, DEFAULT_KEYSETDIR);
 }
 
 static void print_help_usbpd1(int argc, char *argv[])
@@ -595,12 +736,11 @@
 	{"signprivate",  1, NULL, 's'},
 	{"keyblock",     1, NULL, 'b'},
 	{"kernelkey",    1, NULL, 'k'},
-	{"devsign",      1, NULL, 'S'},
-	{"devkeyblock",  1, NULL, 'B'},
 	{"version",      1, NULL, 'v'},
 	{"flags",        1, NULL, 'f'},
 	{"loemdir",      1, NULL, 'd'},
 	{"loemid",       1, NULL, 'l'},
+	{"keyset",       1, NULL, 'K'},
 	{"fv",           1, NULL, OPT_FV},
 	{"infile",       1, NULL, OPT_INFILE},
 	{"datapubkey",   1, NULL, OPT_INFILE},	/* alias */
@@ -629,7 +769,7 @@
 	{"help",         0, NULL, OPT_HELP},
 	{NULL,           0, NULL, 0},
 };
-static const char *short_opts = ":s:b:k:S:B:v:f:d:l:";
+static const char *short_opts = ":s:b:k:v:f:d:l:K:";
 
 /* Return zero on success */
 static int parse_number_opt(const char *arg, const char *name, uint32_t *dest)
@@ -648,12 +788,8 @@
 {
 	char *infile = 0;
 	int i;
-	int ifd = -1;
 	int errorcnt = 0;
-	uint8_t *buf;
-	uint32_t buf_len;
 	char *e = 0;
-	int mapping;
 	int helpind = 0;
 	int longindex;
 
@@ -682,21 +818,6 @@
 				errorcnt++;
 			}
 			break;
-		case 'S':
-			sign_option.devsignprivate =
-				vb2_read_private_key(optarg);
-			if (!sign_option.devsignprivate) {
-				fprintf(stderr, "Error reading %s\n", optarg);
-				errorcnt++;
-			}
-			break;
-		case 'B':
-			sign_option.devkeyblock = vb2_read_keyblock(optarg);
-			if (!sign_option.devkeyblock) {
-				fprintf(stderr, "Error reading %s\n", optarg);
-				errorcnt++;
-			}
-			break;
 		case 'v':
 			sign_option.version_specified = 1;
 			sign_option.version = strtoul(optarg, &e, 0);
@@ -718,6 +839,9 @@
 		case 'l':
 			sign_option.loemid = optarg;
 			break;
+		case 'K':
+			sign_option.keysetdir = optarg;
+			break;
 		case OPT_FV:
 			sign_option.fv_specified = 1;
 			VBOOT_FALLTHROUGH;
@@ -909,6 +1033,10 @@
 			sign_option.type = FILE_TYPE_RAW_FIRMWARE;
 	}
 
+	/* Load keys and keyblocks from keyset path, if they were not provided
+	   earlier. */
+	errorcnt += load_keyset();
+
 	VB2_DEBUG("type=%s\n", futil_file_type_name(sign_option.type));
 
 	/* Check the arguments for the type of thing we want to sign */
@@ -938,7 +1066,6 @@
 		 * may want to read it instead. */
 		break;
 	case FILE_TYPE_BIOS_IMAGE:
-	case FILE_TYPE_OLD_BIOS_IMAGE:
 		errorcnt += no_opt_if(!sign_option.signprivate, "signprivate");
 		errorcnt += no_opt_if(!sign_option.keyblock, "keyblock");
 		errorcnt += no_opt_if(!sign_option.kernel_subkey, "kernelkey");
@@ -1010,56 +1137,18 @@
 	if (errorcnt)
 		goto done;
 
-	if (sign_option.create_new_outfile) {
-		/* The input is read-only, the output is write-only. */
-		mapping = MAP_RO;
-		VB2_DEBUG("open RO %s\n", infile);
-		ifd = open(infile, O_RDONLY);
-		if (ifd < 0) {
-			errorcnt++;
-			fprintf(stderr, "Can't open %s for reading: %s\n",
-				infile, strerror(errno));
-			goto done;
-		}
-	} else {
+	if (!sign_option.create_new_outfile) {
 		/* We'll read-modify-write the output file */
-		mapping = MAP_RW;
 		if (sign_option.inout_file_count > 1)
 			futil_copy_file_or_die(infile, sign_option.outfile);
-		VB2_DEBUG("open RW %s\n", sign_option.outfile);
 		infile = sign_option.outfile;
-		ifd = open(sign_option.outfile, O_RDWR);
-		if (ifd < 0) {
-			errorcnt++;
-			fprintf(stderr, "Can't open %s for writing: %s\n",
-				sign_option.outfile, strerror(errno));
-			goto done;
-		}
 	}
 
-	if (0 != futil_map_file(ifd, mapping, &buf, &buf_len)) {
-		errorcnt++;
-		goto done;
-	}
-
-	errorcnt += futil_file_type_sign(sign_option.type, infile,
-					 buf, buf_len);
-
-	errorcnt += futil_unmap_file(ifd, mapping, buf, buf_len);
-
+	errorcnt += futil_file_type_sign(sign_option.type, infile);
 done:
-	if (ifd >= 0 && close(ifd)) {
-		errorcnt++;
-		fprintf(stderr, "Error when closing ifd: %s\n",
-			strerror(errno));
-	}
-
-	if (sign_option.signprivate)
-		free(sign_option.signprivate);
-	if (sign_option.keyblock)
-		free(sign_option.keyblock);
-	if (sign_option.kernel_subkey)
-		free(sign_option.kernel_subkey);
+	free(sign_option.signprivate);
+	free(sign_option.keyblock);
+	free(sign_option.kernel_subkey);
 	if (sign_option.prikey)
 		vb2_private_key_free(sign_option.prikey);
 
diff --git a/futility/cmd_update.c b/futility/cmd_update.c
index e4b3765..a77b224 100644
--- a/futility/cmd_update.c
+++ b/futility/cmd_update.c
@@ -57,7 +57,6 @@
 
 	{"ccd", 0, NULL, OPT_CCD},
 	{"servo", 0, NULL, OPT_SERVO},
-	{"servo_noreset", 0, NULL, OPT_SERVO_NORESET},
 	{"servo_port", 1, NULL, OPT_SERVO_PORT},
 	{"emulate", 1, NULL, OPT_EMULATE},
 	{"factory", 0, NULL, OPT_FACTORY},
@@ -112,14 +111,17 @@
 		"    --pd_image=FILE \tPD firmware image (i.e, pd.bin)\n"
 		"-t, --try           \tTry A/B update on reboot if possible\n"
 		"-a, --archive=PATH  \tRead resources from archive\n"
-		"    --manifest      \tPrint out a JSON manifest and exit\n"
-		"    --repack=DIR    \tUpdates archive from DIR\n"
 		"    --unpack=DIR    \tExtracts archive to DIR\n"
 		"-p, --programmer=PRG\tChange AP (host) flashrom programmer\n"
 		"    --fast          \tReduce read cycles and do not verify\n"
 		"    --quirks=LIST   \tSpecify the quirks to apply\n"
 		"    --list-quirks   \tPrint all available quirks\n"
-		"-m, --mode=MODE     \tRun updater in specified mode\n"
+		"-m, --mode=MODE     \tRun updater in the specified mode\n"
+		"    --manifest      \tScan the archive to print a manifest in JSON\n"
+		"\n"
+		" * If both --manifest and --fast are specified, the updater\n"
+		"   will not scan the archive and simply dump the previously\n"
+		"   cached manifest (may be out-dated) from the archive.\n"
 		"\n"
 		"Legacy and compatibility options:\n"
 		"    --factory       \tAlias for --mode=factory\n"
@@ -134,7 +136,6 @@
 		"    --gbb_flags=FLAG\tOverride new GBB flags\n"
 		"    --ccd           \tDo fast,force,wp=0,p=raiden_debug_spi\n"
 		"    --servo         \tFlash using Servo (v2, v4, micro, ...)\n"
-		"    --servo_noreset \tLike servo but with 'custom_rst=true'\n"
 		"    --servo_port=PRT\tOverride servod port, implies --servo\n"
 		"    --signature_id=S\tOverride signature ID for key files\n"
 		"    --sys_props=LIST\tList of system properties to override\n"
@@ -144,21 +145,15 @@
 		argv[0]);
 }
 
-static char *add_servo_noreset(char *programmer)
+static void prepare_servo_control(const char *control_name, int on)
 {
-	char *ret;
+	char *cmd;
+	if (!control_name)
+		return;
 
-	if (strstr(programmer, "raiden_debug_spi:target=AP") == NULL) {
-		ERROR("servo_noreset only works for AP flashing over CCD.\n");
-		free(programmer);
-
-		return NULL;
-	}
-
-	ASPRINTF(&ret, "%s,custom_rst=true", programmer);
-	free(programmer);
-
-	return ret;
+	ASPRINTF(&cmd, "dut-control %s:%s", control_name, on ? "on" : "off");
+	free(host_shell(cmd));
+	free(cmd);
 }
 
 static int do_update(int argc, char *argv[])
@@ -166,7 +161,8 @@
 	struct updater_config *cfg;
 	struct updater_config_arguments args = {0};
 	int i, errorcnt = 0, do_update = 1;
-	int detect_servo = 0, do_servo_cpu_fw_spi = 0, servo_noreset = 0;
+	int detect_servo = 0;
+	const char *prepare_ctrl_name = NULL;
 	char *servo_programmer = NULL;
 	char *endptr;
 
@@ -211,6 +207,8 @@
 			break;
 		case OPT_REPACK:
 			args.repack = optarg;
+			ERROR("Sorry, --repack is only for the script.\n");
+			errorcnt ++;
 			break;
 		case OPT_UNPACK:
 			args.unpack = optarg;
@@ -277,14 +275,6 @@
 			args.host_only = 1;
 			detect_servo = 1;
 			break;
-		case OPT_SERVO_NORESET:
-			args.fast_update = 1;
-			args.force_update = 1;
-			args.write_protection = "0";
-			args.host_only = 1;
-			detect_servo = 1;
-			servo_noreset = 1;
-			break;
 		case OPT_SERVO_PORT:
 			setenv(ENV_SERVOD_PORT, optarg, 1);
 			args.fast_update = 1;
@@ -318,10 +308,7 @@
 	}
 
 	if (!errorcnt && detect_servo) {
-		servo_programmer = host_detect_servo(&do_servo_cpu_fw_spi);
-
-		if (servo_programmer && servo_noreset)
-			servo_programmer = add_servo_noreset(servo_programmer);
+		servo_programmer = host_detect_servo(&prepare_ctrl_name);
 
 		if (!servo_programmer)
 			errorcnt++;
@@ -333,8 +320,7 @@
 	 * update (i.e., in updater_setup_config) so we want to turn on
 	 * cpu_fw_spi mode now.
 	 */
-	if (do_servo_cpu_fw_spi)
-		free(host_shell("dut-control cpu_fw_spi:on"));
+	prepare_servo_control(prepare_ctrl_name, 1);
 
 	if (!errorcnt)
 		errorcnt += updater_setup_config(cfg, &args, &do_update);
@@ -353,8 +339,7 @@
 			errorcnt ? "aborted" : "exits successfully");
 	}
 
-	if (do_servo_cpu_fw_spi)
-		free(host_shell("dut-control cpu_fw_spi:off"));
+	prepare_servo_control(prepare_ctrl_name, 0);
 	free(servo_programmer);
 
 	updater_delete_config(cfg);
diff --git a/futility/cmd_validate_rec_mrc.c b/futility/cmd_validate_rec_mrc.c
index 1d35bed..7ba5931 100644
--- a/futility/cmd_validate_rec_mrc.c
+++ b/futility/cmd_validate_rec_mrc.c
@@ -222,31 +222,21 @@
 
 	infile = argv[optind++];
 
-	fd = open(infile, O_RDONLY);
-	if (fd < 0) {
-		fprintf(stderr, "Cannot open %s:%s\n", infile, strerror(errno));
+	if (futil_open_and_map_file(infile, &fd, FILE_RO, &buff, &file_size) !=
+	    FILE_ERR_NONE)
 		return 1;
-	}
-
-	if (futil_map_file(fd, MAP_RO, &buff, &file_size) != FILE_ERR_NONE) {
-		fprintf(stderr, "Cannot map file %s\n", infile);
-		close(fd);
-		return 1;
-	}
 
 	if (offset > file_size) {
 		fprintf(stderr, "File size(%#x) smaller than offset(%#x)\n",
 			file_size, offset);
-		futil_unmap_file(fd, MAP_RO, buff, file_size);
-		close(fd);
+		futil_unmap_and_close_file(fd, FILE_RO, buff, file_size);
 		return 1;
 	}
 
 	if (get_mrc_data_slot((uint16_t *)(buff + offset), &data_offset,
 			      &data_size)) {
 		fprintf(stderr, "Metadata block error\n");
-		futil_unmap_file(fd, MAP_RO, buff, file_size);
-		close(fd);
+		futil_unmap_and_close_file(fd, FILE_RO, buff, file_size);
 		return 1;
 	}
 	offset += data_offset;
@@ -259,12 +249,10 @@
 			"offset=%#x, file size=%#x, data_size=%#x\n",
 			offset, file_size, data_size);
 
-	if (futil_unmap_file(fd, MAP_RO, buff, file_size) != FILE_ERR_NONE) {
-		fprintf(stderr, "Failed to unmap file %s\n", infile);
-		ret = 1;
-	}
+	if (futil_unmap_and_close_file(fd, FILE_RO, buff, file_size) !=
+	    FILE_ERR_NONE)
+		return 1;
 
-	close(fd);
 	return ret;
 }
 
diff --git a/futility/file_type.c b/futility/file_type.c
index b55d13f..29c4667 100644
--- a/futility/file_type.c
+++ b/futility/file_type.c
@@ -24,8 +24,8 @@
 	const char *desc;
 	/* Functions to identify, display, and sign this type of file. */
 	enum futil_file_type (*recognize)(uint8_t *buf, uint32_t len);
-	int (*show)(const char *name, uint8_t *buf, uint32_t len, void *data);
-	int (*sign)(const char *name, uint8_t *buf, uint32_t len, void *data);
+	int (*show)(const char *name, void *data);
+	int (*sign)(const char *name, void *data);
 };
 
 /* Populate a list of file types and operator functions. */
@@ -99,42 +99,29 @@
 enum futil_file_err futil_file_type(const char *filename,
 				    enum futil_file_type *type)
 {
-	int ifd;
-	uint8_t *buf;
-	uint32_t buf_len;
+	int ifd = -1;
+	uint8_t *buf = NULL;
+	uint32_t buf_len = 0;
 	struct stat sb;
 	enum futil_file_err err = FILE_ERR_NONE;
 
 	*type = FILE_TYPE_UNKNOWN;
 
-	ifd = open(filename, O_RDONLY);
-	if (ifd < 0) {
-		fprintf(stderr, "Can't open %s: %s\n",
-			filename, strerror(errno));
-		return FILE_ERR_OPEN;
-	}
+	err = futil_open_file(filename, &ifd, FILE_RO);
+	if (err != FILE_ERR_NONE)
+		goto done;
 
 	if (0 != fstat(ifd, &sb)) {
-		fprintf(stderr, "Can't stat input file: %s\n",
-			strerror(errno));
-		close(ifd);
-		return FILE_ERR_STAT;
+		fprintf(stderr, "Can't stat input file: %s\n", strerror(errno));
+		err = FILE_ERR_STAT;
+		goto done;
 	}
 
 	if (S_ISREG(sb.st_mode) || S_ISBLK(sb.st_mode)) {
-		err = futil_map_file(ifd, MAP_RO, &buf, &buf_len);
-		if (err) {
-			close(ifd);
-			return err;
-		}
-
+		err = futil_map_file(ifd, FILE_RO, &buf, &buf_len);
+		if (err)
+			goto done;
 		*type = futil_file_type_buf(buf, buf_len);
-
-		err = futil_unmap_file(ifd, MAP_RO, buf, buf_len);
-		if (err) {
-			close(ifd);
-			return err;
-		}
 	} else if (S_ISDIR(sb.st_mode)) {
 		err = FILE_ERR_DIR;
 	} else if (S_ISCHR(sb.st_mode)) {
@@ -144,37 +131,27 @@
 	} else if (S_ISSOCK(sb.st_mode)) {
 		err = FILE_ERR_SOCK;
 	}
-
-	if (close(ifd)) {
-		fprintf(stderr, "Error when closing %s: %s\n",
-			filename, strerror(errno));
-		return FILE_ERR_CLOSE;
-	}
-
+done:
+	futil_unmap_and_close_file(ifd, FILE_RO, buf, buf_len);
 	return err;
 }
 
-int futil_file_type_show(enum futil_file_type type,
-			 const char *filename,
-			 uint8_t *buf, uint32_t len)
+int futil_file_type_show(enum futil_file_type type, const char *filename)
 {
 	if (futil_file_types[type].show)
-		return futil_file_types[type].show(filename, buf, len, 0);
+		return futil_file_types[type].show(filename, 0);
 
 	fprintf(stderr, "Don't know how to show %s (type %s)\n",
 		filename, futil_file_type_name(type));
 	return 1;
 }
 
-int futil_file_type_sign(enum futil_file_type type,
-			 const char *filename,
-			 uint8_t *buf, uint32_t len)
+int futil_file_type_sign(enum futil_file_type type, const char *filename)
 {
 	if (futil_file_types[type].sign)
-		return futil_file_types[type].sign(filename, buf, len, 0);
+		return futil_file_types[type].sign(filename, 0);
 
 	fprintf(stderr, "Don't know how to sign %s (type %s)\n",
 		filename, futil_file_type_name(type));
 	return 1;
 }
-
diff --git a/futility/file_type.h b/futility/file_type.h
index e1efe76..5a074b3 100644
--- a/futility/file_type.h
+++ b/futility/file_type.h
@@ -40,22 +40,18 @@
 				    enum futil_file_type *type);
 
 /*
- * Call the show() method on a buffer containing a specific file type.
+ * Call the show() method on a file containing a specific file type.
  * Returns zero on success. It's up to the caller to ensure that only valid
  * file types are invoked.
  */
-int futil_file_type_show(enum futil_file_type type,
-			 const char *filename,
-			 uint8_t *buf, uint32_t len);
+int futil_file_type_show(enum futil_file_type type, const char *filename);
 
 /*
- * Call the sign() method on a buffer containing a specific file type.
+ * Call the sign() method on a file with specific file type.
  * Returns zero on success. It's up to the caller to ensure that only valid
  * file types are invoked.
  */
-int futil_file_type_sign(enum futil_file_type type,
-			 const char *filename,
-			 uint8_t *buf, uint32_t len);
+int futil_file_type_sign(enum futil_file_type type, const char *filename);
 
 /*
  * Declare the file_type functions. Certain functions are reused for more than
@@ -66,14 +62,21 @@
 #define R_(FOO) \
 	enum futil_file_type FOO(uint8_t *buf, uint32_t len);
 #define S_(FOO) \
-	int FOO(const char *name, uint8_t *buf, uint32_t len, void *data);
+	int FOO(const char *name, void *data);
 #define NONE
 #define FILE_TYPE(A, B, C, D, E, F) D E F
 #include "file_type.inc"
 #undef FILE_TYPE
 #undef NONE
+#undef SG_
 #undef S_
 #undef R_
 #pragma GCC diagnostic pop
 
+/* Declared for use inside other show functions. */
+int show_fw_preamble_buf(const char *name, uint8_t *buf, uint32_t len,
+			 void *data);
+int show_vb21_pubkey_buf(const char *name, uint8_t *buf, uint32_t len,
+			 void *data);
+
 #endif  /* VBOOT_REFERENCE_FILE_TYPE_H_ */
diff --git a/futility/file_type.inc b/futility/file_type.inc
index 0d3acc9..7a32f9b 100644
--- a/futility/file_type.inc
+++ b/futility/file_type.inc
@@ -19,10 +19,6 @@
 	  R_(ft_recognize_bios_image),
 	  S_(ft_show_bios),
 	  S_(ft_sign_bios))
-FILE_TYPE(OLD_BIOS_IMAGE,   "oldbios",       "Cr-48 Chrome OS BIOS image",
-	  R_(ft_recognize_bios_image),
-	  S_(ft_show_bios),
-	  S_(ft_sign_bios))
 FILE_TYPE(GBB,              "gbb",           "GBB",
 	  R_(ft_recognize_gbb),
 	  S_(ft_show_gbb),
diff --git a/futility/file_type_bios.c b/futility/file_type_bios.c
index fe0b223..29ecfd4 100644
--- a/futility/file_type_bios.c
+++ b/futility/file_type_bios.c
@@ -5,10 +5,12 @@
 
 #include <errno.h>
 #include <limits.h>
+#include <stdbool.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <string.h>
 
+#include "cbfstool.h"
 #include "file_type_bios.h"
 #include "file_type.h"
 #include "fmap.h"
@@ -27,21 +29,11 @@
 _Static_assert(ARRAY_SIZE(fmap_name) == NUM_BIOS_COMPONENTS,
 	       "Size of fmap_name[] should match NUM_BIOS_COMPONENTS");
 
-static const char * const fmap_oldname[] = {
-	"GBB Area",	  			/* BIOS_FMAP_GBB */
-	"Firmware A Data", 			/* BIOS_FMAP_FW_MAIN_A */
-	"Firmware B Data", 			/* BIOS_FMAP_FW_MAIN_B */
-	"Firmware A Key",  			/* BIOS_FMAP_VBLOCK_A */
-	"Firmware B Key",  			/* BIOS_FMAP_VBLOCK_B */
-};
-_Static_assert(ARRAY_SIZE(fmap_oldname) == NUM_BIOS_COMPONENTS,
-	       "Size of fmap_oldname[] should match NUM_BIOS_COMPONENTS");
-
 static void fmap_limit_area(FmapAreaHeader *ah, uint32_t len)
 {
 	uint32_t sum = ah->area_offset + ah->area_size;
 	if (sum < ah->area_size || sum > len) {
-		VB2_DEBUG("%s %#x + %#x > %#x\n",
+		VB2_DEBUG("%.*s %#x + %#x > %#x\n", FMAP_NAMELEN,
 			  ah->area_name, ah->area_offset, ah->area_size, len);
 		ah->area_offset = 0;
 		ah->area_size = 0;
@@ -50,7 +42,8 @@
 
 /** Show functions **/
 
-int ft_show_gbb(const char *name, uint8_t *buf, uint32_t len, void *data)
+static int show_gbb_buf(const char *name, uint8_t *buf, uint32_t len,
+			void *data)
 {
 	struct vb2_gbb_header *gbb = (struct vb2_gbb_header *)buf;
 	struct bios_state_s *state = (struct bios_state_s *)data;
@@ -85,7 +78,7 @@
 
 	if (retval) {
 		printf("GBB header is invalid, ignoring content\n");
-		return 1;
+		return retval;
 	}
 
 	printf("GBB content:\n");
@@ -135,6 +128,23 @@
 	return retval;
 }
 
+int ft_show_gbb(const char *name, void *data)
+{
+	int retval = 0;
+	int fd = -1;
+	uint8_t *buf;
+	uint32_t len;
+
+	retval = futil_open_and_map_file(name, &fd, FILE_RO, &buf, &len);
+	if (retval)
+		return 1;
+
+	retval = show_gbb_buf(name, buf, len, data);
+
+	futil_unmap_and_close_file(fd, FILE_RO, buf, len);
+	return retval;
+}
+
 /*
  * This handles FW_MAIN_A and FW_MAIN_B while processing a BIOS image.
  *
@@ -165,16 +175,16 @@
 /* Functions to call to show the bios components */
 static int (*fmap_show_fn[])(const char *name, uint8_t *buf, uint32_t len,
 			       void *data) = {
-	ft_show_gbb,
+	show_gbb_buf,
 	fmap_show_fw_main,
 	fmap_show_fw_main,
-	ft_show_fw_preamble,
-	ft_show_fw_preamble,
+	show_fw_preamble_buf,
+	show_fw_preamble_buf,
 };
 _Static_assert(ARRAY_SIZE(fmap_show_fn) == NUM_BIOS_COMPONENTS,
 	       "Size of fmap_show_fn[] should match NUM_BIOS_COMPONENTS");
 
-int ft_show_bios(const char *name, uint8_t *buf, uint32_t len, void *data)
+int ft_show_bios(const char *name, void *data)
 {
 	FmapHeader *fmap;
 	FmapAreaHeader *ah = 0;
@@ -182,6 +192,13 @@
 	enum bios_component c;
 	int retval = 0;
 	struct bios_state_s state;
+	int fd = -1;
+	uint8_t *buf;
+	uint32_t len;
+
+	retval = futil_open_and_map_file(name, &fd, FILE_RO, &buf, &len);
+	if (retval)
+		return 1;
 
 	memset(&state, 0, sizeof(state));
 
@@ -191,8 +208,7 @@
 	fmap = fmap_find(buf, len);
 	for (c = 0; c < NUM_BIOS_COMPONENTS; c++) {
 		/* We know one of these will work, too */
-		if (fmap_find_by_name(buf, len, fmap, fmap_name[c], &ah) ||
-		    fmap_find_by_name(buf, len, fmap, fmap_oldname[c], &ah)) {
+		if (fmap_find_by_name(buf, len, fmap, fmap_name[c], &ah)) {
 			/* But the file might be truncated */
 			fmap_limit_area(ah, len);
 			/* The name is not necessarily null-terminated */
@@ -217,119 +233,41 @@
 		}
 	}
 
+	futil_unmap_and_close_file(fd, FILE_RO, buf, len);
 	return retval;
 }
 
 /** Sign functions **/
 
-/*
- * This handles FW_MAIN_A and FW_MAIN_B while signing a BIOS image. The data is
- * just the RW firmware blob so there's nothing useful to do with it, but we'll
- * mark it as valid so that we'll know that this FMAP area exists and can
- * be signed.
- */
-static int fmap_sign_fw_main(const char *name, uint8_t *buf, uint32_t len,
-			     void *data)
-{
-	struct bios_state_s *state = (struct bios_state_s *)data;
-	state->area[state->c].is_valid = 1;
-	return 0;
-}
-
-/*
- * This handles VBLOCK_A and VBLOCK_B while processing a BIOS image. We don't
- * do any signing here. We just check to see if the existing FMAP area contains
- * a firmware preamble so we can preserve its contents. We do the signing once
- * we've looked over all the components.
- */
-static int fmap_sign_fw_preamble(const char *name, uint8_t *buf, uint32_t len,
-				 void *data)
-{
-	static uint8_t workbuf[VB2_FIRMWARE_WORKBUF_RECOMMENDED_SIZE]
-		__attribute__((aligned(VB2_WORKBUF_ALIGN)));
-	static struct vb2_workbuf wb;
-	vb2_workbuf_init(&wb, workbuf, sizeof(workbuf));
-
-	struct vb2_keyblock *keyblock = (struct vb2_keyblock *)buf;
-	struct bios_state_s *state = (struct bios_state_s *)data;
-
-	/*
-	 * If we have a valid keyblock and fw_preamble, then we can use them to
-	 * determine the size of the firmware body. Otherwise, we'll have to
-	 * just sign the whole region.
-	 */
-	if (VB2_SUCCESS != vb2_verify_keyblock_hash(keyblock, len, &wb)) {
-		fprintf(stderr, "Warning: %s keyblock is invalid. "
-			"Signing the entire FW FMAP region...\n", name);
-		goto whatever;
-	}
-
-	if (vb2_packed_key_looks_ok(&keyblock->data_key,
-				    keyblock->data_key.key_offset +
-				    keyblock->data_key.key_size)) {
-		fprintf(stderr, "Warning: %s public key is invalid. "
-			"Signing the entire FW FMAP region...\n", name);
-		goto whatever;
-	}
-	uint32_t more = keyblock->keyblock_size;
-	struct vb2_fw_preamble *preamble =
-		(struct vb2_fw_preamble *)(buf + more);
-	uint32_t fw_size = preamble->body_signature.data_size;
-	struct bios_area_s *fw_body_area = 0;
-
-	switch (state->c) {
-	case BIOS_FMAP_VBLOCK_A:
-		fw_body_area = &state->area[BIOS_FMAP_FW_MAIN_A];
-		/* Preserve the flags if they're not specified */
-		if (!sign_option.flags_specified)
-			sign_option.flags = preamble->flags;
-		break;
-	case BIOS_FMAP_VBLOCK_B:
-		fw_body_area = &state->area[BIOS_FMAP_FW_MAIN_B];
-		break;
-	default:
-		FATAL("Can only handle VBLOCK_A or VBLOCK_B\n");
-	}
-
-	if (fw_size > fw_body_area->len) {
-		fprintf(stderr,
-			"%s says the firmware is larger than we have\n",
-			name);
-		return 1;
-	}
-
-	/* Update the firmware size */
-	fw_body_area->len = fw_size;
-
-whatever:
-	state->area[state->c].is_valid = 1;
-
-	return 0;
-}
-
 static int write_new_preamble(struct bios_area_s *vblock,
 			      struct bios_area_s *fw_body,
 			      struct vb2_private_key *signkey,
 			      struct vb2_keyblock *keyblock)
 {
-	struct vb2_signature *body_sig;
-	struct vb2_fw_preamble *preamble;
+	struct vb2_signature *body_sig = NULL;
+	struct vb2_fw_preamble *preamble = NULL;
+	int retval = 1;
 
 	body_sig = vb2_calculate_signature(fw_body->buf, fw_body->len, signkey);
 	if (!body_sig) {
-		fprintf(stderr, "Error calculating body signature\n");
-		return 1;
+		ERROR("Error calculating body signature\n");
+		goto end;
 	}
 
-	preamble = vb2_create_fw_preamble(sign_option.version,
+	preamble = vb2_create_fw_preamble(vblock->version,
 			(struct vb2_packed_key *)sign_option.kernel_subkey,
 			body_sig,
 			signkey,
-			sign_option.flags);
+			vblock->flags);
 	if (!preamble) {
-		fprintf(stderr, "Error creating firmware preamble.\n");
+		ERROR("Error creating firmware preamble.\n");
 		free(body_sig);
-		return 1;
+		goto end;
+	}
+
+	if (keyblock->keyblock_size + preamble->preamble_size > vblock->len) {
+		ERROR("Keyblock and preamble do not fit in VBLOCK.\n");
+		goto end;
 	}
 
 	/* Write the new keyblock */
@@ -337,11 +275,13 @@
 	memcpy(vblock->buf, keyblock, more);
 	/* and the new preamble */
 	memcpy(vblock->buf + more, preamble, preamble->preamble_size);
+	retval = 0;
 
+end:
 	free(preamble);
 	free(body_sig);
 
-	return 0;
+	return retval;
 }
 
 static int write_loem(const char *ab, struct bios_area_s *vblock)
@@ -387,123 +327,230 @@
 	struct bios_area_s *fw_b = &state->area[BIOS_FMAP_FW_MAIN_B];
 	int retval = 0;
 
-	if (!vblock_a->is_valid || !vblock_b->is_valid ||
-	    !fw_a->is_valid || !fw_b->is_valid) {
+	if (!vblock_a->is_valid || !fw_a->is_valid) {
 		fprintf(stderr, "Something's wrong. Not changing anything\n");
 		return 1;
 	}
 
-	/* Do A & B differ ? */
-	if (fw_a->len != fw_b->len ||
-	    memcmp(fw_a->buf, fw_b->buf, fw_a->len)) {
-		/* Yes, must use DEV keys for A */
-		if (!sign_option.devsignprivate || !sign_option.devkeyblock) {
-			fprintf(stderr,
-				"FW A & B differ. DEV keys are required.\n");
-			return 1;
-		}
-		retval |= write_new_preamble(vblock_a, fw_a,
-					     sign_option.devsignprivate,
-					     sign_option.devkeyblock);
-	} else {
-		retval |= write_new_preamble(vblock_a, fw_a,
-					     sign_option.signprivate,
-					     sign_option.keyblock);
-	}
-
-	/* FW B is always normal keys */
-	retval |= write_new_preamble(vblock_b, fw_b,
-				     sign_option.signprivate,
+	retval |= write_new_preamble(vblock_a, fw_a, sign_option.signprivate,
 				     sign_option.keyblock);
 
-
-
+	if (vblock_b->is_valid && fw_b->is_valid)
+		retval |= write_new_preamble(vblock_b, fw_b,
+					     sign_option.signprivate,
+					     sign_option.keyblock);
+	else
+		INFO("BIOS image does not have %s. Signing only %s\n",
+		     fmap_name[BIOS_FMAP_FW_MAIN_B],
+		     fmap_name[BIOS_FMAP_FW_MAIN_A]);
 
 	if (sign_option.loemid) {
 		retval |= write_loem("A", vblock_a);
-		retval |= write_loem("B", vblock_b);
+		if (vblock_b->is_valid)
+			retval |= write_loem("B", vblock_b);
 	}
 
 	return retval;
 }
 
-/* Functions to call while preparing to sign the bios */
-static int (*fmap_sign_fn[])(const char *name, uint8_t *buf, uint32_t len,
-			     void *data) = {
-	0,
-	fmap_sign_fw_main,
-	fmap_sign_fw_main,
-	fmap_sign_fw_preamble,
-	fmap_sign_fw_preamble,
-};
-_Static_assert(ARRAY_SIZE(fmap_sign_fn) == NUM_BIOS_COMPONENTS,
-	       "Size of fmap_sign_fn[] should match NUM_BIOS_COMPONENTS");
-
-int ft_sign_bios(const char *name, uint8_t *buf, uint32_t len, void *data)
+/* Prepare firmware slot for signing.
+   If fw_size is not zero, then it will be used as new length of signed area,
+   for zero the length will be taken form FlashMap or preamble. */
+static int prepare_slot(uint8_t *buf, uint32_t len, size_t fw_size,
+			enum bios_component fw_c, enum bios_component vblock_c,
+			struct bios_state_s *state)
 {
 	FmapHeader *fmap;
-	FmapAreaHeader *ah = 0;
-	char ah_name[FMAP_NAMELEN + 1];
-	enum bios_component c;
-	int retval = 0;
-	struct bios_state_s state;
+	FmapAreaHeader *ah;
+	const char *fw_main_name = fmap_name[fw_c];
+	const char *vblock_name = fmap_name[vblock_c];
+	static uint8_t workbuf[VB2_FIRMWARE_WORKBUF_RECOMMENDED_SIZE]
+		__attribute__((aligned(VB2_WORKBUF_ALIGN)));
+	static struct vb2_workbuf wb;
 
-	memset(&state, 0, sizeof(state));
-
-	/* We've already checked, so we know this will work. */
 	fmap = fmap_find(buf, len);
-	for (c = 0; c < NUM_BIOS_COMPONENTS; c++) {
-		/* We know one of these will work, too */
-		if (fmap_find_by_name(buf, len, fmap, fmap_name[c], &ah) ||
-		    fmap_find_by_name(buf, len, fmap, fmap_oldname[c], &ah)) {
-			/* But the file might be truncated */
-			fmap_limit_area(ah, len);
-			/* The name is not necessarily null-terminated */
-			snprintf(ah_name, sizeof(ah_name), "%s", ah->area_name);
+	vb2_workbuf_init(&wb, workbuf, sizeof(workbuf));
 
-			/* Update the state we're passing around */
-			state.c = c;
-			state.area[c].buf = buf + ah->area_offset;
-			state.area[c].len = ah->area_size;
+	VB2_DEBUG("Preparing areas: %s and %s\n", fw_main_name, vblock_name);
 
-			VB2_DEBUG("examining FMAP area %d (%s),"
-				  " offset=0x%08x len=0x%08x\n",
-				  c, ah_name, ah->area_offset, ah->area_size);
+	/* FW_MAIN */
+	if (!fmap_find_by_name(buf, len, fmap, fw_main_name, &ah)) {
+		ERROR("%s area not found in FMAP\n", fw_main_name);
+		return 1;
+	}
+	fmap_limit_area(ah, len);
+	state->area[fw_c].buf = buf + ah->area_offset;
+	state->area[fw_c].is_valid = 1;
+	if (fw_size > ah->area_size) {
+		ERROR("%s size is incorrect.\n", fmap_name[fw_c]);
+		return 1;
+	} else if (fw_size) {
+		state->area[fw_c].len = fw_size;
+	} else {
+		WARN("%s does not contain CBFS. Trying to sign entire area.\n",
+		     fmap_name[fw_c]);
+		state->area[fw_c].len = ah->area_size;
+	}
 
-			/* Go look at it, but abort on error */
-			if (fmap_sign_fn[c])
-				retval += fmap_sign_fn[c](ah_name,
-							  state.area[c].buf,
-							  state.area[c].len,
-							  &state);
+	/* Corresponding VBLOCK */
+	if (!fmap_find_by_name(buf, len, fmap, vblock_name, &ah)) {
+		ERROR("%s area not found in FMAP\n", vblock_name);
+		return 1;
+	}
+	fmap_limit_area(ah, len);
+	state->area[vblock_c].buf = buf + ah->area_offset;
+	state->area[vblock_c].len = ah->area_size;
+
+	struct vb2_keyblock *keyblock =
+		(struct vb2_keyblock *)state->area[vblock_c].buf;
+	int keyblock_valid = 0;
+
+	if (vb2_verify_keyblock_hash(keyblock, state->area[vblock_c].len,
+				     &wb) != VB2_SUCCESS) {
+		WARN("%s keyblock is invalid.\n", vblock_name);
+		goto end;
+	}
+
+	if (vb2_packed_key_looks_ok(&keyblock->data_key,
+				    keyblock->data_key.key_offset +
+					    keyblock->data_key.key_size)) {
+		WARN("%s public key is invalid.\n", vblock_name);
+		goto end;
+	}
+
+	struct vb2_public_key data_key;
+	if (vb2_unpack_key(&data_key, &keyblock->data_key) != VB2_SUCCESS) {
+		WARN("%s data key is invalid. Failed to parse.\n", vblock_name);
+		goto end;
+	}
+
+	if (keyblock->keyblock_size + sizeof(struct vb2_fw_preamble) >
+	    state->area[vblock_c].len) {
+		ERROR("%s is invalid. Keyblock and preamble do not fit.\n",
+		      vblock_name);
+		goto end;
+	}
+
+	struct vb2_fw_preamble *preamble =
+		(struct vb2_fw_preamble *)(state->area[vblock_c].buf +
+					   keyblock->keyblock_size);
+	if (vb2_verify_fw_preamble(preamble,
+				   state->area[vblock_c].len -
+					   keyblock->keyblock_size,
+				   &data_key, &wb)) {
+		WARN("%s preamble is invalid.\n", vblock_name);
+		goto end;
+	}
+
+	if (fw_size == 0) {
+		if (preamble->body_signature.data_size >
+		    state->area[fw_c].len) {
+			ERROR("%s says the firmware is larger than we have.\n",
+			      vblock_name);
+			goto end;
+		} else {
+			state->area[fw_c].len =
+				preamble->body_signature.data_size;
 		}
 	}
 
-	retval += sign_bios_at_end(&state);
+	keyblock_valid = 1;
 
+end:
+	if (sign_option.flags_specified)
+		state->area[vblock_c].flags = sign_option.flags;
+	else if (keyblock_valid)
+		state->area[vblock_c].flags = preamble->flags;
+	else
+		state->area[vblock_c].flags = 0;
+
+	if (sign_option.version_specified)
+		state->area[vblock_c].version = sign_option.version;
+	else if (keyblock_valid)
+		state->area[vblock_c].version = preamble->firmware_version;
+	else
+		state->area[vblock_c].version = 1;
+
+	state->area[vblock_c].is_valid = 1;
+
+	return 0;
+}
+
+int ft_sign_bios(const char *name, void *data)
+{
+	int retval = 0;
+	struct bios_state_s state;
+	int fd = -1;
+	uint8_t *buf = NULL;
+	uint32_t len = 0;
+	size_t fw_main_a_size = 0;
+	size_t fw_main_b_size = 0;
+
+	bool fw_main_a_in_cbfs_mode =
+		cbfstool_truncate(name, fmap_name[BIOS_FMAP_FW_MAIN_A],
+				  &fw_main_a_size) == VB2_SUCCESS;
+
+	bool fw_main_b_in_cbfs_mode =
+		cbfstool_truncate(name, fmap_name[BIOS_FMAP_FW_MAIN_B],
+				  &fw_main_b_size) == VB2_SUCCESS;
+
+	if (fw_main_a_in_cbfs_mode)
+		VB2_DEBUG("CBFS found in area %s\n",
+			  fmap_name[BIOS_FMAP_FW_MAIN_A]);
+	else
+		VB2_DEBUG("CBFS not found in area %s\n",
+			  fmap_name[BIOS_FMAP_FW_MAIN_A]);
+
+	if (fw_main_b_in_cbfs_mode)
+		VB2_DEBUG("CBFS found in area %s\n",
+			  fmap_name[BIOS_FMAP_FW_MAIN_B]);
+	else
+		VB2_DEBUG("CBFS not found in area %s\n",
+			  fmap_name[BIOS_FMAP_FW_MAIN_B]);
+
+	if (futil_open_and_map_file(name, &fd, FILE_MODE_SIGN(sign_option),
+				    &buf, &len))
+		return 1;
+
+	memset(&state, 0, sizeof(state));
+
+	retval = prepare_slot(buf, len, fw_main_a_size, BIOS_FMAP_FW_MAIN_A,
+			      BIOS_FMAP_VBLOCK_A, &state);
+	if (retval)
+		goto done;
+
+	retval = prepare_slot(buf, len, fw_main_b_size, BIOS_FMAP_FW_MAIN_B,
+			      BIOS_FMAP_VBLOCK_B, &state);
+	if (retval && state.area[BIOS_FMAP_FW_MAIN_B].is_valid)
+		goto done;
+
+	retval = sign_bios_at_end(&state);
+done:
+	futil_unmap_and_close_file(fd, FILE_MODE_SIGN(sign_option), buf, len);
 	return retval;
 }
 
 enum futil_file_type ft_recognize_bios_image(uint8_t *buf, uint32_t len)
 {
 	FmapHeader *fmap;
-	enum bios_component c;
 
 	fmap = fmap_find(buf, len);
 	if (!fmap)
 		return FILE_TYPE_UNKNOWN;
 
-	for (c = 0; c < NUM_BIOS_COMPONENTS; c++)
-		if (!fmap_find_by_name(buf, len, fmap, fmap_name[c], 0))
-			break;
-	if (c == NUM_BIOS_COMPONENTS)
-		return FILE_TYPE_BIOS_IMAGE;
+	/* Correct BIOS image should contain at least GBB, FW_MAIN_A and
+	   VBLOCK_A areas. FW_MAIN_B and VBLOCK_B are optional, but will be
+	   signed or shown if present. */
+	const int gbb_and_a_slot_ok =
+		fmap_find_by_name(buf, len, fmap, fmap_name[BIOS_FMAP_GBB],
+				  0) != NULL &&
+		fmap_find_by_name(buf, len, fmap,
+				  fmap_name[BIOS_FMAP_FW_MAIN_A], 0) != NULL &&
+		fmap_find_by_name(buf, len, fmap, fmap_name[BIOS_FMAP_VBLOCK_A],
+				  0) != NULL;
 
-	for (c = 0; c < NUM_BIOS_COMPONENTS; c++)
-		if (!fmap_find_by_name(buf, len, fmap, fmap_oldname[c], 0))
-			break;
-	if (c == NUM_BIOS_COMPONENTS)
-		return FILE_TYPE_OLD_BIOS_IMAGE;
+	if (gbb_and_a_slot_ok)
+		return FILE_TYPE_BIOS_IMAGE;
 
 	return FILE_TYPE_UNKNOWN;
 }
diff --git a/futility/file_type_bios.h b/futility/file_type_bios.h
index ef40107..8aa2d63 100644
--- a/futility/file_type_bios.h
+++ b/futility/file_type_bios.h
@@ -28,6 +28,10 @@
 	uint8_t *buf;
 	uint32_t len;
 	uint32_t is_valid;
+
+	/* VBLOCK only */
+	uint32_t flags;
+	uint32_t version;
 };
 
 /* State to track as we visit all components */
diff --git a/futility/file_type_rwsig.c b/futility/file_type_rwsig.c
index a09782c..b595e25 100644
--- a/futility/file_type_rwsig.c
+++ b/futility/file_type_rwsig.c
@@ -58,7 +58,7 @@
 	       sig->data_size);
 }
 
-int ft_show_rwsig(const char *name, uint8_t *buf, uint32_t len, void *nuthin)
+int ft_show_rwsig(const char *name, void *nuthin)
 {
 	const struct vb21_signature *sig = 0;
 	const struct vb21_packed_key *pkey = show_option.pkey;
@@ -71,6 +71,15 @@
 	uint8_t *data;
 	FmapHeader *fmap;
 	int i;
+	int fd = -1;
+	uint8_t *buf;
+	uint32_t len;
+	int rv;
+
+	if (futil_open_and_map_file(name, &fd, FILE_RO, &buf, &len))
+		return 1;
+
+	rv = 1;
 
 	VB2_DEBUG("name %s len 0x%08x (%d)\n", name, len, len);
 
@@ -81,7 +90,8 @@
 		show_sig(name, sig);
 		if (!show_option.fv) {
 			printf("No data available to verify\n");
-			return show_option.strict;
+			rv = show_option.strict;
+			goto done;
 		}
 		data = show_option.fv;
 		data_size = show_option.fv_size;
@@ -99,15 +109,15 @@
 				fmap_find_by_name(buf, len, fmap, "KEY_RO", 0);
 
 			if (pkey)
-				ft_show_vb21_pubkey(name, (uint8_t *)pkey,
-						pkey->c.total_size, NULL);
+				show_vb21_pubkey_buf(name, (uint8_t *)pkey,
+						     pkey->c.total_size, NULL);
 		}
 
 		sig = (const struct vb21_signature *)
 			fmap_find_by_name(buf, len, fmap, "SIG_RW", &fmaparea);
 		if (!sig) {
 			VB2_DEBUG("No SIG_RW in FMAP.\n");
-			return 1;
+			goto done;
 		}
 
 		sig_size = fmaparea->area_size;
@@ -116,7 +126,7 @@
 			  (uint8_t*)sig - buf, sig_size);
 
 		if (VB2_SUCCESS != vb21_verify_signature(sig, sig_size))
-			return 1;
+			goto done;
 
 		show_sig(name, sig);
 		data = fmap_find_by_name(buf, len, fmap, "EC_RW", &fmaparea);
@@ -129,7 +139,7 @@
 
 		if (!data) {
 			VB2_DEBUG("No EC_RW in FMAP.\n");
-			return 1;
+			goto done;
 		}
 	} else {
 		/* Or maybe this is just the RW portion, that does not
@@ -141,7 +151,7 @@
 
 		if (len < sig_size) {
 			VB2_DEBUG("File is too small\n");
-			return 1;
+			goto done;
 		}
 
 		sig = (const struct vb21_signature *)(buf + len - sig_size);
@@ -151,13 +161,14 @@
 			data_size = sig->data_size;
 			total_data_size = len - sig_size;
 		} else {
-			return 1;
+			goto done;
 		}
 	}
 
 	if (!pkey) {
 		printf("No public key available to verify with\n");
-		return show_option.strict;
+		rv = show_option.strict;
+		goto done;
 	}
 
 	/* We already did this once, so it should work again */
@@ -165,12 +176,12 @@
 			    (const uint8_t *)pkey,
 			    pkey->c.total_size)) {
 		VB2_DEBUG("Can't unpack pubkey\n");
-		return 1;
+		goto done;
 	}
 
 	if (data_size > total_data_size) {
 		VB2_DEBUG("Invalid signature data_size: bigger than total area size.\n");
-		return 1;
+		goto done;
 	}
 
 	/* The sig is destroyed by the verify operation, so make a copy */
@@ -185,7 +196,7 @@
 				     (const struct vb2_public_key *)&key,
 				     &wb)) {
 			fprintf(stderr, "Signature verification failed\n");
-			return 1;
+			goto done;
 		}
 	}
 
@@ -193,33 +204,46 @@
 	for (i = data_size; i < total_data_size; i++) {
 		if (data[i] != 0xff) {
 			fprintf(stderr, "Padding verification failed\n");
-			return 1;
+			goto done;
 		}
 	}
 
 	printf("Signature verification succeeded.\n");
-	return 0;
+	rv = 0;
+done:
+	futil_unmap_and_close_file(fd, FILE_RO, buf, len);
+	return rv;
 }
 
-int ft_sign_rwsig(const char *name, uint8_t *buf, uint32_t len, void *nuthin)
+int ft_sign_rwsig(const char *name, void *nuthin)
 {
 	struct vb21_signature *tmp_sig = 0;
 	struct vb2_public_key *pubkey = 0;
 	struct vb21_packed_key *packedkey = 0;
 	uint8_t *keyb_data = 0;
 	uint32_t keyb_size;
-	uint8_t* data = buf; /* data to be signed */
-	uint32_t r, data_size = len, sig_size = SIGNATURE_RSVD_SIZE;
+	uint8_t *data; /* data to be signed */
+	uint32_t r, data_size, sig_size = SIGNATURE_RSVD_SIZE;
 	int retval = 1;
 	FmapHeader *fmap = NULL;
 	FmapAreaHeader *fmaparea;
 	struct vb21_signature *old_sig = 0;
+	uint8_t *buf = NULL;
+	uint32_t len;
+	int fd = -1;
+
+	if (futil_open_and_map_file(name, &fd, FILE_MODE_SIGN(sign_option),
+				    &buf, &len))
+		return 1;
+
+	data = buf;
+	data_size = len;
 
 	VB2_DEBUG("name %s len  0x%08x (%d)\n", name, len, len);
 
 	/* If we don't have a distinct OUTFILE, look for an existing sig */
 	if (sign_option.inout_file_count < 2) {
-		fmap = fmap_find(buf, len);
+		fmap = fmap_find(data, len);
 
 		if (fmap) {
 			/* This looks like a full image. */
@@ -395,14 +419,12 @@
 	/* Finally */
 	retval = 0;
 done:
-	if (tmp_sig)
-		free(tmp_sig);
+	futil_unmap_and_close_file(fd, FILE_MODE_SIGN(sign_option), buf, len);
+	free(tmp_sig);
 	if (pubkey)
 		vb2_public_key_free(pubkey);
-	if (packedkey)
-		free(packedkey);
-	if (keyb_data)
-		free(keyb_data);
+	free(packedkey);
+	free(keyb_data);
 
 	return retval;
 }
diff --git a/futility/file_type_usbpd1.c b/futility/file_type_usbpd1.c
index 9379188..a128a99 100644
--- a/futility/file_type_usbpd1.c
+++ b/futility/file_type_usbpd1.c
@@ -78,7 +78,7 @@
 	return 1;
 }
 
-int ft_sign_usbpd1(const char *name, uint8_t *buf, uint32_t len, void *data)
+int ft_sign_usbpd1(const char *name, void *data)
 {
 	struct vb2_private_key *key_ptr = 0;
 	struct vb21_signature *sig_ptr = 0;
@@ -94,6 +94,13 @@
 	uint32_t ro_offset;
 	uint32_t rw_offset;
 	uint32_t r;
+	uint8_t *buf = NULL;
+	uint32_t len;
+	int fd = -1;
+
+	if (futil_open_and_map_file(name, &fd, FILE_MODE_SIGN(sign_option),
+					 &buf, &len))
+		return 1;
 
 	VB2_DEBUG("name %s len  %#.8x (%d)\n", name, len, len);
 
@@ -237,6 +244,7 @@
 	/* Finally */
 	retval = 0;
 done:
+	futil_unmap_and_close_file(fd, FILE_MODE_SIGN(sign_option), buf, len);
 	if (key_ptr)
 		vb2_private_key_free(key_ptr);
 	if (keyb_data)
@@ -425,35 +433,47 @@
 }
 
 
-int ft_show_usbpd1(const char *name, uint8_t *buf, uint32_t len, void *data)
+int ft_show_usbpd1(const char *name, void *data)
 {
 	uint32_t ro_size, rw_size, ro_offset, rw_offset;
 	int s, h;
+	int fd = -1;
+	uint8_t *buf;
+	uint32_t len;
+	int rv = 1;
+
+	if (futil_open_and_map_file(name, &fd, FILE_RO, &buf, &len))
+		return 1;
 
 	VB2_DEBUG("name %s len  0x%08x (%d)\n", name, len, len);
 
 	/* Get image locations */
 	if (!parse_size_opts(len, &ro_size, &rw_size, &ro_offset, &rw_offset))
-		return 1;
+		goto done;
 
 	/* TODO: If we don't have a RO image, ask for a public key
 	 * TODO: If we're given an external public key, use it (and its alg) */
 	if (!ro_size) {
 		printf("Can't find the public key\n");
-		return 1;
+		goto done;
 	}
 
 	/* TODO: Only loop through the numbers we haven't been given */
-	for (s = 0; s < ARRAY_SIZE(sigs); s++)
-		for (h = 0; h < ARRAY_SIZE(hashes); h++)
-			if (!check_self_consistency(buf, name,
-						    ro_size, rw_size,
+	for (s = 0; s < ARRAY_SIZE(sigs); s++) {
+		for (h = 0; h < ARRAY_SIZE(hashes); h++) {
+			if (!check_self_consistency(buf, name, ro_size, rw_size,
 						    ro_offset, rw_offset,
-						    sigs[s], hashes[h]))
-				return 0;
+						    sigs[s], hashes[h])) {
+				rv = 0;
+				goto done;
+			}
+		}
+	}
 
 	printf("This doesn't appear to be a complete usbpd1 image\n");
-	return 1;
+done:
+	futil_unmap_and_close_file(fd, FILE_RO, buf, len);
+	return rv;
 }
 
 enum futil_file_type ft_recognize_usbpd1(uint8_t *buf, uint32_t len)
diff --git a/futility/futility.h b/futility/futility.h
index 9d40ba7..fa4d3de 100644
--- a/futility/futility.h
+++ b/futility/futility.h
@@ -132,13 +132,26 @@
 	FILE_ERR_SOCK,
 };
 
+enum file_mode {
+	FILE_RO,
+	FILE_RW,
+};
+
+enum futil_file_err futil_open_file(const char *infile, int *fd,
+				    enum file_mode mode);
+enum futil_file_err futil_close_file(int fd);
+
 /* Wrapper for mmap/munmap. Skips stupidly large files. */
-#define MAP_RO 0
-#define MAP_RW 1
-enum futil_file_err futil_map_file(int fd, int writeable,
-				   uint8_t **buf, uint32_t *len);
-enum futil_file_err futil_unmap_file(int fd, int writeable,
-				     uint8_t *buf, uint32_t len);
+enum futil_file_err futil_map_file(int fd, enum file_mode mode, uint8_t **buf,
+				   uint32_t *len);
+enum futil_file_err futil_unmap_file(int fd, enum file_mode mode, uint8_t *buf,
+				     uint32_t len);
+
+enum futil_file_err futil_open_and_map_file(const char *infile, int *fd,
+					    enum file_mode mode, uint8_t **buf,
+					    uint32_t *len);
+enum futil_file_err futil_unmap_and_close_file(int fd, enum file_mode mode,
+					       uint8_t *buf, uint32_t len);
 
 /*
  * Parse input string as a hex representation of size len, exit with error if
diff --git a/futility/futility_options.h b/futility/futility_options.h
index a79505c..32f240a 100644
--- a/futility/futility_options.h
+++ b/futility/futility_options.h
@@ -34,8 +34,7 @@
 	struct vb2_private_key *signprivate;
 	struct vb2_keyblock *keyblock;
 	struct vb2_packed_key *kernel_subkey;
-	struct vb2_private_key *devsignprivate;
-	struct vb2_keyblock *devkeyblock;
+	const char *keysetdir;
 	uint32_t version;
 	int version_specified;
 	uint32_t flags;
@@ -67,4 +66,7 @@
 };
 extern struct sign_option_s sign_option;
 
+#define FILE_MODE_SIGN(sign_options)                                           \
+	(sign_options.create_new_outfile ? FILE_RO : FILE_RW)
+
 #endif  /* VBOOT_REFERENCE_FUTILITY_OPTIONS_H_ */
diff --git a/futility/misc.c b/futility/misc.c
index 76b8ad1..4aec13a 100644
--- a/futility/misc.c
+++ b/futility/misc.c
@@ -5,6 +5,7 @@
 
 #include <assert.h>
 #include <errno.h>
+#include <fcntl.h>
 #if !defined(HAVE_MACOS) && !defined(__FreeBSD__) && !defined(__OpenBSD__)
 #include <linux/fs.h>		/* For BLKGETSIZE64 */
 #endif
@@ -258,8 +259,40 @@
 	exit(1);
 }
 
+enum futil_file_err futil_open_file(const char *infile, int *fd,
+				    enum file_mode mode)
+{
+	if (mode == FILE_RW) {
+		VB2_DEBUG("open RW %s\n", infile);
+		*fd = open(infile, O_RDWR);
+		if (*fd < 0) {
+			fprintf(stderr, "Can't open %s for writing: %s\n",
+				infile, strerror(errno));
+			return FILE_ERR_OPEN;
+		}
+	} else {
+		VB2_DEBUG("open RO %s\n", infile);
+		*fd = open(infile, O_RDONLY);
+		if (*fd < 0) {
+			fprintf(stderr, "Can't open %s for reading: %s\n",
+				infile, strerror(errno));
+			return FILE_ERR_OPEN;
+		}
+	}
+	return FILE_ERR_NONE;
+}
 
-enum futil_file_err futil_map_file(int fd, int writeable,
+enum futil_file_err futil_close_file(int fd)
+{
+	if (fd >= 0 && close(fd)) {
+		fprintf(stderr, "Error when closing ifd: %s\n",
+			strerror(errno));
+		return FILE_ERR_CLOSE;
+	}
+	return FILE_ERR_NONE;
+}
+
+enum futil_file_err futil_map_file(int fd, enum file_mode mode,
 				   uint8_t **buf, uint32_t *len)
 {
 	struct stat sb;
@@ -284,7 +317,7 @@
 	}
 	reasonable_len = (uint32_t)sb.st_size;
 
-	if (writeable)
+	if (mode == FILE_RW)
 		mmap_ptr = mmap(0, sb.st_size,
 				PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
 	else
@@ -293,7 +326,7 @@
 
 	if (mmap_ptr == (void *)-1) {
 		fprintf(stderr, "Can't mmap %s file: %s\n",
-			writeable ? "output" : "input",
+			mode == FILE_RW ? "output" : "input",
 			strerror(errno));
 		return FILE_ERR_MMAP;
 	}
@@ -303,14 +336,14 @@
 	return FILE_ERR_NONE;
 }
 
-enum futil_file_err futil_unmap_file(int fd, int writeable,
+enum futil_file_err futil_unmap_file(int fd, enum file_mode mode,
 				     uint8_t *buf, uint32_t len)
 {
 	void *mmap_ptr = buf;
 	enum futil_file_err err = FILE_ERR_NONE;
 
-	if (writeable &&
-	    (0 != msync(mmap_ptr, len, MS_SYNC|MS_INVALIDATE))) {
+	if (mode == FILE_RW &&
+	    (0 != msync(mmap_ptr, len, MS_SYNC | MS_INVALIDATE))) {
 		fprintf(stderr, "msync failed: %s\n", strerror(errno));
 		err = FILE_ERR_MSYNC;
 	}
@@ -325,6 +358,36 @@
 	return err;
 }
 
+enum futil_file_err futil_open_and_map_file(const char *infile, int *fd,
+					    enum file_mode mode, uint8_t **buf,
+					    uint32_t *len)
+{
+	enum futil_file_err rv = futil_open_file(infile, fd, mode);
+	if (rv != FILE_ERR_NONE)
+		return rv;
+
+	rv = futil_map_file(*fd, mode,  buf, len);
+	if (rv != FILE_ERR_NONE)
+		futil_close_file(*fd);
+
+	return rv;
+}
+
+enum futil_file_err futil_unmap_and_close_file(int fd, enum file_mode mode,
+					       uint8_t *buf, uint32_t len)
+{
+	enum futil_file_err rv = FILE_ERR_NONE;
+
+	if (buf)
+		rv = futil_unmap_file(fd, mode, buf, len);
+	if (rv != FILE_ERR_NONE)
+		return rv;
+
+	if (fd != -1)
+		return futil_close_file(fd);
+	else
+		return FILE_ERR_NONE;
+}
 
 #define DISK_SECTOR_SIZE 512
 enum futil_file_type ft_recognize_gpt(uint8_t *buf, uint32_t len)
diff --git a/futility/updater.c b/futility/updater.c
index 06a696c..1a5e99d 100644
--- a/futility/updater.c
+++ b/futility/updater.c
@@ -20,6 +20,13 @@
 static const char ROOTKEY_HASH_DEV[] =
 		"b11d74edd286c144e1135b49e7f0bc20cf041f10";
 
+enum try_update_type {
+	TRY_UPDATE_OFF = 0,
+	TRY_UPDATE_AUTO,
+	TRY_UPDATE_DEFERRED_HOLD,
+	TRY_UPDATE_DEFERRED_APPLY,
+};
+
 enum target_type {
 	TARGET_SELF,
 	TARGET_UPDATE,
@@ -323,132 +330,6 @@
 }
 
 /*
- * Emulates writing to firmware.
- * Returns 0 if success, non-zero if error.
- */
-static int emulate_write_firmware(const char *filename,
-				  const struct firmware_image *image,
-				  const char *section_name)
-{
-	struct firmware_image to_image = {0};
-	struct firmware_section from, to;
-	int errorcnt = 0;
-
-	INFO("(emulation) Writing %s from %s to %s (emu=%s).\n",
-	     section_name ? section_name : "the whole image",
-	     image->file_name, image->programmer, filename);
-
-	from.data = image->data;
-	from.size = image->size;
-
-	if (load_firmware_image(&to_image, filename, NULL)) {
-		ERROR("Cannot load image from %s.\n", filename);
-		return -1;
-	}
-
-	if (section_name) {
-		find_firmware_section(&from, image, section_name);
-		if (!from.data) {
-			ERROR("No section %s in source image %s.\n",
-			      section_name, image->file_name);
-			errorcnt++;
-		}
-		find_firmware_section(&to, &to_image, section_name);
-		if (!to.data) {
-			ERROR("No section %s in destination image %s.\n",
-			      section_name, filename);
-			errorcnt++;
-		}
-	} else if (image->size != to_image.size) {
-		ERROR("Image size is different (%s:%d != %s:%d)\n",
-		      image->file_name, image->size, to_image.file_name,
-		      to_image.size);
-		errorcnt++;
-	} else {
-		to.data = to_image.data;
-		to.size = to_image.size;
-	}
-
-	if (!errorcnt) {
-		size_t to_write = VB2_MIN(to.size, from.size);
-
-		assert(from.data && to.data);
-		VB2_DEBUG("Writing %zu bytes\n", to_write);
-		memcpy(to.data, from.data, to_write);
-	}
-
-	if (!errorcnt && vb2_write_file(
-			filename, to_image.data, to_image.size)) {
-		ERROR("Failed writing to file: %s\n", filename);
-		errorcnt++;
-	}
-
-	free_firmware_image(&to_image);
-	return errorcnt;
-}
-
-/*
- * Returns the number of retries when reading or writing to flash.
- */
-static int get_io_retries(struct updater_config *cfg)
-{
-	return 1 + get_config_quirk(QUIRK_EXTRA_RETRIES, cfg);
-}
-
-/*
- * Returns 1 if the programmers in image1 and image2 are the same.
- */
-static int is_the_same_programmer(const struct firmware_image *image1,
-				  const struct firmware_image *image2)
-{
-	assert(image1 && image2);
-
-	/* Including if both are NULL. */
-	if (image1->programmer == image2->programmer)
-		return 1;
-
-	/* Not the same if either one is NULL. */
-	if (!image1->programmer || !image2->programmer)
-		return 0;
-
-	return strcmp(image1->programmer, image2->programmer) == 0;
-}
-
-/*
- * Writes multiple sections from the given firmware image to the system.
- * The 'sections' should be NULL (write the whole image) or a list of section
- * names to write, and ended with NULL.
- * Returns 0 if success, non-zero if error.
- */
-static int write_firmware_sections(struct updater_config *cfg,
-				   const struct firmware_image *image,
-				   const char * const sections[])
-{
-	int r = 0;
-	struct firmware_image *diff_image = NULL;
-
-	if (cfg->emulation) {
-		int i;
-		if (!sections)
-			return emulate_write_firmware(
-					cfg->emulation, image, NULL);
-		for (i = 0; sections[i] && !r; i++) {
-			r |= emulate_write_firmware(
-					cfg->emulation, image, sections[i]);
-		}
-		return r;
-	}
-
-	if (cfg->use_diff_image && cfg->image_current.data &&
-	    is_the_same_programmer(&cfg->image_current, image))
-		diff_image = &cfg->image_current;
-
-	return write_system_firmware(image, diff_image, sections,
-				     &cfg->tempfiles, cfg->do_verify,
-				     get_io_retries(cfg), cfg->verbosity + 1);
-}
-
-/*
  * Writes a single section from the given firmware image to the system.
  * Writes the whole firmware image if the section_name is NULL.
  * Returns 0 if success, non-zero if error.
@@ -460,8 +341,8 @@
 	const char *sections[2] = {0};
 
 	sections[0] = section_name;
-	return write_firmware_sections(cfg, image,
-				       section_name ? sections : NULL);
+	return write_system_firmware(
+			cfg, image, section_name ? sections : NULL);
 }
 
 /*
@@ -1077,7 +958,7 @@
 		struct firmware_image *image_to,
 		int wp_enabled)
 {
-	const char *target;
+	const char *target, *self_target;
 	int has_update = 1;
 	int is_vboot2 = get_system_property(SYS_PROP_FW_VBOOT2, cfg);
 
@@ -1093,7 +974,7 @@
 		return UPDATE_ERR_TPM_ROLLBACK;
 
 	VB2_DEBUG("Firmware %s vboot2.\n", is_vboot2 ?  "is" : "is NOT");
-	target = decide_rw_target(cfg, TARGET_SELF, is_vboot2);
+	self_target = target = decide_rw_target(cfg, TARGET_SELF, is_vboot2);
 	if (target == NULL) {
 		ERROR("TRY-RW update needs system to boot in RW firmware.\n");
 		return UPDATE_ERR_TARGET;
@@ -1105,7 +986,7 @@
 		      target, image_to->file_name);
 		return UPDATE_ERR_INVALID_IMAGE;
 	}
-	if (!cfg->force_update)
+	if (!(cfg->force_update || cfg->try_update == TRY_UPDATE_DEFERRED_HOLD))
 		has_update = section_needs_update(image_from, image_to, target);
 
 	if (has_update) {
@@ -1115,6 +996,24 @@
 
 		if (write_firmware(cfg, image_to, target))
 			return UPDATE_ERR_WRITE_FIRMWARE;
+
+		/*
+		 * If the firmware update requested is part of a deferred update
+		 * HOLD action, the autoupdater/postinstall will later call
+		 * defer update APPLY action to set the correct cookies. So here
+		 * it is valid to keep the self slot as the active firmware even
+		 * though the target slot is always updated (whether the current
+		 * active firmware is the same version or not).
+		 */
+		if (cfg->try_update == TRY_UPDATE_DEFERRED_HOLD) {
+			STATUS(
+			    "DEFERRED UPDATE: Defer setting cookies for %s\n",
+			    target);
+			target = self_target;
+			has_update = 0;
+		}
+	} else {
+		STATUS("NO RW UPDATE: No update for RW firmware.\n");
 	}
 
 	/* Always set right cookies for next boot. */
@@ -1128,9 +1027,6 @@
 		write_firmware(cfg, image_to, FMAP_RW_LEGACY);
 	}
 
-	if (!has_update)
-		STATUS("NO UPDATE: No need to update.\n");
-
 	return UPDATE_ERR_DONE;
 }
 
@@ -1144,14 +1040,17 @@
 		struct firmware_image *image_from,
 		struct firmware_image *image_to)
 {
-	int sections_start = 0;
-	static const char * const sections[] = {
-		FMAP_RW_LEGACY,
+	int i, num = 0;
+	static const char * const required_sections[] = {
 		FMAP_RW_SECTION_A,
 		FMAP_RW_SECTION_B,
-		FMAP_RW_SHARED,
-		NULL,
 	};
+	static const char * const optional_sections[] = {
+		FMAP_RW_LEGACY,
+		FMAP_RW_SHARED,
+	};
+	const char *sections[ARRAY_SIZE(required_sections) +
+			     ARRAY_SIZE(optional_sections) + 1];
 
 	STATUS("RW UPDATE: Updating RW sections (%s, %s, %s, and %s).\n",
 	       FMAP_RW_SECTION_A, FMAP_RW_SECTION_B, FMAP_RW_SHARED,
@@ -1163,17 +1062,29 @@
 	if (check_compatible_tpm_keys(cfg, image_to))
 		return UPDATE_ERR_TPM_ROLLBACK;
 
+	for (i = 0; i < ARRAY_SIZE(required_sections); i++)
+		sections[num++] = required_sections[i];
+
 	/*
+	 * The FMAP_RW_LEGACY is a special optional section.
 	 * We may also consider only updating legacy if legacy_needs_update()
 	 * returns true. However, given this is for 'recovery', it is probably
 	 * better to restore everything to the default states. We may revisit
 	 * this if a new scenario is found.
 	 */
-	if (!firmware_section_exists(image_from, sections[sections_start]) ||
-	    !firmware_section_exists(image_to, sections[sections_start]))
-		sections_start++;
+	for (i = 0; i < ARRAY_SIZE(optional_sections); i++) {
+		const char *name = optional_sections[i];
+		if (!firmware_section_exists(image_from, name) ||
+		    !firmware_section_exists(image_to, name)) {
+			VB2_DEBUG("Skipped optional section: %s\n", name);
+			continue;
+		}
+		sections[num++] = name;
+	}
+	assert(num < ARRAY_SIZE(sections));
+	sections[num] = NULL;
 
-	if (write_firmware_sections(cfg, image_to, &sections[sections_start]))
+	if (write_system_firmware(cfg, image_to, sections))
 		return UPDATE_ERR_WRITE_FIRMWARE;
 
 	return UPDATE_ERR_DONE;
@@ -1260,6 +1171,23 @@
 	int wp_enabled, done = 0;
 	enum updater_error_codes r = UPDATE_ERR_UNKNOWN;
 
+	/*
+	 * For deferred update APPLY action, the only requirement is to set the
+	 * correct cookies to the update target slot.
+	 */
+	if (cfg->try_update == TRY_UPDATE_DEFERRED_APPLY) {
+		INFO("Apply deferred updates, only setting cookies for the "
+		     "next boot slot.\n");
+		int vboot2 = get_system_property(SYS_PROP_FW_VBOOT2, cfg);
+		if (set_try_cookies(
+			cfg,
+			decide_rw_target(cfg, TARGET_UPDATE, vboot2),
+			/*has_update=*/1,
+			vboot2))
+			return UPDATE_ERR_SET_COOKIES;
+		return UPDATE_ERR_DONE;
+	}
+
 	struct firmware_image *image_from = &cfg->image_current,
 			      *image_to = &cfg->image;
 	if (!image_to->data)
@@ -1280,8 +1208,7 @@
 		int ret;
 
 		INFO("Loading current system firmware...\n");
-		ret = load_system_firmware(image_from, &cfg->tempfiles,
-					   get_io_retries(cfg), cfg->verbosity);
+		ret = load_system_firmware(cfg, image_from);
 		if (ret == IMAGE_PARSE_FAILURE && cfg->force_update) {
 			WARN("No compatible firmware in system.\n");
 			cfg->check_platform = 0;
@@ -1344,7 +1271,7 @@
  * Allocates and initializes a updater_config object with default values.
  * Returns the newly allocated object, or NULL on error.
  */
-struct updater_config *updater_new_config()
+struct updater_config *updater_new_config(void)
 {
 	struct updater_config *cfg = (struct updater_config *)calloc(
 			1, sizeof(struct updater_config));
@@ -1402,7 +1329,7 @@
 			       const char *pd_image)
 {
 	int errorcnt = 0;
-	struct archive *ar = cfg->archive;
+	struct u_archive *ar = cfg->archive;
 
 	if (!cfg->image.data && image) {
 		if (image && strcmp(image, "-") == 0) {
@@ -1450,23 +1377,20 @@
 }
 
 /*
- * Applies white label information to an existing model config.
+ * Applies custom label information to an existing model config.
  * Returns 0 on success, otherwise failure.
  */
-static int updater_apply_white_label(struct updater_config *cfg,
+static int updater_apply_custom_label(struct updater_config *cfg,
 				     struct model_config *model,
 				     const char *signature_id)
 {
 	const char *tmp_image = NULL;
 
-	assert(model->is_white_label);
+	assert(model->is_custom_label);
 	if (!signature_id) {
 		if (!cfg->image_current.data) {
-			INFO("Loading system firmware for white label...\n");
-			load_system_firmware(&cfg->image_current,
-					     &cfg->tempfiles,
-					     get_io_retries(cfg),
-					     cfg->verbosity);
+			INFO("Loading system firmware for custom label...\n");
+			load_system_firmware(cfg, &cfg->image_current);
 		}
 		tmp_image = get_firmware_image_temp_file(
 				&cfg->image_current, &cfg->tempfiles);
@@ -1479,7 +1403,7 @@
 			quirk_override_signature_id(
 					cfg, model, &signature_id);
 	}
-	return !!model_apply_white_label(
+	return !!model_apply_custom_label(
 			model, cfg->archive, signature_id, tmp_image);
 }
 
@@ -1494,7 +1418,7 @@
 		int is_factory)
 {
 	int errorcnt = 0;
-	struct archive *ar = cfg->archive;
+	struct u_archive *ar = cfg->archive;
 	const struct model_config *model;
 
 	if (arg->do_manifest) {
@@ -1508,33 +1432,34 @@
 	if (!model)
 		return ++errorcnt;
 
-	/* Load images now so we can get quirks in WL checks. */
+	/* Load images now so we can get quirks in custom label checks. */
 	errorcnt += updater_load_images(
 			cfg, arg, model->image, model->ec_image,
 			model->pd_image);
 
-	if (model->is_white_label && !manifest->has_keyset) {
+	if (model->is_custom_label && !manifest->has_keyset) {
 		/*
 		 * Developers running unsigned updaters (usually local build)
-		 * won't be able match any white label tags.
+		 * won't be able match any custom label tags.
 		 */
 		WARN("No keysets found - this is probably a local build of \n"
-		     "unsigned firmware updater. Skip applying white label.");
-	} else if (model->is_white_label) {
+		     "unsigned firmware updater. Skip applying custom label.");
+	} else if (model->is_custom_label) {
 		/*
-		 * It is fine to fail in updater_apply_white_label for factory
+		 * It is fine to fail in updater_apply_custom_label for factory
 		 * mode so we are not checking the return value; instead we
 		 * verify if the patches do contain new root key.
 		 */
-		updater_apply_white_label(cfg, (struct model_config *)model,
+		updater_apply_custom_label(cfg, (struct model_config *)model,
 					  arg->signature_id);
 		if (!model->patches.rootkey) {
 			if (is_factory ||
 			    is_write_protection_enabled(cfg) ||
-			    get_config_quirk(QUIRK_ALLOW_EMPTY_WLTAG, cfg)) {
-				WARN("No VPD for white label.\n");
+			    get_config_quirk(QUIRK_ALLOW_EMPTY_CUSTOM_LABEL_TAG,
+					     cfg)) {
+				WARN("No VPD for custom label.\n");
 			} else {
-				ERROR("Need VPD set for white label.\n");
+				ERROR("Need VPD set for custom label.\n");
 				return ++errorcnt;
 			}
 		}
@@ -1587,12 +1512,16 @@
 
 	/* Setup update mode. */
 	if (arg->try_update)
-		cfg->try_update = 1;
+		cfg->try_update = TRY_UPDATE_AUTO;
 	if (arg->mode) {
 		if (strcmp(arg->mode, "autoupdate") == 0) {
-			cfg->try_update = 1;
+			cfg->try_update = TRY_UPDATE_AUTO;
+		} else if (strcmp(arg->mode, "deferupdate_hold") == 0) {
+			cfg->try_update = TRY_UPDATE_DEFERRED_HOLD;
+		} else if (strcmp(arg->mode, "deferupdate_apply") == 0) {
+			cfg->try_update = TRY_UPDATE_DEFERRED_APPLY;
 		} else if (strcmp(arg->mode, "recovery") == 0) {
-			cfg->try_update = 0;
+			cfg->try_update = TRY_UPDATE_OFF;
 		} else if (strcmp(arg->mode, "legacy") == 0) {
 			cfg->legacy_update = 1;
 		} else if (strcmp(arg->mode, "factory") == 0 ||
@@ -1608,7 +1537,7 @@
 	if (cfg->factory_update) {
 		/* factory_update must be processed after arg->mode. */
 		check_wp_disabled = 1;
-		cfg->try_update = 0;
+		cfg->try_update = TRY_UPDATE_OFF;
 	}
 	cfg->gbb_flags = arg->gbb_flags;
 	cfg->override_gbb_flags = arg->override_gbb_flags;
@@ -1654,7 +1583,7 @@
 	/* Process archives which may not have valid contents. */
 	if (arg->repack || arg->unpack) {
 		const char *work_name = arg->repack ? arg->repack : arg->unpack;
-		struct archive *from, *to, *work;
+		struct u_archive *from, *to, *work;
 
 		work = archive_open(work_name);
 		if (arg->repack) {
@@ -1674,8 +1603,25 @@
 		return errorcnt;
 	}
 
-	/* Load images from archive. */
-	if (arg->archive) {
+	/* Process the manifest and load images from the archive. */
+	if (arg->archive && arg->do_manifest && arg->fast_update) {
+		/* Quickly load and dump the manifest file from the archive. */
+		const char *manifest_name = "manifest.json";
+		uint8_t *data = NULL;
+		uint32_t size = 0;
+
+		if (archive_has_entry(cfg->archive, manifest_name) &&
+		    archive_read_file(cfg->archive, manifest_name, &data, &size,
+				      NULL) == 0) {
+			/* data is NUL-terminated. */
+			printf("%s\n", data);
+			free(data);
+		} else {
+			ERROR("Failed to read the cached manifest: %s\n",
+			      manifest_name);
+			errorcnt++;
+		}
+	} else if (arg->archive) {
 		struct manifest *m = new_manifest_from_archive(cfg->archive);
 		if (m) {
 			errorcnt += updater_setup_archive(
diff --git a/futility/updater.h b/futility/updater.h
index 6dda928..cf03465 100644
--- a/futility/updater.h
+++ b/futility/updater.h
@@ -42,13 +42,14 @@
 	QUIRK_UNLOCK_ME_FOR_UPDATE,
 	QUIRK_UNLOCK_WILCO_ME_FOR_UPDATE,
 	QUIRK_EVE_SMM_STORE,
-	QUIRK_ALLOW_EMPTY_WLTAG,
+	QUIRK_ALLOW_EMPTY_CUSTOM_LABEL_TAG,
 	QUIRK_EC_PARTIAL_RECOVERY,
 	QUIRK_OVERRIDE_SIGNATURE_ID,
 	QUIRK_PRESERVE_ME,
 	QUIRK_NO_CHECK_PLATFORM,
 	QUIRK_NO_VERIFY,
 	QUIRK_EXTRA_RETRIES,
+	QUIRK_EXTERNAL_FLASHROM,
 	QUIRK_MAX,
 };
 
@@ -64,7 +65,7 @@
 	struct firmware_image ec_image, pd_image;
 	struct system_property system_properties[SYS_PROP_MAX];
 	struct quirk_entry quirks[QUIRK_MAX];
-	struct archive *archive;
+	struct u_archive *archive;
 	struct tempfile tempfiles;
 	int try_update;
 	int force_update;
@@ -105,13 +106,13 @@
 	char *image, *ec_image, *pd_image;
 	struct patch_config patches;
 	char *signature_id;
-	int is_white_label;
+	int is_custom_label;
 };
 
 struct manifest {
 	int num;
 	struct model_config *models;
-	struct archive *archive;
+	struct u_archive *archive;
 	int default_model;
 	int has_keyset;
 };
@@ -207,26 +208,26 @@
  * Returns a pointer to reference to archive (must be released by archive_close
  * when not used), otherwise NULL on error.
  */
-struct archive *archive_open(const char *path);
+struct u_archive *archive_open(const char *path);
 
 /*
  * Closes an archive reference.
  * Returns 0 on success, otherwise non-zero as failure.
  */
-int archive_close(struct archive *ar);
+int archive_close(struct u_archive *ar);
 
 /*
  * Checks if an entry (either file or directory) exists in archive.
  * Returns 1 if exists, otherwise 0
  */
-int archive_has_entry(struct archive *ar, const char *name);
+int archive_has_entry(struct u_archive *ar, const char *name);
 
 /*
  * Reads a file from archive.
  * Returns 0 on success (data and size reflects the file content),
  * otherwise non-zero as failure.
  */
-int archive_read_file(struct archive *ar, const char *fname,
+int archive_read_file(struct u_archive *ar, const char *fname,
 		      uint8_t **data, uint32_t *size, int64_t *mtime);
 
 /*
@@ -235,20 +236,30 @@
  * file system.
  * Returns 0 on success, otherwise non-zero as failure.
  */
-int archive_write_file(struct archive *ar, const char *fname,
+int archive_write_file(struct u_archive *ar, const char *fname,
 		       uint8_t *data, uint32_t size, int64_t mtime);
 
 /*
+ * Traverses all files within archive (directories are ignored).
+ * For every entry, the path (relative the archive root) will be passed to
+ * callback function, until the callback returns non-zero.
+ * The arg argument will also be passed to callback.
+ * Returns 0 on success otherwise non-zero as failure.
+ */
+int archive_walk(struct u_archive *ar, void *arg,
+		 int (*callback)(const char *path, void *arg));
+
+/*
  * Copies all entries from one archive to another.
  * Returns 0 on success, otherwise non-zero as failure.
  */
-int archive_copy(struct archive *from, struct archive *to);
+int archive_copy(struct u_archive *from, struct u_archive *to);
 
 /*
  * Creates a new manifest object by scanning files in archive.
  * Returns the manifest on success, otherwise NULL for failure.
  */
-struct manifest *new_manifest_from_archive(struct archive *archive);
+struct manifest *new_manifest_from_archive(struct u_archive *archive);
 
 /* Releases all resources allocated by given manifest object. */
 void delete_manifest(struct manifest *manifest);
@@ -262,7 +273,7 @@
  */
 int patch_image_by_model(
 		struct firmware_image *image, const struct model_config *model,
-		struct archive *archive);
+		struct u_archive *archive);
 
 /*
  * Finds the existing model_config from manifest that best matches current
@@ -273,14 +284,14 @@
 					       const char *model_name);
 
 /*
- * Applies white label information to an existing model configuration.
+ * Applies custom label information to an existing model configuration.
  * Collects signature ID information from either parameter signature_id or
  * image file (via VPD) and updates model.patches for key files.
  * Returns 0 on success, otherwise failure.
  */
-int model_apply_white_label(
+int model_apply_custom_label(
 		struct model_config *model,
-		struct archive *archive,
+		struct u_archive *archive,
 		const char *signature_id,
 		const char *image);
 
diff --git a/futility/updater_archive.c b/futility/updater_archive.c
index 9c3a608..c6e8f28 100644
--- a/futility/updater_archive.c
+++ b/futility/updater_archive.c
@@ -6,19 +6,20 @@
  */
 
 #include <assert.h>
-#include <ctype.h>
 #include <errno.h>
 #if defined(__OpenBSD__)
 #include <sys/types.h>
 #endif
 #include <fts.h>
-#include <string.h>
-#include <stdio.h>
-#include <stdlib.h>
 #include <sys/stat.h>
 #include <sys/time.h>
 #include <unistd.h>
 
+#ifdef HAVE_LIBARCHIVE
+#include <archive.h>
+#include <archive_entry.h>
+#endif
+
 #ifdef HAVE_LIBZIP
 #ifndef __clang__
 /* If libzip headers were built for Clang but later get included with GCC you
@@ -30,62 +31,16 @@
 #include <zip.h>
 #endif
 
-#ifdef HAVE_CROSID
-#include <crosid.h>
-#endif
-
 #include "host_misc.h"
 #include "updater.h"
-#include "util_misc.h"
 
 /*
  * A firmware update package (archive) is a file packed by either shar(1) or
  * zip(1). See https://chromium.googlesource.com/chromiumos/platform/firmware/
  * for more information.
- *
- * A package for single board (i.e., not Unified Build) will have all the image
- * files in top folder:
- *  - host: 'image.bin' (or 'bios.bin' as legacy name before CL:1318712)
- *  - ec: 'ec.bin'
- *  - pd: 'pd.bin'
- * If white label is supported, a 'keyset/' folder will be available, with key
- * files in it:
- *  - rootkey.$WLTAG
- *  - vblock_A.$WLTAG
- *  - vblock_B.$WLTAG
- * The $WLTAG should come from VPD value 'whitelabel_tag', or the
- * 'customization_id'. Note 'customization_id' is in format LOEM[-VARIANT] and
- * we can only take LOEM as $WLTAG, for example A-B => $WLTAG=A.
- *
- * A package for Unified Build is more complicated. There will be a models/
- * folder, and each model (by $(mosys platform model) ) should appear as a sub
- * folder, with a 'setvars.sh' file inside. The 'setvars.sh' is a shell script
- * describing what files should be used and the signature ID ($SIGID) to use.
- *
- * Similar to write label in non-Unified-Build, the keys and vblock files will
- * be in 'keyset/' folder:
- *  - rootkey.$SIGID
- *  - vblock_A.$SIGID
- *  - vblock_B.$SIGID
- * If $SIGID starts with 'sig-id-in-*' then we have to replace it by VPD value
- * 'whitelabel_tag' as '$MODEL-$WLTAG'.
  */
 
-static const char * const SETVARS_IMAGE_MAIN = "IMAGE_MAIN",
-		  * const SETVARS_IMAGE_EC = "IMAGE_EC",
-		  * const SETVARS_IMAGE_PD = "IMAGE_PD",
-		  * const SETVARS_SIGNATURE_ID = "SIGNATURE_ID",
-		  * const SIG_ID_IN_VPD_PREFIX = "sig-id-in",
-		  * const DIR_KEYSET = "keyset",
-		  * const DIR_MODELS = "models",
-		  * const DEFAULT_MODEL_NAME = "default",
-		  * const VPD_WHITELABEL_TAG = "whitelabel_tag",
-		  * const VPD_CUSTOMIZATION_ID = "customization_id",
-		  * const ENV_VAR_MODEL_DIR = "${MODEL_DIR}",
-		  * const PATH_STARTSWITH_KEYSET = "keyset/",
-		  * const PATH_ENDSWITH_SERVARS = "/setvars.sh";
-
-struct archive {
+struct u_archive {
 	void *handle;
 
 	void * (*open)(const char *name);
@@ -101,7 +56,7 @@
 };
 
 /*
- * -- Begin of archive implementations --
+ * -- The fallback driver (using general file system). --
  */
 
 /* Callback for archive_open on a general file system. */
@@ -189,6 +144,7 @@
 	VB2_DEBUG("Reading %s\n", path);
 	*data = NULL;
 	*size = 0;
+	/* vb2_read_file already has an extra '\0' in the end. */
 	r = vb2_read_file(path, data, size) != VB2_SUCCESS;
 	if (mtime) {
 		if (stat(path, &st) == 0)
@@ -234,6 +190,238 @@
 	return r;
 }
 
+/*
+ * -- The cache driver (used by other drivers). --
+ */
+
+#ifdef HAVE_LIBARCHIVE
+
+/*
+ * For stream-based archives (e.g., tar+gz) we want to create a cache for
+ * storing the names and contents for later processing.
+ */
+struct archive_cache {
+	char *name;
+	uint8_t *data;
+	int64_t mtime;
+	size_t size;
+	int has_data;
+	struct archive_cache *next;
+};
+
+/* Add a new cache node to an existing cache list and return the new head. */
+static struct archive_cache *archive_cache_new(struct archive_cache *cache,
+					       const char *name)
+{
+	struct archive_cache *c;
+
+	c = (struct archive_cache *)calloc(sizeof(*c), 1);
+	if (!c)
+		return NULL;
+
+	c->name = strdup(name);
+	if (!c->name) {
+		free(c);
+		return NULL;
+	}
+
+	c->next = cache;
+	return c;
+}
+
+/* Find and return an entry (by name) from the cache. */
+static struct archive_cache *archive_cache_find(struct archive_cache *c,
+						const char *name)
+{
+	for (; c; c = c->next) {
+		assert(c->name);
+		if (!strcmp(c->name, name))
+			return c;
+	}
+	return NULL;
+}
+
+/* Callback for archive_walk to process all entries in the cache. */
+static int archive_cache_walk(
+		struct archive_cache *c, void *arg,
+		int (*callback)(const char *name, void *arg))
+{
+	for (; c; c = c->next) {
+		assert(c->name);
+		if (callback(c->name, arg))
+			break;
+	}
+	return 0;
+}
+
+/* Delete all entries in the cache. */
+static void *archive_cache_free(struct archive_cache *c)
+{
+	struct archive_cache *next;
+
+	while (c) {
+		next = c->next;
+		free(c->name);
+		free(c->data);
+		free(c);
+		c = next;
+	}
+	return NULL;
+}
+
+/*
+ * -- The libarchive driver (multiple formats but very slow). --
+ */
+
+enum {
+	FILTER_IGNORE,
+	FILTER_ABORT,
+	FILTER_NAME_ONLY,
+	FILTER_READ_ALL,
+};
+
+static struct archive_cache *libarchive_read_file_entries(
+		const char *fpath, int (*filter)(struct archive_entry *entry))
+{
+	struct archive *a = archive_read_new();
+	struct archive_entry *entry;
+	struct archive_cache *c, *cache = NULL;
+	int r;
+
+	assert(a);
+	archive_read_support_filter_all(a);
+	archive_read_support_format_all(a);
+	r = archive_read_open_filename(a, fpath, 10240);
+	if (r != ARCHIVE_OK) {
+		ERROR("Failed parsing archive using libarchive: %s\n", fpath);
+		archive_read_free(a);
+		return NULL;
+	}
+
+	WARN("Loading data from archive: %s ", fpath);
+	while (archive_read_next_header(a, &entry) == ARCHIVE_OK) {
+		fputc('.', stderr);
+		if (archive_entry_filetype(entry) != AE_IFREG)
+			continue;
+		if (filter)
+			r = filter(entry);
+		else
+			r = FILTER_READ_ALL;
+
+		if (r == FILTER_ABORT)
+			break;
+		if (r == FILTER_IGNORE)
+			continue;
+
+		c = archive_cache_new(cache, archive_entry_pathname(entry));
+		if (!c) {
+			ERROR("Internal error: out of memory.\n");
+			archive_cache_free(cache);
+			archive_read_free(a);
+			return NULL;
+		}
+		cache = c;
+
+		if (r == FILTER_NAME_ONLY)
+			continue;
+
+		assert(r == FILTER_READ_ALL);
+		c->size = archive_entry_size(entry);
+		c->mtime = archive_entry_mtime(entry);
+		c->data = (uint8_t *)calloc(1, c->size + 1);
+		if (!c->data) {
+			WARN("Out of memory when loading: %s\n", c->name);
+			continue;
+		}
+		if (archive_read_data(a, c->data, c->size) != c->size) {
+			WARN("Failed reading from archive: %s\n", c->name);
+			continue;
+		}
+		c->has_data = 1;
+	}
+	fputs("\r\n", stderr);  /* Flush the '.' */
+	VB2_DEBUG("Finished loading from archive: %s.\n", fpath);
+
+	archive_read_free(a);
+	return cache;
+}
+
+/* Callback for archive_open on an ARCHIVE file. */
+static void *archive_libarchive_open(const char *name)
+{
+	/*
+	 * The firmware archives today can usually all load into memory
+	 * so we are using a NULL filter. Change that to a specific list in
+	 * future if the /build/$BOARD/firmware archive becomes too large.
+	 */
+	return libarchive_read_file_entries(name, NULL);
+}
+
+/* Callback for archive_close on an ARCHIVE file. */
+static int archive_libarchive_close(void *handle)
+{
+	archive_cache_free(handle);
+	return 0;
+}
+
+/* Callback for archive_has_entry on an ARCHIVE file. */
+static int archive_libarchive_has_entry(void *handle, const char *fname)
+{
+	return archive_cache_find(handle, fname) != NULL;
+}
+
+/* Callback for archive_walk on an ARCHIVE file. */
+static int archive_libarchive_walk(
+		void *handle, void *arg,
+		int (*callback)(const char *name, void *arg))
+{
+	return archive_cache_walk(handle, arg, callback);
+}
+
+/* Callback for archive_read_file on an ARCHIVE file. */
+static int archive_libarchive_read_file(
+		void *handle, const char *fname, uint8_t **data,
+		uint32_t *size, int64_t *mtime)
+{
+	struct archive_cache *c = archive_cache_find(handle, fname);
+
+	if (!c)
+		return 1;
+
+	if (!c->has_data) {
+		/* TODO(hungte) Re-read. */
+		ERROR("Not in the cache: %s\n", fname);
+		return 1;
+	}
+
+	if (mtime)
+		*mtime = c->mtime;
+	if (size)
+		*size = c->size;
+	*data = (uint8_t *)malloc(c->size + 1);
+	if (!*data) {
+		ERROR("Out of memory when reading: %s\n", c->name);
+		return 1;
+	}
+	memcpy(*data, c->data, c->size);
+	(*data)[c->size] = '\0';
+	return 0;
+}
+
+/* Callback for archive_write_file on an ARCHIVE file. */
+static int archive_libarchive_write_file(
+		void *handle, const char *fname, uint8_t *data, uint32_t size,
+		int64_t mtime)
+{
+	ERROR("Not implemented!\n");
+	return 1;
+}
+#endif
+
+/*
+ * -- The libzip driver (for ZIP, the official format for CrOS fw updater). --
+ */
+
 #ifdef HAVE_LIBZIP
 
 /* Callback for archive_open on a ZIP file. */
@@ -303,12 +491,13 @@
 		ERROR("Failed to open entry in ZIP: %s\n", fname);
 		return 1;
 	}
-	*data = (uint8_t *)malloc(stat.size);
+	*data = (uint8_t *)malloc(stat.size + 1);
 	if (*data) {
 		if (zip_fread(fp, *data, stat.size) == stat.size) {
 			if (mtime)
 				*mtime = stat.mtime;
 			*size = stat.size;
+			(*data)[stat.size] = '\0';
 		} else {
 			ERROR("Failed to read entry in zip: %s\n", fname);
 			free(*data);
@@ -348,22 +537,26 @@
 #endif
 
 /*
+ * -- The public functions for using u_archive. --
+ */
+
+/*
  * Opens an archive from given path.
  * The type of archive will be determined automatically.
  * Returns a pointer to reference to archive (must be released by archive_close
  * when not used), otherwise NULL on error.
  */
-struct archive *archive_open(const char *path)
+struct u_archive *archive_open(const char *path)
 {
 	struct stat path_stat;
-	struct archive *ar;
+	struct u_archive *ar;
 
 	if (stat(path, &path_stat) != 0) {
 		ERROR("Cannot identify type of path: %s\n", path);
 		return NULL;
 	}
 
-	ar = (struct archive *)malloc(sizeof(*ar));
+	ar = (struct u_archive *)calloc(sizeof(*ar), 1);
 	if (!ar) {
 		ERROR("Internal error: allocation failure.\n");
 		return NULL;
@@ -379,22 +572,48 @@
 		ar->has_entry = archive_fallback_has_entry;
 		ar->read_file = archive_fallback_read_file;
 		ar->write_file = archive_fallback_write_file;
-	} else {
+	}
+
+	/* Format detection must try ZIP (the official format) first. */
 #ifdef HAVE_LIBZIP
-		VB2_DEBUG("Found file, use ZIP driver: %s\n", path);
-		ar->open = archive_zip_open;
-		ar->close = archive_zip_close;
-		ar->walk = archive_zip_walk;
-		ar->has_entry = archive_zip_has_entry;
-		ar->read_file = archive_zip_read_file;
-		ar->write_file = archive_zip_write_file;
-#else
-		ERROR("Found file, but no drivers were enabled: %s\n", path);
+	if (!ar->open) {
+		ar->handle = archive_zip_open(path);
+
+		if (ar->handle) {
+			VB2_DEBUG("Found a ZIP file: %s\n", path);
+			ar->open = archive_zip_open;
+			ar->close = archive_zip_close;
+			ar->walk = archive_zip_walk;
+			ar->has_entry = archive_zip_has_entry;
+			ar->read_file = archive_zip_read_file;
+			ar->write_file = archive_zip_write_file;
+		}
+	}
+#endif
+
+	/* LIBARCHIVE must be the last driver. */
+#ifdef HAVE_LIBARCHIVE
+	if (!ar->open) {
+		VB2_DEBUG("Found a file, use libarchive: %s\n", path);
+		ar->open = archive_libarchive_open;
+		ar->close = archive_libarchive_close;
+		ar->walk = archive_libarchive_walk;
+		ar->has_entry = archive_libarchive_has_entry;
+		ar->read_file = archive_libarchive_read_file;
+		ar->write_file = archive_libarchive_write_file;
+	}
+#endif
+
+	if (!ar->open) {
+		ERROR("Found a file, but no drivers were selected: %s\n", path);
 		free(ar);
 		return NULL;
-#endif
 	}
-	ar->handle = ar->open(path);
+
+	/* Some drivers may have already opened the archive. */
+	if (!ar->handle)
+		ar->handle = ar->open(path);
+
 	if (!ar->handle) {
 		ERROR("Failed to open archive: %s\n", path);
 		free(ar);
@@ -407,7 +626,7 @@
  * Closes an archive reference.
  * Returns 0 on success, otherwise non-zero as failure.
  */
-int archive_close(struct archive *ar)
+int archive_close(struct u_archive *ar)
 {
 	int r = ar->close(ar->handle);
 	free(ar);
@@ -420,7 +639,7 @@
  * with real file system.
  * Returns 1 if exists, otherwise 0
  */
-int archive_has_entry(struct archive *ar, const char *name)
+int archive_has_entry(struct u_archive *ar, const char *name)
 {
 	if (!ar || *name == '/')
 		return archive_fallback_has_entry(NULL, name);
@@ -434,8 +653,8 @@
  * The arg argument will also be passed to callback.
  * Returns 0 on success otherwise non-zero as failure.
  */
-static int archive_walk(struct archive *ar, void *arg,
-			int (*callback)(const char *path, void *arg))
+int archive_walk(struct u_archive *ar, void *arg,
+		 int (*callback)(const char *path, void *arg))
 {
 	if (!ar)
 		return archive_fallback_walk(NULL, arg, callback);
@@ -446,10 +665,12 @@
  * Reads a file from archive.
  * If entry name (fname) is an absolute path (/file), always read
  * from real file system.
+ * The returned data must always have one extra (not included by size) '\0' in
+ * the end of the allocated buffer for C string processing.
  * Returns 0 on success (data and size reflects the file content),
  * otherwise non-zero as failure.
  */
-int archive_read_file(struct archive *ar, const char *fname,
+int archive_read_file(struct u_archive *ar, const char *fname,
 		      uint8_t **data, uint32_t *size, int64_t *mtime)
 {
 	if (!ar || *fname == '/')
@@ -463,7 +684,7 @@
  * file system.
  * Returns 0 on success, otherwise non-zero as failure.
  */
-int archive_write_file(struct archive *ar, const char *fname,
+int archive_write_file(struct u_archive *ar, const char *fname,
 		       uint8_t *data, uint32_t size, int64_t mtime)
 {
 	if (!ar || *fname == '/')
@@ -472,7 +693,7 @@
 }
 
 struct _copy_arg {
-	struct archive *from, *to;
+	struct u_archive *from, *to;
 };
 
 /* Callback for archive_copy. */
@@ -499,654 +720,8 @@
  * Copies all entries from one archive to another.
  * Returns 0 on success, otherwise non-zero as failure.
  */
-int archive_copy(struct archive *from, struct archive *to)
+int archive_copy(struct u_archive *from, struct u_archive *to)
 {
 	struct _copy_arg arg = { .from = from, .to = to };
 	return archive_walk(from, &arg, archive_copy_callback);
 }
-
-/*
- * -- End of archive implementations --
- */
-
-/* Utility function to convert a string. */
-static void str_convert(char *s, int (*convert)(int c))
-{
-	int c;
-
-	for (; *s; s++) {
-		c = *s;
-		if (!isascii(c))
-			continue;
-		*s = convert(c);
-	}
-}
-
-/* Returns 1 if name ends by given pattern, otherwise 0. */
-static int str_endswith(const char *name, const char *pattern)
-{
-	size_t name_len = strlen(name), pattern_len = strlen(pattern);
-	if (name_len < pattern_len)
-		return 0;
-	return strcmp(name + name_len - pattern_len, pattern) == 0;
-}
-
-/* Returns 1 if name starts by given pattern, otherwise 0. */
-static int str_startswith(const char *name, const char *pattern)
-{
-	return strncmp(name, pattern, strlen(pattern)) == 0;
-}
-
-/* Returns the VPD value by given key name, or NULL on error (or no value). */
-static char *vpd_get_value(const char *fpath, const char *key)
-{
-	char *command, *result;
-
-	assert(fpath);
-	ASPRINTF(&command, "vpd -g %s -f %s 2>/dev/null", key, fpath);
-	result = host_shell(command);
-	free(command);
-
-	if (result && !*result) {
-		free(result);
-		result = NULL;
-	}
-	return result;
-}
-
-/*
- * Reads and parses a setvars type file from archive, then stores into config.
- * Returns 0 on success (at least one entry found), otherwise failure.
- */
-static int model_config_parse_setvars_file(
-		struct model_config *cfg, struct archive *archive,
-		const char *fpath)
-{
-	uint8_t *data;
-	uint32_t len;
-
-	char *ptr_line, *ptr_token;
-	char *line, *k, *v;
-	int valid = 0;
-
-	if (archive_read_file(archive, fpath, &data, &len, NULL) != 0) {
-		ERROR("Failed reading: %s\n", fpath);
-		return -1;
-	}
-
-	/* Valid content should end with \n, or \"; ensure ASCIIZ for parsing */
-	if (len)
-		data[len - 1] = '\0';
-
-	for (line = strtok_r((char *)data, "\n\r", &ptr_line); line;
-	     line = strtok_r(NULL, "\n\r", &ptr_line)) {
-		char *expand_path = NULL;
-		int found_valid = 1;
-
-		/* Format: KEY="value" */
-		k = strtok_r(line, "=", &ptr_token);
-		if (!k)
-			continue;
-		v = strtok_r(NULL, "\"", &ptr_token);
-		if (!v)
-			continue;
-
-		/* Some legacy updaters may be still using ${MODEL_DIR}. */
-		if (str_startswith(v, ENV_VAR_MODEL_DIR)) {
-			ASPRINTF(&expand_path, "%s/%s%s", DIR_MODELS, cfg->name,
-				 v + strlen(ENV_VAR_MODEL_DIR));
-		}
-
-		if (strcmp(k, SETVARS_IMAGE_MAIN) == 0)
-			cfg->image = strdup(v);
-		else if (strcmp(k, SETVARS_IMAGE_EC) == 0)
-			cfg->ec_image = strdup(v);
-		else if (strcmp(k, SETVARS_IMAGE_PD) == 0)
-			cfg->pd_image = strdup(v);
-		else if (strcmp(k, SETVARS_SIGNATURE_ID) == 0) {
-			cfg->signature_id = strdup(v);
-			if (str_startswith(v, SIG_ID_IN_VPD_PREFIX))
-				cfg->is_white_label = 1;
-		} else
-			found_valid = 0;
-		free(expand_path);
-		valid += found_valid;
-	}
-	free(data);
-	return valid == 0;
-}
-
-/*
- * Changes the rootkey in firmware GBB to given new key.
- * Returns 0 on success, otherwise failure.
- */
-static int change_gbb_rootkey(struct firmware_image *image,
-			      const char *section_name,
-			      const uint8_t *rootkey, uint32_t rootkey_len)
-{
-	const struct vb2_gbb_header *gbb = find_gbb(image);
-	uint8_t *gbb_rootkey;
-	if (!gbb) {
-		ERROR("Cannot find GBB in image %s.\n", image->file_name);
-		return -1;
-	}
-	if (gbb->rootkey_size < rootkey_len) {
-		ERROR("New root key (%u bytes) larger than GBB (%u bytes).\n",
-		      rootkey_len, gbb->rootkey_size);
-		return -1;
-	}
-
-	gbb_rootkey = (uint8_t *)gbb + gbb->rootkey_offset;
-	/* See cmd_gbb_utility: root key must be first cleared with zero. */
-	memset(gbb_rootkey, 0, gbb->rootkey_size);
-	memcpy(gbb_rootkey, rootkey, rootkey_len);
-	return 0;
-}
-
-/*
- * Changes the VBlock in firmware section to new data.
- * Returns 0 on success, otherwise failure.
- */
-static int change_vblock(struct firmware_image *image, const char *section_name,
-			 const uint8_t *vblock, uint32_t vblock_len)
-{
-	struct firmware_section section;
-
-	find_firmware_section(&section, image, section_name);
-	if (!section.data) {
-		ERROR("Need section %s in image %s.\n", section_name,
-		      image->file_name);
-		return -1;
-	}
-	if (section.size < vblock_len) {
-		ERROR("Section %s too small (%zu bytes) for vblock (%u bytes).\n",
-		      section_name, section.size, vblock_len);
-		return -1;
-	}
-	memcpy(section.data, vblock, vblock_len);
-	return 0;
-}
-
-/*
- * Applies a key file to firmware image.
- * Returns 0 on success, otherwise failure.
- */
-static int apply_key_file(
-		struct firmware_image *image, const char *path,
-		struct archive *archive, const char *section_name,
-		int (*apply)(struct firmware_image *image, const char *section,
-			     const uint8_t *data, uint32_t len))
-{
-	int r = 0;
-	uint8_t *data = NULL;
-	uint32_t len;
-
-	r = archive_read_file(archive, path, &data, &len, NULL);
-	if (r == 0) {
-		VB2_DEBUG("Loaded file: %s\n", path);
-		r = apply(image, section_name, data, len);
-		if (r)
-			ERROR("Failed applying %s to %s\n", path, section_name);
-	} else {
-		ERROR("Failed reading: %s\n", path);
-	}
-	free(data);
-	return r;
-}
-
-/*
- * Modifies a firmware image from patch information specified in model config.
- * Returns 0 on success, otherwise number of failures.
- */
-int patch_image_by_model(
-		struct firmware_image *image, const struct model_config *model,
-		struct archive *archive)
-{
-	int err = 0;
-	if (model->patches.rootkey)
-		err += !!apply_key_file(
-				image, model->patches.rootkey, archive,
-				FMAP_RO_GBB, change_gbb_rootkey);
-	if (model->patches.vblock_a)
-		err += !!apply_key_file(
-				image, model->patches.vblock_a, archive,
-				FMAP_RW_VBLOCK_A, change_vblock);
-	if (model->patches.vblock_b)
-		err += !!apply_key_file(
-				image, model->patches.vblock_b, archive,
-				FMAP_RW_VBLOCK_B, change_vblock);
-	return err;
-}
-
-/*
- * Finds available patch files by given model.
- * Updates `model` argument with path of patch files.
- */
-static void find_patches_for_model(struct model_config *model,
-				   struct archive *archive,
-				   const char *signature_id)
-{
-	char *path;
-	int i;
-
-	const char *names[] = {
-		"rootkey",
-		"vblock_A",
-		"vblock_B",
-	};
-
-	char **targets[] = {
-		&model->patches.rootkey,
-		&model->patches.vblock_a,
-		&model->patches.vblock_b,
-	};
-
-	assert(ARRAY_SIZE(names) == ARRAY_SIZE(targets));
-	for (i = 0; i < ARRAY_SIZE(names); i++) {
-		ASPRINTF(&path, "%s/%s.%s", DIR_KEYSET, names[i], signature_id);
-		if (archive_has_entry(archive, path))
-			*targets[i] = path;
-		else
-			free(path);
-	}
-}
-
-/*
- * Adds and copies one new model config to the existing list of given manifest.
- * Returns a pointer to the newly allocated config, or NULL on failure.
- */
-static struct model_config *manifest_add_model(
-		struct manifest *manifest,
-		const struct model_config *cfg)
-{
-	struct model_config *model;
-	manifest->num++;
-	manifest->models = (struct model_config *)realloc(
-			manifest->models, manifest->num * sizeof(*model));
-	if (!manifest->models) {
-		ERROR("Internal error: failed to allocate buffer.\n");
-		return NULL;
-	}
-	model = &manifest->models[manifest->num - 1];
-	memcpy(model, cfg, sizeof(*model));
-	return model;
-}
-
-/*
- * A callback function for manifest to scan files in archive.
- * Returns 0 to keep scanning, or non-zero to stop.
- */
-static int manifest_scan_entries(const char *name, void *arg)
-{
-	struct manifest *manifest = (struct manifest *)arg;
-	struct archive *archive = manifest->archive;
-	struct model_config model = {0};
-	char *slash;
-
-	if (str_startswith(name, PATH_STARTSWITH_KEYSET))
-		manifest->has_keyset = 1;
-	if (!str_endswith(name, PATH_ENDSWITH_SERVARS))
-		return 0;
-
-	/* name: models/$MODEL/setvars.sh */
-	model.name = strdup(strchr(name, '/') + 1);
-	slash = strchr(model.name, '/');
-	if (slash)
-		*slash = '\0';
-
-	VB2_DEBUG("Found model <%s> setvars: %s\n", model.name, name);
-	if (model_config_parse_setvars_file(&model, archive, name)) {
-		ERROR("Invalid setvars file: %s\n", name);
-		return 0;
-	}
-
-	/* In legacy setvars.sh, the ec_image and pd_image may not exist. */
-	if (model.ec_image && !archive_has_entry(archive, model.ec_image)) {
-		VB2_DEBUG("Ignore non-exist EC image: %s\n", model.ec_image);
-		free(model.ec_image);
-		model.ec_image = NULL;
-	}
-	if (model.pd_image && !archive_has_entry(archive, model.pd_image)) {
-		VB2_DEBUG("Ignore non-exist PD image: %s\n", model.pd_image);
-		free(model.pd_image);
-		model.pd_image = NULL;
-	}
-
-	/* Find patch files. */
-	if (model.signature_id)
-		find_patches_for_model(&model, archive, model.signature_id);
-
-	return !manifest_add_model(manifest, &model);
-}
-
-/**
- * get_manifest_key() - Wrapper to get the firmware manifest key from crosid
- *
- * @manifest_key_out - Output parameter of the firmware manifest key.
- *
- * Returns:
- * - <0 if libcrosid is unavailable or there was an error reading
- *   device data
- * - >=0 (the matched device index) success
- */
-static int get_manifest_key(char **manifest_key_out)
-{
-#ifdef HAVE_CROSID
-	return crosid_get_firmware_manifest_key(manifest_key_out);
-#else
-	ERROR("This version of futility was compiled without libcrosid "
-	      "(perhaps compiled outside of the Chrome OS build system?) and "
-	      "the update command is not fully supported.  Either compile "
-	      "from the Chrome OS build, or pass --model to manually specify "
-	      "the machine model.\n");
-	return -1;
-#endif
-}
-
-/*
- * Finds the existing model_config from manifest that best matches current
- * system (as defined by model_name).
- * Returns a model_config from manifest, or NULL if not found.
- */
-const struct model_config *manifest_find_model(const struct manifest *manifest,
-					       const char *model_name)
-{
-	char *manifest_key = NULL;
-	const struct model_config *model = NULL;
-	int i;
-	int matched_index;
-
-	/*
-	 * For manifest with single model defined, we should just return because
-	 * there are other mechanisms like platform name check to double confirm
-	 * if the firmware is valid.
-	 */
-	if (manifest->num == 1)
-		return &manifest->models[0];
-
-	if (!model_name) {
-		matched_index = get_manifest_key(&manifest_key);
-		if (matched_index < 0) {
-			ERROR("Failed to get device identity.  "
-			      "Run \"crosid -v\" for explanation.\n");
-			return NULL;
-		}
-
-		INFO("Identified the device using libcrosid, "
-		     "matched chromeos-config index: %d, "
-		     "manifest key (model): %s\n",
-		     matched_index, manifest_key);
-		model_name = manifest_key;
-	}
-
-	for (i = 0; !model && i < manifest->num; i++) {
-		if (strcmp(model_name, manifest->models[i].name) == 0)
-			model = &manifest->models[i];
-	}
-	if (!model) {
-		ERROR("Unsupported model: '%s'.\n", model_name);
-
-		fprintf(stderr,
-			"The firmware manifest key '%s' is not present in this "
-			"updater archive. The known keys to this updater "
-			"archive are:\n", model_name);
-
-		for (i = 0; i < manifest->num; i++)
-			fprintf(stderr, " %s", manifest->models[i].name);
-		fprintf(stderr, "\n\n");
-		fprintf(stderr,
-			"Perhaps you are trying to use an updater archive for "
-			"the wrong board, or designed for an older OS version "
-			"before this model was supported.\n");
-		fprintf(stderr,
-			"Hint: Read the FIRMWARE_MANIFEST_KEY from the output "
-			"of the crosid command.\n");
-	}
-
-
-	free(manifest_key);
-	return model;
-}
-
-/*
- * Determines the signature ID to use for white label.
- * Returns the signature ID for looking up rootkey and vblock files.
- * Caller must free the returned string.
- */
-static char *resolve_signature_id(struct model_config *model, const char *image)
-{
-	int is_unibuild = model->signature_id ? 1 : 0;
-	char *wl_tag = vpd_get_value(image, VPD_WHITELABEL_TAG);
-	char *sig_id = NULL;
-
-	/* Unified build: $model.$wl_tag, or $model (b/126800200). */
-	if (is_unibuild) {
-		if (!wl_tag) {
-			WARN("No VPD '%s' set for white label - use model name "
-			     "'%s' as default.\n", VPD_WHITELABEL_TAG,
-			     model->name);
-			return strdup(model->name);
-		}
-
-		ASPRINTF(&sig_id, "%s-%s", model->name, wl_tag);
-		free(wl_tag);
-		return sig_id;
-	}
-
-	/* Non-Unibuild: Upper($wl_tag), or Upper(${cid%%-*}). */
-	if (!wl_tag) {
-		char *cid = vpd_get_value(image, VPD_CUSTOMIZATION_ID);
-		if (cid) {
-			/* customization_id in format LOEM[-VARIANT]. */
-			char *dash = strchr(cid, '-');
-			if (dash)
-				*dash = '\0';
-			wl_tag = cid;
-		}
-	}
-	if (wl_tag)
-		str_convert(wl_tag, toupper);
-	return wl_tag;
-}
-
-/*
- * Applies white label information to an existing model configuration.
- * Collects signature ID information from either parameter signature_id or
- * image file (via VPD) and updates model.patches for key files.
- * Returns 0 on success, otherwise failure.
- */
-int model_apply_white_label(
-		struct model_config *model,
-		struct archive *archive,
-		const char *signature_id,
-		const char *image)
-{
-	char *sig_id = NULL;
-	int r = 0;
-
-	if (!signature_id) {
-		sig_id = resolve_signature_id(model, image);
-		signature_id = sig_id;
-	}
-
-	if (signature_id) {
-		VB2_DEBUG("Find white label patches by signature ID: '%s'.\n",
-		      signature_id);
-		find_patches_for_model(model, archive, signature_id);
-	} else {
-		signature_id = "";
-		WARN("No VPD '%s' set for white label - use default keys.\n",
-		     VPD_WHITELABEL_TAG);
-	}
-	if (!model->patches.rootkey) {
-		ERROR("No keys found for signature_id: '%s'\n", signature_id);
-		r = 1;
-	} else {
-		INFO("Applied for white label: %s\n", signature_id);
-	}
-	free(sig_id);
-	return r;
-}
-
-/*
- * Creates a new manifest object by scanning files in archive.
- * Returns the manifest on success, otherwise NULL for failure.
- */
-struct manifest *new_manifest_from_archive(struct archive *archive)
-{
-	struct manifest manifest = {0}, *new_manifest;
-	struct model_config model = {0};
-	const char * const host_image_name = "image.bin",
-		   * const old_host_image_name = "bios.bin",
-	           * const ec_name = "ec.bin",
-		   * const pd_name = "pd.bin";
-
-	manifest.archive = archive;
-	manifest.default_model = -1;
-	archive_walk(archive, &manifest, manifest_scan_entries);
-	if (manifest.num == 0) {
-		const char *image_name = NULL;
-		struct firmware_image image = {0};
-
-		/* Try to load from current folder. */
-		if (archive_has_entry(archive, old_host_image_name))
-			image_name = old_host_image_name;
-		else if (archive_has_entry(archive, host_image_name))
-			image_name = host_image_name;
-		else
-			return 0;
-
-		model.image = strdup(image_name);
-		if (archive_has_entry(archive, ec_name))
-			model.ec_image = strdup(ec_name);
-		if (archive_has_entry(archive, pd_name))
-			model.pd_image = strdup(pd_name);
-		/* Extract model name from FWID: $Vendor_$Platform.$Version */
-		if (!load_firmware_image(&image, image_name, archive)) {
-			char *token = NULL;
-			if (strtok(image.ro_version, "_"))
-				token = strtok(NULL, ".");
-			if (token && *token) {
-				str_convert(token, tolower);
-				model.name = strdup(token);
-			}
-			free_firmware_image(&image);
-		}
-		if (!model.name)
-			model.name = strdup(DEFAULT_MODEL_NAME);
-		if (manifest.has_keyset)
-			model.is_white_label = 1;
-		manifest_add_model(&manifest, &model);
-		manifest.default_model = manifest.num - 1;
-	}
-	VB2_DEBUG("%d model(s) loaded.\n", manifest.num);
-	if (!manifest.num) {
-		ERROR("No valid configurations found from archive.\n");
-		return NULL;
-	}
-
-	new_manifest = (struct manifest *)malloc(sizeof(manifest));
-	if (!new_manifest) {
-		ERROR("Internal error: memory allocation error.\n");
-		return NULL;
-	}
-	memcpy(new_manifest, &manifest, sizeof(manifest));
-	return new_manifest;
-}
-
-/* Releases all resources allocated by given manifest object. */
-void delete_manifest(struct manifest *manifest)
-{
-	int i;
-	assert(manifest);
-	for (i = 0; i < manifest->num; i++) {
-		struct model_config *model = &manifest->models[i];
-		free(model->name);
-		free(model->signature_id);
-		free(model->image);
-		free(model->ec_image);
-		free(model->pd_image);
-		free(model->patches.rootkey);
-		free(model->patches.vblock_a);
-		free(model->patches.vblock_b);
-	}
-	free(manifest->models);
-	free(manifest);
-}
-
-static const char *get_gbb_key_hash(const struct vb2_gbb_header *gbb,
-				    int32_t offset, int32_t size)
-{
-	struct vb2_packed_key *key;
-
-	if (!gbb)
-		return "<No GBB>";
-	key = (struct vb2_packed_key *)((uint8_t *)gbb + offset);
-	if (vb2_packed_key_looks_ok(key, size))
-		return "<Invalid key>";
-	return packed_key_sha1_string(key);
-}
-
-/* Prints the information of given image file in JSON format. */
-static void print_json_image(
-		const char *name, const char *fpath, struct model_config *m,
-		struct archive *archive, int indent, int is_host)
-{
-	struct firmware_image image = {0};
-	const struct vb2_gbb_header *gbb = NULL;
-	if (!fpath)
-		return;
-	if (load_firmware_image(&image, fpath, archive))
-		return;
-	if (!is_host)
-		printf(",\n");
-	printf("%*s\"%s\": { \"versions\": { \"ro\": \"%s\", \"rw\": \"%s\" },",
-	       indent, "", name, image.ro_version, image.rw_version_a);
-	indent += 2;
-	if (!is_host) {
-		/* No extra information to be printed */
-        } else if (patch_image_by_model(&image, m, archive) != 0) {
-		ERROR("Failed to patch images by model: %s\n", m->name);
-	} else if (NULL != (gbb = find_gbb(&image))) {
-		printf("\n%*s\"keys\": { \"root\": \"%s\", ",
-		       indent, "",
-		       get_gbb_key_hash(gbb, gbb->rootkey_offset,
-					gbb->rootkey_size));
-		printf("\"recovery\": \"%s\" },",
-		       get_gbb_key_hash(gbb, gbb->recovery_key_offset,
-					gbb->recovery_key_size));
-	}
-	printf("\n%*s\"image\": \"%s\" }", indent, "", fpath);
-	free_firmware_image(&image);
-}
-
-/* Prints the information of objects in manifest (models and images) in JSON. */
-void print_json_manifest(const struct manifest *manifest)
-{
-	int i, indent;
-	struct archive *ar = manifest->archive;
-
-	printf("{\n");
-	for (i = 0, indent = 2; i < manifest->num; i++) {
-		struct model_config *m = &manifest->models[i];
-		printf("%s%*s\"%s\": {\n", i ? ",\n" : "", indent, "", m->name);
-		indent += 2;
-		print_json_image("host", m->image, m, ar, indent, 1);
-		print_json_image("ec", m->ec_image, m, ar, indent, 0);
-		print_json_image("pd", m->pd_image, m, ar, indent, 0);
-		if (m->patches.rootkey) {
-			struct patch_config *p = &m->patches;
-			printf(",\n%*s\"patches\": { \"rootkey\": \"%s\", "
-			       "\"vblock_a\": \"%s\", \"vblock_b\": \"%s\" }",
-			       indent, "", p->rootkey, p->vblock_a,
-			       p->vblock_b);
-		}
-		if (m->signature_id)
-			printf(",\n%*s\"signature_id\": \"%s\"", indent, "",
-			       m->signature_id);
-		printf("\n  }");
-		indent -= 2;
-		assert(indent == 2);
-	}
-	printf("\n}\n");
-}
diff --git a/futility/updater_manifest.c b/futility/updater_manifest.c
new file mode 100644
index 0000000..0760cb9
--- /dev/null
+++ b/futility/updater_manifest.c
@@ -0,0 +1,890 @@
+/* Copyright 2022 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.
+ *
+ * Build up the list of updater resources from an archive.
+ */
+
+#include <assert.h>
+#if defined(__OpenBSD__)
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_CROSID
+#include <crosid.h>
+#endif
+
+#include "updater.h"
+#include "util_misc.h"
+
+/*
+ * The updater reads image files from a package. The package is usually an
+ * archive (see updater_archive.c) with image files and configuration files, and
+ * the meta data is maintained by a "manifest" that described below.
+ *
+ * A package for single board (i.e., not Unified Build) will have all the image
+ * files in top folder:
+ *  - host: 'image.bin' (or 'bios.bin' as legacy name before CL:1318712)
+ *  - ec: 'ec.bin'
+ *  - pd: 'pd.bin'
+ * If custom label is supported, a 'keyset/' folder will be available, with key
+ * files in it:
+ *  - rootkey.$CLTAG
+ *  - vblock_A.$CLTAG
+ *  - vblock_B.$CLTAG
+ * The $CLTAG should come from VPD value 'custom_label_tag'. For legacy devices,
+ * the VPD name may be 'whitelabel_tag', or 'customization_id'.
+ * The 'customization_id' has a different format: LOEM[-VARIANT] and we can only
+ * take LOEM as $CLTAG, for example A-B => $CLTAG=A.
+ *
+ * A package for Unified Build is more complicated. There will be a models/
+ * folder, and each model (by $(mosys platform model) ) should appear as a sub
+ * folder, with a 'setvars.sh' file inside. The 'setvars.sh' is a shell script
+ * describing what files should be used and the signature ID ($SIGID) to use.
+ *
+ * Similar to custom label in non-Unified-Build, the keys and vblock files will
+ * be in 'keyset/' folder:
+ *  - rootkey.$SIGID
+ *  - vblock_A.$SIGID
+ *  - vblock_B.$SIGID
+ * If $SIGID starts with 'sig-id-in-*' then we have to replace it by VPD value
+ * 'custom_label_tag' as '$MODEL-$CLTAG'.
+ */
+
+static const char * const SETVARS_IMAGE_MAIN = "IMAGE_MAIN",
+		  * const SETVARS_IMAGE_EC = "IMAGE_EC",
+		  * const SETVARS_IMAGE_PD = "IMAGE_PD",
+		  * const SETVARS_SIGNATURE_ID = "SIGNATURE_ID",
+		  * const SIG_ID_IN_VPD_PREFIX = "sig-id-in",
+		  * const DIR_KEYSET = "keyset",
+		  * const DIR_MODELS = "models",
+		  * const DEFAULT_MODEL_NAME = "default",
+		  * const VPD_CUSTOM_LABEL_TAG = "custom_label_tag",
+		  * const VPD_CUSTOM_LABEL_TAG_LEGACY = "whitelabel_tag",
+		  * const VPD_CUSTOMIZATION_ID = "customization_id",
+		  * const ENV_VAR_MODEL_DIR = "${MODEL_DIR}",
+		  * const PATH_STARTSWITH_KEYSET = "keyset/",
+		  * const PATH_SIGNER_CONFIG = "signer_config.csv",
+		  * const PATH_ENDSWITH_SETVARS = "/setvars.sh";
+
+/* Utility function to convert a string. */
+static void str_convert(char *s, int (*convert)(int c))
+{
+	int c;
+
+	for (; *s; s++) {
+		c = *s;
+		if (!isascii(c))
+			continue;
+		*s = convert(c);
+	}
+}
+
+/* Returns 1 if name ends by given pattern, otherwise 0. */
+static int str_endswith(const char *name, const char *pattern)
+{
+	size_t name_len = strlen(name), pattern_len = strlen(pattern);
+	if (name_len < pattern_len)
+		return 0;
+	return strcmp(name + name_len - pattern_len, pattern) == 0;
+}
+
+/* Returns 1 if name starts by given pattern, otherwise 0. */
+static int str_startswith(const char *name, const char *pattern)
+{
+	return strncmp(name, pattern, strlen(pattern)) == 0;
+}
+
+/* Returns the VPD value by given key name, or NULL on error (or no value). */
+static char *vpd_get_value(const char *fpath, const char *key)
+{
+	char *command, *result;
+
+	assert(fpath);
+	ASPRINTF(&command, "vpd -g %s -f %s 2>/dev/null", key, fpath);
+	result = host_shell(command);
+	free(command);
+
+	if (result && !*result) {
+		free(result);
+		result = NULL;
+	}
+	return result;
+}
+
+/*
+ * Reads and parses a setvars type file from archive, then stores into config.
+ * Returns 0 on success (at least one entry found), otherwise failure.
+ */
+static int model_config_parse_setvars_file(
+		struct model_config *cfg, struct u_archive *archive,
+		const char *fpath)
+{
+	uint8_t *data;
+	uint32_t len;
+
+	char *ptr_line = NULL, *ptr_token = NULL;
+	char *line, *k, *v;
+	int valid = 0;
+
+	if (archive_read_file(archive, fpath, &data, &len, NULL) != 0) {
+		ERROR("Failed reading: %s\n", fpath);
+		return -1;
+	}
+
+	/* Valid content should end with \n, or \"; ensure ASCIIZ for parsing */
+	if (len)
+		data[len - 1] = '\0';
+
+	for (line = strtok_r((char *)data, "\n\r", &ptr_line); line;
+	     line = strtok_r(NULL, "\n\r", &ptr_line)) {
+		char *expand_path = NULL;
+		int found_valid = 1;
+
+		/* Format: KEY="value" */
+		k = strtok_r(line, "=", &ptr_token);
+		if (!k)
+			continue;
+		v = strtok_r(NULL, "\"", &ptr_token);
+		if (!v)
+			continue;
+
+		/* Some legacy updaters may be still using ${MODEL_DIR}. */
+		if (str_startswith(v, ENV_VAR_MODEL_DIR)) {
+			ASPRINTF(&expand_path, "%s/%s%s", DIR_MODELS, cfg->name,
+				 v + strlen(ENV_VAR_MODEL_DIR));
+		}
+
+		if (strcmp(k, SETVARS_IMAGE_MAIN) == 0)
+			cfg->image = strdup(v);
+		else if (strcmp(k, SETVARS_IMAGE_EC) == 0)
+			cfg->ec_image = strdup(v);
+		else if (strcmp(k, SETVARS_IMAGE_PD) == 0)
+			cfg->pd_image = strdup(v);
+		else if (strcmp(k, SETVARS_SIGNATURE_ID) == 0) {
+			cfg->signature_id = strdup(v);
+			if (str_startswith(v, SIG_ID_IN_VPD_PREFIX))
+				cfg->is_custom_label = 1;
+		} else
+			found_valid = 0;
+		free(expand_path);
+		valid += found_valid;
+	}
+	free(data);
+	return valid == 0;
+}
+
+/*
+ * Changes the rootkey in firmware GBB to given new key.
+ * Returns 0 on success, otherwise failure.
+ */
+static int change_gbb_rootkey(struct firmware_image *image,
+			      const char *section_name,
+			      const uint8_t *rootkey, uint32_t rootkey_len)
+{
+	const struct vb2_gbb_header *gbb = find_gbb(image);
+	uint8_t *gbb_rootkey;
+	if (!gbb) {
+		ERROR("Cannot find GBB in image %s.\n", image->file_name);
+		return -1;
+	}
+	if (gbb->rootkey_size < rootkey_len) {
+		ERROR("New root key (%u bytes) larger than GBB (%u bytes).\n",
+		      rootkey_len, gbb->rootkey_size);
+		return -1;
+	}
+
+	gbb_rootkey = (uint8_t *)gbb + gbb->rootkey_offset;
+	/* See cmd_gbb_utility: root key must be first cleared with zero. */
+	memset(gbb_rootkey, 0, gbb->rootkey_size);
+	memcpy(gbb_rootkey, rootkey, rootkey_len);
+	return 0;
+}
+
+/*
+ * Changes the VBlock in firmware section to new data.
+ * Returns 0 on success, otherwise failure.
+ */
+static int change_vblock(struct firmware_image *image, const char *section_name,
+			 const uint8_t *vblock, uint32_t vblock_len)
+{
+	struct firmware_section section;
+
+	find_firmware_section(&section, image, section_name);
+	if (!section.data) {
+		ERROR("Need section %s in image %s.\n", section_name,
+		      image->file_name);
+		return -1;
+	}
+	if (section.size < vblock_len) {
+		ERROR("'%s' is too small (%zu bytes) for vblock (%u bytes).\n",
+		      section_name, section.size, vblock_len);
+		return -1;
+	}
+	memcpy(section.data, vblock, vblock_len);
+	return 0;
+}
+
+/*
+ * Applies a key file to firmware image.
+ * Returns 0 on success, otherwise failure.
+ */
+static int apply_key_file(
+		struct firmware_image *image, const char *path,
+		struct u_archive *archive, const char *section_name,
+		int (*apply)(struct firmware_image *image, const char *section,
+			     const uint8_t *data, uint32_t len))
+{
+	int r = 0;
+	uint8_t *data = NULL;
+	uint32_t len;
+
+	r = archive_read_file(archive, path, &data, &len, NULL);
+	if (r == 0) {
+		VB2_DEBUG("Loaded file: %s\n", path);
+		r = apply(image, section_name, data, len);
+		if (r)
+			ERROR("Failed applying %s to %s\n", path, section_name);
+	} else {
+		ERROR("Failed reading: %s\n", path);
+	}
+	free(data);
+	return r;
+}
+
+/*
+ * Modifies a firmware image from patch information specified in model config.
+ * Returns 0 on success, otherwise number of failures.
+ */
+int patch_image_by_model(
+		struct firmware_image *image, const struct model_config *model,
+		struct u_archive *archive)
+{
+	int err = 0;
+	if (model->patches.rootkey)
+		err += !!apply_key_file(
+				image, model->patches.rootkey, archive,
+				FMAP_RO_GBB, change_gbb_rootkey);
+	if (model->patches.vblock_a)
+		err += !!apply_key_file(
+				image, model->patches.vblock_a, archive,
+				FMAP_RW_VBLOCK_A, change_vblock);
+	if (model->patches.vblock_b)
+		err += !!apply_key_file(
+				image, model->patches.vblock_b, archive,
+				FMAP_RW_VBLOCK_B, change_vblock);
+	return err;
+}
+
+/*
+ * Finds available patch files by given model.
+ * Updates `model` argument with path of patch files.
+ */
+static void find_patches_for_model(struct model_config *model,
+				   struct u_archive *archive,
+				   const char *signature_id)
+{
+	char *path;
+	int i;
+
+	const char * const names[] = {
+		"rootkey",
+		"vblock_A",
+		"vblock_B",
+	};
+
+	char **targets[] = {
+		&model->patches.rootkey,
+		&model->patches.vblock_a,
+		&model->patches.vblock_b,
+	};
+
+	assert(ARRAY_SIZE(names) == ARRAY_SIZE(targets));
+	for (i = 0; i < ARRAY_SIZE(names); i++) {
+		ASPRINTF(&path, "%s/%s.%s", DIR_KEYSET, names[i], signature_id);
+		if (archive_has_entry(archive, path))
+			*targets[i] = path;
+		else
+			free(path);
+	}
+}
+
+/*
+ * Adds and copies one new model config to the existing list of given manifest.
+ * Returns a pointer to the newly allocated config, or NULL on failure.
+ */
+static struct model_config *manifest_add_model(
+		struct manifest *manifest,
+		const struct model_config *cfg)
+{
+	struct model_config *model;
+	manifest->num++;
+	manifest->models = (struct model_config *)realloc(
+			manifest->models, manifest->num * sizeof(*model));
+	if (!manifest->models) {
+		ERROR("Internal error: failed to allocate buffer.\n");
+		return NULL;
+	}
+	model = &manifest->models[manifest->num - 1];
+	memcpy(model, cfg, sizeof(*model));
+	return model;
+}
+
+/*
+ * A callback function for manifest to scan files in archive.
+ * Returns 0 to keep scanning, or non-zero to stop.
+ */
+static int manifest_scan_entries(const char *name, void *arg)
+{
+	struct manifest *manifest = (struct manifest *)arg;
+	struct u_archive *archive = manifest->archive;
+	struct model_config model = {0};
+	char *slash;
+
+	if (str_startswith(name, PATH_STARTSWITH_KEYSET))
+		manifest->has_keyset = 1;
+	if (!str_endswith(name, PATH_ENDSWITH_SETVARS))
+		return 0;
+
+	/* name: models/$MODEL/setvars.sh */
+	model.name = strdup(strchr(name, '/') + 1);
+	slash = strchr(model.name, '/');
+	if (slash)
+		*slash = '\0';
+
+	VB2_DEBUG("Found model <%s> setvars: %s\n", model.name, name);
+	if (model_config_parse_setvars_file(&model, archive, name)) {
+		ERROR("Invalid setvars file: %s\n", name);
+		return 0;
+	}
+
+	/* In legacy setvars.sh, the ec_image and pd_image may not exist. */
+	if (model.ec_image && !archive_has_entry(archive, model.ec_image)) {
+		VB2_DEBUG("Ignore non-exist EC image: %s\n", model.ec_image);
+		free(model.ec_image);
+		model.ec_image = NULL;
+	}
+	if (model.pd_image && !archive_has_entry(archive, model.pd_image)) {
+		VB2_DEBUG("Ignore non-exist PD image: %s\n", model.pd_image);
+		free(model.pd_image);
+		model.pd_image = NULL;
+	}
+
+	/* Find patch files. */
+	if (model.signature_id)
+		find_patches_for_model(&model, archive, model.signature_id);
+
+	return !manifest_add_model(manifest, &model);
+}
+
+/*
+ * A callback function for manifest to scan files in raw /firmware archive.
+ * Returns 0 to keep scanning, or non-zero to stop.
+ */
+static int manifest_scan_raw_entries(const char *name, void *arg)
+{
+	struct manifest *manifest = (struct manifest *)arg;
+	struct u_archive *archive = manifest->archive;
+	struct model_config model = {0};
+	char *ec_name = NULL;
+	int chars_read = 0;
+
+	/*
+	 * /build/$BOARD/firmware (or CPFE firmware archives) layout:
+	 * - image-${MODEL}{,.serial,.dev...}.bin
+	 * - ${MODEL}/ec.bin
+	 */
+
+	if (sscanf(name, "image-%m[^.].bin%n", &model.name, &chars_read) != 1)
+		return 0;
+
+	/* Ignore the names with extra modifiers like image-$MODEL.serial.bin */
+	if (!chars_read || name[chars_read]) {
+		free(model.name);
+		return 0;
+	}
+
+	VB2_DEBUG("Found model <%s>: %s\n", model.name, name);
+	model.image = strdup(name);
+
+	ASPRINTF(&ec_name, "%s/ec.bin", model.name);
+	if (archive_has_entry(archive, ec_name))
+		model.ec_image = strdup(ec_name);
+	free(ec_name);
+
+	return !manifest_add_model(manifest, &model);
+}
+
+/* Returns the matched model config from the manifest, or NULL if not found. */
+static struct model_config *manifest_get_model_config(
+		const struct manifest *manifest, const char *name)
+{
+	int i = 0;
+
+	for (i = 0; i < manifest->num; i++) {
+		if (!strcmp(name, manifest->models[i].name))
+			return &manifest->models[i];
+	}
+	return NULL;
+}
+
+/*
+ * Creates the manifest from the 'signer_config.csv' file.
+ * Returns 0 on success (loaded), otherwise failure.
+ */
+static int manifest_from_signer_config(struct manifest *manifest)
+{
+	struct u_archive *archive = manifest->archive;
+	uint32_t size;
+	uint8_t *data;
+	char *s, *tok_ptr = NULL;
+
+	if (!archive_has_entry(archive, PATH_SIGNER_CONFIG))
+		return -1;
+
+	/*
+	 * CSV format: model_name,firmware_image,key_id,ec_image
+	 *
+	 * Note the key_id is not signature_id and won't be used, and ec_image
+	 * may be optional (for example sarien).
+	 */
+
+	if (archive_read_file(archive, PATH_SIGNER_CONFIG, &data, &size,NULL)) {
+		ERROR("Failed reading: %s\n", PATH_SIGNER_CONFIG);
+		return -1;
+	}
+
+	/* Skip headers. */
+	s = strtok_r((char *)data, "\n", &tok_ptr);
+	if (!s || !strchr(s, ',')) {
+		ERROR("Invalid %s: missing header.\n", PATH_SIGNER_CONFIG);
+		free(data);
+		return -1;
+	}
+
+	for (s = strtok_r(NULL, "\n", &tok_ptr); s != NULL;
+	     s = strtok_r(NULL, "\n", &tok_ptr)) {
+
+		struct model_config model = {0};
+		int discard_model = 0;
+
+		/*
+		 * Both keyid (%3) and ec_image (%4) are optional so we want to
+		 * read at least 2 fields.
+		 */
+		if (sscanf(s, "%m[^,],%m[^,],%*[^,],%m[^,]",
+		    &model.name, &model.image, &model.ec_image) < 2) {
+			ERROR("Invalid entry(%s): %s\n", PATH_SIGNER_CONFIG, s);
+			discard_model = 1;
+		} else if (strchr(model.name, '-')) {
+			/* format: BaseModel-CustomLabel */
+			char *tok_dash;
+			char *base_model;
+			struct model_config *base_model_config;
+
+			VB2_DEBUG("Found custom-label: %s\n", model.name);
+			discard_model = 1;
+			base_model = strtok_r(model.name, "-", &tok_dash);
+			assert(base_model);
+
+			/*
+			 * Currently we assume the base model (e.g., base_model)
+			 * is always listed before CL models in the CSV file -
+			 * this is based on how the signerbot and the
+			 * chromeos-config works today (validated on octopus).
+			 */
+			base_model_config = manifest_get_model_config(
+					manifest, base_model);
+
+			if (!base_model_config) {
+				ERROR("Invalid CL-model: %s\n", base_model);
+			} else if (!base_model_config->is_custom_label) {
+				base_model_config->is_custom_label = 1;
+				/*
+				 * Rewriting signature_id is not necessary,
+				 * but in order to generate the same manifest
+				 * from setvars, we want to temporarily use
+				 * the special value.
+				 */
+				free(base_model_config->signature_id);
+				base_model_config->signature_id = strdup(
+						"sig-id-in-customization-id");
+			}
+		}
+
+		if (discard_model) {
+			free(model.name);
+			free(model.image);
+			free(model.ec_image);
+			continue;
+		}
+
+		model.signature_id = strdup(model.name);
+		if (!manifest_add_model(manifest, &model))
+			break;
+	}
+	free(data);
+	return 0;
+}
+
+/*
+ * Creates the manifest from a simple (legacy) folder with only 1 set of
+ * firmware images.
+ * Returns 0 on success (loaded), otherwise failure.
+ */
+static int manifest_from_simple_folder(struct manifest *manifest)
+{
+	const char * const host_image_name = "image.bin",
+		   * const old_host_image_name = "bios.bin",
+		   * const ec_name = "ec.bin",
+		   * const pd_name = "pd.bin";
+	struct u_archive *archive = manifest->archive;
+	const char *image_name = NULL;
+	struct firmware_image image = {0};
+	struct model_config model = {0};
+
+	/* Try to load from current folder. */
+	if (archive_has_entry(archive, old_host_image_name))
+		image_name = old_host_image_name;
+	else if (archive_has_entry(archive, host_image_name))
+		image_name = host_image_name;
+	else
+		return 1;
+
+	model.image = strdup(image_name);
+	if (archive_has_entry(archive, ec_name))
+		model.ec_image = strdup(ec_name);
+	if (archive_has_entry(archive, pd_name))
+		model.pd_image = strdup(pd_name);
+	/* Extract model name from FWID: $Vendor_$Platform.$Version */
+	if (!load_firmware_image(&image, image_name, archive)) {
+		char *token = NULL;
+		if (strtok(image.ro_version, "_"))
+			token = strtok(NULL, ".");
+		if (token && *token) {
+			str_convert(token, tolower);
+			model.name = strdup(token);
+		}
+		free_firmware_image(&image);
+	}
+	if (!model.name)
+		model.name = strdup(DEFAULT_MODEL_NAME);
+	if (manifest->has_keyset)
+		model.is_custom_label = 1;
+	manifest_add_model(manifest, &model);
+	manifest->default_model = manifest->num - 1;
+
+	return 0;
+}
+
+/**
+ * get_manifest_key() - Wrapper to get the firmware manifest key from crosid
+ *
+ * @manifest_key_out - Output parameter of the firmware manifest key.
+ *
+ * Returns:
+ * - <0 if libcrosid is unavailable or there was an error reading
+ *   device data
+ * - >=0 (the matched device index) success
+ */
+static int get_manifest_key(char **manifest_key_out)
+{
+#ifdef HAVE_CROSID
+	return crosid_get_firmware_manifest_key(manifest_key_out);
+#else
+	ERROR("This version of futility was compiled without libcrosid "
+	      "(perhaps compiled outside of the Chrome OS build system?) and "
+	      "the update command is not fully supported.  Either compile "
+	      "from the Chrome OS build, or pass --model to manually specify "
+	      "the machine model.\n");
+	return -1;
+#endif
+}
+
+/*
+ * Finds the existing model_config from manifest that best matches current
+ * system (as defined by model_name).
+ * Returns a model_config from manifest, or NULL if not found.
+ */
+const struct model_config *manifest_find_model(const struct manifest *manifest,
+					       const char *model_name)
+{
+	char *manifest_key = NULL;
+	const struct model_config *model = NULL;
+	int i;
+	int matched_index;
+
+	/*
+	 * For manifest with single model defined, we should just return because
+	 * there are other mechanisms like platform name check to double confirm
+	 * if the firmware is valid.
+	 */
+	if (manifest->num == 1)
+		return &manifest->models[0];
+
+	if (!model_name) {
+		matched_index = get_manifest_key(&manifest_key);
+		if (matched_index < 0) {
+			ERROR("Failed to get device identity.  "
+			      "Run \"crosid -v\" for explanation.\n");
+			return NULL;
+		}
+
+		INFO("Identified the device using libcrosid, "
+		     "matched chromeos-config index: %d, "
+		     "manifest key (model): %s\n",
+		     matched_index, manifest_key);
+		model_name = manifest_key;
+	}
+
+	model = manifest_get_model_config(manifest, model_name);
+
+	if (!model) {
+		ERROR("Unsupported model: '%s'.\n", model_name);
+
+		fprintf(stderr,
+			"The firmware manifest key '%s' is not present in this "
+			"updater archive. The known keys to this updater "
+			"archive are:\n", model_name);
+
+		for (i = 0; i < manifest->num; i++)
+			fprintf(stderr, " %s", manifest->models[i].name);
+		fprintf(stderr, "\n\n");
+		fprintf(stderr,
+			"Perhaps you are trying to use an updater archive for "
+			"the wrong board, or designed for an older OS version "
+			"before this model was supported.\n");
+		fprintf(stderr,
+			"Hint: Read the FIRMWARE_MANIFEST_KEY from the output "
+			"of the crosid command.\n");
+	}
+
+
+	free(manifest_key);
+	return model;
+}
+
+/*
+ * Determines the signature ID to use for custom label.
+ * Returns the signature ID for looking up rootkey and vblock files.
+ * Caller must free the returned string.
+ */
+static char *resolve_signature_id(struct model_config *model, const char *image)
+{
+	int is_unibuild = model->signature_id ? 1 : 0;
+	char *tag = vpd_get_value(image, VPD_CUSTOM_LABEL_TAG);
+	char *sig_id = NULL;
+
+	if (tag == NULL)
+		tag = vpd_get_value(image, VPD_CUSTOM_LABEL_TAG_LEGACY);
+
+	/* Unified build: $model.$tag, or $model (b/126800200). */
+	if (is_unibuild) {
+		if (!tag) {
+			WARN("No VPD '%s' set for custom label. "
+			     "Use model name '%s' as default.\n",
+			     VPD_CUSTOM_LABEL_TAG, model->name);
+			return strdup(model->name);
+		}
+
+		ASPRINTF(&sig_id, "%s-%s", model->name, tag);
+		free(tag);
+		return sig_id;
+	}
+
+	/* Non-Unibuild: Upper($tag), or Upper(${cid%%-*}). */
+	if (!tag) {
+		char *cid = vpd_get_value(image, VPD_CUSTOMIZATION_ID);
+		if (cid) {
+			/* customization_id in format LOEM[-VARIANT]. */
+			char *dash = strchr(cid, '-');
+			if (dash)
+				*dash = '\0';
+			tag = cid;
+		}
+	}
+	if (tag)
+		str_convert(tag, toupper);
+	return tag;
+}
+
+/*
+ * Applies custom label information to an existing model configuration.
+ * Collects signature ID information from either parameter signature_id or
+ * image file (via VPD) and updates model.patches for key files.
+ * Returns 0 on success, otherwise failure.
+ */
+int model_apply_custom_label(
+		struct model_config *model,
+		struct u_archive *archive,
+		const char *signature_id,
+		const char *image)
+{
+	char *sig_id = NULL;
+	int r = 0;
+
+	if (!signature_id) {
+		sig_id = resolve_signature_id(model, image);
+		signature_id = sig_id;
+	}
+
+	if (signature_id) {
+		VB2_DEBUG("Find custom label patches by signature ID: '%s'.\n",
+		      signature_id);
+		find_patches_for_model(model, archive, signature_id);
+	} else {
+		signature_id = "";
+		WARN("No VPD '%s' set for custom label - use default keys.\n",
+		     VPD_CUSTOM_LABEL_TAG);
+	}
+	if (!model->patches.rootkey) {
+		ERROR("No keys found for signature_id: '%s'\n", signature_id);
+		r = 1;
+	} else {
+		INFO("Applied for custom label: %s\n", signature_id);
+	}
+	free(sig_id);
+	return r;
+}
+
+/*
+ * Creates a new manifest object by scanning files in archive.
+ * Returns the manifest on success, otherwise NULL for failure.
+ */
+struct manifest *new_manifest_from_archive(struct u_archive *archive)
+{
+	struct manifest manifest = {0}, *new_manifest;
+
+	manifest.archive = archive;
+	manifest.default_model = -1;
+
+	VB2_DEBUG("Try to build a manifest from *%s\n", PATH_ENDSWITH_SETVARS);
+	archive_walk(archive, &manifest, manifest_scan_entries);
+
+	if (manifest.num == 0) {
+		VB2_DEBUG("Try to build a manifest from %s\n",
+			  PATH_SIGNER_CONFIG);
+		manifest_from_signer_config(&manifest);
+	}
+	if (manifest.num == 0) {
+		VB2_DEBUG("Try to build a manifest from a */firmware folder\n");
+		archive_walk(archive, &manifest, manifest_scan_raw_entries);
+	}
+	if (manifest.num == 0) {
+		VB2_DEBUG("Try to build a manifest from a simple folder\n");
+		manifest_from_simple_folder(&manifest);
+	}
+
+	VB2_DEBUG("%d model(s) loaded.\n", manifest.num);
+	if (!manifest.num) {
+		ERROR("No valid configurations found from archive.\n");
+		return NULL;
+	}
+
+	new_manifest = (struct manifest *)malloc(sizeof(manifest));
+	if (!new_manifest) {
+		ERROR("Internal error: memory allocation error.\n");
+		return NULL;
+	}
+	memcpy(new_manifest, &manifest, sizeof(manifest));
+	return new_manifest;
+}
+
+/* Releases all resources allocated by given manifest object. */
+void delete_manifest(struct manifest *manifest)
+{
+	int i;
+	assert(manifest);
+	for (i = 0; i < manifest->num; i++) {
+		struct model_config *model = &manifest->models[i];
+		free(model->name);
+		free(model->signature_id);
+		free(model->image);
+		free(model->ec_image);
+		free(model->pd_image);
+		free(model->patches.rootkey);
+		free(model->patches.vblock_a);
+		free(model->patches.vblock_b);
+	}
+	free(manifest->models);
+	free(manifest);
+}
+
+static const char *get_gbb_key_hash(const struct vb2_gbb_header *gbb,
+				    int32_t offset, int32_t size)
+{
+	struct vb2_packed_key *key;
+
+	if (!gbb)
+		return "<No GBB>";
+	key = (struct vb2_packed_key *)((uint8_t *)gbb + offset);
+	if (vb2_packed_key_looks_ok(key, size))
+		return "<Invalid key>";
+	return packed_key_sha1_string(key);
+}
+
+/* Prints the information of given image file in JSON format. */
+static void print_json_image(
+		const char *name, const char *fpath, struct model_config *m,
+		struct u_archive *archive, int indent, int is_host)
+{
+	struct firmware_image image = {0};
+	const struct vb2_gbb_header *gbb = NULL;
+	if (!fpath)
+		return;
+	if (load_firmware_image(&image, fpath, archive))
+		return;
+	if (!is_host)
+		printf(",\n");
+	printf("%*s\"%s\": { \"versions\": { \"ro\": \"%s\", \"rw\": \"%s\" },",
+	       indent, "", name, image.ro_version, image.rw_version_a);
+	indent += 2;
+	if (is_host) {
+		if (patch_image_by_model(&image, m, archive))
+			ERROR("Failed to patch images by model: %s\n", m->name);
+		else
+			gbb = find_gbb(&image);
+	}
+	if (gbb != NULL) {
+		printf("\n%*s\"keys\": { \"root\": \"%s\", ",
+		       indent, "",
+		       get_gbb_key_hash(gbb, gbb->rootkey_offset,
+					gbb->rootkey_size));
+		printf("\"recovery\": \"%s\" },",
+		       get_gbb_key_hash(gbb, gbb->recovery_key_offset,
+					gbb->recovery_key_size));
+	}
+	printf("\n%*s\"image\": \"%s\" }", indent, "", fpath);
+	free_firmware_image(&image);
+}
+
+/* Prints the information of objects in manifest (models and images) in JSON. */
+void print_json_manifest(const struct manifest *manifest)
+{
+	int i, indent;
+	struct u_archive *ar = manifest->archive;
+
+	printf("{\n");
+	for (i = 0, indent = 2; i < manifest->num; i++) {
+		struct model_config *m = &manifest->models[i];
+		printf("%s%*s\"%s\": {\n", i ? ",\n" : "", indent, "", m->name);
+		indent += 2;
+		print_json_image("host", m->image, m, ar, indent, 1);
+		print_json_image("ec", m->ec_image, m, ar, indent, 0);
+		print_json_image("pd", m->pd_image, m, ar, indent, 0);
+		if (m->patches.rootkey) {
+			struct patch_config *p = &m->patches;
+			printf(",\n%*s\"patches\": { \"rootkey\": \"%s\", "
+			       "\"vblock_a\": \"%s\", \"vblock_b\": \"%s\" }",
+			       indent, "", p->rootkey, p->vblock_a,
+			       p->vblock_b);
+		}
+		if (m->signature_id)
+			printf(",\n%*s\"signature_id\": \"%s\"", indent, "",
+			       m->signature_id);
+		printf("\n  }");
+		indent -= 2;
+		assert(indent == 2);
+	}
+	printf("\n}\n");
+}
diff --git a/futility/updater_quirks.c b/futility/updater_quirks.c
index c8140c1..73b7c2f 100644
--- a/futility/updater_quirks.c
+++ b/futility/updater_quirks.c
@@ -56,18 +56,23 @@
 	{ .match = "Google_Scarlet.", .quirks = "min_platform_version=1" },
 	{ .match = "Google_Trogdor.", .quirks = "min_platform_version=2" },
 
-        /* Legacy white label units. */
-        { .match = "Google_Enguarde.", .quirks = "allow_empty_wltag" },
-        { .match = "Google_Expresso.", .quirks = "allow_empty_wltag" },
-        { .match = "Google_Hana.", .quirks = "allow_empty_wltag" },
-        { .match = "Google_Veyron_Jaq.", .quirks = "allow_empty_wltag" },
-        { .match = "Google_Veyron_Jerry.", .quirks = "allow_empty_wltag" },
-        { .match = "Google_Veyron_Mighty.", .quirks = "allow_empty_wltag" },
-        { .match = "Google_Reks.", .quirks = "allow_empty_wltag" },
-        { .match = "Google_Relm.", .quirks = "allow_empty_wltag" },
-        { .match = "Google_Wizpig.", .quirks = "allow_empty_wltag" },
+        /* Legacy custom label units. */
+	{ .match = "Google_Hana.", .quirks = "allow_empty_custom_label_tag" },
+	{ .match = "Google_Reks.", .quirks = "allow_empty_custom_label_tag" },
+	{ .match = "Google_Relm.", .quirks = "allow_empty_custom_label_tag" },
+	{ .match = "Google_Wizpig.", .quirks = "allow_empty_custom_label_tag" },
+	{ .match = "Google_Enguarde.",
+		.quirks = "allow_empty_custom_label_tag" },
+	{ .match = "Google_Expresso.",
+		.quirks = "allow_empty_custom_label_tag" },
+	{ .match = "Google_Veyron_Jaq.",
+		.quirks = "allow_empty_custom_label_tag" },
+	{ .match = "Google_Veyron_Jerry.",
+		.quirks = "allow_empty_custom_label_tag" },
+	{ .match = "Google_Veyron_Mighty.",
+		.quirks = "allow_empty_custom_label_tag" },
 
-        { .match = "Google_Phaser.", .quirks = "override_signature_id" },
+	{ .match = "Google_Phaser.", .quirks = "override_signature_id" },
 };
 
 /* Preserves meta data and reload image contents from given file path. */
@@ -479,9 +484,9 @@
 		       "dedicated FMAP section.";
 	quirks->apply = quirk_eve_smm_store;
 
-	quirks = &cfg->quirks[QUIRK_ALLOW_EMPTY_WLTAG];
-	quirks->name = "allow_empty_wltag";
-	quirks->help = "chromium/906962; allow devices without white label "
+	quirks = &cfg->quirks[QUIRK_ALLOW_EMPTY_CUSTOM_LABEL_TAG];
+	quirks->name = "allow_empty_custom_label_tag";
+	quirks->help = "chromium/906962; allow devices without custom label "
 		       "tags set to use default keys.";
 	quirks->apply = NULL;  /* Simple config. */
 
@@ -516,6 +521,11 @@
 	quirks->name = "extra_retries";
 	quirks->help = "Extra retries when writing to system firmware.";
 	quirks->apply = NULL;  /* Simple config. */
+
+	quirks = &cfg->quirks[QUIRK_EXTERNAL_FLASHROM];
+	quirks->name = "external_flashrom";
+	quirks->help = "Use external flashrom to access the system firmware.";
+	quirks->apply = NULL;  /* Simple config. */
 }
 
 /*
diff --git a/futility/updater_utils.c b/futility/updater_utils.c
index 18e4000..d879f78 100644
--- a/futility/updater_utils.c
+++ b/futility/updater_utils.c
@@ -209,7 +209,7 @@
  * failure, or IMAGE_PARSE_FAILURE for non-vboot images.
  */
 int load_firmware_image(struct firmware_image *image, const char *file_name,
-			struct archive *archive)
+			struct u_archive *archive)
 {
 	if (!file_name) {
 		ERROR("No file name given\n");
@@ -419,7 +419,7 @@
 static int host_get_wp_hw(void)
 {
 	/* wpsw refers to write protection 'switch', not 'software'. */
-	return VbGetSystemPropertyInt("wpsw_cur");
+	return VbGetSystemPropertyInt("wpsw_cur") ? WP_ENABLED : WP_DISABLED;
 }
 
 /* A helper function to return "fw_vboot2" system property. */
@@ -456,16 +456,21 @@
  * Helper function to detect type of Servo board attached to host.
  * Returns a string as programmer parameter on success, otherwise NULL.
  */
-char *host_detect_servo(int *need_prepare_ptr)
+char *host_detect_servo(const char **prepare_ctrl_name)
 {
 	const char *servo_port = getenv(ENV_SERVOD_PORT);
 	const char *servo_name = getenv(ENV_SERVOD_NAME);
 	char *servo_type = host_shell("dut-control -o servo_type 2>/dev/null");
 	const char *programmer = NULL;
 	char *ret = NULL;
-	int need_prepare = 0;  /* To prepare by dut-control cpu_fw_spi:on */
 	char *servo_serial = NULL;
 
+	static const char * const cpu_fw_spi = "cpu_fw_spi";
+	static const char * const ccd_cpu_fw_spi = "ccd_cpu_fw_spi";
+
+	/* By default, no control is needed. */
+	*prepare_ctrl_name = NULL;
+
 	/* Get serial name if servo port is provided. */
 	if ((servo_port && *servo_port) || (servo_name && *servo_name)) {
 		const char *cmd = "dut-control -o serialname 2>/dev/null";
@@ -475,6 +480,9 @@
 		if (strstr(servo_type, "with_servo_micro"))
 			cmd = ("dut-control -o servo_micro_serialname"
 			       " 2>/dev/null");
+		else if (strstr(servo_type, "with_c2d2"))
+			cmd = ("dut-control -o c2d2_serialname"
+			       " 2>/dev/null");
 		else if (strstr(servo_type, "with_ccd"))
 			cmd = "dut-control -o ccd_serialname 2>/dev/null";
 
@@ -489,15 +497,20 @@
 	} else if (strstr(servo_type, "servo_micro")) {
 		VB2_DEBUG("Selected Servo Micro.\n");
 		programmer = "raiden_debug_spi";
-		need_prepare = 1;
+		*prepare_ctrl_name = cpu_fw_spi;
+	} else if (strstr(servo_type, "c2d2")) {
+		VB2_DEBUG("Selected C2D2.\n");
+		programmer = "raiden_debug_spi";
+		*prepare_ctrl_name = cpu_fw_spi;
 	} else if (strstr(servo_type, "ccd_cr50") ||
 		   strstr(servo_type, "ccd_gsc")) {
 		VB2_DEBUG("Selected CCD.\n");
-		programmer = "raiden_debug_spi:target=AP";
+		programmer = "raiden_debug_spi:target=AP,custom_rst=true";
+		*prepare_ctrl_name = ccd_cpu_fw_spi;
 	} else {
 		VB2_DEBUG("Selected Servo V2.\n");
 		programmer = "ft2232_spi:type=google-servo-v2";
-		need_prepare = 1;
+		*prepare_ctrl_name = cpu_fw_spi;
 	}
 
 	if (programmer) {
@@ -513,29 +526,250 @@
 
 	free(servo_type);
 	free(servo_serial);
-	*need_prepare_ptr = need_prepare;
 
 	return ret;
 }
+/*
+ * Returns 1 if the programmers in image1 and image2 are the same.
+ */
+static int is_the_same_programmer(const struct firmware_image *image1,
+				  const struct firmware_image *image2)
+{
+	assert(image1 && image2);
+
+	/* Including if both are NULL. */
+	if (image1->programmer == image2->programmer)
+		return 1;
+
+	/* Not the same if either one is NULL. */
+	if (!image1->programmer || !image2->programmer)
+		return 0;
+
+	return strcmp(image1->programmer, image2->programmer) == 0;
+}
+
+enum flash_command {
+	FLASH_READ = 0,
+	FLASH_WRITE,
+};
+
+/* Converts the flashrom_params to an equivalent flashrom command. */
+static char *get_flashrom_command(enum flash_command flash_cmd,
+				  struct flashrom_params *params,
+				  const char *image_name,
+				  const char *contents_name)
+{
+	int i, len = 0;
+	char *partial = NULL;
+	char *cmd = NULL;
+
+	if (!image_name)
+		image_name = "<IMAGE>";
+	if (!contents_name)
+		contents_name = "<OLD-IMAGE>";
+
+	for (i = 0; params->regions && params->regions[i]; i++)
+		len += strlen(params->regions[i]) + strlen(" -i ");
+
+	if (len) {
+		partial = (char *)malloc(len + 1);
+		if (!partial) {
+			ERROR("Failed to allocate a string buffer.\n");
+			return NULL;
+		}
+
+		partial[0] = '\0';
+		for (i = 0; params->regions[i]; i++) {
+			strcat(partial, " -i ");
+			strcat(partial, params->regions[i]);
+		}
+		assert(strlen(partial) == len);
+	}
+
+	switch (flash_cmd) {
+	case FLASH_READ:
+		ASPRINTF(&cmd, "flashrom -r %s -p %s%s%s",
+			 image_name,
+			 params->image->programmer,
+			 params->verbose > 1 ? " -V" : "",
+			 partial ? partial : "");
+		break;
+
+	case FLASH_WRITE:
+		ASPRINTF(&cmd, "flashrom -w %s -p %s%s%s%s%s%s",
+			 image_name,
+			 params->image->programmer,
+			 params->flash_contents ? " --flash-contents " : "",
+			 params->flash_contents ? contents_name : "",
+			 params->noverify ? " --noverify" : "",
+			 params->verbose > 1 ? " -V" : "",
+			 partial ? partial : "");
+		break;
+
+	default:
+		ERROR("Unknown command: %d.\n", flash_cmd);
+		break;
+	}
+	free(partial);
+	return cmd;
+}
+
+/*
+ * Emulates writing a firmware image to the system.
+ * Returns 0 if success, non-zero if error.
+ */
+static int emulate_write_firmware(const char *filename,
+				  const struct firmware_image *image,
+				  const char * const sections[])
+{
+	int i, errorcnt = 0;
+	struct firmware_image to_image = {0};
+
+	INFO("Writing from %s to %s (emu=%s).\n",
+	     image->file_name, image->programmer, filename);
+
+	if (load_firmware_image(&to_image, filename, NULL)) {
+		ERROR("Cannot load image from %s.\n", filename);
+		return -1;
+	}
+
+	if (image->size != to_image.size) {
+		ERROR("Image size is different (%s:%d != %s:%d)\n",
+		      image->file_name, image->size, to_image.file_name,
+		      to_image.size);
+		errorcnt++;
+		goto exit;
+	}
+
+	if (!sections) {
+		VB2_DEBUG(" - write the whole image.\n");
+		memmove(to_image.data, image->data, image->size);
+	}
+	for (i = 0; sections && sections[i]; i++) {
+		VB2_DEBUG(" - write the section: %s.\n", sections[i]);
+		if (preserve_firmware_section(image, &to_image, sections[i])) {
+			ERROR("Failed to write the section: %s\n", sections[i]);
+			errorcnt++;
+			/*
+			 * Exit the loop, but still write the file to reflect
+			 * the partial changes - same as real flashrom behavior.
+			 */
+			break;
+		}
+	}
+
+	if (vb2_write_file(filename, to_image.data, to_image.size)) {
+		ERROR("Failed writing to file: %s\n", filename);
+		errorcnt++;
+		goto exit;
+	}
+
+exit:
+	free_firmware_image(&to_image);
+	return errorcnt;
+}
+
+static int external_flashrom(enum flash_command flash_cmd,
+			     struct flashrom_params *params,
+			     struct tempfile *tempfiles)
+{
+	int r;
+	char *cmd;
+	const char *image_name = NULL, *contents_name = NULL;
+
+	switch (flash_cmd) {
+	case FLASH_READ:
+		image_name = create_temp_file(tempfiles);
+		break;
+
+	case FLASH_WRITE:
+		image_name = get_firmware_image_temp_file(
+				params->image, tempfiles);
+		if (params->flash_contents)
+			contents_name = get_firmware_image_temp_file(
+					params->flash_contents, tempfiles);
+		break;
+
+	default:
+		ERROR("Unknown command: %d\n", flash_cmd);
+		return -1;
+	}
+
+	cmd = get_flashrom_command(flash_cmd, params, image_name,
+				   contents_name);
+	if (!cmd)
+		return -1;
+
+	VB2_DEBUG(cmd);
+	r = system(cmd);
+	free(cmd);
+	if (r)
+		return r;
+
+	switch (flash_cmd) {
+	case FLASH_READ:
+		r = load_firmware_image(params->image, image_name, NULL);
+		break;
+	default:
+		break;
+	}
+
+	return r;
+}
+
+static int read_flash(struct flashrom_params *params,
+		      struct updater_config *cfg)
+{
+	if (get_config_quirk(QUIRK_EXTERNAL_FLASHROM, cfg))
+		return external_flashrom(FLASH_READ, params, &cfg->tempfiles);
+
+	return flashrom_read_image(params->image, NULL, params->verbose);
+}
+
+static int write_flash(struct flashrom_params *params,
+		       struct updater_config *cfg)
+{
+	int r;
+
+	if (get_config_quirk(QUIRK_EXTERNAL_FLASHROM, cfg))
+		return external_flashrom(FLASH_WRITE, params, &cfg->tempfiles);
+
+	r = flashrom_write_image(params->image,
+				 params->regions,
+				 params->flash_contents,
+				 !params->noverify,
+				 params->verbose);
+	/*
+	 * Force a newline to flush stdout in case if
+	 * flashrom_write_image left some messages in the buffer.
+	 */
+	fprintf(stdout, "\n");
+	return r;
+}
 
 /*
  * Loads the active system firmware image (usually from SPI flash chip).
  * Returns 0 if success, non-zero if error.
  */
-int load_system_firmware(struct firmware_image *image,
-			 struct tempfile *tempfiles,
-			 int retries, int verbosity)
+int load_system_firmware(struct updater_config *cfg,
+			 struct firmware_image *image)
 {
 	int r, i;
+	char *cmd;
+	const int tries = 1 + get_config_quirk(QUIRK_EXTRA_RETRIES, cfg);
+	struct flashrom_params params = {0};
 
-	INFO("flasrhom -r <IMAGE> -p %s%s\n",
-	     image->programmer,
-	     verbosity ? " -V" : "");
+	params.image = image;
+	params.verbose = cfg->verbosity + 1; /* libflashrom verbose 1 = WARN. */
 
-	for (i = 1, r = -1; i <= retries && r != 0; i++) {
+	cmd = get_flashrom_command(FLASH_READ, &params, NULL, NULL);
+	INFO("%s\n", cmd);
+	free(cmd);
+
+	for (i = 1, r = -1; i <= tries && r != 0; i++, params.verbose++) {
 		if (i > 1)
-			WARN("Retry reading firmware (%d/%d)...\n", i, retries);
-		r = flashrom_read_image(image, NULL, verbosity + 1);
+			WARN("Retry reading firmware (%d/%d)...\n", i, tries);
+		r = read_flash(&params, cfg);
 	}
 	if (!r)
 		r = parse_firmware_image(image);
@@ -548,49 +782,38 @@
  * FMAP section names (and ended with a NULL).
  * Returns 0 if success, non-zero if error.
  */
-int write_system_firmware(const struct firmware_image *image,
-			  const struct firmware_image *diff_image,
-			  const char * const sections[],
-			  struct tempfile *tempfiles,
-			  int do_verify, int retries, int verbosity)
+int write_system_firmware(struct updater_config *cfg,
+			  const struct firmware_image *image,
+			  const char * const sections[])
 {
-	int r, i, len = 0;
-	char *partial = NULL;
+	int r = 0, i;
+	char *cmd;
+	const int tries = 1 + get_config_quirk(QUIRK_EXTRA_RETRIES, cfg);
+	struct flashrom_params params = {0};
+	struct firmware_image *flash_contents = NULL;
 
-	for (i = 0; sections && sections[i]; i++)
-		len += strlen(sections[i]) + strlen(" -i ");
-	if (len) {
-		partial = (char *)malloc(len + 1);
-		if (!partial) {
-			ERROR("Failed to allocate a string buffer.\n");
-			return -1;
-		}
-		partial[0] = '\0';
-		for (i = 0; sections[i]; i++) {
-			strcat(partial, " -i ");
-			strcat(partial, sections[i]);
-		}
-		assert(strlen(partial) == len);
-	}
+	if (cfg->emulation)
+		return emulate_write_firmware(cfg->emulation, image, sections);
 
-	INFO("flashrom -w <IMAGE> -p %s%s%s%s%s\n",
-	     image->programmer,
-	     diff_image ? " --flash-contents <DIFF_IMAGE>" : "",
-	     do_verify ? "" : " --noverify",
-	     verbosity > 1 ? " -V" : "",
-	     partial ? partial : "");
-	free(partial);
+	if (cfg->use_diff_image && cfg->image_current.data &&
+	    is_the_same_programmer(&cfg->image_current, image))
+		flash_contents = &cfg->image_current;
 
-	for (i = 1, r = -1; i <= retries && r != 0; i++) {
+	params.image = (struct firmware_image *)image;
+	params.flash_contents = flash_contents;
+	params.regions = sections;
+	params.noverify = !cfg->do_verify;
+	params.noverify_all = true;
+	params.verbose = cfg->verbosity + 1; /* libflashrom verbose 1 = WARN. */
+
+	cmd = get_flashrom_command(FLASH_WRITE, &params, NULL, NULL);
+	INFO("%s\n", cmd);
+	free(cmd);
+
+	for (i = 1, r = -1; i <= tries && r != 0; i++, params.verbose++) {
 		if (i > 1)
-			WARN("Retry writing firmware (%d/%d)...\n", i, retries);
-		r = flashrom_write_image(image, sections, diff_image, do_verify,
-					 verbosity + 1);
-		/*
-		 * Force a newline to flush stdout in case if
-		 * flashrom_write_image left some messages in the buffer.
-		 */
-		fprintf(stdout, "\n");
+			WARN("Retry writing firmware (%d/%d)...\n", i, tries);
+		r = write_flash(&params, cfg);
 	}
 	return r;
 }
diff --git a/futility/updater_utils.h b/futility/updater_utils.h
index 3eb1d33..a99c38c 100644
--- a/futility/updater_utils.h
+++ b/futility/updater_utils.h
@@ -8,6 +8,7 @@
 #ifndef VBOOT_REFERENCE_FUTILITY_UPDATER_UTILS_H_
 #define VBOOT_REFERENCE_FUTILITY_UPDATER_UTILS_H_
 
+#include <stdbool.h>
 #include <stdio.h>
 #include "fmap.h"
 
@@ -15,7 +16,7 @@
 	ERROR("Failed to allocate memory, abort.\n"); exit(1); } while (0)
 
 /* Structure(s) declared in updater_archive */
-struct archive;
+struct u_archive;
 
 /* flashrom programmers. */
 static const char * const PROG_HOST = "host",
@@ -57,6 +58,23 @@
 /* Include definition of 'struct firmware_image;' */
 #include "flashrom.h"
 
+/* Parameters when invoking flashrom. */
+struct flashrom_params {
+	struct firmware_image *image; /* The firmware image to read/write. */
+	const struct firmware_image *flash_contents; /* --flash-contents */
+	const char *const *regions; /* -i: only read/write <region> */
+	bool noverify; /* -n: don't auto-verify */
+	bool noverify_all; /* -N: verify included regions only */
+	int verbose; /* -V: more verbose output */
+	/* Supported by libflashrom but no exported by flashrom_drv:
+	 *  - force
+	 *  - noverify_all
+	 * Not supported by libflashrom:
+	 *  - do_not_diff
+	 *  - ignore_lock
+	 */
+};
+
 enum {
 	IMAGE_LOAD_SUCCESS = 0,
 	IMAGE_READ_FAILURE = -1,
@@ -71,15 +89,17 @@
  * failure, or IMAGE_PARSE_FAILURE for non-vboot images.
  */
 int load_firmware_image(struct firmware_image *image, const char *file_name,
-			struct archive *archive);
+			struct u_archive *archive);
+
+/* Structure(s) declared in updater.h */
+struct updater_config;
 
 /*
  * Loads the active system firmware image (usually from SPI flash chip).
  * Returns 0 if success, non-zero if error.
  */
-int load_system_firmware(struct firmware_image *image,
-			 struct tempfile *tempfiles,
-			 int retries, int verbosity);
+int load_system_firmware(struct updater_config *cfg,
+			 struct firmware_image *image);
 
 /* Frees the allocated resource from a firmware image object. */
 void free_firmware_image(struct firmware_image *image);
@@ -98,11 +118,9 @@
  * FMAP section names (and ended with a NULL).
  * Returns 0 if success, non-zero if error.
  */
-int write_system_firmware(const struct firmware_image *image,
-			  const struct firmware_image *diff_image,
-			  const char * const sections[],
-			  struct tempfile *tempfiles,
-			  int do_verify, int retries, int verbosity);
+int write_system_firmware(struct updater_config *cfg,
+			  const struct firmware_image *image,
+			  const char * const sections[]);
 
 struct firmware_section {
 	uint8_t *data;
@@ -182,7 +200,7 @@
  * Helper function to detect type of Servo board attached to host.
  * Returns a string as programmer parameter on success, otherwise NULL.
  */
-char *host_detect_servo(int *need_prepare_ptr);
+char *host_detect_servo(const char **prepare_ctrl_name);
 
 /*
  * Returns 1 if a given file (cbfs_entry_name) exists inside a particular CBFS
diff --git a/futility/vb2_helper.c b/futility/vb2_helper.c
index 4d676a6..a8452db 100644
--- a/futility/vb2_helper.c
+++ b/futility/vb2_helper.c
@@ -60,7 +60,7 @@
 	return 1;
 }
 
-int ft_show_vb21_pubkey(const char *name, uint8_t *buf, uint32_t len,
+int show_vb21_pubkey_buf(const char *name, uint8_t *buf, uint32_t len,
 			void *data)
 {
 	struct vb2_public_key key;
@@ -91,6 +91,22 @@
 	return 0;
 }
 
+int ft_show_vb21_pubkey(const char *name, void *data)
+{
+	int fd = -1;
+	uint8_t *buf;
+	uint32_t len;
+	int rv;
+
+	if (futil_open_and_map_file(name, &fd, FILE_RO, &buf, &len))
+		return 1;
+
+	rv = show_vb21_pubkey_buf(name, buf, len, data);
+
+	futil_unmap_and_close_file(fd, FILE_RO, buf, len);
+	return rv;
+}
+
 static int vb2_private_key_sha1sum(struct vb2_private_key *key, uint8_t *digest)
 {
 	uint8_t *buf;
@@ -106,15 +122,23 @@
 	return 1;
 }
 
-int ft_show_vb21_privkey(const char *name, uint8_t *buf, uint32_t len,
-			 void *data)
+int ft_show_vb21_privkey(const char *name, void *data)
 {
 	struct vb2_private_key *key = 0;
 	uint8_t sha1sum[VB2_SHA1_DIGEST_SIZE];
+	int fd = -1;
+	uint8_t *buf;
+	uint32_t len;
+	int rv = 0;
 
-	if (VB2_SUCCESS != vb21_private_key_unpack(&key, buf, len))
+	if (futil_open_and_map_file(name, &fd, FILE_RO, &buf, &len))
 		return 1;
 
+	if (VB2_SUCCESS != vb21_private_key_unpack(&key, buf, len)) {
+		rv = 1;
+		goto done;
+	}
+
 	printf("Private key file:      %s\n", name);
 	printf("  Vboot API:           2.1\n");
 	printf("  Desc:                \"%s\"\n", key->desc ? key->desc : "");
@@ -132,7 +156,9 @@
 		printf("\n");
 	}
 	vb2_private_key_free(key);
-	return 0;
+done:
+	futil_unmap_and_close_file(fd, FILE_RO, buf, len);
+	return rv;
 }
 
 static RSA *rsa_from_buffer(uint8_t *buf, uint32_t len)
@@ -172,7 +198,7 @@
 	return FILE_TYPE_UNKNOWN;
 }
 
-int ft_show_pem(const char *name, uint8_t *buf, uint32_t len, void *data)
+int ft_show_pem(const char *name, void *data)
 {
 	RSA *rsa_key;
 	uint8_t *keyb;
@@ -180,6 +206,13 @@
 	uint32_t keyb_len;
 	int i, bits;
 	const BIGNUM *rsa_key_n, *rsa_key_d;
+	int fd = -1;
+	uint8_t *buf;
+	uint32_t len;
+	int rv = 0;
+
+	if (futil_open_and_map_file(name, &fd, FILE_RO, &buf, &len))
+		return 1;
 
 	/* We're called only after ft_recognize_pem, so this should work. */
 	rsa_key = rsa_from_buffer(buf, len);
@@ -197,7 +230,8 @@
 	if (vb_keyb_from_rsa(rsa_key, &keyb, &keyb_len)) {
 		printf("  Key sha1sum:         <error>");
 		RSA_free(rsa_key);
-		return 1;
+		rv = 1;
+		goto done;
 	}
 
 	printf("  Key sha1sum:         ");
@@ -209,5 +243,7 @@
 
 	free(keyb);
 	RSA_free(rsa_key);
-	return 0;
+done:
+	futil_unmap_and_close_file(fd, FILE_RO, buf, len);
+	return rv;
 }
diff --git a/host/arch/x86/lib/crossystem_arch.c b/host/arch/x86/lib/crossystem_arch.c
index d48cf87..4040ddc 100644
--- a/host/arch/x86/lib/crossystem_arch.c
+++ b/host/arch/x86/lib/crossystem_arch.c
@@ -734,6 +734,8 @@
 	{ "INTC1055:00", FindGpioChipOffsetByLabel },
 	{ "INTC1056:00", FindGpioChipOffsetByLabel },
 	{ "INTC1057:00", FindGpioChipOffsetByLabel },
+	/* INTC108x are for Meteor Lake */
+	{ "INTC1083:00", FindGpioChipOffsetByLabel },
 	/* INT3453 are for GLK */
 	{ "INT3453:00", FindGpioChipOffsetByLabel },
 	{ "INT3453:01", FindGpioChipOffsetByLabel },
diff --git a/host/lib/cbfstool.c b/host/lib/cbfstool.c
new file mode 100644
index 0000000..8f54706
--- /dev/null
+++ b/host/lib/cbfstool.c
@@ -0,0 +1,72 @@
+/* Copyright 2022 The ChromiumOS Authors.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "2common.h"
+#include "2return_codes.h"
+#include "subprocess.h"
+#include "cbfstool.h"
+
+static const char *get_cbfstool_path(void)
+{
+	static const char *cbfstool = NULL;
+
+	if (cbfstool)
+		return cbfstool;
+
+	const char *env_cbfstool = getenv(ENV_CBFSTOOL);
+	if (env_cbfstool && env_cbfstool[0] != '\0') {
+		cbfstool = strdup(env_cbfstool);
+		return cbfstool;
+	}
+
+	cbfstool = DEFAULT_CBFSTOOL;
+	return cbfstool;
+}
+
+vb2_error_t cbfstool_truncate(const char *file, const char *region,
+			      size_t *new_size)
+{
+	int status;
+	char output_buffer[128];
+	const char *cbfstool = get_cbfstool_path();
+
+	struct subprocess_target output = {
+		.type = TARGET_BUFFER_NULL_TERMINATED,
+		.buffer = {
+			.buf = output_buffer,
+			.size = sizeof(output_buffer),
+		},
+	};
+	const char *const argv[] = {
+		cbfstool, file, "truncate", "-r", region, NULL,
+	};
+
+	VB2_DEBUG("Calling: %s '%s' truncate -r '%s'\n", cbfstool, file,
+		  region);
+	status = subprocess_run(argv, &subprocess_null, &output,
+				&subprocess_null);
+
+	if (status < 0) {
+		fprintf(stderr, "%s(): cbfstool invocation failed: %m\n",
+			__func__);
+		exit(1);
+	}
+
+	/* Positive exit code means something is wrong with image. Return zero
+	   as new size, because it might be problem with missing CBFS.*/
+	if (status > 0) {
+		*new_size = 0;
+		return VB2_ERROR_CBFSTOOL;
+	}
+
+	if (sscanf(output_buffer, "%zi", new_size) != 1) {
+		VB2_DEBUG("Failed to parse command output. Unexpected "
+			  "output.\n");
+		*new_size = 0;
+		return VB2_ERROR_CBFSTOOL;
+	}
+
+	return VB2_SUCCESS;
+}
diff --git a/host/lib/crossystem.c b/host/lib/crossystem.c
index a5427c2..6d55335 100644
--- a/host/lib/crossystem.c
+++ b/host/lib/crossystem.c
@@ -38,7 +38,8 @@
 typedef enum VdatStringField {
 	VDAT_STRING_DEPRECATED_TIMERS = 0,  /* Timer values */
 	VDAT_STRING_LOAD_FIRMWARE_DEBUG,  /* LoadFirmware() debug info */
-	VDAT_STRING_DEPRECATED_LOAD_KERNEL_DEBUG,  /* LoadKernel() debug info */
+	VDAT_STRING_DEPRECATED_LOAD_KERNEL_DEBUG,  /* vb2api_load_kernel()
+						      debug info */
 	VDAT_STRING_MAINFW_ACT  /* Active main firmware */
 } VdatStringField;
 
diff --git a/host/lib/flashrom_drv.c b/host/lib/flashrom_drv.c
index 1d9e3fd..19d6384 100644
--- a/host/lib/flashrom_drv.c
+++ b/host/lib/flashrom_drv.c
@@ -189,12 +189,14 @@
 			}
 		}
 		flashrom_layout_set(flashctx, layout);
+	} else if (image->size != len) {
+		r = -1;
+		goto err_cleanup;
 	}
 
-	flashrom_flag_set(flashctx, FLASHROM_FLAG_VERIFY_WHOLE_CHIP, true);
-	flashrom_flag_set(flashctx, FLASHROM_FLAG_VERIFY_AFTER_WRITE, true);
-	if (!do_verify)
-		flashrom_flag_set(flashctx, FLASHROM_FLAG_VERIFY_AFTER_WRITE, false);
+	flashrom_flag_set(flashctx, FLASHROM_FLAG_VERIFY_WHOLE_CHIP, false);
+	flashrom_flag_set(flashctx, FLASHROM_FLAG_VERIFY_AFTER_WRITE,
+			  do_verify);
 
 	r |= flashrom_image_write(flashctx, image->data, image->size,
 				  diff_image ? diff_image->data : NULL);
diff --git a/host/lib/include/cbfstool.h b/host/lib/include/cbfstool.h
new file mode 100644
index 0000000..1343d40
--- /dev/null
+++ b/host/lib/include/cbfstool.h
@@ -0,0 +1,12 @@
+/* Copyright 2022 The ChromiumOS Authors.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "2return_codes.h"
+
+#define ENV_CBFSTOOL "CBFSTOOL"
+#define DEFAULT_CBFSTOOL "cbfstool"
+
+vb2_error_t cbfstool_truncate(const char *file, const char *region,
+			      size_t *new_size);
diff --git a/scripts/image_signing/common.sh b/scripts/image_signing/common.sh
index efa00a2..81bcac3 100644
--- a/scripts/image_signing/common.sh
+++ b/scripts/image_signing/common.sh
@@ -136,5 +136,24 @@
   sudo setfattr -n security.selinux -v "u:object_r:cros_conf_file:s0" "$1"
 }
 
+# Extracts a firmware updater bundle (for firmware image binaries) file
+# (generated by src/platform/firmware/pack_firmware.sh).
+# Args: INPUT_FILE OUTPUT_DIR
+extract_firmware_bundle() {
+  local input="$(readlink -f "$1")"
+  local output_dir="$2"
+  if [[ ! -s "${input}" ]]; then
+    return 1
+  elif grep -q '^##CUTHERE##' "${input}"; then
+    # Bundle supports self-extraction.
+    "${input}" --sb_extract "${output_dir}" ||
+      die "Extracting firmware autoupdate (--sb_extract) failed."
+  else
+    # Legacy bundle - try uudecode.
+    uudecode -o - "${input}" | tar -C "${output_dir}" -zxf - 2>/dev/null ||
+      die "Extracting firmware autoupdate failed."
+  fi
+}
+
 # This will override the trap set in common_minmal.sh
 trap "cleanup" INT TERM EXIT
diff --git a/scripts/image_signing/ensure_amd_psp_flags.sh b/scripts/image_signing/ensure_amd_psp_flags.sh
new file mode 100755
index 0000000..efccb69
--- /dev/null
+++ b/scripts/image_signing/ensure_amd_psp_flags.sh
@@ -0,0 +1,115 @@
+#!/bin/bash
+# Copyright 2022 The ChromiumOS Authors.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Load common constants and variables.
+. "$(dirname "$0")/common.sh"
+
+# Abort on error and uninitialized variables.
+set -eu
+
+declare -A -r REQUIRED_BIT_MASKS=(
+  # Bit 58 - PSP_S0I3_RESUME_VERSTAGE - Run PSP verstage during S0i3 resume.
+  # Checks that FW images have not been tampered with when exiting S0i3.
+  [guybrush]="$((1 << 58))"
+  [zork]="0x0"
+)
+
+declare -A -r FORBIDDEN_BIT_MASKS=(
+  [guybrush]="0x0"
+  [zork]="0x0"
+)
+
+# Grunt uses an old firmware format that amdfwread cannot read.
+# See b/233787191 for skyrim.
+BOARD_IGNORE_LIST=(grunt skyrim)
+
+usage() {
+  echo "$0: Validate AMD PSP soft-fuse flags contained in a ChromeOS image." \
+    "These flags can have security implications and control debug features."
+  echo "Usage $0 <board> <image>"
+}
+
+main() {
+  if [[ $# -ne 2 ]]; then
+    usage
+    exit 1
+  fi
+
+  local board="$1"
+  local image="$2"
+
+  # Check the ignore list.
+  if [[ " ${BOARD_IGNORE_LIST[*]} " == *" ${board} "* ]]; then
+   echo "Skipping ignore-listed board ${board}"
+   exit 0
+  fi
+
+  # Mount the image.
+  local loopdev rootfs
+  if [[ -d "${image}" ]]; then
+    rootfs="${image}"
+  else
+    rootfs="$(make_temp_dir)"
+    loopdev="$(loopback_partscan "${image}")"
+    mount_loop_image_partition_ro "${loopdev}" 3 "${rootfs}"
+  fi
+
+  local firmware_bundle shellball_dir
+  firmware_bundle="${rootfs}/usr/sbin/chromeos-firmwareupdate"
+  shellball_dir="$(make_temp_dir)"
+
+  # Get the board specific bit masks.
+  local required_bit_mask forbidden_bit_mask
+
+  if [[ ! -v "REQUIRED_BIT_MASKS[${board}]" ]]; then
+    die "Missing PSP required bit mask set for ${board}"
+  fi
+
+  if [[ ! -v "FORBIDDEN_BIT_MASKS[${board}]" ]]; then
+    die "Missing PSP forbidden bit mask set for ${board}"
+  fi
+
+  required_bit_mask="${REQUIRED_BIT_MASKS[${board}]}"
+  forbidden_bit_mask="${FORBIDDEN_BIT_MASKS[${board}]}"
+
+  # Extract our firmware.
+  if ! extract_firmware_bundle "${firmware_bundle}" "${shellball_dir}"; then
+    die "Failed to extract firmware bundle"
+  fi
+
+  # Find our images and check the soft-fuse bits in each.
+  declare -a images
+  readarray -t images < <(find "${shellball_dir}" -iname 'bios-*')
+
+  local image
+  for image in "${images[@]}"; do
+    local soft_fuse soft_fuse_output forbidden_set missing_set
+    if ! soft_fuse_output="$(amdfwread --soft-fuse "${image}")"; then
+      die "'amdfwread --soft-fuse ${image}' failed"
+    fi
+
+    # Output format from amdfwread is Soft-fuse:value, where value is in hex.
+    soft_fuse="$(echo "${soft_fuse_output}" | \
+      sed -E -n 's/Soft-fuse:(0[xX][0-9a-fA-F]+)/\1/p')"
+    if [[ -z "${soft_fuse}" ]]; then
+      die "Could not parse Soft-fuse value from output: '${soft_fuse_output}'"
+    fi
+
+    forbidden_set="$((soft_fuse & forbidden_bit_mask))"
+    if [[ "${forbidden_set}" != 0 ]]; then
+      local forbidden_hex
+      forbidden_hex="$(printf %#x "${forbidden_set}")"
+      die "${image}: Forbidden AMD PSP soft-fuse bits set: ${forbidden_hex}"
+    fi
+
+    missing_set="$((~soft_fuse & required_bit_mask))"
+    if [[ "${missing_set}" != 0 ]]; then
+      local missing_hex
+      missing_hex="$(printf %#x "${missing_set}")"
+      die "${image}: Required AMD PSP soft-fuse bits not set: ${missing_hex}"
+    fi
+  done
+}
+main "$@"
diff --git a/scripts/image_signing/gbb_flags_common.sh b/scripts/image_signing/gbb_flags_common.sh
index ed51f15..ae50c10 100755
--- a/scripts/image_signing/gbb_flags_common.sh
+++ b/scripts/image_signing/gbb_flags_common.sh
@@ -62,10 +62,23 @@
   flashrom -p "${programmer}"  -i GBB --noverify-all -w "${file}"
 }
 
-get_programmer_for_servo() {
+CPU_FW_SPI="${FLAGS_FALSE}"
+
+enable_cpu_fw_spi() {
+  dut-control cpu_fw_spi:on >/dev/null
+  CPU_FW_SPI="${FLAGS_TRUE}"
+}
+
+maybe_disable_cpu_fw_spi() {
+  if [ "${CPU_FW_SPI}" = "${FLAGS_TRUE}" ]; then
+    dut-control cpu_fw_spi:off >/dev/null
+  fi
+}
+trap "maybe_disable_cpu_fw_spi" EXIT
+
+update_programmer_for_servo() {
   local servo_type
   local serial
-  local programmer
   servo_type=$(dut-control -o servo_type 2>/dev/null) || \
     die "Failed to get servo information. Is servod running?"
   case "${servo_type}" in
@@ -84,10 +97,8 @@
   esac
   case "${servo_type}" in
     *servo_micro*|*c2d2*)
-      # TODO(sammc): Support servo micro, servo v2 and C2D2. This requires
-      # toggling cpu_fw_spi via dut-control before and after running flashrom.
-      # C2D2 additionally requires a working cpu_fw_spi implementation.
-      die "Unsupported servo type ${servo_type}"
+      programmer="raiden_debug_spi:serial=${serial}"
+      enable_cpu_fw_spi
       ;;
     *ccd_cr50*|*ccd_gsc*)
       programmer="raiden_debug_spi:target=AP,serial=${serial}"
@@ -96,5 +107,4 @@
       die "Unsupported servo type ${servo_type}"
       ;;
   esac
-  echo "${programmer}"
 }
diff --git a/scripts/image_signing/get_gbb_flags.sh b/scripts/image_signing/get_gbb_flags.sh
index 1191e9f..84acaa0 100755
--- a/scripts/image_signing/get_gbb_flags.sh
+++ b/scripts/image_signing/get_gbb_flags.sh
@@ -1,4 +1,4 @@
-#!/bin/sh
+#!/bin/bash
 #
 # Copyright 2017 The Chromium OS Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
@@ -30,7 +30,7 @@
   if [ -z "${FLAGS_file}" ]; then
     image_file="$(make_temp_file)"
     if [ "${FLAGS_servo}" = "${FLAGS_TRUE}" ]; then
-      programmer=$(get_programmer_for_servo)
+      update_programmer_for_servo
     fi
     flashrom_read "${image_file}" "${programmer}"
   fi
diff --git a/scripts/image_signing/make_dev_firmware.sh b/scripts/image_signing/make_dev_firmware.sh
index 0db5638..20c8414 100755
--- a/scripts/image_signing/make_dev_firmware.sh
+++ b/scripts/image_signing/make_dev_firmware.sh
@@ -167,8 +167,6 @@
   local recovery_pubkey="${FLAGS_keys}/recovery_key.vbpubk"
   local firmware_keyblock="${FLAGS_keys}/firmware.keyblock"
   local firmware_prvkey="${FLAGS_keys}/firmware_data_key.vbprivk"
-  local dev_firmware_keyblock="${FLAGS_keys}/dev_firmware.keyblock"
-  local dev_firmware_prvkey="${FLAGS_keys}/dev_firmware_data_key.vbprivk"
   local kernel_sub_pubkey="${FLAGS_keys}/kernel_subkey.vbpubk"
   local ec_efs_pubkey="${FLAGS_keys}/key_ec_efs.vbpubk2"
   local ec_efs_prvkey="${FLAGS_keys}/key_ec_efs.vbprik2"
@@ -281,8 +279,6 @@
     echo "Using keyblocks (developer, normal)..."
   else
     echo "Using keyblocks (normal, normal)..."
-    dev_firmware_prvkey="$firmware_prvkey"
-    dev_firmware_keyblock="$firmware_keyblock"
   fi
 
   debug_msg "Extract firmware version and data key version"
@@ -351,8 +347,6 @@
     "${IMAGE_BIOS}" \
     "${firmware_prvkey}" \
     "${firmware_keyblock}" \
-    "${dev_firmware_prvkey}" \
-    "${dev_firmware_keyblock}" \
     "${kernel_sub_pubkey}" \
     "${firmware_version}" \
     ${optional_opts} ||
diff --git a/scripts/image_signing/resign_firmwarefd.sh b/scripts/image_signing/resign_firmwarefd.sh
index d4cb5b8..ea23315 100755
--- a/scripts/image_signing/resign_firmwarefd.sh
+++ b/scripts/image_signing/resign_firmwarefd.sh
@@ -20,20 +20,12 @@
 DST_FD=$2
 FIRMWARE_DATAKEY=$3
 FIRMWARE_KEYBLOCK=$4
-DEV_FIRMWARE_DATAKEY=$5
-DEV_FIRMWARE_KEYBLOCK=$6
-KERNEL_SUBKEY=$7
+KERNEL_SUBKEY=$5
 # optional
-VERSION=$8
-PREAMBLE_FLAG=$9
-LOEM_OUTPUT_DIR=${10}
-LOEMID=${11}
-
-if [ ! -e $DEV_FIRMWARE_KEYBLOCK ] || [ ! -e $DEV_FIRMWARE_DATAKEY ] ; then
-  echo "No dev firmware keyblock/datakey found. Reusing normal keys."
-  DEV_FIRMWARE_KEYBLOCK="$FIRMWARE_KEYBLOCK"
-  DEV_FIRMWARE_DATAKEY="$FIRMWARE_DATAKEY"
-fi
+VERSION=$6
+PREAMBLE_FLAG=$7
+LOEM_OUTPUT_DIR=$8
+LOEMID=$9
 
 # pass optional args
 [ -n "$VERSION" ] && VERSION="--version $VERSION"
@@ -44,8 +36,6 @@
 exec ${FUTILITY} sign \
   --signprivate $FIRMWARE_DATAKEY \
   --keyblock $FIRMWARE_KEYBLOCK \
-  --devsign $DEV_FIRMWARE_DATAKEY \
-  --devkeyblock $DEV_FIRMWARE_KEYBLOCK \
   --kernelkey $KERNEL_SUBKEY \
   $VERSION \
   $PREAMBLE_FLAG \
diff --git a/scripts/image_signing/set_gbb_flags.sh b/scripts/image_signing/set_gbb_flags.sh
index 3057da5..57fd4c0 100755
--- a/scripts/image_signing/set_gbb_flags.sh
+++ b/scripts/image_signing/set_gbb_flags.sh
@@ -55,7 +55,7 @@
   if [ -z "${FLAGS_file}" ]; then
     image_file="$(make_temp_file)"
     if [ "${FLAGS_servo}" = "${FLAGS_TRUE}" ]; then
-      programmer=$(get_programmer_for_servo)
+      update_programmer_for_servo
     fi
 
     flashrom_read "${image_file}" "${programmer}"
diff --git a/scripts/image_signing/sign_android_image.sh b/scripts/image_signing/sign_android_image.sh
index 5af1aa6..6b65545 100755
--- a/scripts/image_signing/sign_android_image.sh
+++ b/scripts/image_signing/sign_android_image.sh
@@ -250,6 +250,48 @@
   sudo find "${dir}" -exec stat -c '%n:%u:%g:%a' {} + | sort
 }
 
+# Snapshot capabilities in a directory recursively.
+snapshot_capabilities() {
+  local dir=$1
+  sudo find "${dir}" -exec getcap {} + | sort
+}
+
+# Apply capabilities to files in |dir| as specified by |capabilities_list|.
+# See b/179170462.
+apply_capabilities() {
+  local dir=$1
+  local capabilities_list=$2
+  local entry
+
+  while read -ra entry; do
+    if [[ ${#entry[@]} -lt 2 ]]; then
+      error "Unexpected output in capabilities_list of '${entry[*]}'"
+      return 1
+    fi
+    # Output of getcap is either |{file} {capabilities}| or
+    # |{file} = {capabilities}|, so take the first and last element of each
+    # line.
+    info "Setting capabilities ${entry[${#entry[@]}-1]} for ${entry[0]}"
+    sudo setcap "${entry[${#entry[@]}-1]}" "${entry[0]}"
+  done < "${capabilities_list}"
+
+  return 0
+}
+
+# Integrity check that capabilities are unchanged.
+capabilities_integrity_check() {
+  local system_mnt=$1
+  local working_dir=$2
+  snapshot_capabilities "${system_mnt}" > "${working_dir}/capabilities.new"
+  local d
+  if ! d=$(diff "${working_dir}"/capabilities.{orig,new}); then
+    error "Unexpected change of capabilities, diff \n${d}"
+    return 1
+  fi
+
+  return 0
+}
+
 # Integrity check that image content is unchanged.
 image_content_integrity_check() {
   local system_mnt=$1
@@ -265,6 +307,12 @@
   return 0
 }
 
+list_image_files() {
+  local unsquashfs=$1
+  local system_img=$2
+  "${unsquashfs}" -l "${system_img}" | grep ^squashfs-root
+}
+
 sign_android_internal() {
   local root_fs_dir=$1
   local key_dir=$2
@@ -330,8 +378,17 @@
 
   local working_dir=$(make_temp_dir)
   local system_mnt="${working_dir}/mnt"
+  local system_capabilities_orig="${working_dir}/capabilities.orig"
 
-  info "Unpacking squashfs system image to ${system_mnt}"
+  # Extract with xattrs so we can read and audit capabilities. See b/179170462.
+  info "Unpacking squashfs system image with xattrs to ${system_mnt}"
+  sudo "${unsquashfs}" -x -f -no-progress -d "${system_mnt}" "${system_img}"
+  snapshot_capabilities "${system_mnt}" > "${system_capabilities_orig}"
+  sudo rm -rf "${system_mnt}"
+
+  info "Unpacking squashfs system image without xattrs to ${system_mnt}"
+  list_image_files "${unsquashfs}" "${system_img}" > \
+      "${working_dir}/image_file_list.orig"
   sudo "${unsquashfs}" -no-xattrs -f -no-progress -d "${system_mnt}" "${system_img}"
 
   snapshot_file_properties "${system_mnt}" > "${working_dir}/properties.orig"
@@ -399,6 +456,14 @@
     info "Packages cache ${packages_cache} does not exist. Skip regeneration."
   fi
 
+  # Apply original capabilities to system image and verify correctness.
+  if ! apply_capabilities "${system_mnt}" "${system_capabilities_orig}"; then
+    return 1
+  fi
+  if ! capabilities_integrity_check "${system_mnt}" "${working_dir}"; then
+    return 1
+  fi
+
   info "Repacking squashfs image with compression flags '${compression_flags}'"
   local old_size=$(stat -c '%s' "${system_img}")
   # Remove old system image to prevent mksquashfs tries to merge both images.
@@ -408,6 +473,17 @@
     -no-progress
   local new_size=$(stat -c '%s' "${system_img}")
   info "Android system image size change: ${old_size} -> ${new_size}"
+
+  list_image_files "${unsquashfs}" "${system_img}" > \
+      "${working_dir}/image_file_list.new"
+  if d=$(grep -v -F -x -f "${working_dir}"/image_file_list.{new,orig}); then
+    # If we have a line in image_file_list.orig which does not appear in
+    # image_file_list.new, it means some files are removed during signing
+    # process. Here we have already deleted the original Android image so
+    # cannot retry.
+    die "Unexpected change of file list\n${d}"
+  fi
+
   return 0
 }
 
diff --git a/scripts/image_signing/sign_firmware.sh b/scripts/image_signing/sign_firmware.sh
index 0e7ac7c..ebc6cdc 100755
--- a/scripts/image_signing/sign_firmware.sh
+++ b/scripts/image_signing/sign_firmware.sh
@@ -57,8 +57,6 @@
     "${temp_fw}" \
     "${key_dir}/firmware_data_key${loem_key}.vbprivk" \
     "${key_dir}/firmware${loem_key}.keyblock" \
-    "${key_dir}/dev_firmware_data_key${loem_key}.vbprivk" \
-    "${key_dir}/dev_firmware${loem_key}.keyblock" \
     "${key_dir}/kernel_subkey.vbpubk" \
     "${firmware_version}" \
     "" \
diff --git a/scripts/image_signing/sign_gsc_firmware.sh b/scripts/image_signing/sign_gsc_firmware.sh
index af7b7da..a5945ce 100755
--- a/scripts/image_signing/sign_gsc_firmware.sh
+++ b/scripts/image_signing/sign_gsc_firmware.sh
@@ -148,13 +148,11 @@
 # needs to be verified and in certain cases altered.
 #
 # The function verifies that the input manifest is a proper json file, and
-# that the manifest conforms to GSC version numbering and board ID flags
-# conventions for various build images:
-#
-# - only binaries where version is set to CR50_NODE_LOCKED_VERSION can be
-#   converted to node locked images. Board IDs for node locked images come
-#   from signing instructions, and the config1 manifest field value must have
-#   the 0x80000000 bit set.
+# that the manifest conforms to GSC board ID flags conventions for various
+# build images:
+
+# - board IDs for node locked images come from signing instructions, and the
+#   config1 manifest field value must have the 0x80000000 bit set.
 #
 # - when signing pre-pvt binaries (major version number is even) the 0x10
 #   flags bit must be set.
@@ -198,11 +196,6 @@
       if [[ -z ${INSN_DEVICE_ID:-} ]]; then
         die "Node locked target without Device ID value"
       fi
-      # Case of a node locked image, it must have the fixed version.
-      if [[ "${epoch}.${major}.${minor}" != "${CR50_NODE_LOCKED_VERSION}" ]]
-      then
-        die "Won't create node locked images for version $epoch.$major.$minor"
-      fi
 
       local sub
       local devid0
diff --git a/scripts/image_signing/sign_official_build.sh b/scripts/image_signing/sign_official_build.sh
index 2f2621d..22cfa8c 100755
--- a/scripts/image_signing/sign_official_build.sh
+++ b/scripts/image_signing/sign_official_build.sh
@@ -330,25 +330,6 @@
   disable_rw_mount "${rootfs}"
 }
 
-# Extracts a firmware updater bundle (for firmware image binaries) file
-# (generated by src/platform/firmware/pack_firmware.sh).
-# Args: INPUT_FILE OUTPUT_DIR
-extract_firmware_bundle() {
-  local input="$(readlink -f "$1")"
-  local output_dir="$2"
-  if [ ! -s "${input}" ]; then
-    return 1
-  elif grep -q '^##CUTHERE##' "${input}"; then
-    # Bundle supports self-extraction.
-    "$input" --sb_extract "${output_dir}" ||
-      die "Extracting firmware autoupdate (--sb_extract) failed."
-  else
-    # Legacy bundle - try uudecode.
-    uudecode -o - ${input} | tar -C ${output_dir} -zxf - 2>/dev/null ||
-      die "Extracting firmware autoupdate failed."
-  fi
-}
-
 # Repacks firmware updater bundle content from given folder.
 # Args: INPUT_DIR TARGET_SCRIPT
 repack_firmware_bundle() {
@@ -515,14 +496,6 @@
 
         local signprivate="${KEY_DIR}/firmware_data_key${key_suffix}.vbprivk"
         local keyblock="${KEY_DIR}/firmware${key_suffix}.keyblock"
-        local devsign="${KEY_DIR}/dev_firmware_data_key${key_suffix}.vbprivk"
-        local devkeyblock="${KEY_DIR}/dev_firmware${key_suffix}.keyblock"
-
-        if [ ! -e "${devsign}" ] || [ ! -e "${devkeyblock}" ] ; then
-          echo "No dev firmware keyblock/datakey found. Reusing normal keys."
-          devsign="${signprivate}"
-          devkeyblock="${keyblock}"
-        fi
 
         # Path to bios.bin.
         local bios_path="${shellball_dir}/${bios_image}"
@@ -566,8 +539,6 @@
         echo "Signing Bios with:" ${FUTILITY} sign \
           --signprivate "${signprivate}" \
           --keyblock "${keyblock}" \
-          --devsign "${devsign}" \
-          --devkeyblock "${devkeyblock}" \
           --kernelkey "${KEY_DIR}/kernel_subkey.vbpubk" \
           --version "${FIRMWARE_VERSION}" \
           "${extra_args[@]}" \
@@ -576,8 +547,6 @@
         ${FUTILITY} sign \
           --signprivate "${signprivate}" \
           --keyblock "${keyblock}" \
-          --devsign "${devsign}" \
-          --devkeyblock "${devkeyblock}" \
           --kernelkey "${KEY_DIR}/kernel_subkey.vbpubk" \
           --version "${FIRMWARE_VERSION}" \
           "${extra_args[@]}" \
diff --git a/scripts/keygeneration/accessory/create_new_gsc_key.sh b/scripts/keygeneration/accessory/create_new_gsc_key.sh
new file mode 100755
index 0000000..a7e81c1
--- /dev/null
+++ b/scripts/keygeneration/accessory/create_new_gsc_key.sh
@@ -0,0 +1,77 @@
+#!/bin/bash
+
+# Copyright 2022 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.
+
+# Load common constants and functions.
+. "$(dirname "$0")/../common.sh"
+
+usage() {
+  cat <<EOF
+Usage: ${PROG} [options] <key_file_base_name>
+
+Options:
+  -o, --output_dir <dir>:    Where to write the keys (default is cwd)
+EOF
+
+  if [[ $# -ne 0 ]]; then
+    die "$*"
+  else
+    exit 0
+  fi
+}
+
+generate_rsa3070_key() {
+  local base_name="$1"
+  local len="3070"
+
+  echo "creating ${base_name} key pair..."
+
+  # Make the RSA key pair.
+  openssl genrsa -F4 -out "${base_name}.pem" "${len}"
+  openssl rsa -in "${base_name}.pem" -outform PEM \
+    -pubout -out "${base_name}.pem.pub"
+}
+
+main() {
+  set -euo pipefail
+
+  local base_name
+  local output_dir="${PWD}"
+
+  base_name=""
+  while [[ $# -gt 0 ]]; do
+    case "$1" in
+    -h|--help)
+      usage
+      ;;
+    -o|--output_dir)
+      output_dir="$2"
+      if [[ ! -d "${output_dir}" ]]; then
+        die "output dir (${output_dir}) doesn't exist."
+      fi
+      shift
+      ;;
+    -*)
+      usage "Unknown option: $1"
+      ;;
+    *)
+      if [[ -z ${base_name} ]]; then
+        base_name="$1"
+      else
+        usage "Unknown argument $1"
+      fi
+      ;;
+    esac
+    shift
+  done
+
+  if [[ -z ${base_name} ]]; then
+    usage "Key file base name missing"
+  fi
+
+  generate_rsa3070_key "${output_dir}/${base_name}"
+}
+
+main "$@"
diff --git a/scripts/keygeneration/create_new_keys.sh b/scripts/keygeneration/create_new_keys.sh
index 2e1fd22..4a2ad33 100755
--- a/scripts/keygeneration/create_new_keys.sh
+++ b/scripts/keygeneration/create_new_keys.sh
@@ -14,7 +14,6 @@
 Usage: ${PROG} [options]
 
 Options:
-  --devkeyblock          Also generate developer firmware keyblock and data key
   --android              Also generate android keys
   --uefi                 Also generate UEFI keys
   --8k                   Use 8k keys instead of 4k (enables options below)
@@ -36,8 +35,6 @@
 main() {
   set -e
 
-  # Flag to indicate whether we should be generating a developer keyblock flag.
-  local dev_keyblock="false"
   local android_keys="false"
   local uefi_keys="false"
   local root_key_algoid=${ROOT_KEY_ALGOID}
@@ -50,11 +47,6 @@
 
   while [[ $# -gt 0 ]]; do
     case $1 in
-    --devkeyblock)
-      echo "Will also generate developer firmware keyblock and data key."
-      dev_keyblock="true"
-      ;;
-
     --android)
       echo "Will also generate Android keys."
       android_keys="true"
@@ -158,9 +150,6 @@
   make_pair ec_data_key              ${EC_DATAKEY_ALGOID} ${eckey_version}
   make_pair root_key                 ${root_key_algoid}
   make_pair firmware_data_key        ${FIRMWARE_DATAKEY_ALGOID} ${fkey_version}
-  if [[ "${dev_keyblock}" == "true" ]]; then
-    make_pair dev_firmware_data_key    ${DEV_FIRMWARE_DATAKEY_ALGOID} ${fkey_version}
-  fi
   make_pair kernel_subkey            ${KERNEL_SUBKEY_ALGOID} ${ksubkey_version}
   make_pair kernel_data_key          ${KERNEL_DATAKEY_ALGOID} ${kdatakey_version}
 
@@ -178,11 +167,6 @@
   # Ditto EC keyblock
   make_keyblock ec ${EC_KEYBLOCK_MODE} ec_data_key ec_root_key
 
-  if [[ "${dev_keyblock}" == "true" ]]; then
-    # Create the dev firmware keyblock for use only in Developer mode.
-    make_keyblock dev_firmware ${DEV_FIRMWARE_KEYBLOCK_MODE} dev_firmware_data_key root_key
-  fi
-
   # Create the recovery kernel keyblock for use only in Recovery mode.
   make_keyblock recovery_kernel ${RECOVERY_KERNEL_KEYBLOCK_MODE} recovery_kernel_data_key recovery_key
 
diff --git a/tests/cgpt_fuzzer.c b/tests/cgpt_fuzzer.c
index d440ee8..6762bc3 100644
--- a/tests/cgpt_fuzzer.c
+++ b/tests/cgpt_fuzzer.c
@@ -25,7 +25,7 @@
 
 static struct MockDisk mock_disk;
 
-vb2_error_t VbExDiskRead(VbExDiskHandle_t h, uint64_t lba_start,
+vb2_error_t VbExDiskRead(vb2ex_disk_handle_t h, uint64_t lba_start,
 			 uint64_t lba_count, void *buffer)
 {
 	size_t lba_size = mock_disk.size >> mock_disk.sector_shift;
diff --git a/tests/cgptlib_test.c b/tests/cgptlib_test.c
index 6fd7c32..f614977 100644
--- a/tests/cgptlib_test.c
+++ b/tests/cgptlib_test.c
@@ -9,10 +9,10 @@
 #include "../cgpt/cgpt.h"
 #include "cgptlib_internal.h"
 #include "cgptlib_test.h"
+#include "common/tests.h"
 #include "crc32.h"
 #include "crc32_test.h"
 #include "gpt.h"
-#include "test_common.h"
 
 /*
  * Testing partition layout (sector_bytes=512)
diff --git a/tests/chromeos_config_tests.c b/tests/chromeos_config_tests.c
index 2408d8c..6b24a33 100644
--- a/tests/chromeos_config_tests.c
+++ b/tests/chromeos_config_tests.c
@@ -11,8 +11,8 @@
 #include "2common.h"
 #include "2return_codes.h"
 #include "chromeos_config.h"
+#include "common/tests.h"
 #include "host_misc.h"
-#include "test_common.h"
 
 static struct {
 	const char *path;
diff --git a/tests/common.sh b/tests/common.sh
index e69692b..a207922 100644
--- a/tests/common.sh
+++ b/tests/common.sh
@@ -5,7 +5,7 @@
 # found in the LICENSE file.
 
 # Determine script directory.
-SCRIPT_DIR="$(dirname $(realpath "${BASH_SOURCE[0]}"))"
+SCRIPT_DIR="$(dirname "$(realpath "${BASH_SOURCE[0]}")")"
 ROOT_DIR="$(dirname "${SCRIPT_DIR}")"
 SRCDIR="${SRCDIR:-${ROOT_DIR}}"
 BUILD="${BUILD:-${ROOT_DIR}/build}"
@@ -19,8 +19,8 @@
 TESTCASE_DIR="${SCRIPT_DIR}/testcases"
 TESTKEY_SCRATCH_DIR="${TEST_DIR}/testkeys"
 
-if [ ! -d ${TESTKEY_SCRATCH_DIR} ]; then
-  mkdir -p ${TESTKEY_SCRATCH_DIR}
+if [ ! -d "${TESTKEY_SCRATCH_DIR}" ]; then
+  mkdir -p "${TESTKEY_SCRATCH_DIR}"
 fi
 
 # Color output encodings.
@@ -53,7 +53,8 @@
     *) lev=0
       ;;
   esac
-  local x=$(caller $lev)
+  local x
+  x=$(caller $lev)
   local cline=${x%% *}
   local cfunc=${x#* }
   cfunc=${cfunc##*/}
@@ -65,7 +66,6 @@
 }
 
 function check_test_keys {
-  [ -d ${TESTKEY_DIR} ] || \
+  [ -d "${TESTKEY_DIR}" ] || \
     error 1 "You must run gen_test_keys.sh to generate test keys first."
 }
-
diff --git a/tests/common/boot_mode.c b/tests/common/boot_mode.c
new file mode 100644
index 0000000..342b790
--- /dev/null
+++ b/tests/common/boot_mode.c
@@ -0,0 +1,48 @@
+/* Copyright 2022 The ChromiumOS Authors.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Some helper function related to boot mode.
+ */
+
+#include "2api.h"
+#include "2misc.h"
+#include "2nvstorage.h"
+#include "common/boot_mode.h"
+#include "common/tests.h"
+
+void _set_boot_mode(struct vb2_context *ctx, enum vb2_boot_mode boot_mode,
+		    uint32_t recovery_reason, ...)
+{
+	struct vb2_shared_data *sd = vb2_get_sd(ctx);
+
+	switch (boot_mode) {
+	case VB2_BOOT_MODE_MANUAL_RECOVERY:
+		TEST_NEQ(recovery_reason, 0,
+			 "recovery_reason should be set in recovery mode");
+		ctx->flags |= VB2_CONTEXT_RECOVERY_MODE;
+		sd->recovery_reason = recovery_reason;
+		ctx->flags |= VB2_CONTEXT_FORCE_RECOVERY_MODE;
+		ctx->flags |= VB2_CONTEXT_EC_TRUSTED;
+		break;
+	case VB2_BOOT_MODE_BROKEN_SCREEN:
+		TEST_NEQ(recovery_reason, 0,
+			 "recovery_reason should be set in recovery mode");
+		ctx->flags |= VB2_CONTEXT_RECOVERY_MODE;
+		sd->recovery_reason = recovery_reason;
+		break;
+	case VB2_BOOT_MODE_DIAGNOSTICS:
+		vb2_nv_set(ctx, VB2_NV_DIAG_REQUEST, 1);
+		break;
+	case VB2_BOOT_MODE_DEVELOPER:
+		ctx->flags |= VB2_CONTEXT_DEVELOPER_MODE;
+		break;
+	case VB2_BOOT_MODE_NORMAL:
+		break;
+	default:
+		TEST_TRUE(0, "SET_BOOT_MODE: Undefined boot mode");
+		return;
+	}
+	vb2_set_boot_mode(ctx);
+	TEST_EQ(ctx->boot_mode, boot_mode, "Validity check for set boot mode");
+}
diff --git a/tests/common/boot_mode.h b/tests/common/boot_mode.h
new file mode 100644
index 0000000..bd6a3fd
--- /dev/null
+++ b/tests/common/boot_mode.h
@@ -0,0 +1,20 @@
+/* Copyright 2022 The ChromiumOS Authors.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "2api.h"
+
+void _set_boot_mode(struct vb2_context *ctx, enum vb2_boot_mode boot_mode,
+		    uint32_t recovery_reason, ...);
+
+/*
+ * Set the boot mode to the expected boot mode with the recovery reason if
+ * given. Also, set the corresponding ctx flag.
+ *
+ * @param ctx			Vboot context.
+ * @param boot_mode		Boot mode to be set.
+ * @param recovery_reason	Recovery reason set to sd->recovery_reason.
+ */
+#define SET_BOOT_MODE(ctx, boot_mode, ...) \
+	_set_boot_mode(ctx, boot_mode, ##__VA_ARGS__, 0)
diff --git a/tests/test_common.c b/tests/common/tests.c
similarity index 98%
rename from tests/test_common.c
rename to tests/common/tests.c
index 2ce3a5d..7df1613 100644
--- a/tests/test_common.c
+++ b/tests/common/tests.c
@@ -10,7 +10,7 @@
 #include <string.h>
 
 #include "2common.h"
-#include "test_common.h"
+#include "common/tests.h"
 
 /* Global test success flag. */
 int gTestSuccess = 1;
diff --git a/tests/test_common.h b/tests/common/tests.h
similarity index 96%
rename from tests/test_common.h
rename to tests/common/tests.h
index c3da880..656e2d9 100644
--- a/tests/test_common.h
+++ b/tests/common/tests.h
@@ -3,8 +3,8 @@
  * found in the LICENSE file.
  */
 
-#ifndef VBOOT_REFERENCE_TEST_COMMON_H_
-#define VBOOT_REFERENCE_TEST_COMMON_H_
+#ifndef VBOOT_REFERENCE_COMMON_TESTS_H_
+#define VBOOT_REFERENCE_COMMON_TESTS_H_
 
 #include <setjmp.h>
 #include <stdio.h>
@@ -164,4 +164,4 @@
 			abort(); \
 	} while (0)
 
-#endif  /* VBOOT_REFERENCE_TEST_COMMON_H_ */
+#endif  /* VBOOT_REFERENCE_COMMON_TESTS_H_ */
diff --git a/tests/timer_utils.c b/tests/common/timer_utils.c
similarity index 100%
rename from tests/timer_utils.c
rename to tests/common/timer_utils.c
diff --git a/tests/timer_utils.h b/tests/common/timer_utils.h
similarity index 79%
rename from tests/timer_utils.h
rename to tests/common/timer_utils.h
index 8cafdf9..b2a424a 100644
--- a/tests/timer_utils.h
+++ b/tests/common/timer_utils.h
@@ -3,8 +3,8 @@
  * found in the LICENSE file.
  */
 
-#ifndef VBOOT_REFERENCE_TIMER_UTILS_H_
-#define VBOOT_REFERENCE_TIMER_UTILS_H_
+#ifndef VBOOT_REFERENCE_COMMON_TIMER_UTILS_H_
+#define VBOOT_REFERENCE_COMMON_TIMER_UTILS_H_
 
 #include <inttypes.h>
 #include <time.h>
@@ -23,4 +23,4 @@
 /* Get duration in milliseconds. */
 uint32_t GetDurationMsecs(ClockTimerState* ct);
 
-#endif  /* VBOOT_REFERENCE_TIMER_UTILS_H_ */
+#endif  /* VBOOT_REFERENCE_COMMON_TIMER_UTILS_H_ */
diff --git a/tests/crc32_test.c b/tests/crc32_test.c
index 1d1d8cd..ee60cc0 100644
--- a/tests/crc32_test.c
+++ b/tests/crc32_test.c
@@ -4,9 +4,9 @@
  */
 
 #include "cgptlib_test.h"
+#include "common/tests.h"
 #include "crc32.h"
 #include "crc32_test.h"
-#include "test_common.h"
 
 #define MAX_VECTOR_LEN 256
 
diff --git a/tests/devkeys/arv_platform.keyblock b/tests/devkeys/arv_platform.keyblock
new file mode 100644
index 0000000..f3c976c
--- /dev/null
+++ b/tests/devkeys/arv_platform.keyblock
Binary files differ
diff --git a/tests/external_rsa_signer.sh b/tests/external_rsa_signer.sh
index 0724b87..405df42 100755
--- a/tests/external_rsa_signer.sh
+++ b/tests/external_rsa_signer.sh
@@ -1,9 +1,13 @@
 #!/bin/bash
 
+# Copyright (c) 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.
+
 if [ $# -ne 1 ]; then
   echo "Usage: $0 <private_key_pem_file>"
   echo "Reads data to sign from stdin, encrypted data is output to stdout"
   exit 1
 fi
 
-openssl rsautl -sign -inkey $1
+openssl rsautl -sign -inkey "$1"
diff --git a/tests/futility/data/README b/tests/futility/data/README
index 6723f3d..3d577b8 100644
--- a/tests/futility/data/README
+++ b/tests/futility/data/README
@@ -1,6 +1,7 @@
 These are officially signed BIOS images from existing Chromebooks.
 
-  bios_mario_mp.bin   uses old names for the FMAP areas
-  bios_zgb_mp.bin     RW firmware A and B are different
   bios_link_mp.bin    uses the RO_NORMAL flag to skip RW firmware validation
   bios_peppy_mp.bin   doesn't do any of those things
+
+This is dev-signed BIOS image with CBFS support:
+  bios_voxel_dev.bin
diff --git a/tests/futility/data/bios_mario_mp.bin b/tests/futility/data/bios_mario_mp.bin
deleted file mode 100644
index 8a6e8c5..0000000
--- a/tests/futility/data/bios_mario_mp.bin
+++ /dev/null
Binary files differ
diff --git a/tests/futility/data/bios_peppy_dev.bad_keyblock_data_key_offset_too_big.xxd.patch b/tests/futility/data/bios_peppy_dev.bad_keyblock_data_key_offset_too_big.xxd.patch
new file mode 100644
index 0000000..cb79555
--- /dev/null
+++ b/tests/futility/data/bios_peppy_dev.bad_keyblock_data_key_offset_too_big.xxd.patch
@@ -0,0 +1 @@
+00200050: b808 0000 0000 0000 0804 0000 0000 0000  ................
diff --git a/tests/futility/data/bios_peppy_dev.bad_keyblock_data_key_size_big.xxd.patch b/tests/futility/data/bios_peppy_dev.bad_keyblock_data_key_size_big.xxd.patch
new file mode 100644
index 0000000..8667c3f
--- /dev/null
+++ b/tests/futility/data/bios_peppy_dev.bad_keyblock_data_key_size_big.xxd.patch
@@ -0,0 +1 @@
+00200050: 2000 0000 0000 0000 3412 0100 0000 0000   .......4.......
diff --git a/tests/futility/data/bios_peppy_dev.bad_keyblock_fmap_too_small.xxd.patch b/tests/futility/data/bios_peppy_dev.bad_keyblock_fmap_too_small.xxd.patch
new file mode 100644
index 0000000..2c01f2a
--- /dev/null
+++ b/tests/futility/data/bios_peppy_dev.bad_keyblock_fmap_too_small.xxd.patch
@@ -0,0 +1,2 @@
+00610100: 0000 0000 0000 0000 0100 0000 2000 6e00  ............ .n.
+00610110: 0000 5642 4c4f 434b 5f41 0000 0000 0000  ..VBLOCK_A......
diff --git a/tests/futility/data/bios_peppy_dev.bad_keyblock_fmap_too_small_for_whole.xxd.patch b/tests/futility/data/bios_peppy_dev.bad_keyblock_fmap_too_small_for_whole.xxd.patch
new file mode 100644
index 0000000..3d79703
--- /dev/null
+++ b/tests/futility/data/bios_peppy_dev.bad_keyblock_fmap_too_small_for_whole.xxd.patch
@@ -0,0 +1,2 @@
+00610100: 0000 0000 0000 0000 0100 0000 2000 b608  ............ ...
+00610110: 0000 5642 4c4f 434b 5f41 0000 0000 0000  ..VBLOCK_A......
diff --git a/tests/futility/data/bios_peppy_dev.bad_keyblock_hash_data_size_too_small.xxd.patch b/tests/futility/data/bios_peppy_dev.bad_keyblock_hash_data_size_too_small.xxd.patch
new file mode 100644
index 0000000..4f284d9
--- /dev/null
+++ b/tests/futility/data/bios_peppy_dev.bad_keyblock_hash_data_size_too_small.xxd.patch
@@ -0,0 +1 @@
+00200040: 6c00 0000 0000 0000 1700 0000 0000 0000  l...............
diff --git a/tests/futility/data/bios_peppy_dev.bad_keyblock_hash_invalid_contents.xxd.patch b/tests/futility/data/bios_peppy_dev.bad_keyblock_hash_invalid_contents.xxd.patch
new file mode 100644
index 0000000..8b5fefd
--- /dev/null
+++ b/tests/futility/data/bios_peppy_dev.bad_keyblock_hash_invalid_contents.xxd.patch
@@ -0,0 +1 @@
+00200470: babb 3f4b 95db d458 4142 4344 4142 4344  ..?K...XABCDABCD
diff --git a/tests/futility/data/bios_peppy_dev.bad_keyblock_hash_offset_too_big.xxd.patch b/tests/futility/data/bios_peppy_dev.bad_keyblock_hash_offset_too_big.xxd.patch
new file mode 100644
index 0000000..1a7044c
--- /dev/null
+++ b/tests/futility/data/bios_peppy_dev.bad_keyblock_hash_offset_too_big.xxd.patch
@@ -0,0 +1 @@
+00200030: 9808 0000 0000 0000 4000 0000 0000 0000  ........@.......
diff --git a/tests/futility/data/bios_peppy_dev.bad_keyblock_hash_size_too_big.xxd.patch b/tests/futility/data/bios_peppy_dev.bad_keyblock_hash_size_too_big.xxd.patch
new file mode 100644
index 0000000..e6d90bf
--- /dev/null
+++ b/tests/futility/data/bios_peppy_dev.bad_keyblock_hash_size_too_big.xxd.patch
@@ -0,0 +1 @@
+00200030: 4804 0000 0000 0000 b808 0000 0000 0000  H...............
diff --git a/tests/futility/data/bios_peppy_dev.bad_keyblock_invalid_magic.xxd.patch b/tests/futility/data/bios_peppy_dev.bad_keyblock_invalid_magic.xxd.patch
new file mode 100644
index 0000000..00015e2
--- /dev/null
+++ b/tests/futility/data/bios_peppy_dev.bad_keyblock_invalid_magic.xxd.patch
@@ -0,0 +1 @@
+00200000: 4142 4344 4143 4244 0200 0000 0100 0000  ABCDACBD........
diff --git a/tests/futility/data/bios_peppy_dev.bad_keyblock_invalid_major_version.xxd.patch b/tests/futility/data/bios_peppy_dev.bad_keyblock_invalid_major_version.xxd.patch
new file mode 100644
index 0000000..637b90a
--- /dev/null
+++ b/tests/futility/data/bios_peppy_dev.bad_keyblock_invalid_major_version.xxd.patch
@@ -0,0 +1 @@
+00200000: 4348 524f 4d45 4f53 0200 0000 0200 0000  CHROMEOS........
diff --git a/tests/futility/data/bios_peppy_dev.bad_keyblock_size_not_fully_signed.xxd.patch b/tests/futility/data/bios_peppy_dev.bad_keyblock_size_not_fully_signed.xxd.patch
new file mode 100644
index 0000000..1a24d43
--- /dev/null
+++ b/tests/futility/data/bios_peppy_dev.bad_keyblock_size_not_fully_signed.xxd.patch
@@ -0,0 +1 @@
+00200010: 7604 0000 0000 0000 a004 0000 0000 0000  v...............
diff --git a/tests/futility/data/bios_peppy_dev.bad_preamble_body_signature_offset_too_big.xxd.patch b/tests/futility/data/bios_peppy_dev.bad_preamble_body_signature_offset_too_big.xxd.patch
new file mode 100644
index 0000000..31408e5
--- /dev/null
+++ b/tests/futility/data/bios_peppy_dev.bad_preamble_body_signature_offset_too_big.xxd.patch
@@ -0,0 +1 @@
+00200900: 0100 0000 0000 0000 7408 0000 0000 0000  ........t.......
diff --git a/tests/futility/data/bios_peppy_dev.bad_preamble_body_signature_size_too_big.xxd.patch b/tests/futility/data/bios_peppy_dev.bad_preamble_body_signature_size_too_big.xxd.patch
new file mode 100644
index 0000000..083cfbe
--- /dev/null
+++ b/tests/futility/data/bios_peppy_dev.bad_preamble_body_signature_size_too_big.xxd.patch
@@ -0,0 +1 @@
+00200910: 7408 0000 0000 0000 183c 0200 0000 0000  t........<......
diff --git a/tests/futility/data/bios_peppy_dev.bad_preamble_fmap_too_small.xxd.patch b/tests/futility/data/bios_peppy_dev.bad_preamble_fmap_too_small.xxd.patch
new file mode 100644
index 0000000..0f0968d
--- /dev/null
+++ b/tests/futility/data/bios_peppy_dev.bad_preamble_fmap_too_small.xxd.patch
@@ -0,0 +1,2 @@
+00610100: 0000 0000 0000 0000 0100 0000 2000 ee08  ............ ...
+00610110: 0000 5642 4c4f 434b 5f41 0000 0000 0000  ..VBLOCK_A......
diff --git a/tests/futility/data/bios_peppy_dev.bad_preamble_fmap_too_small_for_whole.xxd.patch b/tests/futility/data/bios_peppy_dev.bad_preamble_fmap_too_small_for_whole.xxd.patch
new file mode 100644
index 0000000..bd75624
--- /dev/null
+++ b/tests/futility/data/bios_peppy_dev.bad_preamble_fmap_too_small_for_whole.xxd.patch
@@ -0,0 +1,2 @@
+00610100: 0000 0000 0000 0000 0100 0000 2000 2411  ............ .$.
+00610110: 0000 5642 4c4f 434b 5f41 0000 0000 0000  ..VBLOCK_A......
diff --git a/tests/futility/data/bios_peppy_dev.bad_preamble_header_version_major.xxd.patch b/tests/futility/data/bios_peppy_dev.bad_preamble_header_version_major.xxd.patch
new file mode 100644
index 0000000..456a753
--- /dev/null
+++ b/tests/futility/data/bios_peppy_dev.bad_preamble_header_version_major.xxd.patch
@@ -0,0 +1 @@
+002008d0: 7406 0000 0000 0000 0300 0000 0100 0000  t...............
diff --git a/tests/futility/data/bios_peppy_dev.bad_preamble_header_version_minor.xxd.patch b/tests/futility/data/bios_peppy_dev.bad_preamble_header_version_minor.xxd.patch
new file mode 100644
index 0000000..140b44e
--- /dev/null
+++ b/tests/futility/data/bios_peppy_dev.bad_preamble_header_version_minor.xxd.patch
@@ -0,0 +1 @@
+002008d0: 7406 0000 0000 0000 0200 0000 0000 0000  t...............
diff --git a/tests/futility/data/bios_peppy_dev.bad_preamble_kernel_subkey_offset_too_big.xxd.patch b/tests/futility/data/bios_peppy_dev.bad_preamble_kernel_subkey_offset_too_big.xxd.patch
new file mode 100644
index 0000000..3c5a14c
--- /dev/null
+++ b/tests/futility/data/bios_peppy_dev.bad_preamble_kernel_subkey_offset_too_big.xxd.patch
@@ -0,0 +1 @@
+002008f0: 7408 0000 0000 0000 0700 0000 0000 0000  t...............
diff --git a/tests/futility/data/bios_peppy_dev.bad_preamble_kernel_subkey_size_too_big.xxd.patch b/tests/futility/data/bios_peppy_dev.bad_preamble_kernel_subkey_size_too_big.xxd.patch
new file mode 100644
index 0000000..3c5a14c
--- /dev/null
+++ b/tests/futility/data/bios_peppy_dev.bad_preamble_kernel_subkey_size_too_big.xxd.patch
@@ -0,0 +1 @@
+002008f0: 7408 0000 0000 0000 0700 0000 0000 0000  t...............
diff --git a/tests/futility/data/bios_peppy_dev.bad_preamble_signature_data_size_too_big.xxd.patch b/tests/futility/data/bios_peppy_dev.bad_preamble_signature_data_size_too_big.xxd.patch
new file mode 100644
index 0000000..f23f2e1
--- /dev/null
+++ b/tests/futility/data/bios_peppy_dev.bad_preamble_signature_data_size_too_big.xxd.patch
@@ -0,0 +1 @@
+002008d0: 8408 0000 0000 0000 0200 0000 0100 0000  ................
diff --git a/tests/futility/data/bios_peppy_dev.bad_preamble_signature_data_size_too_small.xxd.patch b/tests/futility/data/bios_peppy_dev.bad_preamble_signature_data_size_too_small.xxd.patch
new file mode 100644
index 0000000..b02b497
--- /dev/null
+++ b/tests/futility/data/bios_peppy_dev.bad_preamble_signature_data_size_too_small.xxd.patch
@@ -0,0 +1 @@
+002008d0: 6400 0000 0000 0000 0200 0000 0100 0000  d...............
diff --git a/tests/futility/data/bios_peppy_dev.bad_preamble_signature_invalid_contents.xxd.patch b/tests/futility/data/bios_peppy_dev.bad_preamble_signature_invalid_contents.xxd.patch
new file mode 100644
index 0000000..0fed2fe
--- /dev/null
+++ b/tests/futility/data/bios_peppy_dev.bad_preamble_signature_invalid_contents.xxd.patch
@@ -0,0 +1,2 @@
+00200f20: 78c9 3a24 85ab ca17 498e c238 4142 4344  x.:$....I..8ABCD
+00200f30: 4142 4344 1cef bf68 b86b cdbc 3782 9f85  ABCD...h.k..7...
diff --git a/tests/futility/data/bios_peppy_dev.bad_preamble_signature_offset_too_big.xxd.patch b/tests/futility/data/bios_peppy_dev.bad_preamble_signature_offset_too_big.xxd.patch
new file mode 100644
index 0000000..5159fef
--- /dev/null
+++ b/tests/futility/data/bios_peppy_dev.bad_preamble_signature_offset_too_big.xxd.patch
@@ -0,0 +1 @@
+002008c0: 7408 0000 0000 0000 0002 0000 0000 0000  t...............
diff --git a/tests/futility/data/bios_peppy_dev.bad_preamble_signature_size_too_big.xxd.patch b/tests/futility/data/bios_peppy_dev.bad_preamble_signature_size_too_big.xxd.patch
new file mode 100644
index 0000000..9afc0ab
--- /dev/null
+++ b/tests/futility/data/bios_peppy_dev.bad_preamble_signature_size_too_big.xxd.patch
@@ -0,0 +1 @@
+002008c0: 6c06 0000 0000 0000 7408 0000 0000 0000  l.......t.......
diff --git a/tests/futility/data/bios_peppy_dev.bin b/tests/futility/data/bios_peppy_dev.bin
new file mode 100644
index 0000000..f241cd0
--- /dev/null
+++ b/tests/futility/data/bios_peppy_dev.bin
Binary files differ
diff --git a/tests/futility/data/bios_voxel_dev.bin b/tests/futility/data/bios_voxel_dev.bin
new file mode 100644
index 0000000..7cd8535
--- /dev/null
+++ b/tests/futility/data/bios_voxel_dev.bin
Binary files differ
diff --git a/tests/futility/data/bios_voxel_dev.no_b_slot.xxd.patch b/tests/futility/data/bios_voxel_dev.no_b_slot.xxd.patch
new file mode 100644
index 0000000..6a01277
--- /dev/null
+++ b/tests/futility/data/bios_voxel_dev.no_b_slot.xxd.patch
@@ -0,0 +1,20 @@
+01804030: 0000 0000 0000 1e00 0000 0000 0000 5000  ..............P.
+018043a0: 0000 0000 0000 0000 0000 c0ff 4f01 4000  ............O.@.
+018043b0: 0000 5257 5f46 5749 445f 4200 0000 0000  ..RW_FWID_B.....
+018043d0: 0000 0000 0000 5001 0000 3000 4d45 5f52  ......P...0.ME_R
+018043e0: 575f 4200 0000 0000 0000 0000 0000 0000  W_B.............
+018043f0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
+01804400: 8001 0000 8000 5750 5f52 4f00 0000 0000  ......WP_RO.....
+01804420: 0000 0000 0000 0000 0000 8001 0040 0000  .............@..
+01804430: 524f 5f56 5044 0000 0000 0000 0000 0000  RO_VPD..........
+01804450: 0800 0040 8001 00c0 7f00 524f 5f53 4543  ...@......RO_SEC
+01804460: 5449 4f4e 0000 0000 0000 0000 0000 0000  TION............
+01804470: 0000 0000 0000 0000 0000 0000 0040 8001  .............@..
+01804480: 0008 0000 464d 4150 0000 0000 0000 0000  ....FMAP........
+018044a0: 0000 0000 0000 0048 8001 4000 0000 524f  .......H..@...RO
+018044b0: 5f46 5249 4400 0000 0000 0000 0000 0000  _FRID...........
+018044d0: 0050 8001 0000 0700 4742 4200 0000 0000  .P......GBB.....
+018044f0: 0000 0000 0000 0000 0000 0050 8701 00b0  ...........P....
+01804500: 7800 434f 5245 424f 4f54 0000 0000 0000  x.COREBOOT......
+01804520: 0000 0000 0050 8701 00b0 7800 434f 5245  .....P....x.CORE
+01804530: 424f 4f54 0000 0000 0000 0000 0000 0000  BOOT............
diff --git a/tests/futility/data/bios_zgb_mp.bin b/tests/futility/data/bios_zgb_mp.bin
deleted file mode 100644
index c85d820..0000000
--- a/tests/futility/data/bios_zgb_mp.bin
+++ /dev/null
Binary files differ
diff --git a/tests/futility/data_bios_voxel_dev.bin_expect.txt b/tests/futility/data_bios_voxel_dev.bin_expect.txt
new file mode 100644
index 0000000..60cd1ea
--- /dev/null
+++ b/tests/futility/data_bios_voxel_dev.bin_expect.txt
@@ -0,0 +1,6 @@
+b11d74edd286c144e1135b49e7f0bc20cf041f10
+c14bd720b70d97394257e3e826bd8f43de48d4ed
+e2c1c92d7d7aa7dfed5e8375edd30b7ae52b7450
+5d2b220899c4403d564092ada3f12d3cc4483223
+e2c1c92d7d7aa7dfed5e8375edd30b7ae52b7450
+5d2b220899c4403d564092ada3f12d3cc4483223
diff --git a/tests/futility/data_bios_zgb_mp.bin_expect.txt b/tests/futility/data_bios_zgb_mp.bin_expect.txt
deleted file mode 100644
index 2a021ce..0000000
--- a/tests/futility/data_bios_zgb_mp.bin_expect.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-9f59876c7f7dc881f02d934786c6b7c2c17dcaac
-9bd99a594c45b6739899a17ec29ac2289ee75463
-a0e4415cd4e271802504cce3a211b54562178fc8
-5d2b220899c4403d564092ada3f12d3cc4483223
-e2c1c92d7d7aa7dfed5e8375edd30b7ae52b7450
-5d2b220899c4403d564092ada3f12d3cc4483223
diff --git a/tests/futility/expect_output/show.tests_futility_data_bios_mario_mp.bin b/tests/futility/expect_output/show.tests_futility_data_bios_mario_mp.bin
deleted file mode 100644
index 6039855..0000000
--- a/tests/futility/expect_output/show.tests_futility_data_bios_mario_mp.bin
+++ /dev/null
@@ -1,63 +0,0 @@
-BIOS:                    tests/futility/data/bios_mario_mp.bin
-GBB header:              GBB Area
-  Version:               1.0
-  Flags:                 0x00000000
-  Regions:                 offset       size
-    hwid                 0x00000080   0x00000100
-    bmpvf                0x00001180   0x0003de80
-    rootkey              0x00000180   0x00001000
-    recovery_key         0x0003f000   0x00001000
-  Size:                  0x00040000 / 0x00040000
-GBB content:
-  HWID:                  {9D799111-A88A-439E-9E1F-FBBB41B00A9A}
-     digest:             <none>
-  Root Key:
-    Vboot API:           1.0
-    Algorithm:           11 RSA8192 SHA512
-    Key Version:         1
-    Key sha1sum:         541f467a7d8747f55ae9087ee4e34155f5ee3cd7
-  Recovery Key:
-    Vboot API:           1.0
-    Algorithm:           11 RSA8192 SHA512
-    Key Version:         1
-    Key sha1sum:         5d0d163b824cab5ae4f23fb2cc012e2a4124f4fe
-Firmware body:           Firmware A Data
-  Offset:                0x00040000
-  Size:                  0x000d0000
-Firmware body:           Firmware B Data
-  Offset:                0x00120000
-  Size:                  0x000d0000
-Keyblock:                Firmware A Key
-  Signature:             valid
-  Size:                  0x8b8
-  Flags:                 7  !DEV DEV !REC
-  Data key algorithm:    8 RSA4096 SHA512
-  Data key version:      1
-  Data key sha1sum:      cd72cfb929765f82baba0a01ed937a73f502f6c5
-Firmware Preamble:
-  Size:                  2164
-  Header version:        2.1
-  Firmware version:      1
-  Kernel key algorithm:  8 RSA4096 SHA512
-  Kernel key version:    1
-  Kernel key sha1sum:    e39e019cb5df3e874f443721540cb261a88c58ef
-  Firmware body size:    851968
-  Preamble flags:        0
-Body verification succeeded.
-Keyblock:                Firmware B Key
-  Signature:             valid
-  Size:                  0x8b8
-  Flags:                 7  !DEV DEV !REC
-  Data key algorithm:    8 RSA4096 SHA512
-  Data key version:      1
-  Data key sha1sum:      cd72cfb929765f82baba0a01ed937a73f502f6c5
-Firmware Preamble:
-  Size:                  2164
-  Header version:        2.1
-  Firmware version:      1
-  Kernel key algorithm:  8 RSA4096 SHA512
-  Kernel key version:    1
-  Kernel key sha1sum:    e39e019cb5df3e874f443721540cb261a88c58ef
-  Firmware body size:    851968
-  Preamble flags:        0
-Body verification succeeded.
diff --git a/tests/futility/expect_output/show.tests_futility_data_bios_peppy_mp.bin b/tests/futility/expect_output/show.tests_futility_data_bios_peppy_mp.bin
new file mode 100644
index 0000000..88733c9
--- /dev/null
+++ b/tests/futility/expect_output/show.tests_futility_data_bios_peppy_mp.bin
@@ -0,0 +1,63 @@
+BIOS:                    tests/futility/data/bios_peppy_mp.bin
+GBB header:              GBB
+  Version:               1.1
+  Flags:                 0x00000039
+  Regions:                 offset       size
+    hwid                 0x00000080   0x00000100
+    bmpvf                0x00001180   0x000ece80
+    rootkey              0x00000180   0x00001000
+    recovery_key         0x000ee000   0x00001000
+  Size:                  0x000ef000 / 0x000ef000
+GBB content:
+  HWID:                  X86 PEPPY TEST 4211
+     digest:             <none>
+  Root Key:
+    Vboot API:           1.0
+    Algorithm:           11 RSA8192 SHA512
+    Key Version:         1
+    Key sha1sum:         fc68bcb88bf9af1907289a9f377d658b3b9fe5b0
+  Recovery Key:
+    Vboot API:           1.0
+    Algorithm:           11 RSA8192 SHA512
+    Key Version:         1
+    Key sha1sum:         bf39d0d3e30cbf6a121416d04df4603ad5310779
+Firmware body:           FW_MAIN_A
+  Offset:                0x00210000
+  Size:                  0x000c0000
+Firmware body:           FW_MAIN_B
+  Offset:                0x00300000
+  Size:                  0x000c0000
+Keyblock:                VBLOCK_A
+  Signature:             valid
+  Size:                  0x8b8
+  Flags:                 7  !DEV DEV !REC
+  Data key algorithm:    8 RSA4096 SHA512
+  Data key version:      1
+  Data key sha1sum:      f917ad29e36aa8a286f978c1aa0550ea31c6a561
+Firmware Preamble:
+  Size:                  2164
+  Header version:        2.1
+  Firmware version:      2
+  Kernel key algorithm:  7 RSA4096 SHA256
+  Kernel key version:    2
+  Kernel key sha1sum:    cc05423373b76acbec23ec45dfa3696a2ea6dc0f
+  Firmware body size:    146456
+  Preamble flags:        0
+Body verification succeeded.
+Keyblock:                VBLOCK_B
+  Signature:             valid
+  Size:                  0x8b8
+  Flags:                 7  !DEV DEV !REC
+  Data key algorithm:    8 RSA4096 SHA512
+  Data key version:      1
+  Data key sha1sum:      f917ad29e36aa8a286f978c1aa0550ea31c6a561
+Firmware Preamble:
+  Size:                  2164
+  Header version:        2.1
+  Firmware version:      2
+  Kernel key algorithm:  7 RSA4096 SHA256
+  Kernel key version:    2
+  Kernel key sha1sum:    cc05423373b76acbec23ec45dfa3696a2ea6dc0f
+  Firmware body size:    146456
+  Preamble flags:        0
+Body verification succeeded.
diff --git a/tests/futility/expect_output/show.tests_futility_data_bios_zgb_mp.bin b/tests/futility/expect_output/show.tests_futility_data_bios_zgb_mp.bin
deleted file mode 100644
index 2f9f807..0000000
--- a/tests/futility/expect_output/show.tests_futility_data_bios_zgb_mp.bin
+++ /dev/null
@@ -1,63 +0,0 @@
-BIOS:                    tests/futility/data/bios_zgb_mp.bin
-GBB header:              GBB
-  Version:               1.0
-  Flags:                 0x00000000
-  Regions:                 offset       size
-    hwid                 0x00000080   0x00000100
-    bmpvf                0x00001180   0x0003de80
-    rootkey              0x00000180   0x00001000
-    recovery_key         0x0003f000   0x00001000
-  Size:                  0x00040000 / 0x00040000
-GBB content:
-  HWID:                  {FA42644C-CF3A-4692-A9D3-1A667CB232E9}
-     digest:             <none>
-  Root Key:
-    Vboot API:           1.0
-    Algorithm:           11 RSA8192 SHA512
-    Key Version:         1
-    Key sha1sum:         9f59876c7f7dc881f02d934786c6b7c2c17dcaac
-  Recovery Key:
-    Vboot API:           1.0
-    Algorithm:           11 RSA8192 SHA512
-    Key Version:         1
-    Key sha1sum:         9bd99a594c45b6739899a17ec29ac2289ee75463
-Firmware body:           FW_MAIN_A
-  Offset:                0x00030000
-  Size:                  0x000dffc0
-Firmware body:           FW_MAIN_B
-  Offset:                0x00120000
-  Size:                  0x000dffc0
-Keyblock:                VBLOCK_A
-  Signature:             valid
-  Size:                  0x8b8
-  Flags:                 6  DEV !REC
-  Data key algorithm:    8 RSA4096 SHA512
-  Data key version:      1
-  Data key sha1sum:      a78aaa1691c2125ef8ccefa1a8a6bea92d38fae6
-Firmware Preamble:
-  Size:                  2164
-  Header version:        2.1
-  Firmware version:      2
-  Kernel key algorithm:  7 RSA4096 SHA256
-  Kernel key version:    2
-  Kernel key sha1sum:    0c9fd5b03ab47d37924ba8a7beb64039d84ed0e1
-  Firmware body size:    917440
-  Preamble flags:        0
-Body verification succeeded.
-Keyblock:                VBLOCK_B
-  Signature:             valid
-  Size:                  0x8b8
-  Flags:                 7  !DEV DEV !REC
-  Data key algorithm:    8 RSA4096 SHA512
-  Data key version:      1
-  Data key sha1sum:      4fe08ed739069d6834b68612eb707998a0825f34
-Firmware Preamble:
-  Size:                  2164
-  Header version:        2.1
-  Firmware version:      2
-  Kernel key algorithm:  7 RSA4096 SHA256
-  Kernel key version:    2
-  Kernel key sha1sum:    0c9fd5b03ab47d37924ba8a7beb64039d84ed0e1
-  Firmware body size:    917440
-  Preamble flags:        0
-Body verification succeeded.
diff --git a/tests/futility/models/whitetip/setvars.sh b/tests/futility/models/customtip/setvars.sh
similarity index 87%
rename from tests/futility/models/whitetip/setvars.sh
rename to tests/futility/models/customtip/setvars.sh
index 4b9cb40..a4bb55a 100755
--- a/tests/futility/models/whitetip/setvars.sh
+++ b/tests/futility/models/customtip/setvars.sh
@@ -7,14 +7,14 @@
 # particular model. The pack_firmware.py script uses this to create a working
 # setvars-model.sh script.
 
-# Version information for model whitetip
+# Version information for model customtip
 TARGET_RO_FWID="Google_Coral.10068.45.0"
 TARGET_FWID="Google_Coral.10068.45.0"
 TARGET_ECID="coral_v1.1.7272-0b44fba22"
 TARGET_PDID=""
 TARGET_PLATFORM="Google_Coral"
 
-# Image and key files for model whitetip
+# Image and key files for model customtip
 IMAGE_MAIN="images/bios_coral.bin"
 IMAGE_EC=""
 IMAGE_PD=""
diff --git a/tests/futility/run_test_scripts.sh b/tests/futility/run_test_scripts.sh
index f224bdf..aeb3477 100755
--- a/tests/futility/run_test_scripts.sh
+++ b/tests/futility/run_test_scripts.sh
@@ -62,11 +62,11 @@
   if [ ! "$rc" ]; then
     echo -e "${COL_GREEN}PASSED${COL_STOP}"
     : $(( pass++ ))
-    rm -f ${OUTDIR}/$j.{stdout,stderr,return}
+    rm -f "${OUTDIR}/$j".{stdout,stderr,return}
   else
     echo -e "${COL_RED}FAILED (${rc:-0}). Stdout is recorded in" \
       "${OUTDIR}/$j.stdout${COL_STOP}"
-    cat ${OUTDIR}/$j.stderr
+    cat "${OUTDIR}/$j.stderr"
   fi
 done
 
diff --git a/tests/futility/test_create.sh b/tests/futility/test_create.sh
index 55c648c..77c7eb4 100755
--- a/tests/futility/test_create.sh
+++ b/tests/futility/test_create.sh
@@ -15,7 +15,7 @@
 # Demonstrate that we can recreate the same vb1 keys without the .keyb files
 for sig in rsa1024 rsa2048 rsa4096 rsa8192; do
   for hash in sha1 sha256 sha512; do
-    ${FUTILITY} --vb1 create --hash_alg "${hash}" \
+    "${FUTILITY}" --vb1 create --hash_alg "${hash}" \
       "${TESTKEYS}/key_${sig}.pem" "${TMP}_key_${sig}.${hash}"
     cmp "${TESTKEYS}/key_${sig}.${hash}.vbprivk" \
       "${TMP}_key_${sig}.${hash}.vbprivk"
@@ -29,7 +29,7 @@
 # prove anything until we've used them to sign some stuff, though.
 for sig in rsa1024 rsa2048 rsa4096 rsa8192; do
   for hash in sha1 sha256 sha512; do
-    ${FUTILITY} --vb21 create --hash_alg "${hash}" \
+    "${FUTILITY}" --vb21 create --hash_alg "${hash}" \
       "${TESTKEYS}/key_${sig}.pem" "${TMP}_key_${sig}.${hash}"
   done
 done
@@ -37,12 +37,12 @@
 # Demonstrate that the sha1sums are the same for all the keys created from the
 # same .pem files, both public and private, vb1 and vb21.
 for sig in rsa1024 rsa2048 rsa4096 rsa8192; do
-  pem_sum=$(${FUTILITY} show "${TESTKEYS}/key_${sig}.pem" |
+  pem_sum=$("${FUTILITY}" show "${TESTKEYS}/key_${sig}.pem" |
     awk '/sha1sum/ {print $3}')
   # expect only one
-  [ $(echo "$pem_sum" | wc -w) = 1 ]
-  num_keys=$(echo ${TMP}_key_${sig}.* | wc -w)
-  key_sums=$(${FUTILITY} show ${TMP}_key_${sig}.* |
+  [ "$(echo "$pem_sum" | wc -w)" = 1 ]
+  num_keys=$(echo "${TMP}_key_${sig}".* | wc -w)
+  key_sums=$("${FUTILITY}" show "${TMP}_key_${sig}".* |
     awk '/sha1sum:|ID:/ {print $NF}')
   num_sums=$(echo "$key_sums" | wc -w)
   # expect one sha1sum (or ID) line per file
@@ -57,7 +57,7 @@
 # the private key.
 for sig in rsa1024 rsa2048 rsa4096 rsa8192; do
   for hash in sha1 sha256 sha512; do
-    ${FUTILITY} --vb21 create --hash_alg "${hash}" \
+    "${FUTILITY}" --vb21 create --hash_alg "${hash}" \
       "${TESTKEYS}/key_${sig}.pub.pem" "${TMP}_key_${sig}.pubonly.${hash}"
     cmp "${TMP}_key_${sig}.pubonly.${hash}.vbpubk2" \
       "${TMP}_key_${sig}.${hash}.vbpubk2"
@@ -65,5 +65,5 @@
 done
 
 # cleanup
-rm -rf ${TMP}*
+rm -rf "${TMP}"*
 exit 0
diff --git a/tests/futility/test_dump_fmap.sh b/tests/futility/test_dump_fmap.sh
index 5ae2e97..b8e418a 100755
--- a/tests/futility/test_dump_fmap.sh
+++ b/tests/futility/test_dump_fmap.sh
@@ -51,5 +51,5 @@
 
 
 # cleanup
-rm -f ${TMP}* FMAP SI_DESC FOO
+rm -f "${TMP}"* FMAP SI_DESC FOO
 exit 0
diff --git a/tests/futility/test_file_types.c b/tests/futility/test_file_types.c
index 3f88872..ee433ba 100644
--- a/tests/futility/test_file_types.c
+++ b/tests/futility/test_file_types.c
@@ -10,7 +10,7 @@
 
 #include "file_type.h"
 #include "futility.h"
-#include "test_common.h"
+#include "common/tests.h"
 
 /*
  * Files that exemplify each type.
@@ -26,8 +26,7 @@
 	{FILE_TYPE_KEYBLOCK,        "tests/devkeys/kernel.keyblock"},
 	{FILE_TYPE_FW_PREAMBLE,     "tests/futility/data/fw_vblock.bin"},
 	{FILE_TYPE_GBB,	            "tests/futility/data/fw_gbb.bin"},
-	{FILE_TYPE_BIOS_IMAGE,      "tests/futility/data/bios_zgb_mp.bin"},
-	{FILE_TYPE_OLD_BIOS_IMAGE,  "tests/futility/data/bios_mario_mp.bin"},
+	{FILE_TYPE_BIOS_IMAGE,      "tests/futility/data/bios_peppy_mp.bin"},
 	{FILE_TYPE_KERN_PREAMBLE,   "tests/futility/data/kern_preamble.bin"},
 	{FILE_TYPE_RAW_FIRMWARE,    },		/* need a test for this */
 	{FILE_TYPE_RAW_KERNEL,	    },		/* need a test for this */
diff --git a/tests/futility/test_file_types.sh b/tests/futility/test_file_types.sh
index 4b7a9fb..313e408 100755
--- a/tests/futility/test_file_types.sh
+++ b/tests/futility/test_file_types.sh
@@ -16,13 +16,13 @@
 # Args are <expected_type>, <file_to_probe>
 test_case() {
     local result
-    result=$(${FUTILITY} show -t "${SRCDIR}/$2" | awk '{print $NF}')
+    result=$("${FUTILITY}" show -t "${SRCDIR}/$2" | awk '{print $NF}')
     [ "$1" = "$result" ]
 }
 
 # Arg is <file_to_probe>
 fail_case() {
-    if ${FUTILITY} show -t "$1" ; then false; else true; fi
+    if "${FUTILITY}" show -t "$1" ; then false; else true; fi
 }
 
 # Known types
@@ -32,8 +32,7 @@
 test_case "keyblock"        "tests/devkeys/kernel.keyblock"
 test_case "fw_pre"          "tests/futility/data/fw_vblock.bin"
 test_case "gbb"	            "tests/futility/data/fw_gbb.bin"
-test_case "bios"            "tests/futility/data/bios_zgb_mp.bin"
-test_case "oldbios"         "tests/futility/data/bios_mario_mp.bin"
+test_case "bios"            "tests/futility/data/bios_peppy_mp.bin"
 test_case "kernel"          "tests/futility/data/kern_preamble.bin"
 # We don't have a way to identify these (yet?)
 # test_case "RAW_FIRMWARE"
@@ -55,22 +54,23 @@
 # often won't work, but it certainly shouldn't core dump.
 
 # We'll ask futility to tell us what types it supports
-TYPES=$(${FUTILITY} show --type help | awk '/^ +/ {print $1}')
+TYPES=$("${FUTILITY}" show --type help | awk '/^ +/ {print $1}')
 
 # And we'll just reuse the same files above.
-FILES=$(awk '/^test_case / {print $NF}' $0 | tr -d '"')
+FILES=$(awk '/^test_case / {print $NF}' "$0" | tr -d '"')
 
 # futility should normally exit with either 0 or 1. Make sure that happens.
 # NOTE: /bin/bash returns values > 125 for special problems like signals.
 # I welcome patches to do this more portably.
 for type in $TYPES; do
     for file in $FILES; do
-        ${FUTILITY} show --type ${type} "${SRCDIR}/${file}" && rc=$? || rc=$?
+        "${FUTILITY}" show --type "${type}" "${SRCDIR}/${file}" && \
+          rc=$? || rc=$?
         [ "$rc" -le 2 ]
     done
 done
 
 
 # cleanup
-rm -rf ${TMP}*
+rm -rf "${TMP}"*
 exit 0
diff --git a/tests/futility/test_gbb_utility.sh b/tests/futility/test_gbb_utility.sh
index bdfa262..5b783c4 100755
--- a/tests/futility/test_gbb_utility.sh
+++ b/tests/futility/test_gbb_utility.sh
@@ -15,56 +15,58 @@
 # First, let's test the basic functionality
 
 # For simplicity, we'll use the same size for all properties.
-${FUTILITY} gbb -c 16,0x10,16,0x10 ${TMP}.blob
+"${FUTILITY}" gbb -c 16,0x10,16,0x10 "${TMP}.blob"
 
 # Flags
-${FUTILITY} gbb -s --flags=0xdeadbeef ${TMP}.blob
-${FUTILITY} gbb -g --flags ${TMP}.blob | grep -i 0xdeadbeef
+"${FUTILITY}" gbb -s --flags=0xdeadbeef "${TMP}.blob"
+"${FUTILITY}" gbb -g --flags "${TMP}.blob" | grep -i 0xdeadbeef
 
 # HWID length should include the terminating null - this is too long
-if ${FUTILITY} gbb -s --hwid="0123456789ABCDEF" ${TMP}.blob; then
+if "${FUTILITY}" gbb -s --hwid="0123456789ABCDEF" "${TMP}.blob"; then
   false;
 fi
 # This works
-${FUTILITY} gbb -s --hwid="0123456789ABCDE" ${TMP}.blob
+"${FUTILITY}" gbb -s --hwid="0123456789ABCDE" "${TMP}.blob"
 # Read it back?
-${FUTILITY} gbb -g ${TMP}.blob | grep "0123456789ABCDE"
+"${FUTILITY}" gbb -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
-if ${FUTILITY} gbb -s --rootkey     ${TMP}.data1.toolong ${TMP}.blob; then false; fi
-if ${FUTILITY} gbb -s --recoverykey ${TMP}.data2.toolong ${TMP}.blob; then false; fi
+dd if=/dev/urandom bs=17 count=1 of="${TMP}.data1.toolong"
+dd if=/dev/urandom bs=17 count=1 of="${TMP}.data2.toolong"
+if "${FUTILITY}" gbb -s --rootkey     "${TMP}.data1.toolong" "${TMP}.blob";
+  then false; fi
+if "${FUTILITY}" gbb -s --recoverykey "${TMP}.data2.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
-${FUTILITY} gbb -s \
-  --rootkey     ${TMP}.data1.short \
-  --recoverykey ${TMP}.data2.short ${TMP}.blob
+dd if=/dev/urandom bs=10 count=1 of="${TMP}.data1.short"
+dd if=/dev/urandom bs=10 count=1 of="${TMP}.data2.short"
+"${FUTILITY}" gbb -s \
+  --rootkey     "${TMP}.data1.short" \
+  --recoverykey "${TMP}.data2.short" "${TMP}.blob"
 # read 'em back
-${FUTILITY} gbb -g \
-  --rootkey     ${TMP}.read1 \
-  --recoverykey ${TMP}.read2 ${TMP}.blob
+"${FUTILITY}" gbb -g \
+  --rootkey     "${TMP}.read1" \
+  --recoverykey "${TMP}.read2" "${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}.data1.short" "${TMP}.read1"
+cmp -n 10 "${TMP}.data2.short" "${TMP}.read2"
 
 # 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 -s --rootkey     ${TMP}.data1 ${TMP}.blob
-${FUTILITY} gbb -s --recoverykey ${TMP}.data2 ${TMP}.blob
+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 -s --rootkey     "${TMP}.data1" "${TMP}.blob"
+"${FUTILITY}" gbb -s --recoverykey "${TMP}.data2" "${TMP}.blob"
 
 # Read 'em back.
-${FUTILITY} gbb -g --rootkey     ${TMP}.read1 ${TMP}.blob
-${FUTILITY} gbb -g --recoverykey ${TMP}.read2 ${TMP}.blob
+"${FUTILITY}" gbb -g --rootkey     "${TMP}.read1" "${TMP}.blob"
+"${FUTILITY}" gbb -g --recoverykey "${TMP}.read2" "${TMP}.blob"
 # Verify
-cmp ${TMP}.data1 ${TMP}.read1
-cmp ${TMP}.data2 ${TMP}.read2
+cmp "${TMP}.data1" "${TMP}.read1"
+cmp "${TMP}.data2" "${TMP}.read2"
 
 
 # Okay, creating GBB blobs seems to work. Now let's make sure that corrupted
@@ -100,82 +102,85 @@
 #
 
 # bad major_version
-cat ${TMP}.blob | ${REPLACE} 0x4 2 > ${TMP}.blob.bad
-if ${FUTILITY} gbb ${TMP}.blob.bad; then false; fi
+"${REPLACE}" 0x4 2 < "${TMP}.blob" > "${TMP}.blob.bad"
+if "${FUTILITY}" gbb "${TMP}.blob.bad"; then false; fi
 
 # header size too large
-cat ${TMP}.blob | ${REPLACE} 0x8 0x81 > ${TMP}.blob.bad
-if ${FUTILITY} gbb ${TMP}.blob.bad; then false; fi
+"${REPLACE}" 0x8 0x81 < "${TMP}.blob" > "${TMP}.blob.bad"
+if "${FUTILITY}" gbb "${TMP}.blob.bad"; then false; fi
 
 # header size too small
-cat ${TMP}.blob | ${REPLACE} 0x8 0x7f > ${TMP}.blob.bad
-if ${FUTILITY} gbb ${TMP}.blob.bad; then false; fi
+"${REPLACE}" 0x8 0x7f < "${TMP}.blob" > "${TMP}.blob.bad"
+if "${FUTILITY}" gbb "${TMP}.blob.bad"; then false; fi
 
 # HWID not null-terminated is invalid
-cat ${TMP}.blob | ${REPLACE} 0x8f 0x41 > ${TMP}.blob.bad
-if ${FUTILITY} gbb ${TMP}.blob.bad; then false; fi
+"${REPLACE}" 0x8f 0x41 < "${TMP}.blob" > "${TMP}.blob.bad"
+if "${FUTILITY}" gbb "${TMP}.blob.bad"; then false; fi
 
 # HWID of length zero is okay
-cat ${TMP}.blob | ${REPLACE} 0x14 0x00 > ${TMP}.blob.ok
-${FUTILITY} gbb ${TMP}.blob.ok
+"${REPLACE}" 0x14 0x00 < "${TMP}.blob" > "${TMP}.blob.ok"
+"${FUTILITY}" gbb "${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 ${TMP}.blob.ok
+"${REPLACE}" 0x14 0x01 < "${TMP}.blob" | "${REPLACE}" 0x80 0x00 \
+  > "${TMP}.blob.ok"
+"${FUTILITY}" gbb "${TMP}.blob.ok"
 
 # zero-length HWID not null-terminated is invalid
-cat ${TMP}.blob | ${REPLACE} 0x8f 0x41 > ${TMP}.blob.bad
-if ${FUTILITY} gbb ${TMP}.blob.bad; then false; fi
+"${REPLACE}" 0x8f 0x41 < "${TMP}.blob" > "${TMP}.blob.bad"
+if "${FUTILITY}" gbb "${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 ${TMP}.blob.bad; then false; fi
-cat ${TMP}.blob | ${REPLACE} 0x10 0x00 > ${TMP}.blob.bad
-if ${FUTILITY} gbb ${TMP}.blob.bad; then false; fi
+"${REPLACE}" 0x10 0x7f < "${TMP}.blob" > "${TMP}.blob.bad"
+if "${FUTILITY}" gbb "${TMP}.blob.bad"; then false; fi
+"${REPLACE}" 0x10 0x00 < "${TMP}.blob" > "${TMP}.blob.bad"
+if "${FUTILITY}" gbb "${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 ${TMP}.blob.bad; then false; fi
-cat ${TMP}.blob | ${REPLACE} 0x18 0x00 > ${TMP}.blob.bad
-if ${FUTILITY} gbb ${TMP}.blob.bad; then false; fi
+"${REPLACE}" 0x18 0x7f < "${TMP}.blob" > "${TMP}.blob.bad"
+if "${FUTILITY}" gbb "${TMP}.blob.bad"; then false; fi
+"${REPLACE}" 0x18 0x00 < "${TMP}.blob" > "${TMP}.blob.bad"
+if "${FUTILITY}" gbb "${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 ${TMP}.blob.bad; then false; fi
-cat ${TMP}.blob | ${REPLACE} 0x28 0x00 > ${TMP}.blob.bad
-if ${FUTILITY} gbb ${TMP}.blob.bad; then false; fi
+"${REPLACE}" 0x28 0x7f < "${TMP}.blob" > "${TMP}.blob.bad"
+if "${FUTILITY}" gbb "${TMP}.blob.bad"; then false; fi
+"${REPLACE}" 0x28 0x00 < "${TMP}.blob" > "${TMP}.blob.bad"
+if "${FUTILITY}" gbb "${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 -g ${TMP}.blob.bad
-cat ${TMP}.blob | ${REPLACE} 0x14 0x41 > ${TMP}.blob.bad
-if ${FUTILITY} gbb -g ${TMP}.blob.bad; then false; fi
+"${REPLACE}" 0x14 0x40 < "${TMP}.blob" > "${TMP}.blob.bad"
+"${FUTILITY}" gbb -g "${TMP}.blob.bad"
+"${REPLACE}" 0x14 0x41 < "${TMP}.blob" > "${TMP}.blob.bad"
+if "${FUTILITY}" gbb -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 -g ${TMP}.blob.bad
-cat ${TMP}.blob | ${REPLACE} 0x1c 0x31 > ${TMP}.blob.bad
-if ${FUTILITY} gbb -g ${TMP}.blob.bad; then false; fi
+"${REPLACE}" 0x1c 0x30 < "${TMP}.blob" > "${TMP}.blob.bad"
+"${FUTILITY}" gbb -g "${TMP}.blob.bad"
+"${REPLACE}" 0x1c 0x31 < "${TMP}.blob" > "${TMP}.blob.bad"
+if "${FUTILITY}" gbb -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 -g ${TMP}.blob.bad
-cat ${TMP}.blob | ${REPLACE} 0x2c 0x11 > ${TMP}.blob.bad
-if ${FUTILITY} gbb -g ${TMP}.blob.bad; then false; fi
+"${REPLACE}" 0x2c 0x10 < "${TMP}.blob" > "${TMP}.blob.bad"
+"${FUTILITY}" gbb -g "${TMP}.blob.bad"
+"${REPLACE}" 0x2c 0x11 < "${TMP}.blob" > "${TMP}.blob.bad"
+if "${FUTILITY}" gbb -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 -g ${TMP}.blob.bad
-if ${FUTILITY} gbb -s --hwid="A" ${TMP}.blob.bad; then false; fi
+"${REPLACE}" 0x14 0x00 < "${TMP}.blob" > "${TMP}.blob.bad"
+"${FUTILITY}" gbb -g "${TMP}.blob.bad"
+if "${FUTILITY}" gbb -s --hwid="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 -g --rootkey     ${TMP}.read1 ${TMP}.blob.bad
-if ${FUTILITY} gbb -s --rootkey  ${TMP}.data1 ${TMP}.blob.bad; then false; fi
+"${REPLACE}" 0x1c 0x00 < "${TMP}.blob" > "${TMP}.blob.bad"
+"${FUTILITY}" gbb -g --rootkey     "${TMP}.read1" "${TMP}.blob.bad"
+if "${FUTILITY}" gbb -s --rootkey  "${TMP}.data1" "${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 -g --recoverykey ${TMP}.read2 ${TMP}.blob.bad
-if ${FUTILITY} gbb -s --recoverykey ${TMP}.data2 ${TMP}.blob.bad; then false; fi
+"${REPLACE}" 0x2c 0x00 < "${TMP}.blob" > "${TMP}.blob.bad"
+"${FUTILITY}" gbb -g --recoverykey "${TMP}.read2" "${TMP}.blob.bad"
+if "${FUTILITY}" gbb -s --recoverykey "${TMP}.data2" "${TMP}.blob.bad";
+  then false; fi
 
 
 # GBB v1.2 adds a sha256 digest field in what was previously padding:
@@ -189,21 +194,21 @@
 
 # See that the digest is updated properly.
 hwid="123456789ABCDEF"
-${FUTILITY} gbb -s --hwid=${hwid} ${TMP}.blob
+"${FUTILITY}" gbb -s --hwid="${hwid}" "${TMP}.blob"
 expect=$(echo -n "$hwid" | sha256sum | cut -d ' ' -f 1)
-[ $(echo -n ${expect} | wc -c) == "64" ]
-${FUTILITY} gbb -g --digest ${TMP}.blob | grep ${expect}
+[ "$(echo -n "${expect}" | wc -c)" == "64" ]
+"${FUTILITY}" gbb -g --digest "${TMP}.blob" | grep "${expect}"
 
 # Garble the digest, see that it's noticed.
 # (assuming these zeros aren't present)
-cat ${TMP}.blob | ${REPLACE} 0x33 0x00 0x00 0x00 0x00 0x00 > ${TMP}.blob.bad
-${FUTILITY} gbb -g --digest ${TMP}.blob.bad | grep '0000000000'
-${FUTILITY} gbb -g --digest ${TMP}.blob.bad | grep 'invalid'
+"${REPLACE}" 0x33 0x00 0x00 0x00 0x00 0x00 < "${TMP}.blob" > "${TMP}.blob.bad"
+"${FUTILITY}" gbb -g --digest "${TMP}.blob.bad" | grep '0000000000'
+"${FUTILITY}" gbb -g --digest "${TMP}.blob.bad" | grep 'invalid'
 
 # Garble the HWID. The digest is unchanged, but now invalid.
-cat ${TMP}.blob | ${REPLACE} 0x84 0x70 0x71 0x72 > ${TMP}.blob.bad
-${FUTILITY} gbb -g --digest ${TMP}.blob.bad | grep 'invalid'
+"${REPLACE}" 0x84 0x70 0x71 0x72 < "${TMP}.blob" > "${TMP}.blob.bad"
+"${FUTILITY}" gbb -g --digest "${TMP}.blob.bad" | grep 'invalid'
 
 # cleanup
-rm -f ${TMP}*
+rm -f "${TMP}"*
 exit 0
diff --git a/tests/futility/test_load_fmap.sh b/tests/futility/test_load_fmap.sh
index a93b71f..7821243 100755
--- a/tests/futility/test_load_fmap.sh
+++ b/tests/futility/test_load_fmap.sh
@@ -10,34 +10,34 @@
 cd "$OUTDIR"
 
 
-IN=${SCRIPT_DIR}/futility/data/bios_link_mp.bin
-BIOS=${TMP}.bios.bin
+IN="${SCRIPT_DIR}/futility/data/bios_link_mp.bin"
+BIOS="${TMP}.bios.bin"
 
-cp ${IN} ${BIOS}
+cp "${IN}" "${BIOS}"
 
-AREAS="RW_SECTION_A VBLOCK_B BOOT_STUB"
-
+AREAS=(RW_SECTION_A VBLOCK_B BOOT_STUB)
+set -x
 # Extract good blobs first
-${FUTILITY} dump_fmap -x ${BIOS} ${AREAS}
+"${FUTILITY}" dump_fmap -x "${BIOS}" "${AREAS[@]}"
 
 # Save the good blobs, make same-size random blobs, create command
-CMDS=""
-for a in ${AREAS}; do
-  size=$(stat -c '%s' $a)
-  mv $a $a.good
-  dd if=/dev/urandom of=$a.rand bs=$size count=1
-  CMDS="$CMDS $a:$a.rand"
+CMDS=( )
+for a in "${AREAS[@]}"; do
+  size=$(stat -c '%s' "$a")
+  mv "$a" "$a.good"
+  dd if=/dev/urandom of="$a.rand" bs="$size" count=1
+  CMDS+=("$a:$a.rand")
 done
 
 # Poke the new blobs in
-${FUTILITY} load_fmap ${BIOS} ${CMDS}
+"${FUTILITY}" load_fmap "${BIOS}" "${CMDS[@]}"
 
 # Pull them back out and see if they match
-${FUTILITY} dump_fmap -x ${BIOS} ${AREAS}
-for a in ${AREAS}; do
-  cmp $a $a.rand
+"${FUTILITY}" dump_fmap -x "${BIOS}" "${AREAS[@]}"
+for a in "${AREAS[@]}"; do
+  cmp "$a" "$a.rand"
 done
 
 # cleanup
-rm -f ${TMP}* ${AREAS} *.rand *.good
+rm -f "${TMP}"* "${AREAS[@]}" ./*.rand ./*.good
 exit 0
diff --git a/tests/futility/test_main.sh b/tests/futility/test_main.sh
index 2e5f4dc..725abe3 100755
--- a/tests/futility/test_main.sh
+++ b/tests/futility/test_main.sh
@@ -10,55 +10,57 @@
 cd "$OUTDIR"
 
 # No args returns nonzero exit code
-${FUTILITY} && false
+"${FUTILITY}" && false
 
 # It's weird but okay if the command is a full path.
-${FUTILITY} /fake/path/to/help  > "$TMP"
+"${FUTILITY}" /fake/path/to/help  > "$TMP"
 grep Usage "$TMP"
 
 # Make sure logging does something.
 LOG="/tmp/futility.log"
-[ -f ${LOG} ] && mv ${LOG} ${LOG}.backup
-touch ${LOG}
-${FUTILITY} help
-grep ${FUTILITY} ${LOG}
-rm -f ${LOG}
-[ -f ${LOG}.backup ] && mv ${LOG}.backup ${LOG}
+[ -f "${LOG}" ] && mv "${LOG}" "${LOG}.backup"
+touch "${LOG}"
+"${FUTILITY}" help
+grep "${FUTILITY}" "${LOG}"
+rm -f "${LOG}"
+[ -f "${LOG}.backup" ] && mv "${LOG}.backup" "${LOG}"
 
 # Use some known digests to verify that things work...
-DEVKEYS=${SRCDIR}/tests/devkeys
+DEVKEYS="${SRCDIR}/tests/devkeys"
 SHA=e78ce746a037837155388a1096212ded04fb86eb
 
 # all progs in the pipelines should work
 set -o pipefail
 
 # If it's invoked as the name of a command we know, it should do that command
-ln -sf ${FUTILITY} vbutil_key
-./vbutil_key --unpack ${DEVKEYS}/installer_kernel_data_key.vbpubk | grep ${SHA}
-ln -sf ${FUTILITY} vbutil_keyblock
-./vbutil_keyblock --unpack ${DEVKEYS}/installer_kernel.keyblock | grep ${SHA}
-cp ${FUTILITY} show
-./show ${SCRIPT_DIR}/futility/data/rec_kernel_part.bin | grep ${SHA}
+ln -sf "${FUTILITY}" vbutil_key
+./vbutil_key --unpack "${DEVKEYS}/installer_kernel_data_key.vbpubk" | \
+  grep "${SHA}"
+ln -sf "${FUTILITY}" vbutil_keyblock
+./vbutil_keyblock --unpack "${DEVKEYS}/installer_kernel.keyblock" | \
+  grep "${SHA}"
+cp "${FUTILITY}" show
+./show "${SCRIPT_DIR}/futility/data/rec_kernel_part.bin" | grep "${SHA}"
 
 # If it's invoked by any other name, expect the command to be the first arg.
-ln -sf ${FUTILITY} muggle
-./muggle vbutil_key --unpack ${DEVKEYS}/installer_kernel_data_key.vbpubk \
-  | grep ${SHA}
-ln -sf ${FUTILITY} buggle
-./buggle vbutil_keyblock --unpack ${DEVKEYS}/installer_kernel.keyblock \
-  | grep ${SHA}
-cp ${FUTILITY} boo
-./boo show ${SCRIPT_DIR}/futility/data/rec_kernel_part.bin | grep ${SHA}
+ln -sf "${FUTILITY}" muggle
+./muggle vbutil_key --unpack "${DEVKEYS}/installer_kernel_data_key.vbpubk" \
+  | grep "${SHA}"
+ln -sf "${FUTILITY}" buggle
+./buggle vbutil_keyblock --unpack "${DEVKEYS}/installer_kernel.keyblock" \
+  | grep "${SHA}"
+cp "${FUTILITY}" boo
+./boo show "${SCRIPT_DIR}/futility/data/rec_kernel_part.bin" | grep "${SHA}"
 
 
 # we expect the first command fail, but the output to match anyway
 set +o pipefail
 
 # If it can't figure out the command at all, it should complain.
-${FUTILITY} muggle | grep Usage:
+"${FUTILITY}" muggle | grep Usage:
 ./buggle futility | grep Usage:
 ./boo | grep Usage:
 
 # cleanup
-rm -f ${TMP}* vbutil_key vbutil_keyblock show muggle buggle boo
+rm -f "${TMP}"* vbutil_key vbutil_keyblock show muggle buggle boo
 exit 0
diff --git a/tests/futility/test_not_really.c b/tests/futility/test_not_really.c
index 3cfc3ed..9bffb1a 100644
--- a/tests/futility/test_not_really.c
+++ b/tests/futility/test_not_really.c
@@ -6,7 +6,7 @@
 #include <stdio.h>
 
 #include "2struct.h"
-#include "test_common.h"
+#include "common/tests.h"
 
 int main(int argc, char *argv[])
 {
diff --git a/tests/futility/test_rwsig.sh b/tests/futility/test_rwsig.sh
index 2bd70ce..3a0a6e3 100755
--- a/tests/futility/test_rwsig.sh
+++ b/tests/futility/test_rwsig.sh
@@ -10,7 +10,7 @@
 cd "$OUTDIR"
 
 DATADIR="${SCRIPT_DIR}/futility/data"
-TESTKEYS=${SRCDIR}/tests/testkeys
+TESTKEYS="${SRCDIR}/tests/testkeys"
 
 SIGS="1024 2048 2048_exp3 3072_exp3 4096 8192"
 HASHES="SHA1 SHA256 SHA512"
@@ -18,13 +18,13 @@
 
 set -o pipefail
 
-infile=${DATADIR}/hammer_dev.bin
-outfile=${TMP}.hammer_dev.bin
-cp ${infile} ${outfile}
+infile="${DATADIR}/hammer_dev.bin"
+outfile="${TMP}.hammer_dev.bin"
+cp "${infile}" "${outfile}"
 # Signing without private key should extract EC_RW.bin
-${FUTILITY} sign --type rwsig --version 2 ${outfile}
-cmp ${infile} ${outfile}
-cmp ${EC_RW} ${DATADIR}/${EC_RW}
+"${FUTILITY}" sign --type rwsig --version 2 "${outfile}"
+cmp "${infile}" "${outfile}"
+cmp "${EC_RW}" "${DATADIR}/${EC_RW}"
 
 for s in $SIGS; do
     echo -n "$s " 1>&3
@@ -34,31 +34,32 @@
         outkeys=${TMP}.${s}_${h}
         outfile=${TMP}.${s}_${h}.bin
 
-        ${FUTILITY} create --desc "Test key" --hash_alg ${h} \
-                    ${pemfile} ${outkeys}
+        "${FUTILITY}" create --desc "Test key" --hash_alg "${h}" \
+                      "${pemfile}" "${outkeys}"
 
         # The input file should be correctly signed to start with
-        ${FUTILITY} show --type rwsig ${infile}
+        "${FUTILITY}" show --type rwsig "${infile}"
 
         # Using the wrong key to verify it should fail
-        if ${FUTILITY} show --type rwsig --pubkey ${outkeys}.vbpubk2 \
-                       ${infile}; then
+        if "${FUTILITY}" show --type rwsig --pubkey "${outkeys}.vbpubk2" \
+                         "${infile}"; then
             exit 1
         fi
 
-        cp ${infile} ${outfile}
+        cp "${infile}" "${outfile}"
 
         # Sign ec.bin with a new private key
-        ${FUTILITY} sign --type rwsig --prikey ${outkeys}.vbprik2 \
-                --version 2 ${outfile}
+        "${FUTILITY}" sign --type rwsig --prikey "${outkeys}.vbprik2" \
+                      --version 2 "${outfile}"
         # Check EC_RW.bin is produced
-        [[ -e ${EC_RW} ]]
+        [[ -e "${EC_RW}" ]]
 
-        ${FUTILITY} show --type rwsig --pubkey ${outkeys}.vbpubk2 ${outfile}
-        ${FUTILITY} show --type rwsig ${outfile}
+        "${FUTILITY}" show --type rwsig --pubkey "${outkeys}.vbpubk2" \
+                      "${outfile}"
+        "${FUTILITY}" show --type rwsig "${outfile}"
     done
 done
 
 # cleanup
-rm -rf ${TMP}*
+rm -rf "${TMP}"*
 exit 0
diff --git a/tests/futility/test_show_contents.sh b/tests/futility/test_show_contents.sh
index e040362..9b3431d 100755
--- a/tests/futility/test_show_contents.sh
+++ b/tests/futility/test_show_contents.sh
@@ -16,8 +16,7 @@
   tests/devkeys/kernel.keyblock
   tests/futility/data/fw_vblock.bin
   tests/futility/data/fw_gbb.bin
-  tests/futility/data/bios_zgb_mp.bin
-  tests/futility/data/bios_mario_mp.bin
+  tests/futility/data/bios_peppy_mp.bin
   tests/futility/data/kern_preamble.bin
   tests/futility/data/sample.vbpubk2
   tests/futility/data/sample.vbprik2
@@ -29,7 +28,7 @@
     outfile="show.${file//\//_}"
     gotfile="${OUTDIR}/${outfile}"
     wantfile="${SRCDIR}/tests/futility/expect_output/${outfile}"
-    ( cd "${SRCDIR}" && ${FUTILITY} show "${file}" ) | tee "${gotfile}"
+    ( cd "${SRCDIR}" && "${FUTILITY}" show "${file}" ) | tee "${gotfile}"
 
     # Uncomment this to update the expected output
     #cp "${gotfile}" "${wantfile}"
@@ -48,7 +47,7 @@
     outfile="vbutil_key.${file//\//_}"
     gotfile="${OUTDIR}/${outfile}"
     wantfile="${SRCDIR}/tests/futility/expect_output/${outfile}"
-    ( cd "${SRCDIR}" && ${FUTILITY} vbutil_key --unpack "${file}" ) \
+    ( cd "${SRCDIR}" && "${FUTILITY}" vbutil_key --unpack "${file}" ) \
         | tee "${gotfile}"
 
     # Uncomment this to update the expected output
@@ -63,7 +62,7 @@
 outfile="vbutil_keyblock.${file//\//_}"
 gotfile="${OUTDIR}/${outfile}"
 wantfile="${SRCDIR}/tests/futility/expect_output/${outfile}"
-( cd "${SRCDIR}" && ${FUTILITY} vbutil_keyblock --unpack "${file}" \
+( cd "${SRCDIR}" && "${FUTILITY}" vbutil_keyblock --unpack "${file}" \
     --signpubkey "tests/devkeys/kernel_subkey.vbpubk" ) \
     | tee "${gotfile}"
 
@@ -83,7 +82,7 @@
 # arbitrary non-zero numbers so we can verify they're printed
 # properly.
 dd bs=1024 count=16 if=/dev/urandom of="${TMP}.fw_main"
-${FUTILITY} vbutil_firmware --vblock "${TMP}.vblock.old" \
+"${FUTILITY}" vbutil_firmware --vblock "${TMP}.vblock.old" \
   --keyblock "${KEYDIR}/firmware.keyblock" \
   --signprivate "${KEYDIR}/firmware_data_key.vbprivk" \
   --version 12 \
@@ -92,7 +91,7 @@
   --flags 42
 
 # Verify
-${FUTILITY} vbutil_firmware --verify "${TMP}.vblock.old" \
+"${FUTILITY}" vbutil_firmware --verify "${TMP}.vblock.old" \
   --signpubkey "${KEYDIR}/root_key.vbpubk" \
   --fv "${TMP}.fw_main" | tee "${gotfile}"
 
@@ -103,5 +102,5 @@
 
 
 # cleanup
-rm -rf "${TMP}*"
+rm -rf "${TMP}"*
 exit 0
diff --git a/tests/futility/test_show_kernel.sh b/tests/futility/test_show_kernel.sh
index 151cf46..f2610a3 100755
--- a/tests/futility/test_show_kernel.sh
+++ b/tests/futility/test_show_kernel.sh
@@ -9,52 +9,52 @@
 # Work in scratch directory
 cd "$OUTDIR"
 
-DEVKEYS=${SRCDIR}/tests/devkeys
-TESTKEYS=${SRCDIR}/tests/testkeys
+DEVKEYS="${SRCDIR}/tests/devkeys"
+TESTKEYS="${SRCDIR}/tests/testkeys"
 
 echo 'Creating test kernel'
 
 # Dummy kernel data
-echo "hi there" > ${TMP}.config.txt
-dd if=/dev/urandom bs=16384 count=1 of=${TMP}.bootloader.bin
-dd if=/dev/urandom bs=32768 count=1 of=${TMP}.kernel.bin
+echo "hi there" > "${TMP}.config.txt"
+dd if=/dev/urandom bs=16384 count=1 of="${TMP}.bootloader.bin"
+dd if=/dev/urandom bs=32768 count=1 of="${TMP}.kernel.bin"
 
 # Pack kernel data key using original vboot utilities.
-${FUTILITY} vbutil_key --pack ${TMP}.datakey.test \
-    --key ${TESTKEYS}/key_rsa2048.keyb --algorithm 4
+"${FUTILITY}" vbutil_key --pack "${TMP}.datakey.test" \
+    --key "${TESTKEYS}/key_rsa2048.keyb" --algorithm 4
 
 # Keyblock with kernel data key is signed by kernel subkey
 # Flags=5 means dev=0 rec=0
-${FUTILITY} vbutil_keyblock --pack ${TMP}.keyblock.test \
-    --datapubkey ${TMP}.datakey.test \
+"${FUTILITY}" vbutil_keyblock --pack "${TMP}.keyblock.test" \
+    --datapubkey "${TMP}.datakey.test" \
     --flags 5 \
-    --signprivate ${DEVKEYS}/kernel_subkey.vbprivk
+    --signprivate "${DEVKEYS}/kernel_subkey.vbprivk"
 
 # Kernel preamble is signed with the kernel data key
-${FUTILITY} vbutil_kernel \
-    --pack ${TMP}.kernel.test \
-    --keyblock ${TMP}.keyblock.test \
-    --signprivate ${TESTKEYS}/key_rsa2048.sha256.vbprivk \
+"${FUTILITY}" vbutil_kernel \
+    --pack "${TMP}.kernel.test" \
+    --keyblock "${TMP}.keyblock.test" \
+    --signprivate "${TESTKEYS}/key_rsa2048.sha256.vbprivk" \
     --version 1 \
     --arch arm \
-    --vmlinuz ${TMP}.kernel.bin \
-    --bootloader ${TMP}.bootloader.bin \
-    --config ${TMP}.config.txt
+    --vmlinuz "${TMP}.kernel.bin" \
+    --bootloader "${TMP}.bootloader.bin" \
+    --config "${TMP}.config.txt"
 
 echo 'Verifying test kernel'
 
 # Verify the kernel
-${FUTILITY} show ${TMP}.kernel.test \
-    --publickey ${DEVKEYS}/kernel_subkey.vbpubk \
-  | egrep 'Signature.*valid'
+"${FUTILITY}" show "${TMP}.kernel.test" \
+    --publickey "${DEVKEYS}/kernel_subkey.vbpubk" \
+  | grep -E 'Signature.*valid'
 
 echo 'Test kernel blob looks good'
 
 # Mess up the padding, make sure it fails.
 rc=0
-${FUTILITY} show ${TMP}.kernel.test \
+"${FUTILITY}" show "${TMP}.kernel.test" \
     --pad 0x100 \
-    --publickey ${DEVKEYS}/kernel_subkey.vbpubk \
+    --publickey "${DEVKEYS}/kernel_subkey.vbpubk" \
   || rc=$?
 [ $rc -ne 0 ]
 [ $rc -lt 128 ]
@@ -63,9 +63,9 @@
 
 # Look waaaaaay off the end of the file, make sure it fails.
 rc=0
-${FUTILITY} show ${TMP}.kernel.test \
+"${FUTILITY}" show "${TMP}.kernel.test" \
     --pad 0x100000 \
-    --publickey ${DEVKEYS}/kernel_subkey.vbpubk \
+    --publickey "${DEVKEYS}/kernel_subkey.vbpubk" \
   || rc=$?
 [ $rc -ne 0 ]
 [ $rc -lt 128 ]
@@ -73,5 +73,5 @@
 echo 'Really invalid args are still invalid'
 
 # cleanup
-rm -rf ${TMP}*
+rm -rf "${TMP}"*
 exit 0
diff --git a/tests/futility/test_show_usbpd1.sh b/tests/futility/test_show_usbpd1.sh
index b5553aa..3998fa7 100755
--- a/tests/futility/test_show_usbpd1.sh
+++ b/tests/futility/test_show_usbpd1.sh
@@ -24,23 +24,24 @@
 
     for test in $TESTS; do
 
-        infile=${DATADIR}/${test}.unsigned
+        infile="${DATADIR}/${test}.unsigned"
 
         for h in $HASHES; do
 
-            pemfile=${TESTKEYS}/key_rsa${s}.pem
-            outfile=${TMP}.${test}_${s}_${h}.new
+            pemfile="${TESTKEYS}/key_rsa${s}.pem"
+            outfile="${TMP}.${test}_${s}_${h}.new"
 
             # sign it
-            ${FUTILITY} sign --type usbpd1 --pem ${pemfile} ${infile} ${outfile}
+            "${FUTILITY}" sign --type usbpd1 --pem "${pemfile}" "${infile}" \
+                          "${outfile}"
 
             # make sure it identifies correctly
-            ${FUTILITY} verify ${outfile}
+            "${FUTILITY}" verify "${outfile}"
 
         done
     done
 done
 
 # cleanup
-rm -rf ${TMP}*
+rm -rf "${TMP}"*
 exit 0
diff --git a/tests/futility/test_show_vs_verify.sh b/tests/futility/test_show_vs_verify.sh
index 4981a38..c0c0872 100755
--- a/tests/futility/test_show_vs_verify.sh
+++ b/tests/futility/test_show_vs_verify.sh
@@ -10,67 +10,67 @@
 cd "$OUTDIR"
 
 # some stuff we'll need
-DEVKEYS=${SRCDIR}/tests/devkeys
+DEVKEYS="${SRCDIR}/tests/devkeys"
 
 # The show command exits with 0 if the data is consistent.
 # The verify command exits with 0 only if all the data is verified.
 
 ####  keyblock
 
-${FUTILITY} show ${DEVKEYS}/firmware.keyblock
+"${FUTILITY}" show "${DEVKEYS}/firmware.keyblock"
 
-if ${FUTILITY} verify ${DEVKEYS}/firmware.keyblock ; then false; fi
+if "${FUTILITY}" verify "${DEVKEYS}/firmware.keyblock" ; then false; fi
 
-${FUTILITY} verify ${DEVKEYS}/firmware.keyblock \
-  --publickey ${DEVKEYS}/root_key.vbpubk
+"${FUTILITY}" verify "${DEVKEYS}/firmware.keyblock" \
+  --publickey "${DEVKEYS}/root_key.vbpubk"
 
 
 #### firmware vblock
 
 # Get some bits to look at
-${FUTILITY} dump_fmap -x ${SCRIPT_DIR}/futility/data/bios_peppy_mp.bin \
-  GBB:${TMP}.gbb VBLOCK_A:${TMP}.vblock_a FW_MAIN_A:${TMP}.fw_main_a
-${FUTILITY} gbb -g -k ${TMP}.rootkey ${TMP}.gbb
+"${FUTILITY}" dump_fmap -x "${SCRIPT_DIR}/futility/data/bios_peppy_mp.bin" \
+  "GBB:${TMP}.gbb" "VBLOCK_A:${TMP}.vblock_a" "FW_MAIN_A:${TMP}.fw_main_a"
+"${FUTILITY}" gbb -g -k "${TMP}.rootkey" "${TMP}.gbb"
 
 
-${FUTILITY} show ${TMP}.vblock_a
+"${FUTILITY}" show "${TMP}.vblock_a"
 
-${FUTILITY} show ${TMP}.vblock_a --publickey ${TMP}.rootkey
+"${FUTILITY}" show "${TMP}.vblock_a" --publickey "${TMP}.rootkey"
 
-${FUTILITY} show ${TMP}.vblock_a \
-  --publickey ${TMP}.rootkey \
-  --fv ${TMP}.fw_main_a
+"${FUTILITY}" show "${TMP}.vblock_a" \
+  --publickey "${TMP}.rootkey" \
+  --fv "${TMP}.fw_main_a"
 
-if ${FUTILITY} verify ${TMP}.vblock_a ; then false ; fi
+if "${FUTILITY}" verify "${TMP}.vblock_a" ; then false ; fi
 
-if ${FUTILITY} verify ${TMP}.vblock_a \
-  --publickey ${TMP}.rootkey ; then false ; fi
+if "${FUTILITY}" verify "${TMP}.vblock_a" \
+  --publickey "${TMP}.rootkey" ; then false ; fi
 
-${FUTILITY} verify ${TMP}.vblock_a \
-  --publickey ${TMP}.rootkey \
-  --fv ${TMP}.fw_main_a
+"${FUTILITY}" verify "${TMP}.vblock_a" \
+  --publickey "${TMP}.rootkey" \
+  --fv "${TMP}.fw_main_a"
 
 
 #### kernel partition
 
-${FUTILITY} show ${SCRIPT_DIR}/futility/data/rec_kernel_part.bin
+"${FUTILITY}" show "${SCRIPT_DIR}/futility/data/rec_kernel_part.bin"
 
-${FUTILITY} show ${SCRIPT_DIR}/futility/data/rec_kernel_part.bin \
-  --publickey ${DEVKEYS}/kernel_subkey.vbpubk
+"${FUTILITY}" show "${SCRIPT_DIR}/futility/data/rec_kernel_part.bin" \
+  --publickey "${DEVKEYS}/kernel_subkey.vbpubk"
 
-${FUTILITY} show ${SCRIPT_DIR}/futility/data/rec_kernel_part.bin \
-  --publickey ${DEVKEYS}/recovery_key.vbpubk
+"${FUTILITY}" show "${SCRIPT_DIR}/futility/data/rec_kernel_part.bin" \
+  --publickey "${DEVKEYS}/recovery_key.vbpubk"
 
-if ${FUTILITY} verify ${SCRIPT_DIR}/futility/data/rec_kernel_part.bin ; \
+if "${FUTILITY}" verify "${SCRIPT_DIR}/futility/data/rec_kernel_part.bin" ; \
   then false ; fi
 
-if ${FUTILITY} verify ${SCRIPT_DIR}/futility/data/rec_kernel_part.bin \
-  --publickey ${DEVKEYS}/kernel_subkey.vbpubk ; then false ; fi
+if "${FUTILITY}" verify "${SCRIPT_DIR}/futility/data/rec_kernel_part.bin" \
+  --publickey "${DEVKEYS}/kernel_subkey.vbpubk" ; then false ; fi
 
-${FUTILITY} verify ${SCRIPT_DIR}/futility/data/rec_kernel_part.bin \
-  --publickey ${DEVKEYS}/recovery_key.vbpubk
+"${FUTILITY}" verify "${SCRIPT_DIR}/futility/data/rec_kernel_part.bin" \
+  --publickey "${DEVKEYS}/recovery_key.vbpubk"
 
 
 # cleanup
-rm -rf ${TMP}*
+rm -rf "${TMP}"*
 exit 0
diff --git a/tests/futility/test_sign_firmware.sh b/tests/futility/test_sign_firmware.sh
index fa5529c..fe8462c 100755
--- a/tests/futility/test_sign_firmware.sh
+++ b/tests/futility/test_sign_firmware.sh
@@ -3,44 +3,98 @@
 # 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"
+me="${0##*/}"
+TMP="${me}.tmp"
 
 # Work in scratch directory
 cd "$OUTDIR"
 
-KEYDIR=${SRCDIR}/tests/devkeys
+KEYDIR="${SRCDIR}/tests/devkeys"
+DATADIR="${SCRIPT_DIR}/futility/data"
 
 # The input BIOS images are all signed with MP keys. We resign them with dev
 # keys, which means we can precalculate the expected results. Note that the
 # script does not change the root or recovery keys in the GBB.
 INFILES="
-${SCRIPT_DIR}/futility/data/bios_link_mp.bin
-${SCRIPT_DIR}/futility/data/bios_mario_mp.bin
-${SCRIPT_DIR}/futility/data/bios_peppy_mp.bin
+${DATADIR}/bios_link_mp.bin
+${DATADIR}/bios_peppy_mp.bin
+"
+
+# BIOS image containing CBFS RW/A and RW/B, and signed with developer keys.
+GOOD_CBFS="${DATADIR}/bios_voxel_dev.bin"
+
+# BIOS image containing CBFS RW/A and RW/B, and signed with developer keys.
+INFILES="${INFILES}
+${GOOD_CBFS}
 "
 
 # We also want to test that we can sign an image without any valid firmware
 # preambles. That one won't be able to tell how much of the FW_MAIN region is
 # the valid firmware, so it'll have to sign the entire region.
-GOOD_VBLOCKS=${SCRIPT_DIR}/futility/data/bios_peppy_mp.bin
+GOOD_VBLOCKS="${DATADIR}/bios_peppy_mp.bin"
 ONEMORE=bios_peppy_mp_no_vblock.bin
-cp ${GOOD_VBLOCKS} ${ONEMORE}
-${FUTILITY} load_fmap ${ONEMORE} VBLOCK_A:/dev/urandom VBLOCK_B:/dev/zero
+CLEAN_B=bios_peppy_mp_clean_b_slot.bin
+cp "${GOOD_VBLOCKS}" "${ONEMORE}"
+cp "${GOOD_VBLOCKS}" "${CLEAN_B}"
+
+GOOD_DEV="${DATADIR}/bios_peppy_dev.bin"
+
+NO_B_SLOT_PATCH="${DATADIR}/bios_voxel_dev.no_b_slot.xxd.patch"
+
+BAD_KEYBLOCK_PATCHES=(
+"${DATADIR}/bios_peppy_dev.bad_keyblock_data_key_offset_too_big.xxd.patch"
+"${DATADIR}/bios_peppy_dev.bad_keyblock_data_key_size_big.xxd.patch"
+"${DATADIR}/bios_peppy_dev.bad_keyblock_hash_data_size_too_small.xxd.patch"
+"${DATADIR}/bios_peppy_dev.bad_keyblock_hash_invalid_contents.xxd.patch"
+"${DATADIR}/bios_peppy_dev.bad_keyblock_hash_offset_too_big.xxd.patch"
+"${DATADIR}/bios_peppy_dev.bad_keyblock_hash_size_too_big.xxd.patch"
+"${DATADIR}/bios_peppy_dev.bad_keyblock_invalid_magic.xxd.patch"
+"${DATADIR}/bios_peppy_dev.bad_keyblock_invalid_major_version.xxd.patch"
+"${DATADIR}/bios_peppy_dev.bad_keyblock_size_not_fully_signed.xxd.patch"
+)
+
+BAD_PREAMBLE_PATCHES=(
+"${DATADIR}/bios_peppy_dev.bad_preamble_body_signature_offset_too_big.xxd.patch"
+"${DATADIR}/bios_peppy_dev.bad_preamble_body_signature_size_too_big.xxd.patch"
+"${DATADIR}/bios_peppy_dev.bad_preamble_header_version_major.xxd.patch"
+"${DATADIR}/bios_peppy_dev.bad_preamble_header_version_minor.xxd.patch"
+"${DATADIR}/bios_peppy_dev.bad_preamble_kernel_subkey_offset_too_big.xxd.patch"
+"${DATADIR}/bios_peppy_dev.bad_preamble_kernel_subkey_size_too_big.xxd.patch"
+"${DATADIR}/bios_peppy_dev.bad_preamble_signature_data_size_too_big.xxd.patch"
+"${DATADIR}/bios_peppy_dev.bad_preamble_signature_data_size_too_small.xxd.patch"
+"${DATADIR}/bios_peppy_dev.bad_preamble_signature_invalid_contents.xxd.patch"
+"${DATADIR}/bios_peppy_dev.bad_preamble_signature_offset_too_big.xxd.patch"
+"${DATADIR}/bios_peppy_dev.bad_preamble_signature_size_too_big.xxd.patch"
+)
+
+BAD_FMAP_KEYBLOCK_PATCHES=(
+"${DATADIR}/bios_peppy_dev.bad_keyblock_fmap_too_small_for_whole.xxd.patch"
+"${DATADIR}/bios_peppy_dev.bad_keyblock_fmap_too_small.xxd.patch"
+)
+
+BAD_FMAP_PREAMBLE_PATCHES=(
+"${DATADIR}/bios_peppy_dev.bad_preamble_fmap_too_small_for_whole.xxd.patch"
+"${DATADIR}/bios_peppy_dev.bad_preamble_fmap_too_small.xxd.patch"
+)
+
+"${FUTILITY}" load_fmap "${ONEMORE}" VBLOCK_A:/dev/urandom VBLOCK_B:/dev/zero
 INFILES="${INFILES} ${ONEMORE}"
 
-set -o pipefail
+# args: xxd_patch_file input_file
+function apply_xxd_patch {
+	xxd -r "${1}" "${2}"
+}
 
-# We've removed dev_firmware keyblock and private keys from ToT test key dir.
-# It's currently only available on few legacy (alex, zgb) devices' key folders
-# on signer bot. Add them to ${KEYDIR} if you need to test that.
-DEV_FIRMWARE_PARAMS=""
-if [ -f "${KEYDIR}/dev_firmware.keyblock" ]; then
-  DEV_FIRMWARE_PARAMS="
-    -S ${KEYDIR}/dev_firmware_data_key.vbprivk
-    -B ${KEYDIR}/dev_firmware.keyblock"
-  INFILES="${INFILES} ${SCRIPT_DIR}/futility/data/bios_zgb_mp.bin"
-fi
+# args: file1 file2
+function cmp_first_line {
+	cmp <(head -n1 "${1}") <(head -n1 "${2}")
+}
+
+function cmp_last_line {
+	cmp <(tail -n1 "${1}") <(tail -n1 "${2}")
+}
+
+set -o pipefail
 
 count=0
 for infile in $INFILES; do
@@ -48,140 +102,391 @@
   base=${infile##*/}
 
   : $(( count++ ))
-  echo -n "$count " 1>&3
+  echo -n "${count} " 1>&3
 
-  outfile=${TMP}.${base}.new
+  outfile="${TMP}.${base}.new"
   loemid="loem"
-  loemdir=${TMP}.${base}_dir
+  loemdir="${TMP}.${base}_dir"
 
-  mkdir -p ${loemdir}
+  mkdir -p "${loemdir}"
 
-  # resign_firmwarefd.sh works on BIOS image files. The args are:
-  #
-  #   infile
-  #   outfile
-  #   firmware_datakey
-  #   firmware_keyblock
-  #   dev_firmware_datakey   (these are only used if RW A & RW B differ)
-  #   dev_firmware_keyblock
-  #   kernel_subkey
-  #   firmware_version
-  #   preamble_flag
-  #   loem_output_dir        (optional: dir for copy of new vblocks)
-  #   loemid                 (optional: copy new vblocks using this name)
-  #
-  #OLD  ${BIN_DIR}/resign_firmwarefd.sh \
-  #OLD    ${infile} \
-  #OLD    ${outfile} \
-  #OLD    ${KEYDIR}/firmware_data_key.vbprivk \
-  #OLD    ${KEYDIR}/firmware.keyblock \
-  #OLD    ${KEYDIR}/dev_firmware_data_key.vbprivk \
-  #OLD    ${KEYDIR}/dev_firmware.keyblock \
-  #OLD    ${KEYDIR}/kernel_subkey.vbpubk \
-  #OLD    14 \
-  #OLD    8 \
-  #OLD    ${loemdir} \
-  #OLD    ${loemid}
-
-  ${FUTILITY} sign \
-    -s ${KEYDIR}/firmware_data_key.vbprivk \
-    -b ${KEYDIR}/firmware.keyblock \
-    ${DEV_FIRMWARE_PARAMS} \
-    -k ${KEYDIR}/kernel_subkey.vbpubk \
+  "${FUTILITY}" sign \
+    -K "${KEYDIR}" \
     -v 14 \
     -f 8 \
-    -d ${loemdir} \
-    -l ${loemid} \
-    ${infile} ${outfile}
+    -d "${loemdir}" \
+    -l "${loemid}" \
+    "${infile}" "${outfile}"
 
   # check the firmware version and preamble flags
-  m=$(${FUTILITY} verify --publickey ${KEYDIR}/root_key.vbpubk ${outfile} \
-    | egrep 'Firmware version: +14$|Preamble flags: +8$' | wc -l)
-  [ "$m" = "4" ]
+  m=$("${FUTILITY}" verify --publickey "${KEYDIR}/root_key.vbpubk" \
+        "${outfile}" | grep -c -E 'Firmware version: +14$|Preamble flags: +8$')
+  [ "${m}" = "4" ]
 
   # check the sha1sums
-  ${FUTILITY} verify --publickey ${KEYDIR}/root_key.vbpubk ${outfile} \
+  "${FUTILITY}" verify --publickey "${KEYDIR}/root_key.vbpubk" "${outfile}" \
     | grep sha1sum \
-    | sed -e 's/.*: \+//' > ${TMP}.${base}.sha.new
-  cmp ${SCRIPT_DIR}/futility/data_${base}_expect.txt ${TMP}.${base}.sha.new
+    | sed -e 's/.*: \+//' > "${TMP}.${base}.sha.new"
+  cmp "${SCRIPT_DIR}/futility/data_${base}_expect.txt" "${TMP}.${base}.sha.new"
 
    # and the LOEM stuff
-   ${FUTILITY} dump_fmap -x ${outfile} \
-     FW_MAIN_A:${loemdir}/fw_main_A FW_MAIN_B:${loemdir}/fw_main_B \
-     "Firmware A Data":${loemdir}/fw_main_A \
-     "Firmware B Data":${loemdir}/fw_main_B
+   "${FUTILITY}" dump_fmap -x "${outfile}" \
+     "FW_MAIN_A:${loemdir}/fw_main_A" "FW_MAIN_B:${loemdir}/fw_main_B"
 
-
-   ${FUTILITY} verify --publickey ${KEYDIR}/root_key.vbpubk \
-     --fv ${loemdir}/fw_main_A \
-     ${loemdir}/vblock_A.${loemid} | grep sha1sum \
-     | sed -e 's/.*: \+//' > ${loemdir}/loem.sha.new
-   ${FUTILITY} verify --publickey ${KEYDIR}/root_key.vbpubk \
-     --fv ${loemdir}/fw_main_B \
-     ${loemdir}/vblock_B.${loemid} | grep sha1sum \
-     | sed -e 's/.*: \+//' >> ${loemdir}/loem.sha.new
+   "${FUTILITY}" verify --publickey "${KEYDIR}/root_key.vbpubk" \
+     --fv "${loemdir}/fw_main_A" \
+     "${loemdir}/vblock_A.${loemid}" | grep sha1sum \
+     | sed -e 's/.*: \+//' > "${loemdir}/loem.sha.new"
+   "${FUTILITY}" verify --publickey "${KEYDIR}/root_key.vbpubk" \
+     --fv "${loemdir}/fw_main_B" \
+     "${loemdir}/vblock_B.${loemid}" | grep sha1sum \
+     | sed -e 's/.*: \+//' >> "${loemdir}/loem.sha.new"
 
   # the vblocks don't have root or recovery keys
-  tail -4 ${SCRIPT_DIR}/futility/data_${base}_expect.txt > ${loemdir}/sha.expect
-  cmp ${loemdir}/sha.expect ${loemdir}/loem.sha.new
+  tail -4 "${SCRIPT_DIR}/futility/data_${base}_expect.txt" \
+    > "${loemdir}/sha.expect"
+  cmp "${loemdir}/sha.expect" "${loemdir}/loem.sha.new"
 
 done
 
 # Make sure that the BIOS with the good vblocks signed the right size.
-GOOD_OUT=${TMP}.${GOOD_VBLOCKS##*/}.new
-MORE_OUT=${TMP}.${ONEMORE##*/}.new
+GOOD_OUT="${TMP}.${GOOD_VBLOCKS##*/}.new"
+MORE_OUT="${TMP}.${ONEMORE##*/}.new"
+GOOD_CBFS_OUT="${TMP}.${GOOD_CBFS##*/}.new"
 
-${FUTILITY} verify --publickey ${KEYDIR}/root_key.vbpubk ${GOOD_OUT} \
-  | awk '/Firmware body size:/ {print $4}' > ${TMP}.good.body
-${FUTILITY} dump_fmap -p ${GOOD_OUT} \
-  | awk '/FW_MAIN_/ {print $3}' > ${TMP}.good.fw_main
+"${FUTILITY}" verify --publickey "${KEYDIR}/root_key.vbpubk" "${GOOD_OUT}" \
+  | awk '/Firmware body size:/ {print $4}' > "${TMP}.good.body"
+"${FUTILITY}" dump_fmap -p "${GOOD_OUT}" \
+  | awk '/FW_MAIN_/ {print $3}' > "${TMP}.good.fw_main"
 # This should fail because they're different
-if cmp ${TMP}.good.body ${TMP}.good.fw_main; then false; fi
+if cmp "${TMP}.good.body" "${TMP}.good.fw_main"; then false; fi
 
 # Make sure that the BIOS with the bad vblocks signed the whole fw body
-${FUTILITY} verify --publickey ${KEYDIR}/root_key.vbpubk ${MORE_OUT} \
-  | awk '/Firmware body size:/ {print $4}' > ${TMP}.onemore.body
-${FUTILITY} dump_fmap -p ${MORE_OUT} \
-  | awk '/FW_MAIN_/ {print $3}' > ${TMP}.onemore.fw_main
+"${FUTILITY}" verify --publickey "${KEYDIR}/root_key.vbpubk" "${MORE_OUT}" \
+  | awk '/Firmware body size:/ {print $4}' > "${TMP}.onemore.body"
+"${FUTILITY}" dump_fmap -p "${MORE_OUT}" \
+  | awk '/FW_MAIN_/ {print $3}' > "${TMP}.onemore.fw_main"
 # These should match
-cmp ${TMP}.onemore.body ${TMP}.onemore.fw_main
-cmp ${TMP}.onemore.body ${TMP}.good.fw_main
+cmp "${TMP}.onemore.body" "${TMP}.onemore.fw_main"
+cmp "${TMP}.onemore.body" "${TMP}.good.fw_main"
+
+"${FUTILITY}" verify --publickey "${KEYDIR}/root_key.vbpubk" \
+    "${GOOD_CBFS_OUT}" \
+  | awk '/Firmware body size:/ {print $4}' > "${TMP}.good_cbfs.body"
+"${FUTILITY}" dump_fmap -p "${GOOD_CBFS_OUT}" \
+  | awk '/FW_MAIN_/ {print $3}' > "${TMP}.good_cbfs.fw_main"
+if cmp "${TMP}.good_cbfs.body" "${TMP}.good_cbfs.fw_main"; then false; fi
 
 
-# Sign the last one again but don't specify the version or the preamble flags.
-# The version should default to 1, but the preamble flags should be preserved.
+# Sign CBFS image after adding new files. Size should increase but still be
+# smaller than FlashMap size.
 : $(( count++ ))
-echo -n "$count " 1>&3
+echo -n "${count} " 1>&3
 
-${FUTILITY} sign \
-  -s ${KEYDIR}/firmware_data_key.vbprivk \
-  -b ${KEYDIR}/firmware.keyblock \
-  ${DEV_FIRMWARE_PARAMS} \
-  -k ${KEYDIR}/kernel_subkey.vbpubk \
-  ${MORE_OUT} ${MORE_OUT}.2
+cp "${GOOD_CBFS_OUT}" "${GOOD_CBFS_OUT}.1"
+truncate -s 512 "${TMP}.zero_512"
+cbfstool "${GOOD_CBFS_OUT}.1" expand -r FW_MAIN_A,FW_MAIN_B
+cbfstool "${GOOD_CBFS_OUT}.1" add \
+  -r FW_MAIN_A,FW_MAIN_B -f "${TMP}.zero_512" -n new-data-file -t raw
 
-m=$(${FUTILITY} verify --publickey ${KEYDIR}/root_key.vbpubk ${MORE_OUT}.2 \
-  | egrep 'Firmware version: +1$|Preamble flags: +8$' | wc -l)
-[ "$m" = "4" ]
+"${FUTILITY}" sign \
+  -s "${KEYDIR}/firmware_data_key.vbprivk" \
+  -K "${KEYDIR}" \
+  "${GOOD_CBFS_OUT}.1"
+
+"${FUTILITY}" verify --publickey "${KEYDIR}/root_key.vbpubk" \
+    "${GOOD_CBFS_OUT}.1" \
+  | awk '/Firmware body size:/ {print $4}' > "${TMP}.good_cbfs.1.body"
+"${FUTILITY}" dump_fmap -p "${GOOD_CBFS_OUT}" \
+  | awk '/FW_MAIN_/ {print $3}' > "${TMP}.good_cbfs.1.fw_main"
+
+# Check if size increased, but also if it was correctly truncated,
+# so it does not span over whole FlashMap area.
+[[ $(head -n1 "${TMP}.good_cbfs.body") \
+  < $(head -n1 "${TMP}.good_cbfs.1.body") ]]
+[[ $(tail -n1 "${TMP}.good_cbfs.body") \
+  < $(tail -n1 "${TMP}.good_cbfs.1.body") ]]
+[[ $(head -n1 "${TMP}.good_cbfs.1.body") \
+  < $(head -n1 "${TMP}.good_cbfs.1.fw_main") ]]
+[[ $(tail -n1 "${TMP}.good_cbfs.1.body") \
+  < $(tail -n1 "${TMP}.good_cbfs.1.fw_main") ]]
+
+
+# Sign image again but don't specify the version or the preamble flags.
+# The firmware version and preamble flags should be preserved.
+# NOTICE: Version preservation behavior changed from defaulting to 1.
+: $(( count++ ))
+echo -n "${count} " 1>&3
+
+"${FUTILITY}" sign \
+  -b "${KEYDIR}/firmware.keyblock" \
+  -K "${KEYDIR}" \
+  "${MORE_OUT}" "${MORE_OUT}.2"
+
+m=$("${FUTILITY}" verify --publickey "${KEYDIR}/root_key.vbpubk" \
+      "${MORE_OUT}.2" | grep -c -E 'Firmware version: +14$|Preamble flags: +8$')
+[ "${m}" = "4" ]
 
 
 # If the original preamble is not present, the preamble flags should be zero.
 : $(( count++ ))
-echo -n "$count " 1>&3
+echo -n "${count} " 1>&3
 
-${FUTILITY} load_fmap ${MORE_OUT} VBLOCK_A:/dev/urandom VBLOCK_B:/dev/zero
-${FUTILITY} sign \
-  -s ${KEYDIR}/firmware_data_key.vbprivk \
-  -b ${KEYDIR}/firmware.keyblock \
-  ${DEV_FIRMWARE_PARAMS} \
-  -k ${KEYDIR}/kernel_subkey.vbpubk \
-  ${MORE_OUT} ${MORE_OUT}.3
+"${FUTILITY}" load_fmap "${MORE_OUT}" VBLOCK_A:/dev/urandom VBLOCK_B:/dev/zero
+"${FUTILITY}" sign \
+  -k "${KEYDIR}/kernel_subkey.vbpubk" \
+  -K "${KEYDIR}" \
+  "${MORE_OUT}" "${MORE_OUT}.3"
 
-m=$(${FUTILITY} verify --publickey ${KEYDIR}/root_key.vbpubk ${MORE_OUT}.3 \
-  | egrep 'Firmware version: +1$|Preamble flags: +0$' | wc -l)
-[ "$m" = "4" ]
+m=$("${FUTILITY}" verify --publickey "${KEYDIR}/root_key.vbpubk" \
+      "${MORE_OUT}.3" | grep -c -E 'Firmware version: +1$|Preamble flags: +0$')
+[ "${m}" = "4" ]
+
+
+# Check signing when B slot is empty
+: $(( count++ ))
+echo -n "${count} " 1>&3
+
+"${FUTILITY}" load_fmap "${CLEAN_B}" VBLOCK_B:/dev/zero FW_MAIN_B:/dev/zero
+"${FUTILITY}" sign \
+  -s "${KEYDIR}/firmware_data_key.vbprivk" \
+  -b "${KEYDIR}/firmware.keyblock" \
+  -K "${KEYDIR}" \
+  "${CLEAN_B}" "${CLEAN_B}.1"
+
+"${FUTILITY}" verify --publickey "${KEYDIR}/root_key.vbpubk" "${CLEAN_B}.1" \
+  | awk '/Firmware body size:/ {print $4}' > "${TMP}.clean_b.body"
+"${FUTILITY}" dump_fmap -p "${CLEAN_B}.1" \
+  | awk '/FW_MAIN_/ {print $3}' > "${TMP}.clean_b.fw_main"
+
+# These should not be equal, as FW_MAIN_A size should be kept intact, when size
+# of FW_MAIN_B should be taken from FlashMap.
+if cmp "${TMP}.clean_b.body" "${TMP}.clean_b.fw_main" ; then false; fi
+if cmp "${TMP}.clean_b.body" "${TMP}.good.body" ; then false; fi
+cmp_first_line "${TMP}.clean_b.body" "${TMP}.good.body"
+cmp_last_line "${TMP}.clean_b.body" "${TMP}.clean_b.fw_main"
+
+# Version for slot A should be kept intact, while for B slot it should default
+# to 1. All flags should be zero.
+m=$("${FUTILITY}" verify --publickey "${KEYDIR}/root_key.vbpubk" \
+        "${CLEAN_B}.1" \
+      | grep -c -E \
+          'Firmware version: +1$|Preamble flags: +0$|Firmware version: +2$')
+[ "${m}" = "4" ]
+
+# Check signing when there is no B slot
+: $(( count++ ))
+echo -n "${count} " 1>&3
+
+NO_B_SLOT="${TMP}.${GOOD_CBFS##*/}.no_b_slot"
+NO_B_SLOT_SIGNED_IMG="${NO_B_SLOT}.signed"
+
+cp "${GOOD_CBFS}" "${NO_B_SLOT}"
+apply_xxd_patch "${NO_B_SLOT_PATCH}" "${NO_B_SLOT}"
+
+"${FUTILITY}" sign \
+  -s "${KEYDIR}/firmware_data_key.vbprivk" \
+  -k "${KEYDIR}/kernel_subkey.vbpubk" \
+  -K "${KEYDIR}" \
+  -v 1 \
+  "${NO_B_SLOT}" "${NO_B_SLOT_SIGNED_IMG}"
+
+"${FUTILITY}" verify --publickey "${KEYDIR}/root_key.vbpubk" \
+    "${NO_B_SLOT_SIGNED_IMG}" \
+  | awk '/Firmware body size:/ {print $4}' > "${TMP}.no_b_slot.body"
+"${FUTILITY}" dump_fmap -p "${NO_B_SLOT_SIGNED_IMG}" \
+  | awk '/FW_MAIN_/ {print $3}' > "${TMP}.no_b_slot.fw_main"
+
+if cmp "${TMP}.no_b_slot.body" "${TMP}.no_b_slot.fw_main" ; then false; fi
+cmp "${TMP}.no_b_slot.body" <(tail -n1 "${TMP}.good_cbfs.body")
+
+m=$("${FUTILITY}" verify --publickey "${KEYDIR}/root_key.vbpubk" \
+        "${NO_B_SLOT_SIGNED_IMG}" \
+      | grep -c -E 'Firmware version: +1$|Preamble flags: +0$')
+[ "${m}" = "2" ]
+
+# Check signing when cbfstool reports incorrect size
+# Signing should fail, as it should not be possible for CBFS contents to be
+# bigger than FlashMap size of the area
+: $(( count++ ))
+echo -n "${count} " 1>&3
+
+CBFSTOOL_STUB="$(realpath "${TMP}.cbfs_stub.sh")"
+echo -en 'echo "0xFFEEDD0"; exit 0;' > "${CBFSTOOL_STUB}"
+chmod +x "${CBFSTOOL_STUB}"
+
+if CBFSTOOL="${CBFSTOOL_STUB}" "${FUTILITY}" sign \
+  -b "${KEYDIR}/firmware.keyblock" \
+  -k "${KEYDIR}/kernel_subkey.vbpubk" \
+  -K "${KEYDIR}" \
+  -v 1 \
+  "${GOOD_CBFS}" "${TMP}.1.${GOOD_CBFS##*/}"
+then
+  false
+fi
+
+# Redefine cbfstool stub to return valid value for FW_MAIN_A and invalid for
+# FW_MAIN_B size. With this behavior futility should fail to sign this image,
+# as cbfstool should never return incorrect size (larger than area).
+cp "${GOOD_CBFS}" "${TMP}.good_cbfs.bin"
+FW_MAIN_A_SIZE="$(printf '0x%x' \
+  "$(cbfstool "${TMP}.good_cbfs.bin" truncate -r FW_MAIN_A)")"
+MARK_FILE="$(realpath "${TMP}.mark1")"
+rm -f "${MARK_FILE}"
+
+cat << EOF > "${CBFSTOOL_STUB}"
+#!/usr/bin/env bash
+if ! [ -f "${MARK_FILE}" ]; then
+  echo "${FW_MAIN_A_SIZE}";
+  echo 1 > "${MARK_FILE}";
+else
+  echo 0xFFFFAA0;
+fi
+exit 0;
+EOF
+
+if CBFSTOOL="${CBFSTOOL_STUB}" "${FUTILITY}" sign \
+  -K "${KEYDIR}" \
+  -v 1 \
+  "${GOOD_CBFS}" "${TMP}.2.${GOOD_CBFS##*/}"
+then
+  false
+fi
+
+
+# Check various incorrect values in VBLOCK (keyblock and preamble)
+: $(( count++ ))
+echo -n "${count} " 1>&3
+
+bad_counter=1
+for keyblock_patch in "${BAD_KEYBLOCK_PATCHES[@]}"; do
+  echo -n "${count}.${bad_counter} " 1>&3
+  BAD_IN="${TMP}.${GOOD_DEV##*/}.bad.${bad_counter}.in.bin"
+  BAD_OUT="${TMP}.${GOOD_DEV##*/}.bad.${bad_counter}.out.bin"
+  cp "${GOOD_DEV}" "${BAD_IN}"
+  apply_xxd_patch "${keyblock_patch}" "${BAD_IN}"
+
+  FUTIL_OUTPUT="$(if "${FUTILITY}" verify \
+                        --publickey "${KEYDIR}/root_key.vbpubk" "${BAD_IN}"; \
+                  then false; fi)"
+  grep -q 'VBLOCK_A keyblock component is invalid' <<< "${FUTIL_OUTPUT}"
+
+  FUTIL_OUTPUT="$("${FUTILITY}" sign \
+    -K "${KEYDIR}" \
+    "${BAD_IN}" "${BAD_OUT}" 2>&1)"
+   grep -q 'VBLOCK_A keyblock is invalid' <<< "${FUTIL_OUTPUT}"
+
+  "${FUTILITY}" verify --publickey "${KEYDIR}/root_key.vbpubk" "${BAD_OUT}" \
+    | awk '/Firmware body size:/ {print $4}' > "${BAD_OUT}.body"
+  "${FUTILITY}" dump_fmap -p "${BAD_OUT}" \
+    | awk '/FW_MAIN_/ {print $3}' > "${BAD_OUT}.fw_main"
+
+  cmp "${BAD_OUT}.fw_main" "${TMP}.good.fw_main"
+  cmp_first_line "${BAD_OUT}.body" "${TMP}.good.fw_main"
+  cmp_last_line "${BAD_OUT}.body" "${TMP}.good.body"
+
+  : $(( bad_counter++ ))
+done
+
+for vblock_patch in "${BAD_PREAMBLE_PATCHES[@]}"; do
+  echo -n "${count}.${bad_counter} " 1>&3
+  BAD_IN="${TMP}.${GOOD_DEV##*/}.bad.${bad_counter}.in.bin"
+  BAD_OUT="${TMP}.${GOOD_DEV##*/}.bad.${bad_counter}.out.bin"
+  cp "${GOOD_DEV}" "${BAD_IN}"
+  apply_xxd_patch "${vblock_patch}" "${BAD_IN}"
+
+  FUTIL_OUTPUT="$(if "${FUTILITY}" verify \
+                       --publickey "${KEYDIR}/root_key.vbpubk" "${BAD_IN}"; \
+                  then false; fi)"
+  grep -q 'VBLOCK_A is invalid' <<< "${FUTIL_OUTPUT}"
+
+  FUTIL_OUTPUT="$("${FUTILITY}" sign \
+    -K "${KEYDIR}" \
+    "${BAD_IN}" "${BAD_OUT}" 2>&1)"
+  grep -q 'VBLOCK_A preamble is invalid' <<< "${FUTIL_OUTPUT}"
+
+  "${FUTILITY}" verify --publickey "${KEYDIR}/root_key.vbpubk" "${BAD_OUT}" \
+    | awk '/Firmware body size:/ {print $4}' > "${BAD_OUT}.body"
+  "${FUTILITY}" dump_fmap -p "${BAD_OUT}" \
+    | awk '/FW_MAIN_/ {print $3}' > "${BAD_OUT}.fw_main"
+
+  cmp "${BAD_OUT}.fw_main" "${TMP}.good.fw_main"
+  cmp_first_line "${BAD_OUT}.body" "${TMP}.good.fw_main"
+  cmp_last_line "${BAD_OUT}.body" "${TMP}.good.body"
+
+  : $(( bad_counter++ ))
+done
+
+for vblock_patch in "${BAD_FMAP_KEYBLOCK_PATCHES[@]}"; do
+  echo -n "${count}.${bad_counter} " 1>&3
+  BAD_IN="${TMP}.${GOOD_DEV##*/}.bad.${bad_counter}.in.bin"
+  BAD_OUT="${TMP}.${GOOD_DEV##*/}.bad.${bad_counter}.out.bin"
+  cp "${GOOD_DEV}" "${BAD_IN}"
+  apply_xxd_patch "${vblock_patch}" "${BAD_IN}"
+
+  FUTIL_OUTPUT="$(if "${FUTILITY}" verify \
+                       --publickey "${KEYDIR}/root_key.vbpubk" "${BAD_IN}"; \
+                  then false; fi)"
+  grep -q 'VBLOCK_A keyblock component is invalid' <<< "${FUTIL_OUTPUT}"
+
+  FUTIL_OUTPUT="$(if "${FUTILITY}" sign \
+                       -K "${KEYDIR}" \
+                       "${BAD_IN}" "${BAD_OUT}" 2>&1; \
+                  then false; fi)"
+  m="$(grep -c -E \
+     'VBLOCK_A keyblock is invalid|Keyblock and preamble do not fit in VBLOCK' \
+     <<< "${FUTIL_OUTPUT}")"
+  [ "${m}" = "2" ]
+
+  : $(( bad_counter++ ))
+done
+
+echo -n "${count}.${bad_counter} " 1>&3
+BAD_IN="${TMP}.${GOOD_DEV##*/}.bad.${bad_counter}.in.bin"
+BAD_OUT="${TMP}.${GOOD_DEV##*/}.bad.${bad_counter}.out.bin"
+cp "${GOOD_DEV}" "${BAD_IN}"
+apply_xxd_patch "${BAD_FMAP_PREAMBLE_PATCHES[0]}" "${BAD_IN}"
+
+FUTIL_OUTPUT="$(if "${FUTILITY}" verify \
+                     --publickey "${KEYDIR}/root_key.vbpubk" "${BAD_IN}"; \
+                then false; fi)"
+grep -q 'VBLOCK_A is invalid' <<< "${FUTIL_OUTPUT}"
+
+FUTIL_OUTPUT="$(if "${FUTILITY}" sign \
+                     -K "${KEYDIR}" \
+                     "${BAD_IN}" "${BAD_OUT}" 2>&1; \
+                then false; fi)"
+m="$(grep -c -E \
+     'VBLOCK_A preamble is invalid|Keyblock and preamble do not fit in VBLOCK' \
+     <<< "${FUTIL_OUTPUT}")"
+[ "${m}" = "2" ]
+
+: $(( bad_counter++ ))
+
+echo -n "${count}.${bad_counter} " 1>&3
+BAD_IN="${TMP}.${GOOD_DEV##*/}.bad.${bad_counter}.in.bin"
+BAD_OUT="${TMP}.${GOOD_DEV##*/}.bad.${bad_counter}.out.bin"
+cp "${GOOD_DEV}" "${BAD_IN}"
+apply_xxd_patch "${BAD_FMAP_PREAMBLE_PATCHES[1]}" "${BAD_IN}"
+
+FUTIL_OUTPUT="$(if "${FUTILITY}" verify \
+                     --publickey "${KEYDIR}/root_key.vbpubk" "${BAD_IN}"; \
+                then false; fi)"
+grep -q 'VBLOCK_A is invalid' <<< "${FUTIL_OUTPUT}"
+
+FUTIL_OUTPUT="$(if "${FUTILITY}" sign \
+                     -K "${KEYDIR}" \
+                     "${BAD_IN}" "${BAD_OUT}" 2>&1; \
+                then false; fi)"
+m="$(grep -c -E \
+       -e 'VBLOCK_A is invalid\. Keyblock and preamble do not fit' \
+       -e 'Keyblock and preamble do not fit in VBLOCK' \
+       <<< "${FUTIL_OUTPUT}")"
+[ "${m}" = "2" ]
+
+: $(( bad_counter++ ))
 
 
 # cleanup
-rm -rf ${TMP}* ${ONEMORE}
+rm -rf "${TMP}"* "${ONEMORE}"
 exit 0
diff --git a/tests/futility/test_sign_fw_main.sh b/tests/futility/test_sign_fw_main.sh
index face801..e22f907 100755
--- a/tests/futility/test_sign_fw_main.sh
+++ b/tests/futility/test_sign_fw_main.sh
@@ -9,38 +9,36 @@
 # Work in scratch directory
 cd "$OUTDIR"
 
-KEYDIR=${SRCDIR}/tests/devkeys
+KEYDIR="${SRCDIR}/tests/devkeys"
 
 # create a firmware blob
-dd bs=1024 count=16 if=/dev/urandom of=${TMP}.fw_main
+dd bs=1024 count=16 if=/dev/urandom of="${TMP}.fw_main"
 
 # try the old way
-${FUTILITY} vbutil_firmware --vblock ${TMP}.vblock.old \
-  --keyblock ${KEYDIR}/firmware.keyblock \
-  --signprivate ${KEYDIR}/firmware_data_key.vbprivk \
+"${FUTILITY}" vbutil_firmware --vblock "${TMP}.vblock.old" \
+  --keyblock "${KEYDIR}/firmware.keyblock" \
+  --signprivate "${KEYDIR}/firmware_data_key.vbprivk" \
   --version 12 \
-  --fv ${TMP}.fw_main \
-  --kernelkey ${KEYDIR}/kernel_subkey.vbpubk \
+  --fv "${TMP}.fw_main" \
+  --kernelkey "${KEYDIR}/kernel_subkey.vbpubk" \
   --flags 42
 
 # verify
-${FUTILITY} vbutil_firmware --verify ${TMP}.vblock.old \
-  --signpubkey ${KEYDIR}/root_key.vbpubk \
-  --fv ${TMP}.fw_main
+"${FUTILITY}" vbutil_firmware --verify "${TMP}.vblock.old" \
+  --signpubkey "${KEYDIR}/root_key.vbpubk" \
+  --fv "${TMP}.fw_main"
 
 # and the new way
-${FUTILITY} --debug sign \
-  --signprivate ${KEYDIR}/firmware_data_key.vbprivk \
-  --keyblock ${KEYDIR}/firmware.keyblock \
-  --kernelkey ${KEYDIR}/kernel_subkey.vbpubk \
+"${FUTILITY}" --debug sign \
+  --keyset "${KEYDIR}" \
   --version 12 \
-  --fv ${TMP}.fw_main \
+  --fv "${TMP}.fw_main" \
   --flags 42 \
-  ${TMP}.vblock.new
+  "${TMP}.vblock.new"
 
 # They should match
-cmp ${TMP}.vblock.old ${TMP}.vblock.new
+cmp "${TMP}.vblock.old" "${TMP}.vblock.new"
 
 # cleanup
-rm -rf ${TMP}*
+rm -rf "${TMP}"*
 exit 0
diff --git a/tests/futility/test_sign_kernel.sh b/tests/futility/test_sign_kernel.sh
index d252745..bba1164 100755
--- a/tests/futility/test_sign_kernel.sh
+++ b/tests/futility/test_sign_kernel.sh
@@ -11,11 +11,11 @@
 
 DEVKEYS=${SRCDIR}/tests/devkeys
 
-echo "hi there" > ${TMP}.config.txt
-echo "hello boys" > ${TMP}.config2.txt
-dd if=/dev/urandom bs=512 count=1 of=${TMP}.bootloader.bin
-dd if=/dev/urandom bs=512 count=1 of=${TMP}.bootloader2.bin
-dd if=/dev/urandom bs=1M count=16 of=${TMP}.kern_partition
+echo "hi there" > "${TMP}.config.txt"
+echo "hello boys" > "${TMP}.config2.txt"
+dd if=/dev/urandom bs=512 count=1 of="${TMP}.bootloader.bin"
+dd if=/dev/urandom bs=512 count=1 of="${TMP}.bootloader2.bin"
+dd if=/dev/urandom bs=1M count=16 of="${TMP}.kern_partition"
 
 # default padding
 padding=49152
@@ -26,273 +26,278 @@
   echo -n "${arch}: 1 " 1>&3
 
   # pack it up the old way
-  ${FUTILITY} --debug vbutil_kernel \
-    --pack ${TMP}.blob1.${arch} \
-    --keyblock ${DEVKEYS}/recovery_kernel.keyblock \
-    --signprivate ${DEVKEYS}/recovery_kernel_data_key.vbprivk \
+  "${FUTILITY}" --debug vbutil_kernel \
+    --pack "${TMP}.blob1.${arch}" \
+    --keyblock "${DEVKEYS}/recovery_kernel.keyblock" \
+    --signprivate "${DEVKEYS}/recovery_kernel_data_key.vbprivk" \
     --version 1 \
-    --config ${TMP}.config.txt \
-    --bootloader ${TMP}.bootloader.bin \
-    --vmlinuz ${SCRIPT_DIR}/futility/data/vmlinuz-${arch}.bin \
-    --arch ${arch} \
-    --pad ${padding} \
+    --config "${TMP}.config.txt" \
+    --bootloader "${TMP}.bootloader.bin" \
+    --vmlinuz "${SCRIPT_DIR}/futility/data/vmlinuz-${arch}.bin" \
+    --arch "${arch}" \
+    --pad "${padding}" \
     --kloadaddr 0x11000
 
   # verify the old way
-  ${FUTILITY} vbutil_kernel --verify ${TMP}.blob1.${arch} \
-    --pad ${padding} \
-    --signpubkey ${DEVKEYS}/recovery_key.vbpubk > ${TMP}.verify1
+  "${FUTILITY}" vbutil_kernel --verify "${TMP}.blob1.${arch}" \
+    --pad "${padding}" \
+    --signpubkey "${DEVKEYS}/recovery_key.vbpubk" > "${TMP}.verify1"
 
   # pack it up the new way
-  ${FUTILITY} --debug sign \
-    --keyblock ${DEVKEYS}/recovery_kernel.keyblock \
-    --signprivate ${DEVKEYS}/recovery_kernel_data_key.vbprivk \
+  "${FUTILITY}" --debug sign \
+    --keyset "${DEVKEYS}/recovery_" \
     --version 1 \
-    --config ${TMP}.config.txt \
-    --bootloader ${TMP}.bootloader.bin \
-    --vmlinuz ${SCRIPT_DIR}/futility/data/vmlinuz-${arch}.bin \
-    --arch ${arch} \
-    --pad ${padding} \
+    --config "${TMP}.config.txt" \
+    --bootloader "${TMP}.bootloader.bin" \
+    --vmlinuz "${SCRIPT_DIR}/futility/data/vmlinuz-${arch}.bin" \
+    --arch "${arch}" \
+    --pad "${padding}" \
     --kloadaddr 0x11000 \
-    --outfile ${TMP}.blob2.${arch}
+    --outfile "${TMP}.blob2.${arch}"
 
-  ${FUTILITY} vbutil_kernel --verify ${TMP}.blob2.${arch} \
-    --pad ${padding} \
-    --signpubkey ${DEVKEYS}/recovery_key.vbpubk > ${TMP}.verify2
+  "${FUTILITY}" vbutil_kernel --verify "${TMP}.blob2.${arch}" \
+    --pad "${padding}" \
+    --signpubkey "${DEVKEYS}/recovery_key.vbpubk" > "${TMP}.verify2"
 
   # they should be identical
-  cmp ${TMP}.blob1.${arch} ${TMP}.blob2.${arch}
-  diff ${TMP}.verify1 ${TMP}.verify2
+  cmp "${TMP}.blob1.${arch}" "${TMP}.blob2.${arch}"
+  diff "${TMP}.verify1" "${TMP}.verify2"
 
   echo -n "2 " 1>&3
 
   # repack it the old way
-  ${FUTILITY} --debug vbutil_kernel \
-    --repack ${TMP}.blob3.${arch} \
-    --oldblob ${TMP}.blob1.${arch} \
-    --signprivate ${DEVKEYS}/kernel_data_key.vbprivk \
-    --keyblock ${DEVKEYS}/kernel.keyblock \
+  "${FUTILITY}" --debug vbutil_kernel \
+    --repack "${TMP}.blob3.${arch}" \
+    --oldblob "${TMP}.blob1.${arch}" \
+    --signprivate "${DEVKEYS}/kernel_data_key.vbprivk" \
+    --keyblock "${DEVKEYS}/kernel.keyblock" \
     --version 2 \
-    --pad ${padding} \
-    --config ${TMP}.config2.txt \
-    --bootloader ${TMP}.bootloader2.bin
+    --pad "${padding}" \
+    --config "${TMP}.config2.txt" \
+    --bootloader "${TMP}.bootloader2.bin"
 
   # verify the old way
-  ${FUTILITY} vbutil_kernel --verify ${TMP}.blob3.${arch} \
-    --pad ${padding} \
-    --signpubkey ${DEVKEYS}/kernel_subkey.vbpubk > ${TMP}.verify3
+  "${FUTILITY}" vbutil_kernel --verify "${TMP}.blob3.${arch}" \
+    --pad "${padding}" \
+    --signpubkey "${DEVKEYS}/kernel_subkey.vbpubk" > "${TMP}.verify3"
 
   # repack it the new way
-  ${FUTILITY} --debug sign \
-    --signprivate ${DEVKEYS}/kernel_data_key.vbprivk \
-    --keyblock ${DEVKEYS}/kernel.keyblock \
+  "${FUTILITY}" --debug sign \
+    --keyset "${DEVKEYS}" \
+    --keyblock "${DEVKEYS}/kernel.keyblock" \
     --version 2 \
-    --pad ${padding} \
-    --config ${TMP}.config2.txt \
-    --bootloader ${TMP}.bootloader2.bin \
-    ${TMP}.blob2.${arch} \
-    ${TMP}.blob4.${arch}
+    --pad "${padding}" \
+    --config "${TMP}.config2.txt" \
+    --bootloader "${TMP}.bootloader2.bin" \
+    "${TMP}.blob2.${arch}" \
+    "${TMP}.blob4.${arch}"
 
-  ${FUTILITY} vbutil_kernel --verify ${TMP}.blob4.${arch} \
-    --pad ${padding} \
-    --signpubkey ${DEVKEYS}/kernel_subkey.vbpubk > ${TMP}.verify4
+  "${FUTILITY}" vbutil_kernel --verify "${TMP}.blob4.${arch}" \
+    --pad "${padding}" \
+    --signpubkey "${DEVKEYS}/kernel_subkey.vbpubk" > "${TMP}.verify4"
 
   # they should be identical
-  cmp ${TMP}.blob3.${arch} ${TMP}.blob4.${arch}
-  diff ${TMP}.verify3 ${TMP}.verify4
+  cmp "${TMP}.blob3.${arch}" "${TMP}.blob4.${arch}"
+  diff "${TMP}.verify3" "${TMP}.verify4"
 
   echo -n "3 " 1>&3
 
   # repack it the new way, in-place
-  cp ${TMP}.blob2.${arch} ${TMP}.blob5.${arch}
-  ${FUTILITY} --debug sign \
-    --signprivate ${DEVKEYS}/kernel_data_key.vbprivk \
-    --keyblock ${DEVKEYS}/kernel.keyblock \
+  cp "${TMP}.blob2.${arch}" "${TMP}.blob5.${arch}"
+  "${FUTILITY}" --debug sign \
+    --signprivate "${DEVKEYS}/kernel_data_key.vbprivk" \
+    --keyblock "${DEVKEYS}/kernel.keyblock" \
     --version 2 \
-    --pad ${padding} \
-    --config ${TMP}.config2.txt \
-    --bootloader ${TMP}.bootloader2.bin \
-    ${TMP}.blob5.${arch}
+    --pad "${padding}" \
+    --config "${TMP}.config2.txt" \
+    --bootloader "${TMP}.bootloader2.bin" \
+    "${TMP}.blob5.${arch}"
 
-  ${FUTILITY} vbutil_kernel --verify ${TMP}.blob5.${arch} \
-    --pad ${padding} \
-    --signpubkey ${DEVKEYS}/kernel_subkey.vbpubk > ${TMP}.verify5
+  "${FUTILITY}" vbutil_kernel --verify "${TMP}.blob5.${arch}" \
+    --pad "${padding}" \
+    --signpubkey "${DEVKEYS}/kernel_subkey.vbpubk" > "${TMP}.verify5"
 
   # they should be identical
-  cmp ${TMP}.blob3.${arch} ${TMP}.blob5.${arch}
-  diff ${TMP}.verify3 ${TMP}.verify5
+  cmp "${TMP}.blob3.${arch}" "${TMP}.blob5.${arch}"
+  diff "${TMP}.verify3" "${TMP}.verify5"
 
   # and now just the vblocks...
   echo -n "4 " 1>&3
 
   # pack the old way
-  ${FUTILITY} vbutil_kernel \
-    --pack ${TMP}.blob1.${arch}.vb1 \
+  "${FUTILITY}" vbutil_kernel \
+    --pack "${TMP}.blob1.${arch}.vb1" \
     --vblockonly \
-    --keyblock ${DEVKEYS}/recovery_kernel.keyblock \
-    --signprivate ${DEVKEYS}/recovery_kernel_data_key.vbprivk \
+    --keyblock "${DEVKEYS}/recovery_kernel.keyblock" \
+    --signprivate "${DEVKEYS}/recovery_kernel_data_key.vbprivk" \
     --version 1 \
-    --config ${TMP}.config.txt \
-    --bootloader ${TMP}.bootloader.bin \
-    --vmlinuz ${SCRIPT_DIR}/futility/data/vmlinuz-${arch}.bin \
-    --arch ${arch} \
-    --pad ${padding} \
+    --config "${TMP}.config.txt" \
+    --bootloader "${TMP}.bootloader.bin" \
+    --vmlinuz "${SCRIPT_DIR}/futility/data/vmlinuz-${arch}.bin" \
+    --arch "${arch}" \
+    --pad "${padding}" \
     --kloadaddr 0x11000
 
   # compare this new vblock with the one from the full pack
-  dd bs=${padding} count=1 if=${TMP}.blob1.${arch} of=${TMP}.blob1.${arch}.vb0
-  cmp ${TMP}.blob1.${arch}.vb0 ${TMP}.blob1.${arch}.vb1
+  dd bs="${padding}" count=1 if="${TMP}.blob1.${arch}" \
+     of="${TMP}.blob1.${arch}.vb0"
+  cmp "${TMP}.blob1.${arch}.vb0" "${TMP}.blob1.${arch}.vb1"
 
   # pack the new way
-  ${FUTILITY} --debug sign \
-    --keyblock ${DEVKEYS}/recovery_kernel.keyblock \
-    --signprivate ${DEVKEYS}/recovery_kernel_data_key.vbprivk \
+  "${FUTILITY}" --debug sign \
+    --keyblock "${DEVKEYS}/recovery_kernel.keyblock" \
+    --signprivate "${DEVKEYS}/recovery_kernel_data_key.vbprivk" \
     --version 1 \
-    --config ${TMP}.config.txt \
-    --bootloader ${TMP}.bootloader.bin \
-    --vmlinuz ${SCRIPT_DIR}/futility/data/vmlinuz-${arch}.bin \
-    --arch ${arch} \
-    --pad ${padding} \
+    --config "${TMP}.config.txt" \
+    --bootloader "${TMP}.bootloader.bin" \
+    --vmlinuz "${SCRIPT_DIR}/futility/data/vmlinuz-${arch}.bin" \
+    --arch "${arch}" \
+    --pad "${padding}" \
     --kloadaddr 0x11000 \
     --vblockonly \
-    ${TMP}.blob2.${arch}.vb1
+    "${TMP}.blob2.${arch}.vb1"
 
   # compare this new vblock with the one from the full pack
-  dd bs=${padding} count=1 if=${TMP}.blob2.${arch} of=${TMP}.blob2.${arch}.vb0
-  cmp ${TMP}.blob2.${arch}.vb0 ${TMP}.blob2.${arch}.vb1
+  dd bs="${padding}" count=1 if="${TMP}.blob2.${arch}" \
+     of="${TMP}.blob2.${arch}.vb0"
+  cmp "${TMP}.blob2.${arch}.vb0" "${TMP}.blob2.${arch}.vb1"
 
   echo -n "5 " 1>&3
 
   # now repack the old way, again emitting just the vblock
-  ${FUTILITY} vbutil_kernel \
-    --repack ${TMP}.blob3.${arch}.vb1 \
+  "${FUTILITY}" vbutil_kernel \
+    --repack "${TMP}.blob3.${arch}.vb1" \
     --vblockonly \
-    --oldblob ${TMP}.blob1.${arch} \
-    --signprivate ${DEVKEYS}/kernel_data_key.vbprivk \
-    --keyblock ${DEVKEYS}/kernel.keyblock \
+    --oldblob "${TMP}.blob1.${arch}" \
+    --signprivate "${DEVKEYS}/kernel_data_key.vbprivk" \
+    --keyblock "${DEVKEYS}/kernel.keyblock" \
     --version 2 \
-    --pad ${padding} \
-    --config ${TMP}.config2.txt \
-    --bootloader ${TMP}.bootloader2.bin
+    --pad "${padding}" \
+    --config "${TMP}.config2.txt" \
+    --bootloader "${TMP}.bootloader2.bin"
 
   # compare the full repacked vblock with the new repacked vblock
-  dd bs=${padding} count=1 if=${TMP}.blob3.${arch} of=${TMP}.blob3.${arch}.vb0
-  cmp ${TMP}.blob3.${arch}.vb0 ${TMP}.blob3.${arch}.vb1
+  dd bs="${padding}" count=1 if="${TMP}.blob3.${arch}" \
+     of="${TMP}.blob3.${arch}.vb0"
+  cmp "${TMP}.blob3.${arch}.vb0" "${TMP}.blob3.${arch}.vb1"
 
   # extract just the kernel blob
-  dd bs=${padding} skip=1 if=${TMP}.blob3.${arch} of=${TMP}.blob3.${arch}.kb0
+  dd bs="${padding}" skip=1 if="${TMP}.blob3.${arch}" \
+     of="${TMP}.blob3.${arch}.kb0"
   # and verify it using the new vblock (no way to do that with vbutil_kernel)
-  ${FUTILITY} --debug verify \
-    --pad ${padding} \
-    --publickey ${DEVKEYS}/kernel_subkey.vbpubk \
-    --fv ${TMP}.blob3.${arch}.kb0 \
-    ${TMP}.blob3.${arch}.vb1 > ${TMP}.verify3v
+  "${FUTILITY}" --debug verify \
+    --pad "${padding}" \
+    --publickey "${DEVKEYS}/kernel_subkey.vbpubk" \
+    --fv "${TMP}.blob3.${arch}.kb0" \
+    "${TMP}.blob3.${arch}.vb1" > "${TMP}.verify3v"
 
   # repack the new way
-  ${FUTILITY} --debug sign \
-    --signprivate ${DEVKEYS}/kernel_data_key.vbprivk \
-    --keyblock ${DEVKEYS}/kernel.keyblock \
+  "${FUTILITY}" --debug sign \
+    --signprivate "${DEVKEYS}/kernel_data_key.vbprivk" \
+    --keyblock "${DEVKEYS}/kernel.keyblock" \
     --version 2 \
-    --config ${TMP}.config2.txt \
-    --bootloader ${TMP}.bootloader2.bin \
-    --pad ${padding} \
+    --config "${TMP}.config2.txt" \
+    --bootloader "${TMP}.bootloader2.bin" \
+    --pad "${padding}" \
     --vblockonly \
-    ${TMP}.blob2.${arch} \
-    ${TMP}.blob4.${arch}.vb1 \
+    "${TMP}.blob2.${arch}" \
+    "${TMP}.blob4.${arch}.vb1" \
 
   # compare the full repacked vblock with the new repacked vblock
-  dd bs=${padding} count=1 if=${TMP}.blob4.${arch} of=${TMP}.blob4.${arch}.vb0
-  cmp ${TMP}.blob4.${arch}.vb0 ${TMP}.blob4.${arch}.vb1
+  dd bs="${padding}" count=1 if="${TMP}.blob4.${arch}" \
+     of="${TMP}.blob4.${arch}.vb0"
+  cmp "${TMP}.blob4.${arch}.vb0" "${TMP}.blob4.${arch}.vb1"
 
   # extract just the kernel blob
-  dd bs=${padding} skip=1 if=${TMP}.blob4.${arch} of=${TMP}.blob4.${arch}.kb0
+  dd bs="${padding}" skip=1 if="${TMP}.blob4.${arch}" \
+     of="${TMP}.blob4.${arch}.kb0"
   # and verify it using the new vblock (no way to do that with vbutil_kernel)
-  ${FUTILITY} --debug verify \
-    --pad ${padding} \
-    --publickey ${DEVKEYS}/kernel_subkey.vbpubk \
-    --fv ${TMP}.blob4.${arch}.kb0 \
-    ${TMP}.blob4.${arch}.vb1 > ${TMP}.verify4v
+  "${FUTILITY}" --debug verify \
+    --pad "${padding}" \
+    --publickey "${DEVKEYS}/kernel_subkey.vbpubk" \
+    --fv "${TMP}.blob4.${arch}.kb0" \
+    "${TMP}.blob4.${arch}.vb1" > "${TMP}.verify4v"
 
 
   echo -n "6 " 1>&3
 
   # Now lets repack some kernel partitions, not just blobs.
-  cp ${TMP}.kern_partition ${TMP}.part1.${arch}
-  dd if=${TMP}.blob1.${arch} of=${TMP}.part1.${arch} conv=notrunc
+  cp "${TMP}.kern_partition" "${TMP}.part1.${arch}"
+  dd if="${TMP}.blob1.${arch}" of="${TMP}.part1.${arch}" conv=notrunc
 
   # Make sure the partitions verify
-  ${FUTILITY} vbutil_kernel --verify ${TMP}.part1.${arch} \
-    --pad ${padding} \
-    --signpubkey ${DEVKEYS}/recovery_key.vbpubk > ${TMP}.verify6
+  "${FUTILITY}" vbutil_kernel --verify "${TMP}.part1.${arch}" \
+    --pad "${padding}" \
+    --signpubkey "${DEVKEYS}/recovery_key.vbpubk" > "${TMP}.verify6"
 
   # The partition should verify the same way as the blob
-  diff ${TMP}.verify1 ${TMP}.verify6
+  diff "${TMP}.verify1" "${TMP}.verify6"
 
   # repack it the old way
-  ${FUTILITY} --debug vbutil_kernel \
-    --repack ${TMP}.part6.${arch} \
-    --oldblob ${TMP}.part1.${arch} \
-    --signprivate ${DEVKEYS}/kernel_data_key.vbprivk \
-    --keyblock ${DEVKEYS}/kernel.keyblock \
+  "${FUTILITY}" --debug vbutil_kernel \
+    --repack "${TMP}.part6.${arch}" \
+    --oldblob "${TMP}.part1.${arch}" \
+    --signprivate "${DEVKEYS}/kernel_data_key.vbprivk" \
+    --keyblock "${DEVKEYS}/kernel.keyblock" \
     --version 2 \
-    --pad ${padding} \
-    --config ${TMP}.config2.txt \
-    --bootloader ${TMP}.bootloader2.bin
+    --pad "${padding}" \
+    --config "${TMP}.config2.txt" \
+    --bootloader "${TMP}.bootloader2.bin"
 
   # verify the old way
-  ${FUTILITY} vbutil_kernel --verify ${TMP}.part6.${arch} \
-    --pad ${padding} \
-    --signpubkey ${DEVKEYS}/kernel_subkey.vbpubk > ${TMP}.verify6.old
+  "${FUTILITY}" vbutil_kernel --verify "${TMP}.part6.${arch}" \
+    --pad "${padding}" \
+    --signpubkey "${DEVKEYS}/kernel_subkey.vbpubk" > "${TMP}.verify6.old"
 
   # this "partition" should actually be the same as the old-way blob
-  cmp ${TMP}.blob3.${arch} ${TMP}.part6.${arch}
+  cmp "${TMP}.blob3.${arch}" "${TMP}.part6.${arch}"
 
   # repack it the new way, in-place
-  cp ${TMP}.part1.${arch} ${TMP}.part6.${arch}.new1
-  ${FUTILITY} --debug sign \
-    --signprivate ${DEVKEYS}/kernel_data_key.vbprivk \
-    --keyblock ${DEVKEYS}/kernel.keyblock \
+  cp "${TMP}.part1.${arch}" "${TMP}.part6.${arch}.new1"
+  "${FUTILITY}" --debug sign \
+    --signprivate "${DEVKEYS}/kernel_data_key.vbprivk" \
+    --keyblock "${DEVKEYS}/kernel.keyblock" \
     --version 2 \
-    --pad ${padding} \
-    --config ${TMP}.config2.txt \
-    --bootloader ${TMP}.bootloader2.bin \
-    ${TMP}.part6.${arch}.new1
+    --pad "${padding}" \
+    --config "${TMP}.config2.txt" \
+    --bootloader "${TMP}.bootloader2.bin" \
+    "${TMP}.part6.${arch}.new1"
 
-  ${FUTILITY} vbutil_kernel --verify ${TMP}.part6.${arch}.new1 \
-    --pad ${padding} \
-    --signpubkey ${DEVKEYS}/kernel_subkey.vbpubk > ${TMP}.verify6.new1
+  "${FUTILITY}" vbutil_kernel --verify "${TMP}.part6.${arch}.new1" \
+    --pad "${padding}" \
+    --signpubkey "${DEVKEYS}/kernel_subkey.vbpubk" > "${TMP}.verify6.new1"
 
   # The verification should be indentical
-  diff ${TMP}.verify6.old ${TMP}.verify6.new1
+  diff "${TMP}.verify6.old" "${TMP}.verify6.new1"
   # But the content should only match up to the size of the kernel blob, since
   # we're modifying an entire partition in-place.
-  blobsize=$(stat -c '%s' ${TMP}.part6.${arch})
-  cmp -n ${blobsize} ${TMP}.part6.${arch} ${TMP}.part6.${arch}.new1
+  blobsize=$(stat -c '%s' "${TMP}.part6.${arch}")
+  cmp -n "${blobsize}" "${TMP}.part6.${arch}" "${TMP}.part6.${arch}.new1"
   # The rest of the partition should be unchanged.
-  cmp -i ${blobsize} ${TMP}.part1.${arch} ${TMP}.part6.${arch}.new1
+  cmp -i "${blobsize}" "${TMP}.part1.${arch}" "${TMP}.part6.${arch}.new1"
 
   # repack it the new way, from input to output
-  cp ${TMP}.part1.${arch} ${TMP}.part1.${arch}.in
-  ${FUTILITY} --debug sign \
-    --signprivate ${DEVKEYS}/kernel_data_key.vbprivk \
-    --keyblock ${DEVKEYS}/kernel.keyblock \
+  cp "${TMP}.part1.${arch}" "${TMP}.part1.${arch}.in"
+  "${FUTILITY}" --debug sign \
+    --signprivate "${DEVKEYS}/kernel_data_key.vbprivk" \
+    --keyblock "${DEVKEYS}/kernel.keyblock" \
     --version 2 \
-    --pad ${padding} \
-    --config ${TMP}.config2.txt \
-    --bootloader ${TMP}.bootloader2.bin \
-    ${TMP}.part1.${arch}.in \
-    ${TMP}.part6.${arch}.new2
+    --pad "${padding}" \
+    --config "${TMP}.config2.txt" \
+    --bootloader "${TMP}.bootloader2.bin" \
+    "${TMP}.part1.${arch}.in" \
+    "${TMP}.part6.${arch}.new2"
 
-  ${FUTILITY} vbutil_kernel --verify ${TMP}.part6.${arch}.new2 \
-    --pad ${padding} \
-    --signpubkey ${DEVKEYS}/kernel_subkey.vbpubk > ${TMP}.verify6.new2
+  "${FUTILITY}" vbutil_kernel --verify "${TMP}.part6.${arch}.new2" \
+    --pad "${padding}" \
+    --signpubkey "${DEVKEYS}/kernel_subkey.vbpubk" > "${TMP}.verify6.new2"
 
   # The input file should not have changed (just being sure).
-  cmp ${TMP}.part1.${arch} ${TMP}.part1.${arch}.in
+  cmp "${TMP}.part1.${arch}" "${TMP}.part1.${arch}.in"
   # The verification should be indentical
-  diff ${TMP}.verify6.old ${TMP}.verify6.new2
+  diff "${TMP}.verify6.old" "${TMP}.verify6.new2"
   # And creating a new output file should only emit a blob's worth
-  cmp ${TMP}.part6.${arch} ${TMP}.part6.${arch}.new2
+  cmp "${TMP}.part6.${arch}" "${TMP}.part6.${arch}.new2"
 
   # Note: We specifically do not test repacking with a different --kloadaddr,
   # because the old way has a bug and does not update params->cmd_line_ptr to
@@ -304,5 +309,5 @@
 try_arch arm
 
 # cleanup
-rm -rf ${TMP}*
+rm -rf "${TMP}"*
 exit 0
diff --git a/tests/futility/test_sign_keyblocks.sh b/tests/futility/test_sign_keyblocks.sh
index f689c89..75d367d 100755
--- a/tests/futility/test_sign_keyblocks.sh
+++ b/tests/futility/test_sign_keyblocks.sh
@@ -16,95 +16,95 @@
 
 
 # Create a copy of an existing keyblock, using the old way
-${FUTILITY} vbutil_keyblock --pack ${TMP}.keyblock0 \
-  --datapubkey ${DEVKEYS}/firmware_data_key.vbpubk \
+"${FUTILITY}" vbutil_keyblock --pack "${TMP}.keyblock0" \
+  --datapubkey "${DEVKEYS}/firmware_data_key.vbpubk" \
   --flags 23 \
-  --signprivate ${DEVKEYS}/root_key.vbprivk
+  --signprivate "${DEVKEYS}/root_key.vbprivk"
 
 # Check it.
-${FUTILITY} vbutil_keyblock --unpack ${TMP}.keyblock0 \
-  --signpubkey ${DEVKEYS}/root_key.vbpubk
+"${FUTILITY}" vbutil_keyblock --unpack "${TMP}.keyblock0" \
+  --signpubkey "${DEVKEYS}/root_key.vbpubk"
 
 # It should be the same as the dev-key firmware keyblock
-cmp ${DEVKEYS}/firmware.keyblock ${TMP}.keyblock0
+cmp "${DEVKEYS}/firmware.keyblock" "${TMP}.keyblock0"
 
 
 # Now create it the new way
-${FUTILITY} --debug sign \
-  --datapubkey ${DEVKEYS}/firmware_data_key.vbpubk \
+"${FUTILITY}" --debug sign \
+  --datapubkey "${DEVKEYS}/firmware_data_key.vbpubk" \
   --flags 23 \
-  --signprivate ${DEVKEYS}/root_key.vbprivk \
-  --outfile ${TMP}.keyblock1
+  --signprivate "${DEVKEYS}/root_key.vbprivk" \
+  --outfile "${TMP}.keyblock1"
 
 # It should be the same too.
-cmp ${DEVKEYS}/firmware.keyblock ${TMP}.keyblock1
+cmp "${DEVKEYS}/firmware.keyblock" "${TMP}.keyblock1"
 
 
 # Create a keyblock without signing it.
 
 # old way
-${FUTILITY} vbutil_keyblock --pack ${TMP}.keyblock0 \
-  --datapubkey ${DEVKEYS}/firmware_data_key.vbpubk \
+"${FUTILITY}" vbutil_keyblock --pack "${TMP}.keyblock0" \
+  --datapubkey "${DEVKEYS}/firmware_data_key.vbpubk" \
   --flags 14
 
 # new way
-${FUTILITY} --debug sign \
+"${FUTILITY}" --debug sign \
   --flags 14 \
-  ${DEVKEYS}/firmware_data_key.vbpubk \
-  ${TMP}.keyblock1
+  "${DEVKEYS}/firmware_data_key.vbpubk" \
+  "${TMP}.keyblock1"
 
-cmp ${TMP}.keyblock0 ${TMP}.keyblock1
+cmp "${TMP}.keyblock0" "${TMP}.keyblock1"
 
 
 # Create one using PEM args
 
 # old way
-${FUTILITY} vbutil_keyblock --pack ${TMP}.keyblock2 \
-  --datapubkey ${DEVKEYS}/firmware_data_key.vbpubk \
-  --signprivate_pem ${TESTKEYS}/key_rsa4096.pem \
+"${FUTILITY}" vbutil_keyblock --pack "${TMP}.keyblock2" \
+  --datapubkey "${DEVKEYS}/firmware_data_key.vbpubk" \
+  --signprivate_pem "${TESTKEYS}/key_rsa4096.pem" \
   --pem_algorithm 8 \
   --flags 9
 
 # verify it
-${FUTILITY} vbutil_keyblock --unpack ${TMP}.keyblock2 \
-  --signpubkey ${TESTKEYS}/key_rsa4096.sha512.vbpubk
+"${FUTILITY}" vbutil_keyblock --unpack "${TMP}.keyblock2" \
+  --signpubkey "${TESTKEYS}/key_rsa4096.sha512.vbpubk"
 
 # new way
-${FUTILITY} --debug sign \
-  --pem_signpriv ${TESTKEYS}/key_rsa4096.pem \
+"${FUTILITY}" --debug sign \
+  --pem_signpriv "${TESTKEYS}/key_rsa4096.pem" \
   --pem_algo 8 \
   --flags 9 \
-  ${DEVKEYS}/firmware_data_key.vbpubk \
-  ${TMP}.keyblock3
+  "${DEVKEYS}/firmware_data_key.vbpubk" \
+  "${TMP}.keyblock3"
 
-cmp ${TMP}.keyblock2 ${TMP}.keyblock3
+cmp "${TMP}.keyblock2" "${TMP}.keyblock3"
 
 # Try it with an external signer
 
 # old way
-${FUTILITY} vbutil_keyblock --pack ${TMP}.keyblock4 \
-  --datapubkey ${DEVKEYS}/firmware_data_key.vbpubk \
-  --signprivate_pem ${TESTKEYS}/key_rsa4096.pem \
+"${FUTILITY}" vbutil_keyblock --pack "${TMP}.keyblock4" \
+  --datapubkey "${DEVKEYS}/firmware_data_key.vbpubk" \
+  --signprivate_pem "${TESTKEYS}/key_rsa4096.pem" \
   --pem_algorithm 8 \
   --flags 19 \
-  --externalsigner ${SIGNER}
+  --externalsigner "${SIGNER}"
 
 # verify it
-${FUTILITY} vbutil_keyblock --unpack ${TMP}.keyblock4 \
-  --signpubkey ${TESTKEYS}/key_rsa4096.sha512.vbpubk
+"${FUTILITY}" vbutil_keyblock --unpack "${TMP}.keyblock4" \
+  --signpubkey "${TESTKEYS}/key_rsa4096.sha512.vbpubk"
 
 # new way
-${FUTILITY} --debug sign \
-  --pem_signpriv ${TESTKEYS}/key_rsa4096.pem \
+"${FUTILITY}" --debug sign \
+  --pem_signpriv "${TESTKEYS}/key_rsa4096.pem" \
   --pem_algo 8 \
-  --pem_external ${SIGNER} \
+  --pem_external "${SIGNER}" \
   --flags 19 \
-  ${DEVKEYS}/firmware_data_key.vbpubk \
-  ${TMP}.keyblock5
+  "${DEVKEYS}/firmware_data_key.vbpubk" \
+  "${TMP}.keyblock5"
 
-cmp ${TMP}.keyblock4 ${TMP}.keyblock5
+cmp "${TMP}.keyblock4" "${TMP}.keyblock5"
 
 
 # cleanup
-rm -rf ${TMP}*
+rm -rf "${TMP}"*
 exit 0
diff --git a/tests/futility/test_sign_usbpd1.sh b/tests/futility/test_sign_usbpd1.sh
index 48e64eb..2324fae 100755
--- a/tests/futility/test_sign_usbpd1.sh
+++ b/tests/futility/test_sign_usbpd1.sh
@@ -24,33 +24,33 @@
     : $(( count++ ))
     echo -n "$count " 1>&3
 
-    pemfile=${DATADIR}/${test}.pem
-    infile=${DATADIR}/${test}.unsigned
-    goodfile=${DATADIR}/${test}.signed
-    outfile=${TMP}.${test}.new
+    pemfile="${DATADIR}/${test}.pem"
+    infile="${DATADIR}/${test}.unsigned"
+    goodfile="${DATADIR}/${test}.signed"
+    outfile="${TMP}.${test}.new"
 
     # Signing the whole thing with futility should produce identical results
-    ${FUTILITY} sign --type usbpd1 --pem ${pemfile} ${infile} ${outfile}
-    cmp ${goodfile} ${outfile}
+    "${FUTILITY}" sign --type usbpd1 --pem "${pemfile}" "${infile}" "${outfile}"
+    cmp "${goodfile}" "${outfile}"
 
     # Now try signing just the RW part
-    size=$(stat -c '%s' ${infile})
+    size=$(stat -c '%s' "${infile}")
     half=$(( size / 2 ))
 
-    newin=${TMP}.${test}.rw_in
-    dd if=${infile} bs=${half} count=1 skip=1 of=${newin}
+    newin="${TMP}.${test}.rw_in"
+    dd if="${infile}" bs="${half}" count=1 skip=1 of="${newin}"
     newgood=${TMP}.${test}.rw_ok
-    dd if=${goodfile} bs=${half} count=1 skip=1 of=${newgood}
-    newout=${TMP}.${test}.rw_out
+    dd if="${goodfile}" bs="${half}" count=1 skip=1 of="${newgood}"
+    newout="${TMP}.${test}.rw_out"
 
     # Sign the RW part alone
-    ${FUTILITY} sign --type usbpd1 --pem ${pemfile} \
+    "${FUTILITY}" sign --type usbpd1 --pem "${pemfile}" \
         --ro_size 0 \
-        ${newin} ${newout}
-    cmp ${newgood} ${newout}
+        "${newin}" "${newout}"
+    cmp "${newgood}" "${newout}"
 
 done
 
 # cleanup
-rm -rf ${TMP}*
+rm -rf "${TMP}"*
 exit 0
diff --git a/tests/futility/test_update.sh b/tests/futility/test_update.sh
index 25c5543..8d8a559 100755
--- a/tests/futility/test_update.sh
+++ b/tests/futility/test_update.sh
@@ -40,12 +40,12 @@
 set -o pipefail
 
 # In all the test scenario, we want to test "updating from PEPPY to LINK".
-TO_IMAGE=${TMP}.src.link
-FROM_IMAGE=${TMP}.src.peppy
+TO_IMAGE="${TMP}.src.link"
+FROM_IMAGE="${TMP}.src.peppy"
 TO_HWID="X86 LINK TEST 6638"
 FROM_HWID="X86 PEPPY TEST 4211"
-cp -f ${LINK_BIOS} ${TO_IMAGE}
-cp -f ${PEPPY_BIOS} ${FROM_IMAGE}
+cp -f "${LINK_BIOS}" "${TO_IMAGE}"
+cp -f "${PEPPY_BIOS}" "${FROM_IMAGE}"
 "${FUTILITY}" load_fmap "${FROM_IMAGE}" \
 	RO_VPD:"${RO_VPD_BLOB}" RW_VPD:"${RO_VPD_BLOB}"
 cp -f "${FROM_IMAGE}" "${FROM_IMAGE}".unpatched
@@ -57,29 +57,34 @@
 	local data="$4"
 
 	# NAME OFFSET SIZE
-	local fmap_info="$(${FUTILITY} dump_fmap -p ${file} ${section})"
-	local base="$(echo "${fmap_info}" | sed 's/^[^ ]* //; s/ [^ ]*$//')"
-	local offset=$((base + section_offset))
+	local fmap_info
+	local base
+	local offset
+
+	fmap_info="$("${FUTILITY}" dump_fmap -p "${file}" "${section}")"
+	base="$(echo "${fmap_info}" | sed 's/^[^ ]* //; s/ [^ ]*$//')"
+	offset=$((base + section_offset))
 	echo "offset: ${offset}"
-	printf "${data}" | dd of="${file}" bs=1 seek="${offset}" conv=notrunc
+	printf "%b" "${data}" | dd of="${file}" bs=1 seek="${offset}" \
+		conv=notrunc
 }
 
 # PEPPY and LINK have different platform element ("Google_Link" and
 # "Google_Peppy") in firmware ID so we want to hack them by changing
 # "Google_" to "Google.".
-patch_file ${TO_IMAGE} RW_FWID_A 0 Google.
-patch_file ${TO_IMAGE} RW_FWID_B 0 Google.
-patch_file ${TO_IMAGE} RO_FRID 0 Google.
-patch_file ${FROM_IMAGE} RW_FWID_A 0 Google.
-patch_file ${FROM_IMAGE} RW_FWID_B 0 Google.
-patch_file ${FROM_IMAGE} RO_FRID 0 Google.
+patch_file "${TO_IMAGE}" RW_FWID_A 0 Google.
+patch_file "${TO_IMAGE}" RW_FWID_B 0 Google.
+patch_file "${TO_IMAGE}" RO_FRID 0 Google.
+patch_file "${FROM_IMAGE}" RW_FWID_A 0 Google.
+patch_file "${FROM_IMAGE}" RW_FWID_B 0 Google.
+patch_file "${FROM_IMAGE}" RO_FRID 0 Google.
 
 unpack_image() {
 	local folder="${TMP}.$1"
 	local image="$2"
 	mkdir -p "${folder}"
-	(cd "${folder}" && ${FUTILITY} dump_fmap -x "../${image}")
-	${FUTILITY} gbb -g --rootkey="${folder}/rootkey" "${image}"
+	(cd "${folder}" && "${FUTILITY}" dump_fmap -x "../${image}")
+	"${FUTILITY}" gbb -g --rootkey="${folder}/rootkey" "${image}"
 }
 
 # Unpack images so we can prepare expected results by individual sections.
@@ -114,19 +119,19 @@
 cp -f "${FROM_IMAGE}" "${TMP}.expected.legacy"
 "${FUTILITY}" gbb -s --hwid="${FROM_HWID}" "${TMP}.expected.full"
 "${FUTILITY}" load_fmap "${TMP}.expected.full" \
-	RW_VPD:${TMP}.from/RW_VPD \
-	RO_VPD:${TMP}.from/RO_VPD
+	"RW_VPD:${TMP}.from/RW_VPD" \
+	"RO_VPD:${TMP}.from/RO_VPD"
 "${FUTILITY}" load_fmap "${TMP}.expected.rw" \
-	RW_SECTION_A:${TMP}.to/RW_SECTION_A \
-	RW_SECTION_B:${TMP}.to/RW_SECTION_B \
-	RW_SHARED:${TMP}.to/RW_SHARED \
-	RW_LEGACY:${TMP}.to/RW_LEGACY
+	"RW_SECTION_A:${TMP}.to/RW_SECTION_A" \
+	"RW_SECTION_B:${TMP}.to/RW_SECTION_B" \
+	"RW_SHARED:${TMP}.to/RW_SHARED" \
+	"RW_LEGACY:${TMP}.to/RW_LEGACY"
 "${FUTILITY}" load_fmap "${TMP}.expected.a" \
-	RW_SECTION_A:${TMP}.to/RW_SECTION_A
+	"RW_SECTION_A:${TMP}.to/RW_SECTION_A"
 "${FUTILITY}" load_fmap "${TMP}.expected.b" \
-	RW_SECTION_B:${TMP}.to/RW_SECTION_B
+	"RW_SECTION_B:${TMP}.to/RW_SECTION_B"
 "${FUTILITY}" load_fmap "${TMP}.expected.legacy" \
-	RW_LEGACY:${TMP}.to/RW_LEGACY
+	"RW_LEGACY:${TMP}.to/RW_LEGACY"
 cp -f "${TMP}.expected.full" "${TMP}.expected.full.gbb12"
 patch_file "${TMP}.expected.full.gbb12" GBB 6 "\x02"
 "${FUTILITY}" gbb -s --hwid="${FROM_HWID}" "${TMP}.expected.full.gbb12"
@@ -149,7 +154,7 @@
 # FMAP_AREA_PRESERVE (\010=0x08).
 TO_IMAGE_WIPE_RW_VPD="${TO_IMAGE}.wipe_rw_vpd"
 cp -f "${TO_IMAGE}" "${TO_IMAGE_WIPE_RW_VPD}"
-patch_file ${TO_IMAGE_WIPE_RW_VPD} FMAP 0x3fc "$(printf '\010')"
+patch_file "${TO_IMAGE_WIPE_RW_VPD}" FMAP 0x3fc "$(printf '\010')"
 cp -f "${TMP}.expected.full" "${TMP}.expected.full.empty_rw_vpd"
 "${FUTILITY}" load_fmap "${TMP}.expected.full.empty_rw_vpd" \
 	RW_VPD:"${TMP}.to/RW_VPD"
@@ -371,20 +376,20 @@
 	--quirks preserve_me \
 	-i "${TO_IMAGE}" --wp=0 --sys_props 0,0x10001,1
 
-# Test archive and manifest.
+# Test archive and manifest. CL_TAG is for custom_label_tag.
 A="${TMP}.archive"
 mkdir -p "${A}/bin"
-echo 'echo "${WL_TAG}"' >"${A}/bin/vpd"
+echo "echo \"\${CL_TAG}\"" >"${A}/bin/vpd"
 chmod +x "${A}/bin/vpd"
 
 cp -f "${LINK_BIOS}" "${A}/bios.bin"
 echo "TEST: Manifest (--manifest, bios.bin)"
-${FUTILITY} update -a "${A}" --manifest >"${TMP}.json.out"
+"${FUTILITY}" update -a "${A}" --manifest >"${TMP}.json.out"
 cmp "${TMP}.json.out" "${SCRIPT_DIR}/futility/link_bios.manifest.json"
 
 mv -f "${A}/bios.bin" "${A}/image.bin"
 echo "TEST: Manifest (--manifest, image.bin)"
-${FUTILITY} update -a "${A}" --manifest >"${TMP}.json.out"
+"${FUTILITY}" update -a "${A}" --manifest >"${TMP}.json.out"
 cmp "${TMP}.json.out" "${SCRIPT_DIR}/futility/link_image.manifest.json"
 
 
@@ -395,44 +400,46 @@
 
 echo "TEST: Output (--mode=output)"
 mkdir -p "${TMP}.output"
-${FUTILITY} update -i "${LINK_BIOS}" --mode=output --output_dir="${TMP}.output"
+"${FUTILITY}" update -i "${LINK_BIOS}" --mode=output \
+	--output_dir="${TMP}.output"
 cmp "${LINK_BIOS}" "${TMP}.output/image.bin"
 
 mkdir -p "${A}/keyset"
 cp -f "${LINK_BIOS}" "${A}/image.bin"
-cp -f "${TMP}.to/rootkey" "${A}/keyset/rootkey.WL"
-cp -f "${TMP}.to/VBLOCK_A" "${A}/keyset/vblock_A.WL"
-cp -f "${TMP}.to/VBLOCK_B" "${A}/keyset/vblock_B.WL"
-${FUTILITY} gbb -s --rootkey="${TMP}.from/rootkey" "${A}/image.bin"
-${FUTILITY} load_fmap "${A}/image.bin" VBLOCK_A:"${TMP}.from/VBLOCK_A"
-${FUTILITY} load_fmap "${A}/image.bin" VBLOCK_B:"${TMP}.from/VBLOCK_B"
+cp -f "${TMP}.to/rootkey" "${A}/keyset/rootkey.CL"
+cp -f "${TMP}.to/VBLOCK_A" "${A}/keyset/vblock_A.CL"
+cp -f "${TMP}.to/VBLOCK_B" "${A}/keyset/vblock_B.CL"
+"${FUTILITY}" gbb -s --rootkey="${TMP}.from/rootkey" "${A}/image.bin"
+"${FUTILITY}" load_fmap "${A}/image.bin" VBLOCK_A:"${TMP}.from/VBLOCK_A"
+"${FUTILITY}" load_fmap "${A}/image.bin" VBLOCK_B:"${TMP}.from/VBLOCK_B"
 
-test_update "Full update (--archive, whitelabel, no VPD)" \
-	"${A}/image.bin" "!Need VPD set for white" \
+test_update "Full update (--archive, custom label, no VPD)" \
+	"${A}/image.bin" "!Need VPD set for custom" \
 	-a "${A}" --wp=0 --sys_props 0,0x10001,1,3
 
-test_update "Full update (--archive, whitelabel, no VPD - factory mode)" \
+test_update "Full update (--archive, custom label, no VPD - factory mode)" \
 	"${LINK_BIOS}" "${A}/image.bin" \
 	-a "${A}" --wp=0 --sys_props 0,0x10001,1,3 --mode=factory
 
-test_update "Full update (--archive, whitelabel, no VPD - quirk mode)" \
+test_update "Full update (--archive, custom label, no VPD - quirk mode)" \
 	"${LINK_BIOS}" "${A}/image.bin" \
-	-a "${A}" --wp=0 --sys_props 0,0x10001,1,3 --quirks=allow_empty_wltag
+	-a "${A}" --wp=0 --sys_props 0,0x10001,1,3 \
+	--quirks=allow_empty_custom_label_tag
 
-test_update "Full update (--archive, WL, single package)" \
+test_update "Full update (--archive, custom label, single package)" \
 	"${A}/image.bin" "${LINK_BIOS}" \
-	-a "${A}" --wp=0 --sys_props 0,0x10001,1,3 --signature_id=WL
+	-a "${A}" --wp=0 --sys_props 0,0x10001,1,3 --signature_id=CL
 
-WL_TAG="WL" PATH="${A}/bin:${PATH}" \
-	test_update "Full update (--archive, WL, fake vpd)" \
+CL_TAG="CL" PATH="${A}/bin:${PATH}" \
+	test_update "Full update (--archive, custom label, fake vpd)" \
 	"${A}/image.bin" "${LINK_BIOS}" \
 	-a "${A}" --wp=0 --sys_props 0,0x10001,1,3
 
 echo "TEST: Output (-a, --mode=output)"
 mkdir -p "${TMP}.outa"
 cp -f "${A}/image.bin" "${TMP}.emu"
-WL_TAG="WL" PATH="${A}/bin:${PATH}" \
-	${FUTILITY} update -a "${A}" --mode=output --emu="${TMP}.emu" \
+CL_TAG="CL" PATH="${A}/bin:${PATH}" \
+	"${FUTILITY}" update -a "${A}" --mode=output --emu="${TMP}.emu" \
 	--output_dir="${TMP}.outa"
 cmp "${LINK_BIOS}" "${TMP}.outa/image.bin"
 
@@ -442,13 +449,13 @@
 mv "${A}/image.bin" "${A}/images/bios_coral.bin"
 cp -f "${PEPPY_BIOS}" "${A}/images/bios_peppy.bin"
 cp -f "${LINK_BIOS}" "${A}/images/bios_link.bin"
-cp -f "${TMP}.to/rootkey" "${A}/keyset/rootkey.whitetip-wl"
-cp -f "${TMP}.to/VBLOCK_A" "${A}/keyset/vblock_A.whitetip-wl"
-cp -f "${TMP}.to/VBLOCK_B" "${A}/keyset/vblock_B.whitetip-wl"
+cp -f "${TMP}.to/rootkey" "${A}/keyset/rootkey.customtip-cl"
+cp -f "${TMP}.to/VBLOCK_A" "${A}/keyset/vblock_A.customtip-cl"
+cp -f "${TMP}.to/VBLOCK_B" "${A}/keyset/vblock_B.customtip-cl"
 cp -f "${PEPPY_BIOS}" "${FROM_IMAGE}.ap"
 cp -f "${LINK_BIOS}" "${FROM_IMAGE}.al"
-patch_file ${FROM_IMAGE}.ap FW_MAIN_A 0 "corrupted"
-patch_file ${FROM_IMAGE}.al FW_MAIN_A 0 "corrupted"
+patch_file "${FROM_IMAGE}.ap" FW_MAIN_A 0 "corrupted"
+patch_file "${FROM_IMAGE}.al" FW_MAIN_A 0 "corrupted"
 test_update "Full update (--archive, model=link)" \
 	"${FROM_IMAGE}.al" "${LINK_BIOS}" \
 	-a "${A}" --wp=0 --sys_props 0,0x10001,1,3 --model=link
@@ -458,28 +465,28 @@
 test_update "Full update (--archive, model=unknown)" \
 	"${FROM_IMAGE}.ap" "!Unsupported model: 'unknown'" \
 	-a "${A}" --wp=0 --sys_props 0,0x10001,1,3 --model=unknown
-test_update "Full update (--archive, model=whitetip, signature_id=WL)" \
+test_update "Full update (--archive, model=customtip, signature_id=CL)" \
 	"${FROM_IMAGE}.al" "${LINK_BIOS}" \
-	-a "${A}" --wp=0 --sys_props 0,0x10001,1,3 --model=whitetip \
-	--signature_id=whitetip-wl
+	-a "${A}" --wp=0 --sys_props 0,0x10001,1,3 --model=customtip \
+	--signature_id=customtip-cl
 
-WL_TAG="wl" PATH="${A}/bin:${PATH}" \
-	test_update "Full update (-a, model=WL, fake VPD)" \
+CL_TAG="cl" PATH="${A}/bin:${PATH}" \
+	test_update "Full update (-a, model=customtip, fake VPD)" \
 	"${FROM_IMAGE}.al" "${LINK_BIOS}" \
-	-a "${A}" --wp=0 --sys_props 0,0x10001,1,3 --model=whitetip
+	-a "${A}" --wp=0 --sys_props 0,0x10001,1,3 --model=customtip
 
-# WL-Unibuild without default keys
-test_update "Full update (--a, model=WL, no VPD, no default keys)" \
-	"${FROM_IMAGE}.al" "!Need VPD set for white" \
-	-a "${A}" --wp=0 --sys_props 0,0x10001,1,3 --model=whitetip
+# Custom label + Unibuild without default keys
+test_update "Full update (--a, model=customtip, no VPD, no default keys)" \
+	"${FROM_IMAGE}.al" "!Need VPD set for custom" \
+	-a "${A}" --wp=0 --sys_props 0,0x10001,1,3 --model=customtip
 
-# WL-Unibuild with default keys as model name
-cp -f "${TMP}.to/rootkey" "${A}/keyset/rootkey.whitetip"
-cp -f "${TMP}.to/VBLOCK_A" "${A}/keyset/vblock_A.whitetip"
-cp -f "${TMP}.to/VBLOCK_B" "${A}/keyset/vblock_B.whitetip"
-test_update "Full update (-a, model=WL, no VPD, default keys)" \
+# Custom label + Unibuild with default keys as model name
+cp -f "${TMP}.to/rootkey" "${A}/keyset/rootkey.customtip"
+cp -f "${TMP}.to/VBLOCK_A" "${A}/keyset/vblock_A.customtip"
+cp -f "${TMP}.to/VBLOCK_B" "${A}/keyset/vblock_B.customtip"
+test_update "Full update (-a, model=customtip, no VPD, default keys)" \
 	"${FROM_IMAGE}.al" "${LINK_BIOS}" \
-	-a "${A}" --wp=0 --sys_props 0,0x10001,1,3 --model=whitetip
+	-a "${A}" --wp=0 --sys_props 0,0x10001,1,3 --model=customtip
 
 # Test special programmer
 if type flashrom >/dev/null 2>&1; then
@@ -507,13 +514,16 @@
 
 	echo "min_platform_version=3" >"${TMP}.quirk"
 	cp -f "${TO_IMAGE}" "${TO_IMAGE}.quirk"
-	${FUTILITY} dump_fmap -x "${TO_IMAGE}" "BOOT_STUB:${TMP}.cbfs"
+	"${FUTILITY}" dump_fmap -x "${TO_IMAGE}" "BOOT_STUB:${TMP}.cbfs"
 	# Create a fake CBFS using FW_MAIN_A size.
 	truncate -s $((0x000dffc0)) "${TMP}.cbfs"
-	${FUTILITY} load_fmap "${TO_IMAGE}.quirk" "FW_MAIN_A:${TMP}.cbfs"
+	"${FUTILITY}" load_fmap "${TO_IMAGE}.quirk" "FW_MAIN_A:${TMP}.cbfs"
 	cbfstool "${TO_IMAGE}.quirk" add -r FW_MAIN_A -n updater_quirks \
 		-f "${TMP}.quirk" -t raw
 	test_update "Full update (failure by CBFS quirks)" \
 		"${FROM_IMAGE}" "!Need platform version >= 3 (current is 2)" \
 		-i "${TO_IMAGE}.quirk" --wp=0 --sys_props 0,0x10001,1,2
 fi
+
+rm -rf "${TMP}"*
+exit 0
diff --git a/tests/gen_fuzz_test_cases.sh b/tests/gen_fuzz_test_cases.sh
index 2b4255d..3e4ca99 100755
--- a/tests/gen_fuzz_test_cases.sh
+++ b/tests/gen_fuzz_test_cases.sh
@@ -12,7 +12,7 @@
 . "$(dirname "$0")/common.sh"
 
 # Use a different directory for fuzzing test cases.
-TESTKEY_DIR=${TESTKEY_DIR:-$(realpath  ${SCRIPT_DIR}/../tests/testkeys)}
+TESTKEY_DIR=${TESTKEY_DIR:-$(realpath  "${SCRIPT_DIR}"/../tests/testkeys)}
 TESTCASE_DIR=${BUILD_DIR}/fuzz_testcases
 TEST_IMAGE_FILE=${TESTCASE_DIR}/testimage
 TEST_IMAGE_SIZE=500000
@@ -26,42 +26,43 @@
   echo "Generating keyblocks..."
   # Firmware keyblock - RSA8192/SHA512 root key, RSA4096/SHA512 firmware
   # signing key.
-  ${FUTILITY} vbutil_keyblock \
-    --pack ${TESTCASE_DIR}/firmware.keyblock \
-    --datapubkey ${TESTKEY_DIR}/key_rsa4096.sha512.vbpubk \
-    --signprivate ${TESTKEY_DIR}/key_rsa8192.sha1.vbprivk
+  "${FUTILITY}" vbutil_keyblock \
+    --pack "${TESTCASE_DIR}/firmware.keyblock" \
+    --datapubkey "${TESTKEY_DIR}/key_rsa4096.sha512.vbpubk" \
+    --signprivate "${TESTKEY_DIR}/key_rsa8192.sha1.vbprivk"
 
   # Kernel keyblock - RSA4096/SHA512 kernel signing subkey, RSA4096/SHA512
   # kernel signing key.
-  ${FUTILITY} vbutil_keyblock \
-    --pack ${TESTCASE_DIR}/kernel.keyblock \
-    --datapubkey ${TESTKEY_DIR}/key_rsa4096.sha512.vbpubk \
-    --signprivate ${TESTKEY_DIR}/key_rsa4096.sha1.vbprivk \
+  "${FUTILITY}" vbutil_keyblock \
+    --pack "${TESTCASE_DIR}/kernel.keyblock" \
+    --datapubkey "${TESTKEY_DIR}/key_rsa4096.sha512.vbpubk" \
+    --signprivate "${TESTKEY_DIR}/key_rsa4096.sha1.vbprivk" \
     --flags 15
 
   echo "Generating signed firmware test image..."
-  ${FUTILITY} vbutil_firmware \
-    --vblock ${TESTCASE_DIR}/firmware.vblock \
-    --keyblock ${TESTCASE_DIR}/firmware.keyblock\
-    --signprivate ${TESTKEY_DIR}/key_rsa4096.sha256.vbprivk \
+  "${FUTILITY}" vbutil_firmware \
+    --vblock "${TESTCASE_DIR}/firmware.vblock" \
+    --keyblock "${TESTCASE_DIR}/firmware.keyblock" \
+    --signprivate "${TESTKEY_DIR}/key_rsa4096.sha256.vbprivk" \
     --version 1 \
-    --fv  $1 \
-    --kernelkey ${TESTKEY_DIR}/key_rsa4096.sha512.vbpubk
+    --fv  "$1" \
+    --kernelkey "${TESTKEY_DIR}/key_rsa4096.sha512.vbpubk"
   # TODO(gauravsh): ALso test with (optional) flags.
-  cp ${TESTKEY_DIR}/key_rsa8192.sha512.vbpubk ${TESTCASE_DIR}/root_key.vbpubk
+  cp "${TESTKEY_DIR}/key_rsa8192.sha512.vbpubk" \
+    "${TESTCASE_DIR}/root_key.vbpubk"
 
   echo "Generating signed kernel test image..."
-  ${FUTILITY} vbutil_kernel \
-    --pack ${TESTCASE_DIR}/kernel.vblock.image \
-    --keyblock ${TESTCASE_DIR}/kernel.keyblock \
-    --signprivate ${TESTKEY_DIR}/key_rsa4096.sha256.vbprivk \
+  "${FUTILITY}" vbutil_kernel \
+    --pack "${TESTCASE_DIR}/kernel.vblock.image" \
+    --keyblock "${TESTCASE_DIR}/kernel.keyblock" \
+    --signprivate "${TESTKEY_DIR}/key_rsa4096.sha256.vbprivk" \
     --version 1 \
-    --vmlinuz ${TEST_IMAGE_FILE} \
-    --bootloader ${TEST_BOOTLOADER_FILE} \
-    --config ${TEST_CONFIG_FILE}
+    --vmlinuz "${TEST_IMAGE_FILE}" \
+    --bootloader "${TEST_BOOTLOADER_FILE}" \
+    --config "${TEST_CONFIG_FILE}"
   # TODO(gauravsh): Also test with (optional) padding.
-  cp ${TESTKEY_DIR}/key_rsa4096.sha512.vbpubk \
-    ${TESTCASE_DIR}/firmware_key.vbpubk
+  cp "${TESTKEY_DIR}/key_rsa4096.sha512.vbpubk" \
+    "${TESTCASE_DIR}/firmware_key.vbpubk"
 }
 
 function pre_work {
@@ -69,18 +70,17 @@
   # NOTE: The kernel and config file can't really be random, but the bootloader
   # can. That's probably close enough.
   echo "Generating test image file..."
-  dd if=/dev/urandom of=${TEST_IMAGE_FILE} bs=${TEST_IMAGE_SIZE} count=1
+  dd if=/dev/urandom of="${TEST_IMAGE_FILE}" bs="${TEST_IMAGE_SIZE}" count=1
   echo "Generating test bootloader file..."
   # TODO(gauravsh): Use a valid bootloader here?
-  dd if=/dev/urandom of=${TEST_BOOTLOADER_FILE} bs=${TEST_BOOTLOADER_SIZE} \
+  dd if=/dev/urandom of="${TEST_BOOTLOADER_FILE}" bs="${TEST_BOOTLOADER_SIZE}" \
     count=1
   echo "Generating test config file..."
   # TODO(gauravsh): Use a valid config file here?
-  dd if=/dev/urandom of=${TEST_CONFIG_FILE} bs=${TEST_CONFIG_SIZE} count=1
+  dd if=/dev/urandom of="${TEST_CONFIG_FILE}" bs="${TEST_CONFIG_SIZE}" count=1
 }
 
-mkdir -p ${TESTCASE_DIR}
+mkdir -p "${TESTCASE_DIR}"
 pre_work
 check_test_keys
-generate_fuzzing_images ${TEST_IMAGE_FILE}
-
+generate_fuzzing_images "${TEST_IMAGE_FILE}"
diff --git a/tests/gen_test_cases.sh b/tests/gen_test_cases.sh
index 540fc2a..80ed90c 100755
--- a/tests/gen_test_cases.sh
+++ b/tests/gen_test_cases.sh
@@ -17,17 +17,17 @@
 function generate_test_signatures {
   echo "Generating test signatures..."
   algorithmcounter=0
-  for keylen in ${key_lengths[@]}
+  for keylen in "${key_lengths[@]}"
   do
-    for hashalgo in ${hash_algos[@]}
+    for hashalgo in "${hash_algos[@]}"
     do
-      openssl dgst -${hashalgo} -binary ${TEST_FILE} > \
-        ${TEST_FILE}.${hashalgo}.digest
-      ${BIN_DIR}/signature_digest_utility $algorithmcounter  \
-        ${TEST_FILE} | openssl rsautl \
-        -sign -pkcs -inkey ${TESTKEY_DIR}/key_rsa${keylen}.pem \
-        > ${TEST_FILE}.rsa${keylen}_${hashalgo}.sig
-      let algorithmcounter=algorithmcounter+1
+      openssl dgst "-${hashalgo}" -binary "${TEST_FILE}" > \
+        "${TEST_FILE}.${hashalgo}.digest"
+      "${BIN_DIR}/signature_digest_utility" "$algorithmcounter"  \
+        "${TEST_FILE}" | openssl rsautl \
+        -sign -pkcs -inkey "${TESTKEY_DIR}/key_rsa${keylen}.pem" \
+        > "${TEST_FILE}.rsa${keylen}_${hashalgo}.sig"
+      algorithmcounter=$((algorithmcounter + 1))
     done
   done
 }
@@ -39,10 +39,10 @@
     echo "(skipping, file already exists)"
     return
   fi
-  dd if=/dev/urandom of=${TEST_FILE} bs=${TEST_FILE_SIZE} count=1
+  dd if=/dev/urandom of="${TEST_FILE}" bs="${TEST_FILE_SIZE}" count=1
 }
 
-mkdir -p ${TESTCASE_DIR}
+mkdir -p "${TESTCASE_DIR}"
 check_test_keys
 generate_test_file
 generate_test_signatures
diff --git a/tests/gen_test_keys.sh b/tests/gen_test_keys.sh
index 04315ac..37722fb 100755
--- a/tests/gen_test_keys.sh
+++ b/tests/gen_test_keys.sh
@@ -17,11 +17,11 @@
 function generate_keys {
   key_index=0
   key_name_base="${TESTKEY_DIR}/key_rsa"
-  for i in ${key_lengths[@]}
+  for i in "${key_lengths[@]}"
   do
     key_base="${key_name_base}${i}"
     if [ -f "${key_base}.keyb" ]; then
-      key_index=$((${key_index} + 1))
+      key_index=$((key_index + 1))
       continue
     fi
 
@@ -33,34 +33,33 @@
         bits="${i%%_exp${exp}}"
     fi
 
-    openssl genrsa -${exp} -out ${key_base}.pem ${bits}
+    openssl genrsa "-${exp}" -out "${key_base}.pem" "${bits}"
     # Generate self-signed certificate from key.
-    openssl req -batch -new -x509 -key ${key_base}.pem \
-      -out ${key_base}.crt
+    openssl req -batch -new -x509 -key "${key_base}.pem" \
+      -out "${key_base}.crt"
 
     # Generate pre-processed key for use by RSA signature verification code.
-    ${BIN_DIR}/dumpRSAPublicKey -cert ${key_base}.crt \
-      > ${key_base}.keyb
+    "${BIN_DIR}/dumpRSAPublicKey" -cert "${key_base}.crt" > "${key_base}.keyb"
 
     alg_index=0
-    for sha_type in ${sha_types[@]}
+    for sha_type in "${sha_types[@]}"
     do
-      alg=$((${key_index} * 3 + ${alg_index}))
+      alg=$((key_index * 3 + alg_index))
   # wrap the public key
-      ${FUTILITY} vbutil_key \
+      "${FUTILITY}" vbutil_key \
         --pack "${key_base}.sha${sha_type}.vbpubk" \
         --key "${key_base}.keyb" \
         --version 1 \
         --algorithm ${alg}
 
   # wrap the private key
-      ${FUTILITY} vbutil_key \
+      "${FUTILITY}" vbutil_key \
         --pack "${key_base}.sha${sha_type}.vbprivk" \
         --key "${key_base}.pem" \
         --algorithm ${alg}
-      alg_index=$((${alg_index} + 1))
+      alg_index=$((alg_index} + 1))
     done
-    key_index=$((${key_index} + 1))
+    key_index=$((key_index + 1))
   done
 }
 
diff --git a/tests/gen_test_vbpubks.sh b/tests/gen_test_vbpubks.sh
index 8ea2759..a318d3d 100755
--- a/tests/gen_test_vbpubks.sh
+++ b/tests/gen_test_vbpubks.sh
@@ -11,16 +11,16 @@
 
 function generate_vpubks {
   algorithmcounter=0
-  for keylen in ${key_lengths[@]}
+  for keylen in "${key_lengths[@]}"
   do
-    for hashalgo in ${hash_algos[@]}
+    for hashalgo in "${hash_algos[@]}"
     do
-      ${FUTILITY} vbutil_key --pack \
-        --in ${TESTKEY_DIR}/key_rsa${keylen}.keyb \
-        --out ${TESTKEY_DIR}/key_rsa${keylen}.${hashalgo}.vbpubk \
+      "${FUTILITY}" vbutil_key --pack \
+        --in "${TESTKEY_DIR}/key_rsa${keylen}.keyb" \
+        --out "${TESTKEY_DIR}/key_rsa${keylen}.${hashalgo}.vbpubk" \
         --version 1 \
-        --algorithm ${algorithmcounter}
-      let algorithmcounter=algorithmcounter+1
+        --algorithm "${algorithmcounter}"
+      algorithmcounter=$((algorithmcounter + 1))
     done
   done
 }
diff --git a/tests/gpt_misc_tests.c b/tests/gpt_misc_tests.c
index e0a8d56..e7ffccf 100644
--- a/tests/gpt_misc_tests.c
+++ b/tests/gpt_misc_tests.c
@@ -8,8 +8,8 @@
 #include "2api.h"
 #include "cgptlib.h"
 #include "cgptlib_internal.h"
+#include "common/tests.h"
 #include "gpt.h"
-#include "test_common.h"
 
 #define LOGCALL(fmt, args...) sprintf(call_log + strlen(call_log), fmt, ##args)
 #define TEST_CALLS(expect_log) TEST_STR_EQ(call_log, expect_log, "  calls")
@@ -29,7 +29,7 @@
 static int disk_read_to_fail;
 static int disk_write_to_fail;
 
-static VbExDiskHandle_t handle;
+static vb2ex_disk_handle_t handle;
 static uint8_t mock_disk[MOCK_SECTOR_SIZE * MOCK_SECTOR_COUNT];
 static GptHeader *mock_gpt_primary =
 	(GptHeader*)&mock_disk[MOCK_SECTOR_SIZE * 1];
@@ -91,7 +91,7 @@
 
 /* Mocks */
 
-vb2_error_t VbExDiskRead(VbExDiskHandle_t h, uint64_t lba_start,
+vb2_error_t VbExDiskRead(vb2ex_disk_handle_t h, uint64_t lba_start,
 			 uint64_t lba_count, void *buffer)
 {
 	LOGCALL("VbExDiskRead(h, %d, %d)\n", (int)lba_start, (int)lba_count);
@@ -105,7 +105,7 @@
 	return VB2_SUCCESS;
 }
 
-vb2_error_t VbExDiskWrite(VbExDiskHandle_t h, uint64_t lba_start,
+vb2_error_t VbExDiskWrite(vb2ex_disk_handle_t h, uint64_t lba_start,
 			  uint64_t lba_count, const void *buffer)
 {
 	LOGCALL("VbExDiskWrite(h, %d, %d)\n", (int)lba_start, (int)lba_count);
@@ -207,7 +207,7 @@
 	 * Invalidate primary GPT header and check that it is
 	 * repaired by GptRepair().
 	 *
-	 * This would normally be called by LoadKernel()->GptInit()
+	 * This would normally be called by vb2api_load_kernel()->GptInit()
 	 * but this callback is mocked in these tests.
 	 */
 	ResetMocks();
@@ -232,7 +232,7 @@
 	 * Invalidate secondary GPT header and check that it can be
 	 * repaired by GptRepair().
 	 *
-	 * This would normally be called by LoadKernel()->GptInit()
+	 * This would normally be called by vb2api_load_kernel()->GptInit()
 	 * but this callback is mocked in these tests.
 	 */
 	ResetMocks();
diff --git a/tests/hmac_test.c b/tests/hmac_test.c
index ed77d7b..708711f 100644
--- a/tests/hmac_test.c
+++ b/tests/hmac_test.c
@@ -10,7 +10,7 @@
 
 #include "2sha.h"
 #include "2hmac.h"
-#include "test_common.h"
+#include "common/tests.h"
 
 const char short_key[] = "key";
 const char message[] = "The quick brown fox jumps over the lazy dog";
diff --git a/tests/load_kernel_tests.sh b/tests/load_kernel_tests.sh
index 45eedf4..cd1bd63 100755
--- a/tests/load_kernel_tests.sh
+++ b/tests/load_kernel_tests.sh
@@ -27,21 +27,21 @@
 dd if=/dev/urandom bs=32768 count=1 of="dummy_kernel.bin"
 
 # Pack kernel data key using original vboot utilities.
-${FUTILITY} vbutil_key --pack datakey.test \
-    --key ${TESTKEY_DIR}/key_rsa2048.keyb --algorithm 4
+"${FUTILITY}" vbutil_key --pack datakey.test \
+    --key "${TESTKEY_DIR}/key_rsa2048.keyb" --algorithm 4
 
 # Keyblock with kernel data key is signed by kernel subkey
 # Flags=21 means dev=0 rec=0 minios=0
-${FUTILITY} vbutil_keyblock --pack keyblock.test \
+"${FUTILITY}" vbutil_keyblock --pack keyblock.test \
     --datapubkey datakey.test \
     --flags 21 \
-    --signprivate ${SCRIPT_DIR}/devkeys/kernel_subkey.vbprivk
+    --signprivate "${SCRIPT_DIR}/devkeys/kernel_subkey.vbprivk"
 
 # Kernel preamble is signed with the kernel data key
-${FUTILITY} vbutil_kernel \
+"${FUTILITY}" vbutil_kernel \
     --pack "kernel.test" \
     --keyblock "keyblock.test" \
-    --signprivate ${TESTKEY_DIR}/key_rsa2048.sha256.vbprivk \
+    --signprivate "${TESTKEY_DIR}/key_rsa2048.sha256.vbprivk" \
     --version 1 \
     --arch arm \
     --vmlinuz "dummy_kernel.bin" \
@@ -51,9 +51,9 @@
 echo 'Verifying test kernel'
 
 # Verify the kernel
-${FUTILITY} vbutil_kernel \
+"${FUTILITY}" vbutil_kernel \
     --verify "kernel.test" \
-    --signpubkey ${SCRIPT_DIR}/devkeys/kernel_subkey.vbpubk
+    --signpubkey "${SCRIPT_DIR}/devkeys/kernel_subkey.vbpubk"
 
 happy 'Kernel verification succeeded'
 
@@ -69,7 +69,7 @@
 
 # And verify it using futility
 echo 'Verifying test disk image'
-${BUILD_RUN}/tests/verify_kernel disk.test \
-    ${SCRIPT_DIR}/devkeys/kernel_subkey.vbpubk
+"${BUILD_RUN}/tests/verify_kernel" disk.test \
+    "${SCRIPT_DIR}/devkeys/kernel_subkey.vbpubk"
 
 happy 'Image verification succeeded'
diff --git a/tests/run_cgpt_tests.sh b/tests/run_cgpt_tests.sh
index 3f18624..7f59205 100755
--- a/tests/run_cgpt_tests.sh
+++ b/tests/run_cgpt_tests.sh
@@ -10,27 +10,26 @@
 . "$(dirname "$0")/common.sh"
 
 CGPT=$(readlink -f "$1")
-[ -x "$CGPT" ] || error "Can't execute $CGPT"
+[ -x ""${CGPT}"" ] || error "Can't execute $CGPT"
 
-MTD="${@:2}"
+MTD=("${@:2}")
 
 # Run tests in a dedicated directory for easy cleanup or debugging.
 DIR="${TEST_DIR}/cgpt_test_dir"
 [ -d "$DIR" ] || mkdir -p "$DIR"
-warning "testing $CGPT in $DIR"
+warning "testing "${CGPT}" in $DIR"
 cd "$DIR"
 
 assert_fail() {
   set +e
-  "$@" 2>/dev/null
-  if [ $? == 0 ]; then
+  if "$@" 2>/dev/null; then
     error "$*" " should have failed but did not"
   fi
   set -e
 }
 
 # Test failure on non existing file.
-assert_fail ${CGPT} show $MTD blah_404_haha
+assert_fail "${CGPT}" show "${MTD[@]}" blah_404_haha
 
 echo "Create an empty file to use as the device..."
 NUM_SECTORS=1000
@@ -76,264 +75,271 @@
 
 RANDOM_DRIVE_GUID='12345678-0000-1111-2222-123456789ABC'
 
-$CGPT create $MTD ${DEV}
+"${CGPT}" create "${MTD[@]}" ${DEV}
 
 run_basic_tests() {
   echo "Create a bunch of partitions, using the real GUID types..."
 
-  $CGPT add $MTD -b ${DATA_START} -s ${DATA_SIZE} -t ${DATA_GUID} \
+  "${CGPT}" add "${MTD[@]}" -b ${DATA_START} -s ${DATA_SIZE} -t ${DATA_GUID} \
     -l "${DATA_LABEL}" ${DEV}
-  $CGPT add $MTD -b ${KERN_START} -s ${KERN_SIZE} -t ${KERN_GUID} \
+  "${CGPT}" add "${MTD[@]}" -b ${KERN_START} -s ${KERN_SIZE} -t ${KERN_GUID} \
     -l "${KERN_LABEL}" ${DEV}
-  $CGPT add $MTD -b ${ROOTFS_START} -s ${ROOTFS_SIZE} -t ${ROOTFS_GUID} \
-    -l "${ROOTFS_LABEL}" ${DEV}
-  $CGPT add $MTD -b ${ESP_START} -s ${ESP_SIZE} -t ${ESP_GUID} \
+  "${CGPT}" add "${MTD[@]}" -b ${ROOTFS_START} -s ${ROOTFS_SIZE} \
+    -t ${ROOTFS_GUID} -l "${ROOTFS_LABEL}" ${DEV}
+  "${CGPT}" add "${MTD[@]}" -b ${ESP_START} -s ${ESP_SIZE} -t ${ESP_GUID} \
     -l "${ESP_LABEL}" ${DEV}
-  $CGPT add $MTD -b ${FUTURE_START} -s ${FUTURE_SIZE} -t ${FUTURE_GUID} \
-    -l "${FUTURE_LABEL}" ${DEV}
-  $CGPT add $MTD -b ${RANDOM_START} -s ${RANDOM_SIZE} -t ${RANDOM_GUID} \
-    -l "${RANDOM_LABEL}" ${DEV}
+  "${CGPT}" add "${MTD[@]}" -b ${FUTURE_START} -s ${FUTURE_SIZE} \
+    -t ${FUTURE_GUID} -l "${FUTURE_LABEL}" ${DEV}
+  "${CGPT}" add "${MTD[@]}" -b ${RANDOM_START} -s ${RANDOM_SIZE} \
+    -t ${RANDOM_GUID} -l "${RANDOM_LABEL}" ${DEV}
 
 
   echo "Extract the start and size of given partitions..."
 
-  X=$($CGPT show $MTD -b -i $DATA_NUM ${DEV})
-  Y=$($CGPT show $MTD -s -i $DATA_NUM ${DEV})
+  X=$("${CGPT}" show "${MTD[@]}" -b -i $DATA_NUM ${DEV})
+  Y=$("${CGPT}" show "${MTD[@]}" -s -i $DATA_NUM ${DEV})
   [ "$X $Y" = "$DATA_START $DATA_SIZE" ] || error
 
-  X=$($CGPT show $MTD -b -i $KERN_NUM ${DEV})
-  Y=$($CGPT show $MTD -s -i $KERN_NUM ${DEV})
+  X=$("${CGPT}" show "${MTD[@]}" -b -i $KERN_NUM ${DEV})
+  Y=$("${CGPT}" show "${MTD[@]}" -s -i $KERN_NUM ${DEV})
   [ "$X $Y" = "$KERN_START $KERN_SIZE" ] || error
 
-  X=$($CGPT show $MTD -b -i $ROOTFS_NUM ${DEV})
-  Y=$($CGPT show $MTD -s -i $ROOTFS_NUM ${DEV})
+  X=$("${CGPT}" show "${MTD[@]}" -b -i $ROOTFS_NUM ${DEV})
+  Y=$("${CGPT}" show "${MTD[@]}" -s -i $ROOTFS_NUM ${DEV})
   [ "$X $Y" = "$ROOTFS_START $ROOTFS_SIZE" ] || error
 
-  X=$($CGPT show $MTD -b -i $ESP_NUM ${DEV})
-  Y=$($CGPT show $MTD -s -i $ESP_NUM ${DEV})
+  X=$("${CGPT}" show "${MTD[@]}" -b -i $ESP_NUM ${DEV})
+  Y=$("${CGPT}" show "${MTD[@]}" -s -i $ESP_NUM ${DEV})
   [ "$X $Y" = "$ESP_START $ESP_SIZE" ] || error
 
-  X=$($CGPT show $MTD -b -i $FUTURE_NUM ${DEV})
-  Y=$($CGPT show $MTD -s -i $FUTURE_NUM ${DEV})
+  X=$("${CGPT}" show "${MTD[@]}" -b -i $FUTURE_NUM ${DEV})
+  Y=$("${CGPT}" show "${MTD[@]}" -s -i $FUTURE_NUM ${DEV})
   [ "$X $Y" = "$FUTURE_START $FUTURE_SIZE" ] || error
 
-  X=$($CGPT show $MTD -b -i $RANDOM_NUM ${DEV})
-  Y=$($CGPT show $MTD -s -i $RANDOM_NUM ${DEV})
+  X=$("${CGPT}" show "${MTD[@]}" -b -i $RANDOM_NUM ${DEV})
+  Y=$("${CGPT}" show "${MTD[@]}" -s -i $RANDOM_NUM ${DEV})
   [ "$X $Y" = "$RANDOM_START $RANDOM_SIZE" ] || error
 
 
   echo "Change the beginning..."
   DATA_START=$((DATA_START + 10))
-  $CGPT add $MTD -i 1 -b ${DATA_START} ${DEV} || error
-  X=$($CGPT show $MTD -b -i 1 ${DEV})
+  "${CGPT}" add "${MTD[@]}" -i 1 -b ${DATA_START} ${DEV} || error
+  X=$("${CGPT}" show "${MTD[@]}" -b -i 1 ${DEV})
   [ "$X" = "$DATA_START" ] || error
 
   echo "Change the size..."
   DATA_SIZE=$((DATA_SIZE + 10))
-  $CGPT add $MTD -i 1 -s ${DATA_SIZE} ${DEV} || error
-  X=$($CGPT show $MTD -s -i 1 ${DEV})
+  "${CGPT}" add "${MTD[@]}" -i 1 -s ${DATA_SIZE} ${DEV} || error
+  X=$("${CGPT}" show "${MTD[@]}" -s -i 1 ${DEV})
   [ "$X" = "$DATA_SIZE" ] || error
 
   echo "Change the type..."
-  $CGPT add $MTD -i 1 -t reserved ${DEV} || error
-  X=$($CGPT show $MTD -t -i 1 ${DEV} | tr 'A-Z' 'a-z')
+  "${CGPT}" add "${MTD[@]}" -i 1 -t reserved ${DEV} || error
+  X=$("${CGPT}" show "${MTD[@]}" -t -i 1 ${DEV} | tr '[:upper:]' '[:lower:]')
   [ "$X" = "$FUTURE_GUID" ] || error
   # arbitrary value
-  $CGPT add $MTD -i 1 -t 610a563a-a55c-4ae0-ab07-86e5bb9db67f ${DEV} || error
-  X=$($CGPT show $MTD -t -i 1 ${DEV})
+  "${CGPT}" add "${MTD[@]}" -i 1 -t 610a563a-a55c-4ae0-ab07-86e5bb9db67f \
+    ${DEV} || error
+  X=$("${CGPT}" show "${MTD[@]}" -t -i 1 ${DEV})
   [ "$X" = "610A563A-A55C-4AE0-AB07-86E5BB9DB67F" ] || error
 
-  $CGPT add $MTD -i 1 -t data ${DEV} || error
-  X=$($CGPT show $MTD -t -i 1 ${DEV} | tr 'A-Z' 'a-z')
+  "${CGPT}" add "${MTD[@]}" -i 1 -t data ${DEV} || error
+  X=$("${CGPT}" show "${MTD[@]}" -t -i 1 ${DEV} | tr '[:upper:]' '[:lower:]')
   [ "$X" = "$DATA_GUID" ] || error
 
-  ORIG_ID=$($CGPT show $MTD -v ${DEV} | \
+  ORIG_ID=$("${CGPT}" show "${MTD[@]}" -v ${DEV} | \
     grep -i "disk uuid" | head -1 | awk ' { print $3 } ' )
   [ ! "$ORIG_ID" = "$RANDOM_DRIVE_GUID" ] || error
-  $CGPT edit $MTD -u ${RANDOM_DRIVE_GUID} ${DEV} || error
-  X=$($CGPT show $MTD -v ${DEV} | grep -i "disk uuid" | \
+  "${CGPT}" edit "${MTD[@]}" -u ${RANDOM_DRIVE_GUID} ${DEV} || error
+  X=$("${CGPT}" show "${MTD[@]}" -v ${DEV} | grep -i "disk uuid" | \
     head -1 | awk ' { print $3 } ' )
   [ "$X" = "${RANDOM_DRIVE_GUID}" ] || error
-  $CGPT edit $MTD -u ${ORIG_ID} ${DEV} || error
-  X=$($CGPT show $MTD -v ${DEV} | grep -i "disk uuid" | \
+  "${CGPT}" edit "${MTD[@]}" -u "${ORIG_ID}" ${DEV} || error
+  X=$("${CGPT}" show "${MTD[@]}" -v ${DEV} | grep -i "disk uuid" | \
     head -1 | awk ' { print $3 } ' )
   [ "$X" = "${ORIG_ID}" ] || error
 }
 run_basic_tests
 
-ORIG_ID=$($CGPT show $MTD -v ${DEV} | \
+ORIG_ID=$("${CGPT}" show "${MTD[@]}" -v ${DEV} | \
   grep -i "disk uuid" | awk ' { print $3 } ' )
 [ ! "$ORIG_ID" = "$RANDOM_DRIVE_GUID" ] || error
-$CGPT edit $MTD -u ${RANDOM_DRIVE_GUID} ${DEV} || error
-X=$($CGPT show $MTD -v ${DEV} | grep -i "disk uuid" | \
+"${CGPT}" edit "${MTD[@]}" -u ${RANDOM_DRIVE_GUID} ${DEV} || error
+X=$("${CGPT}" show "${MTD[@]}" -v ${DEV} | grep -i "disk uuid" | \
   head -1 | awk ' { print $3 } ' )
 [ "$X" = "${RANDOM_DRIVE_GUID}" ] || error
-$CGPT edit $MTD -u ${ORIG_ID} ${DEV} || error
-X=$($CGPT show $MTD -v ${DEV} | grep -i "disk uuid" | \
+"${CGPT}" edit "${MTD[@]}" -u "${ORIG_ID}" ${DEV} || error
+X=$("${CGPT}" show "${MTD[@]}" -v ${DEV} | grep -i "disk uuid" | \
   awk ' { print $3 } ' )
 [ "$X" = "${ORIG_ID}" ] || error
 
 echo "Set the boot partition.."
-$CGPT boot $MTD -i ${KERN_NUM} ${DEV} >/dev/null
+"${CGPT}" boot "${MTD[@]}" -i ${KERN_NUM} ${DEV} >/dev/null
 
 echo "Check the PMBR's idea of the boot partition..."
-X=$($CGPT boot $MTD ${DEV})
-Y=$($CGPT show $MTD -u -i $KERN_NUM $DEV)
+X=$("${CGPT}" boot "${MTD[@]}" ${DEV})
+Y=$("${CGPT}" show "${MTD[@]}" -u -i $KERN_NUM $DEV)
 [ "$X" = "$Y" ] || error
 
 # Input: sequence of priorities
 # Output: ${DEV} has kernel partitions with the given priorities
 make_pri() {
   local idx=0
-  $CGPT create $MTD ${DEV}
+  "${CGPT}" create "${MTD[@]}" ${DEV}
   for pri in "$@"; do
     idx=$((idx+1))
-    $CGPT add $MTD -t kernel -l "kern$idx" -b $((100 + 2 * $idx)) -s 1 -P $pri ${DEV}
+    "${CGPT}" add "${MTD[@]}" -t kernel -l "kern$idx" -b $((100 + 2 * idx)) \
+      -s 1 -P $pri ${DEV}
   done
 }
 
 # Output: returns string containing priorities of all kernels
 get_pri() {
-  echo $(
-  for idx in $($CGPT find $MTD -t kernel ${DEV} | sed -e s@${DEV}@@); do
-    $CGPT show $MTD -i $idx -P ${DEV}
+  local output=()
+  for idx in $("${CGPT}" find "${MTD[@]}" -t kernel ${DEV} | sed -e s@${DEV}@@);
+  do
+    output+=("$("${CGPT}" show "${MTD[@]}" -i "$idx" -P ${DEV})")
   done
-  )
+  echo "${output[@]}"
 }
 
 # Input: list of priorities
 # Operation: expects ${DEV} to contain those kernel priorities
 assert_pri() {
   local expected="$*"
-  local actual=$(get_pri)
+  local actual
+  actual=$(get_pri)
   [ "$actual" = "$expected" ] || \
     error 1 "expected priority \"$expected\", actual priority \"$actual\""
 }
 
 # no kernels at all. This should do nothing.
-$CGPT create $MTD ${DEV}
+"${CGPT}" create "${MTD[@]}" ${DEV}
 
 run_prioritize_tests() {
   echo "Test the cgpt prioritize command..."
-  $CGPT add $MTD -t rootfs -b 100 -s 1 ${DEV}
-  $CGPT prioritize $MTD ${DEV}
+  "${CGPT}" add "${MTD[@]}" -t rootfs -b 100 -s 1 ${DEV}
+  "${CGPT}" prioritize "${MTD[@]}" ${DEV}
   assert_pri ""
 
   # common install/upgrade sequence
   make_pri   2 0 0
-  $CGPT prioritize $MTD -i 1 ${DEV}
+  "${CGPT}" prioritize "${MTD[@]}" -i 1 ${DEV}
   assert_pri 1 0 0
-  $CGPT prioritize $MTD -i 2 ${DEV}
+  "${CGPT}" prioritize "${MTD[@]}" -i 2 ${DEV}
   assert_pri 1 2 0
-  $CGPT prioritize $MTD -i 1 ${DEV}
+  "${CGPT}" prioritize "${MTD[@]}" -i 1 ${DEV}
   assert_pri 2 1 0
-  $CGPT prioritize $MTD -i 2 ${DEV}
+  "${CGPT}" prioritize "${MTD[@]}" -i 2 ${DEV}
   assert_pri 1 2 0
   # lots of kernels, all same starting priority, should go to priority 1
   make_pri   8 8 8 8 8 8 8 8 8 8 8 0 0 8
-  $CGPT prioritize $MTD ${DEV}
+  "${CGPT}" prioritize "${MTD[@]}" ${DEV}
   assert_pri 1 1 1 1 1 1 1 1 1 1 1 0 0 1
 
   # now raise them all up again
-  $CGPT prioritize $MTD -P 4 ${DEV}
+  "${CGPT}" prioritize "${MTD[@]}" -P 4 ${DEV}
   assert_pri 4 4 4 4 4 4 4 4 4 4 4 0 0 4
 
   # set one of them higher, should leave the rest alone
-  $CGPT prioritize $MTD -P 5 -i 3 ${DEV}
+  "${CGPT}" prioritize "${MTD[@]}" -P 5 -i 3 ${DEV}
   assert_pri 4 4 5 4 4 4 4 4 4 4 4 0 0 4
 
   # set one of them lower, should bring the rest down
-  $CGPT prioritize $MTD -P 3 -i 4 ${DEV}
+  "${CGPT}" prioritize "${MTD[@]}" -P 3 -i 4 ${DEV}
   assert_pri 1 1 2 3 1 1 1 1 1 1 1 0 0 1
 
   # raise a group by including the friends of one partition
-  $CGPT prioritize $MTD -P 6 -i 1 -f ${DEV}
+  "${CGPT}" prioritize "${MTD[@]}" -P 6 -i 1 -f ${DEV}
   assert_pri 6 6 4 5 6 6 6 6 6 6 6 0 0 6
 
   # resurrect one, should not affect the others
   make_pri   0 0 0 0 0 0 0 0 0 0 0 0 0 0
-  $CGPT prioritize $MTD -i 2 ${DEV}
+  "${CGPT}" prioritize "${MTD[@]}" -i 2 ${DEV}
   assert_pri 0 1 0 0 0 0 0 0 0 0 0 0 0 0
 
   # resurrect one and all its friends
   make_pri   0 0 0 0 0 0 0 0 1 2 0 0 0 0
-  $CGPT prioritize $MTD -P 5 -i 2 -f ${DEV}
+  "${CGPT}" prioritize "${MTD[@]}" -P 5 -i 2 -f ${DEV}
   assert_pri 5 5 5 5 5 5 5 5 3 4 5 5 5 5
 
   # no options should maintain the same order
-  $CGPT prioritize $MTD ${DEV}
+  "${CGPT}" prioritize "${MTD[@]}" ${DEV}
   assert_pri 3 3 3 3 3 3 3 3 1 2 3 3 3 3
 
   # squish all the ranks
   make_pri   1 1 2 2 3 3 4 4 5 5 0 6 7 7
-  $CGPT prioritize $MTD -P 6 ${DEV}
+  "${CGPT}" prioritize "${MTD[@]}" -P 6 ${DEV}
   assert_pri 1 1 1 1 2 2 3 3 4 4 0 5 6 6
 
   # squish the ranks by not leaving room
   make_pri   1 1 2 2 3 3 4 4 5 5 0 6 7 7
-  $CGPT prioritize $MTD -P 7 -i 3 ${DEV}
+  "${CGPT}" prioritize "${MTD[@]}" -P 7 -i 3 ${DEV}
   assert_pri 1 1 7 1 2 2 3 3 4 4 0 5 6 6
 
   # squish the ranks while bringing the friends along
   make_pri   1 1 2 2 3 3 4 4 5 5 0 6 7 7
-  $CGPT prioritize $MTD -P 6 -i 3 -f ${DEV}
+  "${CGPT}" prioritize "${MTD[@]}" -P 6 -i 3 -f ${DEV}
   assert_pri 1 1 6 6 1 1 2 2 3 3 0 4 5 5
 
   # squish them pretty hard
   make_pri   1 1 2 2 3 3 4 4 5 5 0 6 7 7
-  $CGPT prioritize $MTD -P 2 ${DEV}
+  "${CGPT}" prioritize "${MTD[@]}" -P 2 ${DEV}
   assert_pri 1 1 1 1 1 1 1 1 1 1 0 1 2 2
 
   # squish them really really hard (nobody gets reduced to zero, though)
   make_pri   1 1 2 2 3 3 4 4 5 5 0 6 7 7
-  $CGPT prioritize $MTD -P 1 -i 3 ${DEV}
+  "${CGPT}" prioritize "${MTD[@]}" -P 1 -i 3 ${DEV}
   assert_pri 1 1 1 1 1 1 1 1 1 1 0 1 1 1
 
   make_pri   15 15 14 14 13 13 12 12 11 11 10 10 9 9 8 8 7 7 6 6 5 5 4 4 3 3 2 2 1 1 0
-  $CGPT prioritize $MTD -i 3 ${DEV}
+  "${CGPT}" prioritize "${MTD[@]}" -i 3 ${DEV}
   assert_pri 14 14 15 13 12 12 11 11 10 10  9  9 8 8 7 7 6 6 5 5 4 4 3 3 2 2 1 1 1 1 0
-  $CGPT prioritize $MTD -i 5 ${DEV}
+  "${CGPT}" prioritize "${MTD[@]}" -i 5 ${DEV}
   assert_pri 13 13 14 12 15 11 10 10  9  9  8  8 7 7 6 6 5 5 4 4 3 3 2 2 1 1 1 1 1 1 0
   # but if I bring friends I don't have to squish
-  $CGPT prioritize $MTD -i 1 -f ${DEV}
+  "${CGPT}" prioritize "${MTD[@]}" -i 1 -f ${DEV}
   assert_pri 15 15 13 12 14 11 10 10  9  9  8  8 7 7 6 6 5 5 4 4 3 3 2 2 1 1 1 1 1 1 0
 }
 run_prioritize_tests
 
 echo "Test cgpt repair command"
-$CGPT repair $MTD ${DEV}
-($CGPT show $MTD ${DEV} | grep -q INVALID) && error
+"${CGPT}" repair "${MTD[@]}" ${DEV}
+("${CGPT}" show "${MTD[@]}" ${DEV} | grep -q INVALID) && error
 
 # Zero primary header and partition table and try to repair it.
 dd if=/dev/zero of=${DEV} conv=notrunc bs=512 count=33 2>/dev/null
-$CGPT show $MTD ${DEV} | grep -q INVALID
-$CGPT repair $MTD ${DEV}
-($CGPT show $MTD ${DEV} | grep -q INVALID) && error
+"${CGPT}" show "${MTD[@]}" ${DEV} | grep -q INVALID
+"${CGPT}" repair "${MTD[@]}" ${DEV}
+("${CGPT}" show "${MTD[@]}" ${DEV} | grep -q INVALID) && error
 
 # Zero secondary header and partition table and try to repair it.
-dd if=/dev/zero of=${DEV} seek=$(($NUM_SECTORS - 33)) conv=notrunc bs=512 count=33 2>/dev/null
-$CGPT show $MTD ${DEV} | grep -q INVALID
-$CGPT repair $MTD ${DEV}
-($CGPT show $MTD ${DEV} | grep -q INVALID) && error
+dd if=/dev/zero of=${DEV} seek=$((NUM_SECTORS - 33)) conv=notrunc bs=512 \
+  count=33 2>/dev/null
+"${CGPT}" show "${MTD[@]}" ${DEV} | grep -q INVALID
+"${CGPT}" repair "${MTD[@]}" ${DEV}
+("${CGPT}" show "${MTD[@]}" ${DEV} | grep -q INVALID) && error
 
 echo "Test with IGNOREME primary GPT..."
-$CGPT create $MTD ${DEV}
-$CGPT legacy $MTD -p ${DEV}
-$CGPT show $MTD ${DEV} | egrep -q "IGNORED.*Pri GPT" 2>/dev/null
-($CGPT show $MTD ${DEV} | grep -q "Pri GPT table" 2>/dev/null) && error
-$CGPT repair $MTD ${DEV} 2>/dev/null
-$CGPT show $MTD ${DEV} | egrep -q "IGNORED.*Pri GPT" 2>/dev/null
-($CGPT show $MTD ${DEV} | grep -q "Pri GPT table" 2>/dev/null) && error
-$CGPT legacy $MTD -e ${DEV} 2>/dev/null
-($CGPT show $MTD ${DEV} | egrep -q "IGNORED.*Pri GPT") && error
-$CGPT show $MTD ${DEV} | grep -q "Pri GPT table"
+"${CGPT}" create "${MTD[@]}" ${DEV}
+"${CGPT}" legacy "${MTD[@]}" -p ${DEV}
+"${CGPT}" show "${MTD[@]}" ${DEV} | grep -q -E "IGNORED.*Pri GPT" 2>/dev/null
+("${CGPT}" show "${MTD[@]}" ${DEV} | grep -q "Pri GPT table" 2>/dev/null) && \
+  error
+"${CGPT}" repair "${MTD[@]}" ${DEV} 2>/dev/null
+"${CGPT}" show "${MTD[@]}" ${DEV} | grep -q -E "IGNORED.*Pri GPT" 2>/dev/null
+("${CGPT}" show "${MTD[@]}" ${DEV} | grep -q "Pri GPT table" 2>/dev/null) && \
+  error
+"${CGPT}" legacy "${MTD[@]}" -e ${DEV} 2>/dev/null
+("${CGPT}" show "${MTD[@]}" ${DEV} | grep -q -E "IGNORED.*Pri GPT") && error
+"${CGPT}" show "${MTD[@]}" ${DEV} | grep -q "Pri GPT table"
 
-$CGPT create $MTD ${DEV}
-$CGPT legacy $MTD -p ${DEV}
+"${CGPT}" create "${MTD[@]}" ${DEV}
+"${CGPT}" legacy "${MTD[@]}" -p ${DEV}
 run_basic_tests 2>/dev/null
 
-$CGPT create $MTD ${DEV}
-$CGPT legacy $MTD -p ${DEV}
+"${CGPT}" create "${MTD[@]}" ${DEV}
+"${CGPT}" legacy "${MTD[@]}" -p ${DEV}
 run_prioritize_tests 2>/dev/null
 
 # Now make sure that we don't need write access if we're just looking.
@@ -341,48 +347,47 @@
 chmod 0444 ${DEV}
 
 # These should fail
-$CGPT create $MTD -z ${DEV} 2>/dev/null && error
-$CGPT add $MTD -i 2 -P 3 ${DEV} 2>/dev/null && error
-$CGPT repair $MTD ${DEV} 2>/dev/null && error
-$CGPT prioritize $MTD -i 3 ${DEV} 2>/dev/null && error
+"${CGPT}" create "${MTD[@]}" -z ${DEV} 2>/dev/null && error
+"${CGPT}" add "${MTD[@]}" -i 2 -P 3 ${DEV} 2>/dev/null && error
+"${CGPT}" repair "${MTD[@]}" ${DEV} 2>/dev/null && error
+"${CGPT}" prioritize "${MTD[@]}" -i 3 ${DEV} 2>/dev/null && error
 
 # Most 'boot' usage should fail too.
-$CGPT boot $MTD -p ${DEV} 2>/dev/null && error
+"${CGPT}" boot "${MTD[@]}" -p ${DEV} 2>/dev/null && error
 dd if=/dev/zero of=fake_mbr.bin bs=100 count=1 2>/dev/null
-$CGPT boot $MTD -b fake_mbr.bin ${DEV} 2>/dev/null && error
-$CGPT boot $MTD -i 2 ${DEV} 2>/dev/null && error
+"${CGPT}" boot "${MTD[@]}" -b fake_mbr.bin ${DEV} 2>/dev/null && error
+"${CGPT}" boot "${MTD[@]}" -i 2 ${DEV} 2>/dev/null && error
 
-$CGPT boot $MTD ${DEV} >/dev/null
-$CGPT show $MTD ${DEV} >/dev/null
-$CGPT find $MTD -t kernel ${DEV} >/dev/null
+"${CGPT}" boot "${MTD[@]}" ${DEV} >/dev/null
+"${CGPT}" show "${MTD[@]}" ${DEV} >/dev/null
+"${CGPT}" find "${MTD[@]}" -t kernel ${DEV} >/dev/null
 
 # Enable write access again to test boundary in off device storage
 chmod 600 ${DEV}
 # GPT too small
 dd if=/dev/zero of=${DEV} bs=5632 count=1
-assert_fail $CGPT create -D 1024 ${DEV}
+assert_fail ""${CGPT}"" create -D 1024 ${DEV}
 # GPT is just right for 16 entries (512 + 512 + 16 * 128) * 2 = 6144
 dd if=/dev/zero of=${DEV} bs=6144 count=1
-$CGPT create -D 1024 ${DEV}
+"${CGPT}" create -D 1024 ${DEV}
 # Create a small 8K file to simulate Flash NOR section
 dd if=/dev/zero of=${DEV} bs=8K count=1
 # Drive size is not multiple of 512
-assert_fail $CGPT create -D 511 ${DEV}
-assert_fail $CGPT create -D 513 ${DEV}
-MTD="-D 1024"
+assert_fail ""${CGPT}"" create -D 511 ${DEV}
+assert_fail ""${CGPT}"" create -D 513 ${DEV}
 # Create a GPT table for a device of 1024 bytes (2 sectors)
-$CGPT create $MTD ${DEV}
+"${CGPT}" create -D 1024 ${DEV}
 # Make sure number of entries is reasonable for 8KiB GPT
-X=$($CGPT show -D 1024 -d ${DEV} | grep -c "Number of entries: 24")
+X=$("${CGPT}" show -D 1024 -d ${DEV} | grep -c "Number of entries: 24")
 [ "$X" = "2" ] || error
 # This fails because header verification is off due to different drive size
-assert_fail $CGPT show ${DEV}
+assert_fail ""${CGPT}"" show ${DEV}
 # But this passes because we pass in correct drive size
-$CGPT show $MTD ${DEV}
+"${CGPT}" show -D 1024 ${DEV}
 # This fails because beginning sector is over the size of the device
-assert_fail $CGPT add $MTD -b 2 -s 1 -t data ${DEV}
+assert_fail ""${CGPT}"" add -D 1024 -b 2 -s 1 -t data ${DEV}
 # This fails because partition size is over the size of the device
-assert_fail $CGPT add $MTD -b 0 -s 3 -t data ${DEV}
+assert_fail ""${CGPT}"" add -D 1024 -b 0 -s 3 -t data ${DEV}
 
 
 echo "Done."
diff --git a/tests/run_preamble_tests.sh b/tests/run_preamble_tests.sh
index 429213c..2390215 100755
--- a/tests/run_preamble_tests.sh
+++ b/tests/run_preamble_tests.sh
@@ -34,10 +34,8 @@
     for rr in $algs; do
       if [ "$r" = "$rr" ]; then
         what="verify"
-        cmp="-ne"
       else
         what="reject"
-        cmp="-eq"
       fi
       : $(( tests++ ))
       echo -n "${what} fw_${d}_${r}.vblock with root_${rr}.vbpubk ... "
@@ -45,7 +43,9 @@
         --verify "${V2DIR}/fw_${d}_${r}.vblock" \
         --signpubkey "${DATADIR}/root_${rr}.vbpubk" \
         --fv "${DATADIR}/FWDATA" >/dev/null 2>&1
-      if [ "$?" "$cmp" 0 ]; then
+      if [[ ( $? != 0 && $what == "verify" ) || \
+            ( $? == 0 && $what == "reject" ) ]]
+      then
         echo -e "${COL_RED}FAILED${COL_STOP}"
         : $(( errs++ ))
       else
@@ -62,17 +62,17 @@
     for rr in $algs; do
       if [ "$r" = "$rr" ]; then
         what="verify"
-        cmp="-ne"
       else
         what="reject"
-        cmp="-eq"
       fi
       : $(( tests++ ))
       echo -n "${what} kern_${d}_${r}.vblock with root_${rr}.vbpubk ... "
       "${FUTILITY}" vbutil_kernel \
         --verify "${V2DIR}/kern_${d}_${r}.vblock" \
         --signpubkey "${DATADIR}/root_${rr}.vbpubk" >/dev/null 2>&1
-      if [ "$?" "$cmp" 0 ]; then
+      if [[ ( $? != 0 && $what == "verify" ) || \
+            ( $? == 0 && $what == "reject" ) ]]
+      then
         echo -e "${COL_RED}FAILED${COL_STOP}"
         : $(( errs++ ))
       else
@@ -88,9 +88,9 @@
   for r in $algs; do
       : $(( tests++ ))
       echo -n "verify kern_${d}_${r}.vblock with hash only ... "
-      "${FUTILITY}" vbutil_kernel \
+      if ! "${FUTILITY}" vbutil_kernel \
           --verify "${V2DIR}/kern_${d}_${r}.vblock" >/dev/null 2>&1
-      if [ "$?" -ne 0 ]; then
+      then
         echo -e "${COL_RED}FAILED${COL_STOP}"
         : $(( errs++ ))
       else
diff --git a/tests/run_vbutil_kernel_arg_tests.sh b/tests/run_vbutil_kernel_arg_tests.sh
index 95317ec..6367662 100755
--- a/tests/run_vbutil_kernel_arg_tests.sh
+++ b/tests/run_vbutil_kernel_arg_tests.sh
@@ -44,7 +44,7 @@
   while [ "$b" -lt "${#BOOT_VALS[*]}" ]; do
     echo -n "pack kern_${k}_${b}.vblock ... "
     : $(( tests++ ))
-      "${FUTILITY}" vbutil_kernel \
+      if ! "${FUTILITY}" vbutil_kernel \
         --pack "${TMPDIR}/kern_${k}_${b}.vblock" \
         --keyblock "${KEYBLOCK}" \
         --signprivate "${SIGNPRIVATE}" \
@@ -53,7 +53,7 @@
         --config "${CONFIG}" \
         "${KERN_VALS[$k]}" \
         "${BOOT_VALS[$k]}" >/dev/null
-      if [ "$?" -ne 0 ]; then
+      then
         echo -e "${COL_RED}FAILED${COL_STOP}"
         : $(( errs++ ))
       else
@@ -65,12 +65,12 @@
 done
 
 # Now unpack it
-for v in ${TMPDIR}/kern_*.vblock; do
+for v in "${TMPDIR}"/kern_*.vblock; do
   : $(( tests++ ))
   vv=$(basename "$v")
   echo -n "verify $vv ... "
-  "${FUTILITY}" vbutil_kernel --verify "$v" >/dev/null
-  if [ "$?" -ne 0 ]; then
+  if ! "${FUTILITY}" vbutil_kernel --verify "$v" >/dev/null
+  then
     echo -e "${COL_RED}FAILED${COL_STOP}"
     : $(( errs++ ))
   else
@@ -78,9 +78,9 @@
   fi
   : $(( tests++ ))
   echo -n "verify $vv signed ... "
-  "${FUTILITY}" vbutil_kernel --verify "$v" \
+  if ! "${FUTILITY}" vbutil_kernel --verify "$v" \
     --signpubkey "${SIGNPUBLIC}" >/dev/null
-  if [ "$?" -ne 0 ]; then
+  then
     echo -e "${COL_RED}FAILED${COL_STOP}"
     : $(( errs++ ))
   else
@@ -100,7 +100,7 @@
 USB_SIGNPUBKEY="${DEVKEYS}/recovery_key.vbpubk"
 echo -n "pack USB kernel ... "
 : $(( tests++ ))
-"${FUTILITY}" vbutil_kernel \
+if ! "${FUTILITY}" vbutil_kernel \
   --pack "${USB_KERN}" \
   --keyblock "${USB_KEYBLOCK}" \
   --signprivate "${USB_SIGNPRIVATE}" \
@@ -109,7 +109,7 @@
   --bootloader "${BIG}" \
   --vmlinuz "${BIG}" \
   --arch arm
-if [ "$?" -ne 0 ]; then
+then
   echo -e "${COL_RED}FAILED${COL_STOP}"
   : $(( errs++ ))
 else
@@ -119,10 +119,10 @@
 # And verify it.
 echo -n "verify USB kernel ... "
 : $(( tests++ ))
-"${FUTILITY}" vbutil_kernel \
+if ! "${FUTILITY}" vbutil_kernel \
   --verify "${USB_KERN}" \
   --signpubkey "${USB_SIGNPUBKEY}" >/dev/null
-if [ "$?" -ne 0 ]; then
+then
   echo -e "${COL_RED}FAILED${COL_STOP}"
   : $(( errs++ ))
 else
@@ -139,13 +139,13 @@
 SSD_SIGNPUBKEY="${DEVKEYS}/kernel_subkey.vbpubk"
 echo -n "repack to SSD kernel ... "
 : $(( tests++ ))
-"${FUTILITY}" vbutil_kernel \
+if ! "${FUTILITY}" vbutil_kernel \
   --repack "${SSD_KERN}" \
   --vblockonly \
   --keyblock "${SSD_KEYBLOCK}" \
   --signprivate "${SSD_SIGNPRIVATE}" \
   --oldblob "${TMPDIR}/usb_kern.bin" >/dev/null
-if [ "$?" -ne 0 ]; then
+then
   echo -e "${COL_RED}FAILED${COL_STOP}"
   : $(( errs++ ))
 else
@@ -155,14 +155,14 @@
 # To verify it, we have to replace the vblock from the original image.
 tempfile="${TMPDIR}/foo.bin"
 cat "${SSD_KERN}" > "$tempfile"
-dd if="${USB_KERN}" bs=65536 skip=1 >> $tempfile 2>/dev/null
+dd if="${USB_KERN}" bs=65536 skip=1 >> "$tempfile" 2>/dev/null
 
 echo -n "verify SSD kernel ... "
 : $(( tests++ ))
-"${FUTILITY}" vbutil_kernel \
+if ! "${FUTILITY}" vbutil_kernel \
   --verify "$tempfile" \
   --signpubkey "${SSD_SIGNPUBKEY}" >/dev/null
-if [ "$?" -ne 0 ]; then
+then
   echo -e "${COL_RED}FAILED${COL_STOP}"
   : $(( errs++ ))
 else
@@ -170,7 +170,7 @@
 fi
 
 # Finally make sure that the kernel command line stays good.
-orig=$(cat "${CONFIG}" | tr '\012' ' ')
+orig=$(tr '\012' ' ' < "${CONFIG}")
 packed=$("${FUTILITY}" dump_kernel_config "${USB_KERN}")
 echo -n "check USB kernel config ..."
 : $(( tests++ ))
@@ -184,7 +184,7 @@
 repacked=$("${FUTILITY}" dump_kernel_config "${tempfile}")
 echo -n "check SSD kernel config ..."
 : $(( tests++ ))
-if [ "$orig" != "$packed" ]; then
+if [ "$orig" != "$repacked" ]; then
   echo -e "${COL_RED}FAILED${COL_STOP}"
   : $(( errs++ ))
 else
diff --git a/tests/run_vbutil_tests.sh b/tests/run_vbutil_tests.sh
index 84f66d1..b9db2a7 100755
--- a/tests/run_vbutil_tests.sh
+++ b/tests/run_vbutil_tests.sh
@@ -18,21 +18,19 @@
 
     echo -e "For signing key ${COL_YELLOW}RSA-$keylen/$hashalgo${COL_STOP}:"
     # Pack the key
-    ${FUTILITY} vbutil_key \
-        --pack ${TESTKEY_SCRATCH_DIR}/key_alg${algonum}.vbpubk \
-        --key ${TESTKEY_DIR}/key_rsa${keylen}.keyb \
+    if ! "${FUTILITY}" vbutil_key \
+        --pack "${TESTKEY_SCRATCH_DIR}/key_alg${algonum}.vbpubk" \
+        --key "${TESTKEY_DIR}/key_rsa${keylen}.keyb" \
         --version 1 \
-        --algorithm $algonum
-    if [ $? -ne 0 ]
+        --algorithm "${algonum}"
     then
         return_code=255
     fi
 
     # Unpack the key
     # TODO: should verify we get the same key back out?
-    ${FUTILITY} vbutil_key \
-        --unpack ${TESTKEY_SCRATCH_DIR}/key_alg${algonum}.vbpubk
-    if [ $? -ne 0 ]
+    if ! "${FUTILITY}" vbutil_key \
+        --unpack "${TESTKEY_SCRATCH_DIR}/key_alg${algonum}.vbpubk"
     then
         return_code=255
     fi
@@ -40,12 +38,12 @@
 
 function test_vbutil_key_all {
   algorithmcounter=0
-  for keylen in ${key_lengths[@]}
+  for keylen in "${key_lengths[@]}"
   do
-      for hashalgo in ${hash_algos[@]}
+      for hashalgo in "${hash_algos[@]}"
       do
-          test_vbutil_key_single $algorithmcounter $keylen $hashalgo
-          let algorithmcounter=algorithmcounter+1
+          test_vbutil_key_single "$algorithmcounter" "$keylen" "$hashalgo"
+          algorithmcounter=$((algorithmcounter + 1))
       done
   done
 }
@@ -72,49 +70,45 @@
           keyblockfile="${TESTKEY_SCRATCH_DIR}/"
           keyblockfile+="sign${signing_algonum}_data"
           keyblockfile+="${data_algonum}.keyblock"
-          rm -f ${keyblockfile}
+          rm -f "${keyblockfile}"
 
           # Wrap private key
-          ${FUTILITY} vbutil_key \
-            --pack ${TESTKEY_SCRATCH_DIR}/key_alg${algonum}.vbprivk \
-            --key ${TESTKEY_DIR}/key_rsa${signing_keylen}.pem \
-            --algorithm $signing_algonum
-          if [ $? -ne 0 ]
+          if ! "${FUTILITY}" vbutil_key \
+            --pack "${TESTKEY_SCRATCH_DIR}/key_alg${algonum}.vbprivk" \
+            --key "${TESTKEY_DIR}/key_rsa${signing_keylen}.pem" \
+            --algorithm "${signing_algonum}"
           then
             echo -e "${COL_RED}Wrap vbprivk${COL_STOP}"
             return_code=255
           fi
 
           # Wrap public key
-          ${FUTILITY} vbutil_key \
-            --pack ${TESTKEY_SCRATCH_DIR}/key_alg${algonum}.vbpubk \
-            --key ${TESTKEY_DIR}/key_rsa${signing_keylen}.keyb \
-            --algorithm $signing_algonum
-          if [ $? -ne 0 ]
+          if ! "${FUTILITY}" vbutil_key \
+            --pack "${TESTKEY_SCRATCH_DIR}/key_alg${algonum}.vbpubk" \
+            --key "${TESTKEY_DIR}/key_rsa${signing_keylen}.keyb" \
+            --algorithm "${signing_algonum}"
           then
             echo -e "${COL_RED}Wrap vbpubk${COL_STOP}"
             return_code=255
           fi
 
           # Pack
-          ${FUTILITY} vbutil_keyblock --pack ${keyblockfile} \
+          if ! "${FUTILITY}" vbutil_keyblock --pack "${keyblockfile}" \
             --datapubkey \
-              ${TESTKEY_SCRATCH_DIR}/key_alg${data_algonum}.vbpubk \
+              "${TESTKEY_SCRATCH_DIR}/key_alg${data_algonum}.vbpubk" \
             --signprivate \
-              ${TESTKEY_SCRATCH_DIR}/key_alg${algonum}.vbprivk
-          if [ $? -ne 0 ]
+              "${TESTKEY_SCRATCH_DIR}/key_alg${algonum}.vbprivk"
           then
             echo -e "${COL_RED}Pack${COL_STOP}"
             return_code=255
           fi
 
           # Unpack
-          ${FUTILITY} vbutil_keyblock --unpack ${keyblockfile} \
+          if ! "${FUTILITY}" vbutil_keyblock --unpack "${keyblockfile}" \
             --datapubkey \
-            ${TESTKEY_SCRATCH_DIR}/key_alg${data_algonum}.vbpubk2 \
+              "${TESTKEY_SCRATCH_DIR}/key_alg${data_algonum}.vbpubk2" \
             --signpubkey \
-            ${TESTKEY_SCRATCH_DIR}/key_alg${algonum}.vbpubk
-          if [ $? -ne 0 ]
+              "${TESTKEY_SCRATCH_DIR}/key_alg${algonum}.vbpubk"
           then
             echo -e "${COL_RED}Unpack${COL_STOP}"
             return_code=255
@@ -122,8 +116,8 @@
 
           # Check
           if ! cmp -s \
-            ${TESTKEY_SCRATCH_DIR}/key_alg${data_algonum}.vbpubk \
-            ${TESTKEY_SCRATCH_DIR}/key_alg${data_algonum}.vbpubk2
+            "${TESTKEY_SCRATCH_DIR}/key_alg${data_algonum}.vbpubk" \
+            "${TESTKEY_SCRATCH_DIR}/key_alg${data_algonum}.vbpubk2"
           then
             echo -e "${COL_RED}Check${COL_STOP}"
             return_code=255
@@ -134,27 +128,24 @@
 external signer.${COL_STOP}"
           # Pack using external signer
           # Pack
-          ${FUTILITY} vbutil_keyblock --pack ${keyblockfile} \
+          if ! "${FUTILITY}" vbutil_keyblock --pack "${keyblockfile}" \
             --datapubkey \
-              ${TESTKEY_SCRATCH_DIR}/key_alg${data_algonum}.vbpubk \
+              "${TESTKEY_SCRATCH_DIR}/key_alg${data_algonum}.vbpubk" \
             --signprivate_pem \
-              ${TESTKEY_DIR}/key_rsa${signing_keylen}.pem \
+              "${TESTKEY_DIR}/key_rsa${signing_keylen}.pem" \
             --pem_algorithm "${signing_algonum}" \
             --externalsigner "${SCRIPT_DIR}/external_rsa_signer.sh"
-
-          if [ $? -ne 0 ]
           then
             echo -e "${COL_RED}Pack${COL_STOP}"
             return_code=255
           fi
 
           # Unpack
-          ${FUTILITY} vbutil_keyblock --unpack ${keyblockfile} \
+          if ! "${FUTILITY}" vbutil_keyblock --unpack "${keyblockfile}" \
             --datapubkey \
-            ${TESTKEY_SCRATCH_DIR}/key_alg${data_algonum}.vbpubk2 \
+            "${TESTKEY_SCRATCH_DIR}/key_alg${data_algonum}.vbpubk2" \
             --signpubkey \
-            ${TESTKEY_SCRATCH_DIR}/key_alg${signing_algonum}.vbpubk
-          if [ $? -ne 0 ]
+            "${TESTKEY_SCRATCH_DIR}/key_alg${signing_algonum}.vbpubk"
           then
             echo -e "${COL_RED}Unpack${COL_STOP}"
             return_code=255
@@ -162,8 +153,8 @@
 
           # Check
           if ! cmp -s \
-            ${TESTKEY_SCRATCH_DIR}/key_alg${data_algonum}.vbpubk \
-            ${TESTKEY_SCRATCH_DIR}/key_alg${data_algonum}.vbpubk2
+            "${TESTKEY_SCRATCH_DIR}/key_alg${data_algonum}.vbpubk" \
+            "${TESTKEY_SCRATCH_DIR}/key_alg${data_algonum}.vbpubk2"
           then
             echo -e "${COL_RED}Check${COL_STOP}"
             return_code=255
@@ -177,22 +168,22 @@
 # kernel signing algorithm
   signing_algorithmcounter=0
   data_algorithmcounter=0
-  for signing_keylen in ${key_lengths[@]}
+  for signing_keylen in "${key_lengths[@]}"
   do
-    for signing_hashalgo in ${hash_algos[@]}
+    for signing_hashalgo in "${hash_algos[@]}"
     do
-      let data_algorithmcounter=0
-      for datakeylen in ${key_lengths[@]}
+      data_algorithmcounter=0
+      for datakeylen in "${key_lengths[@]}"
       do
-        for datahashalgo in ${hash_algos[@]}
+        for datahashalgo in "${hash_algos[@]}"
         do
           test_vbutil_keyblock_single \
-                $signing_algorithmcounter $signing_keylen $signing_hashalgo \
-                $data_algorithmcounter $data_keylen $data_hashalgo
-          let data_algorithmcounter=data_algorithmcounter+1
+            "$signing_algorithmcounter" "$signing_keylen" "$signing_hashalgo" \
+            "$data_algorithmcounter" "$data_keylen" "$data_hashalgo"
+          data_algorithmcounter=$((data_algorithmcounter + 1))
         done
       done
-      let signing_algorithmcounter=signing_algorithmcounter+1
+      signing_algorithmcounter=$((signing_algorithmcounter + 1))
     done
   done
 }
@@ -223,4 +214,3 @@
 fi
 
 exit $return_code
-
diff --git a/tests/sha_benchmark.c b/tests/sha_benchmark.c
index 27b1c10..d7ba31a 100644
--- a/tests/sha_benchmark.c
+++ b/tests/sha_benchmark.c
@@ -10,8 +10,8 @@
 #include "2common.h"
 #include "2sha.h"
 #include "2sysincludes.h"
+#include "common/timer_utils.h"
 #include "host_common.h"
-#include "timer_utils.h"
 
 #define TEST_BUFFER_SIZE 4000000
 
diff --git a/tests/subprocess_tests.c b/tests/subprocess_tests.c
index 75d6e49..d65618f 100644
--- a/tests/subprocess_tests.c
+++ b/tests/subprocess_tests.c
@@ -7,7 +7,7 @@
 #include <string.h>
 
 #include "subprocess.h"
-#include "test_common.h"
+#include "common/tests.h"
 
 #define TEST_STRING "hello world"
 #define TEST_STRING_LN TEST_STRING "\n"
diff --git a/tests/test_using_qemu.sh b/tests/test_using_qemu.sh
index 081d963..5ed2974 100755
--- a/tests/test_using_qemu.sh
+++ b/tests/test_using_qemu.sh
@@ -24,11 +24,11 @@
 
 # Don't exit on error, so we can capture the error code
 set +e
-sudo chroot ${SYSROOT} ${QEMU_RUN} \
+sudo chroot "${SYSROOT}" "${QEMU_RUN}" \
     -E LD_LIBRARY_PATH=/lib64:/lib:/usr/lib64:/usr/lib \
-    -E HOME=${HOME} \
-    -E BUILD=${BUILD_RUN} \
-    -- $*
+    -E HOME="${HOME}" \
+    -E BUILD="${BUILD_RUN}" \
+    -- "$@"
 exit_code=$?
 set -e
 
diff --git a/tests/tlcl_tests.c b/tests/tlcl_tests.c
index 328abbe..62d34af 100644
--- a/tests/tlcl_tests.c
+++ b/tests/tlcl_tests.c
@@ -11,8 +11,8 @@
 #include <string.h>
 
 #include "2api.h"
+#include "common/tests.h"
 #include "host_common.h"
-#include "test_common.h"
 #include "tlcl.h"
 #include "tlcl_internal.h"
 
diff --git a/tests/tpm_lite/tpmtest_fastenable.c b/tests/tpm_lite/tpmtest_fastenable.c
index a93dfd7..226a41f 100644
--- a/tests/tpm_lite/tpmtest_fastenable.c
+++ b/tests/tpm_lite/tpmtest_fastenable.c
@@ -15,7 +15,7 @@
 #include <stdio.h>
 
 #include "host_common.h"
-#include "test_common.h"
+#include "common/tests.h"
 #include "tlcl.h"
 #include "tlcl_tests.h"
 
diff --git a/tests/tpm_lite/tpmtest_globallock.c b/tests/tpm_lite/tpmtest_globallock.c
index 14f16de..52f7c8e 100644
--- a/tests/tpm_lite/tpmtest_globallock.c
+++ b/tests/tpm_lite/tpmtest_globallock.c
@@ -11,7 +11,7 @@
 #include <stdlib.h>
 
 #include "host_common.h"
-#include "test_common.h"
+#include "common/tests.h"
 #include "tlcl.h"
 #include "tlcl_tests.h"
 
diff --git a/tests/tpm_lite/tpmtest_redefine_unowned.c b/tests/tpm_lite/tpmtest_redefine_unowned.c
index 0f89870..45bcd93 100644
--- a/tests/tpm_lite/tpmtest_redefine_unowned.c
+++ b/tests/tpm_lite/tpmtest_redefine_unowned.c
@@ -11,7 +11,7 @@
 #include <stdlib.h>
 
 #include "host_common.h"
-#include "test_common.h"
+#include "common/tests.h"
 #include "tlcl.h"
 #include "tlcl_tests.h"
 
diff --git a/tests/tpm_lite/tpmtest_spaceperm.c b/tests/tpm_lite/tpmtest_spaceperm.c
index 8c9232b..70f2b99 100644
--- a/tests/tpm_lite/tpmtest_spaceperm.c
+++ b/tests/tpm_lite/tpmtest_spaceperm.c
@@ -11,7 +11,7 @@
 #include <stdlib.h>
 
 #include "host_common.h"
-#include "test_common.h"
+#include "common/tests.h"
 #include "tlcl.h"
 #include "tlcl_tests.h"
 
diff --git a/tests/tpm_lite/tpmtest_writelimit.c b/tests/tpm_lite/tpmtest_writelimit.c
index 05e4a17..71562b0 100644
--- a/tests/tpm_lite/tpmtest_writelimit.c
+++ b/tests/tpm_lite/tpmtest_writelimit.c
@@ -12,7 +12,7 @@
 
 #include "2common.h"
 #include "host_common.h"
-#include "test_common.h"
+#include "common/tests.h"
 #include "tlcl.h"
 #include "tlcl_tests.h"
 
diff --git a/tests/vb20_api_kernel_tests.c b/tests/vb20_api_kernel_tests.c
index b835251..bc585b4 100644
--- a/tests/vb20_api_kernel_tests.c
+++ b/tests/vb20_api_kernel_tests.c
@@ -14,7 +14,7 @@
 #include "2rsa.h"
 #include "2secdata.h"
 #include "2sysincludes.h"
-#include "test_common.h"
+#include "common/tests.h"
 #include "vboot_struct.h"
 
 /* Common context for tests */
diff --git a/tests/vb20_kernel_tests.c b/tests/vb20_kernel_tests.c
index 8766114..05f21ab 100644
--- a/tests/vb20_kernel_tests.c
+++ b/tests/vb20_kernel_tests.c
@@ -14,7 +14,7 @@
 #include "2rsa.h"
 #include "2secdata.h"
 #include "2sysincludes.h"
-#include "test_common.h"
+#include "common/tests.h"
 
 /* Common context for tests */
 static uint8_t workbuf[VB2_KERNEL_WORKBUF_RECOMMENDED_SIZE]
diff --git a/tests/vb20_rsa_padding_tests.c b/tests/vb20_rsa_padding_tests.c
index 90ff42a..68c32a9 100644
--- a/tests/vb20_rsa_padding_tests.c
+++ b/tests/vb20_rsa_padding_tests.c
@@ -9,10 +9,10 @@
 #include "2common.h"
 #include "2rsa.h"
 #include "2sysincludes.h"
+#include "common/tests.h"
 #include "file_keys.h"
 #include "host_key.h"
 #include "rsa_padding_test.h"
-#include "test_common.h"
 
 vb2_error_t hwcrypto_modexp_return_value = VB2_SUCCESS;
 vb2_error_t vb2ex_hwcrypto_modexp(const struct vb2_public_key *key,
diff --git a/tests/vb21_host_common2_tests.c b/tests/vb21_host_common2_tests.c
index b2ceb95..61ba986 100644
--- a/tests/vb21_host_common2_tests.c
+++ b/tests/vb21_host_common2_tests.c
@@ -12,11 +12,11 @@
 #include "2common.h"
 #include "2rsa.h"
 #include "2sysincludes.h"
+#include "common/tests.h"
 #include "host_common.h"
 #include "host_common21.h"
 #include "host_key21.h"
 #include "host_signature21.h"
-#include "test_common.h"
 #include "util_misc.h"
 
 static const uint8_t test_data[] = "This is some test data to sign.";
diff --git a/tests/vb21_host_common_tests.c b/tests/vb21_host_common_tests.c
index 3ab5dbc..385a0c2 100644
--- a/tests/vb21_host_common_tests.c
+++ b/tests/vb21_host_common_tests.c
@@ -8,10 +8,10 @@
 #include "2common.h"
 #include "2rsa.h"
 #include "2sysincludes.h"
+#include "common/tests.h"
 #include "host_common21.h"
 #include "host_key21.h"
 #include "host_signature21.h"
-#include "test_common.h"
 
 static const uint8_t test_data[] = "This is some test data to sign.";
 
diff --git a/tests/vb21_host_key_tests.c b/tests/vb21_host_key_tests.c
index e8eef9b..e0a1105 100644
--- a/tests/vb21_host_key_tests.c
+++ b/tests/vb21_host_key_tests.c
@@ -11,10 +11,10 @@
 #include "2common.h"
 #include "2rsa.h"
 #include "2sysincludes.h"
+#include "common/tests.h"
 #include "host_common.h"
 #include "host_common21.h"
 #include "host_key21.h"
-#include "test_common.h"
 
 /* Test only the algorithms we use */
 struct alg_combo {
diff --git a/tests/vb21_host_misc_tests.c b/tests/vb21_host_misc_tests.c
index f437803..dbf12e0 100644
--- a/tests/vb21_host_misc_tests.c
+++ b/tests/vb21_host_misc_tests.c
@@ -10,10 +10,10 @@
 
 #include "2common.h"
 #include "2sysincludes.h"
+#include "common/tests.h"
 #include "host_common.h"
 #include "host_common21.h"
 #include "host_misc.h"
-#include "test_common.h"
 
 static void misc_tests(void)
 {
diff --git a/tests/vb21_host_sig_tests.c b/tests/vb21_host_sig_tests.c
index f337eaa..4a9abff 100644
--- a/tests/vb21_host_sig_tests.c
+++ b/tests/vb21_host_sig_tests.c
@@ -11,11 +11,11 @@
 #include "2common.h"
 #include "2rsa.h"
 #include "2sysincludes.h"
+#include "common/tests.h"
 #include "host_common.h"
 #include "host_common21.h"
 #include "host_key21.h"
 #include "host_signature21.h"
-#include "test_common.h"
 
 /* Test only the algorithms we use */
 struct alg_combo {
diff --git a/tests/vb2_api_tests.c b/tests/vb2_api_tests.c
index 15c9bd1..ca2f7e9 100644
--- a/tests/vb2_api_tests.c
+++ b/tests/vb2_api_tests.c
@@ -14,7 +14,7 @@
 #include "2rsa.h"
 #include "2secdata.h"
 #include "2sysincludes.h"
-#include "test_common.h"
+#include "common/tests.h"
 
 /* Common context for tests */
 
@@ -135,8 +135,10 @@
 
 vb2_error_t vb2_check_dev_switch(struct vb2_context *c)
 {
-	if (force_dev_mode)
+	if (force_dev_mode) {
+		c->flags |= VB2_CONTEXT_DEVELOPER_MODE;
 		sd->flags |= VB2_SD_FLAG_DEV_MODE_ENABLED;
+	}
 	return retval_vb2_check_dev_switch;
 }
 
diff --git a/tests/vb2_auxfw_sync_tests.c b/tests/vb2_auxfw_sync_tests.c
index bcdd67a..b9c869a 100644
--- a/tests/vb2_auxfw_sync_tests.c
+++ b/tests/vb2_auxfw_sync_tests.c
@@ -13,9 +13,8 @@
 #include "2misc.h"
 #include "2nvstorage.h"
 #include "2sysincludes.h"
+#include "common/tests.h"
 #include "host_common.h"
-#include "load_kernel_fw.h"
-#include "test_common.h"
 #include "vboot_struct.h"
 
 /* Mock data */
@@ -25,7 +24,8 @@
 static struct vb2_shared_data *sd;
 static struct vb2_gbb_header gbb;
 
-static vb2_error_t auxfw_retval;
+static vb2_error_t auxfw_check_retval;
+static vb2_error_t auxfw_update_retval;
 static int auxfw_update_req;
 static enum vb2_auxfw_update_severity auxfw_mock_severity;
 static enum vb2_auxfw_update_severity auxfw_update_severity;
@@ -46,7 +46,8 @@
 
 	memset(&gbb, 0, sizeof(gbb));
 
-	auxfw_retval = VB2_SUCCESS;
+	auxfw_check_retval = VB2_SUCCESS;
+	auxfw_update_retval = VB2_SUCCESS;
 	auxfw_mock_severity = VB2_AUXFW_NO_UPDATE;
 	auxfw_update_severity = VB2_AUXFW_NO_UPDATE;
 	auxfw_mock_display_available = 1;
@@ -65,7 +66,7 @@
 {
 	*severity = auxfw_mock_severity;
 	auxfw_update_severity = auxfw_mock_severity;
-	return VB2_SUCCESS;
+	return auxfw_check_retval;
 }
 
 vb2_error_t vb2ex_auxfw_update(void)
@@ -77,7 +78,7 @@
 	if (auxfw_update_severity != VB2_AUXFW_NO_DEVICE &&
 	    auxfw_update_severity != VB2_AUXFW_NO_UPDATE)
 		auxfw_update_req = 1;
-	return auxfw_retval;
+	return auxfw_update_retval;
 }
 
 vb2_error_t vb2ex_auxfw_finalize(struct vb2_context *c)
@@ -152,7 +153,13 @@
 
 	ResetMocks();
 	auxfw_mock_severity = VB2_AUXFW_FAST_UPDATE;
-	auxfw_retval = VB2_ERROR_UNKNOWN;
+	auxfw_check_retval = VB2_ERROR_UNKNOWN;
+	test_auxsync(VB2_ERROR_UNKNOWN, VB2_RECOVERY_AUXFW_UPDATE,
+		     "Error checking auxfw");
+
+	ResetMocks();
+	auxfw_mock_severity = VB2_AUXFW_FAST_UPDATE;
+	auxfw_update_retval = VB2_ERROR_UNKNOWN;
 	test_auxsync(VB2_ERROR_UNKNOWN, VB2_RECOVERY_AUXFW_UPDATE,
 		     "Error updating auxfw");
 }
diff --git a/tests/vb2_common2_tests.c b/tests/vb2_common2_tests.c
index 5f065f8..e793e57 100644
--- a/tests/vb2_common2_tests.c
+++ b/tests/vb2_common2_tests.c
@@ -12,10 +12,10 @@
 #include "2common.h"
 #include "2rsa.h"
 #include "2sysincludes.h"
+#include "common/tests.h"
 #include "file_keys.h"
 #include "host_common.h"
 #include "host_key21.h"
-#include "test_common.h"
 
 static const uint8_t test_data[] = "This is some test data to sign.";
 static const uint32_t test_size = sizeof(test_data);
diff --git a/tests/vb2_common3_tests.c b/tests/vb2_common3_tests.c
index 5805204..8e7a20c 100644
--- a/tests/vb2_common3_tests.c
+++ b/tests/vb2_common3_tests.c
@@ -10,12 +10,12 @@
 #include "2common.h"
 #include "2rsa.h"
 #include "2sysincludes.h"
+#include "common/tests.h"
 #include "file_keys.h"
 #include "host_common.h"
 #include "host_keyblock.h"
 #include "host_key.h"
 #include "host_signature.h"
-#include "test_common.h"
 
 static void resign_keyblock(struct vb2_keyblock *h,
 			    const struct vb2_private_key *key)
diff --git a/tests/vb2_common_tests.c b/tests/vb2_common_tests.c
index d76c402..beb8461 100644
--- a/tests/vb2_common_tests.c
+++ b/tests/vb2_common_tests.c
@@ -7,7 +7,7 @@
 
 #include "2common.h"
 #include "2sysincludes.h"
-#include "test_common.h"
+#include "common/tests.h"
 #include "vboot_struct.h"  /* For old struct sizes */
 
 /* Mock data */
diff --git a/tests/vb2_crypto_tests.c b/tests/vb2_crypto_tests.c
index c7d6676..6696feb 100644
--- a/tests/vb2_crypto_tests.c
+++ b/tests/vb2_crypto_tests.c
@@ -12,7 +12,7 @@
 #include "2rsa.h"
 #include "2sha.h"
 #include "2sysincludes.h"
-#include "test_common.h"
+#include "common/tests.h"
 #include "vboot_host.h"
 
 static void hash_algorithm_name_tests(void)
diff --git a/tests/vb2_ec_sync_tests.c b/tests/vb2_ec_sync_tests.c
index f4631b7..f317561 100644
--- a/tests/vb2_ec_sync_tests.c
+++ b/tests/vb2_ec_sync_tests.c
@@ -10,9 +10,8 @@
 #include "2nvstorage.h"
 #include "2secdata.h"
 #include "2sysincludes.h"
+#include "common/tests.h"
 #include "host_common.h"
-#include "load_kernel_fw.h"
-#include "test_common.h"
 #include "vboot_struct.h"
 
 /* Mock data */
diff --git a/tests/vb2_firmware_tests.c b/tests/vb2_firmware_tests.c
index 681e5d6..933f608 100644
--- a/tests/vb2_firmware_tests.c
+++ b/tests/vb2_firmware_tests.c
@@ -14,7 +14,7 @@
 #include "2rsa.h"
 #include "2secdata.h"
 #include "2sysincludes.h"
-#include "test_common.h"
+#include "common/tests.h"
 
 /* Common context for tests */
 static uint8_t workbuf[VB2_FIRMWARE_WORKBUF_RECOMMENDED_SIZE]
diff --git a/tests/vb2_firmware_tests.sh b/tests/vb2_firmware_tests.sh
index 88b6e6a..51803e2 100755
--- a/tests/vb2_firmware_tests.sh
+++ b/tests/vb2_firmware_tests.sh
@@ -50,34 +50,41 @@
 	local fw_algo=$2
 	local kern_algo=$3
 
-	local root_rsa="$(algo_to_rsa ${root_algo})"
-	local fw_rsa="$(algo_to_rsa ${fw_algo})"
-	local kern_rsa="$(algo_to_rsa ${kern_algo})"
+	local root_rsa
+	local fw_rsa
+	local kern_rsa
+	root_rsa="$(algo_to_rsa "${root_algo}")"
+	fw_rsa="$(algo_to_rsa "${fw_algo}")"
+	kern_rsa="$(algo_to_rsa "${kern_algo}")"
 
-	local root_sha="$(algo_to_sha ${root_algo})"
-	local fw_sha="$(algo_to_sha ${fw_algo})"
-	local kern_sha="$(algo_to_sha ${kern_algo})"
+	local root_sha
+	local fw_sha
+	root_sha="$(algo_to_sha "${root_algo}")"
+	fw_sha="$(algo_to_sha "${fw_algo}")"
 
 	# Pack keys using original vboot utilities
-	${FUTILITY} vbutil_key --pack rootkey.test \
-	    --key "${TESTKEY_DIR}/key_${root_rsa}.keyb" --algorithm ${root_algo}
-	${FUTILITY} vbutil_key --pack fwsubkey.test \
-	    --key "${TESTKEY_DIR}/key_${fw_rsa}.keyb" --algorithm ${fw_algo}
-	${FUTILITY} vbutil_key --pack kernkey.test \
-	    --key "${TESTKEY_DIR}/key_${kern_rsa}.keyb" --algorithm ${kern_algo}
+	"${FUTILITY}" vbutil_key --pack rootkey.test \
+	    --key "${TESTKEY_DIR}/key_${root_rsa}.keyb" \
+	    --algorithm "${root_algo}"
+	"${FUTILITY}" vbutil_key --pack fwsubkey.test \
+	    --key "${TESTKEY_DIR}/key_${fw_rsa}.keyb" \
+	    --algorithm "${fw_algo}"
+	"${FUTILITY}" vbutil_key --pack kernkey.test \
+	    --key "${TESTKEY_DIR}/key_${kern_rsa}.keyb" \
+	    --algorithm "${kern_algo}"
 
 	# Create a GBB with the root key
-	${FUTILITY} gbb -c 128,2400,0,0 gbb.test
-	${FUTILITY} gbb gbb.test -s --hwid='Test GBB' \
+	"${FUTILITY}" gbb -c 128,2400,0,0 gbb.test
+	"${FUTILITY}" gbb gbb.test -s --hwid='Test GBB' \
 	  --rootkey=rootkey.test
 
 	# Keyblock with firmware subkey is signed by root key
-	${FUTILITY} vbutil_keyblock --pack keyblock.test \
+	"${FUTILITY}" vbutil_keyblock --pack keyblock.test \
 	    --datapubkey fwsubkey.test \
 	    --signprivate &quo