vboot2: misc higher-level routines

I'm breaking the last chunk of vboot2 into smaller pieces as I add
tests.  This has a bunch of misc routines like the dev switch logic
and GBB header parsing.

BUG=chromium:370082
BRANCH=none
TEST=make clean && VBOOT2=1 COV=1 make

Change-Id: I0f67400d9b59ec21ed5cc155a9b774fd37eb559b
Signed-off-by: Randall Spangler <rspangler@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/203374
Reviewed-by: Bill Richardson <wfrichar@chromium.org>
diff --git a/Makefile b/Makefile
index 2b5c1e2..d4d004d 100644
--- a/Makefile
+++ b/Makefile
@@ -322,6 +322,10 @@
 VBSLK_SRCS += \
 	firmware/stub/vboot_api_stub.c \
 	firmware/stub/vboot_api_stub_disk.c
+
+FWLIB2_SRCS += \
+	firmware/2lib/2stub.c
+
 endif
 
 VBSF_SRCS += ${VBINIT_SRCS}
diff --git a/firmware/2lib/2misc.c b/firmware/2lib/2misc.c
index 9713ffc..c0692c5 100644
--- a/firmware/2lib/2misc.c
+++ b/firmware/2lib/2misc.c
@@ -14,6 +14,93 @@
 #include "2sha.h"
 #include "2rsa.h"
 
+void vb2_workbuf_from_ctx(struct vb2_context *ctx, struct vb2_workbuf *wb)
+{
+	vb2_workbuf_init(wb, ctx->workbuf + ctx->workbuf_used,
+			 ctx->workbuf_size - ctx->workbuf_used);
+}
+
+int vb2_read_gbb_header(struct vb2_context *ctx, struct vb2_gbb_header *gbb)
+{
+	static const uint8_t expect_sig[VB2_GBB_SIGNATURE_SIZE] =
+		VB2_GBB_SIGNATURE;
+	int rv;
+
+	/* Read the entire header */
+	rv = vb2ex_read_resource(ctx, VB2_RES_GBB, 0, gbb, sizeof(*gbb));
+	if (rv)
+		return rv;
+
+	/* Make sure it's really a GBB */
+	if (memcmp(gbb->signature, expect_sig, sizeof(expect_sig)))
+		return VB2_ERROR_GBB_MAGIC;
+
+	/* Check for compatible version */
+	if (gbb->major_version != VB2_GBB_MAJOR_VER)
+		return VB2_ERROR_GBB_VERSION;
+
+	/* Current code is not backwards-compatible to 1.0 headers */
+	if (gbb->minor_version < VB2_GBB_MINOR_VER)
+		return VB2_ERROR_GBB_TOO_OLD;
+
+	/*
+	 * Header size should be at least as big as we expect.  It could be
+	 * bigger, if the header has grown.
+	 */
+	if (gbb->header_size < sizeof(*gbb))
+		return VB2_ERROR_GBB_HEADER_SIZE;
+
+	return VB2_SUCCESS;
+}
+
+void vb2_fail(struct vb2_context *ctx, uint8_t reason, uint8_t subcode)
+{
+	struct vb2_shared_data *sd = vb2_get_sd(ctx);
+
+	/* If NV data hasn't been initialized, initialize it now */
+	if (!(sd->status & VB2_SD_STATUS_NV_INIT))
+		vb2_nv_init(ctx);
+
+	/* See if we were far enough in the boot process to choose a slot */
+	if (sd->status & VB2_SD_STATUS_CHOSE_SLOT) {
+
+		/* Boot failed */
+		vb2_nv_set(ctx, VB2_NV_FW_RESULT, VB2_FW_RESULT_FAILURE);
+
+		/* Use up remaining tries */
+		vb2_nv_set(ctx, VB2_NV_TRY_COUNT, 0);
+
+		/*
+		 * Try the other slot next time.  We'll alternate
+		 * between slots, which may help if one or both slots is
+		 * flaky.
+		 */
+		vb2_nv_set(ctx, VB2_NV_TRY_NEXT, 1 - sd->fw_slot);
+
+		/*
+		 * If we didn't try the other slot last boot, or we tried it
+		 * and it didn't fail, try it next boot.
+		 */
+		if (sd->last_fw_slot != 1 - sd->fw_slot ||
+		    sd->last_fw_result != VB2_FW_RESULT_FAILURE)
+			return;
+	}
+
+	/*
+	 * If we're still here, we failed before choosing a slot, or both
+	 * this slot and the other slot failed in successive boots.  So we
+	 * need to go to recovery.
+	 *
+	 * Set a recovery reason and subcode only if they're not already set.
+	 * If recovery is already requested, it's a more specific error code
+	 * than later code is providing and we shouldn't overwrite it.
+	 */
+	if (!vb2_nv_get(ctx, VB2_NV_RECOVERY_REQUEST)) {
+		vb2_nv_set(ctx, VB2_NV_RECOVERY_REQUEST, reason);
+		vb2_nv_set(ctx, VB2_NV_RECOVERY_SUBCODE, subcode);
+	}
+}
+
 int vb2_init_context(struct vb2_context *ctx)
 {
 	struct vb2_shared_data *sd = vb2_get_sd(ctx);
@@ -29,7 +116,7 @@
 	 */
 	if (ctx->workbuf_size < sizeof(*sd))
 		return VB2_ERROR_INITCTX_WORKBUF_SMALL;
-	if (!vb_aligned(ctx->workbuf, sizeof(uint32_t)))
+	if (!vb_aligned(ctx->workbuf, VB2_WORKBUF_ALIGN))
 		return VB2_ERROR_INITCTX_WORKBUF_ALIGN;
 
 	/* Initialize the shared data at the start of the work buffer */
@@ -37,3 +124,221 @@
 	ctx->workbuf_used = sizeof(*sd);
 	return VB2_SUCCESS;
 }
