summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRandall Spangler <rspangler@chromium.org>2012-07-16 16:42:40 -0700
committerGerrit <chrome-bot@google.com>2012-07-16 18:23:28 -0700
commitd4faa060cc6445cf532e3f9c9cd785e0726f1b82 (patch)
tree2bef9a925ac8cbd622fb2d64b545d18e78a6a55e
parentcc36ff4073c13eb6dc755248f357883316962e94 (diff)
downloadvboot-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.h4
-rw-r--r--firmware/include/vboot_struct.h4
-rw-r--r--firmware/lib/vboot_api_init.c3
-rw-r--r--firmware/lib/vboot_api_kernel.c104
-rw-r--r--firmware/lib/vboot_display.c4
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: