vboot: Split ec software sync to its own file

This was previously done inside vboot_api_kernel.  But it has nothing to
do with kernel verification; that's just the only place where we could
easily put it given that vboot (currently) owns the firmware UI.

No outwardly-visible functionality changes.

BUG=chromium:611535
BRANCH=none
TEST=make runtests; emerge-kevin coreboot depthcharge

Change-Id: I8a434eb4449a5a86b129ecac61ad81d0ad55549c
Signed-off-by: Randall Spangler <rspangler@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/404920
This commit is contained in:
Randall Spangler
2016-10-27 11:20:34 -07:00
committed by chrome-bot
parent e5500a319b
commit 2603675460
6 changed files with 419 additions and 386 deletions

View File

@@ -331,6 +331,7 @@ VBSLK_SRCS = \
firmware/lib/cgptlib/cgptlib.c \
firmware/lib/cgptlib/cgptlib_internal.c \
firmware/lib/cgptlib/crc32.c \
firmware/lib/ec_sync.c \
firmware/lib/gpt_misc.c \
firmware/lib/utility_string.c \
firmware/lib/vboot_api_kernel.c \
@@ -711,6 +712,7 @@ TEST_OBJS += ${TESTLIB_OBJS}
# And some compiled tests.
TEST_NAMES = \
tests/cgptlib_test \
tests/ec_sync_tests \
tests/rollback_index3_tests \
tests/sha_benchmark \
tests/utility_string_tests \
@@ -718,7 +720,6 @@ TEST_NAMES = \
tests/vboot_api_devmode_tests \
tests/vboot_api_kernel_tests \
tests/vboot_api_kernel2_tests \
tests/vboot_api_kernel3_tests \
tests/vboot_api_kernel4_tests \
tests/vboot_api_kernel5_tests \
tests/vboot_api_kernel6_tests \
@@ -1401,6 +1402,7 @@ runtestscripts: test_setup genfuzztestcases
.PHONY: runmisctests
runmisctests: test_setup
${RUNTEST} ${BUILD_RUN}/tests/ec_sync_tests
ifeq (${TPM2_MODE},)
${RUNTEST} ${BUILD_RUN}/tests/tlcl_tests
${RUNTEST} ${BUILD_RUN}/tests/rollback_index2_tests
@@ -1411,7 +1413,6 @@ endif
${RUNTEST} ${BUILD_RUN}/tests/vboot_api_devmode_tests
${RUNTEST} ${BUILD_RUN}/tests/vboot_api_kernel_tests
${RUNTEST} ${BUILD_RUN}/tests/vboot_api_kernel2_tests
${RUNTEST} ${BUILD_RUN}/tests/vboot_api_kernel3_tests
${RUNTEST} ${BUILD_RUN}/tests/vboot_api_kernel4_tests
${RUNTEST} ${BUILD_RUN}/tests/vboot_api_kernel5_tests
${RUNTEST} ${BUILD_RUN}/tests/vboot_api_kernel6_tests

387
firmware/lib/ec_sync.c Normal file
View File