+
+void vb2_check_recovery(struct vb2_context *ctx)
+{
+	struct vb2_shared_data *sd = vb2_get_sd(ctx);
+
+	/*
+	 * Read the current recovery request, unless there's already been a
+	 * failure earlier in the boot process.
+	 */
+	if (!sd->recovery_reason)
+		sd->recovery_reason = vb2_nv_get(ctx, VB2_NV_RECOVERY_REQUEST);
+
+	/* Clear the recovery request so we don't get stuck in recovery mode */
+	if (sd->recovery_reason) {
+		vb2_nv_set(ctx, VB2_NV_RECOVERY_REQUEST,
+			   VB2_RECOVERY_NOT_REQUESTED);
+		/*
+		 * Note that we ignore failures clearing the request.  We only
+		 * hit this code path if recovery mode has already been
+		 * requested, so what more can we do?  Don't want to obscure
+		 * the original reason for going into recovery mode.
+		 */
+	}
+
+	/* If forcing recovery, override recovery reason */
+	if (ctx->flags & VB2_CONTEXT_FORCE_RECOVERY_MODE) {
+		sd->recovery_reason = VB2_RECOVERY_RO_MANUAL;
+		sd->flags = VB2_SD_FLAG_MANUAL_RECOVERY;
+	}
+
+	/* If recovery reason is non-zero, tell caller we need recovery mode */
+	if (sd->recovery_reason)
+		ctx->flags |= VB2_CONTEXT_RECOVERY_MODE;
+}
+
+int vb2_fw_parse_gbb(struct vb2_context *ctx)
+{
+	struct vb2_shared_data *sd = vb2_get_sd(ctx);
+	struct vb2_gbb_header *gbb;
+	struct vb2_workbuf wb;
+	int rv;
+
+	vb2_workbuf_from_ctx(ctx, &wb);
+
+	/* Read GBB into next chunk of work buffer */
+	gbb = vb2_workbuf_alloc(&wb, sizeof(*gbb));
+	if (!gbb)
+		return VB2_ERROR_GBB_WORKBUF;
+
+	rv = vb2_read_gbb_header(ctx, gbb);
+	if (rv)
+		return rv;
+
+	/* Extract the only things we care about at firmware time */
+	sd->gbb_flags = gbb->flags;
+	sd->gbb_rootkey_offset = gbb->rootkey_offset;
+	sd->gbb_rootkey_size = gbb->rootkey_size;
+
+	return VB2_SUCCESS;
+}
+
+int vb2_check_dev_switch(struct vb2_context *ctx)
+{
+	struct vb2_shared_data *sd = vb2_get_sd(ctx);
+	uint32_t flags;
+	uint32_t old_flags;
+	int is_dev = 0;
+	int rv;
+
+	/* Read secure flags */
+	rv = vb2_secdata_get(ctx, VB2_SECDATA_FLAGS, &flags);
+	if (rv)
+		return rv;
+
+	old_flags = flags;
+
+	/* Handle dev disable request */
+	if (vb2_nv_get(ctx, VB2_NV_DISABLE_DEV_REQUEST)) {
+		flags &= ~VB2_SECDATA_FLAG_DEV_MODE;
+
+		/* Clear the request */
+		vb2_nv_set(ctx, VB2_NV_DISABLE_DEV_REQUEST, 0);
+	}
+
+	/* Check virtual dev switch */
+	if (flags & VB2_SECDATA_FLAG_DEV_MODE)
+		is_dev = 1;
+
+	/* Handle forcing dev mode via physical switch */
+	if (ctx->flags & VB2_CONTEXT_FORCE_DEVELOPER_MODE)
+		is_dev = 1;
+
+	/* Check if GBB is forcing dev mode */
+	if (sd->gbb_flags & VB2_GBB_FLAG_FORCE_DEV_SWITCH_ON)
+		is_dev = 1;
+
+	/* Handle whichever mode we end up in */
+	if (is_dev) {
+		/* Developer mode */
+		sd->flags |= VB2_SD_DEV_MODE_ENABLED;
+		ctx->flags |= VB2_CONTEXT_DEVELOPER_MODE;
+
+		flags |= VB2_SECDATA_FLAG_LAST_BOOT_DEVELOPER;
+	} else {
+		/* Normal mode */
+		flags &= ~VB2_SECDATA_FLAG_LAST_BOOT_DEVELOPER;
+
+		/*
+		 * Disable dev_boot_* flags.  This ensures they will be
+		 * initially disabled if the user later transitions back into
+		 * developer mode.
+		 */
+		vb2_nv_set(ctx, VB2_NV_DEV_BOOT_USB, 0);
+		vb2_nv_set(ctx, VB2_NV_DEV_BOOT_LEGACY, 0);
+		vb2_nv_set(ctx, VB2_NV_DEV_BOOT_SIGNED_ONLY, 0);
+	}
+
+	if (flags != old_flags) {
+		/*
+		 * Just changed dev mode state.  Clear TPM owner.  This must be
+		 * done here instead of simply passing a flag to
+		 * vb2_check_tpm_clear(), because we don't want to update
+		 * last_boot_developer and then fail to clear the TPM owner.
+		 */
+		rv = vb2ex_tpm_clear_owner(ctx);
+		if (rv) {
+			/*
+			 * Note that this truncates rv to 8 bit.  Which is not
+			 * as useful as the full error code, but we don't have
+			 * NVRAM space to store the full 32-bit code.
+			 */
+			vb2_fail(ctx, VB2_RECOVERY_TPM_CLEAR_OWNER, rv);
+			return rv;
+		}
+
+		/* Save new flags */
+		rv = vb2_secdata_set(ctx, VB2_SECDATA_FLAGS, flags);
+		if (rv)
+			return rv;
+	}
+
+	return VB2_SUCCESS;
+}
+
+int vb2_check_tpm_clear(struct vb2_context *ctx)
+{
+	int rv;
+
+	/* Check if we've been asked to clear the owner */
+	if (!vb2_nv_get(ctx, VB2_NV_CLEAR_TPM_OWNER_REQUEST))
+		return VB2_SUCCESS;  /* No need to clear */
+
+	/* Request applies one time only */
+	vb2_nv_set(ctx, VB2_NV_CLEAR_TPM_OWNER_REQUEST, 0);
+
+	/* Try clearing */
+	rv = vb2ex_tpm_clear_owner(ctx);
+	if (rv) {
+		/*
+		 * Note that this truncates rv to 8 bit.  Which is not as
+		 * useful as the full error code, but we don't have NVRAM space
+		 * to store the full 32-bit code.
+		 */
+		vb2_fail(ctx, VB2_RECOVERY_TPM_CLEAR_OWNER, rv);
+		return rv;
+	}
+
+	/* Clear successful */
+	vb2_nv_set(ctx, VB2_NV_CLEAR_TPM_OWNER_DONE, 1);
+	return VB2_SUCCESS;
+}
+
+int vb2_select_fw_slot(struct vb2_context *ctx)
+{
+	struct vb2_shared_data *sd = vb2_get_sd(ctx);
+	uint32_t tries;
+
+	/* Get result of last boot */
+	sd->last_fw_slot = vb2_nv_get(ctx, VB2_NV_FW_TRIED);
+	sd->last_fw_result = vb2_nv_get(ctx, VB2_NV_FW_RESULT);
+
+	/* Clear result, since we don't know what will happen this boot */
+	vb2_nv_set(ctx, VB2_NV_FW_RESULT, VB2_FW_RESULT_UNKNOWN);
+
+	/* Get slot to try */
+	sd->fw_slot = vb2_nv_get(ctx, VB2_NV_TRY_NEXT);
+
+	/* Check try count */
+	tries = vb2_nv_get(ctx, VB2_NV_TRY_COUNT);
+
+	if (sd->last_fw_result == VB2_FW_RESULT_TRYING &&
+	    sd->last_fw_slot == sd->fw_slot &&
+	    tries == 0) {
+		/*
+		 * We used up our last try on the previous boot, so fall back
+		 * to the other slot this boot.
+		 */
+		sd->fw_slot = 1 - sd->fw_slot;
+		vb2_nv_set(ctx, VB2_NV_TRY_NEXT, sd->fw_slot);
+	}
+
+	if (tries > 0) {
+		/* Still trying this firmware */
+		vb2_nv_set(ctx, VB2_NV_FW_RESULT, VB2_FW_RESULT_TRYING);
+
+		/* Decrement non-zero try count */
+		vb2_nv_set(ctx, VB2_NV_TRY_COUNT, tries - 1);
+	}
+
+	/* Set context flag if we're using slot B */
+	if (sd->fw_slot)
+		ctx->flags |= VB2_CONTEXT_FW_SLOT_B;
+
+	/* Set status flag */
+	sd->status |= VB2_SD_STATUS_CHOSE_SLOT;
+
+	return VB2_SUCCESS;
+}
diff --git a/firmware/2lib/2stub.c b/firmware/2lib/2stub.c
new file mode 100644
index 0000000..a544a4f
--- /dev/null
+++ b/firmware/2lib/2stub.c
@@ -0,0 +1,23 @@
+/* Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Stub API implementations which should be implemented by the caller.
+ */
+
+#include "2sysincludes.h"
+#include "2api.h"
+
+int vb2ex_tpm_clear_owner(struct vb2_context *ctx)
+{
+	return VB2_SUCCESS;
+}
+
+int vb2ex_read_resource(struct vb2_context *ctx,
+			enum vb2_resource_index index,
+			uint32_t offset,
+			void *buf,
+			uint32_t size)
+{
+	return VB2_SUCCESS;
+}
diff --git a/firmware/2lib/include/2api.h b/firmware/2lib/include/2api.h
index 3fb7589..3a4772b 100644
--- a/firmware/2lib/include/2api.h
+++ b/firmware/2lib/include/2api.h
@@ -143,4 +143,44 @@
 	uint32_t workbuf_used;
 };
 
