/* Copyright 2014 The ChromiumOS Authors
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 *
 * Tests for misc library
 */

#include "2api.h"
#include "2common.h"
#include "2misc.h"
#include "2nvstorage.h"
#include "2secdata.h"
#include "2sysincludes.h"
#include "common/boot_mode.h"
#include "common/tests.h"

/* Common context for tests */
static uint8_t workbuf[VB2_FIRMWARE_WORKBUF_RECOMMENDED_SIZE]
	__attribute__((aligned(VB2_WORKBUF_ALIGN)));
static uint8_t workbuf2[VB2_FIRMWARE_WORKBUF_RECOMMENDED_SIZE]
	__attribute__((aligned(VB2_WORKBUF_ALIGN)));
static struct vb2_context *ctx;
static struct vb2_shared_data *sd;
static struct vb2_gbb_header gbb;
static struct vb2_secdata_fwmp *fwmp;

/* Mocked function data */
static enum vb2_resource_index mock_resource_index;
static void *mock_resource_ptr;
static uint32_t mock_resource_size;
static int mock_tpm_clear_called;
static int mock_tpm_clear_retval;

static void reset_common_data(void)
{
	memset(workbuf, 0xaa, sizeof(workbuf));
	memset(workbuf2, 0xbb, sizeof(workbuf2));

	TEST_SUCC(vb2api_init(workbuf, sizeof(workbuf), &ctx),
		  "vb2api_init failed");

	sd = vb2_get_sd(ctx);
	sd->status |= VB2_SD_STATUS_SECDATA_FWMP_INIT;

	memset(&gbb, 0, sizeof(gbb));

	vb2_nv_init(ctx);

	vb2api_secdata_firmware_create(ctx);
	vb2_secdata_firmware_init(ctx);

	fwmp = (struct vb2_secdata_fwmp *)&ctx->secdata_fwmp;

	mock_tpm_clear_called = 0;
	mock_tpm_clear_retval = VB2_SUCCESS;

	SET_BOOT_MODE(ctx, VB2_BOOT_MODE_NORMAL);
};

/* Mocked functions */

struct vb2_gbb_header *vb2_get_gbb(struct vb2_context *c)
{
	return &gbb;
}

vb2_error_t vb2ex_read_resource(struct vb2_context *c,
				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;
}

vb2_error_t vb2ex_tpm_clear_owner(struct vb2_context *c)
{
	mock_tpm_clear_called++;

	return mock_tpm_clear_retval;
}

/* Tests */
static void init_workbuf_tests(void)
{
	struct vb2_context *orig_ctx;

	/* check constants */
	TEST_TRUE(sizeof(struct vb2_context) < VB2_CONTEXT_MAX_SIZE,
		  "vb2_context max size constant");

	/* vb2api_init() - misaligned */
	TEST_EQ(vb2api_init(workbuf + 1, sizeof(workbuf) - 1, &ctx),
		VB2_ERROR_WORKBUF_ALIGN, "vb2api_init - misaligned");

	/* vb2api_init() - size too small */
	TEST_EQ(vb2api_init(workbuf, sizeof(struct vb2_shared_data) - 1,
			      &ctx), VB2_ERROR_WORKBUF_SMALL,
		"vb2api_init - size too small");

	/* vb2api_init() - success */
	TEST_SUCC(vb2api_init(workbuf, sizeof(workbuf), &ctx),
		  "vb2api_init - success");
	TEST_TRUE((uintptr_t)workbuf < (uintptr_t)ctx &&
		  (uintptr_t)ctx < (uintptr_t)workbuf + sizeof(workbuf),
		  "  return proper pointer");
	struct vb2_context zero_ctx = {0};
	TEST_SUCC(memcmp(ctx, &zero_ctx, sizeof(struct vb2_context)),
		  "  vb2_context set to zero");
	sd = vb2_get_sd(ctx);
	TEST_EQ(sd->magic, VB2_SHARED_DATA_MAGIC, "  set magic");
	TEST_EQ(sd->struct_version_major, VB2_SHARED_DATA_VERSION_MAJOR,
		"  set major version");
	TEST_EQ(sd->struct_version_minor, VB2_SHARED_DATA_VERSION_MINOR,
		"  set minor version");
	TEST_EQ(sd->workbuf_size, sizeof(workbuf), "  set workbuf size");
	TEST_TRUE(sd->workbuf_used - sizeof(struct vb2_shared_data)
		  < VB2_WORKBUF_ALIGN, "  set workbuf used");

	/* vb2api_relocate() - misaligned source */
	reset_common_data();
	memmove(workbuf + 1, workbuf, sizeof(workbuf) - 1);
	TEST_SUCC(vb2api_relocate(workbuf2, workbuf + 1, sizeof(workbuf) - 1,
				  &ctx), "vb2api_relocate - misaligned source");

	/* vb2api_relocate() - misaligned target */
	reset_common_data();
	TEST_EQ(vb2api_relocate(workbuf2 + 1, workbuf, sizeof(workbuf) - 1,
				&ctx),
		VB2_ERROR_WORKBUF_ALIGN, "vb2api_relocate - misaligned target");

	/* vb2api_relocate() - bad magic */
	reset_common_data();
	sd->magic = 0;
	TEST_EQ(vb2api_relocate(workbuf2, workbuf, sizeof(workbuf), &ctx),
		VB2_ERROR_SHARED_DATA_MAGIC, "vb2api_relocate - bad magic");

	/* vb2api_relocate() - small major version */
	reset_common_data();
	sd->struct_version_major--;
	TEST_EQ(vb2api_relocate(workbuf2, workbuf, sizeof(workbuf), &ctx),
		VB2_ERROR_SHARED_DATA_VERSION,
		"vb2api_relocate - small major version");

	/* vb2api_relocate() - big major version */
	reset_common_data();
	sd->struct_version_major++;
	TEST_EQ(vb2api_relocate(workbuf2, workbuf, sizeof(workbuf), &ctx),
		VB2_ERROR_SHARED_DATA_VERSION,
		"vb2api_relocate - big major version");

	/* vb2api_relocate() - small minor version */
	if (VB2_SHARED_DATA_VERSION_MINOR > 0) {
		reset_common_data();
		sd->struct_version_minor--;
		TEST_EQ(vb2api_relocate(workbuf2, workbuf, sizeof(workbuf),
					&ctx),
			VB2_ERROR_SHARED_DATA_VERSION,
			  "vb2api_relocate - small minor version");
	}

	/* vb2api_relocate() - big minor version */
	reset_common_data();
	sd->struct_version_minor++;
	TEST_SUCC(vb2api_relocate(workbuf2, workbuf, sizeof(workbuf), &ctx),
		  "vb2api_relocate - big minor version");

	/* vb2api_relocate() - small workbuf_used */
	reset_common_data();
	sd->workbuf_used = sizeof(struct vb2_shared_data) - 1;
	TEST_EQ(vb2api_relocate(workbuf2, workbuf, sizeof(workbuf), &ctx),
		VB2_ERROR_WORKBUF_INVALID,
		"vb2api_relocate - small workbuf_used");

	/* vb2api_relocate() - workbuf_size < workbuf_used */
	reset_common_data();
	sd->workbuf_used = sd->workbuf_size;
	sd->workbuf_size--;
	TEST_EQ(vb2api_relocate(workbuf2, workbuf, sizeof(workbuf), &ctx),
		VB2_ERROR_WORKBUF_INVALID,
		"vb2api_relocate - workbuf_size < workbuf_used");

	/* vb2api_relocate() - target workbuf too small */
	reset_common_data();
	sd->workbuf_used = sd->workbuf_size - 1;
	TEST_EQ(vb2api_relocate(workbuf2, workbuf, sd->workbuf_used - 1, &ctx),
		VB2_ERROR_WORKBUF_SMALL,
		"vb2api_relocate - target workbuf too small");

	/* vb2api_relocate() - success (same size) */
	reset_common_data();
	orig_ctx = ctx;
	TEST_SUCC(vb2api_relocate(workbuf2, workbuf, sizeof(workbuf), &ctx),
		  "vb2api_relocate - success (same size)");
	sd = vb2_get_sd(ctx);
	TEST_EQ((uintptr_t)orig_ctx - (uintptr_t)workbuf,
		(uintptr_t)ctx - (uintptr_t)workbuf2,
		"  same context pointer");
	TEST_SUCC(memcmp(workbuf2, workbuf, sd->workbuf_used),
		  "  same workbuf");

	/* vb2api_relocate() - success (smaller size) */
	reset_common_data();
	TEST_SUCC(vb2api_relocate(workbuf2, workbuf, sizeof(workbuf) - 1, &ctx),
		  "vb2api_relocate - success (smaller size)");
	sd = vb2_get_sd(ctx);
	TEST_EQ(sd->workbuf_size, sizeof(workbuf) - 1, "  set workbuf size");

	/* vb2api_relocate() - success (larger size) */
	reset_common_data();
	sd->workbuf_size--;
	TEST_SUCC(vb2api_relocate(workbuf2, workbuf, sizeof(workbuf), &ctx),
		  "vb2api_relocate - success (larger size)");
	sd = vb2_get_sd(ctx);
	TEST_EQ(sd->workbuf_size, sizeof(workbuf), "  set workbuf size");

	/* vb2api_relocate() - success (overlapping) */
	reset_common_data();
	orig_ctx = ctx;
	sd->workbuf_size -= VB2_WORKBUF_ALIGN;
	memcpy(workbuf2, workbuf, sd->workbuf_used);
	TEST_SUCC(vb2api_relocate(workbuf + VB2_WORKBUF_ALIGN, workbuf,
				  sizeof(workbuf) - VB2_WORKBUF_ALIGN, &ctx),
		  "vb2api_relocate - success (overlapping)");
	sd = vb2_get_sd(ctx);
	TEST_EQ((uintptr_t)ctx - (uintptr_t)orig_ctx,
		VB2_WORKBUF_ALIGN,
		"  context pointer moved");
	TEST_SUCC(memcmp(workbuf2, workbuf + VB2_WORKBUF_ALIGN,
			 sd->workbuf_used), "  same workbuf");

	/* vb2api_reinit() - workbuf_size < workbuf_used */
	reset_common_data();
	sd->workbuf_size = sd->workbuf_used - 1;
	TEST_EQ(vb2api_reinit(workbuf, &ctx), VB2_ERROR_WORKBUF_INVALID,
		"vb2api_reinit - workbuf_size < workbuf_used");

	/* vb2api_reinit() - success */
	reset_common_data();
	orig_ctx = ctx;
	TEST_SUCC(vb2api_reinit(workbuf, &ctx),
		  "vb2api_reinit - success");
	TEST_EQ((uintptr_t)ctx, (uintptr_t)orig_ctx,
		"  context pointer unchanged");
}

