From 8d9149e5f18fb7d7528e7948e5412e737ca391ff Mon Sep 17 00:00:00 2001 From: Randall Spangler Date: Thu, 27 Oct 2016 11:20:34 -0700 Subject: 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 Signed-off-by: Randall Spangler Reviewed-on: https://chromium-review.googlesource.com/404920 (cherry picked from commit 2603675460d75e3941070c44cfe90ba09a40d0ce) Change-Id: I079e7cbf17599017f6626ec0d8077edabca06d08 Reviewed-on: https://chromium-review.googlesource.com/575647 Tested-by: Caveh Jalali Trybot-Ready: Caveh Jalali Commit-Queue: Caveh Jalali Reviewed-by: Caveh Jalali Reviewed-by: Randall Spangler --- Makefile | 5 +- firmware/lib/ec_sync.c | 387 ++++++++++++++++++++++++++++++++ firmware/lib/include/vboot_kernel.h | 7 +- firmware/lib/vboot_api_kernel.c | 380 +------------------------------ tests/ec_sync_tests.c | 427 +++++++++++++++++++++++++++++++++++ tests/vboot_api_kernel3_tests.c | 430 ------------------------------------ tests/vboot_api_kernel4_tests.c | 3 +- 7 files changed, 836 insertions(+), 803 deletions(-) create mode 100644 firmware/lib/ec_sync.c create mode 100644 tests/ec_sync_tests.c delete mode 100644 tests/vboot_api_kernel3_tests.c diff --git a/Makefile b/Makefile index d93ad08e..dab004ef 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/firmware/lib/ec_sync.c b/firmware/lib/ec_sync.c new file mode 100644 index 00000000..e989bbda --- /dev/null +++ b/firmware/lib/ec_sync.c @@ -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; +} diff --git a/firmware/lib/include/vboot_kernel.h b/firmware/lib/include/vboot_kernel.h index 2c69f969..c7134af3 100644 --- a/firmware/lib/include/vboot_kernel.h +++ b/firmware/lib/include/vboot_kernel.h @@ -67,7 +67,12 @@ VbError_t VbBootRecovery(VbCommonParams *cparams, LoadKernelParams *p); /** * Sync EC device 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_ */ diff --git a/firmware/lib/vboot_api_kernel.c b/firmware/lib/vboot_api_kernel.c index e465fc31..7cf8d8a4 100644 --- a/firmware/lib/vboot_api_kernel.c +++ b/firmware/lib/vboot_api_kernel.c @@ -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__)); diff --git a/tests/ec_sync_tests.c b/tests/ec_sync_tests.c new file mode 100644 index 00000000..28a6a143 --- /dev/null +++ b/tests/ec_sync_tests.c @@ -0,0 +1,427 @@ +/* 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. + * + * Tests for vboot_api_kernel, part 3 - software sync + */ + +#include +#include +#include + +#include "gbb_header.h" +#include "host_common.h" +#include "load_kernel_fw.h" +#include "rollback_index.h" +#include "test_common.h" +#include "vboot_audio.h" +#include "vboot_common.h" +#include "vboot_kernel.h" +#include "vboot_nvstorage.h" +#include "vboot_struct.h" + +/* Mock data */ +static VbCommonParams cparams; +static uint8_t shared_data[VB_SHARED_DATA_MIN_SIZE]; +static VbSharedDataHeader *shared = (VbSharedDataHeader *)shared_data; +static GoogleBinaryBlockHeader gbb; + +static int trust_ec; +static int mock_in_rw; +static VbError_t in_rw_retval; +static int protect_retval; +static int ec_ro_protected; +static int ec_rw_protected; +static int run_retval; +static int ec_run_image; +static int update_retval; +static int ec_ro_updated; +static int ec_rw_updated; +static int get_expected_retval; +static int shutdown_request_calls_left; + +static uint8_t mock_ec_ro_hash[32]; +static uint8_t mock_ec_rw_hash[32]; +static int mock_ec_ro_hash_size; +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; + +/* 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; + + 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)); + + 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; + + // TODO: ensure these are actually needed + + memset(screens_displayed, 0, sizeof(screens_displayed)); + screens_count = 0; +} + +/* Mock functions */ + +uint32_t VbExIsShutdownRequested(void) +{ + if (shutdown_request_calls_left == 0) + return 1; + else if (shutdown_request_calls_left > 0) + shutdown_request_calls_left--; + + return 0; +} + +int VbExTrustEC(int devidx) +{ + return trust_ec; +} + +VbError_t VbExEcRunningRW(int devidx, int *in_rw) +{ + *in_rw = mock_in_rw; + return in_rw_retval; +} + +VbError_t VbExEcProtect(int devidx, enum VbSelectFirmware_t select) +{ + if (select == VB_SELECT_FIRMWARE_READONLY) + ec_ro_protected = 1; + else + ec_rw_protected = 1; + return protect_retval; +} + +VbError_t VbExEcDisableJump(int devidx) +{ + return run_retval; +} + +VbError_t VbExEcJumpToRW(int devidx) +{ + ec_run_image = 1; + mock_in_rw = 1; + return run_retval; +} + +VbError_t VbExEcHashImage(int devidx, enum VbSelectFirmware_t select, + const uint8_t **hash, int *hash_size) +{ + *hash = select == VB_SELECT_FIRMWARE_READONLY ? + mock_ec_ro_hash : mock_ec_rw_hash; + *hash_size = select == VB_SELECT_FIRMWARE_READONLY ? + mock_ec_ro_hash_size : mock_ec_rw_hash_size; + return *hash_size ? VBERROR_SUCCESS : VBERROR_SIMULATED; +} + +VbError_t VbExEcGetExpectedImage(int devidx, enum VbSelectFirmware_t select, + const uint8_t **image, int *image_size) +{ + static uint8_t fake_image[64] = {5, 6, 7, 8}; + *image = fake_image; + *image_size = sizeof(fake_image); + return get_expected_retval; +} + +VbError_t VbExEcGetExpectedImageHash(int devidx, enum VbSelectFirmware_t select, + const uint8_t **hash, int *hash_size) +{ + *hash = want_ec_hash; + *hash_size = want_ec_hash_size; + + return want_ec_hash_size ? VBERROR_SUCCESS : VBERROR_SIMULATED; +} + +VbError_t VbExEcUpdateImage(int devidx, enum VbSelectFirmware_t select, + const uint8_t *image, int image_size) +{ + if (select == VB_SELECT_FIRMWARE_READONLY) { + ec_ro_updated = 1; + mock_ec_ro_hash[0] = update_hash; + } else { + ec_rw_updated = 1; + mock_ec_rw_hash[0] = update_hash; + } + return update_retval; +} + +VbError_t VbDisplayScreen(VbCommonParams *cparams, uint32_t screen, int force, + VbNvContext *vncptr) +{ + if (screens_count < ARRAY_SIZE(screens_displayed)) + screens_displayed[screens_count++] = screen; + + return VBERROR_SUCCESS; +} + +static void test_ssync(VbError_t retval, int recovery_reason, const char *desc) +{ + uint32_t u; + + TEST_EQ(VbEcSoftwareSync(0, &cparams, &mock_vnc), retval, desc); + VbNvGet(&mock_vnc, VBNV_RECOVERY_REQUEST, &u); + TEST_EQ(u, recovery_reason, " recovery reason"); +} + +/* Tests */ + +static void VbSoftwareSyncTest(void) +{ + /* Recovery cases */ + ResetMocks(); + shared->recovery_reason = 123; + test_ssync(0, 0, "In recovery, EC-RO"); + TEST_EQ(ec_rw_protected, 0, " ec rw protected"); + + ResetMocks(); + shared->recovery_reason = 123; + mock_in_rw = 1; + test_ssync(VBERROR_EC_REBOOT_TO_RO_REQUIRED, + 123, "Recovery needs EC-RO"); + + /* AP-RO cases */ + ResetMocks(); + in_rw_retval = VBERROR_SIMULATED; + test_ssync(VBERROR_EC_REBOOT_TO_RO_REQUIRED, + VBNV_RECOVERY_EC_UNKNOWN_IMAGE, "Unknown EC image"); + + ResetMocks(); + shared->flags |= VBSD_LF_USE_RO_NORMAL; + mock_in_rw = 1; + test_ssync(VBERROR_EC_REBOOT_TO_RO_REQUIRED, + 0, "AP-RO needs EC-RO"); + + ResetMocks(); + shared->flags |= VBSD_LF_USE_RO_NORMAL; + test_ssync(0, 0, "AP-RO, EC-RO"); + TEST_EQ(ec_rw_protected, 1, " ec rw protected"); + TEST_EQ(ec_run_image, 0, " ec run image"); + + ResetMocks(); + shared->flags |= VBSD_LF_USE_RO_NORMAL; + run_retval = VBERROR_SIMULATED; + test_ssync(VBERROR_EC_REBOOT_TO_RO_REQUIRED, + VBNV_RECOVERY_EC_SOFTWARE_SYNC, "Stay in RO fail"); + + ResetMocks(); + shared->flags |= VBSD_LF_USE_RO_NORMAL; + protect_retval = VBERROR_SIMULATED; + test_ssync(VBERROR_SIMULATED, + VBNV_RECOVERY_EC_PROTECT, "Protect error"); + + /* No longer check for shutdown requested */ + ResetMocks(); + shared->flags |= VBSD_LF_USE_RO_NORMAL; + shutdown_request_calls_left = 0; + test_ssync(0, 0, "AP-RO shutdown requested"); + + /* Calculate hashes */ + ResetMocks(); + mock_ec_rw_hash_size = 0; + test_ssync(VBERROR_EC_REBOOT_TO_RO_REQUIRED, + VBNV_RECOVERY_EC_HASH_FAILED, "Bad EC hash"); + + ResetMocks(); + mock_ec_rw_hash_size = 16; + test_ssync(VBERROR_EC_REBOOT_TO_RO_REQUIRED, + VBNV_RECOVERY_EC_HASH_SIZE, "Bad EC hash size"); + + ResetMocks(); + want_ec_hash_size = 0; + test_ssync(VBERROR_EC_REBOOT_TO_RO_REQUIRED, + VBNV_RECOVERY_EC_EXPECTED_HASH, "Bad precalculated hash"); + + ResetMocks(); + want_ec_hash_size = 16; + test_ssync(VBERROR_EC_REBOOT_TO_RO_REQUIRED, + VBNV_RECOVERY_EC_HASH_SIZE, + "Hash size mismatch"); + + ResetMocks(); + want_ec_hash_size = 4; + mock_ec_rw_hash_size = 4; + test_ssync(0, 0, "Custom hash size"); + + /* Updates required */ + ResetMocks(); + mock_in_rw = 1; + mock_ec_rw_hash[0]++; + test_ssync(VBERROR_EC_REBOOT_TO_RO_REQUIRED, + 0, "Pending update needs reboot"); + + ResetMocks(); + mock_ec_rw_hash[0]++; + 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"); + TEST_EQ(ec_rw_updated, 1, " ec rw updated"); + TEST_EQ(ec_ro_protected, 1, " ec ro protected"); + TEST_EQ(ec_ro_updated, 0, " ec ro updated"); + + ResetMocks(); + mock_ec_rw_hash[0]++; + mock_ec_ro_hash[0]++; + 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"); + TEST_EQ(ec_rw_updated, 1, " ec rw updated"); + TEST_EQ(ec_ro_protected, 1, " ec ro protected"); + TEST_EQ(ec_ro_updated, 1, " ec ro updated"); + + ResetMocks(); + shared->flags |= VBSD_BOOT_FIRMWARE_WP_ENABLED; + VbNvSet(&mock_vnc, VBNV_TRY_RO_SYNC, 1); + mock_ec_rw_hash[0]++; + mock_ec_ro_hash[0]++; + test_ssync(0, 0, "WP enabled"); + TEST_EQ(ec_rw_protected, 1, " ec rw protected"); + TEST_EQ(ec_run_image, 1, " ec run image"); + TEST_EQ(ec_rw_updated, 1, " ec rw updated"); + TEST_EQ(ec_ro_protected, 1, " ec ro protected"); + TEST_EQ(ec_ro_updated, 0, " ec ro updated"); + + ResetMocks(); + 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"); + TEST_EQ(ec_run_image, 1, " ec run image"); + TEST_EQ(ec_rw_updated, 0, " ec rw not updated"); + TEST_EQ(ec_ro_protected, 1, " ec ro protected"); + TEST_EQ(ec_ro_updated, 1, " ec ro updated"); + + ResetMocks(); + mock_ec_rw_hash[0]++; + mock_ec_ro_hash[0]++; + test_ssync(0, 0, "ro update not requested"); + TEST_EQ(ec_rw_protected, 1, " ec rw protected"); + TEST_EQ(ec_run_image, 1, " ec run image"); + TEST_EQ(ec_rw_updated, 1, " ec rw updated"); + TEST_EQ(ec_ro_protected, 1, " ec ro protected"); + TEST_EQ(ec_ro_updated, 0, " ec ro updated"); + + ResetMocks(); + mock_ec_rw_hash[0]++; + update_hash++; + test_ssync(VBERROR_EC_REBOOT_TO_RO_REQUIRED, + VBNV_RECOVERY_EC_UPDATE, "updated hash mismatch"); + TEST_EQ(ec_rw_protected, 0, " ec rw protected"); + TEST_EQ(ec_run_image, 0, " ec run image"); + TEST_EQ(ec_rw_updated, 1, " ec rw updated"); + TEST_EQ(ec_ro_protected, 0, " ec ro protected"); + TEST_EQ(ec_ro_updated, 0, " ec ro updated"); + + ResetMocks(); + mock_ec_rw_hash[0]++; + update_retval = VBERROR_EC_REBOOT_TO_RO_REQUIRED; + test_ssync(VBERROR_EC_REBOOT_TO_RO_REQUIRED, + 0, "Reboot after rw update"); + TEST_EQ(ec_rw_updated, 1, " ec rw updated"); + TEST_EQ(ec_ro_updated, 0, " ec rw updated"); + + ResetMocks(); + mock_ec_rw_hash[0]++; + update_retval = VBERROR_SIMULATED; + test_ssync(VBERROR_EC_REBOOT_TO_RO_REQUIRED, + VBNV_RECOVERY_EC_UPDATE, "Update failed"); + + ResetMocks(); + mock_ec_rw_hash[0]++; + shared->flags |= VBSD_EC_SLOW_UPDATE; + test_ssync(0, 0, "Slow update"); + TEST_EQ(screens_displayed[0], VB_SCREEN_WAIT, " wait screen"); + + /* RW cases, no update */ + ResetMocks(); + mock_in_rw = 1; + test_ssync(0, 0, "AP-RW, EC-RW"); + + ResetMocks(); + test_ssync(0, 0, "AP-RW, EC-RO -> EC-RW"); + TEST_EQ(ec_rw_protected, 1, " ec rw protected"); + TEST_EQ(ec_run_image, 1, " ec run image"); + TEST_EQ(ec_rw_updated, 0, " ec rw updated"); + TEST_EQ(ec_ro_protected, 1, " ec ro protected"); + TEST_EQ(ec_ro_updated, 0, " ec ro updated"); + + ResetMocks(); + run_retval = VBERROR_SIMULATED; + test_ssync(VBERROR_EC_REBOOT_TO_RO_REQUIRED, + VBNV_RECOVERY_EC_JUMP_RW, "Jump to RW fail"); + + ResetMocks(); + run_retval = VBERROR_EC_REBOOT_TO_RO_REQUIRED; + test_ssync(VBERROR_EC_REBOOT_TO_RO_REQUIRED, + 0, "Jump to RW fail because locked"); + + ResetMocks(); + protect_retval = VBERROR_SIMULATED; + test_ssync(VBERROR_SIMULATED, + VBNV_RECOVERY_EC_PROTECT, "Protect error"); + + /* No longer check for shutdown requested */ + ResetMocks(); + shutdown_request_calls_left = 0; + test_ssync(0, 0, + "AP-RW, EC-RO -> EC-RW shutdown requested"); + + ResetMocks(); + mock_in_rw = 1; + shutdown_request_calls_left = 0; + test_ssync(0, 0, "AP-RW shutdown requested"); +} + +int main(void) +{ + VbSoftwareSyncTest(); + + return gTestSuccess ? 0 : 255; +} diff --git a/tests/vboot_api_kernel3_tests.c b/tests/vboot_api_kernel3_tests.c deleted file mode 100644 index 3eddb739..00000000 --- a/tests/vboot_api_kernel3_tests.c +++ /dev/null @@ -1,430 +0,0 @@ -/* 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. - * - * Tests for vboot_api_kernel, part 3 - software sync - */ - -#include -#include -#include - -#include "gbb_header.h" -#include "host_common.h" -#include "load_kernel_fw.h" -#include "rollback_index.h" -#include "test_common.h" -#include "vboot_audio.h" -#include "vboot_common.h" -#include "vboot_kernel.h" -#include "vboot_nvstorage.h" -#include "vboot_struct.h" - -/* Mock data */ -static VbCommonParams cparams; -static uint8_t shared_data[VB_SHARED_DATA_MIN_SIZE]; -static VbSharedDataHeader *shared = (VbSharedDataHeader *)shared_data; -static GoogleBinaryBlockHeader gbb; - -static int trust_ec; -static int mock_in_rw; -static VbError_t in_rw_retval; -static int protect_retval; -static int ec_ro_protected; -static int ec_rw_protected; -static int run_retval; -static int ec_run_image; -static int update_retval; -static int ec_ro_updated; -static int ec_rw_updated; -static int get_expected_retval; -static int shutdown_request_calls_left; - -static uint8_t mock_ec_ro_hash[32]; -static uint8_t mock_ec_rw_hash[32]; -static int mock_ec_ro_hash_size; -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 uint32_t screens_displayed[8]; -static uint32_t screens_count = 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; - - // TODO: ensure these are actually needed - - memset(screens_displayed, 0, sizeof(screens_displayed)); - screens_count = 0; -} - -/* Mock functions */ - -uint32_t VbExIsShutdownRequested(void) -{ - if (shutdown_request_calls_left == 0) - return 1; - else if (shutdown_request_calls_left > 0) - shutdown_request_calls_left--; - - return 0; -} - -int VbExTrustEC(int devidx) -{ - return trust_ec; -} - -VbError_t VbExEcRunningRW(int devidx, int *in_rw) -{ - *in_rw = mock_in_rw; - return in_rw_retval; -} - -VbError_t VbExEcProtect(int devidx, enum VbSelectFirmware_t select) -{ - if (select == VB_SELECT_FIRMWARE_READONLY) - ec_ro_protected = 1; - else - ec_rw_protected = 1; - return protect_retval; -} - -VbError_t VbExEcDisableJump(int devidx) -{ - return run_retval; -} - -VbError_t VbExEcJumpToRW(int devidx) -{ - ec_run_image = 1; - mock_in_rw = 1; - return run_retval; -} - -VbError_t VbExEcHashImage(int devidx, enum VbSelectFirmware_t select, - const uint8_t **hash, int *hash_size) -{ - *hash = select == VB_SELECT_FIRMWARE_READONLY ? - mock_ec_ro_hash : mock_ec_rw_hash; - *hash_size = select == VB_SELECT_FIRMWARE_READONLY ? - mock_ec_ro_hash_size : mock_ec_rw_hash_size; - return *hash_size ? VBERROR_SUCCESS : VBERROR_SIMULATED; -} - -VbError_t VbExEcGetExpectedImage(int devidx, enum VbSelectFirmware_t select, - const uint8_t **image, int *image_size) -{ - static uint8_t fake_image[64] = {5, 6, 7, 8}; - *image = fake_image; - *image_size = sizeof(fake_image); - return get_expected_retval; -} - -VbError_t VbExEcGetExpectedImageHash(int devidx, enum VbSelectFirmware_t select, - const uint8_t **hash, int *hash_size) -{ - *hash = want_ec_hash; - *hash_size = want_ec_hash_size; - - return want_ec_hash_size ? VBERROR_SUCCESS : VBERROR_SIMULATED; -} - -VbError_t VbExEcUpdateImage(int devidx, enum VbSelectFirmware_t select, - const uint8_t *image, int image_size) -{ - if (select == VB_SELECT_FIRMWARE_READONLY) { - ec_ro_updated = 1; - mock_ec_ro_hash[0] = update_hash; - } else { - ec_rw_updated = 1; - mock_ec_rw_hash[0] = update_hash; - } - return update_retval; -} - -VbError_t VbDisplayScreen(VbCommonParams *cparams, uint32_t screen, int force, - VbNvContext *vncptr) -{ - if (screens_count < ARRAY_SIZE(screens_displayed)) - screens_displayed[screens_count++] = screen; - - return VBERROR_SUCCESS; -} - -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(u, recovery_reason, " recovery reason"); -} - -/* Tests */ - -static void VbSoftwareSyncTest(void) -{ - /* Recovery cases */ - ResetMocks(); - shared->recovery_reason = 123; - test_ssync(0, 0, "In recovery, EC-RO"); - TEST_EQ(ec_rw_protected, 0, " ec rw protected"); - - ResetMocks(); - shared->recovery_reason = 123; - mock_in_rw = 1; - test_ssync(VBERROR_EC_REBOOT_TO_RO_REQUIRED, - 123, "Recovery needs EC-RO"); - - /* AP-RO cases */ - ResetMocks(); - in_rw_retval = VBERROR_SIMULATED; - test_ssync(VBERROR_EC_REBOOT_TO_RO_REQUIRED, - VBNV_RECOVERY_EC_UNKNOWN_IMAGE, "Unknown EC image"); - - ResetMocks(); - shared->flags |= VBSD_LF_USE_RO_NORMAL; - mock_in_rw = 1; - test_ssync(VBERROR_EC_REBOOT_TO_RO_REQUIRED, - 0, "AP-RO needs EC-RO"); - - ResetMocks(); - shared->flags |= VBSD_LF_USE_RO_NORMAL; - test_ssync(0, 0, "AP-RO, EC-RO"); - TEST_EQ(ec_rw_protected, 1, " ec rw protected"); - TEST_EQ(ec_run_image, 0, " ec run image"); - - ResetMocks(); - shared->flags |= VBSD_LF_USE_RO_NORMAL; - run_retval = VBERROR_SIMULATED; - test_ssync(VBERROR_EC_REBOOT_TO_RO_REQUIRED, - VBNV_RECOVERY_EC_SOFTWARE_SYNC, "Stay in RO fail"); - - ResetMocks(); - shared->flags |= VBSD_LF_USE_RO_NORMAL; - protect_retval = VBERROR_SIMULATED; - test_ssync(VBERROR_SIMULATED, - VBNV_RECOVERY_EC_PROTECT, "Protect error"); - - /* No longer check for shutdown requested */ - ResetMocks(); - shared->flags |= VBSD_LF_USE_RO_NORMAL; - shutdown_request_calls_left = 0; - test_ssync(0, 0, "AP-RO shutdown requested"); - - /* Calculate hashes */ - ResetMocks(); - mock_ec_rw_hash_size = 0; - test_ssync(VBERROR_EC_REBOOT_TO_RO_REQUIRED, - VBNV_RECOVERY_EC_HASH_FAILED, "Bad EC hash"); - - ResetMocks(); - mock_ec_rw_hash_size = 16; - test_ssync(VBERROR_EC_REBOOT_TO_RO_REQUIRED, - VBNV_RECOVERY_EC_HASH_SIZE, "Bad EC hash size"); - - ResetMocks(); - want_ec_hash_size = 0; - test_ssync(VBERROR_EC_REBOOT_TO_RO_REQUIRED, - VBNV_RECOVERY_EC_EXPECTED_HASH, "Bad precalculated hash"); - - ResetMocks(); - want_ec_hash_size = 16; - test_ssync(VBERROR_EC_REBOOT_TO_RO_REQUIRED, - VBNV_RECOVERY_EC_HASH_SIZE, - "Hash size mismatch"); - - ResetMocks(); - want_ec_hash_size = 4; - mock_ec_rw_hash_size = 4; - test_ssync(0, 0, "Custom hash size"); - - /* Updates required */ - ResetMocks(); - mock_in_rw = 1; - mock_ec_rw_hash[0]++; - test_ssync(VBERROR_EC_REBOOT_TO_RO_REQUIRED, - 0, "Pending update needs reboot"); - - ResetMocks(); - mock_ec_rw_hash[0]++; - VbNvSet(VbApiKernelGetVnc(), 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"); - TEST_EQ(ec_rw_updated, 1, " ec rw updated"); - TEST_EQ(ec_ro_protected, 1, " ec ro protected"); - TEST_EQ(ec_ro_updated, 0, " ec ro updated"); - - ResetMocks(); - mock_ec_rw_hash[0]++; - mock_ec_ro_hash[0]++; - VbNvSet(VbApiKernelGetVnc(), 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"); - TEST_EQ(ec_rw_updated, 1, " ec rw updated"); - TEST_EQ(ec_ro_protected, 1, " ec ro protected"); - TEST_EQ(ec_ro_updated, 1, " ec ro updated"); - - ResetMocks(); - shared->flags |= VBSD_BOOT_FIRMWARE_WP_ENABLED; - VbNvSet(VbApiKernelGetVnc(), VBNV_TRY_RO_SYNC, 1); - mock_ec_rw_hash[0]++; - mock_ec_ro_hash[0]++; - test_ssync(0, 0, "WP enabled"); - TEST_EQ(ec_rw_protected, 1, " ec rw protected"); - TEST_EQ(ec_run_image, 1, " ec run image"); - TEST_EQ(ec_rw_updated, 1, " ec rw updated"); - TEST_EQ(ec_ro_protected, 1, " ec ro protected"); - TEST_EQ(ec_ro_updated, 0, " ec ro updated"); - - ResetMocks(); - VbNvSet(VbApiKernelGetVnc(), 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"); - TEST_EQ(ec_run_image, 1, " ec run image"); - TEST_EQ(ec_rw_updated, 0, " ec rw not updated"); - TEST_EQ(ec_ro_protected, 1, " ec ro protected"); - TEST_EQ(ec_ro_updated, 1, " ec ro updated"); - - ResetMocks(); - mock_ec_rw_hash[0]++; - mock_ec_ro_hash[0]++; - test_ssync(0, 0, "ro update not requested"); - TEST_EQ(ec_rw_protected, 1, " ec rw protected"); - TEST_EQ(ec_run_image, 1, " ec run image"); - TEST_EQ(ec_rw_updated, 1, " ec rw updated"); - TEST_EQ(ec_ro_protected, 1, " ec ro protected"); - TEST_EQ(ec_ro_updated, 0, " ec ro updated"); - - ResetMocks(); - mock_ec_rw_hash[0]++; - update_hash++; - test_ssync(VBERROR_EC_REBOOT_TO_RO_REQUIRED, - VBNV_RECOVERY_EC_UPDATE, "updated hash mismatch"); - TEST_EQ(ec_rw_protected, 0, " ec rw protected"); - TEST_EQ(ec_run_image, 0, " ec run image"); - TEST_EQ(ec_rw_updated, 1, " ec rw updated"); - TEST_EQ(ec_ro_protected, 0, " ec ro protected"); - TEST_EQ(ec_ro_updated, 0, " ec ro updated"); - - ResetMocks(); - mock_ec_rw_hash[0]++; - update_retval = VBERROR_EC_REBOOT_TO_RO_REQUIRED; - test_ssync(VBERROR_EC_REBOOT_TO_RO_REQUIRED, - 0, "Reboot after rw update"); - TEST_EQ(ec_rw_updated, 1, " ec rw updated"); - TEST_EQ(ec_ro_updated, 0, " ec rw updated"); - - ResetMocks(); - mock_ec_rw_hash[0]++; - update_retval = VBERROR_SIMULATED; - test_ssync(VBERROR_EC_REBOOT_TO_RO_REQUIRED, - VBNV_RECOVERY_EC_UPDATE, "Update failed"); - - ResetMocks(); - mock_ec_rw_hash[0]++; - shared->flags |= VBSD_EC_SLOW_UPDATE; - test_ssync(0, 0, "Slow update"); - TEST_EQ(screens_displayed[0], VB_SCREEN_WAIT, " wait screen"); - - /* RW cases, no update */ - ResetMocks(); - mock_in_rw = 1; - test_ssync(0, 0, "AP-RW, EC-RW"); - - ResetMocks(); - test_ssync(0, 0, "AP-RW, EC-RO -> EC-RW"); - TEST_EQ(ec_rw_protected, 1, " ec rw protected"); - TEST_EQ(ec_run_image, 1, " ec run image"); - TEST_EQ(ec_rw_updated, 0, " ec rw updated"); - TEST_EQ(ec_ro_protected, 1, " ec ro protected"); - TEST_EQ(ec_ro_updated, 0, " ec ro updated"); - - ResetMocks(); - run_retval = VBERROR_SIMULATED; - test_ssync(VBERROR_EC_REBOOT_TO_RO_REQUIRED, - VBNV_RECOVERY_EC_JUMP_RW, "Jump to RW fail"); - - ResetMocks(); - run_retval = VBERROR_EC_REBOOT_TO_RO_REQUIRED; - test_ssync(VBERROR_EC_REBOOT_TO_RO_REQUIRED, - 0, "Jump to RW fail because locked"); - - ResetMocks(); - protect_retval = VBERROR_SIMULATED; - test_ssync(VBERROR_SIMULATED, - VBNV_RECOVERY_EC_PROTECT, "Protect error"); - - /* No longer check for shutdown requested */ - ResetMocks(); - shutdown_request_calls_left = 0; - test_ssync(0, 0, - "AP-RW, EC-RO -> EC-RW shutdown requested"); - - ResetMocks(); - mock_in_rw = 1; - shutdown_request_calls_left = 0; - test_ssync(0, 0, "AP-RW shutdown requested"); -} - -int main(void) -{ - VbSoftwareSyncTest(); - - return gTestSuccess ? 0 : 255; -} diff --git a/tests/vboot_api_kernel4_tests.c b/tests/vboot_api_kernel4_tests.c index 401c61d5..7e4b47dd 100644 --- a/tests/vboot_api_kernel4_tests.c +++ b/tests/vboot_api_kernel4_tests.c @@ -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; } -- cgit v1.2.1