+enum vb2_resource_index {
+
+	/* Google binary block */
+	VB2_RES_GBB,
+
+	/*
+	 * Verified boot block (keyblock+preamble).  Use VB2_CONTEXT_FW_SLOT_B
+	 * to determine whether this refers to slot A or slot B; vboot will
+	 * set that flag to the proper state before reading the vblock.
+	 */
+	VB2_RES_FW_VBLOCK,
+};
+
+/*****************************************************************************/
+/* APIs provided by the caller to verified boot */
+
+/**
+ * Clear the TPM owner.
+ *
+ * @param ctx		Vboot context
+ * @return VB2_SUCCESS, or error code on error.
+ */
+int vb2ex_tpm_clear_owner(struct vb2_context *ctx);
+
+/**
+ * Read a verified boot resource.
+ *
+ * @param ctx		Vboot context
+ * @param index		Resource index to read
+ * @param offset	Byte offset within resource to start at
+ * @param buf		Destination for data
+ * @param size		Amount of data to read
+ * @return VB2_SUCCESS, or error code on error.
+ */
+int vb2ex_read_resource(struct vb2_context *ctx,
+			enum vb2_resource_index index,
+			uint32_t offset,
+			void *buf,
+			uint32_t size);
+
 #endif  /* VBOOT_2_API_H_ */
diff --git a/firmware/2lib/include/2misc.h b/firmware/2lib/include/2misc.h
index bf707ed..f2e259e 100644
--- a/firmware/2lib/include/2misc.h
+++ b/firmware/2lib/include/2misc.h
@@ -10,6 +10,8 @@
 
 #include "2api.h"
 
+struct vb2_gbb_header;
+
 /**
  * Get the shared data pointer from the vboot context
  *
@@ -21,6 +23,39 @@
 }
 
 /**
+ * Initialize a work buffer from the vboot context.
+ *
+ * This sets the work buffer to the unused portion of the context work buffer.
+ *
+ * @param ctx		Vboot context
+ * @param wb		Work buffer to initialize
+ */
+void vb2_workbuf_from_ctx(struct vb2_context *ctx, struct vb2_workbuf *wb);
+
+/**
+ * Read the GBB header.
+ *
+ * @param ctx		Vboot context
+ * @param gbb		Destination for header
+ * @return VB2_SUCCESS, or non-zero if error.
+ */
+int vb2_read_gbb_header(struct vb2_context *ctx, struct vb2_gbb_header *gbb);
+
+/**
+ * Handle vboot failure.
+ *
+ * If the failure occurred after choosing a firmware slot, and the other
+ * firmware slot is not known-bad, try the other firmware slot after reboot.
+ *
+ * If the failure occurred before choosing a firmware slot, or both slots have
+ * failed in successive boots, request recovery.
+ *
+ * @param reason	Recovery reason
+ * @param subcode	Recovery subcode
+ */
+void vb2_fail(struct vb2_context *ctx, uint8_t reason, uint8_t subcode);
+
+/**
  * Set up the verified boot context data, if not already set up.
  *
  * This uses ctx->workbuf_used=0 as a flag to indicate that the data has not
@@ -32,4 +67,49 @@
  */
 int vb2_init_context(struct vb2_context *ctx);
 