static void misc_tests(void)
{
	struct vb2_workbuf wb;

	reset_common_data();
	sd->workbuf_used = VB2_WORKBUF_ALIGN;

	vb2_workbuf_from_ctx(ctx, &wb);

	TEST_PTR_EQ(wb.buf, workbuf + VB2_WORKBUF_ALIGN,
		    "vb_workbuf_from_ctx() buf");
	TEST_EQ(wb.size, sd->workbuf_size - VB2_WORKBUF_ALIGN,
		"vb_workbuf_from_ctx() size");

	reset_common_data();
	sd->status |= VB2_SD_STATUS_RECOVERY_DECIDED;
	TEST_ABORT(VB2_REC_OR_DIE(ctx, "die\n"), "REC_OR_DIE in normal mode");

	reset_common_data();
	sd->status |= VB2_SD_STATUS_RECOVERY_DECIDED;
	ctx->flags |= VB2_CONTEXT_RECOVERY_MODE;
	VB2_REC_OR_DIE(ctx, "VB2_REC_OR_DIE() test in recovery mode\n");
	/* Would exit here if it didn't work as intended. */

	reset_common_data();
	VB2_REC_OR_DIE(ctx, "VB2_REC_OR_DIE() test in fw_phase1\n");
}

static void gbb_tests(void)
{
	struct vb2_gbb_header gbbsrc = {
		.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;

	TEST_EQ(sizeof(struct vb2_gbb_header),
		EXPECTED_VB2_GBB_HEADER_SIZE,
		"sizeof(struct vb2_gbb_header)");

	reset_common_data();

	/* Good contents */
	mock_resource_index = VB2_RES_GBB;
	mock_resource_ptr = &gbbsrc;
	mock_resource_size = sizeof(gbbsrc);
	TEST_SUCC(vb2_read_gbb_header(ctx, &gbbdest), "read gbb header good");
	TEST_SUCC(memcmp(&gbbsrc, &gbbdest, sizeof(gbbsrc)),
		  "read gbb contents");

	mock_resource_index = VB2_RES_GBB + 1;
	TEST_EQ(vb2_read_gbb_header(ctx, &gbbdest),
		VB2_ERROR_EX_READ_RESOURCE_INDEX, "read gbb header missing");
	mock_resource_index = VB2_RES_GBB;

	gbbsrc.signature[0]++;
	TEST_EQ(vb2_read_gbb_header(ctx, &gbbdest),
		VB2_ERROR_GBB_MAGIC, "read gbb header bad magic");
	gbbsrc.signature[0]--;

	gbbsrc.major_version = VB2_GBB_MAJOR_VER + 1;
	TEST_EQ(vb2_read_gbb_header(ctx, &gbbdest),
		VB2_ERROR_GBB_VERSION, "read gbb header major version");
	gbbsrc.major_version = VB2_GBB_MAJOR_VER;

	gbbsrc.minor_version = VB2_GBB_MINOR_VER + 1;
	TEST_SUCC(vb2_read_gbb_header(ctx, &gbbdest),
		  "read gbb header minor++");
	gbbsrc.minor_version = 1;
	TEST_EQ(vb2_read_gbb_header(ctx, &gbbdest),
		VB2_ERROR_GBB_TOO_OLD, "read gbb header 1.1 fails");
	gbbsrc.minor_version = 0;
	TEST_EQ(vb2_read_gbb_header(ctx, &gbbdest),
		VB2_ERROR_GBB_TOO_OLD, "read gbb header 1.0 fails");
	gbbsrc.minor_version = VB2_GBB_MINOR_VER;

	gbbsrc.header_size--;
	TEST_EQ(vb2_read_gbb_header(ctx, &gbbdest),
		VB2_ERROR_GBB_HEADER_SIZE, "read gbb header size");
	TEST_EQ(vb2_fw_init_gbb(ctx),
		VB2_ERROR_GBB_HEADER_SIZE, "init gbb failure");
	gbbsrc.header_size++;

	/* Init GBB */
	int used_before = sd->workbuf_used;
	TEST_SUCC(vb2_fw_init_gbb(ctx), "init gbb");
	/* Manually calculate the location of GBB since we have mocked out the
	   original definition of vb2_get_gbb. */
	struct vb2_gbb_header *current_gbb = vb2_member_of(sd, sd->gbb_offset);
	TEST_SUCC(memcmp(&gbbsrc, current_gbb, sizeof(gbbsrc)),
		  "  copy gbb contents");
	TEST_TRUE(sd->workbuf_used - sizeof(gbbsrc) - used_before
		  < VB2_WORKBUF_ALIGN, "  unexpected workbuf size");

	/* Workbuf failure */
	reset_common_data();
	sd->workbuf_used = sd->workbuf_size - 4;
	TEST_EQ(vb2_fw_init_gbb(ctx),
		VB2_ERROR_GBB_WORKBUF, "init gbb no workbuf");

	/* Check for setting NO_SECDATA_FWMP context flag */
	reset_common_data();
	TEST_SUCC(vb2_fw_init_gbb(ctx), "init gbb");
	TEST_EQ(ctx->flags & VB2_CONTEXT_NO_SECDATA_FWMP, 0,
		"without DISABLE_FWMP: NO_SECDATA_FWMP shouldn't be set");
	reset_common_data();
	gbbsrc.flags |= VB2_GBB_FLAG_DISABLE_FWMP;
	TEST_SUCC(vb2_fw_init_gbb(ctx), "init gbb");
	TEST_NEQ(ctx->flags & VB2_CONTEXT_NO_SECDATA_FWMP, 0,
		 "with DISABLE_FWMP: NO_SECDATA_FWMP should be set");
}

static void fail_tests(void)
{
	/* Early fail (before even NV init) */
	reset_common_data();
	sd->status &= ~VB2_SD_STATUS_NV_INIT;
	vb2api_fail(ctx, 1, 2);
	TEST_NEQ(sd->status & VB2_SD_STATUS_NV_INIT, 0, "vb2api_fail inits NV");
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_RECOVERY_REQUEST),
		1, "vb2api_fail request");
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_RECOVERY_SUBCODE),
		2, "vb2api_fail subcode");

	/* Repeated fail doesn't overwrite the error code */
	vb2api_fail(ctx, 3, 4);
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_RECOVERY_REQUEST),
		1, "vb2api_fail repeat");
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_RECOVERY_SUBCODE),
		2, "vb2api_fail repeat2");

	/* Fail with other slot good doesn't trigger recovery */
	reset_common_data();
	vb2_nv_set(ctx, VB2_NV_TRY_COUNT, 3);
	vb2_nv_set(ctx, VB2_NV_FW_RESULT, VB2_FW_RESULT_UNKNOWN);
	vb2_nv_set(ctx, VB2_NV_FW_TRIED, 0);
	vb2_nv_set(ctx, VB2_NV_FW_PREV_TRIED, 1);
	vb2_nv_set(ctx, VB2_NV_FW_PREV_RESULT, VB2_FW_RESULT_UNKNOWN);
	sd->status |= VB2_SD_STATUS_CHOSE_SLOT;
	vb2api_fail(ctx, 5, 6);
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_RECOVERY_REQUEST), 0, "vb2_failover");
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_FW_RESULT),
		VB2_FW_RESULT_FAILURE, "vb2api_fail this fw");
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_TRY_COUNT), 0,
		"vb2api_fail use up tries");
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_TRY_NEXT), 1,
		"vb2api_fail try other slot");

	/* Fail with other slot already failing triggers recovery */
	reset_common_data();
	vb2_nv_set(ctx, VB2_NV_FW_PREV_TRIED, 0);
	vb2_nv_set(ctx, VB2_NV_FW_PREV_RESULT, VB2_FW_RESULT_FAILURE);
	vb2_nv_set(ctx, VB2_NV_FW_TRIED, 1);
	sd->status |= VB2_SD_STATUS_CHOSE_SLOT;
	vb2api_fail(ctx, 7, 8);
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_RECOVERY_REQUEST), 7,
		"vb2api_fail both slots bad");
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_FW_RESULT),
		VB2_FW_RESULT_FAILURE, "vb2api_fail this fw");
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_TRY_NEXT), 0,
		"vb2api_fail try other slot");

	/* Fail with single slot already failing triggers recovery */
	reset_common_data();
	vb2_nv_set(ctx, VB2_NV_FW_PREV_TRIED, 0);
	vb2_nv_set(ctx, VB2_NV_FW_TRIED, 0);
	vb2_nv_set(ctx, VB2_NV_FW_RESULT, VB2_FW_RESULT_TRYING);
	ctx->flags |= VB2_CONTEXT_SLOT_A_ONLY;
	sd->status |= VB2_SD_STATUS_CHOSE_SLOT;
	vb2api_fail(ctx, 9, 10);
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_RECOVERY_REQUEST), 9,
		"vb2api_fail single slot fail");
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_FW_RESULT),
		VB2_FW_RESULT_FAILURE, "vb2api_fail this fw");
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_TRY_NEXT), 0,
		"vb2api_fail did not try other slot");
}

