diff options
author | Randall Spangler <rspangler@chromium.org> | 2012-07-16 16:42:40 -0700 |
---|---|---|
committer | Gerrit <chrome-bot@google.com> | 2012-07-16 18:23:28 -0700 |
commit | d4faa060cc6445cf532e3f9c9cd785e0726f1b82 (patch) | |
tree | 2bef9a925ac8cbd622fb2d64b545d18e78a6a55e | |
parent | cc36ff4073c13eb6dc755248f357883316962e94 (diff) | |
download | vboot-d4faa060cc6445cf532e3f9c9cd785e0726f1b82.tar.gz |
Partial EC software sync implementation
Doesn't check the EC hash, but does jump to the correct image, for now
assuming the hash is good.
BUG=chrome-os-partner:11087
TEST=manual
- Power+refresh. System boots. EC is in RO (verify via 'ectool version')
- Create a BIOS signed *without* RO-normal.
- Power+refresh. System boots. EC ends up in A.
- ectool eventgetb. Event 0x2000 IS present, indicating EC has rebooted
- ectool eventclearb -1
- Power button to shut down, then power button to power back on.
- ectool eventgetb. Event 0x2000 is NOT present.
- crossystem recovery_request=123 && reboot. System reboots to recovery mode
and EC is in read-only (verify via EC console 'sysinfo')
- Power off and on. System boots. EC ends up in A again.
Change-Id: I39682d1bf7215c62a4b20613d029e78194b98826
Signed-off-by: Randall Spangler <rspangler@chromium.org>
Reviewed-on: https://gerrit.chromium.org/gerrit/27574
Reviewed-by: Bill Richardson <wfrichar@chromium.org>
-rw-r--r-- | firmware/include/vboot_nvstorage.h | 4 | ||||
-rw-r--r-- | firmware/include/vboot_struct.h | 4 | ||||
-rw-r--r-- | firmware/lib/vboot_api_init.c | 3 | ||||
-rw-r--r-- | firmware/lib/vboot_api_kernel.c | 104 | ||||
-rw-r--r-- | firmware/lib/vboot_display.c | 4 |
5 files changed, 118 insertions, 1 deletions
diff --git a/firmware/include/vboot_nvstorage.h b/firmware/include/vboot_nvstorage.h index 845a48a9..2b80cafc 100644 --- a/firmware/include/vboot_nvstorage.h +++ b/firmware/include/vboot_nvstorage.h @@ -97,6 +97,10 @@ typedef enum VbNvParam { /* Recovery mode TPM initialization requires a system reboot. The system was * already in recovery mode for some other reason when this happened. */ #define VBNV_RECOVERY_RO_TPM_REBOOT 0x21 +/* Other EC software sync error */ +#define VBNV_RECOVERY_EC_SOFTWARE_SYNC 0x22 +/* Unable to determine active EC image */ +#define VBNV_RECOVERY_EC_UNKNOWN_IMAGE 0x23 /* Unspecified/unknown error in read-only firmware */ #define VBNV_RECOVERY_RO_UNSPECIFIED 0x3F /* User manually requested recovery by pressing a key at developer diff --git a/firmware/include/vboot_struct.h b/firmware/include/vboot_struct.h index d894e23d..7000f6e8 100644 --- a/firmware/include/vboot_struct.h +++ b/firmware/include/vboot_struct.h @@ -231,8 +231,10 @@ typedef struct VbKernelPreambleHeader { #define VBSD_BOOT_S3_RESUME 0x00000100 /* Read-only firmware supports the normal/developer code path */ #define VBSD_BOOT_RO_NORMAL_SUPPORT 0x00000200 -/* VbInit was told that the system has a virtual dev-switch */ +/* VbInit() was told that the system has a virtual dev-switch */ #define VBSD_HONOR_VIRT_DEV_SWITCH 0x00000400 +/* VbInit() was told the system supports EC software sync */ +#define VBSD_EC_SOFTWARE_SYNC 0x00000800 /* Result codes for VbSharedDataHeader.check_fw_a_result (and b_result) */ #define VBSD_LF_CHECK_NOT_DONE 0 diff --git a/firmware/lib/vboot_api_init.c b/firmware/lib/vboot_api_init.c index 229849e4..dbeb1e66 100644 --- a/firmware/lib/vboot_api_init.c +++ b/firmware/lib/vboot_api_init.c @@ -49,6 +49,7 @@ VbError_t VbInit(VbCommonParams* cparams, VbInitParams* iparams) { shared->timer_vb_init_enter = VbExGetTimer(); /* Copy some boot switch flags */ + /* TODO: in next refactor, just save in/out flags in VbSharedData */ shared->flags = 0; if (iparams->flags & VB_INIT_FLAG_REC_BUTTON_PRESSED) shared->flags |= VBSD_BOOT_REC_SWITCH_ON; @@ -58,6 +59,8 @@ VbError_t VbInit(VbCommonParams* cparams, VbInitParams* iparams) { shared->flags |= VBSD_BOOT_S3_RESUME; if (iparams->flags & VB_INIT_FLAG_RO_NORMAL_SUPPORT) shared->flags |= VBSD_BOOT_RO_NORMAL_SUPPORT; + if (iparams->flags & VB_INIT_FLAG_EC_SOFTWARE_SYNC) + shared->flags |= VBSD_EC_SOFTWARE_SYNC; is_s3_resume = (iparams->flags & VB_INIT_FLAG_S3_RESUME ? 1 : 0); diff --git a/firmware/lib/vboot_api_kernel.c b/firmware/lib/vboot_api_kernel.c index df9f57ce..a17c0243 100644 --- a/firmware/lib/vboot_api_kernel.c +++ b/firmware/lib/vboot_api_kernel.c @@ -348,6 +348,103 @@ VbError_t VbBootRecovery(VbCommonParams* cparams, LoadKernelParams* p) { } +VbError_t VbEcSoftwareSync(VbSharedDataHeader *shared) { + int in_rw = 0; + int rv = VbExEcRunningRW(&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 + * preseve 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() - VbEcSoftwareSync() 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 = VbExEcProtectRW(); + if (rv != VBERROR_SUCCESS) { + VBDEBUG(("VbEcSoftwareSync() - VbExEcProtectRW() returned %d\n", rv)); + VbSetRecoveryRequest(VBNV_RECOVERY_EC_SOFTWARE_SYNC); + return VBERROR_EC_REBOOT_TO_RO_REQUIRED; + } + + rv = VbExEcStayInRO(); + if (rv != VBERROR_SUCCESS) { + VBDEBUG(("VbEcSoftwareSync() - VbExEcStayInRO() 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")); + + /* TODO: If EC-RW wasn't protected when we started, then this boot was + * simply to verify the EC. Shut down instead of continuing. */ + + return VBERROR_SUCCESS; + } + + /* TODO: verify EC-RW hash vs. expected code */ + + if (in_rw) { + /* TODO: if hash doesn't verify, reboot EC so we can reflash it + * with the expected code. */ + VBDEBUG(("VbEcSoftwareSync() in RW; EC-RW\n")); + return VBERROR_SUCCESS; + + } else { + /* TODO: if hash doesn't verify, reflash it with expected code. */ + + /* Protect EC-RW flash */ + rv = VbExEcProtectRW(); + if (rv != VBERROR_SUCCESS) { + VBDEBUG(("VbEcSoftwareSync() - VbExEcProtectRW() returned %d\n", rv)); + VbSetRecoveryRequest(VBNV_RECOVERY_EC_SOFTWARE_SYNC); + return VBERROR_EC_REBOOT_TO_RO_REQUIRED; + } + + /* Tell EC to jump to its RW code */ + VBDEBUG(("VbEcSoftwareSync() jumping to EC-RW\n")); + rv = VbExEcJumpToRW(); + if (rv != VBERROR_SUCCESS) { + VBDEBUG(("VbEcSoftwareSync() - VbExEcJumpToRW() returned %d\n", rv)); + VbSetRecoveryRequest(VBNV_RECOVERY_EC_SOFTWARE_SYNC); + return VBERROR_EC_REBOOT_TO_RO_REQUIRED; + } + + /* TODO: If there was no wake event from the EC (such as power button or + * lid-open), shut down. The AP was powered on simply to verify the EC. */ + VBDEBUG(("VbEcSoftwareSync() in RW; done jumping to EC-RW\n")); + return VBERROR_SUCCESS; + } +} + + VbError_t VbSelectAndLoadKernel(VbCommonParams* cparams, VbSelectAndLoadKernelParams* kparams) { VbSharedDataHeader* shared = (VbSharedDataHeader*)cparams->shared_data_blob; @@ -368,6 +465,13 @@ VbError_t VbSelectAndLoadKernel(VbCommonParams* cparams, kparams->bootloader_size = 0; Memset(kparams->partition_guid, 0, sizeof(kparams->partition_guid)); + /* Do EC software sync if necessary */ + if (shared->flags & VBSD_EC_SOFTWARE_SYNC) { + retval = VbEcSoftwareSync(shared); + if (retval != VBERROR_SUCCESS) + goto VbSelectAndLoadKernel_exit; + } + /* Read the kernel version from the TPM. Ignore errors in recovery mode. */ tpm_status = RollbackKernelRead(&shared->kernel_version_tpm); if (0 != tpm_status) { diff --git a/firmware/lib/vboot_display.c b/firmware/lib/vboot_display.c index e48623b5..ffaa7071 100644 --- a/firmware/lib/vboot_display.c +++ b/firmware/lib/vboot_display.c @@ -443,6 +443,10 @@ static const char *RecoveryReasonString(uint8_t code) { return "Firmware problem outside of verified boot"; case VBNV_RECOVERY_RO_TPM_REBOOT: return "TPM requires a system reboot (should be transient)"; + case VBNV_RECOVERY_EC_SOFTWARE_SYNC: + return "EC software sync error"; + case VBNV_RECOVERY_EC_UNKNOWN_IMAGE: + return "Unable to determine active EC image"; case VBNV_RECOVERY_RO_UNSPECIFIED: return "Unspecified/unknown error in RO firmware"; case VBNV_RECOVERY_RW_DEV_SCREEN: |