+/**
+ * Check for recovery reasons we can determine early in the boot process.
+ *
+ * On exit, check ctx->flags for VB2_CONTEXT_RECOVERY_MODE; if present, jump to
+ * the recovery path instead of continuing with normal boot.  This is the only
+ * direct path to recovery mode.  All other errors later in the boot process
+ * should induce a reboot instead of jumping to recovery, so that recovery mode
+ * starts from a consistent firmware state.
+ *
+ * @param ctx		Vboot context
+ */
+void vb2_check_recovery(struct vb2_context *ctx);
+
+/**
+ * Parse the GBB header.
+ *
+ * @param ctx		Vboot context
+ * @return VB2_SUCCESS, or error code on error.
+ */
+int vb2_fw_parse_gbb(struct vb2_context *ctx);
+
+/**
+ * Check developer switch position.
+ *
+ * @param ctx		Vboot context
+ * @return VB2_SUCCESS, or error code on error.
+ */
+int vb2_check_dev_switch(struct vb2_context *ctx);
+
+/**
+ * Check if we need to clear the TPM owner.
+ *
+ * @param ctx		Vboot context
+ * @return VB2_SUCCESS, or error code on error.
+ */
+int vb2_check_tpm_clear(struct vb2_context *ctx);
+
+/**
+ * Decide which firmware slot to try this boot.
+ *
+ * @param ctx		Vboot context
+ * @return VB2_SUCCESS, or error code on error.
+ */
+int vb2_select_fw_slot(struct vb2_context *ctx);
+
 #endif  /* VBOOT_REFERENCE_VBOOT_2MISC_H_ */
diff --git a/firmware/2lib/include/2return_codes.h b/firmware/2lib/include/2return_codes.h
index e0c4cbc..7c61a49 100644
--- a/firmware/2lib/include/2return_codes.h
+++ b/firmware/2lib/include/2return_codes.h
@@ -9,8 +9,8 @@
 /*
  * Return codes from verified boot functions.
  *
- * TODO: Go through code and replace VB2_ERROR_UNKNOWN with more specific
- * error codes, and make the existing codes more consistent and useful.
+ * Note that other values may be passed through from vb2ex_*() calls; see
+ * the comment for VB2_ERROR_EX below.
  */
 enum vb2_return_code {
 	/* Success - no error */
@@ -233,46 +233,117 @@
 	/* Work buffer unaligned in vb2_init_context() */
 	VB2_ERROR_INITCTX_WORKBUF_ALIGN,
 
+	/* Work buffer too small in vb2_fw_parse_gbb() */
+	VB2_ERROR_GBB_WORKBUF,
+
+	/* Bad magic number in vb2_read_gbb_header() */
+	VB2_ERROR_GBB_MAGIC,
+
+	/* Incompatible version in vb2_read_gbb_header() */
+	VB2_ERROR_GBB_VERSION,
+
+	/* Old version in vb2_read_gbb_header() */
+	VB2_ERROR_GBB_TOO_OLD,
+
+	/* Header size too small in vb2_read_gbb_header() */
+	VB2_ERROR_GBB_HEADER_SIZE,
+
+	/* Work buffer too small for root key in vb2_verify_fw_keyblock() */
+	VB2_ERROR_FW_KEYBLOCK_WORKBUF_ROOT_KEY,
+
+	/* Work buffer too small for header in vb2_verify_fw_keyblock() */
+	VB2_ERROR_FW_KEYBLOCK_WORKBUF_HEADER,
+
+	/* Work buffer too small for keyblock in vb2_verify_fw_keyblock() */
+	VB2_ERROR_FW_KEYBLOCK_WORKBUF,
+
+	/* Keyblock version out of range in vb2_verify_fw_keyblock() */
+	VB2_ERROR_FW_KEYBLOCK_VERSION_RANGE,
+
+	/* Keyblock version rollback in vb2_verify_fw_keyblock() */
+	VB2_ERROR_FW_KEYBLOCK_VERSION_ROLLBACK,
+
+	/* Missing firmware data key in vb2_verify_fw_preamble2() */
+	VB2_ERROR_FW_PREAMBLE2_DATA_KEY,
+
+	/* Work buffer too small for header in vb2_verify_fw_preamble2() */
+	VB2_ERROR_FW_PREAMBLE2_WORKBUF_HEADER,
+
+	/* Work buffer too small for preamble in vb2_verify_fw_preamble2() */
+	VB2_ERROR_FW_PREAMBLE2_WORKBUF,
+
+	/* Firmware version out of range in vb2_verify_fw_preamble2() */
+	VB2_ERROR_FW_PREAMBLE2_VERSION_RANGE,
+
+	/* Firmware version rollback in vb2_verify_fw_preamble2() */
+	VB2_ERROR_FW_PREAMBLE2_VERSION_ROLLBACK,
+
         /**********************************************************************
-	 * TODO: errors which must still be made specific
+	 * API-level errors
 	 */
-	VB2_ERROR_TODO = VB2_ERROR_BASE + 0xff0000,
+	VB2_ERROR_API = VB2_ERROR_BASE + 0x090000,
 
-	/* Work buffer too small */
-	VB2_ERROR_WORKBUF_TOO_SMALL,
+	/* Bag tag in vb2api_init_hash() */
+	VB2_ERROR_API_INIT_HASH_TAG,
 
-	/* Buffer too small (other than the work buffer) */
-	VB2_ERROR_BUFFER_TOO_SMALL,
+	/* Preamble not present in vb2api_init_hash() */
+	VB2_ERROR_API_INIT_HASH_PREAMBLE,
 
-	/* Buffer unaligned */
-	VB2_ERROR_BUFFER_UNALIGNED,
+	/* Work buffer too small in vb2api_init_hash() */
+	VB2_ERROR_API_INIT_HASH_WORKBUF,
 
-	/* Bad GBB header */
-	VB2_ERROR_BAD_GBB_HEADER,
+	/* Missing firmware data key in vb2api_init_hash() */
+	VB2_ERROR_API_INIT_HASH_DATA_KEY,
 
-	/* Bad algorithm - unknown, or unsupported */
-	VB2_ERROR_BAD_ALGORITHM,
+	/* Uninitialized work area in vb2api_extend_hash() */
+	VB2_ERROR_API_EXTEND_HASH_WORKBUF,
 
-	/* Signature check failed */
-	VB2_ERROR_BAD_SIGNATURE,
+	/* Too much data hashed in vb2api_extend_hash() */
+	VB2_ERROR_API_EXTEND_HASH_SIZE,
 
-	/* Bad key */
-	VB2_ERROR_BAD_KEY,
+	/* Preamble not present in vb2api_check_hash() */
+	VB2_ERROR_API_CHECK_HASH_PREAMBLE,
 
-	/* Bad keyblock */
-	VB2_ERROR_BAD_KEYBLOCK,
+	/* Uninitialized work area in vb2api_check_hash() */
+	VB2_ERROR_API_CHECK_HASH_WORKBUF,
 
-	/* Bad preamble */
-	VB2_ERROR_BAD_PREAMBLE,
+	/* Wrong amount of data hashed in vb2api_extend_hash() */
+	VB2_ERROR_API_CHECK_HASH_SIZE,
 
-	/* Bad firmware keyblock version (out of range, or rollback) */
-	VB2_ERROR_FW_KEYBLOCK_VERSION,
+	/* Bag tag in vb2api_check_hash() */
+	VB2_ERROR_API_CHECK_HASH_TAG,
 
-	/* Bad firmware version (out of range, or rollback) */
-	VB2_ERROR_FW_VERSION,
+	/* Missing firmware data key in vb2api_check_hash() */
+	VB2_ERROR_API_CHECK_HASH_DATA_KEY,
 
-	/* Bad hash tag */
-	VB2_ERROR_BAD_TAG,
+	/* Siganature size mismatch in vb2api_check_hash() */
+	VB2_ERROR_API_CHECK_HASH_SIG_SIZE,
+
+	/* Preamble not present in vb2api_get_kernel_subkey() */
+	VB2_ERROR_API_KERNEL_SUBKEY_PREAMBLE,
+
+	/* Destination buffer too small in vb2api_get_kernel_subkey() */
+	VB2_ERROR_API_KERNEL_SUBKEY_DEST_SIZE,
+
+	/* Phase one needs recovery mode */
+	VB2_ERROR_API_PHASE1_RECOVERY,
+
+        /**********************************************************************
+	 * Errors which may be generated by implementations of vb2ex functions.
+	 * Implementation may also return its own specific errors, which should
+	 * NOT be in the range VB2_ERROR_BASE...VB2_ERROR_MAX to avoid
+	 * conflicting with future vboot2 error codes.
+	 */
+	VB2_ERROR_EX = VB2_ERROR_BASE + 0x0a0000,
+
+	/* Resource index not found */
+	VB2_ERROR_EX_READ_RESOURCE_INDEX,
+
+	/* Size of resource not big enough for requested offset and/or size */
+	VB2_ERROR_EX_READ_RESOURCE_SIZE,
+
+	/* TPM clear owner failed */
+	VB2_ERROR_EX_TPM_CLEAR_OWNER,
 
         /**********************************************************************
 	 * Highest non-zero error generated inside vboot library.  Note that
@@ -280,7 +351,6 @@
 	 * still be outside this range.
 	 */
 	VB2_ERROR_MAX = VB2_ERROR_BASE + 0xffffff,
-
 };
 
 #endif  /* VBOOT_2_RETURN_CODES_H_ */