static void previous_boot_fail_tests(void)
{
	/* Previous boot fail (before even NV init) */
	/* Fail with other slot good doesn't trigger recovery */
	reset_common_data();
	vb2_nv_set(ctx, VB2_NV_FW_TRIED, VB2_FW_SLOT_A);
	vb2_nv_set(ctx, VB2_NV_TRY_COUNT, 3);
	vb2_nv_set(ctx, VB2_NV_FW_RESULT, VB2_FW_RESULT_UNKNOWN);
	sd->status &= ~VB2_SD_STATUS_NV_INIT;
	vb2api_previous_boot_fail(ctx, 1, 2);
	TEST_NEQ(sd->status & VB2_SD_STATUS_NV_INIT,
		 0, "vb2api_previous_boot_fail inits NV");
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_RECOVERY_REQUEST),
		0, "vb2_previous_boot_fail over");
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_FW_RESULT),
		VB2_FW_RESULT_FAILURE, "vb2api_previous_boot_fail result");
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_TRY_COUNT),
		0, "vb2api_previous_boot_fail try count");
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_TRY_NEXT),
		1, "vb2api_previous_boot_fail FW tried");

	/* Fail with other slot already failing triggers recovery */
	reset_common_data();
	vb2_nv_set(ctx, VB2_NV_FW_PREV_TRIED, VB2_FW_SLOT_A);
	vb2_nv_set(ctx, VB2_NV_FW_PREV_RESULT, VB2_FW_RESULT_FAILURE);
	vb2_nv_set(ctx, VB2_NV_FW_TRIED, VB2_FW_SLOT_B);
	vb2_nv_set(ctx, VB2_NV_TRY_COUNT, 3);
	vb2_nv_set(ctx, VB2_NV_FW_RESULT, VB2_FW_RESULT_UNKNOWN);
	sd->status &= ~VB2_SD_STATUS_NV_INIT;
	vb2api_previous_boot_fail(ctx, 3, 4);
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_RECOVERY_REQUEST),
		3, "vb2api_previous_boot_fail both slots bad");
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_FW_RESULT),
		VB2_FW_RESULT_FAILURE, "vb2api_previous_boot_fail result");
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_TRY_COUNT),
		0, "vb2api_previous_boot_fail try count");
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_TRY_NEXT),
		0, "vb2api_previous_boot_fail FW tried");

	/* Repeated fail doesn't overwrite the error code */
	reset_common_data();
	vb2_nv_set(ctx, VB2_NV_TRY_NEXT, VB2_FW_SLOT_A);
	vb2_nv_set(ctx, VB2_NV_TRY_COUNT, 3);
	vb2_nv_set(ctx, VB2_NV_FW_RESULT, VB2_FW_RESULT_FAILURE);
	sd->status &= ~VB2_SD_STATUS_NV_INIT;
	vb2api_previous_boot_fail(ctx, 5, 6);
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_FW_RESULT),
		VB2_FW_RESULT_FAILURE, "vb2api_previous_boot_fail result");
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_TRY_COUNT),
		3, "vb2api_previous_boot_fail try count");
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_TRY_NEXT),
		VB2_FW_SLOT_A, "vb2api_previous_boot_fail try next");
}

