summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaisuke Nojiri <dnojiri@chromium.org>2019-10-21 10:07:48 -0700
committerCommit Bot <commit-bot@chromium.org>2020-02-09 08:31:43 +0000
commit37151225f4d791204a75173eec64ebfdaf2cbdec (patch)
treea9680e1fd0897c1ea6e0461055760c42944fdee7
parenta4f7c8ef7c0b9b3206f67299414c2cbd2a5fe84a (diff)
downloadchrome-ec-37151225f4d791204a75173eec64ebfdaf2cbdec.tar.gz
EFS2: Implement Early Firmware Selection ver.2
EFS v1 allowed Chromeboxes to verify RW without AP. EFS v2 will bring the benefts to Chromebooks, which are: - Reduce RO dependency and presence. Allow more code to be updated in the fields. - Remove jumptag and workarounds needed for late sysjump. Major imporvements over v1 are: - No A/B slot required. - No signature in RW or public key in RO. - Rollback-attack protection. - Verifies only RW being used instead of whole RW section. For battery-equipped devices, additional benefts are: - Immediate boot on drained battery. - Support recovery mode regardless of battery condition. - Faster charge in S5/G3. EC-Cr50 communication is based on the shared UART (go/ec-cr50-comm). EFS2 is documented in go/ec-efs2. Signed-off-by: Daisuke Nojiri <dnojiri@chromium.org> BUG=chromium:1045217,chromium:141143112 BRANCH=none TEST=Boot Helios in NORMAL/NO_BOOT/NO_BOOT_RECOVERY/RECOVERY mode. TEST=Wake up EC from hibernate. TEST=Make EC assert PACKET_MODE to wake up Cr50 from deepsleep. Change-Id: I98a4fe1ecc59d106810a75daec3c424f953ff880 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2015357 Reviewed-by: Daisuke Nojiri <dnojiri@chromium.org> Commit-Queue: Daisuke Nojiri <dnojiri@chromium.org> Tested-by: Daisuke Nojiri <dnojiri@chromium.org> Auto-Submit: Daisuke Nojiri <dnojiri@chromium.org>
-rw-r--r--board/endeavour/board.h2
-rw-r--r--board/endeavour/led.c4
-rw-r--r--board/puff/board.h2
-rw-r--r--board/puff/led.c4
-rw-r--r--common/build.mk1
-rw-r--r--common/main.c5
-rw-r--r--common/system.c2
-rw-r--r--common/usb_common.c6
-rw-r--r--common/usb_pd_protocol.c9
-rw-r--r--common/vboot/efs2.c297
-rw-r--r--common/vboot/vboot.c44
-rw-r--r--common/vboot_hash.c89
-rw-r--r--include/config.h8
-rw-r--r--include/vboot.h116
-rw-r--r--include/vboot_hash.h17
-rw-r--r--power/intel_x86.c2
-rw-r--r--power/skylake.c3
17 files changed, 539 insertions, 72 deletions
diff --git a/board/endeavour/board.h b/board/endeavour/board.h
index a37b3ab947..989ad69e78 100644
--- a/board/endeavour/board.h
+++ b/board/endeavour/board.h
@@ -148,7 +148,7 @@ enum OEM_ID {
/* Board specific handlers */
void led_alert(int enable);
-void led_critical(void);
+void show_critical_error(void);
#endif /* !__ASSEMBLER__ */
diff --git a/board/endeavour/led.c b/board/endeavour/led.c
index f6d270c25b..5133b65f74 100644
--- a/board/endeavour/led.c
+++ b/board/endeavour/led.c
@@ -174,7 +174,7 @@ void led_alert(int enable)
}
}
-void led_critical(void)
+void show_critical_error(void)
{
hook_call_deferred(&led_tick_data, -1);
if (led_auto_control_is_enabled(EC_LED_ID_POWER_LED))
@@ -202,7 +202,7 @@ static int command_led(int argc, char **argv)
} else if (!strcasecmp(argv[1], "alert")) {
led_alert(1);
} else if (!strcasecmp(argv[1], "crit")) {
- led_critical();
+ show_critical_error();
} else {
return EC_ERROR_PARAM1;
}
diff --git a/board/puff/board.h b/board/puff/board.h
index 7d0fdbaefc..0c69c58eef 100644
--- a/board/puff/board.h
+++ b/board/puff/board.h
@@ -264,7 +264,7 @@ enum temp_sensor_id {
void board_reset_pd_mcu(void);
void board_set_tcpc_power_mode(int port, int mode);
void led_alert(int enable);
-void led_critical(void);
+void show_critical_error(void);
#endif /* !__ASSEMBLER__ */
diff --git a/board/puff/led.c b/board/puff/led.c
index 49dfbce935..56f0914872 100644
--- a/board/puff/led.c
+++ b/board/puff/led.c
@@ -179,7 +179,7 @@ void led_alert(int enable)
}
}
-void led_critical(void)
+void show_critical_error(void)
{
hook_call_deferred(&led_tick_data, -1);
if (led_auto_control_is_enabled(EC_LED_ID_POWER_LED))
@@ -207,7 +207,7 @@ static int command_led(int argc, char **argv)
} else if (!strcasecmp(argv[1], "alert")) {
led_alert(1);
} else if (!strcasecmp(argv[1], "crit")) {
- led_critical();
+ show_critical_error();
} else {
return EC_ERROR_PARAM1;
}
diff --git a/common/build.mk b/common/build.mk
index cc45be50c0..26294af9db 100644
--- a/common/build.mk
+++ b/common/build.mk
@@ -155,6 +155,7 @@ common-$(CONFIG_USB_PD_TCPC)+=usb_pd_tcpc.o
common-$(CONFIG_USB_UPDATE)+=usb_update.o update_fw.o
common-$(CONFIG_USBC_PPC)+=usbc_ppc.o
common-$(CONFIG_VBOOT_EFS)+=vboot/vboot.o
+common-$(CONFIG_VBOOT_EFS2)+=vboot/efs2.o
common-$(CONFIG_VBOOT_HASH)+=sha256.o vboot_hash.o
common-$(CONFIG_VOLUME_BUTTONS)+=button.o
common-$(CONFIG_VSTORE)+=vstore.o
diff --git a/common/main.c b/common/main.c
index 5bbe6f8a55..bc67ea62b7 100644
--- a/common/main.c
+++ b/common/main.c
@@ -195,12 +195,13 @@ test_mockable __keep int main(void)
button_init();
#endif /* defined(CONFIG_DEDICATED_RECOVERY_BUTTON | CONFIG_VOLUME_BUTTONS) */
-#if defined(CONFIG_VBOOT_EFS)
+#if defined(CONFIG_VBOOT_EFS) || defined(CONFIG_VBOOT_EFS2)
/*
* Execute PMIC reset in case we're here after watchdog reset to unwedge
* AP. This has to be done here because vboot_main may jump to RW.
*/
- chipset_handle_reboot();
+ if (IS_ENABLED(CONFIG_CHIPSET_HAS_PLATFORM_PMIC_RESET))
+ chipset_handle_reboot();
/*
* For RO, it behaves as follows:
* In recovery, it enables PD communication and returns.
diff --git a/common/system.c b/common/system.c
index 61ef4882fc..5d07bfcf90 100644
--- a/common/system.c
+++ b/common/system.c
@@ -612,7 +612,7 @@ enum system_image_copy_t system_get_active_copy(void)
enum system_image_copy_t system_get_update_copy(void)
{
-#ifdef CONFIG_VBOOT_EFS
+#ifdef CONFIG_VBOOT_EFS /* Not needed for EFS2, which is single-slot. */
return system_get_active_copy() == SYSTEM_IMAGE_RW_A ?
SYSTEM_IMAGE_RW_B : SYSTEM_IMAGE_RW_A;
#else
diff --git a/common/usb_common.c b/common/usb_common.c
index 708d06404d..ff6d170a41 100644
--- a/common/usb_common.c
+++ b/common/usb_common.c
@@ -15,6 +15,7 @@
#include "console.h"
#include "ec_commands.h"
#include "hooks.h"
+#include "stdbool.h"
#include "host_command.h"
#include "system.h"
#include "task.h"
@@ -767,3 +768,8 @@ __overridable int pd_custom_vdm(int port, int cnt, uint32_t *payload,
return 0;
}
+
+__overridable bool vboot_allow_usb_pd(void)
+{
+ return false;
+}
diff --git a/common/usb_pd_protocol.c b/common/usb_pd_protocol.c
index 39154f3fe0..fea9d248a4 100644
--- a/common/usb_pd_protocol.c
+++ b/common/usb_pd_protocol.c
@@ -2813,13 +2813,10 @@ static void pd_init_tasks(void)
#if defined(CONFIG_USB_PD_COMM_DISABLED)
enable = 0;
#elif defined(CONFIG_USB_PD_COMM_LOCKED)
- /* Disable PD communication at init if we're in RO and locked. */
- if (!system_is_in_rw() && system_is_locked())
+ /* Disable PD communication if we're in RO, WP is enabled, and EFS
+ * didn't register NO_BOOT. */
+ if (!system_is_in_rw() && system_is_locked() && !vboot_allow_usb_pd())
enable = 0;
-#ifdef CONFIG_VBOOT_EFS
- if (vboot_need_pd_comm())
- enable = 1;
-#endif
#endif
for (i = 0; i < board_get_usb_pd_port_count(); i++)
pd_comm_enabled[i] = enable;
diff --git a/common/vboot/efs2.c b/common/vboot/efs2.c
new file mode 100644
index 0000000000..6cc2bc4692
--- /dev/null
+++ b/common/vboot/efs2.c
@@ -0,0 +1,297 @@
+/* Copyright 2020 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.
+ */
+
+/*
+ * Early Firmware Selection ver.2.
+ *
+ * Verify and jump to a RW image. Register boot mode to Cr50.
+ */
+
+#include "battery.h"
+#include "chipset.h"
+#include "clock.h"
+#include "compile_time_macros.h"
+#include "console.h"
+#include "crc8.h"
+#include "flash.h"
+#include "sha256.h"
+#include "system.h"
+#include "task.h"
+#include "usb_pd.h"
+#include "uart.h"
+#include "vboot.h"
+#include "vboot_hash.h"
+
+#define CPRINTS(format, args...) cprints(CC_VBOOT,"VB " format, ## args)
+#define CPRINTF(format, args...) cprintf(CC_VBOOT,"VB " format, ## args)
+
+static const char *boot_mode_to_string(uint8_t mode)
+{
+ static const char *boot_mode_str[] = {
+ [BOOT_MODE_NORMAL] = "NORMAL",
+ [BOOT_MODE_NO_BOOT] = "NO_BOOT",
+ };
+ if (mode < ARRAY_SIZE(boot_mode_str))
+ return boot_mode_str[mode];
+ return "UNDEF";
+}
+
+/*
+ * Check whether the session has successfully ended or not. ERR_TIMEOUT is
+ * excluded because it's an internal error produced by EC itself.
+ */
+static bool is_valid_cr50_response(enum cr50_comm_err code)
+{
+ return code != CR50_COMM_ERR_TIMEOUT
+ && (code >> 8) == CR50_COMM_ERR_PREFIX;
+}
+
+static void enable_packet_mode(bool enable)
+{
+ /*
+ * This can be done by set_flags(INPUT|PULL_UP). We don't need it now
+ * because Cr50 never initiates communication.
+ */
+ gpio_set_level(GPIO_PACKET_MODE_EN, enable ? 1 : 0);
+}
+
+static enum cr50_comm_err send_to_cr50(const uint8_t *data, size_t size)
+{
+ timestamp_t until;
+ int i, timeout = 0;
+ struct cr50_comm_response res = {};
+
+ /* This will wake up (if it's sleeping) and interrupt Cr50. */
+ enable_packet_mode(true);
+
+ uart_flush_output();
+ uart_clear_input();
+
+ /*
+ * Send packet. No traffic control, assuming Cr50 consumes stream much
+ * faster. TX buffer shouldn't overflow because it's cleared above and
+ * much bigger than the max packet size.
+ *
+ * Disable interrupts so that the data frame will be stored in the Tx
+ * buffer in one piece.
+ */
+ interrupt_disable();
+ uart_put_raw(data, size);
+ interrupt_enable();
+
+ uart_flush_output();
+
+ until.val = get_time().val + CR50_COMM_TIMEOUT;
+
+ /* Make sure console task won't steal the response (in case in the
+ * future we should exchange packets after tasks start). */
+ task_disable_task(TASK_ID_CONSOLE);
+
+ /* Wait for response from Cr50 */
+ for (i = 0; i < sizeof(res); i++) {
+ while (!timeout) {
+ int c = uart_getc();
+ if (c != -1) {
+ res.error = res.error | c << (i*8);
+ break;
+ }
+ msleep(1);
+ timeout = timestamp_expired(until, NULL);
+ }
+ }
+
+ task_enable_task(TASK_ID_CONSOLE);
+
+ /* Exit packet mode */
+ enable_packet_mode(false);
+
+ if (timeout) {
+ CPRINTS("Timeout");
+ return CR50_COMM_ERR_TIMEOUT;
+ }
+
+ CPRINTS("Received 0x%04x", res.error);
+
+ return res.error;
+}
+
+static enum cr50_comm_err cmd_to_cr50(enum cr50_comm_cmd cmd,
+ const uint8_t *data, size_t size)
+{
+ /*
+ * This is on the stack instead of .bss because vboot_main currently is
+ * called only once (from main). Keeping the space unused in .bss would
+ * be wasteful.
+ */
+ struct {
+ uint8_t preamble[CR50_UART_RX_BUFFER_SIZE];
+ uint8_t packet[CR50_COMM_MAX_REQUEST_SIZE];
+ } __packed s;
+ struct cr50_comm_request *p = (struct cr50_comm_request *)s.packet;
+ int retry = CR50_COMM_MAX_RETRY;
+ enum cr50_comm_err rv;
+
+ /* compose a frame = preamble + packet */
+ memset(s.preamble, CR50_COMM_PREAMBLE, sizeof(s.preamble));
+ p->magic = CR50_PACKET_MAGIC;
+ p->struct_version = CR50_COMM_PACKET_VERSION;
+ p->type = cmd;
+ p->size = size;
+ memcpy(p->data, data, size);
+ p->crc = crc8((uint8_t *)&p->type,
+ sizeof(p->type) + sizeof(p->size) + size);
+
+ do {
+ rv = send_to_cr50((uint8_t *)&s,
+ sizeof(s.preamble) + sizeof(*p) + p->size);
+ if (is_valid_cr50_response(rv))
+ break;
+ msleep(5);
+ } while (--retry);
+
+ return rv;
+}
+
+static enum cr50_comm_err verify_hash(void)
+{
+ const uint8_t *hash;
+ int rv;
+
+ rv = vboot_get_rw_hash(&hash);
+ if (rv)
+ return rv;
+
+ CPRINTS("Verifying hash");
+ return cmd_to_cr50(CR50_COMM_CMD_VERIFY_HASH, hash, SHA256_DIGEST_SIZE);
+}
+
+static enum cr50_comm_err set_boot_mode(uint8_t mode)
+{
+ enum cr50_comm_err rv;
+
+ CPRINTS("Setting boot mode to %s(%d)", boot_mode_to_string(mode), mode);
+ rv = cmd_to_cr50(CR50_COMM_CMD_SET_BOOT_MODE,
+ &mode, sizeof(enum boot_mode));
+ if (rv != CR50_COMM_SUCCESS)
+ CPRINTS("Failed to set boot mode");
+ return rv;
+}
+
+static bool pd_comm_enabled;
+
+static void enable_pd(void)
+{
+ CPRINTS("Enable USB-PD");
+ pd_comm_enabled = true;
+}
+
+bool vboot_allow_usb_pd(void)
+{
+ return pd_comm_enabled;
+}
+
+__overridable void show_critical_error(void)
+{
+ CPRINTS("%s", __func__);
+}
+
+static void verify_and_jump(void)
+{
+ enum cr50_comm_err rv = verify_hash();
+
+ switch (rv) {
+ case CR50_COMM_ERR_BAD_PAYLOAD:
+ /* Cr50 should have set NO_BOOT. */
+ CPRINTS("Hash mismatch");
+ enable_pd();
+ break;
+ case CR50_COMM_SUCCESS:
+ rv = system_run_image_copy(SYSTEM_IMAGE_RW);
+ CPRINTS("Failed to jump (0x%x)", rv);
+ show_critical_error();
+ break;
+ default:
+ CPRINTS("Failed to verify RW (0x%x)", rv);
+ show_critical_error();
+ }
+}
+
+__overridable void show_power_shortage(void)
+{
+ CPRINTS("%s", __func__);
+}
+
+static int is_manual_recovery(void)
+{
+ return host_is_event_set(EC_HOST_EVENT_KEYBOARD_RECOVERY);
+}
+
+static bool is_battery_ready(void)
+{
+ /* TODO: Add battery check (https://crbug.com/1045216) */
+ return true;
+}
+
+void vboot_main(void)
+{
+ CPRINTS("Main");
+
+ if (system_is_in_rw()) {
+ /*
+ * We come here and immediately return. LED shows power shortage
+ * but it will be immediately corrected if the adapter can
+ * provide enough power.
+ */
+ CPRINTS("Already in RW");
+ show_power_shortage();
+ return;
+ }
+
+ if (is_manual_recovery()) {
+ CPRINTS("In recovery mode");
+ if (!IS_ENABLED(CONFIG_BATTERY)
+ && !IS_ENABLED(HAS_TASK_KEYSCAN)) {
+ /*
+ * For Chromeboxes, we relax security by allowing PD in
+ * RO. Attackers don't gain meaningful advantage on
+ * built-in-keyboard-less systems.
+ *
+ * Alternatively, we can use NO_BOOT to show a firmware
+ * screen, strictly requiring BJ adapter and keeping PD
+ * disabled.
+ */
+ enable_pd();
+ return;
+ }
+
+ /*
+ * If battery is drained or bad, we will boot in NO_BOOT mode to
+ * inform the user of the problem.
+ */
+ if (!is_battery_ready()) {
+ CPRINTS("Battery not ready or bad");
+ if (set_boot_mode(BOOT_MODE_NO_BOOT) ==
+ CR50_COMM_SUCCESS)
+ enable_pd();
+ }
+
+ /* We'll enter recovery mode immediately, later, or never. */
+ return;
+ }
+
+ verify_and_jump();
+
+ /*
+ * EFS failed. EC-RO may be able to boot AP if:
+ *
+ * - Battery is charged or
+ * - AC adapter supply in RO >= Boot threshold or
+ * - BJ adapter is plugged.
+ *
+ * Once AP boots, software sync will fix the mismatch. If that's the
+ * reason of the failure, we won't come back here next time.
+ */
+ CPRINTS("Exit");
+}
diff --git a/common/vboot/vboot.c b/common/vboot/vboot.c
index fad338229f..93557e7529 100644
--- a/common/vboot/vboot.c
+++ b/common/vboot/vboot.c
@@ -17,6 +17,7 @@
#include "host_command.h"
#include "rsa.h"
#include "rwsig.h"
+#include "stdbool.h"
#include "sha256.h"
#include "shared_mem.h"
#include "system.h"
@@ -32,20 +33,6 @@ static int has_matrix_keyboard(void)
return 0;
}
-static int is_efs_supported(void)
-{
-#ifdef CONFIG_VBOOT_EFS
- return 1;
-#else
- return 0;
-#endif
-}
-
-static int is_low_power_ap_boot_supported(void)
-{
- return 0;
-}
-
static int verify_slot(enum system_image_copy_t slot)
{
const struct vb21_packed_key *vb21_key;
@@ -168,15 +155,14 @@ static int verify_and_jump(void)
}
/* Request more power: charging battery or more powerful AC adapter */
-static void request_power(void)
+__overridable void show_power_shortage(void)
{
CPRINTS("%s", __func__);
}
-static void request_recovery(void)
+__overridable void show_critical_error(void)
{
CPRINTS("%s", __func__);
- led_critical();
}
static int is_manual_recovery(void)
@@ -184,9 +170,9 @@ static int is_manual_recovery(void)
return host_is_event_set(EC_HOST_EVENT_KEYBOARD_RECOVERY);
}
-static int pd_comm_enabled;
+static bool pd_comm_enabled;
-int vboot_need_pd_comm(void)
+bool vboot_allow_usb_pd(void)
{
return pd_comm_enabled;
}
@@ -202,7 +188,7 @@ void vboot_main(void)
* provide enough power.
*/
CPRINTS("Already in RW. Wait for power...");
- request_power();
+ show_power_shortage();
return;
}
@@ -214,14 +200,14 @@ void vboot_main(void)
* though PD communication is enabled.
*/
CPRINTS("HW-WP not asserted.");
- request_power();
+ show_power_shortage();
return;
}
if (is_manual_recovery()) {
CPRINTS("Manual recovery");
if (battery_is_present() || has_matrix_keyboard()) {
- request_power();
+ show_power_shortage();
return;
}
/* We don't request_power because we don't want to assume all
@@ -230,17 +216,7 @@ void vboot_main(void)
* don't gain meaningful advantage on devices without a matrix
* keyboard */
CPRINTS("Enable PD comm");
- pd_comm_enabled = 1;
- return;
- }
-
- if (!is_efs_supported()) {
- if (is_low_power_ap_boot_supported())
- /* If a device supports this feature, AP's boot power
- * threshold should be set low. That will let EC-RO
- * boot AP and softsync take care of RW verification. */
- return;
- request_power();
+ pd_comm_enabled = true;
return;
}
@@ -250,5 +226,5 @@ void vboot_main(void)
clock_enable_module(MODULE_FAST_CPU, 0);
/* Failed to jump. Need recovery. */
- request_recovery();
+ show_critical_error();
}
diff --git a/common/vboot_hash.c b/common/vboot_hash.c
index 0642e09e3c..0387040670 100644
--- a/common/vboot_hash.c
+++ b/common/vboot_hash.c
@@ -13,6 +13,7 @@
#include "host_command.h"
#include "sha256.h"
#include "shared_mem.h"
+#include "stdbool.h"
#include "system.h"
#include "task.h"
#include "timer.h"
@@ -44,6 +45,8 @@ static uint32_t curr_pos;
static const uint8_t *hash; /* Hash, or NULL if not valid */
static int want_abort;
static int in_progress;
+#define VBOOT_HASH_DEFERRED true
+#define VBOOT_HASH_BLOCKING false
static struct sha256_ctx ctx;
@@ -109,6 +112,35 @@ static int read_and_hash_chunk(int offset, int size)
#define SHA256_PRINT_SIZE 4
#endif
+static void hash_next_chunk(size_t size)
+{
+#ifdef CONFIG_MAPPED_STORAGE
+ flash_lock_mapped_storage(1);
+ SHA256_update(&ctx, (const uint8_t *)(CONFIG_MAPPED_STORAGE_BASE +
+ data_offset + curr_pos), size);
+ flash_lock_mapped_storage(0);
+#else
+ if (read_and_hash_chunk(data_offset + curr_pos, size) != EC_SUCCESS)
+ return;
+#endif
+}
+
+static void vboot_hash_all_chunks(void)
+{
+ do {
+ size_t size = MIN(CHUNK_SIZE, data_size - curr_pos);
+ hash_next_chunk(size);
+ curr_pos += size;
+ } while (curr_pos < data_size);
+
+ hash = SHA256_final(&ctx);
+ CPRINTS("hash done %ph", HEX_BUF(hash, SHA256_PRINT_SIZE));
+ in_progress = 0;
+ clock_enable_module(MODULE_FAST_CPU, 0);
+
+ return;
+}
+
/**
* Do next chunk of hashing work, if any.
*/
@@ -126,16 +158,7 @@ static void vboot_hash_next_chunk(void)
/* Compute the next chunk of hash */
size = MIN(CHUNK_SIZE, data_size - curr_pos);
-
-#ifdef CONFIG_MAPPED_STORAGE
- flash_lock_mapped_storage(1);
- SHA256_update(&ctx, (const uint8_t *)(CONFIG_MAPPED_STORAGE_BASE +
- data_offset + curr_pos), size);
- flash_lock_mapped_storage(0);
-#else
- if (read_and_hash_chunk(data_offset + curr_pos, size) != EC_SUCCESS)
- return;
-#endif
+ hash_next_chunk(size);
curr_pos += size;
if (curr_pos >= data_size) {
@@ -159,13 +182,23 @@ static void vboot_hash_next_chunk(void)
}
/**
- * Start computing a hash of <size> bytes of data at flash offset <offset>.
*
* If nonce_size is non-zero, prefixes the <nonce> onto the data to be hashed.
* Returns non-zero if error.
*/
+/**
+ * Start computing a hash of <size> bytes of data at flash offset <offset>.
+ *
+ * @param offset start address of data on flash to compute hash for.
+ * @param size size of data to compute hash for.
+ * @param nonce nonce to differentiate hash.
+ * @param nonce_size size of nonce.
+ * @param deferred True to hash progressively through deferred calls.
+ * False to hash with a blocking single call.
+ * @return ec_error_list.
+ */
static int vboot_hash_start(uint32_t offset, uint32_t size,
- const uint8_t *nonce, int nonce_size)
+ const uint8_t *nonce, int nonce_size, bool deferred)
{
/* Fail if hash computation is already in progress */
if (in_progress)
@@ -195,7 +228,10 @@ static int vboot_hash_start(uint32_t offset, uint32_t size,
if (nonce_size)
SHA256_update(&ctx, nonce, nonce_size);
- hook_call_deferred(&vboot_hash_next_chunk_data, 0);
+ if (deferred)
+ hook_call_deferred(&vboot_hash_next_chunk_data, 0);
+ else
+ vboot_hash_all_chunks();
return EC_SUCCESS;
}
@@ -232,7 +268,9 @@ int vboot_hash_invalidate(int offset, int size)
*/
static uint32_t get_rw_size(void)
{
-#ifdef CONFIG_VBOOT_EFS
+#ifdef CONFIG_VBOOT_EFS /* Only needed for EFS, which signs and verifies
+ * entire RW, thus not needed for EFS2, which
+ * verifies only the used image size. */
return CONFIG_RW_SIZE;
#else
return system_get_image_used(SYSTEM_IMAGE_RW);
@@ -270,11 +308,19 @@ static void vboot_hash_init(void)
{
/* Start computing the hash of RW firmware */
vboot_hash_start(flash_get_rw_offset(system_get_active_copy()),
- get_rw_size(), NULL, 0);
+ get_rw_size(), NULL, 0, VBOOT_HASH_DEFERRED);
}
}
DECLARE_HOOK(HOOK_INIT, vboot_hash_init, HOOK_PRIO_INIT_VBOOT_HASH);
+int vboot_get_rw_hash(const uint8_t **dst)
+{
+ int rv = vboot_hash_start(flash_get_rw_offset(system_get_active_copy()),
+ get_rw_size(), NULL, 0, VBOOT_HASH_BLOCKING);
+ *dst = hash;
+ return rv;
+}
+
#ifdef CONFIG_SAVE_VBOOT_HASH
static int vboot_hash_preserve_state(void)
@@ -345,13 +391,14 @@ static int command_hash(int argc, char **argv)
} else if (!strcasecmp(argv[1], "rw")) {
return vboot_hash_start(
get_offset(EC_VBOOT_HASH_OFFSET_ACTIVE),
- get_rw_size(), NULL, 0);
+ get_rw_size(),
+ NULL, 0, VBOOT_HASH_DEFERRED);
} else if (!strcasecmp(argv[1], "ro")) {
return vboot_hash_start(
CONFIG_EC_PROTECTED_STORAGE_OFF +
CONFIG_RO_STORAGE_OFF,
system_get_image_used(SYSTEM_IMAGE_RO),
- NULL, 0);
+ NULL, 0, VBOOT_HASH_DEFERRED);
}
return EC_ERROR_PARAM2;
}
@@ -373,9 +420,10 @@ static int command_hash(int argc, char **argv)
return vboot_hash_start(offset, size,
(const uint8_t *)&nonce,
- sizeof(nonce));
+ sizeof(nonce), VBOOT_HASH_DEFERRED);
} else
- return vboot_hash_start(offset, size, NULL, 0);
+ return vboot_hash_start(offset, size,
+ NULL, 0, VBOOT_HASH_DEFERRED);
}
DECLARE_CONSOLE_COMMAND(hash, command_hash,
"[abort | ro | rw] | [<offset> <size> [<nonce>]]",
@@ -428,7 +476,8 @@ static int host_start_hash(const struct ec_params_vboot_hash *p)
(offset == EC_VBOOT_HASH_OFFSET_UPDATE))
size = get_rw_size();
offset = get_offset(offset);
- rv = vboot_hash_start(offset, size, p->nonce_data, p->nonce_size);
+ rv = vboot_hash_start(offset, size, p->nonce_data, p->nonce_size,
+ VBOOT_HASH_DEFERRED);
if (rv == EC_SUCCESS)
return EC_RES_SUCCESS;
diff --git a/include/config.h b/include/config.h
index fcd8978840..591235ec43 100644
--- a/include/config.h
+++ b/include/config.h
@@ -4335,8 +4335,14 @@
/*****************************************************************************/
-/* Support early firmware selection */
+/*
+ * Support early firmware selection
+ *
+ * EFS1 is being deprecated. EFS2 is faster, doesn't need two slots, and
+ * supports rollback protection.
+ */
#undef CONFIG_VBOOT_EFS
+#undef CONFIG_VBOOT_EFS2
/* Offset of RW-A image in writable storage when using EFS. */
#undef CONFIG_RW_A_STORAGE_OFF
diff --git a/include/vboot.h b/include/vboot.h
index 776cc16441..771aeda599 100644
--- a/include/vboot.h
+++ b/include/vboot.h
@@ -3,9 +3,15 @@
* found in the LICENSE file.
*/
+#ifndef __CROS_EC_INCLUDE_VBOOT_H
+#define __CROS_EC_INCLUDE_VBOOT_H
+
#include "common.h"
#include "vb21_struct.h"
#include "rsa.h"
+#include "sha256.h"
+#include "stdbool.h"
+#include "timer.h"
/**
* Validate key contents.
@@ -66,3 +72,113 @@ void vboot_main(void);
* @return 1: need PD communication. 0: PD communication is not needed.
*/
int vboot_need_pd_comm(void);
+
+/**
+ * Callback for boards to notify users of vboot error when no display is
+ * available.
+ *
+ * Typically this happens when a Chromebox is booting on a Type-C adapter and
+ * EFS failed.
+ */
+__override_proto void show_critical_error(void);
+
+/**
+ * Callback for boards to notify the user of power shortage.
+ */
+__override_proto void show_power_shortage(void);
+
+/**
+ * Interrupt handler for packet mode entry.
+ *
+ * @param signal GPIO id for packet mode interrupt pin.
+ */
+void packet_mode_interrupt(enum gpio_signal signal);
+
+/* Maximum number of times EC retries packet transmission before giving up. */
+#define CR50_COMM_MAX_RETRY 5
+
+/* EC's timeout for packet transmission to Cr50. */
+#define CR50_COMM_TIMEOUT (50 * MSEC)
+
+/* Preamble character repeated before the packet header starts. */
+#define CR50_COMM_PREAMBLE 0xec
+
+/* Magic characters used to identify ec-cr50-comm packets */
+#define CR50_PACKET_MAGIC 0x4345 /* 'EC' in little endian */
+
+/* version of struct cr50_comm_request */
+#define CR50_COMM_PACKET_VERSION (0 << 4 | 0 << 0) /* 0.0 */
+
+/**
+ * EC-Cr50 data frame looks like the following:
+ *
+ * [preamble][header][payload]
+ *
+ * preamble: 0xec ...
+ * header: struct cr50_comm_request
+ * payload: data[]
+ */
+struct cr50_comm_request {
+ /* Header */
+ uint16_t magic; /* CR50_PACKET_MAGIC */
+ uint8_t struct_version; /* version of this struct msb:lsb=major:minor */
+ uint8_t crc; /* checksum computed from all bytes after crc */
+ uint16_t type; /* CR50_CMD_* */
+ uint8_t size; /* Payload size. Be easy on Cr50 buffer. */
+ /* Payload */
+ uint8_t data[];
+} __packed;
+
+struct cr50_comm_response {
+ uint16_t error;
+} __packed;
+
+#define CR50_COMM_MAX_REQUEST_SIZE (sizeof(struct cr50_comm_request) \
+ + UINT8_MAX)
+#define CR50_UART_RX_BUFFER_SIZE 32 /* TODO: Get from Cr50 header */
+
+/* commands */
+enum cr50_comm_cmd {
+ CR50_COMM_CMD_HELLO = 0x0000,
+ CR50_COMM_CMD_SET_BOOT_MODE = 0x0001,
+ CR50_COMM_CMD_VERIFY_HASH = 0x0002,
+ CR50_COMM_CMD_LIMIT = 0xffff,
+} __packed;
+BUILD_ASSERT(sizeof(enum cr50_comm_cmd) == sizeof(uint16_t));
+
+#define CR50_COMM_ERR_PREFIX 0xec
+
+/* return code */
+enum cr50_comm_err {
+ CR50_COMM_SUCCESS = 0xec00,
+ CR50_COMM_ERR_UNKNOWN = 0xec01,
+ CR50_COMM_ERR_MAGIC = 0xec02,
+ CR50_COMM_ERR_CRC = 0xec03,
+ CR50_COMM_ERR_SIZE = 0xec04,
+ CR50_COMM_ERR_TIMEOUT = 0xec05, /* Generated by EC */
+ CR50_COMM_ERR_BAD_PAYLOAD = 0xec06,
+ CR50_COMM_ERR_UNDEFINED_CMD = 0xec07,
+ CR50_COMM_ERR_STRUCT_VERSION = 0xec08,
+} __packed;
+BUILD_ASSERT(sizeof(enum cr50_comm_err) == sizeof(uint16_t));
+
+/*
+ * BIT(1) : NO_BOOT flag
+ * BIT(0) : RECOVERY flag
+ */
+enum boot_mode {
+ BOOT_MODE_NORMAL = 0x00,
+ BOOT_MODE_NO_BOOT = 0x01,
+} __packed;
+BUILD_ASSERT(sizeof(enum boot_mode) == sizeof(uint8_t));
+
+/**
+ * Indicate PD is allowed (in RO) by vboot or not.
+ *
+ * Overridden by each EFS implementation (EFS1 and EFS2) not by boards.
+ *
+ * @return true - allowed. false - disallowed.
+ */
+__override_proto bool vboot_allow_usb_pd(void);
+
+#endif /* __CROS_EC_INCLUDE_VBOOT_H */
diff --git a/include/vboot_hash.h b/include/vboot_hash.h
index 3d66a7c56d..126872393e 100644
--- a/include/vboot_hash.h
+++ b/include/vboot_hash.h
@@ -11,6 +11,23 @@
#include "common.h"
/**
+ * Get hash of RW image.
+ *
+ * Your task will be blocked until hash computation is done. Hashing can be
+ * aborted only due to internal errors (e.g. read error) but not external
+ * causes.
+ *
+ * This is expected to be called before tasks are initialized. If it's called
+ * after tasks are started, it may starve lower priority tasks.
+ *
+ * See chromium:1047870 for some optimization.
+ *
+ * @param dst (OUT) Address where computed hash is stored.
+ * @return enum ec_error_list.
+ */
+int vboot_get_rw_hash(const uint8_t **dst);
+
+/**
* Invalidate the hash if the hashed data overlaps the specified region.
*
* @param offset Region start offset in flash
diff --git a/power/intel_x86.c b/power/intel_x86.c
index ecf36268cb..334a9367ca 100644
--- a/power/intel_x86.c
+++ b/power/intel_x86.c
@@ -737,7 +737,7 @@ enum ec_error_list intel_x86_wait_power_up_ok(void)
power_up_inhibited = 0;
#endif
-#ifdef CONFIG_VBOOT_EFS
+#if defined(CONFIG_VBOOT_EFS) || defined(CONFIG_VBOOT_EFS2)
/*
* We have to test power readiness here (instead of S5->S3)
* because when entering S5, EC enables EC_ROP_SLP_SUS pin
diff --git a/power/skylake.c b/power/skylake.c
index d51f6079ff..829a0fa370 100644
--- a/power/skylake.c
+++ b/power/skylake.c
@@ -187,7 +187,8 @@ void chipset_handle_reboot(void)
while (1)
; /* wait here */
}
-#ifndef CONFIG_VBOOT_EFS
+#if !defined(CONFIG_VBOOT_EFS) || !defined(CONFIG_VBOOT_EFS2)
+/* This is run in main for EFS1 & EFS2 */
DECLARE_HOOK(HOOK_INIT, chipset_handle_reboot, HOOK_PRIO_FIRST);
#endif
#endif /* CONFIG_CHIPSET_HAS_PLATFORM_RESET */