diff --git a/tests/vb2_misc_tests.c b/tests/vb2_misc_tests.c
index a747a26..74837c5 100644
--- a/tests/vb2_misc_tests.c
+++ b/tests/vb2_misc_tests.c
@@ -5,27 +5,86 @@
  * Tests for misc library
  */
 
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "test_common.h"
-#include "vboot_common.h"
-
+#include "2sysincludes.h"
 #include "2api.h"
 #include "2common.h"
 #include "2misc.h"
+#include "2nvstorage.h"
+#include "2secdata.h"
 
-static void misc_test(void)
+#include "test_common.h"
+
+/* Common context for tests */
+static uint8_t workbuf[VB2_WORKBUF_RECOMMENDED_SIZE]
+	__attribute__ ((aligned (16)));
+static struct vb2_context cc;
+static struct vb2_shared_data *sd;
+
+/* Mocked function data */
+enum vb2_resource_index mock_resource_index;
+void *mock_resource_ptr;
+uint32_t mock_resource_size;
+int mock_tpm_clear_called;
+int mock_tpm_clear_retval;
+
+
+static void reset_common_data(void)
 {
-	uint8_t workbuf[VB2_WORKBUF_RECOMMENDED_SIZE];
+	memset(workbuf, 0xaa, sizeof(workbuf));
 
+	memset(&cc, 0, sizeof(cc));
+	cc.workbuf = workbuf;
+	cc.workbuf_size = sizeof(workbuf);
+
+	vb2_init_context(&cc);
+	sd = vb2_get_sd(&cc);
+
+	vb2_nv_init(&cc);
+
+	vb2_secdata_create(&cc);
+	vb2_secdata_init(&cc);
+
+	mock_tpm_clear_called = 0;
+	mock_tpm_clear_retval = VB2_SUCCESS;
+};
+
+/* Mocked functions */
+
+int vb2ex_read_resource(struct vb2_context *ctx,
+			enum vb2_resource_index index,
+			uint32_t offset,
+			void *buf,
+			uint32_t size)
+{
+	if (index != mock_resource_index)
+		return VB2_ERROR_EX_READ_RESOURCE_INDEX;
+
+	if (offset > mock_resource_size || offset + size > mock_resource_size)
+		return VB2_ERROR_EX_READ_RESOURCE_SIZE;
+
+	memcpy(buf, (uint8_t *)mock_resource_ptr + offset, size);
+	return VB2_SUCCESS;
+}
+
+int vb2ex_tpm_clear_owner(struct vb2_context *ctx)
+{
+	mock_tpm_clear_called++;
+
+	return mock_tpm_clear_retval;
+}
+
+/* Tests */
+
+static void init_context_tests(void)
+{
+	/* Use our own context struct so we can re-init it */
 	struct vb2_context c = {
 		.workbuf = workbuf,
 		.workbuf_size = sizeof(workbuf),
 	};
 
+	reset_common_data();
+
 	TEST_SUCC(vb2_init_context(&c), "Init context good");
 	TEST_EQ(c.workbuf_used, sizeof(struct vb2_shared_data),
 		"Init vbsd");
@@ -48,9 +107,357 @@
 		VB2_ERROR_INITCTX_WORKBUF_ALIGN, "Init unaligned");
 }
 