static void recovery_tests(void)
{
	/* No recovery */
	reset_common_data();
	TEST_EQ(sd->status & VB2_SD_STATUS_RECOVERY_DECIDED,
		0, "recovery not yet decided before testing check_recovery()");
	vb2_check_recovery(ctx);
	TEST_EQ(sd->recovery_reason, 0, "No recovery reason");
	TEST_EQ(ctx->flags & VB2_CONTEXT_RECOVERY_MODE,
		0, "Not recovery mode");
	TEST_NEQ(sd->status & VB2_SD_STATUS_RECOVERY_DECIDED,
		 0, "Recovery decided");

	/* From request */
	reset_common_data();
	vb2_nv_set(ctx, VB2_NV_RECOVERY_REQUEST, 3);
	vb2_check_recovery(ctx);
	TEST_EQ(sd->recovery_reason, 3, "Recovery reason from request");
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_RECOVERY_REQUEST), 3, "NV not cleared");
	TEST_NEQ(ctx->flags & VB2_CONTEXT_RECOVERY_MODE,
		 0, "Recovery mode");
	TEST_NEQ(sd->status & VB2_SD_STATUS_RECOVERY_DECIDED,
		 0, "Recovery decided");

	/* From request, but already failed */
	reset_common_data();
	vb2_nv_set(ctx, VB2_NV_RECOVERY_REQUEST, 4);
	sd->recovery_reason = 5;
	vb2_check_recovery(ctx);
	TEST_EQ(sd->recovery_reason, 5, "Recovery reason already failed");
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_RECOVERY_REQUEST),
		4, "NV not cleared");
	TEST_NEQ(sd->status & VB2_SD_STATUS_RECOVERY_DECIDED,
		 0, "Recovery decided");

	/* Override */
	reset_common_data();
	sd->recovery_reason = 6;
	ctx->flags |= VB2_CONTEXT_FORCE_RECOVERY_MODE;
	vb2_check_recovery(ctx);
	TEST_EQ(sd->recovery_reason, VB2_RECOVERY_RO_MANUAL,
		"Recovery reason forced");
	TEST_NEQ(sd->status & VB2_SD_STATUS_RECOVERY_DECIDED,
		 0, "Recovery decided");

	/* Override subcode TRAIN_AND_REBOOT */
	reset_common_data();
	vb2_nv_set(ctx, VB2_NV_RECOVERY_SUBCODE, VB2_RECOVERY_TRAIN_AND_REBOOT);
	ctx->flags |= VB2_CONTEXT_FORCE_RECOVERY_MODE;
	vb2_check_recovery(ctx);
	TEST_EQ(sd->recovery_reason, VB2_RECOVERY_RO_MANUAL,
		"Recovery reason forced");
	TEST_NEQ(sd->status & VB2_SD_STATUS_RECOVERY_DECIDED,
		 0, "Recovery decided");

	/* Promote subcode from BROKEN screen*/
	reset_common_data();
	vb2_nv_set(ctx, VB2_NV_RECOVERY_SUBCODE, VB2_RECOVERY_US_TEST);
	ctx->flags |= VB2_CONTEXT_FORCE_RECOVERY_MODE;
	vb2_check_recovery(ctx);
	TEST_EQ(sd->recovery_reason, VB2_RECOVERY_US_TEST,
		"Recovery reason forced from BROKEN");
	TEST_NEQ(sd->status & VB2_SD_STATUS_RECOVERY_DECIDED,
		 0, "Recovery decided");
}