@@ -0,0 +1,387 @@
/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*
* EC software sync routines for vboot
*/
#include "2sysincludes.h"
#include "2common.h"
#include "sysincludes.h"
#include "utility.h"
#include "vb2_common.h"
#include "vboot_api.h"
#include "vboot_common.h"
#include "vboot_display.h"
#include "vboot_kernel.h"
#include "vboot_nvstorage.h"
static void request_recovery(VbNvContext *vnc, uint32_t recovery_request)
{
VB2_DEBUG("request_recovery(%d)\n", (int)recovery_request);
VbNvSet(vnc, VBNV_RECOVERY_REQUEST, recovery_request);
}
/**
* Wrapper around VbExEcProtect() which sets recovery reason on error.
*/
static VbError_t EcProtect(int devidx, enum VbSelectFirmware_t select,
VbNvContext *vnc)
{
int rv = VbExEcProtect(devidx, select);
if (rv == VBERROR_EC_REBOOT_TO_RO_REQUIRED) {
VBDEBUG(("VbExEcProtect() needs reboot\n"));
} else if (rv != VBERROR_SUCCESS) {
VBDEBUG(("VbExEcProtect() returned %d\n", rv));
request_recovery(vnc, VBNV_RECOVERY_EC_PROTECT);
}
return rv;
}
static VbError_t EcUpdateImage(int devidx, VbCommonParams *cparams,
enum VbSelectFirmware_t select,
int *need_update, int in_rw, VbNvContext *vnc)
{
VbSharedDataHeader *shared =
(VbSharedDataHeader *)cparams->shared_data_blob;
int rv;
int hash_size;
int ec_hash_size;
const uint8_t *hash = NULL;
const uint8_t *expected = NULL;
const uint8_t *ec_hash = NULL;
int expected_size;
int i;
int rw_request = select != VB_SELECT_FIRMWARE_READONLY;
*need_update = 0;
VBDEBUG(("EcUpdateImage() - "
"Check for %s update\n", rw_request ? "RW" : "RO"));
/* Get current EC hash. */
rv = VbExEcHashImage(devidx, select, &ec_hash, &ec_hash_size);
if (rv) {
VBDEBUG(("EcUpdateImage() - "
"VbExEcHashImage() returned %d\n", rv));
request_recovery(vnc, VBNV_RECOVERY_EC_HASH_FAILED);
return VBERROR_EC_REBOOT_TO_RO_REQUIRED;
}
VBDEBUG(("EC-%s hash: ", rw_request ? "RW" : "RO"));
for (i = 0; i < ec_hash_size; i++)
VBDEBUG(("%02x",ec_hash[i]));
VBDEBUG(("\n"));
/* Get expected EC hash. */
rv = VbExEcGetExpectedImageHash(devidx, select, &hash, &hash_size);
if (rv) {
VBDEBUG(("EcUpdateImage() - "
"VbExEcGetExpectedImageHash() returned %d\n", rv));
request_recovery(vnc, VBNV_RECOVERY_EC_EXPECTED_HASH);
return VBERROR_EC_REBOOT_TO_RO_REQUIRED;
}
if (ec_hash_size != hash_size) {
VBDEBUG(("EcUpdateImage() - "
"EC uses %d-byte hash, but AP-RW contains %d bytes\n",
ec_hash_size, hash_size));
request_recovery(vnc, VBNV_RECOVERY_EC_HASH_SIZE);
return VBERROR_EC_REBOOT_TO_RO_REQUIRED;
}
VBDEBUG(("Expected hash: "));
for (i = 0; i < hash_size; i++)
VBDEBUG(("%02x", hash[i]));
VBDEBUG(("\n"));
*need_update = vb2_safe_memcmp(ec_hash, hash, hash_size);
if (!*need_update)
return VBERROR_SUCCESS;
/* Get expected EC image */
rv = VbExEcGetExpectedImage(devidx, select, &expected, &expected_size);
if (rv) {
VBDEBUG(("EcUpdateImage() - "
"VbExEcGetExpectedImage() returned %d\n", rv));
request_recovery(vnc, VBNV_RECOVERY_EC_EXPECTED_IMAGE);
return VBERROR_EC_REBOOT_TO_RO_REQUIRED;
}
VBDEBUG(("EcUpdateImage() - image len = %d\n", expected_size));
if (in_rw && rw_request) {
/*
* 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(("EcUpdateImage() - 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(("EcUpdateImage() - "
"in RW, need to update RW, so reboot\n"));
return VBERROR_EC_REBOOT_TO_RO_REQUIRED;
}
VBDEBUG(("EcUpdateImage() updating EC-%s...\n",
rw_request ? "RW" : "RO"));
if (shared->flags & VBSD_EC_SLOW_UPDATE) {
VBDEBUG(("EcUpdateImage() - 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(("EcUpdateImage() - 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 = VbExEcUpdateImage(devidx, select, expected, expected_size);
if (rv != VBERROR_SUCCESS) {
VBDEBUG(("EcUpdateImage() - "
"VbExEcUpdateImage() returned %d\n", rv));
/*
* The EC may know it needs a reboot. It may need to
* unprotect the region before updating, or may need to
* reboot after updating. 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)
request_recovery(vnc, VBNV_RECOVERY_EC_UPDATE);
return VBERROR_EC_REBOOT_TO_RO_REQUIRED;
}
/* Verify the EC was updated properly */
rv = VbExEcHashImage(devidx, select, &ec_hash, &ec_hash_size);
if (rv) {
VBDEBUG(("EcUpdateImage() - "
"VbExEcHashImage() returned %d\n", rv));
request_recovery(vnc, VBNV_RECOVERY_EC_HASH_FAILED);
return VBERROR_EC_REBOOT_TO_RO_REQUIRED;
}
if (hash_size != ec_hash_size) {
VBDEBUG(("EcUpdateImage() - "
"VbExEcHashImage() says size %d, not %d\n",
ec_hash_size, hash_size));
request_recovery(vnc, VBNV_RECOVERY_EC_HASH_SIZE);
return VBERROR_EC_REBOOT_TO_RO_REQUIRED;
}
VBDEBUG(("Updated EC-%s hash: ", rw_request ? "RW" : "RO"));
for (i = 0; i < ec_hash_size; i++)
VBDEBUG(("%02x",ec_hash[i]));
VBDEBUG(("\n"));
if (vb2_safe_memcmp(ec_hash, hash, hash_size)){
VBDEBUG(("EcUpdateImage() - "
"Failed to update EC-%s\n", rw_request ?
"RW" : "RO"));
request_recovery(vnc, VBNV_RECOVERY_EC_UPDATE);
return VBERROR_EC_REBOOT_TO_RO_REQUIRED;
}
return VBERROR_SUCCESS;
}
VbError_t VbEcSoftwareSync(int devidx, VbCommonParams *cparams,
VbNvContext *vnc)
{
VbSharedDataHeader *shared =
(VbSharedDataHeader *)cparams->shared_data_blob;
enum VbSelectFirmware_t select_rw =
shared->firmware_index ? VB_SELECT_FIRMWARE_B :
VB_SELECT_FIRMWARE_A;
enum VbSelectFirmware_t select_ro = VB_SELECT_FIRMWARE_READONLY;
int in_rw = 0;
int ro_try_count = 2;
int num_tries = 0;
uint32_t try_ro_sync, recovery_request;
int rv, updated_rw, updated_ro;
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"));
request_recovery(vnc, 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));
request_recovery(vnc, 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 = EcProtect(devidx, select_rw, vnc);
if (rv != VBERROR_SUCCESS)
return rv;
rv = VbExEcDisableJump(devidx);
if (rv != VBERROR_SUCCESS) {
VBDEBUG(("VbEcSoftwareSync() - "
"VbExEcDisableJump() returned %d\n", rv));
request_recovery(vnc, VBNV_RECOVERY_EC_SOFTWARE_SYNC);
return VBERROR_EC_REBOOT_TO_RO_REQUIRED;
}
VBDEBUG(("VbEcSoftwareSync() in RO-Normal; EC-RO\n"));
return VBERROR_SUCCESS;
}
VBDEBUG(("VbEcSoftwareSync() check for RW update.\n"));
/* Update the RW Image. */
rv = EcUpdateImage(devidx, cparams, select_rw, &updated_rw, in_rw, vnc);
if (rv != VBERROR_SUCCESS) {
VBDEBUG(("VbEcSoftwareSync() - "
"EcUpdateImage() returned %d\n", rv));
return rv;
}
/* Tell EC to jump to its RW image */
if (!in_rw) {
VBDEBUG(("VbEcSoftwareSync() jumping to EC-RW\n"));
rv = VbExEcJumpToRW(devidx);
if (rv != VBERROR_SUCCESS) {
VBDEBUG(("VbEcSoftwareSync() - "
"VbExEcJumpToRW() returned %x\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)
request_recovery(vnc, VBNV_RECOVERY_EC_JUMP_RW);
return VBERROR_EC_REBOOT_TO_RO_REQUIRED;
}
}
VbNvGet(vnc, VBNV_TRY_RO_SYNC, &try_ro_sync);
if (!devidx && try_ro_sync &&
!(shared->flags & VBSD_BOOT_FIRMWARE_WP_ENABLED)) {
/* Reset RO Software Sync NV flag */
VbNvSet(vnc, VBNV_TRY_RO_SYNC, 0);
VbNvGet(vnc, VBNV_RECOVERY_REQUEST, &recovery_request);
/* Update the RO Image. */
while (num_tries < ro_try_count) {
VBDEBUG(("VbEcSoftwareSync() RO Software Sync\n"));
/* Get expected EC-RO Image. */
rv = EcUpdateImage(devidx, cparams, select_ro,
&updated_ro, in_rw, vnc);
if (rv == VBERROR_SUCCESS) {
/*
* If the RO update had failed, reset the
* recovery request.
*/
if (num_tries)
request_recovery(vnc, recovery_request);
break;
} else
VBDEBUG(("VbEcSoftwareSync() - "
"EcUpdateImage() returned %d\n", rv));
num_tries++;
}
}
if (rv != VBERROR_SUCCESS)
return rv;
/* Protect RO flash */
rv = EcProtect(devidx, select_ro, vnc);
if (rv != VBERROR_SUCCESS)
return rv;
/* Protect RW flash */
rv = EcProtect(devidx, select_rw, vnc);
if (rv != VBERROR_SUCCESS)
return rv;
rv = VbExEcDisableJump(devidx);
if (rv != VBERROR_SUCCESS) {
VBDEBUG(("VbEcSoftwareSync() - "
"VbExEcDisableJump() returned %d\n", rv));
request_recovery(vnc, 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 (updated_rw &&
!(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"));
VbNvSet(vnc, VBNV_OPROM_NEEDED, 0);
return VBERROR_VGA_OPROM_MISMATCH;
}
return rv;
}

View File

@@ -67,7 +67,12 @@ VbError_t VbBootRecovery(VbCommonParams *cparams, LoadKernelParams *p);
/**
* Sync EC device <devidx> firmware to expected version.
*
* @param devidx EC device index to sync
* @param cparams Common vboot params
* @param vnc NV storage context
*/
VbError_t VbEcSoftwareSync(int devidx, VbCommonParams *cparams);
VbError_t VbEcSoftwareSync(int devidx, VbCommonParams *cparams,
VbNvContext *vnc);
#endif /* VBOOT_REFERENCE_VBOOT_KERNEL_H_ */

View File

@@ -57,6 +57,12 @@ static void VbSetRecoverySubcode(uint32_t recovery_request)
VbNvSet(&vnc, VBNV_RECOVERY_SUBCODE, recovery_request);
}
static void VbNvLoad(void)
{
VbExNvStorageRead(vnc.raw);
VbNvSetup(&vnc);
}
static void VbNvCommit(void)
{
VbNvTeardown(&vnc);
@@ -694,367 +700,6 @@ VbError_t VbBootRecovery(VbCommonParams *cparams, LoadKernelParams *p)
return VBERROR_SUCCESS;
}
/**
* Wrapper around VbExEcProtect() which sets recovery reason on error.
*/
static VbError_t EcProtect(int devidx, enum VbSelectFirmware_t select)
{
int rv = VbExEcProtect(devidx, select);
if (rv == VBERROR_EC_REBOOT_TO_RO_REQUIRED) {
VBDEBUG(("VbExEcProtect() needs reboot\n"));
} else if (rv != VBERROR_SUCCESS) {
VBDEBUG(("VbExEcProtect() returned %d\n", rv));
VbSetRecoveryRequest(VBNV_RECOVERY_EC_PROTECT);
}
return rv;
}
static VbError_t EcUpdateImage(int devidx, VbCommonParams *cparams,
enum VbSelectFirmware_t select,
int *need_update, int in_rw)
{
VbSharedDataHeader *shared =
(VbSharedDataHeader *)cparams->shared_data_blob;
int rv;
int hash_size;
int ec_hash_size;
const uint8_t *hash = NULL;
const uint8_t *expected = NULL;
const uint8_t *ec_hash = NULL;
int expected_size;
int i;
int rw_request = select != VB_SELECT_FIRMWARE_READONLY;
*need_update = 0;
VBDEBUG(("EcUpdateImage() - "
"Check for %s update\n", rw_request ? "RW" : "RO"));
/* Get current EC hash. */
rv = VbExEcHashImage(devidx, select, &ec_hash, &ec_hash_size);
if (rv) {
VBDEBUG(("EcUpdateImage() - "
"VbExEcHashImage() returned %d\n", rv));
VbSetRecoveryRequest(VBNV_RECOVERY_EC_HASH_FAILED);
return VBERROR_EC_REBOOT_TO_RO_REQUIRED;
}
VBDEBUG(("EC-%s hash: ", rw_request ? "RW" : "RO"));
for (i = 0; i < ec_hash_size; i++)
VBDEBUG(("%02x",ec_hash[i]));
VBDEBUG(("\n"));
/* Get expected EC hash. */
rv = VbExEcGetExpectedImageHash(devidx, select, &hash, &hash_size);
if (rv) {
VBDEBUG(("EcUpdateImage() - "
"VbExEcGetExpectedImageHash() returned %d\n", rv));
VbSetRecoveryRequest(VBNV_RECOVERY_EC_EXPECTED_HASH);
return VBERROR_EC_REBOOT_TO_RO_REQUIRED;
}
if (ec_hash_size != hash_size) {
VBDEBUG(("EcUpdateImage() - "
"EC uses %d-byte hash, but AP-RW contains %d bytes\n",
ec_hash_size, hash_size));
VbSetRecoveryRequest(VBNV_RECOVERY_EC_HASH_SIZE);
return VBERROR_EC_REBOOT_TO_RO_REQUIRED;
}
VBDEBUG(("Expected hash: "));
for (i = 0; i < hash_size; i++)
VBDEBUG(("%02x", hash[i]));
VBDEBUG(("\n"));
*need_update = vb2_safe_memcmp(ec_hash, hash, hash_size);
if (!*need_update)
return VBERROR_SUCCESS;
/* Get expected EC image */
rv = VbExEcGetExpectedImage(devidx, select, &expected, &expected_size);
if (rv) {
VBDEBUG(("EcUpdateImage() - "
"VbExEcGetExpectedImage() returned %d\n", rv));
VbSetRecoveryRequest(VBNV_RECOVERY_EC_EXPECTED_IMAGE);
return VBERROR_EC_REBOOT_TO_RO_REQUIRED;
}
VBDEBUG(("EcUpdateImage() - image len = %d\n", expected_size));
if (in_rw && rw_request) {
/*
* 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(("EcUpdateImage() - 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(("EcUpdateImage() - "
"in RW, need to update RW, so reboot\n"));
return VBERROR_EC_REBOOT_TO_RO_REQUIRED;
}
VBDEBUG(("EcUpdateImage() updating EC-%s...\n",
rw_request ? "RW" : "RO"));
if (shared->flags & VBSD_EC_SLOW_UPDATE) {
VBDEBUG(("EcUpdateImage() - 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(("EcUpdateImage() - 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 = VbExEcUpdateImage(devidx, select, expected, expected_size);
if (rv != VBERROR_SUCCESS) {
VBDEBUG(("EcUpdateImage() - "
"VbExEcUpdateImage() returned %d\n", rv));
/*
* The EC may know it needs a reboot. It may need to
* unprotect the region before updating, or may need to
* reboot after updating. 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;
}
/* Verify the EC was updated properly */
rv = VbExEcHashImage(devidx, select, &ec_hash, &ec_hash_size);
if (rv) {
VBDEBUG(("EcUpdateImage() - "
"VbExEcHashImage() returned %d\n", rv));
VbSetRecoveryRequest(VBNV_RECOVERY_EC_HASH_FAILED);
return VBERROR_EC_REBOOT_TO_RO_REQUIRED;
}
if (hash_size != ec_hash_size) {
VBDEBUG(("EcUpdateImage() - "
"VbExEcHashImage() says size %d, not %d\n",
ec_hash_size, hash_size));
VbSetRecoveryRequest(VBNV_RECOVERY_EC_HASH_SIZE);
return VBERROR_EC_REBOOT_TO_RO_REQUIRED;
}
VBDEBUG(("Updated EC-%s hash: ", rw_request ? "RW" : "RO"));
for (i = 0; i < ec_hash_size; i++)
VBDEBUG(("%02x",ec_hash[i]));
VBDEBUG(("\n"));
if (vb2_safe_memcmp(ec_hash, hash, hash_size)){
VBDEBUG(("EcUpdateImage() - "
"Failed to update EC-%s\n", rw_request ?
"RW" : "RO"));
VbSetRecoveryRequest(VBNV_RECOVERY_EC_UPDATE);
return VBERROR_EC_REBOOT_TO_RO_REQUIRED;
}
return VBERROR_SUCCESS;
}
VbError_t VbEcSoftwareSync(int devidx, VbCommonParams *cparams)
{
VbSharedDataHeader *shared =
(VbSharedDataHeader *)cparams->shared_data_blob;
enum VbSelectFirmware_t select_rw =
shared->firmware_index ? VB_SELECT_FIRMWARE_B :
VB_SELECT_FIRMWARE_A;
enum VbSelectFirmware_t select_ro = VB_SELECT_FIRMWARE_READONLY;
int in_rw = 0;
int ro_try_count = 2;
int num_tries = 0;
uint32_t try_ro_sync, recovery_request;
int rv, updated_rw, updated_ro;
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 = EcProtect(devidx, select_rw);
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;
}
VBDEBUG(("VbEcSoftwareSync() check for RW update.\n"));
/* Update the RW Image. */
rv = EcUpdateImage(devidx, cparams, select_rw, &updated_rw, in_rw);
if (rv != VBERROR_SUCCESS) {
VBDEBUG(("VbEcSoftwareSync() - "
"EcUpdateImage() returned %d\n", rv));
return rv;
}
/* Tell EC to jump to its RW image */
if (!in_rw) {
VBDEBUG(("VbEcSoftwareSync() jumping to EC-RW\n"));
rv = VbExEcJumpToRW(devidx);
if (rv != VBERROR_SUCCESS) {
VBDEBUG(("VbEcSoftwareSync() - "
"VbExEcJumpToRW() returned %x\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;
}
}
VbNvGet(&vnc, VBNV_TRY_RO_SYNC, &try_ro_sync);
if (!devidx && try_ro_sync &&
!(shared->flags & VBSD_BOOT_FIRMWARE_WP_ENABLED)) {
/* Reset RO Software Sync NV flag */
VbNvSet(&vnc, VBNV_TRY_RO_SYNC, 0);
VbNvGet(&vnc, VBNV_RECOVERY_REQUEST, &recovery_request);
/* Update the RO Image. */
while (num_tries < ro_try_count) {
VBDEBUG(("VbEcSoftwareSync() RO Software Sync\n"));
/* Get expected EC-RO Image. */
rv = EcUpdateImage(devidx, cparams, select_ro,
&updated_ro, in_rw);
if (rv == VBERROR_SUCCESS) {
/*
* If the RO update had failed, reset the
* recovery request.
*/
if (num_tries)
VbSetRecoveryRequest(recovery_request);
break;
} else
VBDEBUG(("VbEcSoftwareSync() - "
"EcUpdateImage() returned %d\n", rv));
num_tries++;
}
}
if (rv != VBERROR_SUCCESS)
return rv;
/* Protect RO flash */
rv = EcProtect(devidx, select_ro);
if (rv != VBERROR_SUCCESS)
return rv;
/* Protect RW flash */
rv = EcProtect(devidx, select_rw);
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;
}
/*
* 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 (updated_rw &&
!(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"));
VbNvSet(&vnc, VBNV_OPROM_NEEDED, 0);
return VBERROR_VGA_OPROM_MISMATCH;
}
return rv;
}
/* This function is also used by tests */
void VbApiKernelFree(VbCommonParams *cparams)
{
@@ -1082,8 +727,7 @@ VbError_t VbSelectAndLoadKernel(VbCommonParams *cparams,
/* Start timer */
shared->timer_vb_select_and_load_kernel_enter = VbExGetTimer();
VbExNvStorageRead(vnc.raw);
VbNvSetup(&vnc);
VbNvLoad();
/* Fill in params for calls to LoadKernel() */
memset(&p, 0, sizeof(p));
@@ -1118,7 +762,7 @@ VbError_t VbSelectAndLoadKernel(VbCommonParams *cparams,
!(cparams->gbb->flags & GBB_FLAG_DISABLE_EC_SOFTWARE_SYNC)) {
int oprom_mismatch = 0;
retval = VbEcSoftwareSync(0, cparams);
retval = VbEcSoftwareSync(0, cparams, &vnc);
/* Save reboot requested until after possible PD sync */
if (retval == VBERROR_VGA_OPROM_MISMATCH)
oprom_mismatch = 1;
@@ -1128,7 +772,7 @@ VbError_t VbSelectAndLoadKernel(VbCommonParams *cparams,
#ifdef PD_SYNC
if (!(cparams->gbb->flags &
GBB_FLAG_DISABLE_PD_SOFTWARE_SYNC)) {
retval = VbEcSoftwareSync(1, cparams);
retval = VbEcSoftwareSync(1, cparams, &vnc);
if (retval == VBERROR_VGA_OPROM_MISMATCH)
oprom_mismatch = 1;
else if (retval != VBERROR_SUCCESS)
@@ -1380,8 +1024,7 @@ VbError_t VbVerifyMemoryBootImage(VbCommonParams *cparams,
*/
dev_switch = shared->flags & VBSD_BOOT_DEV_SWITCH_ON;
VbExNvStorageRead(vnc.raw);
VbNvSetup(&vnc);
VbNvLoad();
VbNvGet(&vnc, VBNV_DEV_BOOT_FASTBOOT_FULL_CAP,
&allow_fastboot_full_cap);
@@ -1519,8 +1162,7 @@ VbError_t VbUnlockDevice(void)
VbError_t VbLockDevice(void)
{
VbExNvStorageRead(vnc.raw);
VbNvSetup(&vnc);
VbNvLoad();
VBDEBUG(("%s() - Storing request to leave dev-mode.\n",
__func__));

View File

@@ -47,6 +47,7 @@ static int mock_ec_rw_hash_size;
static uint8_t want_ec_hash[32];
static uint8_t update_hash;
static int want_ec_hash_size;
static VbNvContext mock_vnc;
static uint32_t screens_displayed[8];
static uint32_t screens_count = 0;
@@ -64,13 +65,9 @@ static void ResetMocks(void)
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(&mock_vnc, 0, sizeof(VbNvContext));
VbNvSetup(&mock_vnc);
VbNvTeardown(&mock_vnc); /* So CRC gets generated */
memset(&shared_data, 0, sizeof(shared_data));
VbSharedDataInit(shared, sizeof(shared_data));
@@ -207,8 +204,8 @@ static void test_ssync(VbError_t retval, int recovery_reason, const char *desc)
{
uint32_t u;
TEST_EQ(VbEcSoftwareSync(0, &cparams), retval, desc);
VbNvGet(VbApiKernelGetVnc(), VBNV_RECOVERY_REQUEST, &u);
TEST_EQ(VbEcSoftwareSync(0, &cparams, &mock_vnc), retval, desc);
VbNvGet(&mock_vnc, VBNV_RECOVERY_REQUEST, &u);
TEST_EQ(u, recovery_reason, " recovery reason");
}
@@ -300,7 +297,7 @@ static void VbSoftwareSyncTest(void)
ResetMocks();
mock_ec_rw_hash[0]++;
VbNvSet(VbApiKernelGetVnc(), VBNV_TRY_RO_SYNC, 1);
VbNvSet(&mock_vnc, VBNV_TRY_RO_SYNC, 1);
test_ssync(0, 0, "Update rw without reboot");
TEST_EQ(ec_rw_protected, 1, " ec rw protected");
TEST_EQ(ec_run_image, 1, " ec run image");
@@ -311,7 +308,7 @@ static void VbSoftwareSyncTest(void)
ResetMocks();
mock_ec_rw_hash[0]++;
mock_ec_ro_hash[0]++;
VbNvSet(VbApiKernelGetVnc(), VBNV_TRY_RO_SYNC, 1);
VbNvSet(&mock_vnc, VBNV_TRY_RO_SYNC, 1);
test_ssync(0, 0, "Update rw and ro images without reboot");
TEST_EQ(ec_rw_protected, 1, " ec rw protected");
TEST_EQ(ec_run_image, 1, " ec run image");
@@ -321,7 +318,7 @@ static void VbSoftwareSyncTest(void)
ResetMocks();
shared->flags |= VBSD_BOOT_FIRMWARE_WP_ENABLED;
VbNvSet(VbApiKernelGetVnc(), VBNV_TRY_RO_SYNC, 1);
VbNvSet(&mock_vnc, VBNV_TRY_RO_SYNC, 1);
mock_ec_rw_hash[0]++;
mock_ec_ro_hash[0]++;
test_ssync(0, 0, "WP enabled");
@@ -332,7 +329,7 @@ static void VbSoftwareSyncTest(void)
TEST_EQ(ec_ro_updated, 0, " ec ro updated");
ResetMocks();
VbNvSet(VbApiKernelGetVnc(), VBNV_TRY_RO_SYNC, 1);
VbNvSet(&mock_vnc, VBNV_TRY_RO_SYNC, 1);
mock_ec_ro_hash[0]++;
test_ssync(0, 0, "rw update not needed");
TEST_EQ(ec_rw_protected, 1, " ec rw protected");

View File

@@ -81,7 +81,8 @@ VbError_t VbExNvStorageWrite(const uint8_t *buf)
return VBERROR_SUCCESS;
}
VbError_t VbEcSoftwareSync(int devidx, VbCommonParams *cparams)
VbError_t VbEcSoftwareSync(int devidx, VbCommonParams *cparams,
VbNvContext *vnc)
{
return ecsync_retval;
}