+static void misc_tests(void)
+{
+	struct vb2_workbuf wb;
+
+	reset_common_data();
+	cc.workbuf_used = 16;
+
+	vb2_workbuf_from_ctx(&cc, &wb);
+
+	TEST_PTR_EQ(wb.buf, workbuf + 16, "vb_workbuf_from_ctx() buf");
+	TEST_EQ(wb.size, cc.workbuf_size - 16, "vb_workbuf_from_ctx() size");
+}
+
+static void gbb_tests(void)
+{
+	struct vb2_gbb_header gbb = {
+		.signature = {'$', 'G', 'B', 'B'},
+		.major_version = VB2_GBB_MAJOR_VER,
+		.minor_version = VB2_GBB_MINOR_VER,
+		.header_size = sizeof(struct vb2_gbb_header),
+		.flags = 0x1234,
+		.rootkey_offset = 240,
+		.rootkey_size = 1040,
+	};
+
+	struct vb2_gbb_header gbbdest;
+
+	reset_common_data();
+
+	/* Good contents */
+	mock_resource_index = VB2_RES_GBB;
+	mock_resource_ptr = &gbb;
+	mock_resource_size = sizeof(gbb);
+	TEST_SUCC(vb2_read_gbb_header(&cc, &gbbdest), "read gbb header good");
+	TEST_SUCC(memcmp(&gbb, &gbbdest, sizeof(gbb)), "read gbb contents");
+
+	mock_resource_index = VB2_RES_GBB + 1;
+	TEST_EQ(vb2_read_gbb_header(&cc, &gbbdest),
+		VB2_ERROR_EX_READ_RESOURCE_INDEX, "read gbb header missing");
+	mock_resource_index = VB2_RES_GBB;
+
+	gbb.signature[0]++;
+	TEST_EQ(vb2_read_gbb_header(&cc, &gbbdest),
+		VB2_ERROR_GBB_MAGIC, "read gbb header bad magic");
+	gbb.signature[0]--;
+
+	gbb.major_version++;
+	TEST_EQ(vb2_read_gbb_header(&cc, &gbbdest),
+		VB2_ERROR_GBB_VERSION, "read gbb header major version");
+	gbb.major_version--;
+
+	gbb.minor_version++;
+	TEST_SUCC(vb2_read_gbb_header(&cc, &gbbdest),
+		  "read gbb header minor++");
+	gbb.minor_version -= 2;
+	TEST_EQ(vb2_read_gbb_header(&cc, &gbbdest),
+		VB2_ERROR_GBB_TOO_OLD, "read gbb header minor version old");
+	gbb.minor_version++;
+
+	gbb.header_size--;
+	TEST_EQ(vb2_read_gbb_header(&cc, &gbbdest),
+		VB2_ERROR_GBB_HEADER_SIZE, "read gbb header size");
+	TEST_EQ(vb2_fw_parse_gbb(&cc),
+		VB2_ERROR_GBB_HEADER_SIZE, "parse gbb failure");
+	gbb.header_size++;
+
+	/* Parse GBB */
+	TEST_SUCC(vb2_fw_parse_gbb(&cc), "parse gbb");
+	TEST_EQ(sd->gbb_flags, gbb.flags, "gbb flags");
+	TEST_EQ(sd->gbb_rootkey_offset, gbb.rootkey_offset, "rootkey offset");
+	TEST_EQ(sd->gbb_rootkey_size, gbb.rootkey_size, "rootkey size");
+
+	/* Workbuf failure */
+	reset_common_data();
+	cc.workbuf_used = cc.workbuf_size - 4;
+	TEST_EQ(vb2_fw_parse_gbb(&cc),
+		VB2_ERROR_GBB_WORKBUF, "parse gbb no workbuf");
+}
+
+static void fail_tests(void)
+{
+	/* Early fail (before even NV init) */
+	reset_common_data();
+	sd->status &= ~VB2_SD_STATUS_NV_INIT;
+	vb2_fail(&cc, 1, 2);
+	TEST_NEQ(sd->status & VB2_SD_STATUS_NV_INIT, 0, "vb2_fail inits NV");
+	TEST_EQ(vb2_nv_get(&cc, VB2_NV_RECOVERY_REQUEST),
+		1, "vb2_fail request");
+	TEST_EQ(vb2_nv_get(&cc, VB2_NV_RECOVERY_SUBCODE),
+		2, "vb2_fail subcode");
+
+	/* Repeated fail doesn't overwrite the error code */
+	vb2_fail(&cc, 3, 4);
+	TEST_EQ(vb2_nv_get(&cc, VB2_NV_RECOVERY_REQUEST),
+		1, "vb2_fail repeat");
+	TEST_EQ(vb2_nv_get(&cc, VB2_NV_RECOVERY_SUBCODE),
+		2, "vb2_fail repeat2");
+
+	/* Fail with other slot good doesn't trigger recovery */
+	reset_common_data();
+	vb2_nv_set(&cc, VB2_NV_TRY_COUNT, 3);
+	vb2_nv_set(&cc, VB2_NV_FW_RESULT, VB2_FW_RESULT_UNKNOWN);
+	sd->status |= VB2_SD_STATUS_CHOSE_SLOT;
+	sd->fw_slot = 0;
+	sd->last_fw_slot = 1;
+	sd->last_fw_result = VB2_FW_RESULT_UNKNOWN;
+	vb2_fail(&cc, 5, 6);
+	TEST_EQ(vb2_nv_get(&cc, VB2_NV_RECOVERY_REQUEST), 0, "vb2_failover");
+	TEST_EQ(vb2_nv_get(&cc, VB2_NV_FW_RESULT),
+		VB2_FW_RESULT_FAILURE, "vb2_fail this fw");
+	TEST_EQ(vb2_nv_get(&cc, VB2_NV_TRY_COUNT), 0, "vb2_fail use up tries");
+	TEST_EQ(vb2_nv_get(&cc, VB2_NV_TRY_NEXT), 1, "vb2_fail try other slot");
+
+	/* Fail with other slot already failing triggers recovery */
+	reset_common_data();
+	sd->status |= VB2_SD_STATUS_CHOSE_SLOT;
+	sd->fw_slot = 1;
+	sd->last_fw_slot = 0;
+	sd->last_fw_result = VB2_FW_RESULT_FAILURE;
+	vb2_fail(&cc, 7, 8);
+	TEST_EQ(vb2_nv_get(&cc, VB2_NV_RECOVERY_REQUEST), 7,
+		"vb2_fail both slots bad");
+	TEST_EQ(vb2_nv_get(&cc, VB2_NV_FW_RESULT),
+		VB2_FW_RESULT_FAILURE, "vb2_fail this fw");
+	TEST_EQ(vb2_nv_get(&cc, VB2_NV_TRY_NEXT), 0, "vb2_fail try other slot");
+}
+
+static void recovery_tests(void)
+{
+	/* No recovery */
+	reset_common_data();
+	vb2_check_recovery(&cc);
+	TEST_EQ(sd->recovery_reason, 0, "No recovery reason");
+	TEST_EQ(sd->flags & VB2_SD_FLAG_MANUAL_RECOVERY,
+		0, "Not manual recovery");
+	TEST_EQ(cc.flags & VB2_CONTEXT_RECOVERY_MODE,
+		0, "Not recovery mode");
+
+	/* From request */
+	reset_common_data();
+	vb2_nv_set(&cc, VB2_NV_RECOVERY_REQUEST, 3);
+	vb2_check_recovery(&cc);
+	TEST_EQ(sd->recovery_reason, 3, "Recovery reason from request");
+	TEST_EQ(vb2_nv_get(&cc, VB2_NV_RECOVERY_REQUEST), 0, "NV cleared");
+	TEST_EQ(sd->flags & VB2_SD_FLAG_MANUAL_RECOVERY,
+		0, "Not manual recovery");
+	TEST_NEQ(cc.flags & VB2_CONTEXT_RECOVERY_MODE,
+		 0, "Recovery mode");
+
+	/* From request, but already failed */
+	reset_common_data();
+	vb2_nv_set(&cc, VB2_NV_RECOVERY_REQUEST, 4);
+	sd->recovery_reason = 5;
+	vb2_check_recovery(&cc);
+	TEST_EQ(sd->recovery_reason, 5, "Recovery reason already failed");
+	TEST_EQ(vb2_nv_get(&cc, VB2_NV_RECOVERY_REQUEST),
+		0, "NV still cleared");
+
+	/* Override */
+	reset_common_data();
+	sd->recovery_reason = 6;
+	cc.flags |= VB2_CONTEXT_FORCE_RECOVERY_MODE;
+	vb2_check_recovery(&cc);
+	TEST_EQ(sd->recovery_reason, VB2_RECOVERY_RO_MANUAL,
+		"Recovery reason forced");
+	TEST_NEQ(sd->flags & VB2_SD_FLAG_MANUAL_RECOVERY,
+		 0, "SD flag set");
+}
+
+static void dev_switch_tests(void)
+{
+	uint32_t v;
+
+	/* Normal mode */
+	reset_common_data();
+	TEST_SUCC(vb2_check_dev_switch(&cc), "dev mode off");
+	TEST_EQ(sd->flags & VB2_SD_DEV_MODE_ENABLED, 0, "sd not in dev");
+	TEST_EQ(cc.flags & VB2_CONTEXT_DEVELOPER_MODE, 0, "ctx not in dev");
+	TEST_EQ(mock_tpm_clear_called, 0, "no tpm clear");
+
+	/* Dev mode */
+	reset_common_data();
+	vb2_secdata_set(&cc, VB2_SECDATA_FLAGS,
+			(VB2_SECDATA_FLAG_DEV_MODE |
+			 VB2_SECDATA_FLAG_LAST_BOOT_DEVELOPER));
+	TEST_SUCC(vb2_check_dev_switch(&cc), "dev mode on");
+	TEST_NEQ(sd->flags & VB2_SD_DEV_MODE_ENABLED, 0, "sd in dev");
+	TEST_NEQ(cc.flags & VB2_CONTEXT_DEVELOPER_MODE, 0, "ctx in dev");
+	TEST_EQ(mock_tpm_clear_called, 0, "no tpm clear");
+
+	/* Any normal mode boot clears dev boot flags */
+	reset_common_data();
+	vb2_nv_set(&cc, VB2_NV_DEV_BOOT_USB, 1);
+	vb2_nv_set(&cc, VB2_NV_DEV_BOOT_LEGACY, 1);
+	vb2_nv_set(&cc, VB2_NV_DEV_BOOT_SIGNED_ONLY, 1);
+	TEST_SUCC(vb2_check_dev_switch(&cc), "dev mode off");
+	TEST_EQ(vb2_nv_get(&cc, VB2_NV_DEV_BOOT_USB),
+		0, "cleared dev boot usb");
+	TEST_EQ(vb2_nv_get(&cc, VB2_NV_DEV_BOOT_LEGACY),
+		0, "cleared dev boot legacy");
+	TEST_EQ(vb2_nv_get(&cc, VB2_NV_DEV_BOOT_SIGNED_ONLY),
+		0, "cleared dev boot signed only");
+
+	/* Normal-dev transition clears TPM */
+	reset_common_data();
+	vb2_secdata_set(&cc, VB2_SECDATA_FLAGS, VB2_SECDATA_FLAG_DEV_MODE);
+	TEST_SUCC(vb2_check_dev_switch(&cc), "to dev mode");
+	TEST_EQ(mock_tpm_clear_called, 1, "tpm clear");
+	vb2_secdata_get(&cc, VB2_SECDATA_FLAGS, &v);
+	TEST_EQ(v, (VB2_SECDATA_FLAG_DEV_MODE |
+		    VB2_SECDATA_FLAG_LAST_BOOT_DEVELOPER),
+		"last boot developer now");
+
+	/* Dev-normal transition clears TPM too */
+	reset_common_data();
+	vb2_secdata_set(&cc, VB2_SECDATA_FLAGS,
+			VB2_SECDATA_FLAG_LAST_BOOT_DEVELOPER);
+	TEST_SUCC(vb2_check_dev_switch(&cc), "from dev mode");
+	TEST_EQ(mock_tpm_clear_called, 1, "tpm clear");
+	vb2_secdata_get(&cc, VB2_SECDATA_FLAGS, &v);
+	TEST_EQ(v, 0, "last boot not developer now");
+
+	/* Disable dev mode */
+	reset_common_data();
+	vb2_secdata_set(&cc, VB2_SECDATA_FLAGS,
+			(VB2_SECDATA_FLAG_DEV_MODE |
+			 VB2_SECDATA_FLAG_LAST_BOOT_DEVELOPER));
+	vb2_nv_set(&cc, VB2_NV_DISABLE_DEV_REQUEST, 1);
+	TEST_SUCC(vb2_check_dev_switch(&cc), "disable dev request");
+	TEST_EQ(sd->flags & VB2_SD_DEV_MODE_ENABLED, 0, "sd not in dev");
+	TEST_EQ(vb2_nv_get(&cc, VB2_NV_DISABLE_DEV_REQUEST),
+		0, "request cleared");
+
+	/* Force enabled by gbb */
+	reset_common_data();
+	sd->gbb_flags |= VB2_GBB_FLAG_FORCE_DEV_SWITCH_ON;
+	TEST_SUCC(vb2_check_dev_switch(&cc), "dev on via gbb");
+	TEST_NEQ(sd->flags & VB2_SD_DEV_MODE_ENABLED, 0, "sd in dev");
+	vb2_secdata_get(&cc, VB2_SECDATA_FLAGS, &v);
+	TEST_EQ(v, VB2_SECDATA_FLAG_LAST_BOOT_DEVELOPER,
+		"doesn't set dev on in secdata but does set last boot dev");
+	TEST_EQ(mock_tpm_clear_called, 1, "tpm clear");
+
+	/* Force enabled by ctx flag */
+	reset_common_data();
+	cc.flags |= VB2_CONTEXT_FORCE_DEVELOPER_MODE;
+	TEST_SUCC(vb2_check_dev_switch(&cc), "dev on via ctx flag");
+	TEST_NEQ(sd->flags & VB2_SD_DEV_MODE_ENABLED, 0, "sd in dev");
+	vb2_secdata_get(&cc, VB2_SECDATA_FLAGS, &v);
+	TEST_EQ(v, VB2_SECDATA_FLAG_LAST_BOOT_DEVELOPER,
+		"doesn't set dev on in secdata but does set last boot dev");
+	TEST_EQ(mock_tpm_clear_called, 1, "tpm clear");
+
+	/* Simulate clear owner failure */
+	reset_common_data();
+	vb2_secdata_set(&cc, VB2_SECDATA_FLAGS,
+			VB2_SECDATA_FLAG_LAST_BOOT_DEVELOPER);
+	mock_tpm_clear_retval = VB2_ERROR_EX_TPM_CLEAR_OWNER;
+	TEST_EQ(vb2_check_dev_switch(&cc),
+		VB2_ERROR_EX_TPM_CLEAR_OWNER, "tpm clear fail");
+	TEST_EQ(mock_tpm_clear_called, 1, "tpm clear");
+	vb2_secdata_get(&cc, VB2_SECDATA_FLAGS, &v);
+	TEST_EQ(v, VB2_SECDATA_FLAG_LAST_BOOT_DEVELOPER,
+		"last boot still developer");
+	TEST_EQ(vb2_nv_get(&cc, VB2_NV_RECOVERY_REQUEST),
+		VB2_RECOVERY_TPM_CLEAR_OWNER, "requests recovery");
+	TEST_EQ(vb2_nv_get(&cc, VB2_NV_RECOVERY_SUBCODE),
+		(uint8_t)VB2_ERROR_EX_TPM_CLEAR_OWNER, "recovery subcode");
+}
+
+static void tpm_clear_tests(void)
+{
+	/* No clear request */
+	reset_common_data();
+	TEST_SUCC(vb2_check_tpm_clear(&cc), "no clear request");
+	TEST_EQ(mock_tpm_clear_called, 0, "tpm not cleared");
+
+	/* Successful request */
+	reset_common_data();
+	vb2_nv_set(&cc, VB2_NV_CLEAR_TPM_OWNER_REQUEST, 1);
+	TEST_SUCC(vb2_check_tpm_clear(&cc), "clear request");
+	TEST_EQ(vb2_nv_get(&cc, VB2_NV_CLEAR_TPM_OWNER_REQUEST),
+		0, "request cleared");
+	TEST_EQ(vb2_nv_get(&cc, VB2_NV_CLEAR_TPM_OWNER_DONE),
+		1, "done set");
+	TEST_EQ(mock_tpm_clear_called, 1, "tpm cleared");
+
+	/* Failed request */
+	reset_common_data();
+	mock_tpm_clear_retval = VB2_ERROR_EX_TPM_CLEAR_OWNER;
+	vb2_nv_set(&cc, VB2_NV_CLEAR_TPM_OWNER_REQUEST, 1);
+	TEST_EQ(vb2_check_tpm_clear(&cc),
+		VB2_ERROR_EX_TPM_CLEAR_OWNER, "clear failure");
+	TEST_EQ(vb2_nv_get(&cc, VB2_NV_CLEAR_TPM_OWNER_REQUEST),
+		0, "request cleared");
+	TEST_EQ(vb2_nv_get(&cc, VB2_NV_CLEAR_TPM_OWNER_DONE),
+		0, "done not set");
+}
+
+static void select_slot_tests(void)
+{
+	/* Slot A */
+	reset_common_data();
+	TEST_SUCC(vb2_select_fw_slot(&cc), "select slot A");
+	TEST_EQ(vb2_nv_get(&cc, VB2_NV_FW_RESULT),
+		VB2_FW_RESULT_UNKNOWN, "result unknown");
+	TEST_NEQ(sd->status & VB2_SD_STATUS_CHOSE_SLOT, 0, "chose slot");
+	TEST_EQ(sd->fw_slot, 0, "selected A");
+	TEST_EQ(cc.flags & VB2_CONTEXT_FW_SLOT_B, 0, "didn't choose B");
+
+	/* Slot B */
+	reset_common_data();
+	vb2_nv_set(&cc, VB2_NV_TRY_NEXT, 1);
+	TEST_SUCC(vb2_select_fw_slot(&cc), "select slot B");
+	TEST_EQ(vb2_nv_get(&cc, VB2_NV_FW_RESULT),
+		VB2_FW_RESULT_UNKNOWN, "result unknown");
+	TEST_NEQ(sd->status & VB2_SD_STATUS_CHOSE_SLOT, 0, "chose slot");
+	TEST_EQ(sd->fw_slot, 1, "selected B");
+	TEST_NEQ(cc.flags & VB2_CONTEXT_FW_SLOT_B, 0, "ctx says choose B");
+
+	/* Slot A ran out of tries */
+	reset_common_data();
+	vb2_nv_set(&cc, VB2_NV_FW_RESULT, VB2_FW_RESULT_TRYING);
+	TEST_SUCC(vb2_select_fw_slot(&cc), "select slot A out of tries");
+	TEST_EQ(vb2_nv_get(&cc, VB2_NV_TRY_NEXT), 1, "try B next");
+	TEST_NEQ(sd->status & VB2_SD_STATUS_CHOSE_SLOT, 0, "chose slot");
+	TEST_EQ(sd->fw_slot, 1, "selected B");
+	TEST_NEQ(cc.flags & VB2_CONTEXT_FW_SLOT_B, 0, "ctx says choose B");
+
+	/* Slot A used up a try */
+	reset_common_data();
+	vb2_nv_set(&cc, VB2_NV_TRY_COUNT, 3);
+	TEST_SUCC(vb2_select_fw_slot(&cc), "try slot A");
+	TEST_EQ(vb2_nv_get(&cc, VB2_NV_FW_RESULT),
+		VB2_FW_RESULT_TRYING, "result trying");
+	TEST_NEQ(sd->status & VB2_SD_STATUS_CHOSE_SLOT, 0, "chose slot");
+	TEST_EQ(sd->fw_slot, 0, "selected A");
+	TEST_EQ(cc.flags & VB2_CONTEXT_FW_SLOT_B, 0, "didn't choose B");
+	TEST_EQ(vb2_nv_get(&cc, VB2_NV_TRY_COUNT), 2, "tries decremented");
+}
+
 int main(int argc, char* argv[])
 {
-	misc_test();
+	init_context_tests();
+	misc_tests();
+	gbb_tests();
+	fail_tests();
+	recovery_tests();
+	dev_switch_tests();
+	tpm_clear_tests();
+	select_slot_tests();
 
 	return gTestSuccess ? 0 : 255;
 }