static void dev_switch_tests(void)
{
	uint32_t v;

	/* Normal mode */
	reset_common_data();
	TEST_SUCC(vb2_check_dev_switch(ctx), "dev mode off");
	TEST_EQ(sd->flags & VB2_SD_FLAG_DEV_MODE_ENABLED, 0, "  sd not in dev");
	TEST_EQ(ctx->flags & VB2_CONTEXT_DEVELOPER_MODE, 0, "  ctx not in dev");
	TEST_EQ(mock_tpm_clear_called, 0, "  no tpm clear");
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_REQ_WIPEOUT), 0, "  no nv wipeout");

	/* Dev mode */
	reset_common_data();
	vb2_secdata_firmware_set(
		ctx, VB2_SECDATA_FIRMWARE_FLAGS,
		(VB2_SECDATA_FIRMWARE_FLAG_DEV_MODE |
		 VB2_SECDATA_FIRMWARE_FLAG_LAST_BOOT_DEVELOPER));
	TEST_SUCC(vb2_check_dev_switch(ctx), "dev mode on");
	TEST_NEQ(sd->flags & VB2_SD_FLAG_DEV_MODE_ENABLED, 0, "  sd in dev");
	TEST_NEQ(ctx->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(ctx, VB2_NV_DEV_BOOT_EXTERNAL, 1);
	vb2_nv_set(ctx, VB2_NV_DEV_BOOT_ALTFW, 1);
	vb2_nv_set(ctx, VB2_NV_DEV_BOOT_SIGNED_ONLY, 1);
	vb2_nv_set(ctx, VB2_NV_DEV_DEFAULT_BOOT, 1);
	TEST_SUCC(vb2_check_dev_switch(ctx), "dev mode off");
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_DEV_BOOT_EXTERNAL),
		0, "  cleared dev boot external");
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_DEV_BOOT_ALTFW),
		0, "  cleared dev boot altfw");
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_DEV_BOOT_SIGNED_ONLY),
		0, "  cleared dev boot signed only");
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_DEV_DEFAULT_BOOT),
		0, "  cleared dev default boot");

	/* Normal-dev transition clears TPM */
	reset_common_data();
	vb2_secdata_firmware_set(ctx, VB2_SECDATA_FIRMWARE_FLAGS,
				 VB2_SECDATA_FIRMWARE_FLAG_DEV_MODE);
	TEST_SUCC(vb2_check_dev_switch(ctx), "to dev mode");
	TEST_EQ(mock_tpm_clear_called, 1, "  tpm clear");
	v = vb2_secdata_firmware_get(ctx, VB2_SECDATA_FIRMWARE_FLAGS);
	TEST_EQ(v, (VB2_SECDATA_FIRMWARE_FLAG_DEV_MODE |
		    VB2_SECDATA_FIRMWARE_FLAG_LAST_BOOT_DEVELOPER),
		"  last boot developer now");

	/* Dev-normal transition clears TPM too */
	reset_common_data();
	vb2_secdata_firmware_set(ctx, VB2_SECDATA_FIRMWARE_FLAGS,
				 VB2_SECDATA_FIRMWARE_FLAG_LAST_BOOT_DEVELOPER);
	TEST_SUCC(vb2_check_dev_switch(ctx), "from dev mode");
	TEST_EQ(mock_tpm_clear_called, 1, "  tpm clear");
	v = vb2_secdata_firmware_get(ctx, VB2_SECDATA_FIRMWARE_FLAGS);
	TEST_EQ(v, 0, "  last boot not developer now");

	/* Disable dev mode */
	reset_common_data();
	vb2_secdata_firmware_set(
		ctx, VB2_SECDATA_FIRMWARE_FLAGS,
		(VB2_SECDATA_FIRMWARE_FLAG_DEV_MODE |
		 VB2_SECDATA_FIRMWARE_FLAG_LAST_BOOT_DEVELOPER));
	vb2_nv_set(ctx, VB2_NV_DISABLE_DEV_REQUEST, 1);
	TEST_SUCC(vb2_check_dev_switch(ctx), "disable dev request");
	TEST_EQ(sd->flags & VB2_SD_FLAG_DEV_MODE_ENABLED, 0, "  sd not in dev");
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_DISABLE_DEV_REQUEST),
		0, "  request cleared");

	/* Force enabled by GBB */
	reset_common_data();
	gbb.flags |= VB2_GBB_FLAG_FORCE_DEV_SWITCH_ON;
	TEST_SUCC(vb2_check_dev_switch(ctx), "dev on via gbb");
	TEST_NEQ(sd->flags & VB2_SD_FLAG_DEV_MODE_ENABLED, 0, "  sd in dev");
	v = vb2_secdata_firmware_get(ctx, VB2_SECDATA_FIRMWARE_FLAGS);
	TEST_EQ(v, VB2_SECDATA_FIRMWARE_FLAG_LAST_BOOT_DEVELOPER,
		"  doesn't set dev on in secdata_firmware "
		"but does set last boot dev");
	TEST_EQ(mock_tpm_clear_called, 1, "  tpm clear");

	/* Request disable by ctx flag */
	reset_common_data();
	vb2_secdata_firmware_set(
		ctx, VB2_SECDATA_FIRMWARE_FLAGS,
		(VB2_SECDATA_FIRMWARE_FLAG_DEV_MODE |
		 VB2_SECDATA_FIRMWARE_FLAG_LAST_BOOT_DEVELOPER));
	ctx->flags |= VB2_CONTEXT_DISABLE_DEVELOPER_MODE;
	TEST_SUCC(vb2_check_dev_switch(ctx), "disable dev on ctx request");
	TEST_EQ(sd->flags & VB2_SD_FLAG_DEV_MODE_ENABLED, 0, "  sd not in dev");

	/* Simulate clear owner failure */
	reset_common_data();
	vb2_secdata_firmware_set(ctx, VB2_SECDATA_FIRMWARE_FLAGS,
				 VB2_SECDATA_FIRMWARE_FLAG_LAST_BOOT_DEVELOPER);
	mock_tpm_clear_retval = VB2_ERROR_EX_TPM_CLEAR_OWNER;
	TEST_EQ(vb2_check_dev_switch(ctx),
		VB2_ERROR_EX_TPM_CLEAR_OWNER, "tpm clear fail");
	TEST_EQ(mock_tpm_clear_called, 1, "  tpm clear");
	v = vb2_secdata_firmware_get(ctx, VB2_SECDATA_FIRMWARE_FLAGS);
	TEST_EQ(v, VB2_SECDATA_FIRMWARE_FLAG_LAST_BOOT_DEVELOPER,
		"  last boot still developer");
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_RECOVERY_REQUEST),
		VB2_RECOVERY_TPM_CLEAR_OWNER, "  requests recovery");
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_RECOVERY_SUBCODE),
		(uint8_t)VB2_ERROR_EX_TPM_CLEAR_OWNER, "  recovery subcode");

	/*
	 * secdata_firmware failure in normal mode fails and shows dev=0 even
	 * if dev mode was on in the (inaccessible) secdata_firmware. Since this
	 * happens in fw_phase1, we do not abort -- we know that when secdata
	 * is uninitialized here, we must be headed for recovery mode.
	 */
	reset_common_data();
	vb2_secdata_firmware_set(ctx, VB2_SECDATA_FIRMWARE_FLAGS,
				 VB2_SECDATA_FIRMWARE_FLAG_DEV_MODE);
	sd->status &= ~VB2_SD_STATUS_SECDATA_FIRMWARE_INIT;
	TEST_SUCC(vb2_check_dev_switch(ctx), "secdata_firmware fail normal");
	TEST_EQ(sd->flags & VB2_SD_FLAG_DEV_MODE_ENABLED, 0, "  sd not in dev");
	TEST_EQ(ctx->flags & VB2_CONTEXT_DEVELOPER_MODE, 0, "  ctx not in dev");

	/* secdata_firmware failure in recovery mode continues */
	reset_common_data();
	ctx->flags |= VB2_CONTEXT_RECOVERY_MODE;
	sd->status &= ~VB2_SD_STATUS_SECDATA_FIRMWARE_INIT;
	TEST_SUCC(vb2_check_dev_switch(ctx), "secdata_firmware fail recovery");
	TEST_EQ(sd->flags & VB2_SD_FLAG_DEV_MODE_ENABLED, 0, "  sd not in dev");
	TEST_EQ(ctx->flags & VB2_CONTEXT_DEVELOPER_MODE, 0, "  ctx not in dev");

	/* And doesn't check or clear dev disable request */
	reset_common_data();
	ctx->flags |= VB2_CONTEXT_RECOVERY_MODE;
	sd->status &= ~VB2_SD_STATUS_SECDATA_FIRMWARE_INIT;
	vb2_nv_set(ctx, VB2_NV_DISABLE_DEV_REQUEST, 1);
	TEST_SUCC(vb2_check_dev_switch(ctx),
		  "secdata_firmware fail recovery disable");
	TEST_EQ(sd->flags & VB2_SD_FLAG_DEV_MODE_ENABLED, 0, "  sd not in dev");
	TEST_EQ(ctx->flags & VB2_CONTEXT_DEVELOPER_MODE, 0, "  ctx not in dev");
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_DISABLE_DEV_REQUEST),
		1, "  request not cleared");

	/* Can still override with GBB flag */
	reset_common_data();
	ctx->flags |= VB2_CONTEXT_RECOVERY_MODE;
	sd->status &= ~VB2_SD_STATUS_SECDATA_FIRMWARE_INIT;
	gbb.flags |= VB2_GBB_FLAG_FORCE_DEV_SWITCH_ON;
	TEST_SUCC(vb2_check_dev_switch(ctx),
		  "secdata_firmware fail recovery gbb");
	TEST_NEQ(sd->flags & VB2_SD_FLAG_DEV_MODE_ENABLED, 0, "  sd in dev");
	TEST_NEQ(ctx->flags & VB2_CONTEXT_DEVELOPER_MODE, 0, "  ctx in dev");
	TEST_EQ(mock_tpm_clear_called, 1, "  tpm clear");

	/* Force wipeout by ctx flag */
	reset_common_data();
	ctx->flags |= VB2_CONTEXT_FORCE_WIPEOUT_MODE;
	TEST_SUCC(vb2_check_dev_switch(ctx), "wipeout on ctx flag");
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_REQ_WIPEOUT), 1, "  nv wipeout");
}

