From 37151225f4d791204a75173eec64ebfdaf2cbdec Mon Sep 17 00:00:00 2001 From: Daisuke Nojiri Date: Mon, 21 Oct 2019 10:07:48 -0700 Subject: 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 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 Commit-Queue: Daisuke Nojiri Tested-by: Daisuke Nojiri Auto-Submit: Daisuke Nojiri --- board/endeavour/board.h | 2 +- board/endeavour/led.c | 4 +- board/puff/board.h | 2 +- board/puff/led.c | 4 +- common/build.mk | 1 + common/main.c | 5 +- common/system.c | 2 +- common/usb_common.c | 6 + common/usb_pd_protocol.c | 9 +- common/vboot/efs2.c | 297 +++++++++++++++++++++++++++++++++++++++++++++++ common/vboot/vboot.c | 44 ++----- common/vboot_hash.c | 89 ++++++++++---- include/config.h | 8 +- include/vboot.h | 116 ++++++++++++++++++ include/vboot_hash.h | 17 +++ power/intel_x86.c | 2 +- power/skylake.c | 3 +- 17 files changed, 539 insertions(+), 72 deletions(-) create mode 100644 common/vboot/efs2.c 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 bytes of data at flash offset . * * If nonce_size is non-zero, prefixes the onto the data to be hashed. * Returns non-zero if error. */ +/** + * Start computing a hash of bytes of data at flash 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] | [ []]", @@ -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 @@ -10,6 +10,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. * 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 */ -- cgit v1.2.1