VbError_t VbCheckDisplayKey(VbCommonParams *cparams, uint32_t key,
                            VbNvContext *vncptr)
{
	int i;

	/* Update key buffer */
	for(i = 1; i < MAGIC_WORD_LEN; i++)
		MagicBuffer[i - 1] = MagicBuffer[i];
	/* Save as lower-case ASCII */
	MagicBuffer[MAGIC_WORD_LEN - 1] = (key | 0x20) & 0xFF;

	if ('\t' == key) {
		/* Tab = display debug info */
		return VbDisplayDebugInfo(cparams, vncptr);
	} else if (VB_KEY_LEFT == key || VB_KEY_RIGHT == key ||
		   VB_KEY_DOWN == key || VB_KEY_UP == key) {
		/* Arrow keys = change localization */
		uint32_t loc = 0;
		uint32_t count = 0;

		VbNvGet(vncptr, VBNV_LOCALIZATION_INDEX, &loc);
		if (VBERROR_SUCCESS != VbGetLocalizationCount(cparams, &count))
			loc = 0;  /* No localization count (bad GBB?) */
		else if (VB_KEY_RIGHT == key || VB_KEY_UP == key)
			loc = (loc < count - 1 ? loc + 1 : 0);
		else
			loc = (loc > 0 ? loc - 1 : count - 1);
		VBDEBUG(("VbCheckDisplayKey() - change localization to %d\n",
			 (int)loc));
		VbNvSet(vncptr, VBNV_LOCALIZATION_INDEX, loc);

#ifdef SAVE_LOCALE_IMMEDIATELY
		VbNvTeardown(vncptr);  /* really only computes checksum */
		if (vncptr->raw_changed)
			VbExNvStorageWrite(vncptr->raw);
#endif

		/* Force redraw of current screen */
		return VbDisplayScreen(cparams, disp_current_screen, 1, vncptr);
	}

	if (0 == Memcmp(MagicBuffer, MAGIC_WORD, MAGIC_WORD_LEN)) {
		if (VBEASTEREGG)
			(void)VbDisplayScreen(cparams, disp_current_screen,
					      1, vncptr);
	}

  return VBERROR_SUCCESS;
}
VbError_t VbDisplayDebugInfo(VbCommonParams *cparams, VbNvContext *vncptr)
{
	VbSharedDataHeader *shared =
		(VbSharedDataHeader *)cparams->shared_data_blob;
	GoogleBinaryBlockHeader *gbb = cparams->gbb;
	char buf[DEBUG_INFO_SIZE] = "";
	char sha1sum[SHA1_DIGEST_SIZE * 2 + 1];
	char hwid[256];
	uint32_t used = 0;
	VbPublicKey *key;
	VbError_t ret;
	uint32_t i;

	/* Redisplay current screen to overwrite any previous debug output */
	VbDisplayScreen(cparams, disp_current_screen, 1, vncptr);

	/* Add hardware ID */
	VbRegionReadHWID(cparams, hwid, sizeof(hwid));
	used += StrnAppend(buf + used, "HWID: ", DEBUG_INFO_SIZE - used);
	used += StrnAppend(buf + used, hwid, DEBUG_INFO_SIZE - used);

	/* Add recovery reason */
	used += StrnAppend(buf + used,
			"\nrecovery_reason: 0x", DEBUG_INFO_SIZE - used);
	used += Uint64ToString(buf + used, DEBUG_INFO_SIZE - used,
			       shared->recovery_reason, 16, 2);
	used += StrnAppend(buf + used, "  ", DEBUG_INFO_SIZE - used);
	used += StrnAppend(buf + used,
			RecoveryReasonString(shared->recovery_reason),
			DEBUG_INFO_SIZE - used);

	/* Add VbSharedData flags */
	used += StrnAppend(buf + used, "\nVbSD.flags: 0x", DEBUG_INFO_SIZE - used);
	used += Uint64ToString(buf + used, DEBUG_INFO_SIZE - used,
			       shared->flags, 16, 8);

	/* Add raw contents of VbNvStorage */
	used += StrnAppend(buf + used, "\nVbNv.raw:", DEBUG_INFO_SIZE - used);
	for (i = 0; i < VBNV_BLOCK_SIZE; i++) {
		used += StrnAppend(buf + used, " ", DEBUG_INFO_SIZE - used);
		used += Uint64ToString(buf + used, DEBUG_INFO_SIZE - used,
				       vncptr->raw[i], 16, 2);
	}

	/* Add dev_boot_usb flag */
	VbNvGet(vncptr, VBNV_DEV_BOOT_USB, &i);
	used += StrnAppend(buf + used, "\ndev_boot_usb: ", DEBUG_INFO_SIZE - used);
	used += Uint64ToString(buf + used, DEBUG_INFO_SIZE - used, i, 10, 0);

	/* Add dev_boot_legacy flag */
	VbNvGet(vncptr, VBNV_DEV_BOOT_LEGACY, &i);
	used += StrnAppend(buf + used,
			"\ndev_boot_legacy: ", DEBUG_INFO_SIZE - used);
	used += Uint64ToString(buf + used, DEBUG_INFO_SIZE - used, i, 10, 0);

	/* Add dev_boot_signed_only flag */
	VbNvGet(vncptr, VBNV_DEV_BOOT_SIGNED_ONLY, &i);
	used += StrnAppend(buf + used, "\ndev_boot_signed_only: ",
			DEBUG_INFO_SIZE - used);
	used += Uint64ToString(buf + used, DEBUG_INFO_SIZE - used, i, 10, 0);

	/* Add TPM versions */
	used += StrnAppend(buf + used, "\nTPM: fwver=0x", DEBUG_INFO_SIZE - used);
	used += Uint64ToString(buf + used, DEBUG_INFO_SIZE - used,
			       shared->fw_version_tpm, 16, 8);
	used += StrnAppend(buf + used, " kernver=0x", DEBUG_INFO_SIZE - used);
	used += Uint64ToString(buf + used, DEBUG_INFO_SIZE - used,
			       shared->kernel_version_tpm, 16, 8);

	/* Add GBB flags */
	used += StrnAppend(buf + used, "\ngbb.flags: 0x", DEBUG_INFO_SIZE - used);
	if (gbb->major_version == GBB_MAJOR_VER && gbb->minor_version >= 1) {
		used += Uint64ToString(buf + used, DEBUG_INFO_SIZE - used,
				       gbb->flags, 16, 8);
	} else {
		used += StrnAppend(buf + used,
				"0 (default)", DEBUG_INFO_SIZE - used);
	}

	/* Add sha1sum for Root & Recovery keys */
	ret = VbGbbReadRootKey(cparams, &key);
	if (!ret) {
		FillInSha1Sum(sha1sum, key);
		VbExFree(key);
		used += StrnAppend(buf + used, "\ngbb.rootkey: ",
				   DEBUG_INFO_SIZE - used);
		used += StrnAppend(buf + used, sha1sum,
				   DEBUG_INFO_SIZE - used);
	}

	ret = VbGbbReadRecoveryKey(cparams, &key);
	if (!ret) {
		FillInSha1Sum(sha1sum, key);
		VbExFree(key);
		used += StrnAppend(buf + used, "\ngbb.recovery_key: ",
				   DEBUG_INFO_SIZE - used);
		used += StrnAppend(buf + used, sha1sum,
				   DEBUG_INFO_SIZE - used);
	}

	/* If we're in dev-mode, show the kernel subkey that we expect, too. */
	if (0 == shared->recovery_reason) {
		FillInSha1Sum(sha1sum, &shared->kernel_subkey);
		used += StrnAppend(buf + used,
				"\nkernel_subkey: ", DEBUG_INFO_SIZE - used);
		used += StrnAppend(buf + used, sha1sum, DEBUG_INFO_SIZE - used);
	}

	/* Make sure we finish with a newline */
	used += StrnAppend(buf + used, "\n", DEBUG_INFO_SIZE - used);

	/* TODO: add more interesting data:
	 * - Information on current disks */

	buf[DEBUG_INFO_SIZE - 1] = '\0';
	return VbExDisplayDebugInfo(buf);
}
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;
}
VbError_t VbEcSoftwareSync(int devidx, VbCommonParams *cparams)
{
    VbSharedDataHeader *shared =
        (VbSharedDataHeader *)cparams->shared_data_blob;
    int in_rw = 0;
    int rv;
    const uint8_t *ec_hash = NULL;
    int ec_hash_size;
    const uint8_t *rw_hash = NULL;
    int rw_hash_size;
    const uint8_t *expected = NULL;
    int expected_size;
    uint8_t expected_hash[SHA256_DIGEST_SIZE];
    int need_update = 0;
    int i;

    VBDEBUG(("VbEcSoftwareSync(devidx=%d)\n", devidx));

    /* Determine whether the EC is in RO or RW */
    rv = VbExEcRunningRW(devidx, &in_rw);

    if (shared->recovery_reason) {
        /* Recovery mode; just verify the EC is in RO code */
        if (rv == VBERROR_SUCCESS && in_rw == 1) {
            /*
             * EC is definitely in RW firmware.  We want it in
             * read-only code, so preserve the current recovery
             * reason and reboot.
             *
             * We don't reboot on error or unknown EC code, because
             * we could end up in an endless reboot loop.  If we
             * had some way to track that we'd already rebooted for
             * this reason, we could retry only once.
             */
            VBDEBUG(("VbEcSoftwareSync() - "
                     "want recovery but got EC-RW\n"));
            VbSetRecoveryRequest(shared->recovery_reason);
            return VBERROR_EC_REBOOT_TO_RO_REQUIRED;
        }

        VBDEBUG(("VbEcSoftwareSync() in recovery; EC-RO\n"));
        return VBERROR_SUCCESS;
    }

    /*
     * Not in recovery.  If we couldn't determine where the EC was,
     * reboot to recovery.
     */
    if (rv != VBERROR_SUCCESS) {
        VBDEBUG(("VbEcSoftwareSync() - "
                 "VbExEcRunningRW() returned %d\n", rv));
        VbSetRecoveryRequest(VBNV_RECOVERY_EC_UNKNOWN_IMAGE);
        return VBERROR_EC_REBOOT_TO_RO_REQUIRED;
    }

    /* If AP is read-only normal, EC should be in its RO code also. */
    if (shared->flags & VBSD_LF_USE_RO_NORMAL) {
        /* If EC is in RW code, request reboot back to RO */
        if (in_rw == 1) {
            VBDEBUG(("VbEcSoftwareSync() - "
                     "want RO-normal but got EC-RW\n"));
            return VBERROR_EC_REBOOT_TO_RO_REQUIRED;
        }

        /* Protect the RW flash and stay in EC-RO */
        rv = EcProtectRW(devidx);
        if (rv != VBERROR_SUCCESS)
            return rv;

        rv = VbExEcDisableJump(devidx);
        if (rv != VBERROR_SUCCESS) {
            VBDEBUG(("VbEcSoftwareSync() - "
                     "VbExEcDisableJump() returned %d\n", rv));
            VbSetRecoveryRequest(VBNV_RECOVERY_EC_SOFTWARE_SYNC);
            return VBERROR_EC_REBOOT_TO_RO_REQUIRED;
        }

        VBDEBUG(("VbEcSoftwareSync() in RO-Normal; EC-RO\n"));
        return VBERROR_SUCCESS;
    }

    /* Get hash of EC-RW */
    rv = VbExEcHashRW(devidx, &ec_hash, &ec_hash_size);
    if (rv) {
        VBDEBUG(("VbEcSoftwareSync() - "
                 "VbExEcHashRW() returned %d\n", rv));
        VbSetRecoveryRequest(VBNV_RECOVERY_EC_HASH_FAILED);
        return VBERROR_EC_REBOOT_TO_RO_REQUIRED;
    }
    if (ec_hash_size != SHA256_DIGEST_SIZE) {
        VBDEBUG(("VbEcSoftwareSync() - "
                 "VbExEcHashRW() says size %d, not %d\n",
                 ec_hash_size, SHA256_DIGEST_SIZE));
        VbSetRecoveryRequest(VBNV_RECOVERY_EC_HASH_SIZE);
        return VBERROR_EC_REBOOT_TO_RO_REQUIRED;
    }

    VBDEBUG(("EC hash:"));
    for (i = 0; i < SHA256_DIGEST_SIZE; i++)
        VBDEBUG(("%02x", ec_hash[i]));
    VBDEBUG(("\n"));

    /*
     * Get expected EC-RW hash. Note that we've already checked for
     * RO_NORMAL, so we know that the BIOS must be RW-A or RW-B, and
     * therefore the EC must match.
     */
    rv = VbExEcGetExpectedRWHash(devidx, shared->firmware_index ?
                                 VB_SELECT_FIRMWARE_B : VB_SELECT_FIRMWARE_A,
                                 &rw_hash, &rw_hash_size);

    if (rv == VBERROR_EC_GET_EXPECTED_HASH_FROM_IMAGE) {
        /*
         * BIOS has verified EC image but doesn't have a precomputed
         * hash for it, so we must compute the hash ourselves.
         */
        rw_hash = NULL;
    } else if (rv) {
        VBDEBUG(("VbEcSoftwareSync() - "
                 "VbExEcGetExpectedRWHash() returned %d\n", rv));
        VbSetRecoveryRequest(VBNV_RECOVERY_EC_EXPECTED_HASH);
        return VBERROR_EC_REBOOT_TO_RO_REQUIRED;
    } else if (rw_hash_size != SHA256_DIGEST_SIZE) {
        VBDEBUG(("VbEcSoftwareSync() - "
                 "VbExEcGetExpectedRWHash() says size %d, not %d\n",
                 rw_hash_size, SHA256_DIGEST_SIZE));
        VbSetRecoveryRequest(VBNV_RECOVERY_EC_EXPECTED_HASH);
        return VBERROR_EC_REBOOT_TO_RO_REQUIRED;
    } else {
        VBDEBUG(("Expected hash:"));
        for (i = 0; i < SHA256_DIGEST_SIZE; i++)
            VBDEBUG(("%02x", rw_hash[i]));
        VBDEBUG(("\n"));

        need_update = SafeMemcmp(ec_hash, rw_hash, SHA256_DIGEST_SIZE);
    }

    /*
     * Get expected EC-RW image if we're sure we need to update (because the
     * expected hash didn't match the EC) or we still don't know (because
     * there was no expected hash and we need the image to compute one
     * ourselves).
     */
    if (need_update || !rw_hash) {
        /* Get expected EC-RW image */
        rv = VbExEcGetExpectedRW(devidx, shared->firmware_index ?
                                 VB_SELECT_FIRMWARE_B :
                                 VB_SELECT_FIRMWARE_A,
                                 &expected, &expected_size);
        if (rv) {
            VBDEBUG(("VbEcSoftwareSync() - "
                     "VbExEcGetExpectedRW() returned %d\n", rv));
            VbSetRecoveryRequest(VBNV_RECOVERY_EC_EXPECTED_IMAGE);
            return VBERROR_EC_REBOOT_TO_RO_REQUIRED;
        }
        VBDEBUG(("VbEcSoftwareSync() - expected len = %d\n",
                 expected_size));

        /* Hash expected image */
        internal_SHA256(expected, expected_size, expected_hash);
        VBDEBUG(("Computed hash of expected image:"));
        for (i = 0; i < SHA256_DIGEST_SIZE; i++)
            VBDEBUG(("%02x", expected_hash[i]));
        VBDEBUG(("\n"));
    }

    if (!rw_hash) {
        /*
         * BIOS didn't have expected EC hash, so check if we need
         * update by comparing EC hash to the one we just computed.
         */
        need_update = SafeMemcmp(ec_hash, expected_hash,
                                 SHA256_DIGEST_SIZE);
    } else if (need_update &&
               SafeMemcmp(rw_hash, expected_hash, SHA256_DIGEST_SIZE)) {
        /*
         * We need to update, but the expected EC image doesn't match
         * the expected EC hash we were given.
         */
        VBDEBUG(("VbEcSoftwareSync() - "
                 "VbExEcGetExpectedRW() returned %d\n", rv));
        VbSetRecoveryRequest(VBNV_RECOVERY_EC_HASH_MISMATCH);
        return VBERROR_EC_REBOOT_TO_RO_REQUIRED;
    }

    /*
     * TODO: GBB flag to override whether we need update; needed for EC
     * development.
     */

    if (in_rw) {
        if (need_update) {
            /*
             * Check if BIOS should also load VGA Option ROM when
             * rebooting to save another reboot if possible.
             */
            if ((shared->flags & VBSD_EC_SLOW_UPDATE) &&
                    (shared->flags & VBSD_OPROM_MATTERS) &&
                    !(shared->flags & VBSD_OPROM_LOADED)) {
                VBDEBUG(("VbEcSoftwareSync() - Reboot to "
                         "load VGA Option ROM\n"));
                VbNvSet(&vnc, VBNV_OPROM_NEEDED, 1);
            }

            /*
             * EC is running the wrong RW image.  Reboot the EC to
             * RO so we can update it on the next boot.
             */
            VBDEBUG(("VbEcSoftwareSync() - "
                     "in RW, need to update RW, so reboot\n"));
            return VBERROR_EC_REBOOT_TO_RO_REQUIRED;
        }

        VBDEBUG(("VbEcSoftwareSync() in EC-RW and it matches\n"));
        return VBERROR_SUCCESS;
    }

    /* Update EC if necessary */
    if (need_update) {
        VBDEBUG(("VbEcSoftwareSync() updating EC-RW...\n"));

        if (shared->flags & VBSD_EC_SLOW_UPDATE) {
            VBDEBUG(("VbEcSoftwareSync() - "
                     "EC is slow. Show WAIT screen.\n"));

            /* Ensure the VGA Option ROM is loaded */
            if ((shared->flags & VBSD_OPROM_MATTERS) &&
                    !(shared->flags & VBSD_OPROM_LOADED)) {
                VBDEBUG(("VbEcSoftwareSync() - Reboot to "
                         "load VGA Option ROM\n"));
                VbNvSet(&vnc, VBNV_OPROM_NEEDED, 1);
                return VBERROR_VGA_OPROM_MISMATCH;
            }

            VbDisplayScreen(cparams, VB_SCREEN_WAIT, 0, &vnc);
        }

        rv = VbExEcUpdateRW(devidx, expected, expected_size);

        if (rv != VBERROR_SUCCESS) {
            VBDEBUG(("VbEcSoftwareSync() - "
                     "VbExEcUpdateRW() returned %d\n", rv));

            /*
             * The EC may know it needs a reboot.  It may need to
             * unprotect RW before updating, or may need to reboot
             * after RW updated.  Either way, it's not an error
             * requiring recovery mode.
             *
             * If we fail for any other reason, trigger recovery
             * mode.
             */
            if (rv != VBERROR_EC_REBOOT_TO_RO_REQUIRED)
                VbSetRecoveryRequest(VBNV_RECOVERY_EC_UPDATE);

            return VBERROR_EC_REBOOT_TO_RO_REQUIRED;
        }

        /*
         * TODO: should ask EC to recompute its hash to verify it's
         * correct before continuing?
         */
    }

    /* Protect EC-RW flash */
    rv = EcProtectRW(devidx);
    if (rv != VBERROR_SUCCESS)
        return rv;

    /* Tell EC to jump to its RW image */
    VBDEBUG(("VbEcSoftwareSync() jumping to EC-RW\n"));
    rv = VbExEcJumpToRW(devidx);
    if (rv != VBERROR_SUCCESS) {
        VBDEBUG(("VbEcSoftwareSync() - "
                 "VbExEcJumpToRW() returned %d\n", rv));

        /*
         * If the EC booted RO-normal and a previous AP boot has called
         * VbExEcStayInRO(), we need to reboot the EC to unlock the
         * ability to jump to the RW firmware.
         *
         * All other errors trigger recovery mode.
         */
        if (rv != VBERROR_EC_REBOOT_TO_RO_REQUIRED)
            VbSetRecoveryRequest(VBNV_RECOVERY_EC_JUMP_RW);

        return VBERROR_EC_REBOOT_TO_RO_REQUIRED;
    }

    VBDEBUG(("VbEcSoftwareSync() jumped to EC-RW\n"));

    rv = VbExEcDisableJump(devidx);
    if (rv != VBERROR_SUCCESS) {
        VBDEBUG(("VbEcSoftwareSync() - "
                 "VbExEcDisableJump() returned %d\n", rv));
        VbSetRecoveryRequest(VBNV_RECOVERY_EC_SOFTWARE_SYNC);
        return VBERROR_EC_REBOOT_TO_RO_REQUIRED;
    }

    /*
     * Reboot to unload VGA Option ROM if:
     * - RW update was done
     * - the system is NOT in developer mode
     * - the system has slow EC update flag set
     * - the VGA Option ROM was needed and loaded
     */
    if (need_update &&
            !(shared->flags & VBSD_BOOT_DEV_SWITCH_ON) &&
            (shared->flags & VBSD_EC_SLOW_UPDATE) &&
            (shared->flags & VBSD_OPROM_MATTERS) &&
            (shared->flags & VBSD_OPROM_LOADED)) {
        VBDEBUG(("VbEcSoftwareSync() - Reboot to "
                 "unload VGA Option ROM\n"));
        return VBERROR_VGA_OPROM_MISMATCH;
    }

    VBDEBUG(("VbEcSoftwareSync() in RW; done\n"));
    return VBERROR_SUCCESS;
}
VbError_t VbBootRecovery(VbCommonParams *cparams, LoadKernelParams *p)
{
    VbSharedDataHeader *shared =
        (VbSharedDataHeader *)cparams->shared_data_blob;
    uint32_t retval;
    uint32_t key;
    int i;

    VBDEBUG(("VbBootRecovery() start\n"));

    /*
     * If the dev-mode switch is off and the user didn't press the recovery
     * button, require removal of all external media.
     */
    if (!(shared->flags & VBSD_BOOT_DEV_SWITCH_ON) &&
            !(shared->flags & VBSD_BOOT_REC_SWITCH_ON)) {
        VbDiskInfo *disk_info = NULL;
        uint32_t disk_count = 0;

        VBDEBUG(("VbBootRecovery() forcing device removal\n"));

        /* If no media is detected initially, delay and make one extra
         * attempt, in case devices appear later than expected. */
        if (VBERROR_SUCCESS != VbExDiskGetInfo(&disk_info, &disk_count,
                                               VB_DISK_FLAG_REMOVABLE))
            disk_count = 0;

        VbExDiskFreeInfo(disk_info, NULL);
        if (0 == disk_count)
            VbExSleepMs(REC_MEDIA_INIT_DELAY);

        while (1) {
            disk_info = NULL;
            disk_count = 0;
            if (VBERROR_SUCCESS !=
                    VbExDiskGetInfo(&disk_info, &disk_count,
                                    VB_DISK_FLAG_REMOVABLE))
                disk_count = 0;

            VbExDiskFreeInfo(disk_info, NULL);

            if (0 == disk_count) {
                VbDisplayScreen(cparams, VB_SCREEN_BLANK,
                                0, &vnc);
                break;
            }

            VBDEBUG(("VbBootRecovery() "
                     "waiting for %d disks to be removed\n",
                     (int)disk_count));

            VbDisplayScreen(cparams, VB_SCREEN_RECOVERY_REMOVE,
                            0, &vnc);

            /*
             * Scan keyboard more frequently than media, since x86
             * platforms don't like to scan USB too rapidly.
             */
            for (i = 0; i < REC_DISK_DELAY; i += REC_KEY_DELAY) {
                VbCheckDisplayKey(cparams, VbExKeyboardRead(),
                                  &vnc);
                if (VbWantShutdown(cparams->gbb->flags))
                    return VBERROR_SHUTDOWN_REQUESTED;
                VbExSleepMs(REC_KEY_DELAY);
            }
        }
    }

    /* Loop and wait for a recovery image */
    while (1) {
        VBDEBUG(("VbBootRecovery() attempting to load kernel2\n"));
        retval = VbTryLoadKernel(cparams, p, VB_DISK_FLAG_REMOVABLE);

        /*
         * Clear recovery requests from failed kernel loading, since
         * we're already in recovery mode.  Do this now, so that
         * powering off after inserting an invalid disk doesn't leave
         * us stuck in recovery mode.
         */
        VbSetRecoveryRequest(VBNV_RECOVERY_NOT_REQUESTED);

        if (VBERROR_SUCCESS == retval)
            break; /* Found a recovery kernel */

        VbDisplayScreen(cparams, VBERROR_NO_DISK_FOUND == retval ?
                        VB_SCREEN_RECOVERY_INSERT :
                        VB_SCREEN_RECOVERY_NO_GOOD,
                        0, &vnc);

        /*
         * Scan keyboard more frequently than media, since x86
         * platforms don't like to scan USB too rapidly.
         */
        for (i = 0; i < REC_DISK_DELAY; i += REC_KEY_DELAY) {
            key = VbExKeyboardRead();
            /*
             * We might want to enter dev-mode from the Insert
             * screen if all of the following are true:
             *   - user pressed Ctrl-D
             *   - we can honor the virtual dev switch
             *   - not already in dev mode
             *   - user forced recovery mode
             *   - EC isn't pwned
             */
            if (key == 0x04 &&
                    shared->flags & VBSD_HONOR_VIRT_DEV_SWITCH &&
                    !(shared->flags & VBSD_BOOT_DEV_SWITCH_ON) &&
                    (shared->flags & VBSD_BOOT_REC_SWITCH_ON) &&
                    VbExTrustEC(0)) {
                if (!(shared->flags &
                        VBSD_BOOT_REC_SWITCH_VIRTUAL) &&
                        VbExGetSwitches(
                            VB_INIT_FLAG_REC_BUTTON_PRESSED)) {
                    /*
                     * Is the recovery button stuck?  In
                     * any case we don't like this.  Beep
                     * and ignore.
                     */
                    VBDEBUG(("%s() - ^D but rec switch "
                             "is pressed\n", __func__));
                    VbExBeep(120, 400);
                    continue;
                }

                /* Ask the user to confirm entering dev-mode */
                VbDisplayScreen(cparams,
                                VB_SCREEN_RECOVERY_TO_DEV,
                                0, &vnc);
                /* SPACE means no... */
                uint32_t vbc_flags =
                    VB_CONFIRM_SPACE_MEANS_NO |
                    VB_CONFIRM_MUST_TRUST_KEYBOARD;
                switch (VbUserConfirms(cparams, vbc_flags)) {
                case 1:
                    VBDEBUG(("%s() Enabling dev-mode...\n",
                             __func__));
                    if (TPM_SUCCESS != SetVirtualDevMode(1))
                        return VBERROR_TPM_SET_BOOT_MODE_STATE;
                    VBDEBUG(("%s() Reboot so it will take "
                             "effect\n", __func__));
                    if (VbExGetSwitches
                            (VB_INIT_FLAG_ALLOW_USB_BOOT))
                        VbAllowUsbBoot();
                    return VBERROR_TPM_REBOOT_REQUIRED;
                case -1:
                    VBDEBUG(("%s() - Shutdown requested\n",
                             __func__));
                    return VBERROR_SHUTDOWN_REQUESTED;
                default: /* zero, actually */
                    VBDEBUG(("%s() - Not enabling "
                             "dev-mode\n", __func__));
                    /*
                     * Jump out of the outer loop to
                     * refresh the display quickly.
                     */
                    i = 4;
                    break;
                }
            } else {
                VbCheckDisplayKey(cparams, key, &vnc);
            }
            if (VbWantShutdown(cparams->gbb->flags))
                return VBERROR_SHUTDOWN_REQUESTED;
            VbExSleepMs(REC_KEY_DELAY);
        }
    }

    return VBERROR_SUCCESS;
}
VbError_t VbBootDeveloper(VbCommonParams *cparams, LoadKernelParams *p)
{
    GoogleBinaryBlockHeader *gbb = cparams->gbb;
    VbSharedDataHeader *shared =
        (VbSharedDataHeader *)cparams->shared_data_blob;
    uint32_t allow_usb = 0, allow_legacy = 0, ctrl_d_pressed = 0;
    VbAudioContext *audio = 0;

    VBDEBUG(("Entering %s()\n", __func__));

    /* Check if USB booting is allowed */
    VbNvGet(&vnc, VBNV_DEV_BOOT_USB, &allow_usb);
    VbNvGet(&vnc, VBNV_DEV_BOOT_LEGACY, &allow_legacy);

    /* Handle GBB flag override */
    if (gbb->flags & GBB_FLAG_FORCE_DEV_BOOT_USB)
        allow_usb = 1;
    if (gbb->flags & GBB_FLAG_FORCE_DEV_BOOT_LEGACY)
        allow_legacy = 1;

    /* Show the dev mode warning screen */
    VbDisplayScreen(cparams, VB_SCREEN_DEVELOPER_WARNING, 0, &vnc);

    /* Get audio/delay context */
    audio = VbAudioOpen(cparams);

    /* We'll loop until we finish the delay or are interrupted */
    do {
        uint32_t key;

        if (VbWantShutdown(gbb->flags)) {
            VBDEBUG(("VbBootDeveloper() - shutdown requested!\n"));
            VbAudioClose(audio);
            return VBERROR_SHUTDOWN_REQUESTED;
        }

        key = VbExKeyboardRead();
        switch (key) {
        case 0:
            /* nothing pressed */
            break;
        case '\r':
            /* Only disable virtual dev switch if allowed by GBB */
            if (!(gbb->flags & GBB_FLAG_ENTER_TRIGGERS_TONORM))
                break;
        case ' ':
            /* See if we should disable virtual dev-mode switch. */
            VBDEBUG(("%s shared->flags=0x%x\n",
                     __func__, shared->flags));
            if (shared->flags & VBSD_HONOR_VIRT_DEV_SWITCH &&
                    shared->flags & VBSD_BOOT_DEV_SWITCH_ON) {
                /* Stop the countdown while we go ask... */
                VbAudioClose(audio);
                if (gbb->flags & GBB_FLAG_FORCE_DEV_SWITCH_ON) {
                    /*
                     * TONORM won't work (only for
                     * non-shipping devices).
                     */
                    VBDEBUG(("%s() - TONORM rejected by "
                             "FORCE_DEV_SWITCH_ON\n",
                             __func__));
                    VbExDisplayDebugInfo(
                        "WARNING: TONORM prohibited by "
                        "GBB FORCE_DEV_SWITCH_ON.\n\n");
                    VbExBeep(120, 400);
                    break;
                }
                VbDisplayScreen(cparams,
                                VB_SCREEN_DEVELOPER_TO_NORM,
                                0, &vnc);
                /* Ignore space in VbUserConfirms()... */
                switch (VbUserConfirms(cparams, 0)) {
                case 1:
                    VBDEBUG(("%s() - leaving dev-mode.\n",
                             __func__));
                    VbNvSet(&vnc, VBNV_DISABLE_DEV_REQUEST,
                            1);
                    VbDisplayScreen(
                        cparams,
                        VB_SCREEN_TO_NORM_CONFIRMED,
                        0, &vnc);
                    VbExSleepMs(5000);
                    return VBERROR_TPM_REBOOT_REQUIRED;
                case -1:
                    VBDEBUG(("%s() - shutdown requested\n",
                             __func__));
                    return VBERROR_SHUTDOWN_REQUESTED;
                default:
                    /* Stay in dev-mode */
                    VBDEBUG(("%s() - stay in dev-mode\n",
                             __func__));
                    VbDisplayScreen(
                        cparams,
                        VB_SCREEN_DEVELOPER_WARNING,
                        0, &vnc);
                    /* Start new countdown */
                    audio = VbAudioOpen(cparams);
                }
            } else {
                /*
                 * No virtual dev-mode switch, so go directly
                 * to recovery mode.
                 */
                VBDEBUG(("%s() - going to recovery\n",
                         __func__));
                VbSetRecoveryRequest(
                    VBNV_RECOVERY_RW_DEV_SCREEN);
                VbAudioClose(audio);
                return VBERROR_LOAD_KERNEL_RECOVERY;
            }
            break;
        case 0x04:
            /* Ctrl+D = dismiss warning; advance to timeout */
            VBDEBUG(("VbBootDeveloper() - "
                     "user pressed Ctrl+D; skip delay\n"));
            ctrl_d_pressed = 1;
            goto fallout;
            break;
        case 0x0c:
            VBDEBUG(("VbBootDeveloper() - "
                     "user pressed Ctrl+L; Try legacy boot\n"));
            VbTryLegacy(allow_legacy);
            break;

        case VB_KEY_CTRL_ENTER:
        /*
         * The Ctrl-Enter is special for Lumpy test purpose;
         * fall through to Ctrl+U handler.
         */
        case 0x15:
            /* Ctrl+U = try USB boot, or beep if failure */
            VBDEBUG(("VbBootDeveloper() - "
                     "user pressed Ctrl+U; try USB\n"));
            if (!allow_usb) {
                VBDEBUG(("VbBootDeveloper() - "
                         "USB booting is disabled\n"));
                VbExDisplayDebugInfo(
                    "WARNING: Booting from external media "
                    "(USB/SD) has not been enabled. Refer "
                    "to the developer-mode documentation "
                    "for details.\n");
                VbExBeep(120, 400);
                VbExSleepMs(120);
                VbExBeep(120, 400);
            } else {
                /*
                 * Clear the screen to show we get the Ctrl+U
                 * key press.
                 */
                VbDisplayScreen(cparams, VB_SCREEN_BLANK, 0,
                                &vnc);
                if (VBERROR_SUCCESS ==
                        VbTryLoadKernel(cparams, p,
                                        VB_DISK_FLAG_REMOVABLE)) {
                    VBDEBUG(("VbBootDeveloper() - "
                             "booting USB\n"));
                    VbAudioClose(audio);
                    return VBERROR_SUCCESS;
                } else {
                    VBDEBUG(("VbBootDeveloper() - "
                             "no kernel found on USB\n"));
                    VbExBeep(250, 200);
                    VbExSleepMs(120);
                    /*
                     * Clear recovery requests from failed
                     * kernel loading, so that powering off
                     * at this point doesn't put us into
                     * recovery mode.
                     */
                    VbSetRecoveryRequest(
                        VBNV_RECOVERY_NOT_REQUESTED);
                    /* Show dev mode warning screen again */
                    VbDisplayScreen(
                        cparams,
                        VB_SCREEN_DEVELOPER_WARNING,
                        0, &vnc);
                }
            }
            break;
        default:
            VBDEBUG(("VbBootDeveloper() - pressed key %d\n", key));
            VbCheckDisplayKey(cparams, key, &vnc);
            break;
        }
    } while(VbAudioLooping(audio));

fallout:

    /* If defaulting to legacy boot, try that unless Ctrl+D was pressed */
    if ((gbb->flags & GBB_FLAG_DEFAULT_DEV_BOOT_LEGACY) &&
            !ctrl_d_pressed) {
        VBDEBUG(("VbBootDeveloper() - defaulting to legacy\n"));
        VbTryLegacy(allow_legacy);
    }

    /* Timeout or Ctrl+D; attempt loading from fixed disk */
    VBDEBUG(("VbBootDeveloper() - trying fixed disk\n"));
    VbAudioClose(audio);
    return VbTryLoadKernel(cparams, p, VB_DISK_FLAG_FIXED);
}