static void enable_dev_tests(void)
{
	reset_common_data();
	TEST_FAIL(vb2api_enable_developer_mode(ctx),
		 "vb2api_enable_developer_mode - failed");
	TEST_EQ(vb2_secdata_firmware_get(ctx, VB2_SECDATA_FIRMWARE_FLAGS) &
		VB2_SECDATA_FIRMWARE_FLAG_DEV_MODE, 0,
		"  dev mode flag not set");

	reset_common_data();
	SET_BOOT_MODE(ctx, VB2_BOOT_MODE_MANUAL_RECOVERY,
		      VB2_RECOVERY_RO_MANUAL);
	TEST_SUCC(vb2api_enable_developer_mode(ctx),
		  "vb2api_enable_developer_mode - success");
	TEST_NEQ(vb2_secdata_firmware_get(ctx, VB2_SECDATA_FIRMWARE_FLAGS) &
	         VB2_SECDATA_FIRMWARE_FLAG_DEV_MODE, 0,
		 "  dev mode flag set");

	/* secdata_firmware not initialized, aborts */
	reset_common_data();
	SET_BOOT_MODE(ctx, VB2_BOOT_MODE_MANUAL_RECOVERY,
		      VB2_RECOVERY_RO_MANUAL);
	sd->status &= ~VB2_SD_STATUS_SECDATA_FIRMWARE_INIT;
	sd->status |= VB2_SD_STATUS_RECOVERY_DECIDED;
	TEST_SUCC(vb2api_enable_developer_mode(ctx),
		  "secdata_firmware no init, enable dev mode ignored");
	sd->status |= VB2_SD_STATUS_SECDATA_FIRMWARE_INIT;
	TEST_EQ(vb2_secdata_firmware_get(ctx, VB2_SECDATA_FIRMWARE_FLAGS) &
	        VB2_SECDATA_FIRMWARE_FLAG_DEV_MODE, 0,
		"  dev mode flag not set");
}

static void tpm_clear_tests(void)
{
	/* No clear request */
	reset_common_data();
	TEST_SUCC(vb2_check_tpm_clear(ctx), "no clear request");
	TEST_EQ(mock_tpm_clear_called, 0, "tpm not cleared");

	/* Successful request */
	reset_common_data();
	vb2_nv_set(ctx, VB2_NV_CLEAR_TPM_OWNER_REQUEST, 1);
	TEST_SUCC(vb2_check_tpm_clear(ctx), "clear request");
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_CLEAR_TPM_OWNER_REQUEST),
		0, "request cleared");
	TEST_EQ(vb2_nv_get(ctx, 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(ctx, VB2_NV_CLEAR_TPM_OWNER_REQUEST, 1);
	TEST_EQ(vb2_check_tpm_clear(ctx),
		VB2_ERROR_EX_TPM_CLEAR_OWNER, "clear failure");
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_CLEAR_TPM_OWNER_REQUEST),
		0, "request cleared");
	TEST_EQ(vb2_nv_get(ctx, 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(ctx), "select slot A");
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_FW_RESULT),
		VB2_FW_RESULT_UNKNOWN, "result unknown");
	TEST_NEQ(sd->status & VB2_SD_STATUS_CHOSE_SLOT, 0, "chose slot");
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_FW_TRIED), 0, "tried A");
	TEST_EQ(sd->fw_slot, 0, "selected A");
	TEST_EQ(ctx->flags & VB2_CONTEXT_FW_SLOT_B, 0, "didn't choose B");

	/* Slot B */
	reset_common_data();
	vb2_nv_set(ctx, VB2_NV_TRY_NEXT, 1);
	TEST_SUCC(vb2_select_fw_slot(ctx), "select slot B");
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_FW_RESULT),
		VB2_FW_RESULT_UNKNOWN, "result unknown");
	TEST_NEQ(sd->status & VB2_SD_STATUS_CHOSE_SLOT, 0, "chose slot");
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_FW_TRIED), 1, "tried B");
	TEST_EQ(sd->fw_slot, 1, "selected B");
	TEST_NEQ(ctx->flags & VB2_CONTEXT_FW_SLOT_B, 0, "ctx says choose B");

	/* Slot A ran out of tries */
	reset_common_data();
	vb2_nv_set(ctx, VB2_NV_FW_RESULT, VB2_FW_RESULT_TRYING);
	TEST_SUCC(vb2_select_fw_slot(ctx), "select slot A out of tries");
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_TRY_NEXT), 1, "try B next");
	TEST_NEQ(sd->status & VB2_SD_STATUS_CHOSE_SLOT, 0, "chose slot");
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_FW_TRIED), 1, "tried B");
	TEST_EQ(sd->fw_slot, 1, "selected B");
	TEST_NEQ(ctx->flags & VB2_CONTEXT_FW_SLOT_B, 0, "ctx says choose B");

	/* Slot A ran out of tries, but slot B not present */
	reset_common_data();
	ctx->flags |= VB2_CONTEXT_SLOT_A_ONLY;
	vb2_nv_set(ctx, VB2_NV_FW_RESULT, VB2_FW_RESULT_TRYING);
	TEST_EQ(vb2_select_fw_slot(ctx), VB2_ERROR_API_NEXT_SLOT_UNAVAILABLE,
		"don't try slot B if only one slot present");
	TEST_EQ(sd->status & VB2_SD_STATUS_CHOSE_SLOT, 0, "didn't choose slot");
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_FW_TRIED), 0, "tried A");
	TEST_EQ(sd->fw_slot, 0, "selected A");
	TEST_EQ(ctx->flags & VB2_CONTEXT_FW_SLOT_B, 0, "didn't choose B");
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_TRY_NEXT), 0, "didn't try B next");

	/* Slot A ran out of tries, even with nofail active */
	reset_common_data();
	ctx->flags |= VB2_CONTEXT_NOFAIL_BOOT;
	vb2_nv_set(ctx, VB2_NV_FW_RESULT, VB2_FW_RESULT_TRYING);
	TEST_SUCC(vb2_select_fw_slot(ctx), "select slot A out of tries");
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_TRY_NEXT), 1, "try B next");
	TEST_NEQ(sd->status & VB2_SD_STATUS_CHOSE_SLOT, 0, "chose slot");
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_FW_TRIED), 1, "tried B");
	TEST_EQ(sd->fw_slot, 1, "selected B");
	TEST_NEQ(ctx->flags & VB2_CONTEXT_FW_SLOT_B, 0, "ctx says choose B");

	/* Slot A used up a try */
	reset_common_data();
	vb2_nv_set(ctx, VB2_NV_TRY_COUNT, 3);
	TEST_SUCC(vb2_select_fw_slot(ctx), "try slot A");
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_FW_RESULT),
		VB2_FW_RESULT_TRYING, "result trying");
	TEST_NEQ(sd->status & VB2_SD_STATUS_CHOSE_SLOT, 0, "chose slot");
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_FW_TRIED), 0, "tried A");
	TEST_EQ(sd->fw_slot, 0, "selected A");
	TEST_EQ(ctx->flags & VB2_CONTEXT_FW_SLOT_B, 0, "didn't choose B");
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_TRY_COUNT), 2, "tries decremented");

	/* Slot A failed, but nofail active */
	reset_common_data();
	ctx->flags |= VB2_CONTEXT_NOFAIL_BOOT;
	vb2_nv_set(ctx, VB2_NV_TRY_COUNT, 3);
	TEST_SUCC(vb2_select_fw_slot(ctx), "try slot A");
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_FW_RESULT),
		VB2_FW_RESULT_TRYING, "result trying");
	TEST_NEQ(sd->status & VB2_SD_STATUS_CHOSE_SLOT, 0, "chose slot");
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_FW_TRIED), 0, "tried A");
	TEST_EQ(sd->fw_slot, 0, "selected A");
	TEST_EQ(ctx->flags & VB2_CONTEXT_FW_SLOT_B, 0, "didn't choose B");
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_TRY_COUNT), 3, "tries not decremented");

	/* Tried/result get copied to the previous fields */
	reset_common_data();
	vb2_nv_set(ctx, VB2_NV_FW_TRIED, 0);
	vb2_nv_set(ctx, VB2_NV_FW_RESULT, VB2_FW_RESULT_SUCCESS);
	vb2_select_fw_slot(ctx);
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_FW_PREV_TRIED), 0, "prev A");
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_FW_PREV_RESULT),	VB2_FW_RESULT_SUCCESS,
		"prev success");

	reset_common_data();
	vb2_nv_set(ctx, VB2_NV_FW_TRIED, 1);
	vb2_nv_set(ctx, VB2_NV_FW_RESULT, VB2_FW_RESULT_FAILURE);
	vb2_select_fw_slot(ctx);
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_FW_PREV_TRIED), 1, "prev B");
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_FW_PREV_RESULT),	VB2_FW_RESULT_FAILURE,
		"prev failure");
}

static void need_reboot_for_display_tests(void)
{
	/* Display not available, reboot required */
	reset_common_data();
	TEST_EQ(vb2api_need_reboot_for_display(ctx), 1,
		"need_reboot_for_display: need reboot");
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_DISPLAY_REQUEST), 1,
		"  set display request");

	/* Display available, don't need reboot */
	reset_common_data();
	sd->flags |= VB2_SD_FLAG_DISPLAY_AVAILABLE;
	TEST_EQ(vb2api_need_reboot_for_display(ctx), 0,
		"need_reboot_for_display: don't need reboot");
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_DISPLAY_REQUEST), 0,
		"  not set display request");
}

static void clear_recovery_tests(void)
{

	/* Manual recovery */
	reset_common_data();
	SET_BOOT_MODE(ctx, VB2_BOOT_MODE_MANUAL_RECOVERY, 4);
	vb2_nv_set(ctx, VB2_NV_RECOVERY_REQUEST, 5);
	vb2_nv_set(ctx, VB2_NV_RECOVERY_SUBCODE, 13);
	vb2api_clear_recovery(ctx);
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_RECOVERY_REQUEST),
		0, "  request cleared");
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_RECOVERY_SUBCODE),
		0, "  subcode cleared");

	/* Broken screen */
	reset_common_data();
	SET_BOOT_MODE(ctx, VB2_BOOT_MODE_BROKEN_SCREEN, 4);
	vb2_nv_set(ctx, VB2_NV_RECOVERY_REQUEST, 5);
	vb2_nv_set(ctx, VB2_NV_RECOVERY_SUBCODE, 13);
	vb2api_clear_recovery(ctx);
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_RECOVERY_REQUEST),
		0, "  request cleared");
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_RECOVERY_SUBCODE),
		4, "  subcode shifted");
}

static void get_recovery_reason_tests(void)
{
	reset_common_data();
	sd->recovery_reason = 4;
	TEST_EQ(vb2api_get_recovery_reason(ctx), 4, "correct recovery reason");
}

static void diagnostic_ui_enabled_tests(void)
{
	reset_common_data();
	vb2api_secdata_kernel_create(ctx);
	vb2_secdata_kernel_init(ctx);
	TEST_EQ(vb2api_diagnostic_ui_enabled(ctx), 1,
		"diagnostic UI enabled");

	reset_common_data();
	vb2api_secdata_kernel_create(ctx);
	vb2_secdata_kernel_init(ctx);
	vb2_secdata_kernel_set(
		ctx, VB2_SECDATA_KERNEL_FLAGS,
		VB2_SECDATA_KERNEL_FLAG_DIAGNOSTIC_UI_DISABLED);
	TEST_EQ(vb2api_diagnostic_ui_enabled(ctx), 0,
		"diagnostic UI disabled");
}

static void dev_default_boot_tests(void)
{
	/* No default boot */
	reset_common_data();
	TEST_EQ(vb2api_get_dev_default_boot_target(ctx),
		VB2_DEV_DEFAULT_BOOT_TARGET_INTERNAL,
		"no default boot, boot disk");

	/* Set boot altfw by GBB */
	reset_common_data();
	gbb.flags |= VB2_GBB_FLAG_DEFAULT_DEV_BOOT_ALTFW;
	vb2_nv_set(ctx, VB2_NV_DEV_DEFAULT_BOOT,
		   VB2_DEV_DEFAULT_BOOT_TARGET_EXTERNAL);
	TEST_EQ(vb2api_get_dev_default_boot_target(ctx),
		VB2_DEV_DEFAULT_BOOT_TARGET_ALTFW,
		"GBB set default boot altfw");

	/* Boot from internal disk */
	reset_common_data();
	vb2_nv_set(ctx, VB2_NV_DEV_DEFAULT_BOOT,
		   VB2_DEV_DEFAULT_BOOT_TARGET_INTERNAL);
	TEST_EQ(vb2api_get_dev_default_boot_target(ctx),
		VB2_DEV_DEFAULT_BOOT_TARGET_INTERNAL,
		"set default boot internal disk");

	/* Boot from external disk */
	reset_common_data();
	ctx->flags |= VB2_CONTEXT_DEV_BOOT_EXTERNAL_ALLOWED;
	vb2_nv_set(ctx, VB2_NV_DEV_DEFAULT_BOOT,
		   VB2_DEV_DEFAULT_BOOT_TARGET_EXTERNAL);
	TEST_EQ(vb2api_get_dev_default_boot_target(ctx),
		VB2_DEV_DEFAULT_BOOT_TARGET_EXTERNAL,
		"set default boot external disk");

	/* Boot from external disk not allowed */
	reset_common_data();
	vb2_nv_set(ctx, VB2_NV_DEV_DEFAULT_BOOT,
		   VB2_DEV_DEFAULT_BOOT_TARGET_EXTERNAL);
	TEST_EQ(vb2api_get_dev_default_boot_target(ctx),
		VB2_DEV_DEFAULT_BOOT_TARGET_INTERNAL,
		"default boot external not allowed");
	reset_common_data();
	ctx->flags |= VB2_CONTEXT_DEV_BOOT_ALTFW_ALLOWED;
	vb2_nv_set(ctx, VB2_NV_DEV_DEFAULT_BOOT,
		   VB2_DEV_DEFAULT_BOOT_TARGET_EXTERNAL);
	TEST_EQ(vb2api_get_dev_default_boot_target(ctx),
		VB2_DEV_DEFAULT_BOOT_TARGET_INTERNAL,
		"default boot external not allowed");

	/* Boot altfw */
	reset_common_data();
	ctx->flags |= VB2_CONTEXT_DEV_BOOT_ALTFW_ALLOWED;
	vb2_nv_set(ctx, VB2_NV_DEV_DEFAULT_BOOT,
		   VB2_DEV_DEFAULT_BOOT_TARGET_ALTFW);
	TEST_EQ(vb2api_get_dev_default_boot_target(ctx),
		VB2_DEV_DEFAULT_BOOT_TARGET_ALTFW,
		"set default boot altfw");

	/* Boot altfw not allowed */
	reset_common_data();
	vb2_nv_set(ctx, VB2_NV_DEV_DEFAULT_BOOT,
		   VB2_DEV_DEFAULT_BOOT_TARGET_ALTFW);
	TEST_EQ(vb2api_get_dev_default_boot_target(ctx),
		VB2_DEV_DEFAULT_BOOT_TARGET_INTERNAL,
		"default boot altfw not allowed");
	reset_common_data();
	ctx->flags |= VB2_CONTEXT_DEV_BOOT_EXTERNAL_ALLOWED;
	vb2_nv_set(ctx, VB2_NV_DEV_DEFAULT_BOOT,
		   VB2_DEV_DEFAULT_BOOT_TARGET_ALTFW);
	TEST_EQ(vb2api_get_dev_default_boot_target(ctx),
		VB2_DEV_DEFAULT_BOOT_TARGET_INTERNAL,
		"default boot altfw not allowed");
}

