summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaisuke Nojiri <dnojiri@chromium.org>2017-06-29 12:46:22 -0700
committerchrome-bot <chrome-bot@chromium.org>2017-07-13 19:45:57 -0700
commit7630636a0fe8ceb2dbba2b175564a17900d175cf (patch)
treedf62a9bfb8f965442f8a3a975794f70cd330e87e
parent34fed775b65063fb519d11deb11eb1feac7a8ecc (diff)
downloadchrome-ec-7630636a0fe8ceb2dbba2b175564a17900d175cf.tar.gz
vboot: Verify and jump to RW_A or RW_B
This patch gives EC the capability of verifying and jumping to RW_A or RW_B. EC tries the slot stored in a persistent storage (e.g. BBRAM). If verification fails due to invalid contents, EC tries the other slot. AP's expectation and its reaction to the state of the slots and the currently running image are summarized below. Since the system is still unlocked (CONFIG_SYSTEM_UNLOCKED), EC won't try to verify or jump to RW yet. | AP expects X ----------+--------------------------- SLOT_A=X | proceed SLOT_B=X | proceed ----------+--------------------------- SLOT_A=X' | reboot to B SLOT_B=X | proceed ----------+--------------------------- SLOT_A=X | proceed SLOT_B=X' | reboot to A ----------+--------------------------- SLOT_A=X' | write X to B, reboot to B SLOT_B=X' | write X to A, reboot to A BUG=b:38462249 BRANCH=none TEST=Lock the system and boot Fizz on barrel-jack and type-c. Change-Id: I51e3abd4d9af44ab3d531561cb9bfa2e8d775f6a Signed-off-by: Daisuke Nojiri <dnojiri@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/556286 Reviewed-by: Randall Spangler <rspangler@chromium.org>
-rw-r--r--board/fizz/ec.tasklist3
-rw-r--r--common/vboot.c184
-rw-r--r--include/rwsig.h8
3 files changed, 180 insertions, 15 deletions
diff --git a/board/fizz/ec.tasklist b/board/fizz/ec.tasklist
index 06c39984a3..82a214f38d 100644
--- a/board/fizz/ec.tasklist
+++ b/board/fizz/ec.tasklist
@@ -22,7 +22,8 @@
#define CONFIG_TASK_LIST \
TASK_ALWAYS(HOOKS, hook_task, NULL, LARGER_TASK_STACK_SIZE) \
- TASK_NOTEST(CHIPSET, chipset_task, NULL, LARGER_TASK_STACK_SIZE) \
+ /* Larger stack for RW verification (i.e. sha256, rsa) */ \
+ TASK_NOTEST(CHIPSET, chipset_task, NULL, 2048) \
TASK_NOTEST(PDCMD, pd_command_task, NULL, TASK_STACK_SIZE) \
TASK_ALWAYS(HOSTCMD, host_command_task, NULL, LARGER_TASK_STACK_SIZE) \
TASK_ALWAYS(CONSOLE, console_task, NULL, LARGER_TASK_STACK_SIZE) \
diff --git a/common/vboot.c b/common/vboot.c
index 6f16d05380..1e92572c5b 100644
--- a/common/vboot.c
+++ b/common/vboot.c
@@ -4,7 +4,7 @@
*/
/*
- * Implementation of EC's boot verification
+ * Verify and jump to a RW image if power supply is not sufficient.
*/
#include "battery.h"
@@ -12,10 +12,14 @@
#include "chipset.h"
#include "console.h"
#include "host_command.h"
+#include "rsa.h"
#include "rwsig.h"
+#include "sha256.h"
+#include "shared_mem.h"
#include "system.h"
#include "usb_pd.h"
#include "vboot.h"
+#include "vb21_struct.h"
#define CPRINTS(format, args...) cprints(CC_VBOOT, format, ## args)
@@ -31,7 +35,11 @@ static int has_matrix_keyboard(void)
static int is_vboot_ec_supported(void)
{
+#ifdef CONFIG_VBOOT_EC
+ return 1;
+#else
return 0;
+#endif
}
static int is_low_power_ap_boot_supported(void)
@@ -39,24 +47,179 @@ static int is_low_power_ap_boot_supported(void)
return 0;
}
+static int is_vb21_packed_key_valid(const struct vb21_packed_key *key)
+{
+ if (key->c.magic != VB21_MAGIC_PACKED_KEY)
+ return EC_ERROR_INVAL;
+ if (key->key_size != sizeof(struct rsa_public_key))
+ return EC_ERROR_INVAL;
+ return EC_SUCCESS;
+}
+
+static int is_vb21_signature_valid(const struct vb21_signature *sig,
+ const struct vb21_packed_key *key)
+{
+ if (sig->c.magic != VB21_MAGIC_SIGNATURE)
+ return EC_ERROR_INVAL;
+ if (sig->sig_size != RSANUMBYTES)
+ return EC_ERROR_INVAL;
+ if (key->sig_alg != sig->sig_alg)
+ return EC_ERROR_INVAL;
+ if (key->hash_alg != sig->hash_alg)
+ return EC_ERROR_INVAL;
+ /* Sanity check signature offset and data size. */
+ if (sig->sig_offset < sizeof(*sig))
+ return EC_ERROR_INVAL;
+ if (sig->sig_offset + RSANUMBYTES > CONFIG_RW_SIG_SIZE)
+ return EC_ERROR_INVAL;
+ if (sig->data_size > CONFIG_RW_SIZE - CONFIG_RW_SIG_SIZE)
+ return EC_ERROR_INVAL;
+ return EC_SUCCESS;
+}
+
+static int is_padding_valid(const uint8_t *data, unsigned int start, int len)
+{
+ const uint32_t *data32 = (const uint32_t *)data;
+ int i;
+
+ if (len < 0)
+ return EC_ERROR_INVAL;
+
+ if ((start % 4) != 0 || (len % 4) != 0)
+ return EC_ERROR_INVAL;
+
+ for (i = start/4; i < len/4; i++) {
+ if (data32[i] != 0xffffffff)
+ return EC_ERROR_INVAL;
+ }
+
+ return EC_SUCCESS;
+}
+
+static int vboot_verify(const uint8_t *data, int len,
+ const struct rsa_public_key *key, const uint8_t *sig)
+{
+ struct sha256_ctx ctx;
+ uint8_t *hash;
+ uint32_t *workbuf;
+ int err = EC_SUCCESS;
+
+ if (shared_mem_acquire(3 * RSANUMBYTES, (char **)&workbuf)) {
+ CPRINTS("Failed to allocate memory");
+ return EC_ERROR_UNKNOWN;
+ }
+
+ /* Compute hash of the RW firmware */
+ SHA256_init(&ctx);
+ SHA256_update(&ctx, data, len);
+ hash = SHA256_final(&ctx);
+
+ /* Verify the data */
+ if (rsa_verify(key, sig, hash, workbuf) != 1) {
+ CPRINTS("Invalid data");
+ err = EC_ERROR_INVAL;
+ }
+
+ shared_mem_release(workbuf);
+
+ return err;
+}
+
static int verify_slot(int slot)
{
- /* TODO: Handle slot A and B */
- CPRINTS("Verifying S%d", slot);
- return rwsig_check_signature();
+ const uint8_t *data;
+ const struct vb21_packed_key *vb21_key;
+ const struct vb21_signature *vb21_sig;
+ const struct rsa_public_key *key;
+ const uint8_t *sig;
+ int len;
+ int rv = EC_ERROR_INVAL;
+
+ CPRINTS("Verifying RW_%c", slot == VBOOT_EC_SLOT_A ? 'A' : 'B');
+
+ vb21_key = (const struct vb21_packed_key *)(
+ CONFIG_MAPPED_STORAGE_BASE +
+ CONFIG_EC_PROTECTED_STORAGE_OFF +
+ CONFIG_RO_PUBKEY_STORAGE_OFF);
+ if (is_vb21_packed_key_valid(vb21_key)) {
+ CPRINTS("Invalid key");
+ goto exit;
+ }
+ key = (const struct rsa_public_key *)
+ ((const uint8_t *)vb21_key + vb21_key->key_offset);
+
+ if (slot == VBOOT_EC_SLOT_A) {
+ data = (const uint8_t *)(CONFIG_MAPPED_STORAGE_BASE +
+ CONFIG_EC_WRITABLE_STORAGE_OFF +
+ CONFIG_RW_A_STORAGE_OFF);
+ vb21_sig = (const struct vb21_signature *)(
+ CONFIG_MAPPED_STORAGE_BASE +
+ CONFIG_EC_WRITABLE_STORAGE_OFF +
+ CONFIG_RW_A_SIGN_STORAGE_OFF);
+ } else {
+ data = (const uint8_t *)(CONFIG_MAPPED_STORAGE_BASE +
+ CONFIG_EC_WRITABLE_STORAGE_OFF +
+ CONFIG_RW_B_STORAGE_OFF);
+ vb21_sig = (const struct vb21_signature *)(
+ CONFIG_MAPPED_STORAGE_BASE +
+ CONFIG_EC_WRITABLE_STORAGE_OFF +
+ CONFIG_RW_B_SIGN_STORAGE_OFF);
+ }
+
+ if (is_vb21_signature_valid(vb21_sig, vb21_key)) {
+ CPRINTS("Invalid signature");
+ goto exit;
+ }
+ sig = (const uint8_t *)vb21_sig + vb21_sig->sig_offset;
+ len = vb21_sig->data_size;
+
+ if (is_padding_valid(data, len,
+ CONFIG_RW_SIZE - len - CONFIG_RW_SIG_SIZE)) {
+ CPRINTS("Invalid padding");
+ goto exit;
+ }
+
+ if (vboot_verify(data, len, key, sig)) {
+ CPRINTS("Invalid data");
+ goto exit;
+ }
+
+ rv = EC_SUCCESS;
+
+exit:
+ return rv;
}
-static int verify_rw(void)
+static int verify_and_jump(void)
{
uint8_t slot;
+ int rv;
- /* 1. Read BBRAM to decide which slot to verify */
+ /* 1. Decide which slot to try */
if (system_get_bbram(SYSTEM_BBRAM_IDX_TRY_SLOT, &slot)) {
CPRINTS("Failed to read try slot");
slot = VBOOT_EC_SLOT_A;
}
+
/* 2. Verify the slot */
- return verify_slot(slot);
+ rv = verify_slot(slot);
+ if (rv) {
+ if (rv != EC_ERROR_INVAL)
+ /* Unknown error. The other slot isn't worth trying. */
+ return rv;
+ /* Verification error. The other slot is worth trying. */
+ slot = 1 - slot;
+ if (verify_slot(slot))
+ /* Both slots failed */
+ return rv;
+ /* Proceed with the other slot. AP will help us fix it. */
+ }
+
+ /* 3. Jump (and reboot) */
+ system_run_image_copy(slot == VBOOT_EC_SLOT_A ?
+ SYSTEM_IMAGE_RW : SYSTEM_IMAGE_RW_B);
+
+ return EC_ERROR_UNKNOWN;
}
/* Request more power: charging battery or more powerful AC adapter */
@@ -109,10 +272,9 @@ void vboot_ec(void)
return;
}
- if (verify_rw())
- /* Jump (and reboot) */
- rwsig_jump_now();
+ /* If successful, this won't return. */
+ verify_and_jump();
- /* Failed to verify RW. Need recovery. */
+ /* Failed to jump. Need recovery. */
request_recovery();
}
diff --git a/include/rwsig.h b/include/rwsig.h
index 0cb98c83f0..813fa06f9c 100644
--- a/include/rwsig.h
+++ b/include/rwsig.h
@@ -77,12 +77,14 @@ void rwsig_jump_now(void);
#endif /* ! CONFIG_RO_PUBKEY_SIZE */
#ifndef CONFIG_RO_PUBKEY_ADDR
#ifdef CONFIG_RWSIG_TYPE_RWSIG
+#define CONFIG_RO_PUBKEY_STORAGE_OFF (CONFIG_RO_STORAGE_OFF \
+ + CONFIG_RO_SIZE \
+ - CONFIG_RO_PUBKEY_SIZE)
+
/* The pubkey resides at the end of the RO image */
#define CONFIG_RO_PUBKEY_ADDR (CONFIG_PROGRAM_MEMORY_BASE \
+ CONFIG_EC_PROTECTED_STORAGE_OFF \
- + CONFIG_RO_STORAGE_OFF \
- + CONFIG_RO_SIZE \
- - CONFIG_RO_PUBKEY_SIZE)
+ + CONFIG_RO_PUBKEY_STORAGE_OFF)
#else
/*
* usbpd1 type assumes pubkey location at the end of first half of flash,