summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaisuke Nojiri <dnojiri@chromium.org>2017-08-15 18:05:22 -0700
committerchrome-bot <chrome-bot@chromium.org>2017-09-08 20:04:33 -0700
commitf78d505e04051153d16df2a0adb9927a47d81bcd (patch)
treef1aca878acc9a31e59d8b4ea892316b2de182c04
parent35da86f58928bf3063b496d329e1903bdca09ecd (diff)
downloadvboot-f78d505e04051153d16df2a0adb9927a47d81bcd.tar.gz
Support EC early firmware selection
Chromebox ECs perform EFS: verifying firmware before the AP boots. This patch adds support for EC EFS. EC EFS uses two slots: one is active slot and one is used for update. AP is agnostic about which slot is active and which slot is for update. Instead, AP recognizes one slot as 'active' and the other as 'update' (or non active) slot. After update is successful, AP issues a cold reboot command to activate the new slot. BUG=b:65028930,b:65264494 BRANCH=none CQ-DEPEND=CL:616248 TEST=buildall. On Fizz, verify: 1. RW_B is old and updated by soft sync. RW_B is activated and executed after reboot. System continues to boot to OS. 2. RW_A is old and updated by soft sync. RW_A is activated and executed after reboot. System continues to boot to OS. Change-Id: I6ca7686eb28b0b548785cf2c02eca1b67018f469 Signed-off-by: Daisuke Nojiri <dnojiri@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/616346
-rw-r--r--firmware/include/vboot_api.h17
-rw-r--r--firmware/include/vboot_struct.h2
-rw-r--r--firmware/lib/ec_sync.c57
-rw-r--r--firmware/lib/ec_sync_all.c11
4 files changed, 59 insertions, 28 deletions
diff --git a/firmware/include/vboot_api.h b/firmware/include/vboot_api.h
index ccd65961..3df025e2 100644
--- a/firmware/include/vboot_api.h
+++ b/firmware/include/vboot_api.h
@@ -108,6 +108,8 @@ enum VbErrorPredefined_t {
VBERROR_NO_SOUND = 0x10018,
/* VbExBeep() can't make sound in the background */
VBERROR_NO_BACKGROUND_SOUND = 0x10019,
+ /* Need EC to reboot to read-only code to switch RW slot */
+ VBERROR_EC_REBOOT_TO_SWITCH_RW = 0x1001A,
/* Developer has requested a BIOS shell */
VBERROR_BIOS_SHELL_REQUESTED = 0x10020,
/* Need VGA and don't have it, or vice-versa */
@@ -303,12 +305,17 @@ typedef struct VbInitParams {
enum VbSelectFirmware_t {
/* Recovery mode */
VB_SELECT_FIRMWARE_RECOVERY = 0,
- /* Rewritable firmware A/B for normal or developer path */
+ /* DEPRECATED: Rewritable firmware A/B for normal or developer path */
VB_SELECT_FIRMWARE_A = 1,
VB_SELECT_FIRMWARE_B = 2,
/* Read only firmware for normal or developer path. */
VB_SELECT_FIRMWARE_READONLY = 3,
- VB_SELECT_FIRMWARE_COUNT,
+ /* Rewritable EC firmware currently set active */
+ VB_SELECT_FIRMWARE_EC_ACTIVE = 4,
+ /* Rewritable EC firmware currently not set active thus updatable */
+ VB_SELECT_FIRMWARE_EC_UPDATE = 5,
+ /* Keep this at the end */
+ VB_SELECT_FIRMWARE_COUNT,
};
/* Data only used by VbSelectFirmware() */
@@ -961,6 +968,12 @@ VbError_t VbExEcDisableJump(int devidx);
/**
* Read the SHA-256 hash of the selected EC image.
+ *
+ * @param devidx Device index. 0: EC, 1: PD.
+ * @param select Image to get hash of. RO or RW.
+ * @param hash Pointer to the hash.
+ * @param hash_size Pointer to the hash size.
+ * @return VBERROR_... error, VBERROR_SUCCESS on success.
*/
VbError_t VbExEcHashImage(int devidx, enum VbSelectFirmware_t select,
const uint8_t **hash, int *hash_size);
diff --git a/firmware/include/vboot_struct.h b/firmware/include/vboot_struct.h
index 08910d13..3f0d888c 100644
--- a/firmware/include/vboot_struct.h
+++ b/firmware/include/vboot_struct.h
@@ -241,6 +241,8 @@ typedef struct VbKernelPreambleHeader {
#define VBSD_OPROM_LOADED 0x00020000
/* Don't try for boot failures */
#define VBSD_NOFAIL_BOOT 0x00040000
+/* VbInit() was told that the EC firmware supports EFS */
+#define VBSD_EC_EFS 0x00080000
/*
* Supported flags by header version. It's ok to add new flags while keeping
diff --git a/firmware/lib/ec_sync.c b/firmware/lib/ec_sync.c
index 3f90ba5f..10ce5576 100644
--- a/firmware/lib/ec_sync.c
+++ b/firmware/lib/ec_sync.c
@@ -31,6 +31,9 @@
((select) == VB_SELECT_FIRMWARE_READONLY ? VB2_SD_FLAG_ECSYNC_EC_RO : \
((devidx) ? VB2_SD_FLAG_ECSYNC_PD_RW : VB2_SD_FLAG_ECSYNC_EC_RW))
+/* PD doesn't support RW A/B */
+#define RW_AB(devidx) ((devidx) ? 0 : VBSD_EC_EFS)
+
static void request_recovery(struct vb2_context *ctx, uint32_t recovery_request)
{
VB2_DEBUG("request_recovery(%u)\n", recovery_request);
@@ -73,6 +76,20 @@ static void print_hash(const uint8_t *hash, uint32_t hash_size,
VB2_DEBUG_RAW("\n");
}
+static const char *image_name_to_string(enum VbSelectFirmware_t select)
+{
+ switch (select) {
+ case VB_SELECT_FIRMWARE_READONLY:
+ return "RO";
+ case VB_SELECT_FIRMWARE_EC_ACTIVE:
+ return "RW(active)";
+ case VB_SELECT_FIRMWARE_EC_UPDATE:
+ return "RW(update)";
+ default:
+ return "UNKNOWN";
+ }
+}
+
/**
* Check if the hash of the EC code matches the expected hash.
*
@@ -95,8 +112,7 @@ static int check_ec_hash(struct vb2_context *ctx, int devidx,
request_recovery(ctx, VB2_RECOVERY_EC_HASH_FAILED);
return VB2_ERROR_EC_HASH_IMAGE;
}
- print_hash(ec_hash, ec_hash_size,
- select == VB_SELECT_FIRMWARE_READONLY ? "RO" : "RW");
+ print_hash(ec_hash, ec_hash_size, image_name_to_string(select));
/* Get expected EC hash. */
const uint8_t *hash = NULL;
@@ -135,8 +151,7 @@ static VbError_t update_ec(struct vb2_context *ctx, int devidx,
{
struct vb2_shared_data *sd = vb2_get_sd(ctx);
- VB2_DEBUG("updating %s...\n",
- select == VB_SELECT_FIRMWARE_READONLY ? "RO" : "RW");
+ VB2_DEBUG("Updating %s...\n", image_name_to_string(select));
/* Get expected EC image */
const uint8_t *want = NULL;
@@ -252,17 +267,24 @@ static VbError_t sync_one_ec(struct vb2_context *ctx, int devidx,
VbSharedDataHeader *shared =
(VbSharedDataHeader *)cparams->shared_data_blob;
struct vb2_shared_data *sd = vb2_get_sd(ctx);
- const enum VbSelectFirmware_t select_rw =
- shared->firmware_index ? VB_SELECT_FIRMWARE_B :
- VB_SELECT_FIRMWARE_A;
+ int is_rw_ab = shared->flags & RW_AB(devidx);
int rv;
- VB2_DEBUG("devidx=%d\n", devidx);
+ const enum VbSelectFirmware_t select_rw = is_rw_ab ?
+ VB_SELECT_FIRMWARE_EC_UPDATE :
+ VB_SELECT_FIRMWARE_EC_ACTIVE;
+ VB2_DEBUG("devidx=%d select_rw=%d\n", devidx, select_rw);
/* Update the RW Image */
- if (sd->flags & VB2_SD_FLAG_ECSYNC_RW) {
+ if (sd->flags & WHICH_EC(devidx, select_rw)) {
if (VB2_SUCCESS != update_ec(ctx, devidx, select_rw))
return VBERROR_EC_REBOOT_TO_RO_REQUIRED;
+ /* Updated successfully. Cold reboot to switch to the new RW.
+ * TODO: Switch slot and proceed if EC is still in RO. */
+ if (is_rw_ab) {
+ VB2_DEBUG("Rebooting to jump to new EC-RW\n");
+ return VBERROR_EC_REBOOT_TO_SWITCH_RW;
+ }
}
/* Tell EC to jump to its RW image */
@@ -374,13 +396,10 @@ VbError_t ec_sync_phase1(struct vb2_context *ctx, VbCommonParams *cparams)
if (sd->recovery_reason)
return VBERROR_SUCCESS;
- /* See if we need to update RW. Failures trigger recovery mode. */
- const enum VbSelectFirmware_t select_rw =
- shared->firmware_index ? VB_SELECT_FIRMWARE_B :
- VB_SELECT_FIRMWARE_A;
- if (check_ec_hash(ctx, 0, select_rw))
+ /* Check if we need to update RW. Failures trigger recovery mode. */
+ if (check_ec_hash(ctx, 0, VB_SELECT_FIRMWARE_EC_ACTIVE))
return VBERROR_EC_REBOOT_TO_RO_REQUIRED;
- if (do_pd_sync && check_ec_hash(ctx, 1, select_rw))
+ if (do_pd_sync && check_ec_hash(ctx, 1, VB_SELECT_FIRMWARE_EC_ACTIVE))
return VBERROR_EC_REBOOT_TO_RO_REQUIRED;
/*
* See if we need to update EC-RO (devidx=0).
@@ -399,12 +418,12 @@ VbError_t ec_sync_phase1(struct vb2_context *ctx, VbCommonParams *cparams)
* If we're in RW, we need to reboot back to RO because RW can't be
* updated while we're running it.
*
- * TODO: Technically this isn't true for ECs which don't execute from
- * flash. For example, if the EC loads code from SPI into RAM before
- * executing it.
+ * If EC supports RW-A/B slots, we can proceed but we need
+ * to jump to the new RW version later.
*/
if ((sd->flags & VB2_SD_FLAG_ECSYNC_RW) &&
- (sd->flags & VB2_SD_FLAG_ECSYNC_IN_RW)) {
+ (sd->flags & VB2_SD_FLAG_ECSYNC_IN_RW) &&
+ !(shared->flags & VBSD_EC_EFS)) {
return VBERROR_EC_REBOOT_TO_RO_REQUIRED;
}
diff --git a/firmware/lib/ec_sync_all.c b/firmware/lib/ec_sync_all.c
index aeec6079..1d9c7f71 100644
--- a/firmware/lib/ec_sync_all.c
+++ b/firmware/lib/ec_sync_all.c
@@ -28,7 +28,7 @@ VbError_t ec_sync_all(struct vb2_context *ctx, struct VbCommonParams *cparams)
if (rv)
return rv;
- /* Do EC sync phase 1; this determines if we need an update */
+ /* Phase 1; this determines if we need an update */
VbError_t phase1_rv = ec_sync_phase1(ctx, cparams);
int need_wait_screen = ec_will_update_slowly(ctx, cparams) ||
(fw_update == VB_AUX_FW_SLOW_UPDATE);
@@ -60,16 +60,13 @@ VbError_t ec_sync_all(struct vb2_context *ctx, struct VbCommonParams *cparams)
VbDisplayScreen(ctx, cparams, VB_SCREEN_WAIT, 0);
}
- /*
- * Do EC sync phase 2; this applies the update and/or jumps to the
- * correct EC image.
- */
+ /* Phase 2; Applies update and/or jumps to the correct EC image */
rv = ec_sync_phase2(ctx, cparams);
if (rv)
return rv;
/*
- * Do software sync for devices tunneled throught the EC.
+ * Do software sync for devices tunneled through the EC.
*/
rv = VbExUpdateAuxFw();
if (rv)
@@ -91,7 +88,7 @@ VbError_t ec_sync_all(struct vb2_context *ctx, struct VbCommonParams *cparams)
return VBERROR_VGA_OPROM_MISMATCH;
}
- /* Do EC sync phase 3; this completes sync and handles battery cutoff */
+ /* Phase 3; Completes sync and handles battery cutoff */
rv = ec_sync_phase3(ctx, cparams);
if (rv)
return rv;