static void fill_dev_boot_flags_tests(void)
{
	/* Dev boot - allowed by default */
	reset_common_data();
	vb2_fill_dev_boot_flags(ctx);
	TEST_TRUE(ctx->flags & VB2_CONTEXT_DEV_BOOT_ALLOWED,
		  "dev boot - allowed by default");

	/* Dev boot - disabled by FWMP */
	reset_common_data();
	fwmp->flags |= VB2_SECDATA_FWMP_DEV_DISABLE_BOOT;
	vb2_fill_dev_boot_flags(ctx);
	TEST_FALSE(ctx->flags & VB2_CONTEXT_DEV_BOOT_ALLOWED,
		   "dev boot - FWMP disabled");

	/* Dev boot - force enabled by GBB */
	reset_common_data();
	fwmp->flags |= VB2_SECDATA_FWMP_DEV_DISABLE_BOOT;
	gbb.flags |= VB2_GBB_FLAG_FORCE_DEV_SWITCH_ON;
	vb2_fill_dev_boot_flags(ctx);
	TEST_TRUE(ctx->flags & VB2_CONTEXT_DEV_BOOT_ALLOWED,
		  "dev boot - GBB force dev on");

	/* External boot - not allowed by default */
	reset_common_data();
	vb2_fill_dev_boot_flags(ctx);
	TEST_FALSE(ctx->flags & VB2_CONTEXT_DEV_BOOT_EXTERNAL_ALLOWED,
		   "dev boot external - not allowed by default");

	/* External boot - enabled by nvdata */
	reset_common_data();
	vb2_nv_set(ctx, VB2_NV_DEV_BOOT_EXTERNAL, 1);
	vb2_fill_dev_boot_flags(ctx);
	TEST_TRUE(ctx->flags & VB2_CONTEXT_DEV_BOOT_EXTERNAL_ALLOWED,
		  "dev boot external - nvdata enabled");

	/* External boot - enabled by FWMP */
	reset_common_data();
	fwmp->flags |= VB2_SECDATA_FWMP_DEV_ENABLE_EXTERNAL;
	vb2_fill_dev_boot_flags(ctx);
	TEST_TRUE(ctx->flags & VB2_CONTEXT_DEV_BOOT_EXTERNAL_ALLOWED,
		  "dev boot external - secdata enabled");

	/* External boot - force enabled by GBB */
	reset_common_data();
	gbb.flags |= VB2_GBB_FLAG_FORCE_DEV_BOOT_USB;
	vb2_fill_dev_boot_flags(ctx);
	TEST_TRUE(ctx->flags & VB2_CONTEXT_DEV_BOOT_EXTERNAL_ALLOWED,
		  "dev boot external - GBB force enabled");

	/* External boot - set all flags */
	reset_common_data();
	vb2_nv_set(ctx, VB2_NV_DEV_BOOT_EXTERNAL, 1);
	fwmp->flags |= VB2_SECDATA_FWMP_DEV_ENABLE_EXTERNAL;
	gbb.flags |= VB2_GBB_FLAG_FORCE_DEV_BOOT_USB;
	vb2_fill_dev_boot_flags(ctx);
	TEST_TRUE(ctx->flags & VB2_CONTEXT_DEV_BOOT_EXTERNAL_ALLOWED,
		  "dev boot external - all flags set");

	/* Alternate boot - not allowed by default */
	reset_common_data();
	vb2_fill_dev_boot_flags(ctx);
	TEST_FALSE(ctx->flags & VB2_CONTEXT_DEV_BOOT_ALTFW_ALLOWED,
		   "dev boot altfw - not allowed by default");

	/* Alternate boot - enabled by nvdata */
	reset_common_data();
	vb2_nv_set(ctx, VB2_NV_DEV_BOOT_ALTFW, 1);
	vb2_fill_dev_boot_flags(ctx);
	TEST_TRUE(ctx->flags & VB2_CONTEXT_DEV_BOOT_ALTFW_ALLOWED,
		  "dev boot altfw - nvdata enabled");

	/* Alternate boot - enabled by FWMP */
	reset_common_data();
	fwmp->flags |= VB2_SECDATA_FWMP_DEV_ENABLE_ALTFW;
	vb2_fill_dev_boot_flags(ctx);
	TEST_TRUE(ctx->flags & VB2_CONTEXT_DEV_BOOT_ALTFW_ALLOWED,
		  "dev boot altfw - secdata enabled");

	/* Alternate boot - force enabled by GBB */
	reset_common_data();
	gbb.flags |= VB2_GBB_FLAG_FORCE_DEV_BOOT_ALTFW;
	vb2_fill_dev_boot_flags(ctx);
	TEST_TRUE(ctx->flags & VB2_CONTEXT_DEV_BOOT_ALTFW_ALLOWED,
		  "dev boot altfw - GBB force enabled");

	/* Alternate boot - set all flags */
	reset_common_data();
	vb2_nv_set(ctx, VB2_NV_DEV_BOOT_ALTFW, 1);
	fwmp->flags |= VB2_SECDATA_FWMP_DEV_ENABLE_ALTFW;
	gbb.flags |= VB2_GBB_FLAG_FORCE_DEV_BOOT_ALTFW;
	vb2_fill_dev_boot_flags(ctx);
	TEST_TRUE(ctx->flags & VB2_CONTEXT_DEV_BOOT_ALTFW_ALLOWED,
		  "dev boot altfw - all flags set");
}

static void use_dev_screen_short_delay_tests(void)
{
	/* Normal delay */
	reset_common_data();
	TEST_EQ(vb2api_use_short_dev_screen_delay(ctx), 0,
		"short delay: no");

	/* Short delay */
	gbb.flags |= VB2_GBB_FLAG_DEV_SCREEN_SHORT_DELAY;
	TEST_EQ(vb2api_use_short_dev_screen_delay(ctx), 1,
		"short delay: yes");
}

int main(int argc, char* argv[])
{
	init_workbuf_tests();
	misc_tests();
	gbb_tests();
	fail_tests();
	previous_boot_fail_tests();
	recovery_tests();
	dev_switch_tests();
	enable_dev_tests();
	tpm_clear_tests();
	select_slot_tests();
	need_reboot_for_display_tests();
	clear_recovery_tests();
	get_recovery_reason_tests();
	diagnostic_ui_enabled_tests();
	dev_default_boot_tests();
	fill_dev_boot_flags_tests();
	use_dev_screen_short_delay_tests();

	return gTestSuccess ? 0 : 255;
}
