diff options
Diffstat (limited to 'common/vboot/efs2.c')
-rw-r--r-- | common/vboot/efs2.c | 352 |
1 files changed, 0 insertions, 352 deletions
diff --git a/common/vboot/efs2.c b/common/vboot/efs2.c deleted file mode 100644 index e5c3b64f04..0000000000 --- a/common/vboot/efs2.c +++ /dev/null @@ -1,352 +0,0 @@ -/* 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 "hooks.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; -} - -__overridable void board_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; - uint32_t lock_key; - struct cr50_comm_response res = {}; - - /* This will wake up (if it's sleeping) and interrupt Cr50. */ - board_enable_packet_mode(true); - - uart_flush_output(); - uart_clear_input(); - - if (uart_shell_stop()) { - /* Failed to stop the shell. */ - board_enable_packet_mode(false); - return CR50_COMM_ERR_UNKNOWN; - } - - /* - * 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. - */ - lock_key = irq_lock(); - uart_put_raw(data, size); - irq_unlock(lock_key); - - uart_flush_output(); - - until.val = get_time().val + CR50_COMM_TIMEOUT; - - /* - * Make sure console task won't steal the response in case we exchange - * packets after tasks start. - */ -#ifndef CONFIG_ZEPHYR - if (task_start_called()) - task_disable_task(TASK_ID_CONSOLE); -#endif /* !CONFIG_ZEPHYR */ - - /* 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); - } - } - - uart_shell_start(); -#ifndef CONFIG_ZEPHYR - if (task_start_called()) - task_enable_task(TASK_ID_CONSOLE); -#endif /* CONFIG_ZEPHYR */ - - /* Exit packet mode */ - board_enable_packet_mode(false); - - CPRINTS("Received 0x%04x", res.error); - - if (timeout) { - CPRINTS("Timeout"); - return CR50_COMM_ERR_TIMEOUT; - } - - 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 = cros_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; - - /* Wake up Cr50 beforehand in case it's asleep. */ - board_enable_packet_mode(true); - CPRINTS("Ping Cr50"); - msleep(1); - board_enable_packet_mode(false); - - 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: - system_set_reset_flags(EC_RESET_FLAG_EFS); - rv = system_run_image_copy(EC_IMAGE_RW); - CPRINTS("Failed to jump (0x%x)", rv); - system_clear_reset_flags(EC_RESET_FLAG_EFS); - 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 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 (system_is_manual_recovery() || - (system_get_reset_flags() & EC_RESET_FLAG_STAY_IN_RO)) { - if (system_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"); -} - -void hook_shutdown(void) -{ - CPRINTS("%s", __func__); - - /* - * We filter the cases which can be interfered with if we execute - * system_reset in HOOK_CHIPSET_SHUTDOWN context. Most cases are - * filtered out by system_is_in_rw (e.g. system_common_shutdown, - * check_pending_cutoff). - */ - if (system_is_in_rw()) - return; - - /* - * We can't reset here because it'll completely tear down the power - * and disturb the PCH's power sequence. We instead sysjump. - * - * Note that this does not reduce the security. Even if it's hijacked in - * NO_BOOT mode, an RO still needs to go through a cold reset to clear - * NO_BOOT flag since Cr50 rejects to switch from NO_BOOT to NORMAL. - * If a spoofed matching hash is passed to Cr50, Cr50 would reset EC. - */ - system_set_reset_flags(EC_RESET_FLAG_AP_IDLE); - verify_and_jump(); -} -/* - * There can be hooks which are needed to set external chips to a certain state - * in S5. If the initial state (i.e. AP_OFF state) is different from what those - * hooks realize, they need to be considered. This hook runs last (i.e. - * HOOK_PRIO_LAST) to make our landing on S5 as mild as possible. - */ -DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN_COMPLETE, hook_shutdown, HOOK_PRIO_LAST); |