int RestoreNvFromBackup(VbNvContext *vnc)
{
	VbNvContext bvnc;
	uint32_t value;
	int i;

	VBDEBUG(("TPM: %s()\n", __func__));

	if (TPM_SUCCESS != RollbackBackupRead(bvnc.raw))
		return 1;

	VbNvSetup(&bvnc);
	if (bvnc.regenerate_crc) {
		VBDEBUG(("TPM: Oops, backup is no good.\n"));
		return 1;
	}

	for (i = 0; i < ARRAY_SIZE(backup_params); i++) {
		VbNvGet(&bvnc, backup_params[i], &value);
		VbNvSet(vnc, backup_params[i], value);
	}

	/* VbNvTeardown(&bvnc); is not needed. We're done with it. */
	return 0;
}
Example #2
0
static void enable_graphics(void)
{
	if (!CONFIG_OPROM_MATTERS)
		return;

	int oprom_loaded = flag_fetch(FLAG_OPROM);

	// Manipulating vboot's internal data and calling its internal
	// functions is NOT NICE and will give you athlete's foot and make
	// you unpopular at parties. Right now it's the only way to ensure
	// graphics are enabled, though, so it's a necessary evil.
	if (!oprom_loaded) {
		printf("Enabling graphics.\n");

		VbNvContext context;

		VbExNvStorageRead(context.raw);
		VbNvSetup(&context);

		VbNvSet(&context, VBNV_OPROM_NEEDED, 1);

		VbNvTeardown(&context);
		VbExNvStorageWrite(context.raw);

		printf("Rebooting.\n");
		if (cold_reboot())
			halt();
	}
}
Example #3
0
int VbGetNvStorage(VbNvParam param) {
  uint32_t value;
  int retval;
  static VbNvContext cached_vnc;

  /* TODO: locking around NV access */
  if (!vnc_read) {
    if (0 != VbReadNvStorage(&cached_vnc))
      return -1;
    vnc_read = 1;
  }

  if (0 != VbNvSetup(&cached_vnc))
    return -1;
  retval = VbNvGet(&cached_vnc, param, &value);
  if (0 != VbNvTeardown(&cached_vnc))
    return -1;
  if (0 != retval)
    return -1;

  /* TODO: If vnc.raw_changed, attempt to reopen NVRAM for write and
   * save the new defaults.  If we're able to, log. */
  /* TODO: release lock */

  return (int)value;
}
Example #4
0
int VbSetNvStorage(VbNvParam param, int value) {
  VbNvContext vnc;
  int retval = -1;
  int i;

  if (0 != VbReadNvStorage(&vnc))
    return -1;

  if (0 != VbNvSetup(&vnc))
    goto VbSetNvCleanup;
  i = VbNvSet(&vnc, param, (uint32_t)value);
  if (0 != VbNvTeardown(&vnc))
    goto VbSetNvCleanup;
  if (0 != i)
    goto VbSetNvCleanup;

  if (vnc.raw_changed) {
    vnc_read = 0;
    if (0 != VbWriteNvStorage(&vnc))
      goto VbSetNvCleanup;
  }

  /* Success */
  retval = 0;

VbSetNvCleanup:
  /* TODO: release lock */
  return retval;
}
Example #5
0
/* Reset mock data (for use before each test) */
static void ResetMocks(void)
{
	Memset(&cparams, 0, sizeof(cparams));
	cparams.shared_data_size = sizeof(shared_data);
	cparams.shared_data_blob = shared_data;
	cparams.gbb_data = &gbb;

	Memset(&gbb, 0, sizeof(gbb));
	gbb.major_version = GBB_MAJOR_VER;
	gbb.minor_version = GBB_MINOR_VER;
	gbb.flags = 0;

	/*
	 * Only the outermost vboot_api_kernel call sets vboot_api_kernel's
	 * vnc.  So clear it here too.
	 */
	Memset(VbApiKernelGetVnc(), 0, sizeof(VbNvContext));
	VbNvSetup(VbApiKernelGetVnc());
	VbNvTeardown(VbApiKernelGetVnc()); /* So CRC gets generated */

	Memset(&shared_data, 0, sizeof(shared_data));
	VbSharedDataInit(shared, sizeof(shared_data));

	trust_ec = 0;
	mock_in_rw = 0;
	ec_ro_protected = 0;
	ec_rw_protected = 0;
	ec_run_image = 0;   /* 0 = RO, 1 = RW */
	ec_ro_updated = 0;
	ec_rw_updated = 0;
	in_rw_retval = VBERROR_SUCCESS;
	protect_retval = VBERROR_SUCCESS;
	update_retval = VBERROR_SUCCESS;
	run_retval = VBERROR_SUCCESS;
	get_expected_retval = VBERROR_SUCCESS;
	shutdown_request_calls_left = -1;

	Memset(mock_ec_ro_hash, 0, sizeof(mock_ec_ro_hash));
	mock_ec_ro_hash[0] = 42;
	mock_ec_ro_hash_size = sizeof(mock_ec_ro_hash);

	Memset(mock_ec_rw_hash, 0, sizeof(mock_ec_rw_hash));
	mock_ec_rw_hash[0] = 42;
	mock_ec_rw_hash_size = sizeof(mock_ec_rw_hash);

	Memset(want_ec_hash, 0, sizeof(want_ec_hash));
	want_ec_hash[0] = 42;
	want_ec_hash_size = sizeof(want_ec_hash);

	update_hash = 42;

	Memset(mock_sha, 0, sizeof(want_ec_hash));
	mock_sha[0] = 42;

	// TODO: ensure these are actually needed

	Memset(screens_displayed, 0, sizeof(screens_displayed));
	screens_count = 0;
}
Example #6
0
/* Reset mock data (for use before each test) */
static void ResetMocks(void)
{
	int gbb_used;

	Memset(gbb_data, 0, sizeof(gbb_data));
	gbb->major_version = GBB_MAJOR_VER;
	gbb->minor_version = GBB_MINOR_VER;
	gbb->flags = 0;
	gbb_used = sizeof(GoogleBinaryBlockHeader);

	gbb->hwid_offset = gbb_used;
	strcpy(gbb_data + gbb->hwid_offset, "Test HWID");
	gbb->hwid_size = strlen(gbb_data + gbb->hwid_offset) + 1;
	gbb_used = (gbb_used + gbb->hwid_size + 7) & ~7;

	gbb->bmpfv_offset = gbb_used;
	bhdr = (BmpBlockHeader *)(gbb_data + gbb->bmpfv_offset);
	gbb->bmpfv_size = sizeof(BmpBlockHeader);
	gbb_used = (gbb_used + gbb->bmpfv_size + 7) & ~7;
	memcpy(bhdr->signature, BMPBLOCK_SIGNATURE, BMPBLOCK_SIGNATURE_SIZE);
	bhdr->major_version = BMPBLOCK_MAJOR_VERSION;
	bhdr->minor_version = BMPBLOCK_MINOR_VERSION;
	bhdr->number_of_localizations = 3;

	Memset(&cparams, 0, sizeof(cparams));
	cparams.shared_data_size = sizeof(shared_data);
	cparams.shared_data_blob = shared_data;
	cparams.gbb_data = gbb;
	cparams.gbb_size = sizeof(gbb_data);

	/*
	 * Note, VbApiKernelFree() expects this to be allocated by
	 * VbExMalloc(), so we cannot just assign it staticly.
	 */
	cparams.gbb = VbExMalloc(sizeof(*gbb));
	gbb->header_size = sizeof(*gbb);
	gbb->rootkey_offset = gbb_used;
	gbb->rootkey_size = 64;
	gbb_used += 64;
	gbb->recovery_key_offset = gbb_used;
	gbb->recovery_key_size = 64;
	gbb_used += 64;
	memcpy(cparams.gbb, gbb, sizeof(*gbb));

	Memset(&vnc, 0, sizeof(vnc));
	VbNvSetup(&vnc);
	VbNvTeardown(&vnc);                   /* So CRC gets generated */

	Memset(&shared_data, 0, sizeof(shared_data));
	VbSharedDataInit(shared, sizeof(shared_data));

	*debug_info = 0;
}
VbError_t VbLockDevice(void)
{
    VbExNvStorageRead(vnc.raw);
    VbNvSetup(&vnc);

    VBDEBUG(("%s() - Storing request to leave dev-mode.\n",
             __func__));
    VbNvSet(&vnc, VBNV_DISABLE_DEV_REQUEST,
            1);

    VbNvTeardown(&vnc);
    if (vnc.raw_changed)
        VbExNvStorageWrite(vnc.raw);

    VBDEBUG(("%s() Mode change will take effect on next reboot.\n",
             __func__));

    return VBERROR_SUCCESS;
}
int SaveNvToBackup(VbNvContext *vnc)
{
	VbNvContext bvnc;
	uint32_t value;
	int i;

	VBDEBUG(("TPM: %s()\n", __func__));

	/* Read it first. No point in writing the same data. */
	if (TPM_SUCCESS != RollbackBackupRead(bvnc.raw))
		return 1;

	VbNvSetup(&bvnc);
	VBDEBUG(("TPM: existing backup is %s\n",
		 bvnc.regenerate_crc ? "bad" : "good"));

	for (i = 0; i < ARRAY_SIZE(backup_params); i++) {
		VbNvGet(vnc, backup_params[i], &value);
		VbNvSet(&bvnc, backup_params[i], value);
	}

	VbNvTeardown(&bvnc);

	if (!bvnc.raw_changed) {
		VBDEBUG(("TPM: Nothing's changed, not writing backup\n"));
		/* Clear the request flag, since we're happy. */
		VbNvSet(vnc, VBNV_BACKUP_NVRAM_REQUEST, 0);
		return 0;
	}

	if (TPM_SUCCESS == RollbackBackupWrite(bvnc.raw)) {
		/* Clear the request flag if we wrote successfully too */
		VbNvSet(vnc, VBNV_BACKUP_NVRAM_REQUEST, 0);
		return 0;
	}

	VBDEBUG(("TPM: Sorry, couldn't write backup.\n"));
	return 1;
}
Example #9
0
VbError_t VbInit(VbCommonParams *cparams, VbInitParams *iparams)
{
	VbSharedDataHeader *shared =
		(VbSharedDataHeader *)cparams->shared_data_blob;
	GoogleBinaryBlockHeader gbb;
	VbNvContext vnc;
	VbError_t retval = VBERROR_SUCCESS;
	uint32_t recovery = VBNV_RECOVERY_NOT_REQUESTED;
	int is_s3_resume = 0;
	uint32_t s3_debug_boot = 0;
	uint32_t require_official_os = 0;
	uint32_t tpm_version = 0;
	uint32_t tpm_status = 0;
	int has_virt_dev_switch = 0;
	int is_hw_dev = 0;
	int is_virt_dev = 0;
	uint32_t disable_dev_request = 0;
	uint32_t clear_tpm_owner_request = 0;
	int is_dev = 0;
	uint32_t backup_requested = 0;
	uint32_t backup_for_safety = 0;
	int lost_nvram;

	/* Initialize output flags */
	iparams->out_flags = 0;

	retval = VbGbbReadHeader_static(cparams, &gbb);
	if (retval)
		return retval;

	VBDEBUG(("VbInit() input flags 0x%x gbb flags 0x%x\n", iparams->flags,
		 gbb.flags));

	/* Set up NV storage */
	VbExNvStorageRead(vnc.raw);
	VbNvSetup(&vnc);
	lost_nvram = vnc.regenerate_crc;

	/* Initialize shared data structure */
	if (0 != VbSharedDataInit(shared, cparams->shared_data_size)) {
		VBDEBUG(("Shared data init error\n"));
		return VBERROR_INIT_SHARED_DATA;
	}

	shared->timer_vb_init_enter = VbExGetTimer();

	/* Copy some boot switch flags */
	/* TODO: in next refactor, just save in/out flags in VbSharedData */
	shared->flags = 0;
	if (iparams->flags & VB_INIT_FLAG_REC_BUTTON_PRESSED)
		shared->flags |= VBSD_BOOT_REC_SWITCH_ON;
	if (iparams->flags & VB_INIT_FLAG_WP_ENABLED)
		shared->flags |= VBSD_BOOT_FIRMWARE_WP_ENABLED;
	if (iparams->flags & VB_INIT_FLAG_SW_WP_ENABLED)
		shared->flags |= VBSD_BOOT_FIRMWARE_SW_WP_ENABLED;
	if (iparams->flags & VB_INIT_FLAG_S3_RESUME)
		shared->flags |= VBSD_BOOT_S3_RESUME;
	if (iparams->flags & VB_INIT_FLAG_RO_NORMAL_SUPPORT)
		shared->flags |= VBSD_BOOT_RO_NORMAL_SUPPORT;
	if (iparams->flags & VB_INIT_FLAG_EC_SOFTWARE_SYNC)
		shared->flags |= VBSD_EC_SOFTWARE_SYNC;
	if (iparams->flags & VB_INIT_FLAG_EC_SLOW_UPDATE)
		shared->flags |= VBSD_EC_SLOW_UPDATE;
	if (iparams->flags & VB_INIT_FLAG_VIRTUAL_REC_SWITCH)
		shared->flags |= VBSD_BOOT_REC_SWITCH_VIRTUAL;
	if (iparams->flags & VB_INIT_FLAG_OPROM_MATTERS)
		shared->flags |= VBSD_OPROM_MATTERS;
	if (iparams->flags & VB_INIT_FLAG_OPROM_LOADED)
		shared->flags |= VBSD_OPROM_LOADED;

	is_s3_resume = (iparams->flags & VB_INIT_FLAG_S3_RESUME ? 1 : 0);

	/* Check if the OS is requesting a debug S3 reset */
	VbNvGet(&vnc, VBNV_DEBUG_RESET_MODE, &s3_debug_boot);
	if (s3_debug_boot) {
		if (is_s3_resume) {
			VBDEBUG(("VbInit() requesting S3 debug boot\n"));
			iparams->out_flags |= VB_INIT_OUT_S3_DEBUG_BOOT;
			is_s3_resume = 0;  /* Proceed as if normal boot */
		}

		/*
		 * Clear the request even if this is a normal boot, since we
		 * don't want the NEXT S3 resume to be a debug reset unless the
		 * OS asserts the request again.
		 */
		VbNvSet(&vnc, VBNV_DEBUG_RESET_MODE, 0);
	}

	/*
	 * If this isn't a S3 resume, read the current recovery request, then
	 * clear it so we don't get stuck in recovery mode.
	 */
	if (!is_s3_resume) {
		VbNvGet(&vnc, VBNV_RECOVERY_REQUEST, &recovery);
		VBDEBUG(("VbInit sees recovery request = %d\n", recovery));
		if (VBNV_RECOVERY_NOT_REQUESTED != recovery)
			VbNvSet(&vnc, VBNV_RECOVERY_REQUEST,
				VBNV_RECOVERY_NOT_REQUESTED);
	}

	/*
	 * If the previous boot failed in the firmware somewhere outside of
	 * verified boot, and recovery is not requested for our own reasons,
	 * request recovery mode.  This gives the calling firmware a way to
	 * request recovery if it finds something terribly wrong.
	 */
	if (VBNV_RECOVERY_NOT_REQUESTED == recovery &&
	    iparams->flags & VB_INIT_FLAG_PREVIOUS_BOOT_FAIL) {
		recovery = VBNV_RECOVERY_RO_FIRMWARE;
	}

	/*
	 * If recovery button is pressed, override recovery reason.  Note that
	 * we do this in the S3 resume path also.
	 */
	if (iparams->flags & VB_INIT_FLAG_REC_BUTTON_PRESSED)
		recovery = VBNV_RECOVERY_RO_MANUAL;

	/*
	 * Copy current recovery reason to shared data. If we fail later on, it
	 * won't matter, since we'll just reboot.
	 */
	shared->recovery_reason = (uint8_t)recovery;
	VBDEBUG(("VbInit now sets shared->recovery_reason = %d\n", recovery));

	/*
	 * If this is a S3 resume, resume the TPM.
	 *
	 * FIXME: I think U-Boot won't ever ask us to do this. Can we remove
	 * it?
	 */
	if (is_s3_resume) {
		if (TPM_SUCCESS != RollbackS3Resume()) {
			/*
			 * If we can't resume, just do a full reboot.  No need
			 * to go to recovery mode here, since if the TPM is
			 * really broken we'll catch it on the next boot.
			 */
			retval = VBERROR_TPM_S3_RESUME;
		}
	} else {
		/* Should we pay attention to the TPM's virtual dev-switch? */
		if (iparams->flags & VB_INIT_FLAG_VIRTUAL_DEV_SWITCH) {
			shared->flags |= VBSD_HONOR_VIRT_DEV_SWITCH;
			has_virt_dev_switch = 1;
		}

		/*
		 * We always believe the HW dev-switch, since there's one
		 * attached to servo which may be active even on systems
		 * without a physical switch. The EC may also implement a fake
		 * dev-switch for testing.
		 */
		if (iparams->flags & VB_INIT_FLAG_DEV_SWITCH_ON)
			is_hw_dev = 1;

		/* We may be asked to clear the virtual dev-switch at boot. */
		VbNvGet(&vnc, VBNV_DISABLE_DEV_REQUEST, &disable_dev_request);

		/* Allow GBB flag to override dev switch */
		if (gbb.flags & GBB_FLAG_FORCE_DEV_SWITCH_ON)
			is_hw_dev = 1;

		/* Have we been explicitly asked to clear the TPM owner? */
		VbNvGet(&vnc, VBNV_CLEAR_TPM_OWNER_REQUEST,
			&clear_tpm_owner_request);

		/*
		 * Initialize the TPM. If the developer mode state has changed
		 * since the last boot, we need to clear TPM ownership. If the
		 * TPM space is initialized by this call, the virtual
		 * dev-switch will be disabled by default)
		 */
		VBDEBUG(("TPM: Call RollbackFirmwareSetup(r%d, d%d)\n",
			 recovery, is_hw_dev));
		tpm_status = RollbackFirmwareSetup(is_hw_dev,
						   disable_dev_request,
						   clear_tpm_owner_request,
						   /* two outputs on success */
						   &is_virt_dev, &tpm_version);

		if (0 != tpm_status) {
			VBDEBUG(("Unable to setup TPM and read "
				 "firmware version (0x%x)\n", tpm_status));

			if (TPM_E_MUST_REBOOT == tpm_status) {
				/*
				 * TPM wants to reboot into the same mode we're
				 * in now
				 */
				VBDEBUG(("TPM requires a reboot.\n"));
				if (!recovery) {
					/*
					 * Not recovery mode.  Just reboot (not
					 * into recovery).
					 */
					retval = VBERROR_TPM_REBOOT_REQUIRED;
					goto VbInit_exit;
				} else if (VBNV_RECOVERY_RO_TPM_REBOOT !=
					   shared->recovery_reason) {
					/*
					 * In recovery mode now, and we haven't
					 * requested a TPM reboot yet, so
					 * request one.
					 */
					VbNvSet(&vnc, VBNV_RECOVERY_REQUEST,
						VBNV_RECOVERY_RO_TPM_REBOOT);
					retval = VBERROR_TPM_REBOOT_REQUIRED;
					goto VbInit_exit;
				}
			}

			if (!recovery) {
				VbNvSet(&vnc, VBNV_RECOVERY_REQUEST,
					VBNV_RECOVERY_RO_TPM_S_ERROR);
				VbNvSet(&vnc, VBNV_RECOVERY_SUBCODE,
					tpm_status);
				retval = VBERROR_TPM_FIRMWARE_SETUP;
				goto VbInit_exit;
			}
		}

		/* TPM setup succeeded, or we're in recovery mode and ignoring
		 * errors. What did we learn? */
		shared->fw_version_tpm_start = tpm_version;
		shared->fw_version_tpm = tpm_version;
		if (is_hw_dev || (has_virt_dev_switch && is_virt_dev)) {
			is_dev = 1;
			shared->flags |= VBSD_BOOT_DEV_SWITCH_ON;
		}
		if (disable_dev_request && !is_virt_dev)
			VbNvSet(&vnc, VBNV_DISABLE_DEV_REQUEST, 0);
		if (clear_tpm_owner_request) {
			VbNvSet(&vnc, VBNV_CLEAR_TPM_OWNER_REQUEST, 0);
			VbNvSet(&vnc, VBNV_CLEAR_TPM_OWNER_DONE, 1);
		}
	}

	/*
	 * If the nvram state was lost, try to restore the bits we care about
	 * from the backup in the TPM. It's okay if we can't, though.
	 * Note: None of the bits that we back up should have been referenced
	 * before this point. Otherwise, they'll just be overwritten here.
	 * All the other bits will be unchanged from whatever has happened to
	 * them since VbNvSetup() reinitialized the VbNvContext.
	 */
	if (lost_nvram)
		RestoreNvFromBackup(&vnc);

	/* Allow BIOS to load arbitrary option ROMs? */
	if (gbb.flags & GBB_FLAG_LOAD_OPTION_ROMS)
		iparams->out_flags |= VB_INIT_OUT_ENABLE_OPROM;

	/* Factory may need to boot custom OSes when the dev-switch is on */
	if (is_dev && (gbb.flags & GBB_FLAG_ENABLE_ALTERNATE_OS))
		iparams->out_flags |= VB_INIT_OUT_ENABLE_ALTERNATE_OS;

	/* Set output flags */
	if (VBNV_RECOVERY_NOT_REQUESTED != recovery) {
		/* Requesting recovery mode */
		iparams->out_flags |= (VB_INIT_OUT_ENABLE_RECOVERY |
				       VB_INIT_OUT_CLEAR_RAM |
				       VB_INIT_OUT_ENABLE_DISPLAY |
				       VB_INIT_OUT_ENABLE_USB_STORAGE);
	} else if (is_dev) {
		/* Developer switch is on, so need to support dev mode */
		iparams->out_flags |= (VB_INIT_OUT_ENABLE_DEVELOPER |
				       VB_INIT_OUT_CLEAR_RAM |
				       VB_INIT_OUT_ENABLE_DISPLAY |
				       VB_INIT_OUT_ENABLE_USB_STORAGE);
		/* ... which may or may not include custom OSes */
		VbNvGet(&vnc, VBNV_DEV_BOOT_SIGNED_ONLY, &require_official_os);
		if (!require_official_os)
			iparams->out_flags |= VB_INIT_OUT_ENABLE_ALTERNATE_OS;

		/*
		 * Dev-mode needs the VGA option ROM to be loaded so it can
		 * display the scary boot screen. If we don't have it, we need
		 * to request it and reboot so it can be loaded.
		 */
		if ((iparams->flags & VB_INIT_FLAG_OPROM_MATTERS) &&
		    !(iparams->flags & VB_INIT_FLAG_OPROM_LOADED)) {
			VbNvSet(&vnc, VBNV_OPROM_NEEDED, 1);
			/*
			 * If VbInit() is run before Option ROMs are run it
			 * can still respond to the VbNv flag and does not
			 * need to reboot here.
			 */
			if (!(iparams->flags & VB_INIT_FLAG_BEFORE_OPROM_LOAD))
				retval = VBERROR_VGA_OPROM_MISMATCH;
			VBDEBUG(("VbInit() needs oprom, doesn't have it\n"));
		}

	} else {
		/*
		 * Normal mode, so disable dev_boot_* flags.  This ensures they
		 * will be initially disabled if the user later transitions
		 * back into developer mode.
		 */
		VbNvSet(&vnc, VBNV_DEV_BOOT_USB, 0);
		VbNvSet(&vnc, VBNV_DEV_BOOT_LEGACY, 0);
		VbNvSet(&vnc, VBNV_DEV_BOOT_SIGNED_ONLY, 0);
		/*
		 * Back up any changes now, so these values can't be forgotten
		 * by draining the battery. We really only care about these
		 * three fields, but it's uncommon for any others to change so
		 * this is an easier test than checking each one.
		 */
		if (vnc.regenerate_crc)
			backup_for_safety = 1;

		/*
		 * If we don't need the VGA option ROM but got it anyway, stop
		 * asking for it and reboot in case there's some vulnerability
		 * in using it.
		 */
		if ((iparams->flags & VB_INIT_FLAG_OPROM_MATTERS) &&
		    (iparams->flags & VB_INIT_FLAG_OPROM_LOADED)) {
			VbNvSet(&vnc, VBNV_OPROM_NEEDED, 0);
			/*
			 * If VbInit() is run before Option ROMs are run it
			 * can still respond to the VbNv flag and does not
			 * need to reboot here.
			 */
			if (!(iparams->flags & VB_INIT_FLAG_BEFORE_OPROM_LOAD))
				retval = VBERROR_VGA_OPROM_MISMATCH;
			VBDEBUG(("VbInit() has oprom, doesn't need it\n"));
		}
	}

VbInit_exit:
	/*
	 * If we successfully backup the NV storage, it will clear the
	 * VBNV_BACKUP_NVRAM_REQUEST field, so we want to do it before
	 * calling VbNvTeardown(). It's okay if we can't backup, though.
	 */
	VbNvGet(&vnc, VBNV_BACKUP_NVRAM_REQUEST, &backup_requested);
	if (backup_requested || backup_for_safety)
		SaveNvToBackup(&vnc);

	/* Tear down NV storage */
	VbNvTeardown(&vnc);
	if (vnc.raw_changed)
		VbExNvStorageWrite(vnc.raw);

	VBDEBUG(("VbInit() output flags 0x%x\n", iparams->out_flags));

	shared->timer_vb_init_exit = VbExGetTimer();

	VBDEBUG(("VbInit() returning 0x%x\n", retval));

	return retval;
}
/* Main routine */
int main(int argc, char* argv[]) {

  const char* image_name;
  uint64_t key_size;
  uint8_t* key_blob = NULL;
  VbSharedDataHeader* shared;
  GoogleBinaryBlockHeader* gbb;
  VbError_t rv;
  int c, argsleft;
  int errorcnt = 0;
  char *e = 0;

  Memset(&lkp, 0, sizeof(LoadKernelParams));
  lkp.bytes_per_lba = LBA_BYTES;
  lkp.boot_flags = BOOT_FLAG_RECOVERY;
  Memset(&vnc, 0, sizeof(VbNvContext));
  VbNvSetup(&vnc);
  lkp.nv_context = &vnc;
  Memset(&cparams, 0, sizeof(VbCommonParams));

  /* Parse options */
  opterr = 0;
  while ((c=getopt(argc, argv, ":b:")) != -1)
  {
    switch (c)
    {
    case 'b':
      lkp.boot_flags = strtoull(optarg, &e, 0);
      if (!*optarg || (e && *e))
      {
        fprintf(stderr, "Invalid argument to -%c: \"%s\"\n", c, optarg);
        errorcnt++;
      }
      break;
    case '?':
      fprintf(stderr, "Unrecognized switch: -%c\n", optopt);
      errorcnt++;
      break;
    case ':':
      fprintf(stderr, "Missing argument to -%c\n", optopt);
      errorcnt++;
      break;
    default:
      errorcnt++;
      break;
    }
  }

  /* Update argc */
  argsleft = argc - optind;

  if (errorcnt || !argsleft)
  {
    fprintf(stderr, "usage: %s [options] <drive_image> [<sign_key>]\n",
            argv[0]);
    fprintf(stderr, "\noptions:\n");
    /* These cases are because uint64_t isn't necessarily the same as ULL. */
    fprintf(stderr, "  -b NUM     boot flag bits (default %" PRIu64 "):\n",
            (uint64_t)BOOT_FLAG_RECOVERY);
    fprintf(stderr, "               %" PRIu64 " = developer mode on\n",
            (uint64_t)BOOT_FLAG_DEVELOPER);
    fprintf(stderr, "               %" PRIu64 " = recovery mode on\n",
            (uint64_t)BOOT_FLAG_RECOVERY);
    return 1;
  }

  image_name = argv[optind];

  /* Read header signing key blob */
  if (argsleft > 1) {
    key_blob = ReadFile(argv[optind+1], &key_size);
    if (!key_blob) {
      fprintf(stderr, "Unable to read key file %s\n", argv[optind+1]);
      return 1;
    }
    printf("Read %" PRIu64 " bytes of key from %s\n", key_size, argv[optind+1]);
  }

  /* Initialize the GBB */
  lkp.gbb_size = sizeof(GoogleBinaryBlockHeader) + key_size;
  lkp.gbb_data = (void*)malloc(lkp.gbb_size);
  gbb = (GoogleBinaryBlockHeader*)lkp.gbb_data;
  cparams.gbb = gbb;
  Memset(gbb, 0, lkp.gbb_size);
  Memcpy(gbb->signature, GBB_SIGNATURE, GBB_SIGNATURE_SIZE);
  gbb->major_version = GBB_MAJOR_VER;
  gbb->minor_version = GBB_MINOR_VER;
  gbb->header_size = sizeof(GoogleBinaryBlockHeader);
  /* Fill in the given key, if any, for both root and recovery */
  if (key_blob) {
    gbb->rootkey_offset = gbb->header_size;
    gbb->rootkey_size = key_size;
    Memcpy((uint8_t*)gbb + gbb->rootkey_offset, key_blob, key_size);

    gbb->recovery_key_offset = gbb->rootkey_offset;
    gbb->recovery_key_size = key_size;
  }

  /* Initialize the shared data area */
  lkp.shared_data_blob = malloc(VB_SHARED_DATA_REC_SIZE);
  lkp.shared_data_size = VB_SHARED_DATA_REC_SIZE;
  shared = (VbSharedDataHeader*)lkp.shared_data_blob;
  if (0 != VbSharedDataInit(shared, lkp.shared_data_size)) {
    fprintf(stderr, "Unable to init shared data\n");
    return 1;
  }
  /* Copy in the key blob, if any */
  if (key_blob) {
    if (0 != VbSharedDataSetKernelKey(shared, (VbPublicKey*)key_blob)) {
      fprintf(stderr, "Unable to set key in shared data\n");
      return 1;
    }
  }

  /* Free the key blob, now that we're done with it */
  free(key_blob);

  printf("bootflags = %" PRIu64 "\n", lkp.boot_flags);

  /* Get image size */
  printf("Reading from image: %s\n", image_name);
  image_file = fopen(image_name, "rb");
  if (!image_file) {
    fprintf(stderr, "Unable to open image file %s\n", image_name);
    return 1;
  }
  fseek(image_file, 0, SEEK_END);
  lkp.streaming_lba_count = (ftell(image_file) / LBA_BYTES);
  lkp.gpt_lba_count = lkp.streaming_lba_count;
  rewind(image_file);
  printf("Streaming LBA count: %" PRIu64 "\n", lkp.streaming_lba_count);

  /* Allocate a buffer for the kernel */
  lkp.kernel_buffer = malloc(KERNEL_BUFFER_SIZE);
  if(!lkp.kernel_buffer) {
    fprintf(stderr, "Unable to allocate kernel buffer.\n");
    return 1;
  }
  lkp.kernel_buffer_size = KERNEL_BUFFER_SIZE;

  /* Call LoadKernel() */
  rv = LoadKernel(&lkp, &cparams);
  printf("LoadKernel() returned %d\n", rv);

  if (VBERROR_SUCCESS == rv) {
    printf("Partition number:   %" PRIu64 "\n", lkp.partition_number);
    printf("Bootloader address: %" PRIu64 "\n", lkp.bootloader_address);
    printf("Bootloader size:    %" PRIu64 "\n", lkp.bootloader_size);
    printf("Partition guid:     "
           "%02x%02x%02x%02x-%02x%02x-%02x%02x"
           "-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
           lkp.partition_guid[3],
           lkp.partition_guid[2],
           lkp.partition_guid[1],
           lkp.partition_guid[0],
           lkp.partition_guid[5],
           lkp.partition_guid[4],
           lkp.partition_guid[7],
           lkp.partition_guid[6],
           lkp.partition_guid[8],
           lkp.partition_guid[9],
           lkp.partition_guid[10],
           lkp.partition_guid[11],
           lkp.partition_guid[12],
           lkp.partition_guid[13],
           lkp.partition_guid[14],
           lkp.partition_guid[15]);
  }

  fclose(image_file);
  free(lkp.kernel_buffer);
  return rv != VBERROR_SUCCESS;
}
/* Reset mock data (for use before each test) */
static void ResetMocks(void) {
	GoogleBinaryBlockHeader *gbb;
	BmpBlockHeader *bhdr;
	ImageInfo *image_info;
	ScreenLayout *layout;
	int gbb_used;

	Memset(&vnc, 0, sizeof(vnc));
	VbNvSetup(&vnc);
	VbNvTeardown(&vnc);                   /* So CRC gets generated */

	Memset(&cparams, 0, sizeof(cparams));
	cparams.shared_data_size = sizeof(shared_data);
	cparams.shared_data_blob = shared_data;

	Memset(&fparams, 0, sizeof(fparams));

	Memset(gbb_data, 0, sizeof(gbb_data));
	gbb = (GoogleBinaryBlockHeader *)gbb_data;
	gbb->major_version = GBB_MAJOR_VER;
	gbb->minor_version = GBB_MINOR_VER;
	gbb->flags = 0;
	gbb_used = sizeof(GoogleBinaryBlockHeader);

	gbb->hwid_offset = gbb_used;
	strcpy(gbb_data + gbb->hwid_offset, "Test HWID");
	gbb->hwid_size = strlen(gbb_data + gbb->hwid_offset) + 1;
	gbb_used = (gbb_used + gbb->hwid_size + 7) & ~7;

	gbb->bmpfv_offset = gbb_used;
	bhdr = (BmpBlockHeader *)(gbb_data + gbb->bmpfv_offset);
	gbb->bmpfv_size = sizeof(BmpBlockHeader);
	gbb_used = (gbb_used + gbb->bmpfv_size + 7) & ~7;
	memcpy(bhdr->signature, BMPBLOCK_SIGNATURE, BMPBLOCK_SIGNATURE_SIZE);
	bhdr->major_version = BMPBLOCK_MAJOR_VERSION;
	bhdr->minor_version = BMPBLOCK_MINOR_VERSION;
	bhdr->number_of_localizations = 3;
	bhdr->number_of_screenlayouts = 1;

	layout = (ScreenLayout *)(gbb_data + gbb_used);
	gbb_used += sizeof(*layout);
	layout->images[0].x = 1;
	layout->images[0].image_info_offset = gbb_used - gbb->bmpfv_offset;

	/* First image is uncompressed */
	image_info = (ImageInfo *)(gbb_data + gbb_used);
	image_info->format = FORMAT_BMP;
	image_info->compressed_size = ORIGINAL_SIZE;
	image_info->original_size = ORIGINAL_SIZE;
	image_info->compression = COMPRESS_NONE;
	gbb_used += sizeof(*image_info);
	strcpy(gbb_data + gbb_used, "original");
	gbb_used += ORIGINAL_SIZE;

	/* Second image is compressed */
	layout->images[1].image_info_offset = gbb_used - gbb->bmpfv_offset;
	layout->images[1].x = 2;
	image_info = (ImageInfo *)(gbb_data + gbb_used);
	image_info->format = FORMAT_BMP;
	image_info->compressed_size = COMPRESSED_SIZE;
	image_info->original_size = ORIGINAL_SIZE;
	image_info->compression = COMPRESS_LZMA1;
	gbb_used += sizeof(*image_info) + COMPRESSED_SIZE;

	Memset(&shared_data, 0, sizeof(shared_data));
	VbSharedDataInit(shared, sizeof(shared_data));
	shared->fw_keyblock_flags = 0xABCDE0;

	mock_tpm_version = mock_lf_tpm_version = 0x20004;
	shared->fw_version_tpm_start = mock_tpm_version;
	mock_lf_retval = 0;
	mock_seen_region = 0;
}
static void VbNvStorageTest(void) {

  VbNvField* vnf;
  VbNvContext c;
  uint8_t goodcrc;
  uint32_t data;

  memset(&c, 0xA6, sizeof(c));

  /* Open with invalid data should set defaults */
  TEST_EQ(VbNvSetup(&c), 0, "VbNvSetup()");
  TEST_EQ(c.raw[0], 0x70, "VbNvSetup() reset header byte");
  /* Close then regenerates the CRC */
  TEST_EQ(VbNvTeardown(&c), 0, "VbNvTeardown()");
  TEST_NEQ(c.raw[15], 0, "VbNvTeardown() CRC");
  TEST_EQ(c.raw_changed, 1, "VbNvTeardown() changed");
  goodcrc = c.raw[15];
  /* Another open-close pair should not cause further changes */
  VbNvSetup(&c);
  VbNvTeardown(&c);
  TEST_EQ(c.raw_changed, 0, "VbNvTeardown() didn't change");
  TEST_EQ(c.raw[15], goodcrc, "VbNvTeardown() CRC same");

  /* Perturbing the header should force defaults */
  c.raw[0] ^= 0x40;
  VbNvSetup(&c);
  TEST_EQ(c.raw[0], 0x70, "VbNvSetup() reset header byte again");
  /* Close then regenerates the CRC */
  VbNvTeardown(&c);
  TEST_EQ(c.raw_changed, 1, "VbNvTeardown() changed again");
  TEST_EQ(c.raw[15], goodcrc, "VbNvTeardown() CRC same again");

  /* So should perturbing some other byte */
  TEST_EQ(c.raw[11], 0, "Kernel byte starts at 0");
  c.raw[11] = 12;
  VbNvSetup(&c);
  TEST_EQ(c.raw[11], 0, "VbNvSetup() reset kernel byte");
  /* Close then regenerates the CRC */
  VbNvTeardown(&c);
  TEST_EQ(c.raw_changed, 1, "VbNvTeardown() changed again");
  TEST_EQ(c.raw[15], goodcrc, "VbNvTeardown() CRC same again");

  /* Clear the kernel and firmware flags */
  VbNvSetup(&c);
  TEST_EQ(VbNvGet(&c, VBNV_FIRMWARE_SETTINGS_RESET, &data), 0,
          "Get firmware settings reset");
  TEST_EQ(data, 1, "Firmware settings are reset");
  TEST_EQ(VbNvSet(&c, VBNV_FIRMWARE_SETTINGS_RESET, 0), 0,
          "Clear firmware settings reset");
  VbNvGet(&c, VBNV_FIRMWARE_SETTINGS_RESET, &data);
  TEST_EQ(data, 0, "Firmware settings are clear");

  TEST_EQ(VbNvGet(&c, VBNV_KERNEL_SETTINGS_RESET, &data), 0,
          "Get kernel settings reset");
  TEST_EQ(data, 1, "Kernel settings are reset");
  TEST_EQ(VbNvSet(&c, VBNV_KERNEL_SETTINGS_RESET, 0), 0,
          "Clear kernel settings reset");
  VbNvGet(&c, VBNV_KERNEL_SETTINGS_RESET, &data);
  TEST_EQ(data, 0, "Kernel settings are clear");
  TEST_EQ(c.raw[0], 0x40, "Header byte now just has the header bit");
  VbNvTeardown(&c);
  /* That should have changed the CRC */
  TEST_NEQ(c.raw[15], goodcrc, "VbNvTeardown() CRC changed due to flags clear");

  /* Test explicitly setting the reset flags again */
  VbNvSetup(&c);
  VbNvSet(&c, VBNV_FIRMWARE_SETTINGS_RESET, 1);
  VbNvGet(&c, VBNV_FIRMWARE_SETTINGS_RESET, &data);
  TEST_EQ(data, 1, "Firmware settings forced reset");
  VbNvSet(&c, VBNV_FIRMWARE_SETTINGS_RESET, 0);

  VbNvSet(&c, VBNV_KERNEL_SETTINGS_RESET, 1);
  VbNvGet(&c, VBNV_KERNEL_SETTINGS_RESET, &data);
  TEST_EQ(data, 1, "Kernel settings forced reset");
  VbNvSet(&c, VBNV_KERNEL_SETTINGS_RESET, 0);
  VbNvTeardown(&c);

  /* Get/set an invalid field */
  VbNvSetup(&c);
  TEST_EQ(VbNvGet(&c, -1, &data), 1, "Get invalid setting");
  TEST_EQ(VbNvSet(&c, -1, 0), 1, "Set invalid setting");
  VbNvTeardown(&c);

  /* Test other fields */
  VbNvSetup(&c);
  for (vnf = nvfields; vnf->desc; vnf++) {
    TEST_EQ(VbNvGet(&c, vnf->param, &data), 0, vnf->desc);
    TEST_EQ(data, vnf->default_value, vnf->desc);

    TEST_EQ(VbNvSet(&c, vnf->param, vnf->test_value), 0, vnf->desc);
    TEST_EQ(VbNvGet(&c, vnf->param, &data), 0, vnf->desc);
    TEST_EQ(data, vnf->test_value, vnf->desc);

    TEST_EQ(VbNvSet(&c, vnf->param, vnf->test_value2), 0, vnf->desc);
    TEST_EQ(VbNvGet(&c, vnf->param, &data), 0, vnf->desc);
    TEST_EQ(data, vnf->test_value2, vnf->desc);
  }
  VbNvTeardown(&c);

  /* None of those changes should have caused a reset to defaults */
  VbNvSetup(&c);
  VbNvGet(&c, VBNV_FIRMWARE_SETTINGS_RESET, &data);
  TEST_EQ(data, 0, "Firmware settings are still clear");
  VbNvGet(&c, VBNV_KERNEL_SETTINGS_RESET, &data);
  TEST_EQ(data, 0, "Kernel settings are still clear");
  VbNvTeardown(&c);

  /* Verify writing identical settings doesn't cause the CRC to regenerate */
  VbNvSetup(&c);
  TEST_EQ(c.regenerate_crc, 0, "No regen CRC on open");
  for (vnf = nvfields; vnf->desc; vnf++)
    TEST_EQ(VbNvSet(&c, vnf->param, vnf->test_value2), 0, vnf->desc);
  TEST_EQ(c.regenerate_crc, 0, "No regen CRC if data not changed");
  VbNvTeardown(&c);
  TEST_EQ(c.raw_changed, 0, "No raw change if data not changed");

  /* Test out-of-range fields mapping to defaults */
  VbNvSetup(&c);
  VbNvSet(&c, VBNV_TRY_B_COUNT, 16);
  VbNvGet(&c, VBNV_TRY_B_COUNT, &data);
  TEST_EQ(data, 15, "Try b count out of range");
  VbNvSet(&c, VBNV_RECOVERY_REQUEST, 0x101);
  VbNvGet(&c, VBNV_RECOVERY_REQUEST, &data);
  TEST_EQ(data, VBNV_RECOVERY_LEGACY, "Recovery request out of range");
  VbNvSet(&c, VBNV_LOCALIZATION_INDEX, 0x102);
  VbNvGet(&c, VBNV_LOCALIZATION_INDEX, &data);
  TEST_EQ(data, 0, "Localization index out of range");
  VbNvTeardown(&c);
}
VbError_t VbSelectFirmware(VbCommonParams *cparams,
                           VbSelectFirmwareParams *fparams)
{
	VbSharedDataHeader *shared =
		(VbSharedDataHeader *)cparams->shared_data_blob;
	VbNvContext vnc;
	VbError_t retval = VBERROR_UNKNOWN; /* Default to error */
	int is_rec = (shared->recovery_reason ? 1 : 0);
	int is_dev = (shared->flags & VBSD_BOOT_DEV_SWITCH_ON ? 1 : 0);
	uint32_t tpm_status = 0;

	/* Start timer */
	shared->timer_vb_select_firmware_enter = VbExGetTimer();

	/* Load NV storage */
	VbExNvStorageRead(vnc.raw);
	VbNvSetup(&vnc);

	if (is_rec) {
		/*
		 * Recovery is requested; go straight to recovery without
		 * checking the RW firmware.
		 */
		VBDEBUG(("VbSelectFirmware() detected recovery request\n"));

		/* Go directly to recovery mode */
		fparams->selected_firmware = VB_SELECT_FIRMWARE_RECOVERY;
	} else {
		/* Chain to LoadFirmware() */
		retval = LoadFirmware(cparams, fparams, &vnc);

		/* Exit if we failed to find an acceptable firmware */
		if (VBERROR_SUCCESS != retval)
			goto VbSelectFirmware_exit;

		/* Translate the selected firmware path */
		if (shared->flags & VBSD_LF_USE_RO_NORMAL) {
			/* Request the read-only normal/dev code path */
			fparams->selected_firmware =
				VB_SELECT_FIRMWARE_READONLY;
		} else if (0 == shared->firmware_index)
			fparams->selected_firmware = VB_SELECT_FIRMWARE_A;
		else {
			fparams->selected_firmware = VB_SELECT_FIRMWARE_B;
		}

		/* Update TPM if necessary */
		if (shared->fw_version_tpm_start < shared->fw_version_tpm) {
			tpm_status =
				RollbackFirmwareWrite(shared->fw_version_tpm);
			if (0 != tpm_status) {
				VBDEBUG(("Can't write FW version to TPM.\n"));
				VbNvSet(&vnc, VBNV_RECOVERY_REQUEST,
					VBNV_RECOVERY_RO_TPM_W_ERROR);
				retval = VBERROR_TPM_WRITE_FIRMWARE;
				goto VbSelectFirmware_exit;
			}
		}

		/* Lock firmware versions in TPM */
		tpm_status = RollbackFirmwareLock();
		if (0 != tpm_status) {
			VBDEBUG(("Unable to lock firmware version in TPM.\n"));
			VbNvSet(&vnc, VBNV_RECOVERY_REQUEST,
				VBNV_RECOVERY_RO_TPM_L_ERROR);
			retval = VBERROR_TPM_LOCK_FIRMWARE;
			goto VbSelectFirmware_exit;
		}
	}

	/*
	 * At this point, we have a good idea of how we are going to
	 * boot. Update the TPM with this state information.
	 */
	tpm_status = SetTPMBootModeState(is_dev, is_rec,
					 shared->fw_keyblock_flags);
	if (0 != tpm_status) {
		VBDEBUG(("Can't update the TPM with boot mode information.\n"));
		if (!is_rec) {
			VbNvSet(&vnc, VBNV_RECOVERY_REQUEST,
				VBNV_RECOVERY_RO_TPM_U_ERROR);
			retval = VBERROR_TPM_SET_BOOT_MODE_STATE;
			goto VbSelectFirmware_exit;
		}
	}

	/* Success! */
	retval = VBERROR_SUCCESS;

 VbSelectFirmware_exit:

	/* Save NV storage */
	VbNvTeardown(&vnc);
	if (vnc.raw_changed)
		VbExNvStorageWrite(vnc.raw);

	/* Stop timer */
	shared->timer_vb_select_firmware_exit = VbExGetTimer();

	/* Should always have a known error code */
	VbAssert(VBERROR_UNKNOWN != retval);

	return retval;
}
VbError_t VbSelectAndLoadKernel(VbCommonParams *cparams,
                                VbSelectAndLoadKernelParams *kparams)
{
    VbSharedDataHeader *shared =
        (VbSharedDataHeader *)cparams->shared_data_blob;
    VbError_t retval = VBERROR_SUCCESS;
    LoadKernelParams p;
    uint32_t tpm_status = 0;

    /* Start timer */
    shared->timer_vb_select_and_load_kernel_enter = VbExGetTimer();

    VbExNvStorageRead(vnc.raw);
    VbNvSetup(&vnc);

    /* Clear output params in case we fail */
    kparams->disk_handle = NULL;
    kparams->partition_number = 0;
    kparams->bootloader_address = 0;
    kparams->bootloader_size = 0;
    kparams->flags = 0;
    Memset(kparams->partition_guid, 0, sizeof(kparams->partition_guid));

    cparams->bmp = NULL;
    cparams->gbb = VbExMalloc(sizeof(*cparams->gbb));
    retval = VbGbbReadHeader_static(cparams, cparams->gbb);
    if (VBERROR_SUCCESS != retval)
        goto VbSelectAndLoadKernel_exit;

    /* Do EC software sync if necessary */
    if ((shared->flags & VBSD_EC_SOFTWARE_SYNC) &&
            !(cparams->gbb->flags & GBB_FLAG_DISABLE_EC_SOFTWARE_SYNC)) {
        int oprom_mismatch = 0;

        retval = VbEcSoftwareSync(0, cparams);
        /* Save reboot requested until after possible PD sync */
        if (retval == VBERROR_VGA_OPROM_MISMATCH)
            oprom_mismatch = 1;
        else if (retval != VBERROR_SUCCESS)
            goto VbSelectAndLoadKernel_exit;

#ifdef PD_SYNC
        if (!(cparams->gbb->flags &
                GBB_FLAG_DISABLE_PD_SOFTWARE_SYNC)) {
            retval = VbEcSoftwareSync(1, cparams);
            if (retval == VBERROR_VGA_OPROM_MISMATCH)
                oprom_mismatch = 1;
            else if (retval != VBERROR_SUCCESS)
                goto VbSelectAndLoadKernel_exit;
        }
#endif

        /* Request reboot to unload VGA Option ROM */
        if (oprom_mismatch) {
            retval = VBERROR_VGA_OPROM_MISMATCH;
            goto VbSelectAndLoadKernel_exit;
        }
    }

    /* Read kernel version from the TPM.  Ignore errors in recovery mode. */
    tpm_status = RollbackKernelRead(&shared->kernel_version_tpm);
    if (0 != tpm_status) {
        VBDEBUG(("Unable to get kernel versions from TPM\n"));
        if (!shared->recovery_reason) {
            VbSetRecoveryRequest(VBNV_RECOVERY_RW_TPM_R_ERROR);
            retval = VBERROR_TPM_READ_KERNEL;
            goto VbSelectAndLoadKernel_exit;
        }
    }
    shared->kernel_version_tpm_start = shared->kernel_version_tpm;

    /* Fill in params for calls to LoadKernel() */
    Memset(&p, 0, sizeof(p));
    p.shared_data_blob = cparams->shared_data_blob;
    p.shared_data_size = cparams->shared_data_size;
    p.gbb_data = cparams->gbb_data;
    p.gbb_size = cparams->gbb_size;

    /*
     * This could be set to NULL, in which case the vboot header
     * information about the load address and size will be used.
     */
    p.kernel_buffer = kparams->kernel_buffer;
    p.kernel_buffer_size = kparams->kernel_buffer_size;

    p.nv_context = &vnc;
    p.boot_flags = 0;
    if (shared->flags & VBSD_BOOT_DEV_SWITCH_ON)
        p.boot_flags |= BOOT_FLAG_DEVELOPER;

    /* Handle separate normal and developer firmware builds. */
#if defined(VBOOT_FIRMWARE_TYPE_NORMAL)
    /* Normal-type firmware always acts like the dev switch is off. */
    p.boot_flags &= ~BOOT_FLAG_DEVELOPER;
#elif defined(VBOOT_FIRMWARE_TYPE_DEVELOPER)
    /* Developer-type firmware fails if the dev switch is off. */
    if (!(p.boot_flags & BOOT_FLAG_DEVELOPER)) {
        /*
         * Dev firmware should be signed with a key that only verifies
         * when the dev switch is on, so we should never get here.
         */
        VBDEBUG(("Developer firmware called with dev switch off!\n"));
        VbSetRecoveryRequest(VBNV_RECOVERY_RW_DEV_MISMATCH);
        retval = VBERROR_DEV_FIRMWARE_SWITCH_MISMATCH;
        goto VbSelectAndLoadKernel_exit;
    }
#else
    /*
     * Recovery firmware, or merged normal+developer firmware.  No need to
     * override flags.
     */
#endif

    /* Select boot path */
    if (shared->recovery_reason) {
        /* Recovery boot */
        p.boot_flags |= BOOT_FLAG_RECOVERY;
        retval = VbBootRecovery(cparams, &p);
        VbExEcEnteringMode(0, VB_EC_RECOVERY);
        VbDisplayScreen(cparams, VB_SCREEN_BLANK, 0, &vnc);

    } else if (p.boot_flags & BOOT_FLAG_DEVELOPER) {
        /* Developer boot */
        retval = VbBootDeveloper(cparams, &p);
        VbExEcEnteringMode(0, VB_EC_DEVELOPER);
        VbDisplayScreen(cparams, VB_SCREEN_BLANK, 0, &vnc);

    } else {
        /* Normal boot */
        VbExEcEnteringMode(0, VB_EC_NORMAL);
        retval = VbBootNormal(cparams, &p);

        if ((1 == shared->firmware_index) &&
                (shared->flags & VBSD_FWB_TRIED)) {
            /*
             * Special cases for when we're trying a new firmware
             * B.  These are needed because firmware updates also
             * usually change the kernel key, which means that the
             * B firmware can only boot a new kernel, and the old
             * firmware in A can only boot the previous kernel.
             *
             * Don't advance the TPM if we're trying a new firmware
             * B, because we don't yet know if the new kernel will
             * successfully boot.  We still want to be able to fall
             * back to the previous firmware+kernel if the new
             * firmware+kernel fails.
             *
             * If we found only invalid kernels, reboot and try
             * again.  This allows us to fall back to the previous
             * firmware+kernel instead of giving up and going to
             * recovery mode right away.  We'll still go to
             * recovery mode if we run out of tries and the old
             * firmware can't find a kernel it likes.
             */
            if (VBERROR_INVALID_KERNEL_FOUND == retval) {
                VBDEBUG(("Trying firmware B, "
                         "and only found invalid kernels.\n"));
                VbSetRecoveryRequest(VBNV_RECOVERY_NOT_REQUESTED);
                goto VbSelectAndLoadKernel_exit;
            }
        } else {
            /* Not trying a new firmware B. */

            /* See if we need to update the TPM. */
            VBDEBUG(("Checking if TPM kernel version needs "
                     "advancing\n"));
            if (shared->kernel_version_tpm >
                    shared->kernel_version_tpm_start) {
                tpm_status = RollbackKernelWrite(
                                 shared->kernel_version_tpm);
                if (0 != tpm_status) {
                    VBDEBUG(("Error writing kernel "
                             "versions to TPM.\n"));
                    VbSetRecoveryRequest(VBNV_RECOVERY_RW_TPM_W_ERROR);
                    retval = VBERROR_TPM_WRITE_KERNEL;
                    goto VbSelectAndLoadKernel_exit;
                }
            }
        }
    }

    if (VBERROR_SUCCESS != retval)
        goto VbSelectAndLoadKernel_exit;

    /* Save disk parameters */
    kparams->disk_handle = p.disk_handle;
    kparams->partition_number = (uint32_t)p.partition_number;
    kparams->bootloader_address = p.bootloader_address;
    kparams->bootloader_size = (uint32_t)p.bootloader_size;
    kparams->flags = p.flags;
    Memcpy(kparams->partition_guid, p.partition_guid,
           sizeof(kparams->partition_guid));

    /* Lock the kernel versions.  Ignore errors in recovery mode. */
    tpm_status = RollbackKernelLock(shared->recovery_reason);
    if (0 != tpm_status) {
        VBDEBUG(("Error locking kernel versions.\n"));
        if (!shared->recovery_reason) {
            VbSetRecoveryRequest(VBNV_RECOVERY_RW_TPM_L_ERROR);
            retval = VBERROR_TPM_LOCK_KERNEL;
            goto VbSelectAndLoadKernel_exit;
        }
    }

VbSelectAndLoadKernel_exit:

    VbApiKernelFree(cparams);

    VbNvTeardown(&vnc);
    if (vnc.raw_changed)
        VbExNvStorageWrite(vnc.raw);

    /* Stop timer */
    shared->timer_vb_select_and_load_kernel_exit = VbExGetTimer();

    kparams->kernel_buffer = p.kernel_buffer;
    kparams->kernel_buffer_size = p.kernel_buffer_size;

    VBDEBUG(("VbSelectAndLoadKernel() returning %d\n", (int)retval));

    /* Pass through return value from boot path */
    return retval;
}